wuheng 1 year ago
parent
commit
b1da594a6f

+ 3 - 1
build/plugins/index.ts

@@ -4,6 +4,7 @@ import vueJsx from '@vitejs/plugin-vue-jsx';
 import unocss from '@unocss/vite';
 import progress from 'vite-plugin-progress';
 import pageRoute from '@soybeanjs/vite-plugin-vue-page-route';
+import PurgeIcons from 'vite-plugin-purge-icons';
 import unplugin from './unplugin';
 import mock from './mock';
 import visualizer from './visualizer';
@@ -26,7 +27,8 @@ export function setupVitePlugins(viteEnv: ImportMetaEnv): (PluginOption | Plugin
     unocss(),
     mock(viteEnv),
     progress(),
-    pageRoute()
+    pageRoute(),
+    PurgeIcons()
   ];
 
   if (viteEnv.VITE_VISUALIZER === 'Y') {

+ 3 - 0
package.json

@@ -32,6 +32,8 @@
     "@fast-crud/fast-extends": "^1.13.6",
     "@fast-crud/ui-interface": "^1.13.6",
     "@fast-crud/ui-naive": "^1.13.6",
+    "@iconify/iconify": "2.0.1",
+    "@purge-icons/generated": "0.7.0",
     "@soybeanjs/vue-materials": "^0.1.9",
     "@vicons/antd": "^0.12.0",
     "@vueuse/core": "^10.1.2",
@@ -51,6 +53,7 @@
     "swiper": "^9.3.2",
     "ua-parser-js": "^1.0.35",
     "vditor": "^3.9.2",
+    "vite-plugin-purge-icons": "0.7.0",
     "vue": "3.3.4",
     "vue-i18n": "^9.2.2",
     "vue-monoplasty-slide-verify": "^1.3.1",

+ 122 - 66
pnpm-lock.yaml

@@ -34,6 +34,12 @@ dependencies:
   '@fast-crud/ui-naive':
     specifier: ^1.13.6
     version: 1.13.6
+  '@iconify/iconify':
+    specifier: 2.0.1
+    version: 2.0.1
+  '@purge-icons/generated':
+    specifier: 0.7.0
+    version: 0.7.0
   '@soybeanjs/vue-materials':
     specifier: ^0.1.9
     version: 0.1.9(vue@3.3.4)
@@ -91,6 +97,9 @@ dependencies:
   vditor:
     specifier: ^3.9.2
     version: 3.9.2
+  vite-plugin-purge-icons:
+    specifier: 0.7.0
+    version: 0.7.0(vite@4.3.8)
   vue:
     specifier: 3.3.4
     version: 3.3.4
@@ -2757,7 +2766,6 @@ packages:
     cpu: [arm64]
     os: [android]
     requiresBuild: true
-    dev: true
     optional: true
 
   /@esbuild/android-arm@0.17.18:
@@ -2766,7 +2774,6 @@ packages:
     cpu: [arm]
     os: [android]
     requiresBuild: true
-    dev: true
     optional: true
 
   /@esbuild/android-x64@0.17.18:
@@ -2775,7 +2782,6 @@ packages:
     cpu: [x64]
     os: [android]
     requiresBuild: true
-    dev: true
     optional: true
 
   /@esbuild/darwin-arm64@0.17.18:
@@ -2784,7 +2790,6 @@ packages:
     cpu: [arm64]
     os: [darwin]
     requiresBuild: true
-    dev: true
     optional: true
 
   /@esbuild/darwin-x64@0.17.18:
@@ -2793,7 +2798,6 @@ packages:
     cpu: [x64]
     os: [darwin]
     requiresBuild: true
-    dev: true
     optional: true
 
   /@esbuild/freebsd-arm64@0.17.18:
@@ -2802,7 +2806,6 @@ packages:
     cpu: [arm64]
     os: [freebsd]
     requiresBuild: true
-    dev: true
     optional: true
 
   /@esbuild/freebsd-x64@0.17.18:
@@ -2811,7 +2814,6 @@ packages:
     cpu: [x64]
     os: [freebsd]
     requiresBuild: true
-    dev: true
     optional: true
 
   /@esbuild/linux-arm64@0.17.18:
@@ -2820,7 +2822,6 @@ packages:
     cpu: [arm64]
     os: [linux]
     requiresBuild: true
-    dev: true
     optional: true
 
   /@esbuild/linux-arm@0.17.18:
@@ -2829,7 +2830,6 @@ packages:
     cpu: [arm]
     os: [linux]
     requiresBuild: true
-    dev: true
     optional: true
 
   /@esbuild/linux-ia32@0.17.18:
@@ -2838,7 +2838,6 @@ packages:
     cpu: [ia32]
     os: [linux]
     requiresBuild: true
-    dev: true
     optional: true
 
   /@esbuild/linux-loong64@0.14.54:
@@ -2856,7 +2855,6 @@ packages:
     cpu: [loong64]
     os: [linux]
     requiresBuild: true
-    dev: true
     optional: true
 
   /@esbuild/linux-mips64el@0.17.18:
@@ -2865,7 +2863,6 @@ packages:
     cpu: [mips64el]
     os: [linux]
     requiresBuild: true
-    dev: true
     optional: true
 
   /@esbuild/linux-ppc64@0.17.18:
@@ -2874,7 +2871,6 @@ packages:
     cpu: [ppc64]
     os: [linux]
     requiresBuild: true
-    dev: true
     optional: true
 
   /@esbuild/linux-riscv64@0.17.18:
@@ -2883,7 +2879,6 @@ packages:
     cpu: [riscv64]
     os: [linux]
     requiresBuild: true
-    dev: true
     optional: true
 
   /@esbuild/linux-s390x@0.17.18:
@@ -2892,7 +2887,6 @@ packages:
     cpu: [s390x]
     os: [linux]
     requiresBuild: true
-    dev: true
     optional: true
 
   /@esbuild/linux-x64@0.17.18:
@@ -2901,7 +2895,6 @@ packages:
     cpu: [x64]
     os: [linux]
     requiresBuild: true
-    dev: true
     optional: true
 
   /@esbuild/netbsd-x64@0.17.18:
@@ -2910,7 +2903,6 @@ packages:
     cpu: [x64]
     os: [netbsd]
     requiresBuild: true
-    dev: true
     optional: true
 
   /@esbuild/openbsd-x64@0.17.18:
@@ -2919,7 +2911,6 @@ packages:
     cpu: [x64]
     os: [openbsd]
     requiresBuild: true
-    dev: true
     optional: true
 
   /@esbuild/sunos-x64@0.17.18:
@@ -2928,7 +2919,6 @@ packages:
     cpu: [x64]
     os: [sunos]
     requiresBuild: true
-    dev: true
     optional: true
 
   /@esbuild/win32-arm64@0.17.18:
@@ -2937,7 +2927,6 @@ packages:
     cpu: [arm64]
     os: [win32]
     requiresBuild: true
-    dev: true
     optional: true
 
   /@esbuild/win32-ia32@0.17.18:
@@ -2946,7 +2935,6 @@ packages:
     cpu: [ia32]
     os: [win32]
     requiresBuild: true
-    dev: true
     optional: true
 
   /@esbuild/win32-x64@0.17.18:
@@ -2955,7 +2943,6 @@ packages:
     cpu: [x64]
     os: [win32]
     requiresBuild: true
-    dev: true
     optional: true
 
   /@eslint-community/eslint-utils@4.4.0(eslint@8.41.0):
@@ -3070,11 +3057,27 @@ packages:
     engines: {node: '>=6.9.0'}
     dev: true
 
+  /@iconify/iconify@2.0.0-rc.6:
+    resolution: {integrity: sha512-pXvLXqLPQsjpDY4qbbyh5cPEtakTfWfQCAo6SdYNhYQzat+/0fbeEhScryqaketNAG0bT4/+deKezkJZTvbuSg==}
+    dependencies:
+      cross-fetch: 3.1.8
+    transitivePeerDependencies:
+      - encoding
+    dev: false
+
+  /@iconify/iconify@2.0.1:
+    resolution: {integrity: sha512-heGCmdiRc58+TQjKPiem8cmNZj7YXDG3TphRVw0UGjsz8/OKgN2ncBPy1kfFIiv5aKhUsij6WVTfOMUS7YgTbA==}
+    dependencies:
+      cross-fetch: 3.1.8
+    transitivePeerDependencies:
+      - encoding
+    dev: false
+
   /@iconify/json@2.2.67:
     resolution: {integrity: sha512-yRA3PTP9JW8eT+rtUGF5HLFtKT0l9BVJsvFqKvg1zIVzPRc04fFzJVrF5qxZwSN25ttZAe3LUXUmH7rwI5GGEw==}
     dependencies:
       '@iconify/types': 2.0.0
-      pathe: 1.1.0
+      pathe: 1.1.1
     dev: true
 
   /@iconify/types@2.0.0:
@@ -3216,12 +3219,10 @@ packages:
     dependencies:
       '@nodelib/fs.stat': 2.0.5
       run-parallel: 1.2.0
-    dev: true
 
   /@nodelib/fs.stat@2.0.5:
     resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==}
     engines: {node: '>= 8'}
