Kaynağa Gözat

完成卡管理前端编写

month-red-love 1 ay önce
ebeveyn
işleme
238299b0e6
98 değiştirilmiş dosya ile 5924 ekleme ve 1280 silme
  1. BIN
      back-ui/admin.zip
  2. 565 70
      back-ui/package-lock.json
  3. 3 1
      back-ui/package.json
  4. 392 12
      back-ui/pnpm-lock.yaml
  5. 45 19
      back-ui/src/api/dz/cards.js
  6. 8 0
      back-ui/src/api/dz/classes.js
  7. 58 0
      back-ui/src/api/dz/papers.js
  8. 9 0
      back-ui/src/api/dz/school.js
  9. 17 0
      back-ui/src/api/dz/teacherclass.js
  10. 15 0
      back-ui/src/assets/icons/svg/share.svg
  11. 15 0
      back-ui/src/assets/styles/tailwind.css
  12. 41 15
      back-ui/src/components/AddressSelect/index.vue
  13. 25 5
      back-ui/src/components/Form/index.vue
  14. 37 0
      back-ui/src/components/SearchForm/index.vue
  15. 357 0
      back-ui/src/components/Table/index.vue
  16. 1 9
      back-ui/src/layout/components/Navbar.vue
  17. 1 0
      back-ui/src/main.js
  18. 36 7
      back-ui/src/views/dz/agent/index.vue
  19. 157 0
      back-ui/src/views/dz/cards/components/ApplyCardDialog.vue
  20. 237 0
      back-ui/src/views/dz/cards/components/AssignCardDialog.vue
  21. 166 0
      back-ui/src/views/dz/cards/components/AssociateCampusDialog.vue
  22. 225 0
      back-ui/src/views/dz/cards/components/CardGenerationDialog.vue
  23. 91 0
      back-ui/src/views/dz/cards/components/CloseCardDialog.vue
  24. 201 0
      back-ui/src/views/dz/cards/components/EditStudentDialog.vue
  25. 91 0
      back-ui/src/views/dz/cards/components/PaymentDialog.vue
  26. 91 0
      back-ui/src/views/dz/cards/components/RefundDialog.vue
  27. 91 0
      back-ui/src/views/dz/cards/components/ReopenCardDialog.vue
  28. 222 136
      back-ui/src/views/dz/cards/config/form.js
  29. 187 0
      back-ui/src/views/dz/cards/config/table.js
  30. 578 404
      back-ui/src/views/dz/cards/index.vue
  31. 0 1
      back-ui/src/views/dz/classes/index.vue
  32. 19 0
      back-ui/src/views/dz/papers/components/paper-by-hand.vue
  33. 19 0
      back-ui/src/views/dz/papers/components/paper-by-intelligent.vue
  34. 47 0
      back-ui/src/views/dz/papers/components/paper-exact-conditions.vue
  35. 18 0
      back-ui/src/views/dz/papers/components/paper-full-conditions.vue
  36. 21 0
      back-ui/src/views/dz/papers/components/paper-hand-exact.vue
  37. 21 0
      back-ui/src/views/dz/papers/components/paper-hand-full.vue
  38. 13 0
      back-ui/src/views/dz/papers/components/paper-intelligent-exact.vue
  39. 13 0
      back-ui/src/views/dz/papers/components/paper-intelligent-form.vue
  40. 13 0
      back-ui/src/views/dz/papers/components/paper-intelligent-full.vue
  41. 41 0
      back-ui/src/views/dz/papers/components/paper-knowledge-tree.vue
  42. 10 0
      back-ui/src/views/dz/papers/detail.vue
  43. 92 0
      back-ui/src/views/dz/papers/hooks/usePaperExactCondition.js
  44. 39 0
      back-ui/src/views/dz/papers/hooks/usePaperFullCondition.js
  45. 16 2
      back-ui/src/views/dz/papers/index.vue
  46. 5 4
      back-ui/src/views/dz/school/index.vue
  47. 118 1
      back-ui/src/views/dz/teacher/index.vue
  48. 8 8
      back-ui/src/views/dz/teacherclass/index.vue
  49. 11 1
      back-ui/src/views/system/user/index.vue
  50. 9 0
      back-ui/tailwind.config.cjs
  51. 2 1
      back-ui/vite.config.js
  52. 208 359
      back-ui/yarn.lock
  53. 50 2
      ie-admin/src/main/java/com/ruoyi/web/controller/dz/DzAgentController.java
  54. 24 0
      ie-admin/src/main/java/com/ruoyi/web/controller/dz/DzClassesController.java
  55. 38 1
      ie-admin/src/main/java/com/ruoyi/web/controller/dz/DzSchoolController.java
  56. 70 3
      ie-admin/src/main/java/com/ruoyi/web/controller/dz/DzTeacherClassController.java
  57. 5 1
      ie-admin/src/main/java/com/ruoyi/web/controller/dz/DzTeacherController.java
  58. 79 0
      ie-admin/src/main/java/com/ruoyi/web/controller/front/FrontCustomerMarjorsController.java
  59. 103 0
      ie-admin/src/main/java/com/ruoyi/web/controller/front/FrontCustomerUniversityController.java
  60. 3 1
      ie-admin/src/main/java/com/ruoyi/web/controller/learn/LearnQuestionsController.java
  61. 28 5
      ie-admin/src/main/java/com/ruoyi/web/controller/learn/LearnTeacherController.java
  62. 4 4
      ie-admin/src/main/java/com/ruoyi/web/service/ExamService.java
  63. 266 2
      ie-admin/src/main/java/com/ruoyi/web/service/LearnTeacherService.java
  64. 9 28
      ie-admin/src/main/java/com/ruoyi/web/service/PaperService.java
  65. 2 0
      ie-admin/src/main/java/com/ruoyi/web/service/SysRegisterService.java
  66. 65 0
      ie-common/src/main/java/com/ruoyi/common/enums/BoolValues.java
  67. 24 0
      ie-system/src/main/java/com/ruoyi/dz/domain/DzAgent.java
  68. 18 0
      ie-system/src/main/java/com/ruoyi/dz/domain/DzClasses.java
  69. 12 1
      ie-system/src/main/java/com/ruoyi/dz/domain/DzSchool.java
  70. 2 1
      ie-system/src/main/java/com/ruoyi/dz/domain/DzTeacher.java
  71. 27 17
      ie-system/src/main/java/com/ruoyi/dz/domain/DzTeacherClass.java
  72. 9 9
      ie-system/src/main/java/com/ruoyi/dz/mapper/DzSchoolMapper.java
  73. 9 9
      ie-system/src/main/java/com/ruoyi/dz/mapper/DzTeacherClassMapper.java
  74. 9 9
      ie-system/src/main/java/com/ruoyi/dz/service/IDzSchoolService.java
  75. 9 8
      ie-system/src/main/java/com/ruoyi/dz/service/IDzTeacherClassService.java
  76. 14 8
      ie-system/src/main/java/com/ruoyi/dz/service/impl/DzSchoolServiceImpl.java
  77. 13 8
      ie-system/src/main/java/com/ruoyi/dz/service/impl/DzTeacherClassServiceImpl.java
  78. 1 0
      ie-system/src/main/java/com/ruoyi/ie/mapper/AMarjorPlanMapper.java
  79. 12 1
      ie-system/src/main/java/com/ruoyi/learn/domain/LearnDirectedKnowledge.java
  80. 22 0
      ie-system/src/main/java/com/ruoyi/learn/domain/LearnPaperQuestion.java
  81. 23 13
      ie-system/src/main/java/com/ruoyi/learn/domain/LearnTestPaper.java
  82. 70 14
      ie-system/src/main/java/com/ruoyi/learn/domain/TestPaperVO.java
  83. 2 0
      ie-system/src/main/java/com/ruoyi/learn/mapper/LearnDirectedKnowledgeMapper.java
  84. 4 0
      ie-system/src/main/java/com/ruoyi/learn/mapper/LearnStudentMapper.java
  85. 4 0
      ie-system/src/main/java/com/ruoyi/learn/mapper/LearnTestPaperMapper.java
  86. 9 8
      ie-system/src/main/java/com/ruoyi/system/mapper/SysAreaMapper.java
  87. 1 0
      ie-system/src/main/java/com/ruoyi/system/service/ISysAreaService.java
  88. 5 0
      ie-system/src/main/java/com/ruoyi/system/service/impl/SysAreaServiceImpl.java
  89. 22 19
      ie-system/src/main/resources/mapper/dz/DzAgentMapper.xml
  90. 14 1
      ie-system/src/main/resources/mapper/dz/DzSchoolMapper.xml
  91. 31 11
      ie-system/src/main/resources/mapper/dz/DzTeacherClassMapper.xml
  92. 5 0
      ie-system/src/main/resources/mapper/ie/AMarjorPlanMapper.xml
  93. 11 1
      ie-system/src/main/resources/mapper/learn/LearnDirectedKnowledgeMapper.xml
  94. 19 1
      ie-system/src/main/resources/mapper/learn/LearnStudentMapper.xml
  95. 20 10
      ie-system/src/main/resources/mapper/learn/LearnTestPaperMapper.xml
  96. 13 0
      ie-system/src/main/resources/mapper/learn/LearnTestStudentMapper.xml
  97. 10 0
      ie-system/src/main/resources/mapper/system/SysAreaMapper.xml
  98. 29 29
      sql/v_0.2_base.sql

BIN
back-ui/admin.zip


Dosya farkı çok büyük olduğundan ihmal edildi
+ 565 - 70
back-ui/package-lock.json


+ 3 - 1
back-ui/package.json

@@ -17,8 +17,9 @@
   },
   "dependencies": {
     "@element-plus/icons-vue": "2.3.1",
+    "@tailwindcss/vite": "^4.1.14",
     "@vueup/vue-quill": "1.2.0",
-    "@vueuse/core": "13.3.0",
+    "@vueuse/core": "^13.3.0",
     "axios": "1.9.0",
     "clipboard": "2.0.11",
     "echarts": "5.6.0",
@@ -32,6 +33,7 @@
     "pinia": "3.0.2",
     "sortablejs": "^1.15.6",
     "splitpanes": "4.0.4",
+    "tailwindcss": "^4.1.13",
     "vue": "3.5.16",
     "vue-cropper": "1.1.1",
     "vue-router": "4.5.1",

+ 392 - 12
back-ui/pnpm-lock.yaml

@@ -11,11 +11,14 @@ importers:
       '@element-plus/icons-vue':
         specifier: 2.3.1
         version: 2.3.1(vue@3.5.16)
+      '@tailwindcss/vite':
+        specifier: ^4.1.14
+        version: 4.1.14(vite@6.3.5(@types/node@24.6.1)(jiti@2.6.1)(lightningcss@1.30.1)(sass-embedded@1.89.1))
       '@vueup/vue-quill':
         specifier: 1.2.0
         version: 1.2.0(vue@3.5.16)
       '@vueuse/core':
-        specifier: 13.3.0
+        specifier: ^13.3.0
         version: 13.3.0(vue@3.5.16)
       axios:
         specifier: 1.9.0
@@ -56,6 +59,9 @@ importers:
       splitpanes:
         specifier: 4.0.4
         version: 4.0.4(vue@3.5.16)
+      tailwindcss:
+        specifier: ^4.1.13
+        version: 4.1.14
       vue:
         specifier: 3.5.16
         version: 3.5.16
@@ -71,7 +77,7 @@ importers:
     devDependencies:
       '@vitejs/plugin-vue':
         specifier: 5.2.4
-        version: 5.2.4(vite@6.3.5(@types/node@24.6.1)(sass-embedded@1.89.1))(vue@3.5.16)
+        version: 5.2.4(vite@6.3.5(@types/node@24.6.1)(jiti@2.6.1)(lightningcss@1.30.1)(sass-embedded@1.89.1))(vue@3.5.16)
       sass-embedded:
         specifier: 1.89.1
         version: 1.89.1
@@ -83,13 +89,13 @@ importers:
         version: 1.0.1
       vite:
         specifier: 6.3.5
-        version: 6.3.5(@types/node@24.6.1)(sass-embedded@1.89.1)
+        version: 6.3.5(@types/node@24.6.1)(jiti@2.6.1)(lightningcss@1.30.1)(sass-embedded@1.89.1)
       vite-plugin-compression:
         specifier: 0.5.1
-        version: 0.5.1(vite@6.3.5(@types/node@24.6.1)(sass-embedded@1.89.1))
+        version: 0.5.1(vite@6.3.5(@types/node@24.6.1)(jiti@2.6.1)(lightningcss@1.30.1)(sass-embedded@1.89.1))
       vite-plugin-svg-icons:
         specifier: 2.0.1
-        version: 2.0.1(vite@6.3.5(@types/node@24.6.1)(sass-embedded@1.89.1))
+        version: 2.0.1(vite@6.3.5(@types/node@24.6.1)(jiti@2.6.1)(lightningcss@1.30.1)(sass-embedded@1.89.1))
 
 packages:
 
@@ -294,9 +300,26 @@ packages:
     resolution: {integrity: sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==}
     engines: {node: '>=12'}
 
+  '@isaacs/fs-minipass@4.0.1':
+    resolution: {integrity: sha512-wgm9Ehl2jpeqP3zw/7mo3kRHFp5MEDhqAdwy1fTGkHAwnkGOVsgpvQhL8B5n1qlb01jV3n/bI0ZfZp5lWA1k4w==}
+    engines: {node: '>=18.0.0'}
+
+  '@jridgewell/gen-mapping@0.3.13':
+    resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==}
+
+  '@jridgewell/remapping@2.3.5':
+    resolution: {integrity: sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==}
+
+  '@jridgewell/resolve-uri@3.1.2':
+    resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==}
+    engines: {node: '>=6.0.0'}
+
   '@jridgewell/sourcemap-codec@1.5.5':
     resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==}
 
+  '@jridgewell/trace-mapping@0.3.31':
+    resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==}
+
   '@nodelib/fs.scandir@2.1.5':
     resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==}
     engines: {node: '>= 8'}
@@ -438,6 +461,96 @@ packages:
   '@sxzz/popperjs-es@2.11.7':
     resolution: {integrity: sha512-Ccy0NlLkzr0Ex2FKvh2X+OyERHXJ88XJ1MXtsI9y9fGexlaXaVTPzBCRBwIxFkORuOb+uBqeu+RqnpgYTEZRUQ==}
 
+  '@tailwindcss/node@4.1.14':
+    resolution: {integrity: sha512-hpz+8vFk3Ic2xssIA3e01R6jkmsAhvkQdXlEbRTk6S10xDAtiQiM3FyvZVGsucefq764euO/b8WUW9ysLdThHw==}
+
+  '@tailwindcss/oxide-android-arm64@4.1.14':
+    resolution: {integrity: sha512-a94ifZrGwMvbdeAxWoSuGcIl6/DOP5cdxagid7xJv6bwFp3oebp7y2ImYsnZBMTwjn5Ev5xESvS3FFYUGgPODQ==}
+    engines: {node: '>= 10'}
+    cpu: [arm64]
+    os: [android]
+
+  '@tailwindcss/oxide-darwin-arm64@4.1.14':
+    resolution: {integrity: sha512-HkFP/CqfSh09xCnrPJA7jud7hij5ahKyWomrC3oiO2U9i0UjP17o9pJbxUN0IJ471GTQQmzwhp0DEcpbp4MZTA==}
+    engines: {node: '>= 10'}
+    cpu: [arm64]
+    os: [darwin]
+
+  '@tailwindcss/oxide-darwin-x64@4.1.14':
+    resolution: {integrity: sha512-eVNaWmCgdLf5iv6Qd3s7JI5SEFBFRtfm6W0mphJYXgvnDEAZ5sZzqmI06bK6xo0IErDHdTA5/t7d4eTfWbWOFw==}
+    engines: {node: '>= 10'}
+    cpu: [x64]
+    os: [darwin]
+
+  '@tailwindcss/oxide-freebsd-x64@4.1.14':
+    resolution: {integrity: sha512-QWLoRXNikEuqtNb0dhQN6wsSVVjX6dmUFzuuiL09ZeXju25dsei2uIPl71y2Ic6QbNBsB4scwBoFnlBfabHkEw==}
+    engines: {node: '>= 10'}
+    cpu: [x64]
+    os: [freebsd]
+
+  '@tailwindcss/oxide-linux-arm-gnueabihf@4.1.14':
+    resolution: {integrity: sha512-VB4gjQni9+F0VCASU+L8zSIyjrLLsy03sjcR3bM0V2g4SNamo0FakZFKyUQ96ZVwGK4CaJsc9zd/obQy74o0Fw==}
+    engines: {node: '>= 10'}
+    cpu: [arm]
+    os: [linux]
+
+  '@tailwindcss/oxide-linux-arm64-gnu@4.1.14':
+    resolution: {integrity: sha512-qaEy0dIZ6d9vyLnmeg24yzA8XuEAD9WjpM5nIM1sUgQ/Zv7cVkharPDQcmm/t/TvXoKo/0knI3me3AGfdx6w1w==}
+    engines: {node: '>= 10'}
+    cpu: [arm64]
+    os: [linux]
+
+  '@tailwindcss/oxide-linux-arm64-musl@4.1.14':
+    resolution: {integrity: sha512-ISZjT44s59O8xKsPEIesiIydMG/sCXoMBCqsphDm/WcbnuWLxxb+GcvSIIA5NjUw6F8Tex7s5/LM2yDy8RqYBQ==}
+    engines: {node: '>= 10'}
+    cpu: [arm64]
+    os: [linux]
+
+  '@tailwindcss/oxide-linux-x64-gnu@4.1.14':
+    resolution: {integrity: sha512-02c6JhLPJj10L2caH4U0zF8Hji4dOeahmuMl23stk0MU1wfd1OraE7rOloidSF8W5JTHkFdVo/O7uRUJJnUAJg==}
+    engines: {node: '>= 10'}
+    cpu: [x64]
+    os: [linux]
+
+  '@tailwindcss/oxide-linux-x64-musl@4.1.14':
+    resolution: {integrity: sha512-TNGeLiN1XS66kQhxHG/7wMeQDOoL0S33x9BgmydbrWAb9Qw0KYdd8o1ifx4HOGDWhVmJ+Ul+JQ7lyknQFilO3Q==}
+    engines: {node: '>= 10'}
+    cpu: [x64]
+    os: [linux]
+
+  '@tailwindcss/oxide-wasm32-wasi@4.1.14':
+    resolution: {integrity: sha512-uZYAsaW/jS/IYkd6EWPJKW/NlPNSkWkBlaeVBi/WsFQNP05/bzkebUL8FH1pdsqx4f2fH/bWFcUABOM9nfiJkQ==}
+    engines: {node: '>=14.0.0'}
+    cpu: [wasm32]
+    bundledDependencies:
+      - '@napi-rs/wasm-runtime'
+      - '@emnapi/core'
+      - '@emnapi/runtime'
+      - '@tybys/wasm-util'
+      - '@emnapi/wasi-threads'
+      - tslib
+
+  '@tailwindcss/oxide-win32-arm64-msvc@4.1.14':
+    resolution: {integrity: sha512-Az0RnnkcvRqsuoLH2Z4n3JfAef0wElgzHD5Aky/e+0tBUxUhIeIqFBTMNQvmMRSP15fWwmvjBxZ3Q8RhsDnxAA==}
+    engines: {node: '>= 10'}
+    cpu: [arm64]
+    os: [win32]
+
+  '@tailwindcss/oxide-win32-x64-msvc@4.1.14':
+    resolution: {integrity: sha512-ttblVGHgf68kEE4om1n/n44I0yGPkCPbLsqzjvybhpwa6mKKtgFfAzy6btc3HRmuW7nHe0OOrSeNP9sQmmH9XA==}
+    engines: {node: '>= 10'}
+    cpu: [x64]
+    os: [win32]
+
+  '@tailwindcss/oxide@4.1.14':
+    resolution: {integrity: sha512-23yx+VUbBwCg2x5XWdB8+1lkPajzLmALEfMb51zZUBYaYVPDQvBSD/WYDqiVyBIo2BZFa3yw1Rpy3G2Jp+K0dw==}
+    engines: {node: '>= 10'}
+
+  '@tailwindcss/vite@4.1.14':
+    resolution: {integrity: sha512-BoFUoU0XqgCUS1UXWhmDJroKKhNXeDzD7/XwabjkDIAbMnc4ULn5e2FuEuBbhZ6ENZoSYzKlzvZ44Yr6EUDUSA==}
+    peerDependencies:
+      vite: ^5.2.0 || ^6 || ^7
+
   '@trysound/sax@0.2.0':
     resolution: {integrity: sha512-L7z9BgrNEcYyUYtF+HaEfiS5ebkh9jXqbszz7pC0hRBPaatV0XjSD3+eHrpqFemQfgwiFF0QPIarnIihIDn7OA==}
     engines: {node: '>=10.13.0'}
@@ -693,6 +806,10 @@ packages:
     resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==}
     engines: {node: '>=10'}
 
+  chownr@3.0.0:
+    resolution: {integrity: sha512-+IxzY9BZOQd/XuYPRmrvEVjF/nqj5kgT4kEq7VofrDoM1MxoRjEWkrCC3EtLi59TVawxTAn+orJwFQcrqEN1+g==}
+    engines: {node: '>=18'}
+
   class-utils@0.3.6:
     resolution: {integrity: sha512-qOhPa/Fj7s6TY8H8esGu5QNpMMQxz79h+urzrNYN6mn+9BnxlDGf5QZ+XeCDsxSjPqsSR56XOZOJmpeurnLMeg==}
     engines: {node: '>=0.10.0'}
@@ -843,6 +960,10 @@ packages:
   delegate@3.2.0:
     resolution: {integrity: sha512-IofjkYBZaZivn0V8nnsMJGBr4jVLxHDheKSW88PyxS5QC4Vo9ZbZVvhzlSxY87fVq3STR6r+4cGepyHkcWOQSw==}
 
+  detect-libc@2.1.1:
+    resolution: {integrity: sha512-ecqj/sy1jcK1uWrwpR67UhYrIFQ+5WlGxth34WquCbamhFA6hkkwiu37o6J5xCHdo1oixJRfVRw+ywV+Hq/0Aw==}
+    engines: {node: '>=8'}
+
   dom-serializer@0.2.2:
     resolution: {integrity: sha512-2/xPb3ORsQ42nHYiSunXkDjPLBaEj/xTwUO4B7XCZQTRk7EBtTOPaygh10YAAh2OI1Qrp6NWfpAhzswj0ydt9g==}
 
@@ -898,6 +1019,10 @@ packages:
     resolution: {integrity: sha512-/kyM18EfinwXZbno9FyUGeFh87KC8HRQBQGildHZbEuRyWFOmv1U10o9BBp8XVZDVNNuQKyIGIu5ZYAAXJ0V2Q==}
     engines: {node: '>= 4'}
 
+  enhanced-resolve@5.18.3:
+    resolution: {integrity: sha512-d4lC8xfavMeBjzGr2vECC3fsGXziXZQyJxD868h2M/mBI3PwAuODxAkLkq5HYuvrPYcUtiLzsTo8U3PgX3Ocww==}
+    engines: {node: '>=10.13.0'}
+
   entities@1.1.2:
     resolution: {integrity: sha512-f2LZMYl1Fzu7YSBKg+RoROelpOaNrcGmE9AZubeDfrCEia483oW4MI4VyFd5VNHIgQ/7qm1I0wUHK1eJnn2y2w==}
 
@@ -1360,6 +1485,10 @@ packages:
   jackspeak@3.4.3:
     resolution: {integrity: sha512-OGlZQpz2yfahA/Rd1Y8Cd9SIEsqvXkLVoSw/cgwhnhFMDbsQFeZYoJJ7bIZBS9BcamUW96asq/npPWugM+RQBw==}
 