-    dev: true
 
   /@nodelib/fs.walk@1.2.8:
     resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==}
@@ -3229,7 +3230,6 @@ packages:
     dependencies:
       '@nodelib/fs.scandir': 2.1.5
       fastq: 1.15.0
-    dev: true
 
   /@npmcli/fs@2.1.2:
     resolution: {integrity: sha512-yOJKRvohFOaLqipNtwYB9WugyZKhC/DZC4VYPmpaCzDBrA8YpK3qHZ8/HGscMnE4GqbkLNuVcCnxkeQEdGt6LQ==}
@@ -3318,7 +3318,7 @@ packages:
     engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0}
     dependencies:
       cross-spawn: 7.0.3
-      fast-glob: 3.2.12
+      fast-glob: 3.3.1
       is-glob: 4.0.3
       open: 9.1.0
       picocolors: 1.0.0
@@ -3350,6 +3350,27 @@ packages:
     resolution: {integrity: sha512-a5Sab1C4/icpTZVzZc5Ghpz88yQtGOyNqYXcZgOssB2uuAr+wF/MvN6bgtW32q7HHrvBki+BsZ0OuNv6EV3K9g==}
     dev: true
 
+  /@purge-icons/core@0.7.0:
+    resolution: {integrity: sha512-PaCeTFjkQUX+MzBsNg3L8x5aCZqXwaUSNw1FY3Jn7wlLqNqxRNoShw5P//a1DQAy7hLlUHvEL6IGeDoN/xf98A==}
+    dependencies:
+      '@iconify/iconify': 2.0.0-rc.6
+      axios: 0.21.4(debug@4.3.4)
+      debug: 4.3.4
+      fast-glob: 3.3.1
+      fs-extra: 9.1.0
+    transitivePeerDependencies:
+      - encoding
+      - supports-color
+    dev: false
+
+  /@purge-icons/generated@0.7.0:
+    resolution: {integrity: sha512-4SHVpZnKaW5ekRTjhPY9b1pALVlF0pDuGIDRAlxAm0V+gQVOL0+Ghav6U9XqXFj2kiG1+eQ8swpvB+kd0a+tqg==}
+    dependencies:
+      '@iconify/iconify': 2.0.1
+    transitivePeerDependencies:
+      - encoding
+    dev: false
+
   /@rollup/plugin-babel@5.3.1(@babel/core@7.21.8)(rollup@2.79.1):
     resolution: {integrity: sha512-WFfdLWU/xVWKeRQnKmIAQULUI7Il0gZnBIH/ZFO069wYIfPu+8zrfp/KMW0atmELoRDq8FbiP3VCss9MhCut7Q==}
     engines: {node: '>= 10.0.0'}
@@ -3719,7 +3740,6 @@ packages:
 
   /@types/node@20.2.1:
     resolution: {integrity: sha512-DqJociPbZP1lbZ5SQPk4oag6W7AyaGMO6gSfRwq3PWl4PXTwJpRQJhDq4W0kzrg3w6tJ1SwlvGZ5uKFHY13LIg==}
-    dev: true
 
   /@types/normalize-package-data@2.4.1:
     resolution: {integrity: sha512-Gj7cI7z+98M282Tqmp2K5EIsoouUEzbBJhQQzDE3jSIRk6r9gsz0oUokqIUR4u1R3dMHo0pDHM7sNOHyhulypw==}
@@ -4847,7 +4867,6 @@ packages:
     dependencies:
       normalize-path: 3.0.0
       picomatch: 2.3.1
-    dev: true
 
   /aproba@2.0.0:
     resolution: {integrity: sha512-lYe4Gx7QT+MKGbDsA+Z+he/Wtef0BiwDOlK/XkBrdfsh9J/jPPXbX0tE9x9cl27Tmu5gg3QUbUrQYa/y+KOHPQ==}
@@ -5008,7 +5027,6 @@ packages:
   /at-least-node@1.0.0:
     resolution: {integrity: sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==}
     engines: {node: '>= 4.0.0'}
-    dev: true
 
   /atob@2.1.2:
     resolution: {integrity: sha512-Wm6ukoaOGJi/73p/cl2GvLjTI5JM1k/O14isD73YML8StrH/7/lRFgmg8nICZgD3bZZvjwCGxtMOD3wWNAu8cg==}
@@ -5021,10 +5039,18 @@ packages:
     engines: {node: '>= 0.4'}
     dev: true
 
+  /axios@0.21.4(debug@4.3.4):
+    resolution: {integrity: sha512-ut5vewkiu8jjGBdqpM44XxjuCjq9LAKeHVmoVfHVzy8eHgxxq8SbAVQNovDA8mVi05kP0Ea/n/UzcSHcTJQfNg==}
+    dependencies:
+      follow-redirects: 1.15.2(debug@4.3.4)
+    transitivePeerDependencies:
+      - debug
+    dev: false
+
   /axios@1.4.0:
     resolution: {integrity: sha512-S4XCWMEmzvo64T9GfvQDOXgYRDJ/wsSZc7Jvdgx5u1sd0JwsuPLqb3SYmusag+edF6ziyMensPVqLTSc1PiSEA==}
     dependencies:
-      follow-redirects: 1.15.2
+      follow-redirects: 1.15.2(debug@4.3.4)
       form-data: 4.0.0
       proxy-from-env: 1.1.0
     transitivePeerDependencies:
@@ -5095,7 +5121,6 @@ packages:
   /binary-extensions@2.2.0:
     resolution: {integrity: sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==}
     engines: {node: '>=8'}
-    dev: true
 
   /bluebird@3.7.2:
     resolution: {integrity: sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==}
@@ -5168,7 +5193,6 @@ packages:
     engines: {node: '>=8'}
     dependencies:
       fill-range: 7.0.1
-    dev: true
 
   /browserslist@4.21.5:
     resolution: {integrity: sha512-tUkiguQGW7S3IhB7N+c2MV/HZPSCPAAiYBZXLsBhFB/PCy6ZKKsZrmBayHV9fdGV/ARIfJ14NkxKzRDjvp7L6w==}
@@ -5387,7 +5411,6 @@ packages:
       readdirp: 3.6.0
     optionalDependencies:
       fsevents: 2.3.2
-    dev: true
 
   /chownr@2.0.0:
     resolution: {integrity: sha512-bIomtDF5KGpdogkLd9VspvFzk9KfpyyGlS8YFVZl7TGPBHL5snIOnxeshwVgPteQ9b4Eydl+pVbIyE1DcvCWgQ==}
@@ -5859,6 +5882,14 @@ packages:
       cross-spawn: 7.0.3
     dev: true
 
+  /cross-fetch@3.1.8:
+    resolution: {integrity: sha512-cvA+JwZoU0Xq+h6WkMvAUqPEYy92Obet6UdKLfW60qn99ftItKjB5T+BkyWOFWe2pUyfQ+IJHmpOTznqk1M6Kg==}
+    dependencies:
+      node-fetch: 2.6.13
+    transitivePeerDependencies:
+      - encoding
+    dev: false
+
   /cross-spawn@7.0.3:
     resolution: {integrity: sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==}
     engines: {node: '>= 8'}
@@ -6863,7 +6894,6 @@ packages:
       '@esbuild/win32-arm64': 0.17.18
       '@esbuild/win32-ia32': 0.17.18
       '@esbuild/win32-x64': 0.17.18
-    dev: true
 
   /escalade@3.1.1:
     resolution: {integrity: sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==}
@@ -7505,7 +7535,6 @@ packages:
       glob-parent: 5.1.2
       merge2: 1.4.1
       micromatch: 4.0.5
-    dev: true
 
   /fast-json-stable-stringify@2.1.0:
     resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==}
@@ -7528,7 +7557,6 @@ packages:
     resolution: {integrity: sha512-wBrocU2LCXXa+lWBt8RoIRD89Fi8OdABODa/kEnyeyjS5aZO5/GNvI5sEINADqP/h8M29UHTHUb53sUu5Ihqdw==}
     dependencies:
       reusify: 1.0.4
-    dev: true
 
   /fecha@4.2.3:
     resolution: {integrity: sha512-OP2IUU6HeYKJi3i0z4A19kHMQoLVs4Hc+DPqqxI2h/DPZHTm/vjsfC6P0b4jCMy14XizLBqvndQ+UilD7707Jw==}
@@ -7574,7 +7602,6 @@ packages:
     engines: {node: '>=8'}
     dependencies:
       to-regex-range: 5.0.1
-    dev: true
 
   /finalhandler@1.1.2:
     resolution: {integrity: sha512-aAWcW57uxVNrQZqFXjITpW3sIUQmHGG3qSb9mUah9MgMC4NeWhNOlNjXEYq3HjRAvL6arUviZGGJsBg6z0zsWA==}
@@ -7633,7 +7660,7 @@ packages:
     resolution: {integrity: sha512-5nqDSxl8nn5BSNxyR3n4I6eDmbolI6WT+QqR547RwxQapgjQBmtktdP+HTBb/a/zLsbzERTONyUB5pefh5TtjQ==}
     dev: true
 
-  /follow-redirects@1.15.2:
+  /follow-redirects@1.15.2(debug@4.3.4):
     resolution: {integrity: sha512-VQLG33o04KaQ8uYi2tVNbdrWp1QWxNNea+nmIB4EVM28v0hmP17z7aG1+wAkNzVq4KeXTq3221ye5qTJP91JwA==}
     engines: {node: '>=4.0'}
     peerDependencies:
@@ -7641,6 +7668,8 @@ packages:
     peerDependenciesMeta:
       debug:
         optional: true
+    dependencies:
+      debug: 4.3.4
     dev: false
 
   /for-each@0.3.3:
@@ -7736,7 +7765,6 @@ packages:
       graceful-fs: 4.2.11
       jsonfile: 6.1.0
       universalify: 2.0.0
-    dev: true
 
   /fs-minipass@2.1.0:
     resolution: {integrity: sha512-V/JgOLFCS+R6Vcq0slCuaeWEdNC3ouDlJMNIsacH2VtALiu9mV4LPrHc5cDl8k5aw6J8jwgWWpiTo5RYhmIzvg==}
@@ -7764,7 +7792,6 @@ packages:
     engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0}
     os: [darwin]
     requiresBuild: true
-    dev: true
     optional: true
 
   /ftp@0.3.10:
@@ -7926,7 +7953,6 @@ packages:
     engines: {node: '>= 6'}
     dependencies:
       is-glob: 4.0.3
-    dev: true
 
   /glob-parent@6.0.2:
     resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==}
@@ -8009,7 +8035,7 @@ packages:
     dependencies:
       array-union: 2.1.0
       dir-glob: 3.0.1
-      fast-glob: 3.2.12
+      fast-glob: 3.3.1
       ignore: 5.2.4
       merge2: 1.4.1
       slash: 3.0.0
@@ -8360,7 +8386,6 @@ packages:
 
   /immutable@4.3.0:
     resolution: {integrity: sha512-0AOCmOip+xgJwEVTQj1EfiDDOkPmuyllDuTuEX+DDXUgapLAsBIfkg3sxCYyCEA8mQqZrrxPUGjcOQ2JS3WLkg==}
-    dev: true
 
   /import-fresh@3.3.0:
     resolution: {integrity: sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==}
@@ -8481,7 +8506,6 @@ packages:
     engines: {node: '>=8'}
     dependencies:
       binary-extensions: 2.2.0