+  jiti@2.6.1:
+    resolution: {integrity: sha512-ekilCSN1jwRvIbgeg/57YFh8qQDNbwDb9xT/qu2DAHbFFZUicIl4ygVaAvzveMhMVr3LnpSKTNnwt8PoOfmKhQ==}
+    hasBin: true
+
   js-base64@2.6.4:
     resolution: {integrity: sha512-pZe//GGmwJndub7ZghVHz7vjb2LgC1m8B07Au3eYqeqv9emhESByMXxaEgkUkEqJe87oBbSniGYoQNIBklc7IQ==}
 
@@ -1401,6 +1530,70 @@ packages:
     resolution: {integrity: sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==}
     engines: {node: '>=0.10.0'}
 
+  lightningcss-darwin-arm64@1.30.1:
+    resolution: {integrity: sha512-c8JK7hyE65X1MHMN+Viq9n11RRC7hgin3HhYKhrMyaXflk5GVplZ60IxyoVtzILeKr+xAJwg6zK6sjTBJ0FKYQ==}
+    engines: {node: '>= 12.0.0'}
+    cpu: [arm64]
+    os: [darwin]
+
+  lightningcss-darwin-x64@1.30.1:
+    resolution: {integrity: sha512-k1EvjakfumAQoTfcXUcHQZhSpLlkAuEkdMBsI/ivWw9hL+7FtilQc0Cy3hrx0AAQrVtQAbMI7YjCgYgvn37PzA==}
+    engines: {node: '>= 12.0.0'}
+    cpu: [x64]
+    os: [darwin]
+
+  lightningcss-freebsd-x64@1.30.1:
+    resolution: {integrity: sha512-kmW6UGCGg2PcyUE59K5r0kWfKPAVy4SltVeut+umLCFoJ53RdCUWxcRDzO1eTaxf/7Q2H7LTquFHPL5R+Gjyig==}
+    engines: {node: '>= 12.0.0'}
+    cpu: [x64]
+    os: [freebsd]
+
+  lightningcss-linux-arm-gnueabihf@1.30.1:
+    resolution: {integrity: sha512-MjxUShl1v8pit+6D/zSPq9S9dQ2NPFSQwGvxBCYaBYLPlCWuPh9/t1MRS8iUaR8i+a6w7aps+B4N0S1TYP/R+Q==}
+    engines: {node: '>= 12.0.0'}
+    cpu: [arm]
+    os: [linux]
+
+  lightningcss-linux-arm64-gnu@1.30.1:
+    resolution: {integrity: sha512-gB72maP8rmrKsnKYy8XUuXi/4OctJiuQjcuqWNlJQ6jZiWqtPvqFziskH3hnajfvKB27ynbVCucKSm2rkQp4Bw==}
+    engines: {node: '>= 12.0.0'}
+    cpu: [arm64]
+    os: [linux]
+
+  lightningcss-linux-arm64-musl@1.30.1:
+    resolution: {integrity: sha512-jmUQVx4331m6LIX+0wUhBbmMX7TCfjF5FoOH6SD1CttzuYlGNVpA7QnrmLxrsub43ClTINfGSYyHe2HWeLl5CQ==}
+    engines: {node: '>= 12.0.0'}
+    cpu: [arm64]
+    os: [linux]
+
+  lightningcss-linux-x64-gnu@1.30.1:
+    resolution: {integrity: sha512-piWx3z4wN8J8z3+O5kO74+yr6ze/dKmPnI7vLqfSqI8bccaTGY5xiSGVIJBDd5K5BHlvVLpUB3S2YCfelyJ1bw==}
+    engines: {node: '>= 12.0.0'}
+    cpu: [x64]
+    os: [linux]
+
+  lightningcss-linux-x64-musl@1.30.1:
+    resolution: {integrity: sha512-rRomAK7eIkL+tHY0YPxbc5Dra2gXlI63HL+v1Pdi1a3sC+tJTcFrHX+E86sulgAXeI7rSzDYhPSeHHjqFhqfeQ==}
+    engines: {node: '>= 12.0.0'}
+    cpu: [x64]
+    os: [linux]
+
+  lightningcss-win32-arm64-msvc@1.30.1:
+    resolution: {integrity: sha512-mSL4rqPi4iXq5YVqzSsJgMVFENoa4nGTT/GjO2c0Yl9OuQfPsIfncvLrEW6RbbB24WtZ3xP/2CCmI3tNkNV4oA==}
+    engines: {node: '>= 12.0.0'}
+    cpu: [arm64]
+    os: [win32]
+
+  lightningcss-win32-x64-msvc@1.30.1:
+    resolution: {integrity: sha512-PVqXh48wh4T53F/1CCu8PIPCxLzWyCnn/9T5W1Jpmdy5h9Cwd+0YQS6/LwhHXSafuc61/xg9Lv5OrCby6a++jg==}
+    engines: {node: '>= 12.0.0'}
+    cpu: [x64]
+    os: [win32]
+
+  lightningcss@1.30.1:
+    resolution: {integrity: sha512-xi6IyHML+c9+Q3W0S4fCQJOym42pyurFiJUHEcEyHS0CeKzia4yZDEsLlqOFykxOdHpNy0NmvVO31vcSqAxJCg==}
+    engines: {node: '>= 12.0.0'}
+
   loader-utils@1.4.2:
     resolution: {integrity: sha512-I5d00Pd/jwMD2QCduo657+YM/6L3KZu++pmX9VFncxaxvHcru9jx1lBaFft+r4Mt2jK0Yhp41XlRAihzPxHNCg==}
     engines: {node: '>=4.0.0'}
@@ -1500,6 +1693,10 @@ packages:
     resolution: {integrity: sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==}
     engines: {node: '>=16 || 14 >=14.17'}
 
+  minizlib@3.1.0:
+    resolution: {integrity: sha512-KZxYo1BUkWD2TVFLr0MQoM8vUUigWD3LlD83a/75BqC+4qE0Hb1Vo5v1FgcfaNXvfXzr+5EhQ6ing/CaBijTlw==}
+    engines: {node: '>= 18'}
+
   mitt@3.0.1:
     resolution: {integrity: sha512-vKivATfr97l2/QBCYAkXYDbrIWPM2IIKEl7YPhjCvKlG3kE2gm+uBo6nEXK3M5/Ffh/FLpKExzOQ3JJoJGFKBw==}
 
@@ -2061,6 +2258,17 @@ packages:
     resolution: {integrity: sha512-GTt8rSKje5FilG+wEdfCkOcLL7LWqpMlr2c3LRuKt/YXxcJ52aGSbGBAdI4L3aaqfrBt6y711El53ItyH1NWzg==}
     engines: {node: '>=16.0.0'}
 
+  tailwindcss@4.1.14:
+    resolution: {integrity: sha512-b7pCxjGO98LnxVkKjaZSDeNuljC4ueKUddjENJOADtubtdo8llTaJy7HwBMeLNSSo2N5QIAgklslK1+Ir8r6CA==}
+
+  tapable@2.3.0:
+    resolution: {integrity: sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg==}
+    engines: {node: '>=6'}
+
+  tar@7.5.1:
+    resolution: {integrity: sha512-nlGpxf+hv0v7GkWBK2V9spgactGOp0qvfWRxUMjqHyzrt3SgwE48DIv/FhqPHJYLHpgW1opq3nERbz5Anq7n1g==}
+    engines: {node: '>=18'}
+
   tiny-emitter@2.1.0:
     resolution: {integrity: sha512-NB6Dk1A9xgQPMoGqC5CVXn123gWyte215ONT5Pp5a0yt4nlEoO1ZWeCwpncaekPHXO60i47ihFnZPiRPjRMq4Q==}
 
@@ -2290,6 +2498,10 @@ packages:
     resolution: {integrity: sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==}
     engines: {node: '>=12'}
 
+  yallist@5.0.0:
+    resolution: {integrity: sha512-YgvUTfwqyc7UXVMrB+SImsVYSmTS8X/tSrtdNZMImM+n7+QTriRXyXim0mBrTXNeqzVF0KWGgHPeiyViFFrNDw==}
+    engines: {node: '>=18'}
+
   zrender@5.6.1:
     resolution: {integrity: sha512-OFXkDJKcrlx5su2XbzJvj/34Q3m6PvyCZkVPHGYpcCJ52ek4U/ymZyfuV1nKE23AyBJ51E/6Yr0mhZ7xGTO4ag==}
 
@@ -2416,8 +2628,29 @@ snapshots:
       wrap-ansi: 8.1.0
       wrap-ansi-cjs: wrap-ansi@7.0.0
 
+  '@isaacs/fs-minipass@4.0.1':
+    dependencies:
+      minipass: 7.1.2
+
+  '@jridgewell/gen-mapping@0.3.13':
+    dependencies:
+      '@jridgewell/sourcemap-codec': 1.5.5
+      '@jridgewell/trace-mapping': 0.3.31
+
+  '@jridgewell/remapping@2.3.5':
+    dependencies:
+      '@jridgewell/gen-mapping': 0.3.13
+      '@jridgewell/trace-mapping': 0.3.31
+
+  '@jridgewell/resolve-uri@3.1.2': {}
+
   '@jridgewell/sourcemap-codec@1.5.5': {}
 
+  '@jridgewell/trace-mapping@0.3.31':
+    dependencies:
+      '@jridgewell/resolve-uri': 3.1.2
+      '@jridgewell/sourcemap-codec': 1.5.5
+
   '@nodelib/fs.scandir@2.1.5':
     dependencies:
       '@nodelib/fs.stat': 2.0.5
@@ -2511,6 +2744,77 @@ snapshots:
 
   '@sxzz/popperjs-es@2.11.7': {}
 
+  '@tailwindcss/node@4.1.14':
+    dependencies:
+      '@jridgewell/remapping': 2.3.5
+      enhanced-resolve: 5.18.3
+      jiti: 2.6.1
+      lightningcss: 1.30.1
+      magic-string: 0.30.19
+      source-map-js: 1.2.1
+      tailwindcss: 4.1.14
+
+  '@tailwindcss/oxide-android-arm64@4.1.14':
+    optional: true
+
+  '@tailwindcss/oxide-darwin-arm64@4.1.14':
+    optional: true
+
+  '@tailwindcss/oxide-darwin-x64@4.1.14':
+    optional: true
+
+  '@tailwindcss/oxide-freebsd-x64@4.1.14':
+    optional: true
+
+  '@tailwindcss/oxide-linux-arm-gnueabihf@4.1.14':
+    optional: true
+
+  '@tailwindcss/oxide-linux-arm64-gnu@4.1.14':
+    optional: true
+
+  '@tailwindcss/oxide-linux-arm64-musl@4.1.14':
+    optional: true
+
+  '@tailwindcss/oxide-linux-x64-gnu@4.1.14':
+    optional: true
+
+  '@tailwindcss/oxide-linux-x64-musl@4.1.14':
+    optional: true
+
+  '@tailwindcss/oxide-wasm32-wasi@4.1.14':
+    optional: true
+
+  '@tailwindcss/oxide-win32-arm64-msvc@4.1.14':
+    optional: true
+
+  '@tailwindcss/oxide-win32-x64-msvc@4.1.14':
+    optional: true
+
+  '@tailwindcss/oxide@4.1.14':
+    dependencies:
+      detect-libc: 2.1.1
+      tar: 7.5.1
+    optionalDependencies:
+      '@tailwindcss/oxide-android-arm64': 4.1.14
+      '@tailwindcss/oxide-darwin-arm64': 4.1.14
+      '@tailwindcss/oxide-darwin-x64': 4.1.14
+      '@tailwindcss/oxide-freebsd-x64': 4.1.14
+      '@tailwindcss/oxide-linux-arm-gnueabihf': 4.1.14
+      '@tailwindcss/oxide-linux-arm64-gnu': 4.1.14
+      '@tailwindcss/oxide-linux-arm64-musl': 4.1.14
+      '@tailwindcss/oxide-linux-x64-gnu': 4.1.14
+      '@tailwindcss/oxide-linux-x64-musl': 4.1.14
+      '@tailwindcss/oxide-wasm32-wasi': 4.1.14
+      '@tailwindcss/oxide-win32-arm64-msvc': 4.1.14
+      '@tailwindcss/oxide-win32-x64-msvc': 4.1.14
+
+  '@tailwindcss/vite@4.1.14(vite@6.3.5(@types/node@24.6.1)(jiti@2.6.1)(lightningcss@1.30.1)(sass-embedded@1.89.1))':
+    dependencies:
+      '@tailwindcss/node': 4.1.14
+      '@tailwindcss/oxide': 4.1.14
+      tailwindcss: 4.1.14
+      vite: 6.3.5(@types/node@24.6.1)(jiti@2.6.1)(lightningcss@1.30.1)(sass-embedded@1.89.1)
+
   '@trysound/sax@0.2.0': {}
 
   '@types/estree@1.0.8': {}
@@ -2533,9 +2837,9 @@ snapshots:
 
   '@types/web-bluetooth@0.0.21': {}
 
-  '@vitejs/plugin-vue@5.2.4(vite@6.3.5(@types/node@24.6.1)(sass-embedded@1.89.1))(vue@3.5.16)':
+  '@vitejs/plugin-vue@5.2.4(vite@6.3.5(@types/node@24.6.1)(jiti@2.6.1)(lightningcss@1.30.1)(sass-embedded@1.89.1))(vue@3.5.16)':
     dependencies:
-      vite: 6.3.5(@types/node@24.6.1)(sass-embedded@1.89.1)
+      vite: 6.3.5(@types/node@24.6.1)(jiti@2.6.1)(lightningcss@1.30.1)(sass-embedded@1.89.1)
       vue: 3.5.16
 
   '@vue/compiler-core@3.5.16':
@@ -2832,6 +3136,8 @@ snapshots:
       ansi-styles: 4.3.0
       supports-color: 7.2.0
 
+  chownr@3.0.0: {}
+
   class-utils@0.3.6:
     dependencies:
       arr-union: 3.1.0
@@ -2985,6 +3291,8 @@ snapshots:
 
   delegate@3.2.0: {}
 
+  detect-libc@2.1.1: {}
+
   dom-serializer@0.2.2:
     dependencies:
       domelementtype: 2.3.0
@@ -3066,6 +3374,11 @@ snapshots:
 
   emojis-list@3.0.0: {}
 
+  enhanced-resolve@5.18.3:
+    dependencies:
+      graceful-fs: 4.2.11
+      tapable: 2.3.0
+
   entities@1.1.2: {}
 
   entities@2.2.0: {}
@@ -3612,6 +3925,8 @@ snapshots:
     optionalDependencies:
       '@pkgjs/parseargs': 0.11.0
 
+  jiti@2.6.1: {}
+
   js-base64@2.6.4: {}
 
   js-beautify@1.14.11:
@@ -3649,6 +3964,51 @@ snapshots:
 
   kind-of@6.0.3: {}
 
+  lightningcss-darwin-arm64@1.30.1:
+    optional: true
+
+  lightningcss-darwin-x64@1.30.1:
+    optional: true
+
+  lightningcss-freebsd-x64@1.30.1:
+    optional: true
+
+  lightningcss-linux-arm-gnueabihf@1.30.1:
+    optional: true
+
+  lightningcss-linux-arm64-gnu@1.30.1:
+    optional: true
+
+  lightningcss-linux-arm64-musl@1.30.1:
+    optional: true
+
+  lightningcss-linux-x64-gnu@1.30.1:
+    optional: true
+
+  lightningcss-linux-x64-musl@1.30.1:
+    optional: true
+
+  lightningcss-win32-arm64-msvc@1.30.1:
+    optional: true
+
+  lightningcss-win32-x64-msvc@1.30.1:
+    optional: true
+
+  lightningcss@1.30.1:
+    dependencies:
+      detect-libc: 2.1.1
+    optionalDependencies:
+      lightningcss-darwin-arm64: 1.30.1
+      lightningcss-darwin-x64: 1.30.1
+      lightningcss-freebsd-x64: 1.30.1
+      lightningcss-linux-arm-gnueabihf: 1.30.1
+      lightningcss-linux-arm64-gnu: 1.30.1
+      lightningcss-linux-arm64-musl: 1.30.1
+      lightningcss-linux-x64-gnu: 1.30.1
+      lightningcss-linux-x64-musl: 1.30.1
+      lightningcss-win32-arm64-msvc: 1.30.1
+      lightningcss-win32-x64-msvc: 1.30.1
+
   loader-utils@1.4.2:
     dependencies:
       big.js: 5.2.2
@@ -3749,6 +4109,10 @@ snapshots:
 
   minipass@7.1.2: {}
 
+  minizlib@3.1.0:
+    dependencies:
+      minipass: 7.1.2
+
   mitt@3.0.1: {}
 
   mixin-deep@1.3.2:
@@ -4384,6 +4748,18 @@ snapshots:
 
   sync-message-port@1.1.3: {}
 
+  tailwindcss@4.1.14: {}
+
+  tapable@2.3.0: {}
+
+  tar@7.5.1:
+    dependencies:
+      '@isaacs/fs-minipass': 4.0.1
+      chownr: 3.0.0
+      minipass: 7.1.2
+      minizlib: 3.1.0
+      yallist: 5.0.0
+
   tiny-emitter@2.1.0: {}
 
   tinyglobby@0.2.15:
@@ -4545,16 +4921,16 @@ snapshots:
 
   vary@1.1.2: {}
 
-  vite-plugin-compression@0.5.1(vite@6.3.5(@types/node@24.6.1)(sass-embedded@1.89.1)):
+  vite-plugin-compression@0.5.1(vite@6.3.5(@types/node@24.6.1)(jiti@2.6.1)(lightningcss@1.30.1)(sass-embedded@1.89.1)):
     dependencies:
       chalk: 4.1.2
       debug: 4.4.3
       fs-extra: 10.1.0
-      vite: 6.3.5(@types/node@24.6.1)(sass-embedded@1.89.1)
+      vite: 6.3.5(@types/node@24.6.1)(jiti@2.6.1)(lightningcss@1.30.1)(sass-embedded@1.89.1)
     transitivePeerDependencies:
       - supports-color
 
-  vite-plugin-svg-icons@2.0.1(vite@6.3.5(@types/node@24.6.1)(sass-embedded@1.89.1)):
+  vite-plugin-svg-icons@2.0.1(vite@6.3.5(@types/node@24.6.1)(jiti@2.6.1)(lightningcss@1.30.1)(sass-embedded@1.89.1)):
     dependencies:
       '@types/svgo': 2.6.4
       cors: 2.8.5
@@ -4564,11 +4940,11 @@ snapshots:
       pathe: 0.2.0
       svg-baker: 1.7.0
       svgo: 2.8.0
-      vite: 6.3.5(@types/node@24.6.1)(sass-embedded@1.89.1)
+      vite: 6.3.5(@types/node@24.6.1)(jiti@2.6.1)(lightningcss@1.30.1)(sass-embedded@1.89.1)
     transitivePeerDependencies:
       - supports-color
 
-  vite@6.3.5(@types/node@24.6.1)(sass-embedded@1.89.1):
+  vite@6.3.5(@types/node@24.6.1)(jiti@2.6.1)(lightningcss@1.30.1)(sass-embedded@1.89.1):
     dependencies:
       esbuild: 0.25.10
       fdir: 6.5.0(picomatch@4.0.3)
@@ -4579,6 +4955,8 @@ snapshots:
     optionalDependencies:
       '@types/node': 24.6.1
       fsevents: 2.3.3
+      jiti: 2.6.1
+      lightningcss: 1.30.1
       sass-embedded: 1.89.1
 
   vue-cropper@1.1.1: {}
@@ -4664,6 +5042,8 @@ snapshots:
       string-width: 5.1.2
       strip-ansi: 7.1.2
 
+  yallist@5.0.0: {}
+
   zrender@5.6.1:
     dependencies:
       tslib: 2.3.0

+ 45 - 19
back-ui/src/api/dz/cards.js

@@ -1,44 +1,70 @@
-import request from '@/utils/request'
+import request from "@/utils/request";
 
 // 查询学习卡列表
 export function listCards(query) {
   return request({
-    url: '/dz/cards/list',
-    method: 'get',
-    params: query
-  })
+    url: "/dz/cards/list",
+    method: "get",
+    params: query,
+  });
 }
 
 // 查询学习卡详细
 export function getCards(cardId) {
   return request({
-    url: '/dz/cards/' + cardId,
-    method: 'get'
-  })
+    url: "/dz/cards/" + cardId,
+    method: "get",
+  });
 }
 
 // 新增学习卡
 export function addCards(data) {
   return request({
-    url: '/dz/cards',
-    method: 'post',
-    data: data
-  })
+    url: "/dz/cards",
+    method: "post",
+    data: data,
+  });
 }
 
 // 修改学习卡
 export function updateCards(data) {
   return request({
-    url: '/dz/cards',
-    method: 'put',
-    data: data
-  })
+    url: "/dz/cards",
+    method: "put",
+    data: data,
+  });
 }
 
 // 删除学习卡
 export function delCards(cardId) {
   return request({
-    url: '/dz/cards/' + cardId,
-    method: 'delete'
-  })
+    url: "/dz/cards/" + cardId,
+    method: "delete",
+  });
+}
+
+// 制卡
+export function issueCard(institutionId, type, count) {
+  return request({
+    url: "/dz/cards/issueCard",
+    method: "post",
+    params: {
+      institutionId,
+      type,
+      count,
+    },
+  });
+}
+
+// 分配卡
+export function assignCard(agentId, begin, end) {
+  return request({
+    url: "/dz/cards/assignCard",
+    method: "post",
+    params: {
+      agentId,
+      begin,
+      end,
+    },
+  });
 }

+ 8 - 0
back-ui/src/api/dz/classes.js

@@ -9,6 +9,14 @@ export function listClasses(query) {
   })
 }
 