-    dev: true
 
   /is-boolean-object@1.1.2:
     resolution: {integrity: sha512-gDYaKHJmnj4aWxyj6YHyXVpdQawtVLHU5cb+eztPGczf6cjuTdwve5ZIEfgXqH4e57An1D1AKf8CZ3kYrQRqYA==}
@@ -8581,7 +8605,6 @@ packages:
   /is-extglob@2.1.1:
     resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==}
     engines: {node: '>=0.10.0'}
-    dev: true
 
   /is-fullwidth-code-point@3.0.0:
     resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==}
@@ -8598,7 +8621,6 @@ packages:
     engines: {node: '>=0.10.0'}
     dependencies:
       is-extglob: 2.1.1
-    dev: true
 
   /is-hotkey@0.2.0:
     resolution: {integrity: sha512-UknnZK4RakDmTgz4PI1wIph5yxSs/mvChWs9ifnlXsKuXgWmOkY/hAE0H/k2MIqH0RlRye0i1oC07MCRSD28Mw==}
@@ -8662,7 +8684,6 @@ packages:
   /is-number@7.0.0:
     resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==}
     engines: {node: '>=0.12.0'}
-    dev: true
 
   /is-obj@1.0.1:
     resolution: {integrity: sha512-l4RyHgRqGN4Y3+9JHVrNqO+tN0rV5My76uW5/nuO4K1b6vw5G8d/cmFjP9tRfEsdhZNt0IFdZuK/c2Vr4Nb+Qg==}
@@ -9002,7 +9023,6 @@ packages:
       universalify: 2.0.0
     optionalDependencies:
       graceful-fs: 4.2.11
-    dev: true
 
   /jsonlines@0.1.1:
     resolution: {integrity: sha512-ekDrAGso79Cvf+dtm+mL8OBI2bmAOt3gssYs833De/C9NmIpWDWyUO4zPgB5x2/OhY366dkhgfPMYfwZF7yOZA==}
@@ -9455,7 +9475,6 @@ packages:
   /merge2@1.4.1:
     resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==}
     engines: {node: '>= 8'}
-    dev: true
 
   /micromatch@3.1.0:
     resolution: {integrity: sha512-3StSelAE+hnRvMs8IdVW7Uhk8CVed5tp+kLLGlBP6WiRAXS21GPGu/Nat4WNPXj2Eoc24B02SaeoyozPMfj0/g==}
@@ -9484,7 +9503,6 @@ packages:
     dependencies:
       braces: 3.0.2
       picomatch: 2.3.1
-    dev: true
 
   /mime-db@1.52.0:
     resolution: {integrity: sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==}
@@ -9831,6 +9849,18 @@ packages:
     resolution: {integrity: sha512-5IAMBTl9p6PaAjYCnMv5FmqIF6GcZnawAVnzaCG0rX2aYZJ4CxEkZNtVPuTRug7fL7wyM5BQYTlAzcyMPi6oTQ==}
     dev: true
 
+  /node-fetch@2.6.13:
+    resolution: {integrity: sha512-StxNAxh15zr77QvvkmveSQ8uCQ4+v5FkvNTj0OESmiHu+VRi/gXArXtkWMElOsOUNLtUEvI4yS+rdtOHZTwlQA==}
+    engines: {node: 4.x || >=6.0.0}
+    peerDependencies:
+      encoding: ^0.1.0
+    peerDependenciesMeta:
+      encoding:
+        optional: true
+    dependencies:
+      whatwg-url: 5.0.0
+    dev: false
+
   /node-gyp@9.3.1:
     resolution: {integrity: sha512-4Q16ZCqq3g8awk6UplT7AuxQ35XN4R/yf/+wSAwcBUAjg7l58RTactWaP8fIDTi0FzI7YcVLujwExakZlfWkXg==}
     engines: {node: ^12.13 || ^14.13 || >=16}
@@ -9895,7 +9925,6 @@ packages:
   /normalize-path@3.0.0:
     resolution: {integrity: sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==}
     engines: {node: '>=0.10.0'}
-    dev: true
 
   /normalize-url@8.0.0:
     resolution: {integrity: sha512-uVFpKhj5MheNBJRTiMZ9pE/7hD1QTeEvugSJW/OmLzAp78PB5O6adfMNTvmfKhXBkvCzC+rqifWcVYpGFwTjnw==}
@@ -10452,10 +10481,6 @@ packages:
     resolution: {integrity: sha512-sTitTPYnn23esFR3RlqYBWn4c45WGeLcsKzQiUpXJAyfcWkolvlYpV8FLo7JishK946oQwMFUCHXQ9AjGPKExw==}
     dev: true
 
-  /pathe@1.1.0:
-    resolution: {integrity: sha512-ODbEPR0KKHqECXW1GoxdDb+AZvULmXjVPy4rt+pGo2+TnjJTIPJQSVS6N63n8T2Ip+syHhbn52OewKicV0373w==}
-    dev: true
-
   /pathe@1.1.1:
     resolution: {integrity: sha512-d+RQGp0MAYTIaDBIMmOfMwz3E+LOZnxx1HZd5R18mmCZY0QBlK0LDZfPc8FW8Ed2DlvsuE6PRjroDY+wg4+j/Q==}
     dev: true
@@ -10488,7 +10513,6 @@ packages:
   /picomatch@2.3.1:
     resolution: {integrity: sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==}
     engines: {node: '>=8.6'}
-    dev: true
 
   /picomodal@3.0.0:
     resolution: {integrity: sha512-FoR3TDfuLlqUvcEeK5ifpKSVVns6B4BQvc8SDF6THVMuadya6LLtji0QgUDSStw0ZR2J7I6UGi5V2V23rnPWTw==}
@@ -10838,7 +10862,6 @@ packages:
 
   /queue-microtask@1.2.3:
     resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==}
-    dev: true
 
   /quick-lru@4.0.1:
     resolution: {integrity: sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g==}
@@ -11005,7 +11028,6 @@ packages:
     engines: {node: '>=8.10.0'}
     dependencies:
       picomatch: 2.3.1
-    dev: true
 
   /redent@3.0.0:
     resolution: {integrity: sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==}
@@ -11182,7 +11204,6 @@ packages:
   /reusify@1.0.4:
     resolution: {integrity: sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==}
     engines: {iojs: '>=1.0.0', node: '>=0.10.0'}
-    dev: true
 
   /rfdc@1.3.0:
     resolution: {integrity: sha512-V2hovdzFbOi77/WajaSMXk2OLm+xNIeQdMMuB7icj7bk6zi2F8GGAxigcnDFpJHbNyNcgyJDiP+8nOrY5cZGrA==}
@@ -11218,6 +11239,17 @@ packages:
       glob: 10.2.2
     dev: true
 
+  /rollup-plugin-purge-icons@0.7.0:
+    resolution: {integrity: sha512-zAff7SrjC2nA7TCm6gaOclh1cZ2IBupX1tnebn+sfvcvrezu+avS7k0BhhAC2pAtfhdOvD6G/2a+kkkm6hvpiw==}
+    engines: {node: '>= 12'}
+    dependencies:
+      '@purge-icons/core': 0.7.0
+      '@purge-icons/generated': 0.7.0
+    transitivePeerDependencies:
+      - encoding
+      - supports-color
+    dev: false
+
   /rollup-plugin-terser@7.0.2(rollup@2.79.1):
     resolution: {integrity: sha512-w3iIaU4OxcF52UUXiZNsNeuXIMDvFrr+ZXK6bFZ0Q60qyVfq4uLptoS4bbq3paG3x216eQllFZX7zt6TIImguQ==}
     deprecated: This package has been deprecated and is no longer maintained. Please use @rollup/plugin-terser
@@ -11262,7 +11294,6 @@ packages:
     hasBin: true
     optionalDependencies:
       fsevents: 2.3.2
-    dev: true
 
   /run-applescript@5.0.0:
     resolution: {integrity: sha512-XcT5rBksx1QdIhlFOCtgZkB99ZEouFZ1E2Kc2LHqNW13U3/74YGdkQRmThTwxy4QIyookibDKYZOPqX//6BlAg==}
@@ -11275,7 +11306,6 @@ packages:
     resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==}
     dependencies:
       queue-microtask: 1.2.3
-    dev: true
 
   /rw@1.3.3:
     resolution: {integrity: sha512-PdhdWy89SiZogBLaw42zdeqtRJ//zFd2PgQavcICDUgJT5oW10QCRKbJ6bg4r0/UY2M6BWd5tkxuGFRvCkgfHQ==}
@@ -11329,7 +11359,6 @@ packages:
       chokidar: 3.5.3
       immutable: 4.3.0
       source-map-js: 1.0.2
-    dev: true
 
   /sax@1.2.4:
     resolution: {integrity: sha512-NqVDv9TpANUjFm0N8uM5GxL36UgKi9/atZw+x7YFnQ8ckwFGKrl4xX4yWtrey3UJm5nP1kUbnYgLopqWNSRhWw==}
@@ -12192,7 +12221,6 @@ packages:
     engines: {node: '>=8.0'}
     dependencies:
       is-number: 7.0.0
-    dev: true
 
   /to-regex@3.0.2:
     resolution: {integrity: sha512-FWtleNAtZ/Ki2qtqej2CXTOayOH9bHDQF+Q48VpWyDXjbYxA4Yz8iDB31zXOBUlOHHKidDbqGVrTUvQMPmBGBw==}
@@ -12221,6 +12249,10 @@ packages:
     engines: {node: '>=6'}
     dev: true
 
+  /tr46@0.0.3:
+    resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==}
+    dev: false
+
   /tr46@1.0.1:
     resolution: {integrity: sha512-dTpowEjclQ7Kgx5SdBkqRzVhERQXov8/l9Ft9dVM9fmg0W0KQSVaXX9T4i6twCPNtYiZM53lpSSUAwJbFPOHxA==}
     dependencies:
@@ -12507,7 +12539,6 @@ packages:
   /universalify@2.0.0:
     resolution: {integrity: sha512-hAZsKq7Yy11Zu1DE0OzWjw7nnLZmJZYTDZZyEFHZdUhV8FkH5MCfoU1XMaxXovpyW5nq5scPqq0ZDP9Zyl04oQ==}
     engines: {node: '>= 10.0.0'}
-    dev: true
 
   /unocss@0.54.1(postcss@8.4.23)(rollup@2.79.1)(vite@4.3.8):
     resolution: {integrity: sha512-tT2hkDzjf2KV/neYKG/nVYxlpmgn36PGfrT3rE2zk+gaEMU+bU42CisiSkRQ7c2w4d/+zLeCmLz+dj71D8LhFA==}
@@ -12816,6 +12847,21 @@ packages:
       vite: 4.3.8(@types/node@20.2.1)(sass@1.62.1)
     dev: true
 
+  /vite-plugin-purge-icons@0.7.0(vite@4.3.8):
+    resolution: {integrity: sha512-oGZUKFAL4waIZIeiCPT5KZvGbBA500AO/03oxW+ODTKUMq+0jbh9s+T8NPzfJQFC1jtE7eUb2ium82IP/gxZjA==}
+    engines: {node: '>= 12'}
+    peerDependencies:
+      vite: ^2.0.0-beta.3
+    dependencies:
+      '@purge-icons/core': 0.7.0
+      '@purge-icons/generated': 0.7.0
+      rollup-plugin-purge-icons: 0.7.0
+      vite: 4.3.8(@types/node@20.2.1)(sass@1.62.1)
+    transitivePeerDependencies:
+      - encoding
+      - supports-color
+    dev: false
+
   /vite-plugin-pwa@0.15.0(vite@4.3.8)(workbox-build@6.5.4)(workbox-window@6.5.4):
     resolution: {integrity: sha512-gpmx3BeubsRIXRBkjPToOTJbo8fknNmZFQs24i0TPZyaNVa0n27YHDo0Y72amnO70WvHKGE3e1fn8SYUP7e8SA==}
     peerDependencies:
@@ -12883,7 +12929,6 @@ packages:
       sass: 1.62.1
     optionalDependencies:
       fsevents: 2.3.2
-    dev: true
 
   /vm2@3.9.17:
     resolution: {integrity: sha512-AqwtCnZ/ERcX+AVj9vUsphY56YANXxRuqMb7GsDtAr0m0PcQX3u0Aj3KWiXM0YAHy7i6JEeHrwOnwXbGYgRpAw==}
@@ -13126,6 +13171,10 @@ packages:
     engines: {node: '>= 8'}
     dev: false
 
+  /webidl-conversions@3.0.1:
+    resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==}
+    dev: false
+
   /webidl-conversions@4.0.2:
     resolution: {integrity: sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==}
     dev: true
@@ -13139,6 +13188,13 @@ packages:
     resolution: {integrity: sha512-kyDivFZ7ZM0BVOUteVbDFhlRt7Ah/CSPwJdi8hBpkK7QLumUqdLtVfm/PX/hkcnrvr0i77fO5+TjZ94Pe+C9iw==}
     dev: true
 
+  /whatwg-url@5.0.0:
+    resolution: {integrity: sha512-saE57nupxk6v3HY35+jzBwYa0rKSy0XR8JSxZPwgLr7ys0IBzhGviA1/TUGJLmSVqs8pb9AnvICXEuOHLprYTw==}
+    dependencies:
+      tr46: 0.0.3
+      webidl-conversions: 3.0.1
+    dev: false
+
   /whatwg-url@7.1.0:
     resolution: {integrity: sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg==}
     dependencies:

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

@@ -82,7 +82,8 @@ const locale: LocaleMessages<I18nType.Schema> = {
       lesson: {
         _value: 'Lesson Management',
         group: 'class',
-        classroom: 'classroom'
+        classroom: 'classroom',
+        schedule: 'schedule'
       },
       about: 'About'
     }

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