+export function listAllClass(query) {
+  return request({
+    url: '/dz/classes/listAll',
+    method: 'get',
+    params: query
+  })
+}
+
 // 查询学生班级列表
 export function listAllSchool(query) {
   return request({

+ 58 - 0
back-ui/src/api/dz/papers.js

@@ -0,0 +1,58 @@
+import request from '@/utils/request'
+
+/// 科目列表
+export function getPaperSubjects(query) {
+    return request({
+        url: '/learn/teaching/subjects',
+        method: 'get',
+        params: query
+    })
+}
+
+/// 科目列表
+export function getPaperKnowledges(query) {
+    return request({
+        url: '/learn/teaching/knowledges',
+        method: 'get',
+        params: query
+    })
+}
+
+/// 省份列表
+export function getPaperProvinces(query) {
+    return request({
+        url: '/learn/teaching/provinces',
+        method: 'get',
+        params: query
+    })
+}
+
+/// 考生类型
+export function getPaperExamTypes(query) {
+    // query: {location}
+    return request({
+        url: '/learn/teaching/examTypes',
+        method: 'get',
+        params: query
+    })
+}
+
+/// 院校
+export function getPaperUniversities(query) {
+    // query: {batchId}
+    return request({
+        url: '/learn/teaching/universities',
+        method: 'get',
+        params: query
+    })
+}
+
+/// 专业组
+export function getPaperMajors(query) {
+    // query: {location, examType, batchId, universityId}
+    return request({
+        url: '/learn/teaching/majors',
+        method: 'get',
+        params: query
+    })
+}

+ 9 - 0
back-ui/src/api/dz/school.js

@@ -1,5 +1,14 @@
 import request from '@/utils/request'
 
+// 查询查询院校列表
+export function listUniversity(query) {
+  return request({
+    url: '/front/university/list',
+    method: 'get',
+    params: query
+  })
+}
+
 // 查询机构校区列表
 export function listAllSchool(query) {
   return request({

+ 17 - 0
back-ui/src/api/dz/teacherclass.js

@@ -9,6 +9,15 @@ export function listTeacherclass(query) {
   })
 }
 
+// 查询教师班级关系列表
+export function listAllTeacherClass(query) {
+  return request({
+    url: '/dz/teacherclass/listAllTeacherClass',
+    method: 'get',
+    params: query
+  })
+}
+
 // 查询教师班级关系详细
 export function getTeacherclass(id) {
   return request({
@@ -26,6 +35,14 @@ export function addTeacherclass(data) {
   })
 }
 
+export function batchBindTeacherClass(data) {
+  return request({
+    url: '/dz/teacherclass/batchBindTeacherClass',
+    method: 'post',
+    data: data
+  })
+}
+
 // 修改教师班级关系
 export function updateTeacherclass(data) {
   return request({

+ 15 - 0
back-ui/src/assets/icons/svg/share.svg

@@ -0,0 +1,15 @@
+<svg width="1024" height="1024" viewBox="0 0 1024 1024" xmlns="http://www.w3.org/2000/svg">
+  <!-- 中心圆 -->
+  <circle cx="512" cy="512" r="64" fill="currentColor"/>
+  
+  <!-- 箭头线条 -->
+  <path d="M512 192L512 832" stroke="currentColor" stroke-width="8" stroke-linecap="round"/>
+  <path d="M192 512L832 512" stroke="currentColor" stroke-width="8" stroke-linecap="round"/>
+  
+  <!-- 对角线箭头 -->
+  <path d="M224 224L800 800" stroke="currentColor" stroke-width="8" stroke-linecap="round"/>
+  <path d="M800 224L224 800" stroke="currentColor" stroke-width="8" stroke-linecap="round"/>
+</svg>
+
+
+

+ 15 - 0
back-ui/src/assets/styles/tailwind.css

@@ -0,0 +1,15 @@
+@layer theme, base, components, utilities;
+@import "tailwindcss/theme.css" layer(theme);
+@import "tailwindcss/utilities.css" layer(utilities);
+/* 没有使用base, 禁用了preflight, 否则会和若依自己的样式有冲突 */
+
+@theme {
+    --color-main: '#303030';
+    --color-content: '#606060';
+    --color-tips: '#909090';
+    --color-primary: var(--el-color-primary);
+    --color-warning: var(--el-color-warning);
+    --color-danger: var(--el-color-danger);
+    --color-success: var(--el-color-success);
+    --color-info: var(--el-color-info);
+}

+ 41 - 15
back-ui/src/components/AddressSelect/index.vue

@@ -1,22 +1,48 @@
 <template>
-  <el-cascader :options="data" :props="props" v-model="ids" clearable @change="handleChange" />
+  <el-cascader
+    :options="data"
+    :props="cascaderProps"
+    v-model="ids"
+    clearable
+    @change="handleChange"
+  />
 </template>
 <script setup>
+import { ref, watch } from "vue";
 import { cascaderAreaList } from "@/api/system/area";
-const data = ref([]);
-const ids = ref('');
-const opts = defineProps({
-  value: String
-});
-watch(opts, (val) => {
-  ids.value = val;
+
+const props = defineProps({
+  modelValue: [String, Array],
 });
-const emit = defineEmits(['update:value', 'change']);
-const props = {
+
+const emit = defineEmits(["update:modelValue", "change"]);
+
+const data = ref([]);
+const ids = ref(props.modelValue || []);
+
+// 监听父组件传入的值变化
+watch(
+  () => props.modelValue,
+  (newVal) => {
+    ids.value = newVal || [];
+  },
+  { immediate: true }
+);
+
+// 监听内部值变化并通知父组件
+watch(
+  ids,
+  (newVal) => {
+    emit("update:modelValue", newVal);
+  },
+  { deep: true }
+);
+
+const cascaderProps = {
   multiple: false,
   checkStrictly: true,
-  label: 'areaName',
-  value: 'areaId',
+  label: "areaName",
+  value: "areaId",
   lazy: true,
   async lazyLoad(node, resolve) {
     const { level, value } = node;
@@ -31,9 +57,9 @@ const props = {
 async function getAreaList(parentId) {
   return cascaderAreaList(parentId);
 }
-function handleChange() {
-  emit('update:value', ids.value || []);
-  emit('change', ids.value || []);
+function handleChange(value) {
+  emit("update:modelValue", ids.value || []);
+  emit("change", ids.value || []);
 }
 </script>
 <style lang="scss" scoped></style>

+ 25 - 5
back-ui/src/components/Form/index.vue

@@ -177,11 +177,11 @@
       <el-col
         v-for="(item, index) in formConfig"
         :key="index"
-        :span="item.span || 24"
-        :xs="item.xs || 24"
-        :sm="item.sm || 12"
-        :md="item.md || 8"
-        :lg="item.lg || 6"
+        :span="item.span || 4"
+        :xs="item.xs || 12"
+        :sm="item.sm || 8"
+        :md="item.md || 6"
+        :lg="item.lg || 4"
         :xl="item.xl || 4"
       >
         <el-form-item
@@ -398,6 +398,26 @@ const formRef = ref();
 const formData = reactive({});
 const formRules = reactive({});
 
+// 监听父组件传入的modelValue变化,同步到formData
+watch(
+  () => props.modelValue,
+  (newValue) => {
+    if (newValue) {
+      Object.assign(formData, newValue);
+    }
+  },
+  { immediate: true, deep: true }
+);
+
+// 监听formData变化,同步回父组件
+watch(
+  formData,
+  (newData) => {
+    emit("update:modelValue", { ...newData });
+  },
+  { deep: true }
+);
+
 // 计算属性
 const formConfig = computed(() => props.config);
 

+ 37 - 0
back-ui/src/components/SearchForm/index.vue

@@ -5,6 +5,7 @@
       ref="formRef"
       :config="searchConfig"
       :model-value="searchData"
+      @update:model-value="handleFormUpdate"
       :inline="inline"
       :size="size"
       :label-width="labelWidth"
@@ -126,6 +127,42 @@ const formRef = ref();
 const searchData = reactive({});
 const expandCollapse = ref(true);
 
+// 处理Form组件update:model-value事件
+const handleFormUpdate = (newData) => {
+  Object.assign(searchData, newData);
+};
+
+// 监听父组件传入的modelValue变化,同步到searchData
+watch(
+  () => props.modelValue,
+  (newValue) => {
+    if (newValue) {
+      Object.assign(searchData, newValue);
+    }
+  },
+  { immediate: true, deep: true }
+);
+
+// 监听searchData变化,同步回父组件
+watch(
+  searchData,
+  (newData) => {
+    emit("update:modelValue", { ...newData });
+  },
+  { deep: true }
+);
+
+// 强制监听searchData中的areaIds字段
+watch(
+  () => searchData.areaIds,
+  (newAreaIds, oldAreaIds) => {
+    if (newAreaIds !== oldAreaIds) {
+      emit("update:modelValue", { ...searchData });
+    }
+  },
+  { immediate: false, deep: true }
+);
+
 // 计算属性 - 筛选出可搜索的字段
 const searchConfig = computed(() => {
   let config = props.config.filter((item) => item.search !== false);

+ 357 - 0
back-ui/src/components/Table/index.vue

@@ -0,0 +1,357 @@
+<template>
+  <div class="table-container">
+    <el-table
+      v-loading="loading"
+      :data="tableData"
+      @selection-change="handleSelectionChange"
+      :stripe="stripe"
+      :border="border"
+      :height="height"
+      :max-height="maxHeight"
+      v-bind="$attrs"
+    >
+      <!-- 多选列 -->
+      <el-table-column
+        v-if="selectionMode === 'multiple'"
+        type="selection"
+        width="55"
+        align="center"
+      />
+
+      <!-- 单选列 -->
+      <el-table-column
+        v-if="selectionMode === 'single'"
+        width="55"
+        align="center"
+      >
+        <template #default="scope">
+          <el-radio
+            v-model="selectedRowKey"
+            :label="scope.row[rowKey]"
+            @change="handleSingleSelection(scope.row)"
+          >
+            &nbsp;
+          </el-radio>
+        </template>
+      </el-table-column>
+
+      <!-- 序号列 -->
+      <el-table-column
+        v-if="showIndex"
+        label="序号"
+        type="index"
+        width="80"
+        align="center"
+      />
+
+      <!-- 动态列 -->
+      <template v-for="column in columns" :key="column.prop || column.label">
+        <el-table-column
+          :label="column.label"
+          :prop="column.prop"
+          :align="column.align || 'center'"
+          :width="column.width"
+          :min-width="column.minWidth"
+          :fixed="column.fixed"
+          :show-overflow-tooltip="column.showOverflowTooltip !== false"
+          :type="column.type === 'index' ? 'index' : undefined"
+        >
+          <template #default="scope">
+            <!-- 序号列 - 使用Element Plus默认序号 -->
+            <template v-if="column.type === 'index'">
+              {{ scope.$index + 1 }}
+            </template>
+
+            <!-- 字典标签 -->
+            <template v-else-if="column.type === 'dict'">
+              <dict-tag
+                :options="column.options"
+                :value="scope.row[column.prop]"
+              />
+            </template>
+
+            <!-- 时间格式化 -->
+            <template v-else-if="column.type === 'time'">
+              <span>{{
+                parseTime(
+                  scope.row[column.prop],
+                  column.format || "{y}-{m}-{d}"
+                )
+              }}</span>
+            </template>
+
+            <!-- 图片 -->
+            <template v-else-if="column.type === 'image'">
+              <el-image
+                :src="scope.row[column.prop]"
+                :preview-src-list="
+                  column.previewList || [scope.row[column.prop]]
+                "
+                :style="{
+                  width: column.width || '50px',
+                  height: column.height || '50px',
+                }"
+                fit="cover"
+                v-if="scope.row[column.prop]"
+              />
+            </template>
+
+            <!-- 状态标签 -->
+            <template v-else-if="column.type === 'status'">
+              <el-tag
+                :type="getStatusTagType(scope.row, column)"
+                :size="column.tagSize || 'default'"
+              >
+                {{ getStatusText(scope.row, column) }}
+              </el-tag>
+            </template>
+
+            <!-- 自定义slot -->
+            <template v-else-if="column.type === 'slot'">
+              <slot :name="column.slotName" :row="scope.row" :column="column" />
+            </template>
+
+            <!-- 普通文本 -->
+            <template v-else>
+              <span :class="column.textClass">{{
+                scope.row[column.prop]
+              }}</span>
+            </template>
+          </template>
+        </el-table-column>
+      </template>
+
+      <!-- 操作列 -->
+      <el-table-column
+        v-if="actions && actions.length > 0"
+        label="操作"
+        align="center"
+        :width="actionWidth"
+        class-name="small-padding fixed-width"
+      >
+        <template #default="scope">
+          <template v-for="action in actions" :key="action.key">
+            <el-button
+              :link="action.link !== false"
+              :type="action.type || 'primary'"
+              :icon="action.icon"
+              :size="action.size || 'small'"
+              :disabled="getActionDisabled(scope.row, action)"
+              @click="handleAction(action, scope.row)"
+              v-hasPermi="action.permission"
+            >
+              {{ action.label }}
+            </el-button>
+            <el-divider
+              v-if="
+                action.showDivider !== false &&
+                action !== actions[actions.length - 1]
+              "
+              direction="vertical"
+            />
+          </template>
+        </template>
+      </el-table-column>
+    </el-table>
+
+    <!-- 分页 -->
+    <pagination
+      v-if="showPagination"
+      v-show="total > 0"
+      :total="total"
+      :page.sync="queryParams.pageNum"
+      :limit.sync="queryParams.pageSize"
+      @pagination="getList"
+    />
+  </div>
+</template>
+
+<script setup>
+import { ref, computed, watch } from "vue";
+import { parseTime } from "@/utils/ruoyi";
+// import { useDict } from '@/stores/modules/dict' // 不需要,父组件传递
+import Pagination from "@/components/Pagination/index.vue";
+
+const props = defineProps({
+  // 表格数据
+  data: {
+    type: Array,
+    default: () => [],
+  },
+  // 表格列配置
+  columns: {
+    type: Array,
+    default: () => [],
+  },
+  // 操作按钮配置
+  actions: {
+    type: Array,
+    default: () => [],
+  },
+  // 加载状态
+  loading: {
+    type: Boolean,
+    default: false,
+  },
+  // 总数
+  total: {
+    type: Number,
+    default: 0,
+  },
+  // 查询参数
+  queryParams: {
+    type: Object,
+    default: () => ({
+      pageNum: 1,
+      pageSize: 10,
+    }),
+  },
+  // 是否显示多选
+  showSelection: {
+    type: Boolean,
+    default: false,
+  },
+  // 是否显示序号
+  showIndex: {
+    type: Boolean,
+    default: false,
+  },
+  // 是否显示分页
+  showPagination: {
+    type: Boolean,
+    default: true,
+  },
+  // 表格属性
+  stripe: {
+    type: Boolean,
+    default: true,
+  },
+  border: {
+    type: Boolean,
+    default: false,
+  },
+  height: {
+    type: [String, Number],
+    default: null,
+  },
+  maxHeight: {
+    type: [String, Number],
+    default: null,
+  },
+  // 操作列宽度
+  actionWidth: {
+    type: [String, Number],
+    default: "auto",
+  },
+  // 选择模式:'single' | 'multiple' | 'none'
+  selectionMode: {
+    type: String,
+    default: "none",
+    validator: (value) => ["single", "multiple", "none"].includes(value),
+  },
+  // 当前选中的行
+  selectedRows: {
+    type: Array,
+    default: () => [],
+  },
+  // 选择键字段名
+  rowKey: {
+    type: String,
+    default: "id",
+  },
+});
+
+const emit = defineEmits(["selection-change", "action", "getList"]);
+
+// 计算属性
+const tableData = computed(() => props.data);
+
+// 单选相关
+const selectedRowKey = ref(null);
+
+// 监听选中行变化
+watch(
+  () => props.selectedRows,
+  (newRows) => {
+    if (props.selectionMode === "single" && newRows.length > 0) {
+      selectedRowKey.value = newRows[0][props.rowKey];
+    } else if (props.selectionMode === "single" && newRows.length === 0) {
+      selectedRowKey.value = null;
+    }
+  },
+  { immediate: true }
+);
+
+// 单选处理
+const handleSingleSelection = (row) => {
+  emit("selection-change", [row]);
+};
+
+// 多选处理
+const handleSelectionChange = (selection) => {
+  emit("selection-change", selection);
+};
+
+// 执行操作
+const handleAction = (action, row) => {
+  emit("action", action, row);
+};
+
+// 获取操作按钮禁用状态
+const getActionDisabled = (row, action) => {
+  if (typeof action.disabled === "function") {
+    return action.disabled(row);
+  }
+  return action.disabled || false;
+};
+
+// 获取状态标签类型
+const getStatusTagType = (row, column) => {
+  if (typeof column.statusType === "function") {
+    return column.statusType(row);
+  }
+
+  const value = row[column.prop];
+  if (column.statusMap) {
+    return column.statusMap[value]?.type || "info";
+  }
+
+  // 默认状态映射
+  const statusMap = {
+    0: "info",
+    1: "success",
+    2: "warning",
+    3: "danger",
+  };
+  return statusMap[value] || "info";
+};
+
+// 获取状态文本
+const getStatusText = (row, column) => {
+  if (typeof column.statusText === "function") {
+    return column.statusText(row);
+  }
+
+  const value = row[column.prop];
+  if (column.statusMap) {
+    return column.statusMap[value]?.text || value;
+  }
+
+  return value;
+};
+
+// 获取列表数据
+const getList = () => {
+  emit("getList");
+};
+</script>
+
+<style scoped>
+.table-container {
+  width: 100%;
+}
+
+.el-table {
+  width: 100%;
+}
+</style>

+ 1 - 9
back-ui/src/layout/components/Navbar.vue

@@ -8,14 +8,6 @@
       <template v-if="appStore.device !== 'mobile'">
         <header-search id="header-search" class="right-menu-item" />
 
-        <el-tooltip content="源码地址" effect="dark" placement="bottom">
-          <ruo-yi-git id="ruoyi-git" class="right-menu-item hover-effect" />
-        </el-tooltip>
-
-        <el-tooltip content="文档地址" effect="dark" placement="bottom">
-          <ruo-yi-doc id="ruoyi-doc" class="right-menu-item hover-effect" />
-        </el-tooltip>
-
         <screenfull id="screenfull" class="right-menu-item hover-effect" />
 
         <el-tooltip content="主题模式" effect="dark" placement="bottom">
@@ -178,7 +170,7 @@ function toggleTheme() {
 
         svg {
           transition: transform 0.3s;
-          
+
           &:hover {
             transform: scale(1.15);
           }

+ 1 - 0
back-ui/src/main.js

@@ -7,6 +7,7 @@ import 'element-plus/dist/index.css'
 import 'element-plus/theme-chalk/dark/css-vars.css'
 import locale from 'element-plus/es/locale/lang/zh-cn'
 
+import '@/assets/styles/tailwind.css'
 import '@/assets/styles/index.scss' // global css
 
 import App from './App'

+ 36 - 7
back-ui/src/views/dz/agent/index.vue

@@ -76,7 +76,8 @@
                           <el-table-column label="归属机构" align="center" key="deptName" prop="dept.deptName" :show-overflow-tooltip="true" />
                           <el-table-column label="联系电话" align="center" prop="phonenumber" />
                           <!--      <el-table-column label="上级代理商ID" align="center" prop="parentId" />-->
-                          <el-table-column label="学校/校区" align="center" prop="schools" />
+<!--                          <el-table-column label="学校/校区" align="center" prop="schools" />-->
+                          <el-table-column label="学校/校区" align="center" prop="schoolName" />
                           <el-table-column label="备注" align="center" prop="remark" />
                           <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
                               <template #default="scope">
@@ -95,7 +96,7 @@
     <el-dialog :title="title" v-model="open" width="500px" append-to-body>
       <el-form ref="agentRef" :model="form" :rules="rules" label-width="80px">
         <el-form-item label="用户ID" prop="userId">
-          <el-input v-model="form.userId" placeholder="请输入用户ID" />
+          <el-input v-model="form.userId" placeholder="请输入用户ID" disabled/>
         </el-form-item>
           <el-form-item label="代理商" prop="name">
               <el-input v-model="form.name" placeholder="请输入代理商" />
@@ -117,9 +118,20 @@
             check-strictly
           />
         </el-form-item>
-        <el-form-item label="负责校区" prop="schools">
-          <el-input v-model="form.schools" placeholder="请输入负责校区" />
-        </el-form-item>
+<!--        <el-form-item label="负责校区" prop="schools">-->
+<!--          <el-input v-model="form.schools" placeholder="请输入负责校区" />-->
+<!--        </el-form-item>-->
+
+          <el-form-item label="关联校区" prop="classIds">
+              <el-select v-model="form.schoolIds" multiple placeholder="请选择校区" style="width: 100%">
+                  <el-option
+                          v-for="item in schoolOptions"
+                          :key="item.id"
+                          :label="item.name"
+                          :value="item.id"
+                  />
+              </el-select>
+          </el-form-item>
         <el-form-item label="备注" prop="remark">
           <el-input v-model="form.remark" placeholder="请输入备注" />
         </el-form-item>
@@ -140,6 +152,7 @@ import { deptTreeSelect } from "@/api/system/user"
 import useAppStore from '@/store/modules/app'
 import { Splitpanes, Pane } from "splitpanes"
 import "splitpanes/dist/splitpanes.css"
+import { listAllSchool } from "@/api/dz/school"
 
 const { proxy } = getCurrentInstance()
 const appStore = useAppStore()
@@ -155,6 +168,7 @@ const refreshTable = ref(true)
 const deptName = ref("")
 const deptOptions = ref(undefined)
 const enabledDeptOptions = ref(undefined)
+const schoolOptions = ref(undefined)
 
 const data = reactive({
   form: {},
@@ -164,7 +178,8 @@ const data = reactive({
     deptId: null,
     phonenumber: null,
     parentId: null,
-    schools: null,
+    // schools: null,
+    schoolIds: []
   },
   rules: {
   }
@@ -244,7 +259,8 @@ function reset() {
     name: null,
     phonenumber: null,
     parentId: null,
-    schools: null,
+    // schools: null,
+    schoolIds: [],
     remark: null
   }
   proxy.resetForm("agentRef")
@@ -271,6 +287,8 @@ function resetQuery() {
 function handleAdd(row) {
   reset()
   getTreeselect()
+  //处理学校
+  getSchools(row)
   if (row != null && row.agentId) {
     form.value.parentId = row.agentId
   } else {
@@ -298,11 +316,22 @@ async function handleUpdate(row) {
   }
   getAgent(row.agentId).then(response => {
     form.value = response.data
+    //处理学校
+    getSchools(row)
+
     open.value = true
     title.value = "修改机构代理"
   })
 }
 
+function getSchools(row){
+    //处理学校
+    const deptId = row.deptId;
+    listAllSchool(deptId).then(response => {
+        schoolOptions.value= response.data
+    })
+}
+
 /** 提交按钮 */
 function submitForm() {
   proxy.$refs["agentRef"].validate(valid => {

+ 157 - 0
back-ui/src/views/dz/cards/components/ApplyCardDialog.vue

@@ -0,0 +1,157 @@
+<template>
+  <el-dialog
+    title="申请开卡"
+    v-model="visible"
+    width="500px"
+    append-to-body
+    align-center
+    :center="true"
+  >
+    <div class="apply-card-content">
+      <el-form
+        ref="applyCardFormRef"
+        :model="form"
+        :rules="rules"
+        label-width="100px"
+      >
+        <!-- 卡号段 -->
+        <el-form-item label="卡号段" prop="cardRange" :required="true">
+          <div style="display: flex; align-items: center; gap: 10px">
+            <el-input
+              v-model="form.beginCardNo"
+              placeholder="开始卡号"
+              style="flex: 1"
+            />
+            <span>-</span>
+            <el-input
+              v-model="form.endCardNo"
+              placeholder="结束卡号"
+              style="flex: 1"
+            />
+          </div>
+        </el-form-item>
+
+        <!-- 学校 -->
+        <el-form-item label="学校" prop="schoolId">
+          <el-select
+            v-model="form.schoolId"
+            placeholder="请选择学校"
+            style="width: 100%"
+          >
+            <el-option
+              v-for="school in schoolList"
+              :key="school.id"
+              :label="school.name"
+              :value="school.id"
+            ></el-option>
+          </el-select>
+        </el-form-item>
+      </el-form>
+    </div>
+
+    <template #footer>
+      <div class="dialog-footer">
+        <el-button @click="handleCancel">取消</el-button>
+        <el-button type="primary" @click="handleSubmit">
+          <svg-icon
+            icon-class="lightning"
+            class="mr-1"
+            style="font-size: 14px"
+          />
+          提交
+        </el-button>
+      </div>
+    </template>
+  </el-dialog>
+</template>
+
+<script setup>
+import { ref, computed, watch } from "vue";
+
+const props = defineProps({
+  modelValue: {
+    type: Boolean,
+    default: false,
+  },
+  schoolList: {
+    type: Array,
+    default: () => [],
+  },
+});
+
+const emit = defineEmits(["update:modelValue", "confirm"]);
+
+const visible = computed({
+  get: () => props.modelValue,
+  set: (value) => emit("update:modelValue", value),
+});
+
+const applyCardFormRef = ref();
+
+const form = ref({
+  beginCardNo: "",
+  endCardNo: "",
+  schoolId: null,
+});
+
+const rules = {
+  beginCardNo: [
+    { required: true, message: "开始卡号不能为空", trigger: "blur" },
+  ],
+  endCardNo: [{ required: true, message: "结束卡号不能为空", trigger: "blur" }],
+};
+
+// 重置表单
+function resetForm() {
+  form.value = {
+    beginCardNo: "",
+    endCardNo: "",
+    schoolId: null,
+  };
+}
+
+// 取消
+function handleCancel() {
+  visible.value = false;
+  resetForm();
+}
+
+// 提交申请
+async function handleSubmit() {
+  applyCardFormRef.value.validate(async (valid) => {
+    if (valid) {
+      try {
+        const data = {
+          beginCardNo: form.value.beginCardNo,
+          endCardNo: form.value.endCardNo,
+          schoolId: form.value.schoolId,
+        };
+
+        emit("confirm", data);
+        visible.value = false;
+        resetForm();
+      } catch (error) {
+        console.error("申请开卡失败:", error);
+        throw error;
+      }
+    }
+  });
+}
+
+// 监听弹窗关闭,重置表单
+watch(visible, (newVal) => {
+  if (!newVal) {
+    resetForm();
+  }
+});
+</script>
+
+<style scoped>
+.apply-card-content {
+  padding: 20px 0;
+}
+
+.dialog-footer {
+  text-align: right;
+}
+</style>

+ 237 - 0
back-ui/src/views/dz/cards/components/AssignCardDialog.vue

@@ -0,0 +1,237 @@
+<template>
+  <el-dialog
+    title="分配卡"
+    v-model="visible"
+    width="500px"
+    append-to-body
+    align-center
+    :center="true"
+  >
+    <el-form
+      ref="assignCardFormRef"
+      :model="form"
+      :rules="rules"
+      label-width="100px"
+    >
+      <!-- 卡类型 -->
+      <el-form-item label="卡类型" prop="cardType" :required="true">
+        <el-select
+          v-model="form.cardType"
+          placeholder="请选择卡类型"
+          style="width: 100%"
+        >
+          <el-option
+            v-for="dict in cardTypeOptions"
+            :key="dict.value"
+            :label="dict.label"
+            :value="parseInt(dict.value)"
+          ></el-option>
+        </el-select>
+      </el-form-item>
+
+      <!-- 卡号段 -->
+      <el-form-item label="卡号段" prop="cardRange" :required="true">
+        <div style="display: flex; align-items: center; gap: 10px">
+          <el-input
+            v-model="form.beginCardNo"
+            placeholder="开始卡号"
+            style="flex: 1"
+          />
+          <span>-</span>
+          <el-input
+            v-model="form.endCardNo"
+            placeholder="结束卡号"
+            style="flex: 1"
+          />
+        </div>
+      </el-form-item>
+
+      <!-- 代理商 -->
+      <el-form-item label="代理商" prop="agentId" :required="true">
+        <el-select
+          v-model="form.agentId"
+          placeholder="请选择代理商"
+          style="width: 100%"
+        >
+          <el-option
+            v-for="agent in agentList"
+            :key="agent.id"
+            :label="agent.name"
+            :value="agent.id"
+          ></el-option>
+        </el-select>
+      </el-form-item>
+
+      <!-- 省份 -->
+      <el-form-item label="省份" prop="province">
+        <el-select
+          v-model="form.province"
+          placeholder="请选择省份"
+          style="width: 100%"
+        >
+          <el-option label="全部" value="all"></el-option>
+          <el-option label="湖南" value="hunan"></el-option>
+          <el-option label="广东" value="guangdong"></el-option>
+          <el-option label="北京" value="beijing"></el-option>
+          <el-option label="上海" value="shanghai"></el-option>
+        </el-select>
+      </el-form-item>
+
+      <!-- 学校 -->
+      <el-form-item label="学校" prop="school">
+        <el-select
+          v-model="form.school"
+          placeholder="请选择学校"
+          style="width: 100%"
+        >
+          <el-option label="全部" value="all"></el-option>
+          <el-option
+            v-for="school in schoolList"
+            :key="school.id"
+            :label="school.name"
+            :value="school.id"
+          ></el-option>
+        </el-select>
+      </el-form-item>
+
+      <!-- 考生类别 -->
+      <el-form-item label="考生类别" prop="studentCategory">
+        <el-select
+          v-model="form.studentCategory"
+          placeholder="请选择考生类别"
+          style="width: 100%"
+        >
+          <el-option label="全部" value="all"></el-option>
+          <el-option label="普通考生" value="normal"></el-option>
+          <el-option label="艺术考生" value="art"></el-option>
+          <el-option label="体育考生" value="sports"></el-option>
+        </el-select>
+      </el-form-item>
+    </el-form>
+
+    <template #footer>
+      <div class="dialog-footer">
+        <el-button @click="handleCancel">取消</el-button>
+        <el-button type="primary" @click="handleSubmit">分配</el-button>
+        <el-button @click="handleReassign">重新分配</el-button>
+      </div>
+    </template>
+  </el-dialog>
+</template>
+
+<script setup>
+import { ref, computed, watch } from "vue";
+import { assignCard } from "@/api/dz/cards";
+
+const props = defineProps({
+  modelValue: {
+    type: Boolean,
+    default: false,
+  },
+  agentList: {
+    type: Array,
+    default: () => [],
+  },
+  schoolList: {
+    type: Array,
+    default: () => [],
+  },
+  cardTypeOptions: {
+    type: Array,
+    default: () => [],
+  },
+});
+
+const emit = defineEmits(["update:modelValue", "success"]);
+
+const visible = computed({
+  get: () => props.modelValue,
+  set: (value) => emit("update:modelValue", value),
+});
+
+const assignCardFormRef = ref();
+
+const form = ref({
+  cardType: null,
+  beginCardNo: "",
+  endCardNo: "",
+  agentId: null,
+  province: "all",
+  school: "all",
+  studentCategory: "all",
+});
+
+const rules = {
+  cardType: [{ required: true, message: "卡类型不能为空", trigger: "change" }],
+  beginCardNo: [
+    { required: true, message: "开始卡号不能为空", trigger: "blur" },
+  ],
+  endCardNo: [{ required: true, message: "结束卡号不能为空", trigger: "blur" }],
+  agentId: [{ required: true, message: "代理商不能为空", trigger: "change" }],
+};
+
+// 重置表单
+function resetForm() {
+  form.value = {
+    cardType: null,
+    beginCardNo: "",
+    endCardNo: "",
+    agentId: null,
+    province: "all",
+    school: "all",
+    studentCategory: "all",
+  };
+}
+
+// 取消
+function handleCancel() {
+  visible.value = false;
+  resetForm();
+}
+
+// 提交
+async function handleSubmit() {
+  assignCardFormRef.value.validate(async (valid) => {
+    if (valid) {
+      try {
+        // 获取开始号和结束号
+        const begin = form.value.beginCardNo;
+        const end = form.value.endCardNo;
+
+        if (!begin || !end) {
+          throw new Error("请填写完整的卡号段");
+        }
+
+        // 调用分配卡API
+        await assignCard(form.value.agentId, begin, end);
+
+        emit("success", "分配卡成功!");
+        visible.value = false;
+        resetForm();
+      } catch (error) {
+        console.error("分配卡失败:", error);
+        throw error;
+      }
+    }
+  });
+}
+
+// 重新分配
+function handleReassign() {
+  resetForm();
+  emit("success", "已重置分配表单,请重新填写");
+}
+
+// 监听弹窗关闭,重置表单
+watch(visible, (newVal) => {
+  if (!newVal) {
+    resetForm();
+  }
+});
+</script>
+
+<style scoped>
+.dialog-footer {
+  text-align: right;
+}
+</style>

+ 166 - 0
back-ui/src/views/dz/cards/components/AssociateCampusDialog.vue

@@ -0,0 +1,166 @@
+<template>
+  <el-dialog
+    title="关联校区"
+    v-model="visible"
+    width="500px"
+    append-to-body
+    align-center
+    :center="true"
+  >
+    <div class="associate-campus-content">
+      <el-form
+        ref="associateCampusFormRef"
+        :model="form"
+        :rules="rules"
+        label-width="100px"
+      >
+        <!-- 卡号段 -->
+        <el-form-item label="卡号段" prop="cardRange" :required="true">
+          <div style="display: flex; align-items: center; gap: 10px">
+            <el-input
+              v-model="form.beginCardNo"
+              placeholder="开始卡号"
+              style="flex: 1"
+            />
+            <span>-</span>
+            <el-input
+              v-model="form.endCardNo"
+              placeholder="结束卡号"
+              style="flex: 1"
+            />
+          </div>
+        </el-form-item>
+
+        <!-- 校区 -->
+        <el-form-item label="校区" prop="campusId" :required="true">
+          <el-select
+            v-model="form.campusId"
+            placeholder="请选择校区"
+            style="width: 100%"
+          >
+            <el-option label="全部" value="all"></el-option>
+            <el-option
+              v-for="campus in campusList"
+              :key="campus.id"
+              :label="campus.name"
+              :value="campus.id"
+            ></el-option>
+          </el-select>
+        </el-form-item>
+      </el-form>
+    </div>
+
+    <template #footer>
+      <div class="dialog-footer">
+        <el-button @click="handleCancel">取消</el-button>
+        <el-button type="primary" @click="handleConfirm">
+          <svg-icon
+            icon-class="lightning"
+            class="mr-1"
+            style="font-size: 14px"
+          />
+          确认
+        </el-button>
+      </div>
+    </template>
+  </el-dialog>
+</template>
+
+<script setup>
+import { ref, computed, watch } from "vue";
+
+const props = defineProps({
+  modelValue: {
+    type: Boolean,
+    default: false,
+  },
+  campusList: {
+    type: Array,
+    default: () => [],
+  },
+});
+
+const emit = defineEmits(["update:modelValue", "confirm"]);
+
+const visible = computed({
+  get: () => props.modelValue,
+  set: (value) => emit("update:modelValue", value),
+});
+
+const associateCampusFormRef = ref();
+
+const form = ref({
+  beginCardNo: "",
+  endCardNo: "",
+  campusId: null,
+});
+
+const rules = {
+  beginCardNo: [
+    { required: true, message: "开始卡号不能为空", trigger: "blur" },
+  ],
+  endCardNo: [{ required: true, message: "结束卡号不能为空", trigger: "blur" }],
+  campusId: [{ required: true, message: "校区不能为空", trigger: "change" }],
+};
+
+// 重置表单
+function resetForm() {
+  form.value = {
+    beginCardNo: "",
+    endCardNo: "",
+    campusId: null,
+  };
+}
+
+// 取消
+function handleCancel() {
+  visible.value = false;
+  resetForm();
+}
+
+// 确认关联
+async function handleConfirm() {
+  associateCampusFormRef.value.validate(async (valid) => {
+    if (valid) {
+      try {
+        const data = {
+          beginCardNo: form.value.beginCardNo,
+          endCardNo: form.value.endCardNo,
+          campusId: form.value.campusId,
+        };
+
+        emit("confirm", data);
+        visible.value = false;
+        resetForm();
+      } catch (error) {
+        console.error("关联校区失败:", error);
+        throw error;
+      }
+    }
+  });
+}
+
+// 监听弹窗关闭,重置表单
+watch(visible, (newVal) => {
+  if (!newVal) {
+    resetForm();
+  }
+});
+</script>
+
+<style scoped>
+.associate-campus-content {
+  padding: 20px 0;
+}
+
+.hint-text {
+  color: #1890ff;
+  font-size: 12px;
+  margin-bottom: 20px;
+  padding-left: 100px;
+}
+
+.dialog-footer {
+  text-align: right;
+}
+</style>

+ 225 - 0
back-ui/src/views/dz/cards/components/CardGenerationDialog.vue

@@ -0,0 +1,225 @@
+<template>
+  <el-dialog
+    title="制卡"
+    v-model="visible"
+    width="500px"
+    append-to-body
+    align-center
+    :center="true"
+  >
+    <el-form
+      ref="cardGenerationFormRef"
+      :model="form"
+      :rules="rules"
+      label-width="100px"
+    >
+      <!-- 机构 -->
+      <el-form-item label="机构" prop="institutionId" :required="true">
+        <el-select
+          v-model="form.institutionId"
+          placeholder="请选择机构"
+          style="width: 100%"
+        >
+          <el-option
+            v-for="institution in institutionList"
+            :key="institution.deptId"
+            :label="institution.deptName"
+            :value="institution.deptId"
+          ></el-option>
+        </el-select>
+      </el-form-item>
+
+      <!-- 卡类型 -->
+      <el-form-item label="卡类型" prop="type" :required="true">
+        <el-select
+          v-model="form.type"
+          placeholder="请选择卡类型"
+          style="width: 100%"
+        >
+          <el-option
+            v-for="dict in cardTypeOptions"
+            :key="dict.value"
+            :label="dict.label"
+            :value="parseInt(dict.value)"
+          ></el-option>
+        </el-select>
+      </el-form-item>
+
+      <!-- 省份 -->
+      <el-form-item label="省份">
+        <el-select
+          v-model="form.province"
+          placeholder="请选择省份"
+          style="width: 100%"
+        >
+          <el-option label="全部" value="all"></el-option>
+          <el-option label="湖南" value="hunan"></el-option>
+          <el-option label="广东" value="guangdong"></el-option>
+          <el-option label="北京" value="beijing"></el-option>
+          <el-option label="上海" value="shanghai"></el-option>
+        </el-select>
+      </el-form-item>
+
+      <!-- 学校 -->
+      <el-form-item label="学校">
+        <el-select
+          v-model="form.school"
+          placeholder="请选择学校"
+          style="width: 100%"
+        >
+          <el-option label="全部" value="all"></el-option>
+          <el-option
+            v-for="school in schoolList"
+            :key="school.id"
+            :label="school.name"
+            :value="school.id"
+          ></el-option>
+        </el-select>
+      </el-form-item>
+
+      <!-- 卡数量 -->
+      <el-form-item label="卡数量" prop="cardQuantity" :required="true">
+        <el-input-number
+          v-model="form.cardQuantity"
+          :min="1"
+          :max="1000"
+          placeholder="请输入卡数量"
+          style="width: 100%"
+        />
+      </el-form-item>
+    </el-form>
+
+    <template #footer>
+      <div class="dialog-footer">
+        <el-button @click="handleCancel">取消</el-button>
+        <el-button type="primary" @click="handleSubmit">开始制卡</el-button>
+      </div>
+    </template>
+  </el-dialog>
+</template>
+
+<script setup>
+import { ref, computed, watch } from "vue";
+import { issueCard } from "@/api/dz/cards";
+
+const props = defineProps({
+  modelValue: {
+    type: Boolean,
+    default: false,
+  },
+  institutionList: {
+    type: Array,
+    default: () => [],
+  },
+  schoolList: {
+    type: Array,
+    default: () => [],
+  },
+  cardTypeOptions: {
+    type: Array,
+    default: () => [],
+  },
+});
+
+const emit = defineEmits(["update:modelValue", "success"]);
+
+const visible = computed({
+  get: () => props.modelValue,
+  set: (value) => emit("update:modelValue", value),
+});
+
+const cardGenerationFormRef = ref();
+
+const form = ref({
+  institutionId: null,
+  type: null,
+  province: "all",
+  school: "all",
+  cardQuantity: 1,
+});
+
+const rules = {
+  institutionId: [
+    { required: true, message: "机构不能为空", trigger: "change" },
+  ],
+  type: [{ required: true, message: "卡类型不能为空", trigger: "change" }],
+  cardQuantity: [
+    { required: true, message: "卡数量不能为空", trigger: "blur" },
+    {
+      type: "number",
+      min: 1,
+      max: 1000,
+      message: "请输入正确的卡数量",
+      trigger: "blur",
+    },
+  ],
+};
+
+// 获取卡类型枚举
+function getCardTypeEnum(typeValue) {
+  const cardTypeMap = {
+    6: "Platform",
+    2: "Dept",
+    7: "ECard",
+    8: "Test",
+  };
+  return cardTypeMap[typeValue] || "ECard"; // 默认返回电子卡
+}
+
+// 重置表单
+function resetForm() {
+  form.value = {
+    institutionId: null,
+    type: null,
+    province: "all",
+    school: "all",
+    cardQuantity: 1,
+  };
+}
+
+// 取消
+function handleCancel() {
+  visible.value = false;
+  resetForm();
+}
+
+// 提交
+async function handleSubmit() {
+  cardGenerationFormRef.value.validate(async (valid) => {
+    if (valid) {
+      try {
+        const cardData = {
+          institutionId: form.value.institutionId,
+          type: form.value.type,
+          count: parseInt(form.value.cardQuantity),
+        };
+        await issueCard(
+          cardData.institutionId,
+          getCardTypeEnum(cardData.type),
+          cardData.count
+        );
+
+        emit("success", "制卡成功!");
+        visible.value = false;
+        resetForm();
+      } catch (error) {
+        console.error("制卡失败:", error);
+        throw error;
+      }
+    }
+  });
+}
+
+// 监听弹窗关闭,重置表单
+watch(visible, (newVal) => {
+  if (!newVal) {
+    resetForm();
+  }
+});
+</script>
+
+<style scoped>
+.dialog-footer {
+  text-align: right;
+}
+</style>

+ 91 - 0
back-ui/src/views/dz/cards/components/CloseCardDialog.vue

@@ -0,0 +1,91 @@
+<template>
+  <el-dialog
+    title="关卡"
+    v-model="visible"
+    width="400px"
+    append-to-body
+    align-center
+    :center="true"
+  >
+    <div class="close-card-confirm-content">
+      <div class="confirm-item">
+        <el-icon class="warning-icon">
+          <Warning />
+        </el-icon>
+        <span class="confirm-text">
+          是否确认关卡vip卡编号为{{ cardNo }}的数据项?
+        </span>
+      </div>
+    </div>
+
+    <template #footer>
+      <div class="dialog-footer">
+        <el-button @click="handleCancel">取消</el-button>
+        <el-button type="primary" @click="handleConfirm">确定</el-button>
+      </div>
+    </template>
+  </el-dialog>
+</template>
+
+<script setup>
+import { ref, computed } from "vue";
+import { Warning } from "@element-plus/icons-vue";
+
+const props = defineProps({
+  modelValue: {
+    type: Boolean,
+    default: false,
+  },
+  cardNo: {
+    type: [String, Number],
+    default: "",
+  },
+});
+
+const emit = defineEmits(["update:modelValue", "confirm"]);
+
+const visible = computed({
+  get: () => props.modelValue,
+  set: (value) => emit("update:modelValue", value),
+});
+
+// 取消
+function handleCancel() {
+  visible.value = false;
+}
+
+// 确认关卡
+function handleConfirm() {
+  emit("confirm", props.cardNo);
+  visible.value = false;
+}
+</script>
+
+<style scoped>
+.close-card-confirm-content {
+  padding: 20px 0;
+}
+
+.confirm-item {
+  display: flex;
+  align-items: flex-start;
+  gap: 12px;
+}
+
+.warning-icon {
+  color: #e6a23c;
+  font-size: 20px;
+  margin-top: 2px;
+  flex-shrink: 0;
+}
+
+.confirm-text {
+  font-size: 14px;
+  line-height: 1.5;
+  color: #ffffff;
+}
+
+.dialog-footer {
+  text-align: right;
+}
+</style>

+ 201 - 0
back-ui/src/views/dz/cards/components/EditStudentDialog.vue

@@ -0,0 +1,201 @@
+<template>
+  <el-dialog title="编辑" v-model="visible" width="500px" append-to-body>
+    <el-form ref="formRef" :model="form" :rules="rules" label-width="100px">
+      <!-- 学校 -->
+      <el-form-item label="学校" prop="schoolId">
+        <el-select
+          v-model="form.schoolId"
+          placeholder="请选择学校"
+          style="width: 100%"
+        >
+          <el-option
+            v-for="school in schoolList"
+            :key="school.id"
+            :label="school.name"
+            :value="school.id"
+          ></el-option>
+        </el-select>
+      </el-form-item>
+
+      <!-- 班级 -->
+      <el-form-item label="班级" prop="classId">
+        <el-select
+          v-model="form.classId"
+          placeholder="请选择班级"
+          style="width: 100%"
+        >
+          <el-option
+            v-for="classItem in classList"
+            :key="classItem.id"
+            :label="classItem.name"
+            :value="classItem.id"
+          ></el-option>
+        </el-select>
+      </el-form-item>
+
+      <!-- 姓名 -->
+      <el-form-item label="姓名" prop="nickName">
+        <el-input v-model="form.nickName" placeholder="请输入姓名" />
+      </el-form-item>
+
+      <!-- 手机号 -->
+      <el-form-item label="手机号" prop="phone">
+        <el-input v-model="form.phone" placeholder="请输入手机号" />
+      </el-form-item>
+
+      <!-- 语数英 -->
+      <el-form-item label="语数英" prop="chineseMathEnglish">
+        <el-input
+          v-model="form.chineseMathEnglish"
+          placeholder="请输入语数英成绩"
+        />
+      </el-form-item>
+
+      <!-- 职业技能 -->
+      <el-form-item label="职业技能" prop="vocationalSkills">
+        <el-input
+          v-model="form.vocationalSkills"
+          placeholder="请输入职业技能成绩"
+        />
+      </el-form-item>
+    </el-form>
+
+    <template #footer>
+      <div class="dialog-footer">
+        <el-button type="primary" @click="handleSubmit">保 存</el-button>
+        <el-button @click="handleCancel">取 消</el-button>
+      </div>
+    </template>
+  </el-dialog>
+</template>
+
+<script setup>
+import { ref, computed, watch } from "vue";
+import { getCards, addCards, updateCards } from "@/api/dz/cards";
+
+const props = defineProps({
+  modelValue: {
+    type: Boolean,
+    default: false,
+  },
+  cardId: {
+    type: [String, Number],
+    default: null,
+  },
+  schoolList: {
+    type: Array,
+    default: () => [],
+  },
+  classList: {
+    type: Array,
+    default: () => [],
+  },
+});
+
+const emit = defineEmits(["update:modelValue", "success"]);
+
+const visible = computed({
+  get: () => props.modelValue,
+  set: (value) => emit("update:modelValue", value),
+});
+
+const formRef = ref();
+
+const form = ref({
+  cardId: null,
+  schoolId: null,
+  classId: null,
+  nickName: "",
+  phone: "",
+  chineseMathEnglish: "",
+  vocationalSkills: "",
+});
+
+const rules = {
+  schoolId: [{ required: true, message: "学校不能为空", trigger: "change" }],
+  classId: [{ required: true, message: "班级不能为空", trigger: "change" }],
+  nickName: [{ required: true, message: "姓名不能为空", trigger: "blur" }],
+  phone: [
+    { required: true, message: "手机号不能为空", trigger: "blur" },
+    {
+      pattern: /^1[3-9]\d{9}$/,
+      message: "请输入正确的手机号",
+      trigger: "blur",
+    },
+  ],
+  chineseMathEnglish: [
+    { required: true, message: "语数英成绩不能为空", trigger: "blur" },
+  ],
+  vocationalSkills: [
+    { required: true, message: "职业技能成绩不能为空", trigger: "blur" },
+  ],
+};
+
+// 重置表单
+function resetForm() {
+  form.value = {
+    cardId: null,
+    schoolId: null,
+    classId: null,
+    nickName: "",
+    phone: "",
+    chineseMathEnglish: "",
+    vocationalSkills: "",
+  };
+}
+
+// 取消
+function handleCancel() {
+  visible.value = false;
+  resetForm();
+}
+
+// 提交
+async function handleSubmit() {
+  formRef.value.validate(async (valid) => {
+    if (valid) {
+      try {
+        if (form.value.cardId != null) {
+          await updateCards(form.value);
+        } else {
+          await addCards(form.value);
+        }
+
+        emit("success", "保存成功!");
+        visible.value = false;
+        resetForm();
+      } catch (error) {
+        console.error("保存失败:", error);
+        throw error;
+      }
+    }
+  });
+}
+
+// 获取卡片数据
+async function fetchCardData() {
+  if (props.cardId) {
+    try {
+      const response = await getCards(props.cardId);
+      form.value = response.data;
+    } catch (error) {
+      console.error("获取卡片数据失败:", error);
+    }
+  }
+}
+
+// 监听弹窗打开,获取数据
+watch(visible, (newVal) => {
+  if (newVal && props.cardId) {
+    fetchCardData();
+  } else if (!newVal) {
+    resetForm();
+  }
+});
+</script>
+
+<style scoped>
+.dialog-footer {
+  text-align: right;
+}
+</style>

+ 91 - 0
back-ui/src/views/dz/cards/components/PaymentDialog.vue

@@ -0,0 +1,91 @@
+<template>
+  <el-dialog
+    title="缴费"
+    v-model="visible"
+    width="400px"
+    append-to-body
+    align-center
+    :center="true"
+  >
+    <div class="payment-confirm-content">
+      <div class="confirm-item">
+        <el-icon class="warning-icon">
+          <Warning />
+        </el-icon>
+        <span class="confirm-text">
+          是否确认缴费vip卡编号为{{ cardNo }}的数据项?
+        </span>
+      </div>
+    </div>
+
+    <template #footer>
+      <div class="dialog-footer">
+        <el-button @click="handleCancel">取消</el-button>
+        <el-button type="primary" @click="handleConfirm">确定</el-button>
+      </div>
+    </template>
+  </el-dialog>
+</template>
+
+<script setup>
+import { ref, computed } from "vue";
+import { Warning } from "@element-plus/icons-vue";
+
+const props = defineProps({
+  modelValue: {
+    type: Boolean,
+    default: false,
+  },
+  cardNo: {
+    type: [String, Number],
+    default: "",
+  },
+});
+
+const emit = defineEmits(["update:modelValue", "confirm"]);
+
+const visible = computed({
+  get: () => props.modelValue,
+  set: (value) => emit("update:modelValue", value),
+});
+
+// 取消
+function handleCancel() {
+  visible.value = false;
+}
+
+// 确认缴费
+function handleConfirm() {
+  emit("confirm", props.cardNo);
+  visible.value = false;
+}
+</script>
+
+<style scoped>
+.payment-confirm-content {
+  padding: 20px 0;
+}
+
+.confirm-item {
+  display: flex;
+  align-items: flex-start;
+  gap: 12px;
+}
+
+.warning-icon {
+  color: #e6a23c;
+  font-size: 20px;
+  margin-top: 2px;
+  flex-shrink: 0;
+}
+
+.confirm-text {
+  font-size: 14px;
+  line-height: 1.5;
+  color: #ffffff;
+}
+
+.dialog-footer {
+  text-align: right;
+}
+</style>

+ 91 - 0
back-ui/src/views/dz/cards/components/RefundDialog.vue

@@ -0,0 +1,91 @@
+<template>
+  <el-dialog
+    title="退费"
+    v-model="visible"
+    width="400px"
+    append-to-body
+    align-center
+    :center="true"
+  >
+    <div class="refund-confirm-content">
+      <div class="confirm-item">
+        <el-icon class="warning-icon">
+          <Warning />
+        </el-icon>
+        <span class="confirm-text">
+          是否确认退费vip卡编号为{{ cardNo }}的数据项?
+        </span>
+      </div>
+    </div>
+
+    <template #footer>
+      <div class="dialog-footer">
+        <el-button @click="handleCancel">取消</el-button>
+        <el-button type="primary" @click="handleConfirm">确定</el-button>
+      </div>
+    </template>
+  </el-dialog>
+</template>
+
+<script setup>
+import { ref, computed } from "vue";
+import { Warning } from "@element-plus/icons-vue";
+
+const props = defineProps({
+  modelValue: {
+    type: Boolean,
+    default: false,
+  },
+  cardNo: {
+    type: [String, Number],
+    default: "",
+  },
+});
+
+const emit = defineEmits(["update:modelValue", "confirm"]);
+
+const visible = computed({
+  get: () => props.modelValue,
+  set: (value) => emit("update:modelValue", value),
+});
+
+// 取消
+function handleCancel() {
+  visible.value = false;
+}
+
+// 确认退费
+function handleConfirm() {
+  emit("confirm", props.cardNo);
+  visible.value = false;
+}
+</script>
+
+<style scoped>
+.refund-confirm-content {
+  padding: 20px 0;
+}
+
+.confirm-item {
+  display: flex;
+  align-items: flex-start;
+  gap: 12px;
+}
+
+.warning-icon {
+  color: #e6a23c;
+  font-size: 20px;
+  margin-top: 2px;
+  flex-shrink: 0;
+}
+
+.confirm-text {
+  font-size: 14px;
+  line-height: 1.5;
+  color: #ffffff;
+}
+
+.dialog-footer {
+  text-align: right;
+}
+</style>

+ 91 - 0
back-ui/src/views/dz/cards/components/ReopenCardDialog.vue

@@ -0,0 +1,91 @@
+<template>
+  <el-dialog
+    title="重开"
+    v-model="visible"
+    width="400px"
+    append-to-body
+    align-center
+    :center="true"
+  >
+    <div class="reopen-card-confirm-content">
+      <div class="confirm-item">
+        <el-icon class="warning-icon">
+          <Warning />
+        </el-icon>
+        <span class="confirm-text">
+          是否确认重开VIP卡编号为{{ cardNo }}的数据项?
+        </span>
+      </div>
+    </div>
+
+    <template #footer>
+      <div class="dialog-footer">
+        <el-button @click="handleCancel">取消</el-button>
+        <el-button type="primary" @click="handleConfirm">确定</el-button>
+      </div>
+    </template>
+  </el-dialog>
+</template>
+
+<script setup>
+import { ref, computed } from "vue";
+import { Warning } from "@element-plus/icons-vue";
+
+const props = defineProps({
+  modelValue: {
+    type: Boolean,
+    default: false,
+  },
+  cardNo: {
+    type: [String, Number],
+    default: "",
+  },
+});
+
+const emit = defineEmits(["update:modelValue", "confirm"]);
+
+const visible = computed({
+  get: () => props.modelValue,
+  set: (value) => emit("update:modelValue", value),
+});
+
+// 取消
+function handleCancel() {
+  visible.value = false;
+}
+
+// 确认重开
+function handleConfirm() {
+  emit("confirm", props.cardNo);
+  visible.value = false;
+}
+</script>
+
+<style scoped>
+.reopen-card-confirm-content {
+  padding: 20px 0;
+}
+
+.confirm-item {
+  display: flex;
+  align-items: flex-start;
+  gap: 12px;
+}
+
+.warning-icon {
+  color: #e6a23c;
+  font-size: 20px;
+  margin-top: 2px;
+  flex-shrink: 0;
+}
+
+.confirm-text {
+  font-size: 14px;
+  line-height: 1.5;
+  color: #ffffff;
+}
+
+.dialog-footer {
+  text-align: right;
+}
+</style>

+ 222 - 136
back-ui/src/views/dz/cards/config/form.js

@@ -1,206 +1,292 @@
 const info = [
+  // 第一行
   {
-    label: '省市区',
-    name: 'areaIds',
+    label: "省市区",
+    name: "areaIds",
     value: [],
-    type: 'slot',
-    slotName: 'areaSelect',
+    type: "slot",
+    slotName: "areaSelect",
     search: true,
   },
   {
-    label: '分配学校',
-    name: 'assignSchoolId',
-    value: '',
-    type: 'select',
+    label: "分配学校",
+    name: "assignSchoolId",
+    value: "",
+    type: "select",
     option: [],
-    optionLabel: 'name',
-    optionValue: 'id',
+    optionLabel: "name",
+    optionValue: "id",
     search: true,
   },
   {
-    label: '卡状态',
-    name: 'status',
-    value: '',
-    type: 'select',
+    label: "注册学校",
+    name: "registerSchoolId",
+    value: "",
+    type: "select",
     option: [],
-    optionLabel: 'label',
-    optionValue: 'value',
+    optionLabel: "name",
+    optionValue: "id",
     search: true,
   },
   {
-    label: '分配状态',
-    name: 'distributeStatus',
-    value: '',
-    type: 'select',
+    label: "校区",
+    name: "campusId",
+    value: "",
+    type: "select",
     option: [],
-    optionLabel: 'label',
-    optionValue: 'value',
+    optionLabel: "name",
+    optionValue: "id",
     search: true,
   },
   {
-    label: '过期状态',
-    name: 'timeStatus',
-    value: '',
-    type: 'select',
+    label: "代理商",
+    name: "agentId",
+    value: "",
+    type: "select",
     option: [],
-    optionLabel: 'label',
-    optionValue: 'value',
+    optionLabel: "name",
+    optionValue: "id",
     search: true,
   },
+
+  // 第二行
   {
-    label: '缴费状态',
-    name: 'payStatus',
-    value: '',
-    type: 'select',
-    option: [],
-    optionLabel: 'label',
-    optionValue: 'value',
+    label: "账号",
+    name: "cardNo",
+    value: "",
+    type: "text",
     search: true,
   },
   {
-    label: '是否结算',
-    name: 'isSettlement',
-    value: '',
-    type: 'select',
-    option: [],
-    optionLabel: 'label',
-    optionValue: 'value',
+    label: "初始密码",
+    name: "password",
+    value: "",
+    type: "text",
     search: true,
   },
   {
-    label: '一级代理商ID',
-    name: 'agentId',
-    value: '',
-    type: 'text',
+    label: "卡号段",
+    name: "cardNoRange",
+    value: "",
+    type: "text",
     search: true,
+    placeholder: "开始卡号-结束卡号",
   },
   {
-    label: '末级代理商ID',
-    name: 'leftAgentId',
-    value: '',
-    type: 'text',
+    label: "卡类型",
+    name: "type",
+    value: "",
+    type: "select",
+    option: [],
+    optionLabel: "label",
+    optionValue: "value",
     search: true,
   },
   {
-    label: '安排校区',
-    name: 'campusId',
-    value: '',
-    type: 'text',
+    label: "分配考生类别",
+    name: "studentCategory",
+    value: "",
+    type: "select",
+    option: [],
+    optionLabel: "label",
+    optionValue: "value",
     search: true,
   },
+
+  // 第三行
   {
-    label: '分配学校',
-    name: 'assignSchoolId',
-    value: '',
-    type: 'text',
+    label: "分配状态",
+    name: "distributeStatus",
+    value: "",
+    type: "select",
+    option: [],
+    optionLabel: "label",
+    optionValue: "value",
     search: true,
   },
   {
-    label: '校区id',
-    name: 'schoolId',
-    value: '',
-    type: 'text',
+    label: "使用状态",
+    name: "status",
+    value: "",
+    type: "select",
+    option: [],
+    optionLabel: "label",
+    optionValue: "value",
     search: true,
   },
   {
-    label: '校区班级ID',
-    name: 'classesId',
-    value: '',
-    type: 'text',
+    label: "过期状态",
+    name: "timeStatus",
+    value: "",
+    type: "select",
+    option: [],
+    optionLabel: "label",
+    optionValue: "value",
     search: true,
   },
   {
-    label: '班级/入学年份',
-    name: 'year',
-    value: '',
-    type: 'text',
+    label: "结束状态",
+    name: "isSettlement",
+    value: "",
+    type: "select",
+    option: [
+      { label: "是", value: 1 },
+      { label: "否", value: 0 },
+    ],
+    optionLabel: "label",
+    optionValue: "value",
     search: true,
   },
+
+  // 其他字段(不在搜索表单显示但保留的)
   {
-    label: '高考年份',
-    name: 'endYear',
-    value: '',
-    type: 'text',
-    search: true,
+    label: "卡号",
+    name: "cardNo",
+    value: "",
+    type: "text",
+    search: false,
   },
   {
-    label: '开卡ID',
-    name: 'openId',
-    value: '',
-    type: 'text',
-    search: true,
+    label: "密码",
+    name: "password",
+    value: "",
+    type: "text",
+    search: false,
   },
   {
-    label: '分配时间',
-    name: 'distributeTime',
-    value: '',
-    type: 'date',
-    dateType: 'date',
-    valueFormat: 'YYYY-MM-DD',
-    search: true,
+    label: "末级代理商ID",
+    name: "leftAgentId",
+    value: "",
+    type: "select",
+    option: [],
+    optionLabel: "name",
+    optionValue: "id",
+    search: false,
   },
   {
-    label: '过期时间',
-    name: 'outDate',
-    value: '',
-    type: 'date',
-    dateType: 'date',
-    valueFormat: 'YYYY-MM-DD',
-    search: true,
+    label: "学校班级ID",
+    name: "schoolId",
+    value: "",
+    type: "text",
+    search: false,
   },
   {
-    label: '开卡时间',
-    name: 'openTime',
-    value: '',
-    type: 'date',
-    dateType: 'date',
-    valueFormat: 'YYYY-MM-DD',
-    search: true,
+    label: "班级ID",
+    name: "classId",
+    value: "",
+    type: "text",
+    search: false,
   },
   {
-    label: '缴费时间',
-    name: 'payTime',
-    value: '',
-    type: 'date',
-    dateType: 'date',
-    valueFormat: 'YYYY-MM-DD',
-    search: true,
+    label: "入学年份",
+    name: "year",
+    value: "",
+    type: "text",
+    search: false,
   },
   {
-    label: '激活时间',
-    name: 'activeTime',
-    value: '',
-    type: 'date',
-    dateType: 'date',
-    valueFormat: 'YYYY-MM-DD',
-    search: true,
+    label: "高考年份",
+    name: "endYear",
+    value: "",
+    type: "text",
+    search: false,
   },
   {
-    label: '结算时间',
-    name: 'settlementTime',
-    value: '',
-    type: 'date',
-    dateType: 'date',
-    valueFormat: 'YYYY-MM-DD',
-    search: true,
+    label: "开卡ID",
+    name: "openId",
+    value: "",
+    type: "text",
+    search: false,
   },
   {
-    label: '退费时间',
-    name: 'refundTime',
-    value: '',
-    type: 'date',
-    dateType: 'date',
-    valueFormat: 'YYYY-MM-DD',
-    search: true,
+    label: "分配时间",
+    name: "distributeTime",
+    value: "",
+    type: "date",
+    dateType: "date",
+    valueFormat: "YYYY-MM-DD",
+    search: false,
   },
   {
-    label: '关卡时间',
-    name: 'closeTime',
-    value: '',
-    type: 'date',
-    dateType: 'date',
-    valueFormat: 'YYYY-MM-DD',
-    search: true,
+    label: "过期时间",
+    name: "outDate",
+    value: "",
+    type: "date",
+    dateType: "date",
+    valueFormat: "YYYY-MM-DD",
+    search: false,
+  },
+  {
+    label: "开卡时间",
+    name: "openTime",
+    value: "",
+    type: "date",
+    dateType: "date",
+    valueFormat: "YYYY-MM-DD",
+    search: false,
+  },
+  {
+    label: "缴费时间",
+    name: "payTime",
+    value: "",
+    type: "date",
+    dateType: "date",
+    valueFormat: "YYYY-MM-DD",
+    search: false,
+  },
+  {
+    label: "激活时间",
+    name: "activeTime",
+    value: "",
+    type: "date",
+    dateType: "date",
+    valueFormat: "YYYY-MM-DD",
+    search: false,
+  },
+  {
+    label: "结算时间",
+    name: "settlementTime",
+    value: "",
+    type: "date",
+    dateType: "date",
+    valueFormat: "YYYY-MM-DD",
+    search: false,
+  },
+  {
+    label: "退费时间",
+    name: "refundTime",
+    value: "",
+    type: "date",
+    dateType: "date",
+    valueFormat: "YYYY-MM-DD",
+    search: false,
+  },
+  {
+    label: "关卡时间",
+    name: "closeTime",
+    value: "",
+    type: "date",
+    dateType: "date",
+    valueFormat: "YYYY-MM-DD",
+    search: false,
+  },
+  {
+    label: "创建时间",
+    name: "createTime",
+    value: "",
+    type: "date",
+    dateType: "date",
+    valueFormat: "YYYY-MM-DD",
+    search: false,
+  },
+  {
+    label: "更新时间",
+    name: "updateTime",
+    value: "",
+    type: "date",
+    dateType: "date",
+    valueFormat: "YYYY-MM-DD",
+    search: false,
   },
 ];
 

+ 187 - 0
back-ui/src/views/dz/cards/config/table.js

@@ -0,0 +1,187 @@
+const tableConfig = {
+  // 表格列配置
+  columns: [
+    {
+      label: "序号",
+      type: "index",
+      align: "center",
+      width: 80,
+    },
+    {
+      label: "卡号",
+      prop: "cardNo",
+      align: "center",
+      width: 120,
+      showOverflowTooltip: true,
+    },
+    {
+      label: "密码",
+      prop: "password",
+      align: "center",
+      width: 100,
+    },
+    {
+      label: "姓名—手机",
+      prop: "studentInfo",
+      align: "center",
+      type: "slot",
+      slotName: "studentInfo", // 自定义插槽显示姓名-手机
+      width: 150,
+    },
+    {
+      label: "卡类型",
+      prop: "type",
+      align: "center",
+      type: "dict",
+      options: [], // 动态设置
+      width: 100,
+    },
+    {
+      label: "分配学校",
+      prop: "assignSchoolId",
+      align: "center",
+      type: "dict",
+      options: [], // 动态设置学校选项
+      width: 120,
+    },
+    {
+      label: "注册学校",
+      prop: "schoolName",
+      align: "center",
+      type: "dict",
+      options: [], // 动态设置学校选项
+      width: 120,
+    },
+    {
+      label: "班级",
+      prop: "classId",
+      align: "center",
+      type: "dict",
+      options: [], // 动态设置班级选项
+      width: 100,
+    },
+    {
+      label: "校区",
+      prop: "campusId",
+      align: "center",
+      type: "dict",
+      options: [], // 动态设置校区选项
+      width: 100,
+    },
+    {
+      label: "校区班级",
+      prop: "classId",
+      align: "center",
+      type: "dict",
+      options: [], // 动态设置校区班级选项
+      width: 120,
+    },
+    {
+      label: "分配考生类型",
+      prop: "assignExamType",
+      align: "center",
+      type: "dict",
+      options: [], // 动态设置
+      width: 120,
+    },
+    {
+      label: "分配状态",
+      prop: "distributeStatus",
+      align: "center",
+      type: "dict",
+      options: [], // 动态设置
+      width: 100,
+    },
+    {
+      label: "使用状态",
+      prop: "status",
+      align: "center",
+      type: "dict",
+      options: [], // 动态设置
+      width: 100,
+    },
+    {
+      label: "过期状态",
+      prop: "timeStatus",
+      align: "center",
+      type: "dict",
+      options: [], // 动态设置
+      width: 100,
+    },
+    {
+      label: "结算状态",
+      prop: "isSettlement",
+      align: "center",
+      type: "dict",
+      options: [], // 动态设置
+      width: 100,
+    },
+    {
+      label: "激活时间",
+      prop: "activeTime",
+      align: "center",
+      type: "time",
+      format: "{y}-{m}-{d}",
+      width: 120,
+    },
+    {
+      label: "到期时间",
+      prop: "outDate",
+      align: "center",
+      type: "time",
+      format: "{y}-{m}-{d}",
+      width: 120,
+    },
+    {
+      label: "缴费时间",
+      prop: "payTime",
+      align: "center",
+      type: "time",
+      format: "{y}-{m}-{d}",
+      width: 120,
+    },
+    {
+      label: "开卡时间",
+      prop: "openTime",
+      align: "center",
+      type: "time",
+      format: "{y}-{m}-{d}",
+      width: 120,
+    },
+  ],
+
+  // 操作按钮配置
+  actions: [
+    {
+      key: "edit",
+      label: "修改",
+      type: "primary",
+      icon: "Edit",
+      link: true,
+      permission: ["dz:cards:edit"],
+    },
+    {
+      key: "delete",
+      label: "删除",
+      type: "primary",
+      icon: "Delete",
+      link: true,
+      permission: ["dz:cards:remove"],
+    },
+  ],
+
+  // 表格属性配置
+  tableProps: {
+    stripe: true,
+    border: false,
+    showSelection: false, // 移除多选列
+    showIndex: false, // 使用自定义序号列
+    showPagination: true,
+    actionWidth: "auto",
+    // 选择模式配置
+    selectionMode: "multiple", // 'single' | 'multiple' | 'none'
+    rowKey: "cardId", // 选择键字段名
+  },
+};
+
+export default tableConfig;

Dosya farkı çok büyük olduğundan ihmal edildi
+ 578 - 404
back-ui/src/views/dz/cards/index.vue


+ 0 - 1
back-ui/src/views/dz/classes/index.vue

@@ -195,7 +195,6 @@
   const { proxy } = getCurrentInstance()
   const { bool_values } = proxy.useDict('bool_values')
 
-
   const classesList = ref([])
   const schoolOptions = ref([])
   const open = ref(false)

+ 19 - 0
back-ui/src/views/dz/papers/components/paper-by-hand.vue

@@ -0,0 +1,19 @@
+<template>
+    <el-tabs type="border-card">
+        <el-tab-pane label="全量组卷">
+            <paper-hand-full />
+        </el-tab-pane>
+        <el-tab-pane label="定向组卷">
+            <paper-hand-exact />
+        </el-tab-pane>
+    </el-tabs>
+</template>
+
+<script setup name="PaperByHand">
+import PaperHandFull from "@/views/dz/papers/components/paper-hand-full.vue";
+import PaperHandExact from "@/views/dz/papers/components/paper-hand-exact.vue";
+</script>
+
+<style scoped>
+
+</style>

+ 19 - 0
back-ui/src/views/dz/papers/components/paper-by-intelligent.vue

@@ -0,0 +1,19 @@
+<template>
+    <el-tabs type="border-card">
+        <el-tab-pane label="全量组卷">
+            <paper-intelligent-full />
+        </el-tab-pane>
+        <el-tab-pane label="定向组卷">
+            <paper-intelligent-exact />
+        </el-tab-pane>
+    </el-tabs>
+</template>
+
+<script setup name="PaperByIntelligent">
+import PaperIntelligentFull from "@/views/dz/papers/components/paper-intelligent-full.vue";
+import PaperIntelligentExact from "@/views/dz/papers/components/paper-intelligent-exact.vue";
+</script>
+
+<style scoped>
+
+</style>

+ 47 - 0
back-ui/src/views/dz/papers/components/paper-exact-conditions.vue

@@ -0,0 +1,47 @@
+<template>
+    <div class="flex flex-row items-center gap-2">
+        <span :class="labelClass">省份:</span>
+        <el-select v-model="location" style="width: 280px">
+            <el-option v-for="p in provinces" :label="p.dictLabel" :value="p.dictValue"/>
+        </el-select>
+    </div>
+    <div class="flex flex-row items-center gap-2 mt-5">
+        <span :class="labelClass">考生类型:</span>
+        <el-select v-model="examType" style="width: 280px">
+            <el-option v-for="t in examTypes" :label="t.dictLabel" :value="t.dictValue"/>
+        </el-select>
+    </div>
+    <div class="flex flex-row items-center gap-2 mt-5">
+        <span :class="labelClass">院校:</span>
+        <el-select v-model="universityId" style="width: 280px">
+            <el-option v-for="u in universities" :label="u.name" :value="u.id"/>
+        </el-select>
+    </div>
+    <div class="flex flex-row items-center gap-2 mt-5">
+        <span :class="labelClass">专业组:</span>
+        <el-radio-group v-model="majorPlanId">
+            <el-radio-button v-for="m in majors" :label="m.majorGroup" :value="m.id"/>
+        </el-radio-group>
+    </div>
+    <div class="flex flex-row items-center gap-2 mt-5">
+        <span :class="labelClass">科目:</span>
+        <el-radio-group v-model="subjectId">
+            <el-radio-button v-for="s in subjects" :label="s.subjectName" :value="s.subjectId"/>
+        </el-radio-group>
+    </div>
+</template>
+
+<script setup name="PaperExactConditions">
+import {useInjectPaperExactCondition} from "@/views/dz/papers/hooks/usePaperExactCondition.js";
+
+const labelClass = 'break-keep text-sm font-bold w-[80px]'
+const {location, provinces,
+    examType, examTypes,
+    universityId, universities,
+    majorPlanId, majors,
+    subjectId, subjects} = useInjectPaperExactCondition()
+</script>
+
+<style scoped>
+
+</style>

+ 18 - 0
back-ui/src/views/dz/papers/components/paper-full-conditions.vue

@@ -0,0 +1,18 @@
+<template>
+    <div class="flex flex-row items-center gap-2">
+        <span class="break-keep text-sm font-bold">科目:</span>
+        <el-radio-group v-model="subjectId">
+            <el-radio-button v-for="s in subjects" :label="s.subjectName" :value="s.subjectId"/>
+        </el-radio-group>
+    </div>
+</template>
+
+<script setup name="PaperFullConditions">
+import {useInjectPaperFullCondition} from "@/views/dz/papers/hooks/usePaperFullCondition.js";
+
+const {subjectId, subjects} = useInjectPaperFullCondition()
+</script>
+
+<style scoped>
+
+</style>

+ 21 - 0
back-ui/src/views/dz/papers/components/paper-hand-exact.vue

@@ -0,0 +1,21 @@
+<template>
+    <paper-exact-conditions/>
+    <el-container class="mt-5">
+        <el-aside width="350px">
+            <paper-knowledge-tree exact-mode/>
+        </el-aside>
+        <el-main></el-main>
+    </el-container>
+</template>
+
+<script setup name="PaperHandExact">
+import PaperExactConditions from "@/views/dz/papers/components/paper-exact-conditions.vue";
+import PaperKnowledgeTree from "@/views/dz/papers/components/paper-knowledge-tree.vue";
+import {useProvidePaperExactCondition} from "@/views/dz/papers/hooks/usePaperExactCondition.js";
+
+useProvidePaperExactCondition()
+</script>
+
+<style scoped>
+
+</style>

+ 21 - 0
back-ui/src/views/dz/papers/components/paper-hand-full.vue

@@ -0,0 +1,21 @@
+<template>
+    <paper-full-conditions />
+    <el-container class="mt-5">
+        <el-aside width="350px">
+            <paper-knowledge-tree/>
+        </el-aside>
+        <el-main></el-main>
+    </el-container>
+</template>
+
+<script setup name="PaperHandFull">
+import PaperFullConditions from "@/views/dz/papers/components/paper-full-conditions.vue";
+import {useProvidePaperFullCondition} from "@/views/dz/papers/hooks/usePaperFullCondition.js";
+import PaperKnowledgeTree from "@/views/dz/papers/components/paper-knowledge-tree.vue";
+
+const {subjectId, knowledgeId} = useProvidePaperFullCondition()
+</script>
+
+<style scoped>
+
+</style>

+ 13 - 0
back-ui/src/views/dz/papers/components/paper-intelligent-exact.vue

@@ -0,0 +1,13 @@
+<template>
+
+</template>
+
+<script>
+export default {
+    name: "paper-intelligent-exact"
+}
+</script>
+
+<style scoped>
+
+</style>

+ 13 - 0
back-ui/src/views/dz/papers/components/paper-intelligent-form.vue

@@ -0,0 +1,13 @@
+<template>
+
+</template>
+
+<script>
+export default {
+    name: "paper-intelligent-form"
+}
+</script>
+
+<style scoped>
+
+</style>

+ 13 - 0
back-ui/src/views/dz/papers/components/paper-intelligent-full.vue

@@ -0,0 +1,13 @@
+<template>
+
+</template>
+
+<script>
+export default {
+    name: "paper-intelligent-full"
+}
+</script>
+
+<style scoped>
+
+</style>

+ 41 - 0
back-ui/src/views/dz/papers/components/paper-knowledge-tree.vue

@@ -0,0 +1,41 @@
+<template>
+    <el-input v-model="keyword" placeholder="请输入知识点名称" clearable prefix-icon="Search"
+              style="margin-bottom: 20px"/>
+    <el-tree :data="knowledges" :props="{ label: 'name', children: 'children' }" :expand-on-click-node="false"
+             :filter-node-method="filterNode" node-key="id" highlight-current default-expand-all
+             :show-checkbox="allowMultiple" @node-click="handleNodeClick" @check="handleNodeCheck"/>
+</template>
+
+<script setup name="PaperKnowledgeTree">
+import {useInjectPaperFullCondition} from "@/views/dz/papers/hooks/usePaperFullCondition.js";
+import {useInjectPaperExactCondition} from "@/views/dz/papers/hooks/usePaperExactCondition.js";
+
+const props = defineProps({
+    allowMultiple: Boolean,
+    exactMode: Boolean
+})
+
+const keyword = ref('')
+const {knowledgeId, knowledges, knowledgeCheckNodes} = props.exactMode
+    ? useInjectPaperExactCondition()
+    : useInjectPaperFullCondition()
+
+const filterNode = function (value, data) {
+    if (!value) return true
+    return data.name.indexOf(value) !== -1
+}
+
+const handleNodeClick = function (data) {
+    knowledgeId.value = data.id
+}
+
+const handleNodeCheck = function (data, {checkedNodes}) {
+    // 多选时,只保留叶子节点
+    knowledgeCheckNodes.value = checkedNodes.filter(k => !!!k.children?.length)
+    console.log('knowledgeCheckNodes', knowledgeCheckNodes.value)
+}
+</script>
+
+<style scoped>
+
+</style>

+ 10 - 0
back-ui/src/views/dz/papers/detail.vue

@@ -0,0 +1,10 @@
+<template>
+
+</template>
+
+<script setup name="PaperDetail">
+</script>
+
+<style scoped>
+
+</style>

+ 92 - 0
back-ui/src/views/dz/papers/hooks/usePaperExactCondition.js

@@ -0,0 +1,92 @@
+import {injectLocal, provideLocal} from "@vueuse/core";
+import {
+    getPaperExamTypes,
+    getPaperKnowledges,
+    getPaperMajors,
+    getPaperProvinces,
+    getPaperSubjects,
+    getPaperUniversities
+} from "@/api/dz/papers.js";
+
+const key = Symbol('PaperExactCondition')
+
+export const useProvidePaperExactCondition = function () {
+    const location = ref('')
+    const provinces = ref([])
+    const examType = ref('')
+    const examTypes = ref([])
+    const universityId = ref('')
+    const universities = ref([])
+    const majorPlanId = ref('')
+    const majors = ref([])
+
+    const subjects = ref([])
+    const subjectId = ref('')
+    const knowledges = ref([])
+    const knowledgeId = ref('') // 单选
+    const knowledgeCheckNodes = ref([]) // 多选的节点
+    const knowledgeIds = computed(() => knowledgeCheckNodes.value.map(k => k.id))
+
+    const payload = {
+        location, provinces, examType, examTypes, universityId, universities, majorPlanId, majors,
+        subjects, subjectId, knowledges, knowledgeId, knowledgeCheckNodes, knowledgeIds
+    }
+    provideLocal(key, payload)
+
+    // hooks
+    onMounted(async () => {
+        const res = await getPaperProvinces()
+        provinces.value = res.data
+    })
+    watch(location, async () => {
+        // clean
+        examType.value = ''
+        examTypes.value = []
+        universityId.value = ''
+        universities.value = []
+
+        if (!location.value) return
+        const resT = await getPaperExamTypes({location: toValue(location)})
+        examTypes.value = resT.data
+
+        const resU = await getPaperUniversities({location: toValue(location)})
+        universities.value = resU.data
+    })
+    watch([examType, universityId], async ([examType, universityId]) => {
+        // clean
+        majorPlanId.value = ''
+        majors.value = []
+
+        if (!examType || !universityId) return
+        const res = await getPaperMajors({location: toValue(location), examType, universityId})
+        majors.value = res.data
+        if (res.data.length) majorPlanId.value = res.data[0].id
+    })
+    watch(universityId, async (universityId) => {
+        // clean
+        subjects.value = []
+        subjectId.value = ''
+
+        if (!universityId) return
+        const res = await getPaperSubjects({universityId})
+        subjects.value = res.data
+        if (res.data.length) subjectId.value = res.data[0].subjectId
+    })
+    watch([majorPlanId, subjectId], async ([majorPlanId, subjectId]) => {
+        // clean
+        knowledges.value = []
+        knowledgeId.value = '' // 单选的情况
+        knowledgeCheckNodes.value = [] // 多选的情况
+
+        if (!subjectId || !majorPlanId) return
+        // 获取知识点数据
+        const res = await getPaperKnowledges({subjectId, majorPlanId})
+        knowledges.value = res.data
+    })
+
+    return payload
+}
+
+export const useInjectPaperExactCondition = function () {
+    return injectLocal(key)
+}

+ 39 - 0
back-ui/src/views/dz/papers/hooks/usePaperFullCondition.js

@@ -0,0 +1,39 @@
+import {injectLocal, provideLocal} from "@vueuse/core";
+import {getPaperKnowledges, getPaperSubjects} from "@/api/dz/papers.js";
+import {watch} from "vue";
+
+const key = Symbol('PaperFullCondition')
+
+export const useProvidePaperFullCondition = function () {
+    const subjects = ref([])
+    const subjectId = ref('')
+    const knowledges = ref([])
+    const knowledgeId = ref('') // 单选
+    const knowledgeCheckNodes = ref([]) // 多选的节点
+    const knowledgeIds = computed(() => knowledgeCheckNodes.value.map(k => k.id))
+
+    const payload = {subjectId, subjects, knowledgeId, knowledges, knowledgeCheckNodes, knowledgeIds}
+    provideLocal(key, payload)
+
+    onMounted(async () => {
+        const res = await getPaperSubjects()
+        subjects.value = res.data
+        // 给一个默认值
+        if (res.data.length) subjectId.value = res.data[0].subjectId
+    })
+    watch(subjectId, async () => {
+        // 先清空以前的知识点
+        knowledges.value = []
+        knowledgeId.value = '' // 单选的情况
+        knowledgeCheckNodes.value = [] // 多选的情况
+
+        // 获取知识点数据
+        const res = await getPaperKnowledges({subjectId: toValue(subjectId)})
+        knowledges.value = res.data
+    })
+    return payload
+}
+
+export const useInjectPaperFullCondition = function () {
+    return injectLocal(key)
+}

+ 16 - 2
back-ui/src/views/dz/papers/index.vue

@@ -1,5 +1,19 @@
 <template>
-  <div>组卷功能</div>
+  <div class="app-container">
+    <el-tabs>
+      <el-tab-pane label="手动组卷">
+        <paper-by-hand />
+      </el-tab-pane>
+      <el-tab-pane label="智能组卷">
+        <paper-by-intelligent />
+      </el-tab-pane>
+    </el-tabs>
+  </div>
 </template>
 
-<script setup></script>
+<script setup name="PaperIndex">
+import PaperByHand from "@/views/dz/papers/components/paper-by-hand.vue";
+import PaperByIntelligent from "@/views/dz/papers/components/paper-by-intelligent.vue";
+</script>
+
+<style scoped></style>

+ 5 - 4
back-ui/src/views/dz/school/index.vue

@@ -102,9 +102,10 @@
               <el-table-column label="归属机构" align="center" key="deptName" prop="dept.deptName" :show-overflow-tooltip="true" />
               <el-table-column label="省份" align="center" prop="location" />
 
-              <el-table-column label="省" align="center" prop="pro" />
-              <el-table-column label="市" align="center" prop="city" />
-              <el-table-column label="区" align="center" prop="area" />
+<!--              <el-table-column label="省" align="center" prop="pro" />-->
+<!--              <el-table-column label="市" align="center" prop="city" />-->
+<!--              <el-table-column label="区" align="center" prop="area" />-->
+              <el-table-column label="所属地区" align="center" prop="proCityAreaName" />
               <el-table-column label="状态" align="center" prop="status">
                 <template #default="scope">
                   <dict-tag :options="bool_values" :value="scope.row.status"/>
@@ -226,7 +227,7 @@
     },
     rules: {
       status: [
-        { required: true, message: "状态(0:无效,1:有效)不能为空", trigger: "change" },
+        { required: true, message: "状态不能为空", trigger: "change" },
         { required: true, message: "机构不能为空", trigger: "change" }
       ],
     }

+ 118 - 1
back-ui/src/views/dz/teacher/index.vue

@@ -90,6 +90,7 @@
       <el-table-column label="用户ID" align="center" prop="userId" />
       <el-table-column label="操作" align="center" class-name="small-padding fixed-width">
         <template #default="scope">
+          <el-button link type="primary" icon="" @click="handleUpdateTeacherClass(scope.row)" v-hasPermi="['dz:teacher:edit']">关联班级</el-button>
           <el-button link type="primary" icon="Edit" @click="handleUpdate(scope.row)" v-hasPermi="['dz:teacher:edit']">修改</el-button>
           <el-button link type="primary" icon="Delete" @click="handleDelete(scope.row)" v-hasPermi="['dz:teacher:remove']">删除</el-button>
         </template>
@@ -131,18 +132,50 @@
         </div>
       </template>
     </el-dialog>
+
+      <!-- 关联班级 -->
+      <el-dialog :title="titleTeacherClass" v-model="openTeacherClass" width="500px" append-to-body>
+          <el-form ref="teacherClassRef" :model="formTeacherClass" :rules="rulesTeacherClass" label-width="80px">
+<!--              <el-form-item label="教师ID" v-show="false" prop="teacherId">-->
+<!--                  <el-input v-model="formTeacherClass.teacherId" placeholder="请输入教师ID" disabled/>-->
+<!--              </el-form-item>-->
+              <el-form-item label="教师姓名" prop="name">
+                  <el-input v-model="formTeacherClass.name" placeholder="请输入教师姓名" disabled/>
+              </el-form-item>
+              <el-form-item label="关联班级" prop="classIds">
+                  <el-select v-model="formTeacherClass.classIds" multiple placeholder="请选择班级" style="width: 100%">
+                      <el-option
+                              v-for="item in classOptions"
+                              :key="item.classId"
+                              :label="item.name"
+                              :value="item.classId"
+                      />
+                  </el-select>
+              </el-form-item>
+          </el-form>
+          <template #footer>
+              <div class="dialog-footer">
+                  <el-button type="primary" @click="submitFormTeacherClass">确 定</el-button>
+                  <el-button @click="cancelTeacherClass">取 消</el-button>
+              </div>
+          </template>
+      </el-dialog>
   </div>
 </template>
 
 <script setup name="Teacher">
 import { listTeacher, getTeacher, delTeacher, addTeacher, updateTeacher } from "@/api/dz/teacher"
 import { listAllSchool } from "@/api/dz/school"
+import { listAllClass } from "@/api/dz/classes"
+import { batchBindTeacherClass,listAllTeacherClass } from "@/api/dz/teacherclass"
 
 const { proxy } = getCurrentInstance()
 
 const teacherList = ref([])
 const schoolOptions = ref([])
+const classOptions = ref([])
 const open = ref(false)
+const openTeacherClass = ref(false)
 const loading = ref(true)
 const showSearch = ref(true)
 const ids = ref([])
@@ -150,9 +183,15 @@ const single = ref(true)
 const multiple = ref(true)
 const total = ref(0)
 const title = ref("")
+const titleTeacherClass = ref("")
 
 const data = reactive({
   form: {},
+  formTeacherClass: {
+      teacherId: null,
+      name: null,
+      classIds: []
+  },
   queryParams: {
     pageNum: 1,
     pageSize: 10,
@@ -161,10 +200,16 @@ const data = reactive({
     name: null
   },
   rules: {
+  },
+  rulesTeacherClass: {
+      // 可以添加验证规则
+      classIds: [
+          { required: true, message: "请选择至少一个班级", trigger: "change" }
+      ]
   }
 })
 
-const { queryParams, form, rules } = toRefs(data)
+const { queryParams, form, formTeacherClass, rules, rulesTeacherClass } = toRefs(data)
 
 /** 查询老师列表 */
 function getList() {
@@ -183,12 +228,19 @@ function getSchoolList() {
   })
 }
 
+// function getClassListBySchool() {
+//     listAll().then(response => {
+//         classOptions.value = response.data || []
+//     })
+// }
+
 // 取消按钮
 function cancel() {
   open.value = false
   reset()
 }
 
+
 // 表单重置
 function reset() {
   form.value = {
@@ -200,6 +252,22 @@ function reset() {
   proxy.resetForm("teacherRef")
 }
 
+// 取消按钮
+function cancelTeacherClass() {
+    openTeacherClass.value = false
+    resetTeacherClass()
+}
+
+// 表单重置
+function resetTeacherClass() {
+    formTeacherClass.value = {
+        teacherId: null,
+        classIds: [],
+        name: null
+    }
+    proxy.resetForm("teacherClassRef")
+}
+
 /** 搜索按钮操作 */
 function handleQuery() {
   queryParams.value.pageNum = 1
@@ -276,9 +344,58 @@ function handleExport() {
   }, `teacher_${new Date().getTime()}.xlsx`)
 }
 
+/** 关联老师班级,按钮操作 */
+function handleUpdateTeacherClass(row) {
+    resetTeacherClass()
+    // const _schoolId = row.schoolId
+    const submitData = {
+        schoolId: row.schoolId,
+        teacherId: row.teacherId
+    }
+    listAllClass(submitData).then(response => {
+        classOptions.value = response.data || []
+
+        // 然后设置数据
+        formTeacherClass.value.teacherId = row.teacherId
+        formTeacherClass.value.name = row.name
+
+        listAllTeacherClass(submitData).then(response => {
+            formTeacherClass.value.classIds = (response.data || []).map(item => item.classId)
+        })
+
+        openTeacherClass.value = true
+        titleTeacherClass.value = "关联班级"
+    })
+}
+
+/** 关联老师班级,提交按钮 */
+function submitFormTeacherClass() {
+    proxy.$refs["teacherClassRef"].validate(valid => {
+        if (valid) {
+            // 构建提交参数
+            const submitData = {
+                teacherId: formTeacherClass.value.teacherId,
+                classIds: formTeacherClass.value.classIds
+            }
+            // console.log("submitData"+submitData)
+
+            // 调用关联班级的API
+            // 假设API函数名为 updateTeacherClassRelation
+            batchBindTeacherClass(submitData).then(response => {
+                proxy.$modal.msgSuccess("关联成功")
+                openTeacherClass.value = false
+                getList()
+            }).catch(() => {
+                proxy.$modal.msgError("关联失败")
+            })
+        }
+    })
+}
+
 onMounted(() => {
   getSchoolList()
   getList()
+  // getClassListBySchool()
 })
 
 </script>

+ 8 - 8
back-ui/src/views/dz/teacherclass/index.vue

@@ -9,9 +9,9 @@
           @keyup.enter="handleQuery"
         />
       </el-form-item>
-      <el-form-item label="班级id" prop="classesId">
+      <el-form-item label="班级id" prop="classId">
         <el-input
-          v-model="queryParams.classesId"
+          v-model="queryParams.classId"
           placeholder="请输入班级id"
           clearable
           @keyup.enter="handleQuery"
@@ -77,7 +77,7 @@
       <el-table-column type="selection" width="55" align="center" />
       <el-table-column label="标识" align="center" prop="id" />
       <el-table-column label="老师id" align="center" prop="teacherId" />
-      <el-table-column label="班级id" align="center" prop="classesId" />
+      <el-table-column label="班级id" align="center" prop="classId" />
       <el-table-column label="有效状态" align="center" prop="status" />
       <el-table-column label="备注" align="center" prop="remark" />
       <el-table-column label="结束日期" align="center" prop="outDate" width="180">
@@ -92,7 +92,7 @@
         </template>
       </el-table-column>
     </el-table>
-    
+
     <pagination
       v-show="total>0"
       :total="total"
@@ -107,8 +107,8 @@
         <el-form-item label="老师id" prop="teacherId">
           <el-input v-model="form.teacherId" placeholder="请输入老师id" />
         </el-form-item>
-        <el-form-item label="班级id" prop="classesId">
-          <el-input v-model="form.classesId" placeholder="请输入班级id" />
+        <el-form-item label="班级id" prop="classId">
+          <el-input v-model="form.classId" placeholder="请输入班级id" />
         </el-form-item>
         <el-form-item label="备注" prop="remark">
           <el-input v-model="form.remark" placeholder="请输入备注" />
@@ -153,7 +153,7 @@ const data = reactive({
     pageNum: 1,
     pageSize: 10,
     teacherId: null,
-    classesId: null,
+    classId: null,
     status: null,
     outDate: null
   },
@@ -184,7 +184,7 @@ function reset() {
   form.value = {
     id: null,
     teacherId: null,
-    classesId: null,
+    classId: null,
     status: null,
     remark: null,
     outDate: null

+ 11 - 1
back-ui/src/views/system/user/index.vue

@@ -20,6 +20,11 @@
               <el-form-item label="用户名称" prop="userName">
                 <el-input v-model="queryParams.userName" placeholder="请输入用户名称" clearable style="width: 240px" @keyup.enter="handleQuery" />
               </el-form-item>
+              <el-form-item label="用户类型" prop="userType">
+                <el-select v-model="queryParams.status" placeholder="用户状态" clearable style="width: 240px">
+                  <el-option v-for="dict in user_type" :key="dict.value" :label="dict.label" :value="dict.value" />
+                </el-select>
+              </el-form-item>
               <el-form-item label="手机号码" prop="phonenumber">
                 <el-input v-model="queryParams.phonenumber" placeholder="请输入手机号码" clearable style="width: 240px" @keyup.enter="handleQuery" />
               </el-form-item>
@@ -61,6 +66,11 @@
               <el-table-column label="用户编号" align="center" key="userId" prop="userId" v-if="columns.userId.visible" />
               <el-table-column label="用户名称" align="center" key="userName" prop="userName" v-if="columns.userName.visible" :show-overflow-tooltip="true" />
               <el-table-column label="用户昵称" align="center" key="nickName" prop="nickName" v-if="columns.nickName.visible" :show-overflow-tooltip="true" />
+              <el-table-column label="用户类型" align="center" key="userType" v-if="columns.status.visible">
+                <template #default="scope">
+                  <dict-tag :options="user_type" :value="scope.row.userType" />
+                </template>
+              </el-table-column>
               <el-table-column label="机构" align="center" key="deptName" prop="dept.deptName" v-if="columns.deptName.visible" :show-overflow-tooltip="true" />
               <el-table-column label="手机号码" align="center" key="phonenumber" prop="phonenumber" v-if="columns.phonenumber.visible" width="120" />
               <el-table-column label="状态" align="center" key="status" v-if="columns.status.visible">
@@ -223,7 +233,7 @@ import "splitpanes/dist/splitpanes.css"
 const router = useRouter()
 const appStore = useAppStore()
 const { proxy } = getCurrentInstance()
-const { sys_normal_disable, sys_user_sex } = proxy.useDict("sys_normal_disable", "sys_user_sex")
+const { sys_normal_disable, sys_user_sex, user_type } = proxy.useDict("sys_normal_disable", "sys_user_sex", "user_type")
 
 const userList = ref([])
 const open = ref(false)

+ 9 - 0
back-ui/tailwind.config.cjs

@@ -0,0 +1,9 @@
+/** @type {import('tailwindcss').Config} */
+/* 这个文件在这个项目里没有实际作用,只是为了让tailwind的提示起作用,真实起作用的是assets/style/tailwind.css,注意与它保持一致 */
+module.exports = {
+  content: [
+    './index.html',
+    './src/**/*.{vue,js,ts,jsx,tsx}'
+  ]
+}
+

+ 2 - 1
back-ui/vite.config.js

@@ -1,6 +1,7 @@
 import { defineConfig, loadEnv } from 'vite'
 import path from 'path'
 import createVitePlugins from './vite/plugins'
+import tailwindcss from '@tailwindcss/vite'
 
 const baseUrl = 'https://dz.shineking.top/prod-api' // 后端接口
 
@@ -13,7 +14,7 @@ export default defineConfig(({ mode, command }) => {
     // 默认情况下,vite 会假设你的应用是被部署在一个域名的根路径上
     // 例如 https://www.ruoyi.vip/。如果应用被部署在一个子路径上,你就需要用这个选项指定这个子路径。例如,如果你的应用被部署在 https://www.ruoyi.vip/admin/,则设置 baseUrl 为 /admin/。
     base: VITE_APP_ENV === 'production' ? '/admin/' : '/admin/',
-    plugins: createVitePlugins(env, command === 'build'),
+    plugins: [createVitePlugins(env, command === 'build'), tailwindcss()],
     resolve: {
       // https://cn.vitejs.dev/config/#resolve-alias
       alias: {

Dosya farkı çok büyük olduğundan ihmal edildi
+ 208 - 359
back-ui/yarn.lock


+ 50 - 2
ie-admin/src/main/java/com/ruoyi/web/controller/dz/DzAgentController.java

@@ -1,7 +1,16 @@
 package com.ruoyi.web.controller.dz;
 
+import java.util.Arrays;
 import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.stream.Collectors;
 import javax.servlet.http.HttpServletResponse;
+
+import cn.hutool.core.collection.CollectionUtil;
+import com.ruoyi.common.utils.StringUtils;
+import com.ruoyi.dz.domain.DzSchool;
+import com.ruoyi.dz.service.IDzSchoolService;
 import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.web.bind.annotation.GetMapping;
@@ -22,7 +31,7 @@ import com.ruoyi.common.utils.poi.ExcelUtil;
 
 /**
  * 机构代理Controller
- * 
+ *
  * @author ruoyi
  * @date 2025-09-12
  */
@@ -32,6 +41,8 @@ public class DzAgentController extends BaseController
 {
     @Autowired
     private IDzAgentService dzAgentService;
+    @Autowired
+    private IDzSchoolService schoolService;
 
     /**
      * 查询机构代理列表
@@ -41,6 +52,32 @@ public class DzAgentController extends BaseController
     public AjaxResult list(DzAgent dzAgent)
     {
         List<DzAgent> list = dzAgentService.selectDzAgentList(dzAgent);
+        //处理关联学校的显示
+        List<Long> distinctSchoolIds = list.stream()
+                .map(DzAgent::getSchoolIds)
+                .filter(Objects::nonNull)        // 过滤null数组
+                .filter(array -> array.length > 0) // 过滤空数组
+                .flatMap(Arrays::stream).filter(Objects::nonNull)        // 过滤数组中的null元素
+                .distinct()
+                .collect(Collectors.toList());
+        //查询学校名称
+        if (CollectionUtil.isNotEmpty(distinctSchoolIds)){
+            Map<Long, DzSchool> schoolMap = schoolService.selectDzSchoolListByIds(distinctSchoolIds).stream().collect(Collectors.toMap(DzSchool::getId, school -> school));
+            for (DzAgent agent : list) {
+                if (agent.getSchoolIds() != null&&agent.getSchoolIds().length>0) {
+                    String schoolName = Arrays.stream(agent.getSchoolIds())
+                            .filter(Objects::nonNull)  // 过滤null值
+                            .map(schoolId -> schoolMap.get(schoolId))  // 根据ID获取学校对象
+                            .filter(Objects::nonNull)  // 过滤map中不存在的学校
+                            .map(DzSchool::getName)    // 获取学校名称
+                            .filter(name -> name != null && !name.trim().isEmpty())  // 过滤空名称
+                            .collect(Collectors.joining(", "));  // 用逗号连接
+
+                    agent.setSchoolName(schoolName);
+                }
+            }
+        }
+
         return success(list);
     }
 
@@ -75,6 +112,7 @@ public class DzAgentController extends BaseController
     @PostMapping
     public AjaxResult add(@RequestBody DzAgent dzAgent)
     {
+        setSchools(dzAgent);
         return toAjax(dzAgentService.insertDzAgent(dzAgent));
     }
 
@@ -86,15 +124,25 @@ public class DzAgentController extends BaseController
     @PutMapping
     public AjaxResult edit(@RequestBody DzAgent dzAgent)
     {
+        setSchools(dzAgent);
         return toAjax(dzAgentService.updateDzAgent(dzAgent));
     }
 
+    private void setSchools(DzAgent dzAgent){
+        String schools = dzAgent.getSchools();
+        Long[] schoolIds = dzAgent.getSchoolIds();
+        if (null!=schoolIds&&schoolIds.length>0){
+            schools = Arrays.stream(schoolIds).filter(Objects::nonNull).map(String::valueOf) .collect(Collectors.joining(","));
+            dzAgent.setSchools(schools);
+        }
+    }
+
     /**
      * 删除机构代理
      */
     @PreAuthorize("@ss.hasPermi('dz:agent:remove')")
     @Log(title = "机构代理", businessType = BusinessType.DELETE)
-	@DeleteMapping("/{agentIds}")
+    @DeleteMapping("/{agentIds}")
     public AjaxResult remove(@PathVariable Long[] agentIds)
     {
         return toAjax(dzAgentService.deleteDzAgentByAgentIds(agentIds));

+ 24 - 0
ie-admin/src/main/java/com/ruoyi/web/controller/dz/DzClassesController.java

@@ -1,7 +1,13 @@
 package com.ruoyi.web.controller.dz;
 
 import java.util.List;
+import java.util.Objects;
+import java.util.stream.Collectors;
 import javax.servlet.http.HttpServletResponse;
+
+import com.ruoyi.dz.domain.DzTeacher;
+import com.ruoyi.dz.domain.DzTeacherClass;
+import com.ruoyi.dz.service.IDzTeacherClassService;
 import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.web.bind.annotation.GetMapping;
@@ -33,6 +39,8 @@ public class DzClassesController extends BaseController
 {
     @Autowired
     private IDzClassesService dzClassesService;
+    @Autowired
+    private IDzTeacherClassService teacherClassService;
 
     /**
      * 查询学生班级列表
@@ -46,6 +54,22 @@ public class DzClassesController extends BaseController
         return getDataTable(list);
     }
 
+    @GetMapping("/listAll")
+    public AjaxResult listAll(DzClasses dzClasses)
+    {
+        List<DzClasses> list = dzClassesService.selectDzClassesList(dzClasses);
+//        if (null!=dzClasses.getTeacherId()){
+//            DzTeacherClass query = new DzTeacherClass().setTeacherId(dzClasses.getTeacherId());
+//            Long[] classIds = teacherClassService.selectDzTeacherClassList(query).stream()
+//                    .map(DzTeacherClass::getClassId)
+//                    .filter(Objects::nonNull)
+//                    .distinct() // 去重(如果需要)
+//                    .toArray(Long[]::new);
+//            list.stream().forEach(c->c.setClassIds(classIds));
+//        }
+        return AjaxResult.success(list);
+    }
+
     /**
      * 导出学生班级列表
      */

+ 38 - 1
ie-admin/src/main/java/com/ruoyi/web/controller/dz/DzSchoolController.java

@@ -1,7 +1,16 @@
 package com.ruoyi.web.controller.dz;
 
 import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.stream.Collectors;
+import java.util.stream.Stream;
 import javax.servlet.http.HttpServletResponse;
+
+import cn.hutool.core.collection.CollectionUtil;
+import com.ruoyi.common.utils.StringUtils;
+import com.ruoyi.system.domain.SysArea;
+import com.ruoyi.system.service.ISysAreaService;
 import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.web.bind.annotation.GetMapping;
@@ -33,6 +42,8 @@ public class DzSchoolController extends BaseController
 {
     @Autowired
     private IDzSchoolService dzSchoolService;
+    @Autowired
+    private ISysAreaService areaService;
 
     /**
      * 查询机构校区列表
@@ -42,7 +53,33 @@ public class DzSchoolController extends BaseController
     public TableDataInfo list(DzSchool dzSchool)
     {
         startPage();
-        List<DzSchool> list = dzSchoolService.selectDzSchoolList(dzSchool);
+        List<DzSchool> list= dzSchoolService.selectDzSchoolList(dzSchool);
+        //处理省市区
+        List<Long> areaIds = list.stream()
+                .flatMap(school -> Stream.of(
+                        school.getPro(),school.getCity(),school.getArea()
+                ))
+                .filter(Objects::nonNull).distinct().collect(Collectors.toList());
+        if (CollectionUtil.isNotEmpty(areaIds)){
+            Map<Long, SysArea> areaMap = areaService.selectSysAreaListByIds(areaIds)
+                    .stream().collect(Collectors.toMap(SysArea::getAreaId,area -> area));
+            list.forEach(school -> {
+                StringBuilder proCityAreaName = new StringBuilder();
+                if (null!=school.getPro()&&areaMap.containsKey(school.getPro())){
+                    proCityAreaName.append(areaMap.get(school.getPro()).getAreaName());
+                }
+                if (null!=school.getCity()&&areaMap.containsKey(school.getCity())){
+                    proCityAreaName.append(areaMap.get(school.getCity()).getAreaName());
+                }
+                if (null!=school.getArea()&&areaMap.containsKey(school.getArea())){
+                    proCityAreaName.append(areaMap.get(school.getArea()).getAreaName());
+                }
+                if (StringUtils.isNotEmpty(proCityAreaName.toString())){
+                    school.setProCityAreaName(proCityAreaName.toString());
+                }
+            });
+        }
+
         return getDataTable(list);
     }
 

+ 70 - 3
ie-admin/src/main/java/com/ruoyi/web/controller/dz/DzTeacherClassController.java

@@ -1,7 +1,13 @@
 package com.ruoyi.web.controller.dz;
 
+import java.util.Arrays;
 import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.stream.Collectors;
 import javax.servlet.http.HttpServletResponse;
+
+import com.ruoyi.common.enums.BoolValues;
 import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.web.bind.annotation.GetMapping;
@@ -23,7 +29,7 @@ import com.ruoyi.common.core.page.TableDataInfo;
 
 /**
  * 教师班级关系Controller
- * 
+ *
  * @author ruoyi
  * @date 2025-09-12
  */
@@ -46,6 +52,13 @@ public class DzTeacherClassController extends BaseController
         return getDataTable(list);
     }
 
+    @GetMapping("/listAllTeacherClass")
+    public AjaxResult listAllTeacherClass(DzTeacherClass dzTeacherClass)
+    {
+        List<DzTeacherClass> list = dzTeacherClassService.selectDzTeacherClassList(dzTeacherClass);
+        return AjaxResult.success(list);
+    }
+
     /**
      * 导出教师班级关系列表
      */
@@ -73,13 +86,67 @@ public class DzTeacherClassController extends BaseController
      * 新增教师班级关系
      */
     @PreAuthorize("@ss.hasPermi('dz:teacherclass:add')")
-    @Log(title = "教师班级关系", businessType = BusinessType.INSERT)
+    @Log(title = "关联教师班级关系", businessType = BusinessType.INSERT)
     @PostMapping
     public AjaxResult add(@RequestBody DzTeacherClass dzTeacherClass)
     {
         return toAjax(dzTeacherClassService.insertDzTeacherClass(dzTeacherClass));
     }
 
+    @Log(title = "批量关联教师班级关系", businessType = BusinessType.INSERT)
+    @PostMapping("/batchBindTeacherClass")
+    public AjaxResult batchBindTeacherClass(@RequestBody DzTeacherClass dzTeacherClass)
+    {
+        List<Long> insertClassIds = Arrays.stream(dzTeacherClass.getClassIds()).collect(Collectors.toList());
+
+        DzTeacherClass cond = new DzTeacherClass().setTeacherId(dzTeacherClass.getTeacherId());
+        List<DzTeacherClass> alreadyList = dzTeacherClassService.selectDzTeacherClassList(cond);
+        List<Long> dbClassIds = alreadyList.stream().map(DzTeacherClass::getClassId).collect(Collectors.toList());
+
+        // 2. 找出需要删除的班级ID(存在于dbClassIds但不存在于insertClassIds)
+        List<Long> deleteClassIds = dbClassIds.stream()
+                .filter(dbId -> !insertClassIds.contains(dbId))
+                .collect(Collectors.toList());
+
+        // 3. 找出需要新增的班级ID(存在于insertClassIds但不存在于dbClassIds)
+        List<Long> newClassIds = insertClassIds.stream()
+                .filter(insertId -> !dbClassIds.contains(insertId))
+                .collect(Collectors.toList());
+
+        // 4. 执行删除操作
+        if (!deleteClassIds.isEmpty()) {
+            //alreadyList转换为Map<Long,Long>,key为classesId,value为id(删除时需要用id)
+            Map<Long,Long> map = alreadyList.stream() .collect(Collectors.toMap( DzTeacherClass::getClassId, DzTeacherClass::getId, (existing, replacement) -> existing ));
+            Long[] deleteIds = deleteClassIds.stream()
+                    .map(classId -> map.get(classId))  // 通过classId获取对应的id
+                    .filter(Objects::nonNull)          // 过滤掉null值
+                    .toArray(Long[]::new);
+            if (deleteIds.length > 0) {
+                String[] stringDeleteIds = Arrays.stream(deleteIds)
+                        .map(id -> String.valueOf(id))  // Long 转 String
+                        .toArray(String[]::new);
+                dzTeacherClassService.deleteDzTeacherClassByIds(stringDeleteIds);
+            }
+        }
+
+        // 5. 执行新增操作
+        Long teacherId = dzTeacherClass.getTeacherId();
+        if (!newClassIds.isEmpty()) {
+            List<DzTeacherClass> insertList = newClassIds.stream()
+                    .map(classId -> {
+                        DzTeacherClass insert = new DzTeacherClass();
+                        insert.setTeacherId(teacherId);
+                        insert.setClassId(classId);
+                        insert.setStatus(BoolValues.yes.getValue()); // 设置有效状态
+                        return insert;
+                    })
+                    .collect(Collectors.toList());
+            dzTeacherClassService.insertBatch(insertList);
+        }
+
+        return AjaxResult.success();
+    }
+
     /**
      * 修改教师班级关系
      */
@@ -96,7 +163,7 @@ public class DzTeacherClassController extends BaseController
      */
     @PreAuthorize("@ss.hasPermi('dz:teacherclass:remove')")
     @Log(title = "教师班级关系", businessType = BusinessType.DELETE)
-	@DeleteMapping("/{ids}")
+    @DeleteMapping("/{ids}")
     public AjaxResult remove(@PathVariable String[] ids)
     {
         return toAjax(dzTeacherClassService.deleteDzTeacherClassByIds(ids));

+ 5 - 1
ie-admin/src/main/java/com/ruoyi/web/controller/dz/DzTeacherController.java

@@ -2,6 +2,8 @@ package com.ruoyi.web.controller.dz;
 
 import java.util.List;
 import javax.servlet.http.HttpServletResponse;
+
+import com.ruoyi.dz.service.IDzTeacherClassService;
 import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.web.bind.annotation.GetMapping;
@@ -23,7 +25,7 @@ import com.ruoyi.common.core.page.TableDataInfo;
 
 /**
  * 老师Controller
- * 
+ *
  * @author ruoyi
  * @date 2025-09-12
  */
@@ -33,6 +35,8 @@ public class DzTeacherController extends BaseController
 {
     @Autowired
     private IDzTeacherService dzTeacherService;
+    @Autowired
+    private IDzTeacherClassService teacherClassService;
 
     /**
      * 查询老师列表

+ 79 - 0
ie-admin/src/main/java/com/ruoyi/web/controller/front/FrontCustomerMarjorsController.java

@@ -0,0 +1,79 @@
+package com.ruoyi.web.controller.front;
+
+import com.alibaba.fastjson2.JSONObject;
+import com.ruoyi.common.core.controller.BaseController;
+import com.ruoyi.common.core.domain.AjaxResult;
+import com.ruoyi.common.core.page.TableDataInfo;
+import com.ruoyi.common.utils.SecurityUtils;
+import com.ruoyi.sy.domain.SyMajor;
+import com.ruoyi.sy.service.ISyMajorService;
+import com.ruoyi.syzy.domain.BCustomerMarjors;
+import com.ruoyi.syzy.service.IBCustomerMarjorsService;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import io.swagger.annotations.ApiParam;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.util.List;
+
+@RestController
+@Api(tags = "关注的专业")
+@RequestMapping("front/customer/marjors")
+public class FrontCustomerMarjorsController extends BaseController {
+
+    @Autowired
+    private IBCustomerMarjorsService customerMarjorsService;
+    @Autowired
+    private ISyMajorService majorService;
+
+    @GetMapping("list")
+    @ApiOperation("01 关注专业列表")
+    public TableDataInfo list(@ApiParam(value = "页数", example = "1") @RequestParam Integer pageNum,
+        @ApiParam(value = "页大小", example = "15") @RequestParam Integer pageSize) {
+        String customerCode = SecurityUtils.getLoginUser().getUser().getCode();
+        startPage();
+        List<JSONObject> arr = customerMarjorsService.listMyByPage(customerCode);
+        return getDataTable(arr);
+    }
+
+    @GetMapping("remove")
+    @ApiOperation("02 移除关注专业")
+    public AjaxResult remove(@ApiParam("数据Id") @RequestParam String code) {
+        String customerCode = SecurityUtils.getLoginUser().getUser().getCode();
+
+        SyMajor major = majorService.selectSyMajorByCode(code);
+        if(null==major){
+            return AjaxResult.error("未找到对应编码"+code);
+        }
+        Long marjorId = major.getId();
+
+        BCustomerMarjors customerMarjors= new BCustomerMarjors();
+        customerMarjors.setCustomerCode(customerCode);
+        customerMarjors.setMarjorId(marjorId);
+        List<BCustomerMarjors> customerMarjorsList=customerMarjorsService.selectBCustomerMarjorsList(customerMarjors);
+        customerMarjorsList.forEach(ll->{
+            Long id = ll.getId();
+            customerMarjorsService.remove(customerCode, id);
+        });
+
+        return success();
+    }
+
+    @GetMapping("add")
+    @ApiOperation("03 关注专业")
+    public AjaxResult add(@ApiParam("专业code") @RequestParam String code) {
+        String customerCode = SecurityUtils.getLoginUser().getUser().getCode();
+        SyMajor major = majorService.selectSyMajorByCode(code);
+        if(null==major){
+            return AjaxResult.error("未找到对应编码"+code);
+        }
+        Long marjorId = major.getId();
+        customerMarjorsService.add(customerCode, marjorId);
+        return success();
+    }
+
+}

+ 103 - 0
ie-admin/src/main/java/com/ruoyi/web/controller/front/FrontCustomerUniversityController.java

@@ -0,0 +1,103 @@
+package com.ruoyi.web.controller.front;
+
+import com.ruoyi.common.core.controller.BaseController;
+import com.ruoyi.common.core.domain.AjaxResult;
+import com.ruoyi.common.core.page.TableDataInfo;
+import com.ruoyi.common.utils.SecurityUtils;
+import com.ruoyi.common.utils.StringUtils;
+import com.ruoyi.system.service.ISysDictTypeService;
+import com.ruoyi.syzy.domain.BBusiWishUniversities;
+import com.ruoyi.syzy.domain.BCustomerUniversities;
+import com.ruoyi.syzy.service.IBBusiWishUniversitiesService;
+import com.ruoyi.syzy.service.IBCustomerUniversitiesService;
+import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
+import io.swagger.annotations.ApiParam;
+import org.apache.commons.collections4.CollectionUtils;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RequestParam;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.util.List;
+
+@RestController
+@Api(tags = "关注的院校")
+@RequestMapping("front/customer/university")
+public class FrontCustomerUniversityController extends BaseController {
+
+    @Autowired
+    private IBCustomerUniversitiesService customerUniversitiesService;
+    @Autowired
+    private IBBusiWishUniversitiesService wishUniversitiesService;
+    @Autowired
+    private ISysDictTypeService dictTypeService;
+
+    @GetMapping("list")
+    @ApiOperation("01 关注院校列表")
+    public TableDataInfo list(@ApiParam(value = "页数", example = "1") @RequestParam Integer pageNum,
+        @ApiParam(value = "页大小", example = "15") @RequestParam Integer pageSize) {
+        String customerCode = SecurityUtils.getLoginUser().getUser().getCode();
+        startPage();
+        List<BBusiWishUniversities> arr = wishUniversitiesService.listMyByPage(customerCode);
+        //处理院校星级竞争力
+        arr.stream().forEach(t -> {
+            if(StringUtils.isNotEmpty(t.getStar())){
+                t.setStar(dictTypeService.getDictDataByType("university_stars",t.getStar()));
+            }
+        });
+        return getDataTable(arr);
+    }
+
+    @GetMapping("remove")
+    @ApiOperation("02 移除关注院校")
+    public AjaxResult remove(@ApiParam("院校Id") @RequestParam Long universityId) {
+        String customerCode = SecurityUtils.getLoginUser().getUser().getCode();
+        BBusiWishUniversities universities = wishUniversitiesService.selectBBusiWishUniversitiesById(universityId);
+        if (null == universities) {
+            return AjaxResult.error("未找到对应id" + universityId);
+        }
+        BCustomerUniversities upd = new BCustomerUniversities();
+        BCustomerUniversities cond = new BCustomerUniversities();
+        cond.setCustomerCode(customerCode);
+        cond.setUniversityId(universityId);
+        cond.setStatus(1L);
+        List<BCustomerUniversities> list = customerUniversitiesService.selectBCustomerUniversitiesList(cond);
+        list.forEach(t -> {
+            upd.setId(t.getId());
+            upd.setStatus(0L);
+            customerUniversitiesService.updateBCustomerUniversities(upd);
+            wishUniversitiesService.updateCollect(universityId, -1);
+        });
+        return success();
+    }
+
+    @GetMapping("add")
+    @ApiOperation("03 关注院校")
+    public AjaxResult add(@ApiParam("院校id") @RequestParam Long universityId) {
+        String customerCode = SecurityUtils.getLoginUser().getUser().getCode();
+        BBusiWishUniversities universities = wishUniversitiesService.selectBBusiWishUniversitiesById(universityId);
+        if (null == universities) {
+            return AjaxResult.error("未找到对应id" + universityId);
+        }
+        BCustomerUniversities cond = new BCustomerUniversities();
+        cond.setCustomerCode(customerCode);
+        cond.setUniversityId(universityId);
+        List<BCustomerUniversities> list = customerUniversitiesService.selectBCustomerUniversitiesList(cond);
+        if (CollectionUtils.isNotEmpty(list)) {
+            if (list.get(0).getStatus() != 1L) {
+                BCustomerUniversities upd = new BCustomerUniversities();
+                upd.setId(list.get(0).getId());
+                upd.setStatus(1L);
+                customerUniversitiesService.updateBCustomerUniversities(upd);
+            }
+        } else {
+            cond.setStatus(1L);
+            customerUniversitiesService.insertBCustomerUniversities(cond);
+        }
+        wishUniversitiesService.updateCollect(universityId, 1);
+        return success();
+    }
+
+}

+ 3 - 1
ie-admin/src/main/java/com/ruoyi/web/controller/learn/LearnQuestionsController.java

@@ -4,6 +4,7 @@ import java.util.List;
 import javax.servlet.http.HttpServletResponse;
 
 import io.swagger.annotations.Api;
+import io.swagger.annotations.ApiOperation;
 import org.springframework.security.access.prepost.PreAuthorize;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.web.bind.annotation.GetMapping;
@@ -31,7 +32,7 @@ import com.ruoyi.common.core.page.TableDataInfo;
  */
 @RestController
 @RequestMapping("/learn/questions")
-@Api("后台-学习 - 题")
+@Api(tags = "后台-学习 - 题")
 public class LearnQuestionsController extends BaseController
 {
     @Autowired
@@ -42,6 +43,7 @@ public class LearnQuestionsController extends BaseController
      */
     @PreAuthorize("@ss.hasPermi('learn:questions:list')")
     @GetMapping("/list")
+    @ApiOperation("查询列表")
     public TableDataInfo list(LearnQuestions learnQuestions)
     {
         startPage();

+ 28 - 5
ie-admin/src/main/java/com/ruoyi/web/controller/learn/LearnTeacherController.java

@@ -1,6 +1,8 @@
 package com.ruoyi.web.controller.learn;
 
+import cn.hutool.core.lang.Dict;
 import com.alibaba.fastjson2.JSONObject;
+import com.google.common.collect.Lists;
 import com.ruoyi.common.annotation.Anonymous;
 import com.ruoyi.common.core.controller.BaseController;
 import com.ruoyi.common.core.domain.AjaxResult;
@@ -10,7 +12,6 @@ import com.ruoyi.dz.domain.DzControl;
 import com.ruoyi.dz.domain.DzSubject;
 import com.ruoyi.dz.service.IDzControlService;
 import com.ruoyi.dz.service.IDzSubjectService;
-import com.ruoyi.ie.service.IAMarjorPlanService;
 import com.ruoyi.learn.domain.TestPaperVO;
 import com.ruoyi.web.service.LearnTeacherService;
 import io.swagger.annotations.Api;
@@ -126,12 +127,34 @@ public class LearnTeacherController extends BaseController {
         return AjaxResult.success(learnTeacherService.getKnowledgeTree(subjectId, majorPlanIds));
     }
 
+    @GetMapping("/questionTypes")
+    @ApiOperation("题型列表")
+    public AjaxResult questionTypes(@ApiParam("科目ID") Long subjectId, @RequestParam(required = false) @ApiParam("专业计划ID") Long[] knowledgeIds)
+    {
+        List<Dict> dictList = Lists.newArrayList();
+        dictList.add(Dict.create().set("dictLabel", "单选题").set("dictValue", "1"));
+        dictList.add(Dict.create().set("dictLabel", "多选题").set("dictValue", "2"));
+        dictList.add(Dict.create().set("dictLabel", "判断题").set("dictValue", "3"));
+        dictList.add(Dict.create().set("dictLabel", "分析题").set("dictValue", "4"));
+        return AjaxResult.success(dictList);
+    }
+
     @PreAuthorize("@ss.hasPermi('learn:test_paper:add')")
-    @PostMapping("/build")
-    @ApiOperation("批量组卷")
-    public AjaxResult batchBuild(@RequestBody TestPaperVO.TestPapersBuildReq req)
+    @PostMapping("/build/auto")
+    @ApiOperation("批量自动组卷")
+    public AjaxResult batchBuildAuto(@RequestBody TestPaperVO.TestPapersBuildAutoReq req)
     {
-        return toAjax(true);
+        req.setBuildType(TestPaperVO.PaperBuildType.Auto);
+        return AjaxResult.success(learnTeacherService.buildPapersAuto(req));
+    }
+
+    @PreAuthorize("@ss.hasPermi('learn:test_paper:add')")
+    @PostMapping("/build/manual")
+    @ApiOperation("批量手动组卷")
+    public AjaxResult batchBuildManual(@RequestBody TestPaperVO.TestPapersBuildManualReq req)
+    {
+        req.setBuildType(TestPaperVO.PaperBuildType.Manual);
+        return AjaxResult.success(learnTeacherService.buildPapersManual(req));
     }
 
     @PreAuthorize("@ss.hasPermi('learn:test_paper:query')")

+ 4 - 4
ie-admin/src/main/java/com/ruoyi/web/service/ExamService.java

@@ -116,13 +116,13 @@ public class ExamService {
             paperMapper.updateLearnPaper(up);
         }
 
-        PaperService.PaperDef paperDef = new PaperService.PaperDef();
+        TestPaperVO.PaperDef paperDef = new TestPaperVO.PaperDef();
         paperDef.setTotal(15L);
         paperDef.setFillExclude(true);
         paperDef.setKnowIds(Lists.newArrayList(knowledgeId));
-        List<PaperService.TypeDef> typeDefList= Lists.newArrayList();
-        typeDefList.add(new PaperService.TypeDef("单选题", "单选题", 80, 1));
-        typeDefList.add(new PaperService.TypeDef("判断题", "判断题", 10, 2));
+        List<TestPaperVO.TypeDef> typeDefList= Lists.newArrayList();
+        typeDefList.add(new TestPaperVO.TypeDef("单选题", "单选题", 80, 1));
+        typeDefList.add(new TestPaperVO.TypeDef("判断题", "判断题", 10, 2));
         paperDef.setTypes(typeDefList);
         List<LearnPaperQuestion> pqList = paperService.getQuestions(studentId,  paperDef);
 

+ 266 - 2
ie-admin/src/main/java/com/ruoyi/web/service/LearnTeacherService.java

@@ -1,34 +1,60 @@
 package com.ruoyi.web.service;
 
 import cn.hutool.core.lang.Dict;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
+import com.ruoyi.common.annotation.Excel;
 import com.ruoyi.common.core.domain.TreeEntity;
+import com.ruoyi.common.utils.SecurityUtils;
 import com.ruoyi.dz.domain.DzClasses;
 import com.ruoyi.dz.mapper.DzClassesMapper;
+import com.ruoyi.enums.PaperType;
+import com.ruoyi.ie.domain.AMarjorPlan;
 import com.ruoyi.ie.mapper.AMarjorPlanMapper;
-import com.ruoyi.learn.domain.LearnKnowledgeTree;
+import com.ruoyi.learn.domain.*;
+import com.ruoyi.learn.mapper.LearnDirectedKnowledgeMapper;
 import com.ruoyi.learn.mapper.LearnKnowledgeTreeMapper;
+import com.ruoyi.learn.mapper.LearnStudentMapper;
+import com.ruoyi.learn.mapper.LearnTestPaperMapper;
 import com.ruoyi.syzy.domain.BBusiWishUniversities;
 import com.ruoyi.syzy.mapper.BBusiWishUniversitiesMapper;
 import lombok.Data;
 import org.apache.commons.compress.utils.Lists;
+import org.apache.commons.lang3.ArrayUtils;
+import org.apache.commons.lang3.StringUtils;
+import org.apache.commons.lang3.tuple.Pair;
 import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import org.springframework.util.CollectionUtils;
 
+import javax.validation.ValidationException;
+import java.util.Calendar;
 import java.util.List;
 import java.util.Map;
+import java.util.Set;
 import java.util.stream.Collectors;
+import java.util.stream.Stream;
 
 @Service
 public class LearnTeacherService {
     private final DzClassesMapper dzClassesMapper;
     private final LearnKnowledgeTreeMapper learnKnowledgeTreeMapper;
+    private final LearnStudentMapper learnStudentMapper;
     private final AMarjorPlanMapper marjorPlanMapper;
     private final BBusiWishUniversitiesMapper busiWishUniversitiesMapper;
+    private final LearnDirectedKnowledgeMapper learnDirectedKnowledgeMapper;
+    private final PaperService paperService;
+    private final LearnTestPaperMapper learnTestPaperMapper;
 
-    public LearnTeacherService(DzClassesMapper dzClassesMapper, LearnKnowledgeTreeMapper learnKnowledgeTreeMapper, AMarjorPlanMapper marjorPlanMapper, BBusiWishUniversitiesMapper busiWishUniversitiesMapper) {
+    public LearnTeacherService(DzClassesMapper dzClassesMapper, LearnKnowledgeTreeMapper learnKnowledgeTreeMapper, LearnStudentMapper learnStudentMapper, AMarjorPlanMapper marjorPlanMapper, BBusiWishUniversitiesMapper busiWishUniversitiesMapper, LearnDirectedKnowledgeMapper learnDirectedKnowledgeMapper, PaperService paperService, LearnTestPaperMapper learnTestPaperMapper) {
         this.dzClassesMapper = dzClassesMapper;
         this.learnKnowledgeTreeMapper = learnKnowledgeTreeMapper;
+        this.learnStudentMapper = learnStudentMapper;
         this.marjorPlanMapper = marjorPlanMapper;
         this.busiWishUniversitiesMapper = busiWishUniversitiesMapper;
+        this.learnDirectedKnowledgeMapper = learnDirectedKnowledgeMapper;
+        this.paperService = paperService;
+        this.learnTestPaperMapper = learnTestPaperMapper;
     }
 
     public List<DzClasses> getClasses(Long teacherId)
@@ -45,7 +71,220 @@ public class LearnTeacherService {
                 .set("majorName", t.getMajorName()).set("count", t.getXuefei()).set("total", t.getPlanTotal())).collect(Collectors.toList());
     }
 
+    @Transactional(rollbackFor = Exception.class)
+    public String buildPapersAuto(TestPaperVO.TestPapersBuildAutoReq req) {
+        // 查询 院校,专业组,专业计划范围, 明细一级时只有一个,否则就是批量
+        List<LearnStudent> studentList = learnStudentMapper.selectLearnStudentsByMap(req.toMap());
+        if(CollectionUtils.isEmpty(studentList)) {
+            throw new ValidationException("无可用计划可检查");
+        }
+        Set<Long> planIdSet = Sets.newHashSet();
+        Map<Long, List<LearnStudent>> universityMap = Maps.newHashMap();
+        for(LearnStudent ls : studentList){
+            planIdSet.add(ls.getMajorPlanId());
+            List<LearnStudent> list = universityMap.get(ls.getUniversityId());
+            if(null == list) {
+                list = Lists.newArrayList();
+                universityMap.put(ls.getUniversityId(), list);
+            }
+            list.add(ls);
+        }
+        // 查询已经生成的
+        Map<Long, Map<String, LearnTestPaper>> universityDirectPaperMap = learnTestPaperMapper.selectByBatchAndUniversityIds(req.getBatchId(), universityMap.keySet()).stream()
+                .collect(Collectors.groupingBy(LearnTestPaper::getUniversityId, Collectors.toMap(LearnTestPaper::getDirectKey, a -> a)));
+        if(null == req.getDirectType() || !req.getDirectType()) { // 全量
+            LearnPaper paper = new LearnPaper();
+            paper.setPaperName(req.getTitle());
+            paper.setYear(Calendar.getInstance().get(Calendar.YEAR));
+            paper.setPaperType(PaperType.Test.name());
+            paper.setSubjectId(req.getSubjectId());
+            paper.setDirectKey("");
+            for(Long universityId : universityMap.keySet()) {
+                Map<String, LearnTestPaper> directPaperMap = universityDirectPaperMap.get(universityId);
+                if(null != directPaperMap && directPaperMap.containsKey("")) {
+                    continue;
+                }
+                if(null == paper.getId()) {
+                    Pair<LearnPaper, List<LearnPaperQuestion>> paperResult = paperService.buildPaper(null, paper, req.getPaperDef());
+                    paperService.savePaper(paper, paperResult.getRight());
+                }
+                LearnTestPaper testPaper = new LearnTestPaper();
+                testPaper.setBatchId(req.getBatchId());
+                testPaper.setUniversityId(universityId);
+                testPaper.setDirectKey("");
+                testPaper.setPaperId(paper.getId());
+                learnTestPaperMapper.insertLearnTestPaper(testPaper);
+            }
+            return "";
+        }
+        TestPaperVO.PaperDef paperDef = req.getPaperDef();
+        if(null != req.getPlanIds() && req.getPlanIds().size() == 1) {
+            if(!CollectionUtils.isEmpty(paperDef.getKnowIds())) {
+                throw new ValidationException("批量时不支持自定义知识点");
+            }
+        }
+        Map<Long, Map<String, LearnDirectedKnowledge>> universityDirectedKnowledgeMap = learnDirectedKnowledgeMapper.selectByUniversityIds(universityMap.keySet().toArray(new Long[universityMap.size()])).stream().collect(
+                Collectors.groupingBy(LearnDirectedKnowledge::getUniversityId, Collectors.toMap(LearnDirectedKnowledge::getDirectKey, a -> a)));
+        Map<Long, AMarjorPlan> planMap = marjorPlanMapper.selectAMarjorPlanByIds(planIdSet.toArray(new Long[planIdSet.size()])).stream().collect(Collectors.toMap(AMarjorPlan::getId, a -> a));
+
+        LearnDirectedKnowledge directedKnowledge = null;
+        String directedKey = null;
+
+        for(Long universityId : universityMap.keySet()) {
+            Map<String, LearnDirectedKnowledge> directedKnowledgeMap = universityDirectedKnowledgeMap.get(universityId);
+            for(LearnStudent ls : universityMap.get(universityId)) {
+                AMarjorPlan plan = planMap.get(ls.getMajorPlanId());
+                String groupName = StringUtils.trimToEmpty(plan.getMajorGroup());
+                if(CollectionUtils.isEmpty(paperDef.getKnowIds())) {
+                    if(null != (directedKnowledge = directedKnowledgeMap.get((directedKey = groupName + "_" + plan.getMajorName())))) {
+                        paperDef.setKnowIds(Stream.of(directedKnowledge.getKnowledges().split(",")).map(t -> Long.parseLong(t.trim())).collect(Collectors.toList()));
+                    } else if(null != (directedKnowledge = directedKnowledgeMap.get(directedKey = groupName))) {
+                        paperDef.setKnowIds(Stream.of(directedKnowledge.getKnowledges().split(",")).map(t -> Long.parseLong(t.trim())).collect(Collectors.toList()));
+                    } else {
+                        throw new ValidationException("院校没有配置定向知识点,生成失败: " + universityId + ":" + groupName + ":" + plan.getMajorName());
+                    }
+                } else if(null != directedKnowledgeMap) {
+                    if(null != (directedKnowledge = directedKnowledgeMap.get((directedKey = groupName + "_" + plan.getMajorName())))) {
+                    } else if(null != (directedKnowledge = directedKnowledgeMap.get(directedKey = groupName))) {
+                    } else {
+                        throw new ValidationException("院校没有配置定向知识点,生成失败" + universityId + ":" + directedKey);
+                    }
+                } else {
+                    throw new ValidationException("院校没有配置定向知识点,生成失败: " + universityId);
+                }
+                Map<String, LearnTestPaper> directPaperMap = universityDirectPaperMap.get(universityId);
+                if(null != directPaperMap && directPaperMap.containsKey(directedKey)) {
+                    continue;
+                }
+                LearnPaper paper = new LearnPaper();
+                paper.setSubjectId(req.getSubjectId());
+                paper.setPaperName(req.getTitle());
+                paper.setYear(plan.getYear());
+                paper.setPaperType(PaperType.Test.name());
+                paper.setDirectKey(directedKey);
+                Pair<LearnPaper, List<LearnPaperQuestion>> paperResult = paperService.buildPaper(null, paper, paperDef);
+                paperService.savePaper(paperResult.getKey(), paperResult.getValue());
+
+                LearnTestPaper testPaper = new LearnTestPaper();
+                testPaper.setBatchId(req.getBatchId());
+                testPaper.setUniversityId(universityId);
+                testPaper.setDirectKey(directedKey);
+                testPaper.setPaperId(paper.getId());
+                learnTestPaperMapper.insertLearnTestPaper(testPaper);
+                directPaperMap.put(directedKey, testPaper);
+            }
+        }
+        return "";
+    }
+
+    @Transactional(rollbackFor = Exception.class)
+    public String buildPapersManual(TestPaperVO.TestPapersBuildManualReq req) {
+        if(null == req.getSubjectId()) {
+            throw new ValidationException("未选择科目");
+        }
+        // 查询 院校,专业组,专业计划范围, 明细一级时只有一个,否则就是批量
+        List<LearnStudent> studentList = learnStudentMapper.selectLearnStudentsByMap(req.toMap());
+        if(CollectionUtils.isEmpty(studentList)) {
+            throw new ValidationException("无可用计划可检查");
+        }
+        Set<Long> planIdSet = Sets.newHashSet();
+        Map<Long, List<LearnStudent>> universityMap = Maps.newHashMap();
+        for(LearnStudent ls : studentList){
+            planIdSet.add(ls.getMajorPlanId());
+            List<LearnStudent> list = universityMap.get(ls.getUniversityId());
+            if(null == list) {
+                list = Lists.newArrayList();
+                universityMap.put(ls.getUniversityId(), list);
+            }
+            list.add(ls);
+        }
+        // 查询已经生成的
+        Map<Long, Map<String, LearnTestPaper>> universityDirectPaperMap = learnTestPaperMapper.selectByBatchAndUniversityIds(req.getBatchId(), universityMap.keySet()).stream()
+                .collect(Collectors.groupingBy(LearnTestPaper::getUniversityId, Collectors.toMap(LearnTestPaper::getDirectKey, a -> a)));
+
+        LearnPaper paper = new LearnPaper();
+        paper.setPaperName(req.getTitle());
+        paper.setYear(Calendar.getInstance().get(Calendar.YEAR));
+        paper.setPaperType(PaperType.Test.name());
+        if(null == req.getDirectType() || !req.getDirectType()) { // 全量
+            paper.setSubjectId(req.getSubjectId());
+            paper.setDirectKey("");
+            for(Long universityId : universityMap.keySet()) {
+                Map<String, LearnTestPaper> directPaperMap = universityDirectPaperMap.get(universityId);
+                if(null != directPaperMap && directPaperMap.containsKey("")) {
+                    continue;
+                }
+                if(null == paper.getId()) {
+                    paperService.savePaper(paper, req.getQuestions());
+                }
+                LearnTestPaper testPaper = new LearnTestPaper();
+                testPaper.setBatchId(req.getBatchId());
+                testPaper.setUniversityId(universityId);
+                testPaper.setDirectKey("");
+                testPaper.setPaperId(paper.getId());
+                testPaper.setConditions("");
+                testPaper.setCreatorId(SecurityUtils.getUserId());
+                learnTestPaperMapper.insertLearnTestPaper(testPaper);
+            }
+            return "";
+        } // 定向
+        // 准备定向数据
+        Map<Long, Map<String, LearnDirectedKnowledge>> universityDirectedKnowledgeMap = learnDirectedKnowledgeMapper.selectByUniversityIds(universityMap.keySet().toArray(new Long[universityMap.size()])).stream().collect(
+                Collectors.groupingBy(LearnDirectedKnowledge::getUniversityId, Collectors.toMap(LearnDirectedKnowledge::getDirectKey, a -> a)));
+        Map<Long, AMarjorPlan> planMap = marjorPlanMapper.selectAMarjorPlanByIds(planIdSet.toArray(new Long[planIdSet.size()])).stream().collect(Collectors.toMap(AMarjorPlan::getId, a -> a));
+
+        String directedKey = null;
+        for(Long universityId : universityMap.keySet()) {
+            Map<String, LearnTestPaper> directPaperMap = universityDirectPaperMap.get(universityId);
+            Map<String, LearnDirectedKnowledge> directedKnowledgeMap = universityDirectedKnowledgeMap.get(universityId);
+            for(LearnStudent ls : universityMap.get(universityId)) { // Group+Name
+                AMarjorPlan plan = planMap.get(ls.getMajorPlanId());
+                String groupName = StringUtils.trimToEmpty(plan.getMajorGroup());
+                if(null != (directedKnowledgeMap.get(directedKey = groupName + "_" + plan.getMajorName()))) {
+                    if(null != directPaperMap.get(directedKey)) {
+                        continue;
+                    }
+                } else if(null != directedKnowledgeMap.get(directedKey = groupName)) {
+                    if(null != directPaperMap.get(directedKey)) {
+                        continue;
+                    }
+                } else {
+                    continue;
+                }
+                if(null == paper.getId()) {
+                    paperService.savePaper(paper, req.getQuestions());
+                }
+                LearnTestPaper testPaper = new LearnTestPaper();
+                testPaper.setBatchId(req.getBatchId());
+                testPaper.setUniversityId(universityId);
+                testPaper.setDirectKey(directedKey);
+                testPaper.setPaperId(paper.getId());
+                learnTestPaperMapper.insertLearnTestPaper(testPaper);
+                directPaperMap.put(directedKey, testPaper);
+            }
+        }
+        return "";
+    }
+
     public List<TreeNode> getKnowledgeTree(Long subjectId, Long[] planIds) {
+        Set<Long> knowledgeIdSet = Sets.newHashSet();
+        if(ArrayUtils.isNotEmpty(planIds)) {
+            List<AMarjorPlan> planList = marjorPlanMapper.selectAMarjorPlanByIds(planIds);
+            AMarjorPlan curr = planList.get(0);
+            LearnDirectedKnowledge dkCond = new LearnDirectedKnowledge();
+            dkCond.setUniversityId(curr.getUniversityId());
+            dkCond.setYear(curr.getYear());
+            Map<String, LearnDirectedKnowledge> directedKnowledgeMap = learnDirectedKnowledgeMapper.selectLearnDirectedKnowledgeList(dkCond).stream().collect(Collectors.toMap(LearnDirectedKnowledge::getDirectKey, t -> t));
+            LearnDirectedKnowledge directedKnowledge = null;
+            for(AMarjorPlan plan : planList) {
+                String groupName = StringUtils.trimToEmpty(plan.getMajorGroup());
+                if(null != (directedKnowledge = directedKnowledgeMap.get(groupName + "_" + plan.getMajorName()))) {
+                    knowledgeIdSet.addAll(Stream.of(directedKnowledge.getKnowledges().split(",")).map(t -> Long.parseLong(t.trim())).collect(Collectors.toList()));
+                } else if(null != (directedKnowledge = directedKnowledgeMap.get(groupName))) {
+                    knowledgeIdSet.addAll(Stream.of(directedKnowledge.getKnowledges().split(",")).map(t -> Long.parseLong(t.trim())).collect(Collectors.toList()));
+                }
+            }
+        }
         LearnKnowledgeTree ktCond = new LearnKnowledgeTree();
         ktCond.setSubjectId(subjectId);
         List<LearnKnowledgeTree> ktList = learnKnowledgeTreeMapper.selectLearnKnowledgeTreeList(ktCond);
@@ -58,6 +297,9 @@ public class LearnTeacherService {
             }
             teMap.get(kt.getPid()).getChildren().add(teMap.get(kt.getId()));
         }
+        for(TreeNode tn : treeNodeList) {
+            tn.setChecked(knowledgeIdSet);
+        }
         return treeNodeList;
     }
 
@@ -65,10 +307,32 @@ public class LearnTeacherService {
     public static class TreeNode {
         private Long id;
         private String name;
+        private Integer status;
         List<TreeNode> children = Lists.newArrayList();
         public TreeNode(Long id, String name) {
             this.id = id;
             this.name = name;
+            this.status = 0;
+        }
+
+        public boolean setChecked(Set<Long> idSet) {
+            boolean checkSelf = idSet.contains(id);
+            if(CollectionUtils.isEmpty(children)) {
+                this.status = checkSelf ? 1 : 0;
+                return true;
+            }
+            int count = 0;
+            for(TreeNode tn : children) {
+                if(checkSelf || idSet.contains(tn.getId())) {
+                    tn.setStatus(1);
+                    count++;
+                }
+            }
+            if(children.size() == count) {
+                this.status = 1;
+                return true;
+            }
+            return false;
         }
     }
 }

+ 9 - 28
ie-admin/src/main/java/com/ruoyi/web/service/PaperService.java

@@ -10,9 +10,7 @@ import com.ruoyi.enums.PaperStatus;
 import com.ruoyi.enums.PaperType;
 import com.ruoyi.learn.domain.*;
 import com.ruoyi.learn.mapper.*;
-import lombok.AllArgsConstructor;
 import lombok.Data;
-import lombok.NoArgsConstructor;
 import org.apache.commons.lang3.tuple.Pair;
 import org.springframework.stereotype.Service;
 
@@ -78,9 +76,9 @@ public class PaperService {
         paper.setStatus(PaperStatus.Valid.getVal());
         paper.setDirectKey(directedKnowledge.getDirectKey());
 
-        PaperService.PaperDef paperDef = JSONObject.parseObject(directedKnowledge.getConditions(), PaperDef.class);
+        TestPaperVO.PaperDef paperDef = JSONObject.parseObject(directedKnowledge.getConditions(), TestPaperVO.PaperDef.class);
         paperDef.setKnowIds(Stream.of(directedKnowledge.getKnowledges().split(",")).map(Long::valueOf).collect(Collectors.toList()));
-        paperDef.setTypes(JSONArray.parseArray(directedKnowledge.getQuestionTypes(), TypeDef.class));
+        paperDef.setTypes(JSONArray.parseArray(directedKnowledge.getQuestionTypes(), TestPaperVO.TypeDef.class));
         return buildPaper(null, paper, paperDef);
     }
 
@@ -91,7 +89,7 @@ public class PaperService {
      * @param paperDef
      * @return
      */
-    public Pair<LearnPaper, List<LearnPaperQuestion>> buildPaper(Long studentId, LearnPaper paper, PaperService.PaperDef paperDef) {
+    public Pair<LearnPaper, List<LearnPaperQuestion>> buildPaper(Long studentId, LearnPaper paper, TestPaperVO.PaperDef paperDef) {
         if(null == studentId){
             paperDef.setFillExclude(false);
         }
@@ -123,17 +121,17 @@ public class PaperService {
      * @param paperDef
      * @return
      */
-    public List<LearnPaperQuestion> getQuestions(Long studentId, PaperDef paperDef) {
+    public List<LearnPaperQuestion> getQuestions(Long studentId, TestPaperVO.PaperDef paperDef) {
         // 题型分布定义, 知识点列表, 分值定义
         // 统计知识点+类型的有效数量 TODO 总量可以缓存
         Map<String, KnowTypeAssign> knowTypeAssignMap = Maps.newHashMap();
-        List<String> typeSet = paperDef.getTypes().stream().map(TypeDef::getType).collect(Collectors.toList());
+        List<String> typeSet = paperDef.getTypes().stream().map(TestPaperVO.TypeDef::getType).collect(Collectors.toList());
         Map cond = Maps.newHashMap();
         cond.put("studentId", studentId);
         cond.put("knowIds", paperDef.getKnowIds());
         cond.put("types", typeSet);
         setValue(knowTypeAssignMap, cond, 1); // 填充排除总量
-        if (paperDef.fillExclude) {
+        if (paperDef.getFillExclude()) {
             cond.remove("studentId");
             setValue(knowTypeAssignMap, cond, 2); // 按需填充总量
         }
@@ -161,8 +159,8 @@ public class PaperService {
                     avgKnowTypeCount = 1L;
                 }
                 typeCount = 0;
-                for (TypeDef typeDef : paperDef.getTypes()) {
-                    Long tmpMinKnowTypeCount = assignKnownCount(knowId, typeDef.getType(), knowTypeAssignMap, avgKnowTypeCount, paperDef.fillExclude, assignCount);
+                for (TestPaperVO.TypeDef typeDef : paperDef.getTypes()) {
+                    Long tmpMinKnowTypeCount = assignKnownCount(knowId, typeDef.getType(), knowTypeAssignMap, avgKnowTypeCount, paperDef.getFillExclude(), assignCount);
                     if (tmpMinKnowTypeCount > 0) {
                         minKnowTypeCount = Math.min(minKnowTypeCount, tmpMinKnowTypeCount);
                         knowSet.add(knowId);
@@ -182,7 +180,7 @@ public class PaperService {
         Random random = new Random();
         List<LearnPaperQuestion> pqList = Lists.newArrayList();
         Set<Long> existQuestionIdSet = Sets.newHashSet();
-        for (TypeDef typeDef : paperDef.getTypes()) {
+        for (TestPaperVO.TypeDef typeDef : paperDef.getTypes()) {
             for (Long knowId : paperDef.getKnowIds()) {
                 String key = knowId + "_" + typeDef.getType();
                 KnowTypeAssign ktc = knowTypeAssignMap.get(key);
@@ -312,21 +310,4 @@ public class PaperService {
         Long exclCount; // 排除总数
         Long total; // 全量总数
     }
-
-    @Data
-    @AllArgsConstructor
-    @NoArgsConstructor
-    public static class TypeDef {
-        String title;
-        String type;
-        Integer count;
-        Integer score;
-    }
-    @Data
-    public static class PaperDef {
-        Long total;
-        List<Long> knowIds;
-        Boolean fillExclude; // 不足时是否填充排除的
-        List<TypeDef> types;
-    }
 }

+ 2 - 0
ie-admin/src/main/java/com/ruoyi/web/service/SysRegisterService.java

@@ -7,6 +7,7 @@ import com.ruoyi.common.exception.base.BaseException;
 import com.ruoyi.common.utils.bean.BeanUtils;
 import com.ruoyi.dz.domain.DzCards;
 import com.ruoyi.dz.service.IDzCardsService;
+import com.ruoyi.enums.UserTypeEnum;
 import com.ruoyi.framework.web.service.TokenService;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.stereotype.Component;
@@ -147,6 +148,7 @@ public class SysRegisterService
             }
         }
         if(null == user.getUserId()) {
+            user.setUserType(UserTypeEnum.Card.getVal());
             userService.insertUser(user);
         } else {
             userService.updateUser(user);

+ 65 - 0
ie-common/src/main/java/com/ruoyi/common/enums/BoolValues.java

@@ -0,0 +1,65 @@
+package com.ruoyi.common.enums;
+
+import com.ruoyi.common.utils.StringUtils;
+
+/**
+ * 0否,1是
+ */
+public enum BoolValues {
+    other(-1, "其他"),
+    no(0, "否"),
+    yes(1, "是"),
+    ;
+
+    private Integer value;
+    private String remark;
+
+    private BoolValues(Integer value, String remark) {
+        this.value = value;
+        this.remark = remark;
+    }
+
+    public Integer getValue() {
+        return value;
+    }
+
+    public String getRemark() {
+        return remark;
+    }
+
+    public static BoolValues getByValue(String value) {
+        for (BoolValues o : BoolValues.values()) {
+            if (o.getValue().equals(value)) {
+                return o;
+            }
+        }
+        return null;
+    }
+
+    public static Boolean isTrue(Object value)
+    {
+        if(StringUtils.isNull(value)){
+            return false;
+        }
+        if(value instanceof Long||value instanceof Integer){
+            return BoolValues.yes.getValue()==Integer.parseInt(String.valueOf(value));
+        }
+        return false;
+    }
+
+    public static Boolean isOther(Object value)
+    {
+        if(StringUtils.isNull(value)){
+            return false;
+        }
+        if(value instanceof Long||value instanceof Integer){
+            return BoolValues.other.getValue()==Integer.parseInt(String.valueOf(value));
+        }
+        return false;
+    }
+
+    public static Boolean isFalse(Object value)
+    {
+        return !isTrue(value);
+    }
+}

+ 24 - 0
ie-system/src/main/java/com/ruoyi/dz/domain/DzAgent.java

@@ -1,11 +1,16 @@
 package com.ruoyi.dz.domain;
 
 import com.ruoyi.common.core.domain.entity.SysDept;
+import org.apache.commons.lang3.StringUtils;
 import org.apache.commons.lang3.builder.ToStringBuilder;
 import org.apache.commons.lang3.builder.ToStringStyle;
 import com.ruoyi.common.annotation.Excel;
 import com.ruoyi.common.core.domain.TreeEntity;
 
+import java.util.Arrays;
+import java.util.Objects;
+import java.util.stream.Collectors;
+
 /**
  * 机构代理对象 dz_agent
  *
@@ -40,11 +45,30 @@ public class DzAgent extends TreeEntity
     /** 负责校区列表 */
     @Excel(name = "负责校区列表")
     private String schools;
+    private Long[] schoolIds;
+    private String schoolName;
 
     private String username;
 
     private SysDept dept;
 
+    public String getSchoolName() {
+        return schoolName;
+    }
+
+    public void setSchoolName(String schoolName) {
+        this.schoolName = schoolName;
+    }
+
+    public Long[] getSchoolIds() {
+        return (null==schoolIds||schoolIds.length==0)?(StringUtils.isNotEmpty(schools)?
+                Arrays.stream(schools.split(",")).map(String::trim).filter(s -> !s.isEmpty()).map(Long::valueOf).toArray(Long[]::new):new Long[0]):schoolIds;
+    }
+
+    public void setSchoolIds(Long[] schoolIds) {
+        this.schoolIds = schoolIds;
+    }
+
     public SysDept getDept() {
         return dept;
     }

+ 18 - 0
ie-system/src/main/java/com/ruoyi/dz/domain/DzClasses.java

@@ -17,6 +17,8 @@ public class DzClasses extends BaseEntity
 
     /** ID */
     private Long classId;
+    private Long[] classIds;
+    private Long teacherId;
 
     /** 学校 */
     @Excel(name = "学校")
@@ -44,6 +46,22 @@ public class DzClasses extends BaseEntity
     @Excel(name = "统计数据")
     private String stats;
 
+    public Long[] getClassIds() {
+        return classIds;
+    }
+
+    public void setClassIds(Long[] classIds) {
+        this.classIds = classIds;
+    }
+
+    public Long getTeacherId() {
+        return teacherId;
+    }
+
+    public void setTeacherId(Long teacherId) {
+        this.teacherId = teacherId;
+    }
+
     public DzSchool getSchool() {
         return school;
     }

+ 12 - 1
ie-system/src/main/java/com/ruoyi/dz/domain/DzSchool.java

@@ -6,6 +6,8 @@ import org.apache.commons.lang3.builder.ToStringStyle;
 import com.ruoyi.common.annotation.Excel;
 import com.ruoyi.common.core.domain.BaseEntity;
 
+import java.util.List;
+
 /**
  * 机构校区对象 dz_school
  *
@@ -15,7 +17,6 @@ import com.ruoyi.common.core.domain.BaseEntity;
 public class DzSchool extends BaseEntity
 {
     private static final long serialVersionUID = 1L;
-
     /** $column.columnComment */
     private Long id;
 
@@ -43,12 +44,22 @@ public class DzSchool extends BaseEntity
     @Excel(name = "区")
     private Long area;
 
+    String proCityAreaName ;
+
     /** 状态(0:无效,1:有效) */
     @Excel(name = "状态(0:无效,1:有效)")
     private Integer status;
 
     private SysDept dept;
 
+    public String getProCityAreaName() {
+        return proCityAreaName;
+    }
+
+    public void setProCityAreaName(String proCityAreaName) {
+        this.proCityAreaName = proCityAreaName;
+    }
+
     public SysDept getDept() {
         return dept;
     }

+ 2 - 1
ie-system/src/main/java/com/ruoyi/dz/domain/DzTeacher.java

@@ -63,9 +63,10 @@ public class DzTeacher extends BaseEntity
         this.username = username;
     }
 
-    public void setTeacherId(Long teacherId)
+    public DzTeacher setTeacherId(Long teacherId)
     {
         this.teacherId = teacherId;
+        return this;
     }
 
     public Long getTeacherId()

+ 27 - 17
ie-system/src/main/java/com/ruoyi/dz/domain/DzTeacherClass.java

@@ -9,7 +9,7 @@ import com.ruoyi.common.core.domain.BaseEntity;
 
 /**
  * 教师班级关系对象 dz_teacher_class
- * 
+ *
  * @author ruoyi
  * @date 2025-09-12
  */
@@ -18,7 +18,7 @@ public class DzTeacherClass extends BaseEntity
     private static final long serialVersionUID = 1L;
 
     /** 标识 */
-    private String id;
+    private Long id;
 
     /** 老师id */
     @Excel(name = "老师id")
@@ -26,73 +26,83 @@ public class DzTeacherClass extends BaseEntity
 
     /** 班级id */
     @Excel(name = "班级id")
-    private Long classesId;
+    private Long classId;
+    private Long[] classIds;
 
     /** 有效状态 */
     @Excel(name = "有效状态")
-    private Long status;
+    private Integer status;
 
     /** 结束日期 */
     @JsonFormat(pattern = "yyyy-MM-dd")
     @Excel(name = "结束日期", width = 30, dateFormat = "yyyy-MM-dd")
     private Date outDate;
 
-    public void setId(String id) 
+    public void setId(Long id)
     {
         this.id = id;
     }
 
-    public String getId() 
+    public Long getId()
     {
         return id;
     }
 
-    public void setTeacherId(Long teacherId) 
+    public DzTeacherClass setTeacherId(Long teacherId)
     {
         this.teacherId = teacherId;
+        return this;
     }
 
-    public Long getTeacherId() 
+    public Long getTeacherId()
     {
         return teacherId;
     }
 
-    public void setClassesId(Long classesId) 
+    public void setClassId(Long classId)
     {
-        this.classesId = classesId;
+        this.classId = classId;
     }
 
-    public Long getClassesId() 
+    public Long getClassId()
     {
-        return classesId;
+        return classId;
     }
 
-    public void setStatus(Long status) 
+    public void setStatus(Integer status)
     {
         this.status = status;
     }
 
-    public Long getStatus() 
+    public Integer getStatus()
     {
         return status;
     }
 
-    public void setOutDate(Date outDate) 
+    public void setOutDate(Date outDate)
     {
         this.outDate = outDate;
     }
 
-    public Date getOutDate() 
+    public Date getOutDate()
     {
         return outDate;
     }
 
+    public Long[] getClassIds() {
+        return classIds;
+    }
+
+    public void setClassIds(Long[] classIds) {
+        this.classIds = classIds;
+    }
+
     @Override
     public String toString() {
         return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)
             .append("id", getId())
             .append("teacherId", getTeacherId())
-            .append("classesId", getClassesId())
+            .append("classId", getClassId())
             .append("status", getStatus())
             .append("remark", getRemark())
             .append("outDate", getOutDate())

+ 9 - 9
ie-system/src/main/java/com/ruoyi/dz/mapper/DzSchoolMapper.java

@@ -5,15 +5,15 @@ import com.ruoyi.dz.domain.DzSchool;
 
 /**
  * 机构校区Mapper接口
- * 
+ *
  * @author ruoyi
  * @date 2025-09-12
  */
-public interface DzSchoolMapper 
+public interface DzSchoolMapper
 {
     /**
      * 查询机构校区
-     * 
+     *
      * @param id 机构校区主键
      * @return 机构校区
      */
@@ -21,15 +21,15 @@ public interface DzSchoolMapper
 
     /**
      * 查询机构校区列表
-     * 
+     *
      * @param dzSchool 机构校区
      * @return 机构校区集合
      */
     public List<DzSchool> selectDzSchoolList(DzSchool dzSchool);
-
+    public List<DzSchool> selectDzSchoolListByIds(List<Long> schoolIds);
     /**
      * 新增机构校区
-     * 
+     *
      * @param dzSchool 机构校区
      * @return 结果
      */
@@ -37,7 +37,7 @@ public interface DzSchoolMapper
 
     /**
      * 修改机构校区
-     * 
+     *
      * @param dzSchool 机构校区
      * @return 结果
      */
@@ -45,7 +45,7 @@ public interface DzSchoolMapper
 
     /**
      * 删除机构校区
-     * 
+     *
      * @param id 机构校区主键
      * @return 结果
      */
@@ -53,7 +53,7 @@ public interface DzSchoolMapper
 
     /**
      * 批量删除机构校区
-     * 
+     *
      * @param ids 需要删除的数据主键集合
      * @return 结果
      */

+ 9 - 9
ie-system/src/main/java/com/ruoyi/dz/mapper/DzTeacherClassMapper.java

@@ -5,15 +5,15 @@ import com.ruoyi.dz.domain.DzTeacherClass;
 
 /**
  * 教师班级关系Mapper接口
- * 
+ *
  * @author ruoyi
  * @date 2025-09-12
  */
-public interface DzTeacherClassMapper 
+public interface DzTeacherClassMapper
 {
     /**
      * 查询教师班级关系
-     * 
+     *
      * @param id 教师班级关系主键
      * @return 教师班级关系
      */
@@ -21,7 +21,7 @@ public interface DzTeacherClassMapper
 
     /**
      * 查询教师班级关系列表
-     * 
+     *
      * @param dzTeacherClass 教师班级关系
      * @return 教师班级关系集合
      */
@@ -29,15 +29,15 @@ public interface DzTeacherClassMapper
 
     /**
      * 新增教师班级关系
-     * 
+     *
      * @param dzTeacherClass 教师班级关系
      * @return 结果
      */
     public int insertDzTeacherClass(DzTeacherClass dzTeacherClass);
-
+    public int insertBatch(List<DzTeacherClass> list);
     /**
      * 修改教师班级关系
-     * 
+     *
      * @param dzTeacherClass 教师班级关系
      * @return 结果
      */
@@ -45,7 +45,7 @@ public interface DzTeacherClassMapper
 
     /**
      * 删除教师班级关系
-     * 
+     *
      * @param id 教师班级关系主键
      * @return 结果
      */
@@ -53,7 +53,7 @@ public interface DzTeacherClassMapper
 
     /**
      * 批量删除教师班级关系
-     * 
+     *
      * @param ids 需要删除的数据主键集合
      * @return 结果
      */

+ 9 - 9
ie-system/src/main/java/com/ruoyi/dz/service/IDzSchoolService.java

@@ -5,15 +5,15 @@ import com.ruoyi.dz.domain.DzSchool;
 
 /**
  * 机构校区Service接口
- * 
+ *
  * @author ruoyi
  * @date 2025-09-12
  */
-public interface IDzSchoolService 
+public interface IDzSchoolService
 {
     /**
      * 查询机构校区
-     * 
+     *
      * @param id 机构校区主键
      * @return 机构校区
      */
@@ -21,15 +21,15 @@ public interface IDzSchoolService
 
     /**
      * 查询机构校区列表
-     * 
+     *
      * @param dzSchool 机构校区
      * @return 机构校区集合
      */
     public List<DzSchool> selectDzSchoolList(DzSchool dzSchool);
-
+    public List<DzSchool> selectDzSchoolListByIds(List<Long> schoolIds);
     /**
      * 新增机构校区
-     * 
+     *
      * @param dzSchool 机构校区
      * @return 结果
      */
@@ -37,7 +37,7 @@ public interface IDzSchoolService
 
     /**
      * 修改机构校区
-     * 
+     *
      * @param dzSchool 机构校区
      * @return 结果
      */
@@ -45,7 +45,7 @@ public interface IDzSchoolService
 
     /**
      * 批量删除机构校区
-     * 
+     *
      * @param ids 需要删除的机构校区主键集合
      * @return 结果
      */
@@ -53,7 +53,7 @@ public interface IDzSchoolService
 
     /**
      * 删除机构校区信息
-     * 
+     *
      * @param id 机构校区主键
      * @return 结果
      */

+ 9 - 8
ie-system/src/main/java/com/ruoyi/dz/service/IDzTeacherClassService.java

@@ -5,15 +5,15 @@ import com.ruoyi.dz.domain.DzTeacherClass;
 
 /**
  * 教师班级关系Service接口
- * 
+ *
  * @author ruoyi
  * @date 2025-09-12
  */
-public interface IDzTeacherClassService 
+public interface IDzTeacherClassService
 {
     /**
      * 查询教师班级关系
-     * 
+     *
      * @param id 教师班级关系主键
      * @return 教师班级关系
      */
@@ -21,7 +21,7 @@ public interface IDzTeacherClassService
 
     /**
      * 查询教师班级关系列表
-     * 
+     *
      * @param dzTeacherClass 教师班级关系
      * @return 教师班级关系集合
      */
@@ -29,15 +29,16 @@ public interface IDzTeacherClassService
 
     /**
      * 新增教师班级关系
-     * 
+     *
      * @param dzTeacherClass 教师班级关系
      * @return 结果
      */
     public int insertDzTeacherClass(DzTeacherClass dzTeacherClass);
+    public int insertBatch(List<DzTeacherClass> list);
 
     /**
      * 修改教师班级关系
-     * 
+     *
      * @param dzTeacherClass 教师班级关系
      * @return 结果
      */
@@ -45,7 +46,7 @@ public interface IDzTeacherClassService
 
     /**
      * 批量删除教师班级关系
-     * 
+     *
      * @param ids 需要删除的教师班级关系主键集合
      * @return 结果
      */
@@ -53,7 +54,7 @@ public interface IDzTeacherClassService
 
     /**
      * 删除教师班级关系信息
-     * 
+     *
      * @param id 教师班级关系主键
      * @return 结果
      */

+ 14 - 8
ie-system/src/main/java/com/ruoyi/dz/service/impl/DzSchoolServiceImpl.java

@@ -10,19 +10,19 @@ import com.ruoyi.dz.service.IDzSchoolService;
 
 /**
  * 机构校区Service业务层处理
- * 
+ *
  * @author ruoyi
  * @date 2025-09-12
  */
 @Service
-public class DzSchoolServiceImpl implements IDzSchoolService 
+public class DzSchoolServiceImpl implements IDzSchoolService
 {
     @Autowired
     private DzSchoolMapper dzSchoolMapper;
 
     /**
      * 查询机构校区
-     * 
+     *
      * @param id 机构校区主键
      * @return 机构校区
      */
@@ -34,7 +34,7 @@ public class DzSchoolServiceImpl implements IDzSchoolService
 
     /**
      * 查询机构校区列表
-     * 
+     *
      * @param dzSchool 机构校区
      * @return 机构校区
      */
@@ -44,9 +44,15 @@ public class DzSchoolServiceImpl implements IDzSchoolService
         return dzSchoolMapper.selectDzSchoolList(dzSchool);
     }
 
+    @Override
+    public List<DzSchool> selectDzSchoolListByIds(List<Long> schoolIds)
+    {
+        return dzSchoolMapper.selectDzSchoolListByIds(schoolIds);
+    }
+
     /**
      * 新增机构校区
-     * 
+     *
      * @param dzSchool 机构校区
      * @return 结果
      */
@@ -59,7 +65,7 @@ public class DzSchoolServiceImpl implements IDzSchoolService
 
     /**
      * 修改机构校区
-     * 
+     *
      * @param dzSchool 机构校区
      * @return 结果
      */
@@ -72,7 +78,7 @@ public class DzSchoolServiceImpl implements IDzSchoolService
 
     /**
      * 批量删除机构校区
-     * 
+     *
      * @param ids 需要删除的机构校区主键
      * @return 结果
      */
@@ -84,7 +90,7 @@ public class DzSchoolServiceImpl implements IDzSchoolService
 
     /**
      * 删除机构校区信息
-     * 
+     *
      * @param id 机构校区主键
      * @return 结果
      */

+ 13 - 8
ie-system/src/main/java/com/ruoyi/dz/service/impl/DzTeacherClassServiceImpl.java

@@ -9,19 +9,19 @@ import com.ruoyi.dz.service.IDzTeacherClassService;
 
 /**
  * 教师班级关系Service业务层处理
- * 
+ *
  * @author ruoyi
  * @date 2025-09-12
  */
 @Service
-public class DzTeacherClassServiceImpl implements IDzTeacherClassService 
+public class DzTeacherClassServiceImpl implements IDzTeacherClassService
 {
     @Autowired
     private DzTeacherClassMapper dzTeacherClassMapper;
 
     /**
      * 查询教师班级关系
-     * 
+     *
      * @param id 教师班级关系主键
      * @return 教师班级关系
      */
@@ -33,7 +33,7 @@ public class DzTeacherClassServiceImpl implements IDzTeacherClassService
 
     /**
      * 查询教师班级关系列表
-     * 
+     *
      * @param dzTeacherClass 教师班级关系
      * @return 教师班级关系
      */
@@ -45,7 +45,7 @@ public class DzTeacherClassServiceImpl implements IDzTeacherClassService
 
     /**
      * 新增教师班级关系
-     * 
+     *
      * @param dzTeacherClass 教师班级关系
      * @return 结果
      */
@@ -55,9 +55,14 @@ public class DzTeacherClassServiceImpl implements IDzTeacherClassService
         return dzTeacherClassMapper.insertDzTeacherClass(dzTeacherClass);
     }
 
+    @Override
+    public int insertBatch(List<DzTeacherClass> list) {
+        return dzTeacherClassMapper.insertBatch(list);
+    }
+
     /**
      * 修改教师班级关系
-     * 
+     *
      * @param dzTeacherClass 教师班级关系
      * @return 结果
      */
@@ -69,7 +74,7 @@ public class DzTeacherClassServiceImpl implements IDzTeacherClassService
 
     /**
      * 批量删除教师班级关系
-     * 
+     *
      * @param ids 需要删除的教师班级关系主键
      * @return 结果
      */
@@ -81,7 +86,7 @@ public class DzTeacherClassServiceImpl implements IDzTeacherClassService
 
     /**
      * 删除教师班级关系信息
-     * 
+     *
      * @param id 教师班级关系主键
      * @return 结果
      */

+ 1 - 0
ie-system/src/main/java/com/ruoyi/ie/mapper/AMarjorPlanMapper.java

@@ -14,6 +14,7 @@ import org.apache.ibatis.annotations.Param;
  */
 public interface AMarjorPlanMapper 
 {
+    public List<AMarjorPlan> selectAMarjorPlanByIds(Long[] ids);
     public List<AMarjorPlan>  selectMajorForUniversity(@Param("universityId") Long universityId, @Param("year") Integer year, @Param("batchId") Long batchId);
     /**
      * 查询专业计划要求

+ 12 - 1
ie-system/src/main/java/com/ruoyi/learn/domain/LearnDirectedKnowledge.java

@@ -22,6 +22,9 @@ public class LearnDirectedKnowledge extends BaseEntity
     @Excel(name = "年度")
     private Integer year;
 
+    @Excel(name = "院校ID")
+    private Long universityId;
+
     /** 定向Key */
     @Excel(name = "定向Key")
     private String directKey;
@@ -82,7 +85,15 @@ public class LearnDirectedKnowledge extends BaseEntity
         return year;
     }
 
-    public void setDirectKey(String directKey) 
+    public Long getUniversityId() {
+        return universityId;
+    }
+
+    public void setUniversityId(Long universityId) {
+        this.universityId = universityId;
+    }
+
+    public void setDirectKey(String directKey)
     {
         this.directKey = directKey;
     }

+ 22 - 0
ie-system/src/main/java/com/ruoyi/learn/domain/LearnPaperQuestion.java

@@ -34,6 +34,12 @@ public class LearnPaperQuestion extends BaseEntity
     @Excel(name = "分值")
     private Integer score;
 
+    @Excel(name = "题型")
+    private String type;
+
+    @Excel(name = "难度")
+    private Integer diff;
+
     public void setId(Long id) 
     {
         this.id = id;
@@ -84,6 +90,22 @@ public class LearnPaperQuestion extends BaseEntity
         return score;
     }
 
+    public String getType() {
+        return type;
+    }
+
+    public void setType(String type) {
+        this.type = type;
+    }
+
+    public Integer getDiff() {
+        return diff;
+    }
+
+    public void setDiff(Integer diff) {
+        this.diff = diff;
+    }
+
     @Override
     public String toString() {
         return new ToStringBuilder(this,ToStringStyle.MULTI_LINE_STYLE)

+ 23 - 13
ie-system/src/main/java/com/ruoyi/learn/domain/LearnTestPaper.java

@@ -20,7 +20,11 @@ public class LearnTestPaper extends BaseEntity
 
     /** 批次id */
     @Excel(name = "批次id")
-    private Long batchId;
+    private Integer batchId;
+
+    /** 院校id */
+    @Excel(name = "院校id")
+    private Long universityId;
 
     /** 定向key */
     @Excel(name = "定向key")
@@ -32,7 +36,7 @@ public class LearnTestPaper extends BaseEntity
 
     /** 试卷生成条件 */
     @Excel(name = "试卷生成条件")
-    private String condions;
+    private String conditions;
 
     /** 创建人 */
     @Excel(name = "创建人")
@@ -48,17 +52,25 @@ public class LearnTestPaper extends BaseEntity
         return id;
     }
 
-    public void setBatchId(Long batchId) 
+    public void setBatchId(Integer batchId)
     {
         this.batchId = batchId;
     }
 
-    public Long getBatchId() 
+    public Integer getBatchId()
     {
         return batchId;
     }
 
-    public void setDirectKey(String directKey) 
+    public Long getUniversityId() {
+        return universityId;
+    }
+
+    public void setUniversityId(Long universityId) {
+        this.universityId = universityId;
+    }
+
+    public void setDirectKey(String directKey)
     {
         this.directKey = directKey;
     }
@@ -78,17 +90,15 @@ public class LearnTestPaper extends BaseEntity
         return paperId;
     }
 
-    public void setCondions(String condions) 
-    {
-        this.condions = condions;
+    public String getConditions() {
+        return conditions;
     }
 
-    public String getCondions() 
-    {
-        return condions;
+    public void setConditions(String conditions) {
+        this.conditions = conditions;
     }
 
-    public void setCreatorId(Long creatorId) 
+    public void setCreatorId(Long creatorId)
     {
         this.creatorId = creatorId;
     }
@@ -105,7 +115,7 @@ public class LearnTestPaper extends BaseEntity
             .append("batchId", getBatchId())
             .append("directKey", getDirectKey())
             .append("paperId", getPaperId())
-            .append("condions", getCondions())
+            .append("conditions", getConditions())
             .append("creatorId", getCreatorId())
             .append("createTime", getCreateTime())
             .toString();

+ 70 - 14
ie-system/src/main/java/com/ruoyi/learn/domain/TestPaperVO.java

@@ -1,38 +1,75 @@
 package com.ruoyi.learn.domain;
 
+import com.google.common.collect.Maps;
 import io.swagger.annotations.ApiModel;
 import io.swagger.annotations.ApiModelProperty;
+import lombok.AllArgsConstructor;
 import lombok.Data;
+import lombok.NoArgsConstructor;
 
 import java.util.List;
 import java.util.Map;
 
 public class TestPaperVO {
-    enum PaperBuildType {
+    public static enum PaperBuildType {
         Manual, Auto
     }
 
-    @ApiModel("批量组卷请求")
+    @ApiModel("基本组卷请求")
     @Data
     public static class TestPapersBuildReq {
-        @ApiModelProperty("班级")
-        List<Long> classIds;
-        @ApiModelProperty("考生类型")
-        List<String> examTypes;
-        @ApiModelProperty("组卷类型")
+        @ApiModelProperty("名称")
+        String title;
+        @ApiModelProperty("总分")
+        Integer total;
+
+        @ApiModelProperty("批次")
+        Integer batchId;
+        @ApiModelProperty("省份")
+        String location;
+        @ApiModelProperty("组卷方式")
         PaperBuildType buildType;
-        @ApiModelProperty("定向类型 null是全部")
+        @ApiModelProperty("是否定向")
         Boolean directType;
+
+        @ApiModelProperty("科目")
+        Long subjectId;
+
+        @ApiModelProperty("考生类型")
+        List<String> examTypes;
+        @ApiModelProperty("班级")
+        List<Long> classIds;
         @ApiModelProperty("院校")
         List<Long> universityIds;
         @ApiModelProperty("专业组(单院校有效)")
         List<String> groups;
-        @ApiModelProperty("科目")
-        List<Integer> subjectIds;
-        @ApiModelProperty("知识点")
-        List<Long> knowledgeIds;
-        @ApiModelProperty("题型要求")
-        Map<String, Integer> questionTypeNum;
+        @ApiModelProperty("专业组(单专业组有效)")
+        List<Long> planIds;
+
+        public Map<String, Object> toMap() {
+            Map<String, Object> map = Maps.newHashMap();
+            map.put("batchId", batchId);
+            map.put("location", location);
+            map.put("examTypes", examTypes);
+            map.put("classIds", classIds);
+            map.put("universityIds", universityIds);
+            map.put("groups", groups);
+            map.put("planIds", planIds);
+            return map;
+        }
+    }
+
+    @ApiModel("手动组卷请求")
+    @Data
+    public static class TestPapersBuildManualReq extends TestPapersBuildReq {
+        @ApiModelProperty("试题列表")
+        List<LearnPaperQuestion> questions;
+    }
+    @ApiModel("智能组卷请求")
+    @Data
+    public static class TestPapersBuildAutoReq extends TestPapersBuildReq {
+        @ApiModelProperty("试卷要求")
+        PaperDef paperDef;
     }
 
     @ApiModel("批量查询请求")
@@ -86,4 +123,23 @@ public class TestPaperVO {
         @ApiModelProperty("学生")
         List<Long> studentIds;
     }
+
+
+    @Data
+    public static class PaperDef {
+        Long total;
+        List<Long> knowIds;
+        Boolean fillExclude; // 不足时是否填充排除的
+        List<TestPaperVO.TypeDef> types;
+    }
+
+    @Data
+    @AllArgsConstructor
+    @NoArgsConstructor
+    public static class TypeDef {
+        String title;
+        String type;
+        Integer count;
+        Integer score;
+    }
 }

+ 2 - 0
ie-system/src/main/java/com/ruoyi/learn/mapper/LearnDirectedKnowledgeMapper.java

@@ -11,6 +11,8 @@ import com.ruoyi.learn.domain.LearnDirectedKnowledge;
  */
 public interface LearnDirectedKnowledgeMapper 
 {
+    public List<LearnDirectedKnowledge> selectByUniversityIds(Long[] universityIds);
+
     /**
      * 查询定向知识点关系
      * 

+ 4 - 0
ie-system/src/main/java/com/ruoyi/learn/mapper/LearnStudentMapper.java

@@ -1,6 +1,8 @@
 package com.ruoyi.learn.mapper;
 
 import java.util.List;
+import java.util.Map;
+
 import com.ruoyi.learn.domain.LearnStudent;
 
 /**
@@ -11,6 +13,8 @@ import com.ruoyi.learn.domain.LearnStudent;
  */
 public interface LearnStudentMapper 
 {
+    public List<LearnStudent> selectLearnStudentsByMap(Map cond);
+
     /**
      * 查询学生
      * 

+ 4 - 0
ie-system/src/main/java/com/ruoyi/learn/mapper/LearnTestPaperMapper.java

@@ -1,7 +1,9 @@
 package com.ruoyi.learn.mapper;
 
+import java.util.Collection;
 import java.util.List;
 import com.ruoyi.learn.domain.LearnTestPaper;
+import org.apache.ibatis.annotations.Param;
 
 /**
  * 批次测试卷Mapper接口
@@ -11,6 +13,8 @@ import com.ruoyi.learn.domain.LearnTestPaper;
  */
 public interface LearnTestPaperMapper 
 {
+    public List<LearnTestPaper> selectByBatchAndUniversityIds(@Param("batchId") Integer batchId, @Param("universityIds") Collection<Long> universityIds);
+
     /**
      * 查询批次测试卷
      * 

+ 9 - 8
ie-system/src/main/java/com/ruoyi/system/mapper/SysAreaMapper.java

@@ -5,15 +5,15 @@ import com.ruoyi.system.domain.SysArea;
 
 /**
  * 区域-省市区县Mapper接口
- * 
+ *
  * @author ruoyi
  * @date 2025-09-29
  */
-public interface SysAreaMapper 
+public interface SysAreaMapper
 {
     /**
      * 查询区域-省市区县
-     * 
+     *
      * @param areaId 区域-省市区县主键
      * @return 区域-省市区县
      */
@@ -21,15 +21,16 @@ public interface SysAreaMapper
 
     /**
      * 查询区域-省市区县列表
-     * 
+     *
      * @param sysArea 区域-省市区县
      * @return 区域-省市区县集合
      */
     public List<SysArea> selectSysAreaList(SysArea sysArea);
+    public List<SysArea> selectSysAreaListByIds(List<Long> ids);
 
     /**
      * 新增区域-省市区县
-     * 
+     *
      * @param sysArea 区域-省市区县
      * @return 结果
      */
@@ -37,7 +38,7 @@ public interface SysAreaMapper
 
     /**
      * 修改区域-省市区县
-     * 
+     *
      * @param sysArea 区域-省市区县
      * @return 结果
      */
@@ -45,7 +46,7 @@ public interface SysAreaMapper
 
     /**
      * 删除区域-省市区县
-     * 
+     *
      * @param areaId 区域-省市区县主键
      * @return 结果
      */
@@ -53,7 +54,7 @@ public interface SysAreaMapper
 
     /**
      * 批量删除区域-省市区县
-     * 
+     *
      * @param areaIds 需要删除的数据主键集合
      * @return 结果
      */

+ 1 - 0
ie-system/src/main/java/com/ruoyi/system/service/ISysAreaService.java

@@ -26,6 +26,7 @@ public interface ISysAreaService
      * @return 区域-省市区县集合
      */
     public List<SysArea> selectSysAreaList(SysArea sysArea);
+    public List<SysArea> selectSysAreaListByIds(List<Long> ids);
     public List<SysArea> listTree(SysArea criteria);
     /**
      * 新增区域-省市区县

+ 5 - 0
ie-system/src/main/java/com/ruoyi/system/service/impl/SysAreaServiceImpl.java

@@ -47,6 +47,11 @@ public class SysAreaServiceImpl implements ISysAreaService
     {
         return sysAreaMapper.selectSysAreaList(sysArea);
     }
+    @Override
+    public List<SysArea> selectSysAreaListByIds(List<Long> ids)
+    {
+        return sysAreaMapper.selectSysAreaListByIds(ids);
+    }
 
     @Override
     public List<SysArea> listTree(SysArea criteria) {

+ 22 - 19
ie-system/src/main/resources/mapper/dz/DzAgentMapper.xml

@@ -5,12 +5,12 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
 <mapper namespace="com.ruoyi.dz.mapper.DzAgentMapper">
 
     <resultMap type="DzAgent" id="DzAgentResult">
-        <result property="agentId"    column="agentId"    />
-        <result property="deptId"    column="deptId"    />
-        <result property="userId"    column="userId"    />
+        <result property="agentId"    column="agent_id"    />
+        <result property="deptId"    column="dept_id"    />
+        <result property="userId"    column="user_id"    />
         <result property="name"    column="name"    />
         <result property="phonenumber"    column="phonenumber"    />
-        <result property="parentId"    column="parentId"    />
+        <result property="parentId"    column="parent_id"    />
         <result property="schools"    column="schools"    />
         <result property="remark"    column="remark"    />
         <association property="dept"     javaType="SysDept"         resultMap="deptResult" />
@@ -27,37 +27,40 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
     </resultMap>
 
     <sql id="selectDzAgentVo">
-        select t1.agentId, t1.deptId, t1.userId, t1.name, t1.phonenumber, t1.parentId, t1.schools, t1.remark,
+        select t1.agent_id, t1.dept_id, t1.user_id, t1.name, t1.phonenumber, t1.parent_id, t1.schools, t1.remark,
         d.dept_id, d.parent_id, d.ancestors, d.dept_name, d.order_num, d.leader, d.status as dept_status
         from dz_agent t1
-        left join sys_dept d on t1.deptId = d.dept_id
+        left join sys_dept d on t1.dept_id = d.dept_id
     </sql>
 
     <select id="selectDzAgentList" parameterType="DzAgent" resultMap="DzAgentResult">
         <include refid="selectDzAgentVo"/>
         <where>
-            <if test="userId != null "> and t1.userId = #{userId}</if>
-            <if test="deptId != null "> and t1.deptId = #{deptId}</if>
+            <if test="userId != null "> and t1.user_id = #{userId}</if>
+<!--            <if test="deptId != null "> and t1.dept_id = #{deptId}</if>-->
+            <if test="deptId != null ">
+                AND (t1.dept_id = #{deptId} OR t1.dept_id IN ( SELECT t.dept_id FROM sys_dept t WHERE find_in_set(#{deptId}, ancestors) ))
+            </if>
             <if test="name != null  and name != ''"> and t1.name like concat('%', #{name}, '%')</if>
             <if test="phonenumber != null  and phonenumber != ''"> and t1.phonenumber  like concat('%', #{phonenumber}, '%')</if>
-            <if test="parentId != null "> and t1.parentId = #{parentId}</if>
+            <if test="parentId != null "> and t1.parent_id = #{parentId}</if>
             <if test="schools != null  and schools != ''"> and t1.schools = #{schools}</if>
         </where>
     </select>
 
     <select id="selectDzAgentByAgentId" parameterType="Long" resultMap="DzAgentResult">
         <include refid="selectDzAgentVo"/>
-        where t1.agentId = #{agentId}
+        where t1.agent_id = #{agentId}
     </select>
 
     <insert id="insertDzAgent" parameterType="DzAgent" useGeneratedKeys="true" keyProperty="agentId">
         insert into dz_agent
         <trim prefix="(" suffix=")" suffixOverrides=",">
-            <if test="deptId != null">deptId,</if>
-            <if test="userId != null">userId,</if>
+            <if test="deptId != null">dept_id,</if>
+            <if test="userId != null">user_id,</if>
             <if test="name != null">name,</if>
             <if test="phonenumber != null">phonenumber,</if>
-            <if test="parentId != null">parentId,</if>
+            <if test="parentId != null">parent_id,</if>
             <if test="schools != null">schools,</if>
             <if test="remark != null">remark,</if>
          </trim>
@@ -75,23 +78,23 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
     <update id="updateDzAgent" parameterType="DzAgent">
         update dz_agent
         <trim prefix="SET" suffixOverrides=",">
-            <if test="userId != null">userId = #{userId},</if>
-            <if test="deptId != null">deptId = #{deptId},</if>
+            <if test="userId != null">user_id = #{userId},</if>
+            <if test="deptId != null">dept_id = #{deptId},</if>
             <if test="name != null">name = #{name},</if>
             <if test="phonenumber != null">phonenumber = #{phonenumber},</if>
-            <if test="parentId != null">parentId = #{parentId},</if>
+            <if test="parentId != null">parent_id = #{parentId},</if>
             <if test="schools != null">schools = #{schools},</if>
             <if test="remark != null">remark = #{remark},</if>
         </trim>
-        where agentId = #{agentId}
+        where agent_id = #{agentId}
     </update>
 
     <delete id="deleteDzAgentByAgentId" parameterType="Long">
-        delete from dz_agent where agentId = #{agentId}
+        delete from dz_agent where agent_id = #{agentId}
     </delete>
 
     <delete id="deleteDzAgentByAgentIds" parameterType="String">
-        delete from dz_agent where agentId in
+        delete from dz_agent where agent_id in
         <foreach item="agentId" collection="array" open="(" separator="," close=")">
             #{agentId}
         </foreach>

+ 14 - 1
ie-system/src/main/resources/mapper/dz/DzSchoolMapper.xml

@@ -40,7 +40,10 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
         <include refid="selectDzSchoolVo"/>
         <where>
             <if test="name != null  and name != ''"> and t1.name like concat('%', #{name}, '%')</if>
-            <if test="deptId != null "> and t1.dept_id = #{deptId}</if>
+<!--            <if test="deptId != null "> and t1.dept_id = #{deptId}</if>-->
+            <if test="deptId != null ">
+                AND (t1.dept_id = #{deptId} OR t1.dept_id IN ( SELECT t.dept_id FROM sys_dept t WHERE find_in_set(#{deptId}, ancestors) ))
+            </if>
             <if test="location != null  and location != ''"> and t1.location = #{location}</if>
             <if test="pro != null "> and t1.pro = #{pro}</if>
             <if test="city != null "> and t1.city = #{city}</if>
@@ -49,6 +52,16 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
         </where>
     </select>
 
+    <select id="selectDzSchoolListByIds" parameterType="list" resultMap="DzSchoolResult">
+        <include refid="selectDzSchoolVo"/>
+        WHERE 1=1
+        AND t1.id IN
+        <foreach collection="list" item="id" open="(" separator="," close=")">
+            #{id}
+        </foreach>
+        ORDER BY t1.id ASC
+    </select>
+
     <select id="selectDzSchoolById" parameterType="Long" resultMap="DzSchoolResult">
         <include refid="selectDzSchoolVo"/>
         where t1.id = #{id}

+ 31 - 11
ie-system/src/main/resources/mapper/dz/DzTeacherClassMapper.xml

@@ -3,30 +3,30 @@
 PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
 "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
 <mapper namespace="com.ruoyi.dz.mapper.DzTeacherClassMapper">
-    
+
     <resultMap type="DzTeacherClass" id="DzTeacherClassResult">
         <result property="id"    column="id"    />
         <result property="teacherId"    column="teacher_id"    />
-        <result property="classesId"    column="classes_id"    />
+        <result property="classId"    column="class_id"    />
         <result property="status"    column="status"    />
         <result property="remark"    column="remark"    />
         <result property="outDate"    column="out_date"    />
     </resultMap>
 
     <sql id="selectDzTeacherClassVo">
-        select id, teacher_id, classes_id, status, remark, out_date from dz_teacher_class
+        select id, teacher_id, class_id, status, remark, out_date from dz_teacher_class
     </sql>
 
     <select id="selectDzTeacherClassList" parameterType="DzTeacherClass" resultMap="DzTeacherClassResult">
         <include refid="selectDzTeacherClassVo"/>
-        <where>  
+        <where>
             <if test="teacherId != null "> and teacher_id = #{teacherId}</if>
-            <if test="classesId != null "> and classes_id = #{classesId}</if>
+            <if test="classId != null "> and class_id = #{classId}</if>
             <if test="status != null "> and status = #{status}</if>
             <if test="outDate != null "> and out_date = #{outDate}</if>
         </where>
     </select>
-    
+
     <select id="selectDzTeacherClassById" parameterType="String" resultMap="DzTeacherClassResult">
         <include refid="selectDzTeacherClassVo"/>
         where id = #{id}
@@ -36,25 +36,45 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
         insert into dz_teacher_class
         <trim prefix="(" suffix=")" suffixOverrides=",">
             <if test="teacherId != null">teacher_id,</if>
-            <if test="classesId != null">classes_id,</if>
+            <if test="classId != null">class_id,</if>
             <if test="status != null">status,</if>
             <if test="remark != null">remark,</if>
             <if test="outDate != null">out_date,</if>
          </trim>
         <trim prefix="values (" suffix=")" suffixOverrides=",">
             <if test="teacherId != null">#{teacherId},</if>
-            <if test="classesId != null">#{classesId},</if>
+            <if test="classId != null">#{classId},</if>
             <if test="status != null">#{status},</if>
             <if test="remark != null">#{remark},</if>
             <if test="outDate != null">#{outDate},</if>
          </trim>
     </insert>
 
+    <!-- 批量插入教师班级关联关系 -->
+    <insert id="insertBatch" parameterType="java.util.List">
+        INSERT INTO dz_teacher_class (
+        teacher_id,
+        class_id,
+        status,
+        create_time,
+        create_by
+        ) VALUES
+        <foreach collection="list" item="item" separator=",">
+            (
+            #{item.teacherId},
+            #{item.classId},
+            #{item.status},
+            #{item.createTime},
+            #{item.createBy}
+            )
+        </foreach>
+    </insert>
+
     <update id="updateDzTeacherClass" parameterType="DzTeacherClass">
         update dz_teacher_class
         <trim prefix="SET" suffixOverrides=",">
             <if test="teacherId != null">teacher_id = #{teacherId},</if>
-            <if test="classesId != null">classes_id = #{classesId},</if>
+            <if test="classId != null">class_id = #{classId},</if>
             <if test="status != null">status = #{status},</if>
             <if test="remark != null">remark = #{remark},</if>
             <if test="outDate != null">out_date = #{outDate},</if>
@@ -67,9 +87,9 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
     </delete>
 
     <delete id="deleteDzTeacherClassByIds" parameterType="String">
-        delete from dz_teacher_class where id in 
+        delete from dz_teacher_class where id in
         <foreach item="id" collection="array" open="(" separator="," close=")">
             #{id}
         </foreach>
     </delete>
-</mapper>
+</mapper>

+ 5 - 0
ie-system/src/main/resources/mapper/ie/AMarjorPlanMapper.xml

@@ -107,6 +107,11 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
         where id = #{id}
     </select>
 
+    <select id="selectAMarjorPlanByIds" parameterType="Long" resultMap="AMarjorPlanResult">
+        <include refid="selectAMarjorPlanVo"/>
+        where id in <foreach item="id" collection="array" open="(" separator="," close=")">#{id}</foreach>
+    </select>
+
     <select id="selectMajorCodesByIds" parameterType="String" resultType="String">
         SELECT distinct m1.code majorCode
         FROM `a_marjor_plan` p

+ 11 - 1
ie-system/src/main/resources/mapper/learn/LearnDirectedKnowledgeMapper.xml

@@ -7,6 +7,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
     <resultMap type="LearnDirectedKnowledge" id="LearnDirectedKnowledgeResult">
         <result property="id"    column="id"    />
         <result property="year"    column="year"    />
+        <result property="universityId"    column="university_id"    />
         <result property="directKey"    column="direct_key"    />
         <result property="enrollFormula"    column="enrollFormula"    />
         <result property="cultural"    column="cultural"    />
@@ -20,13 +21,14 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
     </resultMap>
 
     <sql id="selectLearnDirectedKnowledgeVo">
-        select id, year, direct_key, enrollFormula, cultural, professional, subjects, question_types, knowledges, locations, examineeTypes, conditions from learn_directed_knowledge
+        select id, year, university_id, direct_key, enrollFormula, cultural, professional, subjects, question_types, knowledges, locations, examineeTypes, conditions from learn_directed_knowledge
     </sql>
 
     <select id="selectLearnDirectedKnowledgeList" parameterType="LearnDirectedKnowledge" resultMap="LearnDirectedKnowledgeResult">
         <include refid="selectLearnDirectedKnowledgeVo"/>
         <where>  
             <if test="year != null "> and year = #{year}</if>
+            <if test="universityId != null  and universityId != ''"> and university_id = #{universityId}</if>
             <if test="directKey != null  and directKey != ''"> and direct_key = #{directKey}</if>
             <if test="enrollFormula != null  and enrollFormula != ''"> and enrollFormula = #{enrollFormula}</if>
             <if test="cultural != null  and cultural != ''"> and cultural = #{cultural}</if>
@@ -45,10 +47,16 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
         where id = #{id}
     </select>
 
+    <select id="selectByUniversityIds" parameterType="Long" resultMap="LearnDirectedKnowledgeResult">
+        <include refid="selectLearnDirectedKnowledgeVo"/>
+        where id in <foreach item="id" collection="array" open="(" separator="," close=")">#{id}</foreach>
+    </select>
+
     <insert id="insertLearnDirectedKnowledge" parameterType="LearnDirectedKnowledge" useGeneratedKeys="true" keyProperty="id">
         insert into learn_directed_knowledge
         <trim prefix="(" suffix=")" suffixOverrides=",">
             <if test="year != null">year,</if>
+            <if test="universityId != null">university_id,</if>
             <if test="directKey != null">direct_key,</if>
             <if test="enrollFormula != null">enrollFormula,</if>
             <if test="cultural != null">cultural,</if>
@@ -62,6 +70,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
          </trim>
         <trim prefix="values (" suffix=")" suffixOverrides=",">
             <if test="year != null">#{year},</if>
+            <if test="universityId != null">#{universityId},</if>
             <if test="directKey != null">#{directKey},</if>
             <if test="enrollFormula != null">#{enrollFormula},</if>
             <if test="cultural != null">#{cultural},</if>
@@ -79,6 +88,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
         update learn_directed_knowledge
         <trim prefix="SET" suffixOverrides=",">
             <if test="year != null">year = #{year},</if>
+            <if test="universityId != null">university_id = #{universityId},</if>
             <if test="directKey != null">direct_key = #{directKey},</if>
             <if test="enrollFormula != null">enrollFormula = #{enrollFormula},</if>
             <if test="cultural != null">cultural = #{cultural},</if>

+ 19 - 1
ie-system/src/main/resources/mapper/learn/LearnStudentMapper.xml

@@ -9,11 +9,12 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
         <result property="classId"    column="class_id"    />
         <result property="campusId"    column="campus_id"    />
         <result property="universityId"    column="university_id"    />
+        <result property="majorGroup"    column="major_group"    />
         <result property="majorPlanId"    column="major_plan_id"    />
     </resultMap>
 
     <sql id="selectLearnStudentVo">
-        select student_id, class_id, campus_id, university_id, major_plan_id from learn_student
+        select student_id, class_id, campus_id, university_id, major_group,major_plan_id from learn_student
     </sql>
 
     <select id="selectLearnStudentList" parameterType="LearnStudent" resultMap="LearnStudentResult">
@@ -22,6 +23,20 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="directKey != null  and directKey != ''"> and direct_key = #{directKey}</if>
         </where>
     </select>
+
+    <select id="selectLearnStudentsByMap" parameterType="map" resultMap="LearnStudentResult">
+        SELECT distinct ls.`university_id`, ls.`major_group`, ls.`major_plan_id`
+        FROM `learn_student` ls
+        LEFT JOIN `learn_test_student` ts ON ts.`student_id` = ls.`student_id` AND ts.`batch_id` = #{batchId}
+        JOIN `sys_user` u ON ls.`student_id` = u.`user_id`
+        <where> ts.`id` IS NULL and u.`location` = #{location}
+            <if test="examTypes != null  and examTypes.size() > 0"> AND u.`exam_type` in <foreach item="id" collection="examTypes" open="(" separator="," close=")">#{id}</foreach></if>
+            <if test="classIds != null  and classIds.size() > 0"> AND ls.`class_id` IN <foreach item="id" collection="classIds" open="(" separator="," close=")">#{id}</foreach></if>
+            <if test="universityIds != null  and universityIds.size() > 0"> AND ls.`university_id` IN <foreach item="id" collection="universityIds" open="(" separator="," close=")">#{id}</foreach></if>
+            <if test="groups != null  and groups.size() > 0"> AND ls.`major_group` IN <foreach item="id" collection="groups" open="(" separator="," close=")">#{id}</foreach></if>
+            <if test="planIds != null  and planIds.size() > 0"> AND ls.`major_plan_id` IN <foreach item="id" collection="planIds" open="(" separator="," close=")">#{id}</foreach></if>
+        </where>
+    </select>
     
     <select id="selectLearnStudentByStudentId" parameterType="Long" resultMap="LearnStudentResult">
         <include refid="selectLearnStudentVo"/>
@@ -35,6 +50,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="classId != null">class_id,</if>
             <if test="campusId != null">campus_id,</if>
             <if test="universityId != null">university_id,</if>
+            <if test="majorGroup != null">major_group,</if>
             <if test="majorPlanId != null">major_plan_id,</if>
          </trim>
         <trim prefix="values (" suffix=")" suffixOverrides=",">
@@ -42,6 +58,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="classId != null">#{classId},</if>
             <if test="campusId != null">#{campusId},</if>
             <if test="universityId != null">#{universityId},</if>
+            <if test="majorGroup != null">#{majorGroup},</if>
             <if test="majorPlanId != null">#{majorPlanId},</if>
          </trim>
     </insert>
@@ -52,6 +69,7 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
             <if test="classId != null">class_id = #{classId},</if>
             <if test="campusId != null">campus_id = #{campusId},</if>
             <if test="universityId != null">university_id = #{universityId},</if>
+            <if test="majorGroup != null">major_group = #{majorGroup},</if>
             <if test="majorPlanId != null">major_plan_id = #{majorPlanId},</if>
         </trim>
         where student_id = #{studentId}

+ 20 - 10
ie-system/src/main/resources/mapper/learn/LearnTestPaperMapper.xml

@@ -7,28 +7,35 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
     <resultMap type="LearnTestPaper" id="LearnTestPaperResult">
         <result property="id"    column="id"    />
         <result property="batchId"    column="batch_id"    />
+        <result property="universityId"    column="university_id"    />
         <result property="directKey"    column="direct_key"    />
         <result property="paperId"    column="paper_id"    />
-        <result property="condions"    column="condions"    />
+        <result property="conditions"    column="conditions"    />
         <result property="creatorId"    column="creator_id"    />
         <result property="createTime"    column="create_time"    />
     </resultMap>
 
     <sql id="selectLearnTestPaperVo">
-        select id, batch_id, direct_key, paper_id, condions, creator_id, create_time from learn_test_paper
+        select id, batch_id, university_id, direct_key, paper_id, conditions, creator_id, create_time from learn_test_paper
     </sql>
 
     <select id="selectLearnTestPaperList" parameterType="LearnTestPaper" resultMap="LearnTestPaperResult">
         <include refid="selectLearnTestPaperVo"/>
         <where>  
             <if test="batchId != null "> and batch_id = #{batchId}</if>
+            <if test="universityId != null "> and university_id = #{universityId}</if>
             <if test="directKey != null  and directKey != ''"> and direct_key = #{directKey}</if>
             <if test="paperId != null "> and paper_id = #{paperId}</if>
-            <if test="condions != null  and condions != ''"> and condions = #{condions}</if>
+            <if test="conditions != null  and conditions != ''"> and conditions = #{conditions}</if>
             <if test="creatorId != null "> and creator_id = #{creatorId}</if>
         </where>
     </select>
-    
+
+    <select id="selectByBatchAndUniversityIds" resultMap="LearnTestPaperResult">
+        <include refid="selectLearnTestPaperVo"/>
+        where batch_id = #{batchId} and university_id in <foreach item="id" collection="universityIds" open="(" separator="," close=")">#{id}</foreach>
+    </select>
+
     <select id="selectLearnTestPaperById" parameterType="String" resultMap="LearnTestPaperResult">
         <include refid="selectLearnTestPaperVo"/>
         where id = #{id}
@@ -38,17 +45,19 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
         insert into learn_test_paper
         <trim prefix="(" suffix=")" suffixOverrides=",">
             <if test="batchId != null">batch_id,</if>
-            <if test="directKey != null and directKey != ''">direct_key,</if>
+            <if test="universityId != null">university_id,</if>
+            <if test="directKey != null ">direct_key,</if>
             <if test="paperId != null">paper_id,</if>
-            <if test="condions != null and condions != ''">condions,</if>
+            <if test="conditions != null">conditions,</if>
             <if test="creatorId != null">creator_id,</if>
             <if test="createTime != null">create_time,</if>
          </trim>
         <trim prefix="values (" suffix=")" suffixOverrides=",">
             <if test="batchId != null">#{batchId},</if>
-            <if test="directKey != null and directKey != ''">#{directKey},</if>
+            <if test="universityId != null">#{universityId},</if>
+            <if test="directKey != null">#{directKey},</if>
             <if test="paperId != null">#{paperId},</if>
-            <if test="condions != null and condions != ''">#{condions},</if>
+            <if test="conditions != null">#{conditions},</if>
             <if test="creatorId != null">#{creatorId},</if>
             <if test="createTime != null">#{createTime},</if>
          </trim>
@@ -58,9 +67,10 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
         update learn_test_paper
         <trim prefix="SET" suffixOverrides=",">
             <if test="batchId != null">batch_id = #{batchId},</if>
-            <if test="directKey != null and directKey != ''">direct_key = #{directKey},</if>
+            <if test="universityId != null">university_id = #{universityId},</if>
+            <if test="directKey != null">direct_key = #{directKey},</if>
             <if test="paperId != null">paper_id = #{paperId},</if>
-            <if test="condions != null and condions != ''">condions = #{condions},</if>
+            <if test="conditions != null">conditions = #{conditions},</if>
             <if test="creatorId != null">creator_id = #{creatorId},</if>
             <if test="createTime != null">create_time = #{createTime},</if>
         </trim>

+ 13 - 0
ie-system/src/main/resources/mapper/learn/LearnTestStudentMapper.xml

@@ -20,6 +20,19 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
         select id, batch_id, student_id, direct_key, paper_id, class_id, examinee_id, status, create_time from learn_test_student
     </sql>
 
+    <select id="selectDirectedStudentList" parameterType="LearnTestStudent" resultMap="LearnTestStudentResult">
+        <include refid="selectLearnTestStudentVo"/>
+        <where>
+            <if test="batchId != null "> and batch_id = #{batchId}</if>
+            <if test="studentId != null "> and student_id = #{studentId}</if>
+            <if test="directKey != null  and directKey != ''"> and direct_key = #{directKey}</if>
+            <if test="paperId != null "> and paper_id = #{paperId}</if>
+            <if test="classId != null "> and class_id = #{classId}</if>
+            <if test="examineeId != null "> and examinee_id = #{examineeId}</if>
+            <if test="status != null "> and status = #{status}</if>
+        </where>
+    </select>
+
     <select id="selectLearnTestStudentList" parameterType="LearnTestStudent" resultMap="LearnTestStudentResult">
         <include refid="selectLearnTestStudentVo"/>
         <where>  

+ 10 - 0
ie-system/src/main/resources/mapper/system/SysAreaMapper.xml

@@ -71,6 +71,16 @@ PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN"
         order by area_id asc
     </select>
 
+    <select id="selectSysAreaListByIds" parameterType="list" resultMap="SysAreaResult">
+        <include refid="selectSysAreaVo"/>
+        WHERE status = '0'
+        AND area_id IN
+        <foreach collection="list" item="id" open="(" separator="," close=")">
+            #{id}
+        </foreach>
+        ORDER BY area_id ASC
+    </select>
+
     <select id="selectSysAreaByAreaId" parameterType="Long" resultMap="SysAreaResult">
         <include refid="selectSysAreaVo"/>
         where area_id = #{areaId}

+ 29 - 29
sql/v_0.2_base.sql

@@ -1,38 +1,38 @@
 -- ----------------------------
--- 1、部门
+-- 1、机构
 -- ----------------------------
 drop table if exists sys_dept;
 create table sys_dept (
-  dept_id           bigint(20)      not null auto_increment    comment '部门id',
-  parent_id         bigint(20)      default 0                  comment '父部门id',
+  dept_id           bigint(20)      not null auto_increment    comment '机构id',
+  parent_id         bigint(20)      default 0                  comment '父机构id',
   ancestors         varchar(50)     default ''                 comment '祖级列表',
-  dept_name         varchar(30)     default ''                 comment '部门名称',
+  dept_name         varchar(30)     default ''                 comment '机构名称',
   order_num         int(4)          default 0                  comment '显示顺序',
   leader            varchar(20)     default null               comment '负责人',
   phone             varchar(11)     default null               comment '联系电话',
   email             varchar(50)     default null               comment '邮箱',
-  status            char(1)         default '0'                comment '部门状态(0正常 1停用)',
+  status            char(1)         default '0'                comment '机构状态(0正常 1停用)',
   del_flag          char(1)         default '0'                comment '删除标志(0代表存在 2代表删除)',
   create_by         varchar(64)     default ''                 comment '创建者',
   create_time 	    datetime                                   comment '创建时间',
   update_by         varchar(64)     default ''                 comment '更新者',
   update_time       datetime                                   comment '更新时间',
   primary key (dept_id)
-) engine=innodb auto_increment=200 comment = '部门表';
+) engine=innodb auto_increment=200 comment = '机构表';
 
 -- ----------------------------
--- 初始化-部门表数据
+-- 初始化-机构表数据
 -- ----------------------------
 insert into sys_dept values(100,  0,   '0',          '若依科技',   0, '若依', '15888888888', 'ry@qq.com', '0', '0', 'admin', sysdate(), '', null);
 insert into sys_dept values(101,  100, '0,100',      '深圳总公司', 1, '若依', '15888888888', 'ry@qq.com', '0', '0', 'admin', sysdate(), '', null);
 insert into sys_dept values(102,  100, '0,100',      '长沙分公司', 2, '若依', '15888888888', 'ry@qq.com', '0', '0', 'admin', sysdate(), '', null);
-insert into sys_dept values(103,  101, '0,100,101',  '研发部门',   1, '若依', '15888888888', 'ry@qq.com', '0', '0', 'admin', sysdate(), '', null);
-insert into sys_dept values(104,  101, '0,100,101',  '市场部门',   2, '若依', '15888888888', 'ry@qq.com', '0', '0', 'admin', sysdate(), '', null);
-insert into sys_dept values(105,  101, '0,100,101',  '测试部门',   3, '若依', '15888888888', 'ry@qq.com', '0', '0', 'admin', sysdate(), '', null);
-insert into sys_dept values(106,  101, '0,100,101',  '财务部门',   4, '若依', '15888888888', 'ry@qq.com', '0', '0', 'admin', sysdate(), '', null);
-insert into sys_dept values(107,  101, '0,100,101',  '运维部门',   5, '若依', '15888888888', 'ry@qq.com', '0', '0', 'admin', sysdate(), '', null);
-insert into sys_dept values(108,  102, '0,100,102',  '市场部门',   1, '若依', '15888888888', 'ry@qq.com', '0', '0', 'admin', sysdate(), '', null);
-insert into sys_dept values(109,  102, '0,100,102',  '财务部门',   2, '若依', '15888888888', 'ry@qq.com', '0', '0', 'admin', sysdate(), '', null);
+insert into sys_dept values(103,  101, '0,100,101',  '研发机构',   1, '若依', '15888888888', 'ry@qq.com', '0', '0', 'admin', sysdate(), '', null);
+insert into sys_dept values(104,  101, '0,100,101',  '市场机构',   2, '若依', '15888888888', 'ry@qq.com', '0', '0', 'admin', sysdate(), '', null);
+insert into sys_dept values(105,  101, '0,100,101',  '测试机构',   3, '若依', '15888888888', 'ry@qq.com', '0', '0', 'admin', sysdate(), '', null);
+insert into sys_dept values(106,  101, '0,100,101',  '财务机构',   4, '若依', '15888888888', 'ry@qq.com', '0', '0', 'admin', sysdate(), '', null);
+insert into sys_dept values(107,  101, '0,100,101',  '运维机构',   5, '若依', '15888888888', 'ry@qq.com', '0', '0', 'admin', sysdate(), '', null);
+insert into sys_dept values(108,  102, '0,100,102',  '市场机构',   1, '若依', '15888888888', 'ry@qq.com', '0', '0', 'admin', sysdate(), '', null);
+insert into sys_dept values(109,  102, '0,100,102',  '财务机构',   2, '若依', '15888888888', 'ry@qq.com', '0', '0', 'admin', sysdate(), '', null);
 
 
 -- ----------------------------
@@ -41,7 +41,7 @@ insert into sys_dept values(109,  102, '0,100,102',  '财务部门',   2, '若
 drop table if exists sys_user;
 create table sys_user (
   user_id           bigint(20)      not null auto_increment    comment '用户ID',
-  dept_id           bigint(20)      default null               comment '部门ID',
+  dept_id           bigint(20)      default null               comment '机构ID',
   user_name         varchar(30)     not null                   comment '用户账号',
   nick_name         varchar(30)     not null                   comment '用户昵称',
   user_type         varchar(2)      default '00'               comment '用户类型(00系统用户)',
@@ -107,9 +107,9 @@ create table sys_role (
   role_name            varchar(30)     not null                   comment '角色名称',
   role_key             varchar(100)    not null                   comment '角色权限字符串',
   role_sort            int(4)          not null                   comment '显示顺序',
-  data_scope           char(1)         default '1'                comment '数据范围(1:全部数据权限 2:自定数据权限 3:本部门数据权限 4:本部门及以下数据权限)',
+  data_scope           char(1)         default '1'                comment '数据范围(1:全部数据权限 2:自定数据权限 3:本机构数据权限 4:本机构及以下数据权限)',
   menu_check_strictly  tinyint(1)      default 1                  comment '菜单树选择项是否关联显示',
-  dept_check_strictly  tinyint(1)      default 1                  comment '部门树选择项是否关联显示',
+  dept_check_strictly  tinyint(1)      default 1                  comment '机构树选择项是否关联显示',
   status               char(1)         not null                   comment '角色状态(0正常 1停用)',
   del_flag             char(1)         default '0'                comment '删除标志(0代表存在 2代表删除)',
   create_by            varchar(64)     default ''                 comment '创建者',
@@ -167,7 +167,7 @@ insert into sys_menu values('4', '若依官网', '0', '4', 'http://ruoyi.vip', n
 insert into sys_menu values('100',  '用户管理', '1',   '1', 'user',       'system/user/index',        '', '', 1, 0, 'C', '0', '0', 'system:user:list',        'user',          'admin', sysdate(), '', null, '用户管理菜单');
 insert into sys_menu values('101',  '角色管理', '1',   '2', 'role',       'system/role/index',        '', '', 1, 0, 'C', '0', '0', 'system:role:list',        'peoples',       'admin', sysdate(), '', null, '角色管理菜单');
 insert into sys_menu values('102',  '菜单管理', '1',   '3', 'menu',       'system/menu/index',        '', '', 1, 0, 'C', '0', '0', 'system:menu:list',        'tree-table',    'admin', sysdate(), '', null, '菜单管理菜单');
-insert into sys_menu values('103',  '部门管理', '1',   '4', 'dept',       'system/dept/index',        '', '', 1, 0, 'C', '0', '0', 'system:dept:list',        'tree',          'admin', sysdate(), '', null, '部门管理菜单');
+insert into sys_menu values('103',  '机构管理', '1',   '4', 'dept',       'system/dept/index',        '', '', 1, 0, 'C', '0', '0', 'system:dept:list',        'tree',          'admin', sysdate(), '', null, '机构管理菜单');
 insert into sys_menu values('104',  '岗位管理', '1',   '5', 'post',       'system/post/index',        '', '', 1, 0, 'C', '0', '0', 'system:post:list',        'post',          'admin', sysdate(), '', null, '岗位管理菜单');
 insert into sys_menu values('105',  '字典管理', '1',   '6', 'dict',       'system/dict/index',        '', '', 1, 0, 'C', '0', '0', 'system:dict:list',        'dict',          'admin', sysdate(), '', null, '字典管理菜单');
 insert into sys_menu values('106',  '参数设置', '1',   '7', 'config',     'system/config/index',      '', '', 1, 0, 'C', '0', '0', 'system:config:list',      'edit',          'admin', sysdate(), '', null, '参数设置菜单');
@@ -204,11 +204,11 @@ insert into sys_menu values('1012', '菜单查询', '102', '1',  '', '', '', '',
 insert into sys_menu values('1013', '菜单新增', '102', '2',  '', '', '', '', 1, 0, 'F', '0', '0', 'system:menu:add',            '#', 'admin', sysdate(), '', null, '');
 insert into sys_menu values('1014', '菜单修改', '102', '3',  '', '', '', '', 1, 0, 'F', '0', '0', 'system:menu:edit',           '#', 'admin', sysdate(), '', null, '');
 insert into sys_menu values('1015', '菜单删除', '102', '4',  '', '', '', '', 1, 0, 'F', '0', '0', 'system:menu:remove',         '#', 'admin', sysdate(), '', null, '');
--- 部门管理按钮
-insert into sys_menu values('1016', '部门查询', '103', '1',  '', '', '', '', 1, 0, 'F', '0', '0', 'system:dept:query',          '#', 'admin', sysdate(), '', null, '');
-insert into sys_menu values('1017', '部门新增', '103', '2',  '', '', '', '', 1, 0, 'F', '0', '0', 'system:dept:add',            '#', 'admin', sysdate(), '', null, '');
-insert into sys_menu values('1018', '部门修改', '103', '3',  '', '', '', '', 1, 0, 'F', '0', '0', 'system:dept:edit',           '#', 'admin', sysdate(), '', null, '');
-insert into sys_menu values('1019', '部门删除', '103', '4',  '', '', '', '', 1, 0, 'F', '0', '0', 'system:dept:remove',         '#', 'admin', sysdate(), '', null, '');
+-- 机构管理按钮
+insert into sys_menu values('1016', '机构查询', '103', '1',  '', '', '', '', 1, 0, 'F', '0', '0', 'system:dept:query',          '#', 'admin', sysdate(), '', null, '');
+insert into sys_menu values('1017', '机构新增', '103', '2',  '', '', '', '', 1, 0, 'F', '0', '0', 'system:dept:add',            '#', 'admin', sysdate(), '', null, '');
+insert into sys_menu values('1018', '机构修改', '103', '3',  '', '', '', '', 1, 0, 'F', '0', '0', 'system:dept:edit',           '#', 'admin', sysdate(), '', null, '');
+insert into sys_menu values('1019', '机构删除', '103', '4',  '', '', '', '', 1, 0, 'F', '0', '0', 'system:dept:remove',         '#', 'admin', sysdate(), '', null, '');
 -- 岗位管理按钮
 insert into sys_menu values('1020', '岗位查询', '104', '1',  '', '', '', '', 1, 0, 'F', '0', '0', 'system:post:query',          '#', 'admin', sysdate(), '', null, '');
 insert into sys_menu values('1021', '岗位新增', '104', '2',  '', '', '', '', 1, 0, 'F', '0', '0', 'system:post:add',            '#', 'admin', sysdate(), '', null, '');
@@ -378,17 +378,17 @@ insert into sys_role_menu values ('2', '1059');
 insert into sys_role_menu values ('2', '1060');
 
 -- ----------------------------
--- 8、角色和部门关联表  角色1-N部门
+-- 8、角色和机构关联表  角色1-N机构
 -- ----------------------------
 drop table if exists sys_role_dept;
 create table sys_role_dept (
   role_id   bigint(20) not null comment '角色ID',
-  dept_id   bigint(20) not null comment '部门ID',
+  dept_id   bigint(20) not null comment '机构ID',
   primary key(role_id, dept_id)
-) engine=innodb comment = '角色和部门关联表';
+) engine=innodb comment = '角色和机构关联表';
 
 -- ----------------------------
--- 初始化-角色和部门关联表数据
+-- 初始化-角色和机构关联表数据
 -- ----------------------------
 insert into sys_role_dept values ('2', '100');
 insert into sys_role_dept values ('2', '101');
@@ -425,7 +425,7 @@ create table sys_oper_log (
   request_method    varchar(10)     default ''                 comment '请求方式',
   operator_type     int(1)          default 0                  comment '操作类别(0其它 1后台用户 2手机端用户)',
   oper_name         varchar(50)     default ''                 comment '操作人员',
-  dept_name         varchar(50)     default ''                 comment '部门名称',
+  dept_name         varchar(50)     default ''                 comment '机构名称',
   oper_url          varchar(255)    default ''                 comment '请求URL',
   oper_ip           varchar(128)    default ''                 comment '主机地址',
   oper_location     varchar(255)    default ''                 comment '操作地点',
@@ -701,4 +701,4 @@ create table gen_table_column (
   update_by         varchar(64)     default ''                 comment '更新者',
   update_time       datetime                                   comment '更新时间',
   primary key (column_id)
-) engine=innodb auto_increment=1 comment = '代码生成业务表字段';
+) engine=innodb auto_increment=1 comment = '代码生成业务表字段';

Bu fark içinde çok fazla dosya değişikliği olduğu için bazı dosyalar gösterilmiyor