@@ -82,7 +82,8 @@ const locale: LocaleMessages<I18nType.Schema> = {
       lesson: {
         _value: '课程管理',
         group: '班级管理',
-        classroom: '教室管理'
+        classroom: '教室管理',
+        schedule: '排课管理'
       },
       about: '关于'
     }

+ 1 - 0
src/plugins/assets.ts

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

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

@@ -14,6 +14,17 @@ const management: AuthRoute.Route = {
         localIcon: 'class'
       }
     },
+    {
+      name: 'lesson_schedule',
+      path: '/lesson/schedule',
+      component: 'self',
+      meta: {
+        title: '排课管理',
+        i18nTitle: 'message.routes.lesson.schedule',
+        requiresAuth: true,
+        icon: 'healthicons:i-schedule-school-date-time'
+      }
+    },
     {
       name: 'lesson_classroom',
       path: '/lesson/classroom',

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

@@ -55,9 +55,9 @@ declare namespace PageRoute {
     | 'lesson'
     | 'lesson_classroom'
     | 'lesson_group'
+    | 'lesson_schedule'
     | 'management'
     | 'management_auth'
-    | 'management_group'
     | 'management_role'
     | 'management_route'
     | 'management_sort'
@@ -116,8 +116,9 @@ declare namespace PageRoute {
     | 'function_tab-detail'
     | 'function_tab-multi-detail'
     | 'function_tab'
-    | 'lesson_group'
     | 'lesson_classroom'
+    | 'lesson_group'
+    | 'lesson_schedule'
     | 'management_auth'
     | 'management_role'
     | 'management_route'

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

@@ -389,6 +389,7 @@ declare namespace I18nType {
         _value: string;
         group: string;
         classroom: string;
+        schedule: string;
       };
       about: string;
     };

+ 3 - 2
src/views/index.ts

@@ -32,9 +32,10 @@ export const views: Record<
   'function_tab-detail': () => import('./function/tab-detail/index.vue'),
   'function_tab-multi-detail': () => import('./function/tab-multi-detail/index.vue'),
   function_tab: () => import('./function/tab/index.vue'),
-  management_auth: () => import('./management/auth/index.vue'),
-  lesson_group: () => import('./lesson/group/index.vue'),
   lesson_classroom: () => import('./lesson/classroom/index.vue'),
+  lesson_group: () => import('./lesson/group/index.vue'),
+  lesson_schedule: () => import('./lesson/schedule/index.vue'),
+  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'),

+ 1 - 1
src/views/lesson/classroom/index.vue

@@ -1,5 +1,5 @@
 <template>
-  <div class="h-full">
+  <div class="h-full bg-white">
     <fs-crud ref="crudRef" v-bind="crudBinding" />
   </div>
 </template>

+ 56 - 8
src/views/lesson/group/api.ts

@@ -57,14 +57,6 @@ export function addRequest(params: SelectConditionParams): Promise<Service.Reque
   return request.post(`/class/addClass`, params);
 }
 
-export function queryUserById(uid: number): Promise<Service.RequestResult<QueryParams>> {
-  return request.get(`/userinfo/queryUserById/${uid}`);
-}
-
-export function queryByRealname(realname: string): Promise<Service.RequestResult<QueryParams>> {
-  return request.get(`/userinfo/queryUserByRealname/${realname}`);
-}
-
 export async function queryAll(): Promise<UserList> {
   const ret: UserList = {};
   const respone: Service.RequestResult = await request.get(`/userinfo/queryAll`);
@@ -74,3 +66,59 @@ export async function queryAll(): Promise<UserList> {
   });
   return ret;
 }
+
+/**
+ * 查询所有的班级类
+ * @param {string} classId
+ * @returns
+ */
+export function queryClassStudentById(classId: number): Promise<Service.RequestResult<ClassStudentParams[]>> {
+  return request.get(`/class/queryClassStudentById/${classId}`);
+}
+
+export interface ClassStudentParams {
+  studentId: number;
+}
+export interface StudentRes {
+  status: boolean;
+  msg: string;
+  data: Record<string, unknown>;
+  code: number;
+}
+
+export interface StudentParams {
+  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 labelTransfer {
+  label?: string;
+  value?: number;
+}
+
+export function getAll(): Promise<Service.RequestResult<StudentParams[]>> {
+  return request.get(`/student/getAll`);
+}
+
+export function submitGroupStudentForm(groupId: number, params?: number[]): Promise<Service.RequestResult<StudentRes>> {
+  return request.post(`/class/submitGroupStudentForm/${groupId}`, params);
+}

+ 30 - 14
src/views/lesson/group/crud.ts

@@ -1,4 +1,4 @@
-import type { CreateCrudOptionsRet } from '@fast-crud/fast-crud';
+import type { CreateCrudOptionsProps, CreateCrudOptionsRet } from '@fast-crud/fast-crud';
 import { dict } from '@fast-crud/fast-crud';
 import _ from 'lodash';
 import { isString } from '~/src/utils';
@@ -10,7 +10,8 @@ const dictOpt = {
   label: 'relname',
   value: 'id'
 };
-function curd(): CreateCrudOptionsRet {
+function curd({ context }: CreateCrudOptionsProps): CreateCrudOptionsRet {
+  const openModel = context?.openModel;
   return {
     crudOptions: {
       request: {
@@ -49,12 +50,12 @@ function curd(): CreateCrudOptionsRet {
         }
       },
       form: {
-        beforeSubmit: context => {
-          if (isString(context.form.createUid)) {
-            context.form.createUid = null;
+        beforeSubmit: localContext => {
+          if (isString(localContext.form.createUid)) {
+            localContext.form.createUid = null;
           }
-          if (isString(context.form.manageId)) {
-            context.form.manageId = null;
+          if (isString(localContext.form.manageId)) {
+            localContext.form.manageId = null;
           }
           return true;
         }
@@ -91,9 +92,9 @@ function curd(): CreateCrudOptionsRet {
         createUid: {
           title: '创建用户',
           type: 'text',
-          valueBuilder(context) {
-            if (userList[context.row.createUid]) {
-              context.row.createUid = userList[context.row.createUid];
+          valueBuilder(localContext) {
+            if (userList[localContext.row.createUid]) {
+              localContext.row.createUid = userList[localContext.row.createUid];
             }
           },
           form: {
@@ -113,9 +114,9 @@ function curd(): CreateCrudOptionsRet {
         manageId: {
           title: '负责人',
           type: 'text',
-          valueBuilder(context) {
-            if (userList[context.row.manageId]) {
-              context.row.manageId = userList[context.row.manageId];
+          valueBuilder(localContext) {
+            if (userList[localContext.row.manageId]) {
+              localContext.row.manageId = userList[localContext.row.manageId];
             }
           },
           form: {
@@ -176,13 +177,28 @@ function curd(): CreateCrudOptionsRet {
           studentManager: {
             text: null,
             size: 'small',
-            icon: 'ExpandOutlined',
+            icon: 'raphael:people',
             tooltip: {
               slots: {
                 default() {
                   return '管理学员';
                 }
               }
+            },
+            click: ({ row }) => {
+              openModel(row.id);
+            }
+          },
+          schedule: {
+            text: null,
+            size: 'small',
+            icon: 'uil:schedule',
+            tooltip: {
+              slots: {
+                default() {
+                  return '查看排课计划';
+                }
+              }
             }
           }
         }

+ 40 - 5
src/views/lesson/group/index.vue

@@ -1,15 +1,50 @@
 <template>
-  <div class="h-full">
-    <fs-crud ref="crudRef" v-bind="crudBinding" />
+  <div class="h-full bg-white">
+    <fs-crud v-if="!pageType" ref="crudRef" v-bind="crudBinding" />
+    <div v-if="pageType" class="wh-full flex-col-center">
+      <n-card style="width: 1200px" title="编辑班级学员" :bordered="false" size="huge" role="dialog" aria-modal="true">
+        <n-transfer ref="transfer" v-model:value="classStuList" :options="studentsAll" virtual-scroll />
+        <n-button-group style="margin-top: 1rem; float: right">
+          <n-button type="primary" @click="submitGroupStudent">提交</n-button>
+          <n-button type="primary" @click="pageType = !pageType">取消</n-button>
+        </n-button-group>
+      </n-card>
+    </div>
   </div>
 </template>
 
 <script setup lang="ts">
-import { onMounted } from 'vue';
+import { onMounted, ref } from 'vue';
+import type { Option, OptionValue } from 'naive-ui/es/transfer/src/interface';
 import { useFs } from '@fast-crud/fast-crud';
 import createCrudOptions from './crud';
-
-const { crudRef, crudBinding, crudExpose } = useFs({ createCrudOptions });
+import { queryClassStudentById, getAll, submitGroupStudentForm } from './api';
+const pageType = ref<boolean>(false);
+const classStuList = ref<number[] | undefined>([]);
+const studentsAll = ref<Option[]>();
+const groupId = ref<number>(0);
+async function submitGroupStudent() {
+  await submitGroupStudentForm(groupId.value, classStuList.value);
+  window.$message?.success('操作成功!');
+  pageType.value = !pageType.value;
+}
+const context: any = {
+  openModel: async (classId: number) => {
+    groupId.value = classId;
+    const classStudent = await queryClassStudentById(classId);
+    const studentAll = await getAll();
+    classStuList.value = classStudent.data?.map(r => r.studentId);
+    studentsAll.value = studentAll.data?.map(r => {
+      return {
+        label: r.studentName ? r.studentName + r.phone : '',
+        value: (r.id ?? 0) as OptionValue,
+        disabled: false
+      };
+    });
+    pageType.value = true;
+  }
+};
+const { crudRef, crudBinding, crudExpose } = useFs({ createCrudOptions, context });
 
 onMounted(() => {
   crudExpose.doRefresh();

+ 163 - 0
src/views/lesson/schedule/api.ts

@@ -0,0 +1,163 @@
+import { request } from '@/service/request';
+
+// 参数接口
+export interface ScheduleParams {
+  id?: number;
+  week?: number;
+  startTime?: number;
+  endTime?: number;
+  roomId?: number;
+  classId?: number;
+  assistantId?: number;
+  teacherId?: number;
+  categoryId?: number;
+  lessonTime?: number;
+  subjectsId?: number;
+  createTime?: number;
+  modifyTime?: number;
+  createUid?: number;
+  disabled?: string;
+}
+
+// 响应接口
+export interface scheduleRes {
+  status: boolean;
+  msg: string;
+  data: Record<string, unknown>;
+  code: number;
+}
+
+/**
+ * 添加课程表
+ * @param {string} repeatTime
+ * @param {object} params EasEduSchedule
+ * @param {number} params.id
+ * @param {number} params.week 当周周几
+ * @param {object} params.startTime 起始时间
+ * @param {object} params.endTime 结束时间
+ * @param {number} params.roomId 教室ID
+ * @param {number} params.classId 班级ID
+ * @param {number} params.assistantId 助教ID
+ * @param {number} params.teacherId 教师ID
+ * @param {number} params.categoryId 授课类别
+ * @param {number} params.subjectsId 授课内容
+ * @param {object} params.createTime 创建时间
+ * @param {object} params.modifyTime 修改时间
+ * @param {number} params.createUid 创建用户ID
+ * @param {string} params.disabled 状态
+ * @returns
+ */
+export function addSchedule(repeatTime: number, params: ScheduleParams): Promise<Service.RequestResult<scheduleRes>> {
+  return request.post(`/schedule/add?repeatTime=${repeatTime}`, params);
+}
+
+// 参数接口
+export interface QueryConflictParams {
+  startTime?: Record<string, unknown>;
+  endTime?: Record<string, unknown>;
+  roomId?: number;
+  teacherId?: number;
+}
+
+/**
+ * 查询排课是否冲突
+ * @param {object} params ConflictDto
+ * @param {object} params.startTime 课程开始时间
+ * @param {object} params.endTime 课程结束时间
+ * @param {number} params.roomId 教室ID
+ * @param {number} params.teacherId 教师ID
+ * @returns
+ */
+export function queryConflict(params: QueryConflictParams): Promise<Service.RequestResult<scheduleRes>> {
+  return request.put(`/schedule/queryConflict`, params);
+}
+
+export interface UserParams {
+  id?: number;
+  username?: string;
+  passwd?: string;
+  email?: string;
+  relname?: string;
+  phone?: string;
+  address?: string;
+  createTime?: Record<string, unknown>;
+  modifyTime?: Record<string, unknown>;
+  createUid?: number;
+  disabled?: string;
+}
+
+/**
+ * 查询用户信息根据用户名
+ * @param {string} realname
+ * @returns
+ */
+export function queryByRealname(realname: string): Promise<Service.RequestResult<UserParams[]>> {
+  return request.get(`/userinfo/queryUserByRealname/${realname}`);
+}
+
+export interface CategoryParams {
+  id?: number;
+  name?: string;
+  description?: string;
+  createTime?: Record<string, unknown>;
+  modifyTime?: Record<string, unknown>;
+  createUid?: number;
+  disabled?: string;
+}
+
+/**
+ * 根据条件进行查询课程类别
+ * @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 queryCategoryParams(
+  pageNum: number,
+  pageSize: number,
+  params: CategoryParams
+): Promise<Service.RequestResult<CategoryParams[]>> {
+  return request.post(`/category/query?pageNum=${pageNum}&pageSize=${pageSize}`, params);
+}
+
+// 参数接口
+export interface SubjectParams {
+  id?: number;
+  name?: string;
+  description?: string;
+  categoryId?: number;
+  createTime?: Record<string, unknown>;
+  modifyTime?: Record<string, unknown>;
+  createUid?: number;
+  disabled?: string;
+}
+
+/**
+ * 根据条件进行查询课程
+ * @param {string} pageNum
+ * @param {string} pageSize
+ * @param {object} params EasEduSubjects
+ * @param {number} params.id
+ * @param {string} params.name 学科名称
+ * @param {string} params.description 学科描述
+ * @param {number} params.categoryId 学科分类
+ * @param {object} params.createTime 创建时间
+ * @param {object} params.modifyTime 修改时间
+ * @param {number} params.createUid 创建用户ID
+ * @param {string} params.disabled 状态
+ * @returns
+ */
+export function querySubject(
+  pageNum: number,
+  pageSize: number,
+  params: SubjectParams
+): Promise<Service.RequestResult<SubjectParams[]>> {
+  return request.post(`/subject/selectByCondition?pageNum=${pageNum}&pageSize=${pageSize}`, params);
+}

+ 193 - 0
src/views/lesson/schedule/index.vue

@@ -0,0 +1,193 @@
+<template>
+  <div class="h-full bg-white">
+    <n-space>
+      <n-button type="primary" @click="pageState = !pageState"> 新增排课 </n-button>
+    </n-space>
+    <div v-if="pageState">
+      <table border="1" style="width: 100%">
+        <caption>
+          排课表
+        </caption>
+        <tbody width="100">
+          <tr>
+            <th>周一</th>
+            <th>周二</th>
+            <th>周三</th>
+            <th>周四</th>
+            <th>周五</th>
+            <th>周六</th>
+            <th>周日</th>
+          </tr>
+          <tr class="text-center">
+            <td>李四</td>
+            <td>员工</td>
+            <td>李四</td>
+            <td>李四</td>
+            <td>李四</td>
+            <td>李四</td>
+            <td>李四</td>
+          </tr>
+        </tbody>
+      </table>
+    </div>
+    <div v-if="!pageState" class="wh-full flex-col-center">
+      <n-form ref="formRef" size="large" label-placement="left" style="margin-right: 5rem" :model="model">
+        <n-form-item label="排课班级" path="inputValue">
+          <n-input-number v-model:value="model.classId" type="number" disabled placeholder="请选择排课的班级" />
+        </n-form-item>
+        <n-form-item label="任课老师" path="teacherId">
+          <n-select
+            v-model:value="model.teacherId"
+            placeholder="请选择任课老师"
+            :options="teacherOptions"
+            filterable
+            clearable
+            remote
+            @search="teacherSearch"
+          />
+        </n-form-item>
+        <n-form-item label="助教老师" path="multipleSelectValue">
+          <n-select
+            v-model:value="model.assistantId"
+            placeholder="请选择助教老师"
+            :options="generalOptions"
+            filterable
+            clearable
+            remote
+            @search="assistantSearch"
+          />
+        </n-form-item>
+        <n-form-item label="教授科目" path="multipleSelectValue">
+          <n-cascader
+            v-model:value="model.subjectsId"
+            placeholder="请选择课程"
+            :options="categoryAndSubjectOptions"
+            show-path
+            check-strategy="child"
+            remote
+            :on-load="categoryAndSubjectLoad"
+          />
+        </n-form-item>
+        <n-form-item label="上课时间" path="datetimeValue">
+          <n-space>
+            <n-date-picker v-model:value="dateValue" type="date" />
+            <n-time-picker v-model:value="model.startTime" />
+            <n-time-picker v-model:value="model.endTime" />
+          </n-space>
+        </n-form-item>
+        <n-form-item label="是否连选" path="switchValue">
+          <n-switch v-model:value="isRepeatTime" />
+        </n-form-item>
+        <n-form-item label="连选天数" path="sliderValue">
+          <n-slider v-model:value="repeatTime" :step="1" :max="7" />
+        </n-form-item>
+        <n-form-item label="单节课时长" path="inputNumberValue">
+          <n-input-number v-model:value="model.lessonTime" />
+        </n-form-item>
+        <div style="display: flex; justify-content: flex-end">
+          <n-button type="primary" @click="handleValidateButtonClick"> 确认提交 </n-button>
+        </div>
+      </n-form>
+    </div>
+  </div>
+</template>
+<script setup lang="ts">
+import { ref, reactive } from 'vue';
+import type { CascaderOption } from 'naive-ui';
+import { queryByRealname, queryCategoryParams, querySubject } from './api';
+import type { ScheduleParams } from './api';
+const model = reactive<ScheduleParams>({
+  week: 0,
+  startTime: 0,
+  endTime: 0,
+  roomId: 0,
+  classId: 0,
+  assistantId: 0,
+  teacherId: 0,
+  categoryId: 0,
+  subjectsId: 0,
+  createTime: 0,
+  modifyTime: 0,
+  createUid: 0,
+  disabled: 'N'
+});
+
+const dateValue = ref<number>();
+const isRepeatTime = ref<boolean>(true);
+const repeatTime = ref<number>(1);
+const pageState = ref<boolean>(true);
+const categoryAndSubjectOptions = ref<any[]>([]);
+
+const teacherOptions = ref();
+function teacherSearch(keyword: string) {
+  if (keyword.length <= 0 || !keyword.length) {
+    teacherOptions.value = [];
+    return;
+  }
+  queryByRealname(keyword).then(respone => {
+    teacherOptions.value = respone.data?.map(r => {
+      return {
+        label: r.relname,
+        value: r.id
+      };
+    });
+  });
+}
+
+const generalOptions = ref();
+function assistantSearch(keyword: string) {
+  if (keyword.length <= 0 || !keyword.length) {
+    generalOptions.value = [];
+    return;
+  }
+  queryByRealname(keyword).then(respone => {
+    generalOptions.value = respone.data?.map(r => {
+      return {
+        label: r.relname,
+        value: r.id
+      };
+    });
+  });
+}
+
+function categoryAndSubjectLoad(option: CascaderOption) {
+  model.categoryId = option.value as number;
+  return new Promise<void>(resolve => {
+    option.children = [];
+    querySubject(1, 100, {
+      categoryId: option.value as number
+    }).then(respone => {
+      respone.data?.map(r => {
+        option.children?.push({
+          label: r.name,
+          value: r.id,
+          depth: 2,
+          isLeaf: true
+        });
+        return r;
+      });
+      resolve();
+    });
+  });
+}
+
+function handleValidateButtonClick() {
+  // console.log(model);
+}
+
+function loadData() {
+  queryCategoryParams(1, 100, {}).then(respone => {
+    respone.data?.map(r => {
+      categoryAndSubjectOptions.value.push({
+        label: r.name,
+        value: r.id,
+        depth: 1,
+        isLeaf: false
+      });
+      return r;
+    });
+  });
+}
+loadData();
+</script>
+<style scoped></style>

+ 1 - 1
src/views/management/student/index.vue

@@ -1,5 +1,5 @@
 <template>
-  <div class="h-full">
+  <div class="h-full bg-white">
     <fs-crud ref="crudRef" v-bind="crudBinding" />
     <n-modal v-model:show="showModal" preset="dialog" title="Dialog">
       <template #header>