Ver Fonte

Merge branch 'iteration-large-version' into jenkins

lex há 1 ano atrás
pai
commit
5d5d6c1a80
67 ficheiros alterados com 1968 adições e 860 exclusões
  1. 1 1
      dev-dist/sw.js
  2. 167 39
      package-lock.json
  3. 2 0
      package.json
  4. 5 0
      src/api/user.ts
  5. BIN
      src/common/images/icon-download.png
  6. BIN
      src/common/images/icon-sort-asc.png
  7. BIN
      src/common/images/icon-sort-default.png
  8. BIN
      src/common/images/icon-sort-desc.png
  9. 21 5
      src/components/card-type/index.module.less
  10. 171 33
      src/components/card-type/index.tsx
  11. 1 1
      src/components/layout/index.tsx
  12. 38 22
      src/components/layout/layoutTop.tsx
  13. 1 1
      src/views/attend-class/component/tools/pen.tsx
  14. BIN
      src/views/attend-class/image/right_icon1.png
  15. BIN
      src/views/attend-class/image/right_icon2.png
  16. BIN
      src/views/attend-class/image/right_icon9.png
  17. 54 8
      src/views/attend-class/index.module.less
  18. 211 316
      src/views/attend-class/index.tsx
  19. BIN
      src/views/attend-class/model/train-type/images/e-back-look.png
  20. BIN
      src/views/attend-class/model/train-type/images/e-delete.png
  21. 0 23
      src/views/attend-class/model/train-type/images/e-delete.svg
  22. BIN
      src/views/attend-class/model/train-type/images/e-edit.png
  23. 0 23
      src/views/attend-class/model/train-type/images/e-edit.svg
  24. BIN
      src/views/attend-class/model/train-type/images/e-message.png
  25. BIN
      src/views/attend-class/model/train-type/images/e-report-status.png
  26. BIN
      src/views/attend-class/model/train-type/images/e-report.png
  27. 0 31
      src/views/attend-class/model/train-type/images/e-tag.svg
  28. BIN
      src/views/attend-class/model/train-type/images/p-delete.png
  29. 0 23
      src/views/attend-class/model/train-type/images/p-delete.svg
  30. BIN
      src/views/attend-class/model/train-type/images/p-edit.png
  31. 0 23
      src/views/attend-class/model/train-type/images/p-edit.svg
  32. 5 4
      src/views/attend-class/model/train-type/index.module.less
  33. 140 18
      src/views/attend-class/model/train-type/index.tsx
  34. 2 2
      src/views/classList/api.ts
  35. 5 6
      src/views/classList/components/afterWork.tsx
  36. 3 3
      src/views/classList/components/afterWorkDetail.tsx
  37. 88 28
      src/views/classList/components/classStudent.tsx
  38. 4 4
      src/views/classList/components/testRecode.tsx
  39. 4 0
      src/views/classList/index.module.less
  40. 1 1
      src/views/home/components/practiceData.tsx
  41. 108 22
      src/views/home/components/practiceRanking.tsx
  42. 11 0
      src/views/home/index2.module.less
  43. 24 1
      src/views/login/components/codeLogin.tsx
  44. 11 0
      src/views/login/components/forgotPassword.tsx
  45. 24 3
      src/views/login/components/pwdLogin.tsx
  46. 4 1
      src/views/login/index.tsx
  47. 1 1
      src/views/natural-resources/components/my-collect/index.tsx
  48. 5 5
      src/views/natural-resources/components/my-collect/search-group-resources.tsx
  49. 1 0
      src/views/natural-resources/components/my-resources/index.tsx
  50. 56 43
      src/views/prepare-lessons/components/lesson-main/courseware/index.tsx
  51. 1 4
      src/views/prepare-lessons/components/resource-main/components/resource-item/index.tsx
  52. 7 7
      src/views/prepare-lessons/components/resource-main/components/resource-item/resource-search-group/index.tsx
  53. 1 1
      src/views/prepare-lessons/components/resource-main/components/select-music/index.tsx
  54. 15 22
      src/views/prepare-lessons/model/select-resources/index.tsx
  55. 149 0
      src/views/prepare-lessons/model/select-resources/select-item/class-search-group/index.module.less
  56. 249 0
      src/views/prepare-lessons/model/select-resources/select-item/class-search-group/index.tsx
  57. 21 58
      src/views/prepare-lessons/model/select-resources/select-item/index.tsx
  58. 7 7
      src/views/prepare-lessons/model/select-resources/select-item/resource-search-group/index.tsx
  59. 5 0
      src/views/studentList/components/baseInfo.tsx
  60. 9 6
      src/views/studentList/components/evaluationRecords.tsx
  61. 3 3
      src/views/studentList/components/practiceData.tsx
  62. 31 33
      src/views/studentList/components/studentAfterWork.tsx
  63. 24 0
      src/views/studentList/index.module.less
  64. 51 13
      src/views/studentList/index.tsx
  65. 6 15
      src/views/studentList/modals/studentTraomomhDetails.tsx
  66. 16 0
      src/views/studentList/modals/update-student/index.module.less
  67. 204 0
      src/views/studentList/modals/update-student/index.tsx

+ 1 - 1
dev-dist/sw.js

@@ -87,7 +87,7 @@ define(['./workbox-5357ef54'], function (workbox) {
       },
       {
         url: 'index.html',
-        revision: '0.1ul47euvio8'
+        revision: '0.s728ohtnejg'
       }
     ],
     {}

+ 167 - 39
package-lock.json

@@ -21,7 +21,9 @@
         "dayjs": "^1.11.7",
         "echarts": "^5.4.2",
         "eventemitter3": "^5.0.1",
+        "file-saver": "^2.0.5",
         "html2canvas": "^1.4.1",
+        "jszip": "^3.10.1",
         "lib-flexible": "^0.3.2",
         "lodash": "^4.17.21",
         "lodash-es": "^4.17.21",
@@ -2988,11 +2990,6 @@
         "url": "https://github.com/sponsors/antfu"
       }
     },
-<<<<<<< HEAD
-=======
-<<<<<<< HEAD
-=======
->>>>>>> startLogin
     "node_modules/@xmldom/xmldom": {
       "version": "0.8.10",
       "resolved": "https://registry.npmmirror.com/@xmldom/xmldom/-/xmldom-0.8.10.tgz",
@@ -3001,10 +2998,6 @@
         "node": ">=10.0.0"
       }
     },
-<<<<<<< HEAD
-=======
->>>>>>> online
->>>>>>> startLogin
     "node_modules/acorn": {
       "version": "8.10.0",
       "license": "MIT",
@@ -3771,11 +3764,11 @@
         "url": "https://opencollective.com/core-js"
       }
     },
-<<<<<<< HEAD
-=======
-<<<<<<< HEAD
-=======
->>>>>>> startLogin
+    "node_modules/core-util-is": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmmirror.com/core-util-is/-/core-util-is-1.0.3.tgz",
+      "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ=="
+    },
     "node_modules/cos-js-sdk-v5": {
       "version": "1.4.20",
       "resolved": "https://registry.npmmirror.com/cos-js-sdk-v5/-/cos-js-sdk-v5-1.4.20.tgz",
@@ -3784,10 +3777,6 @@
         "@xmldom/xmldom": "^0.8.6"
       }
     },
-<<<<<<< HEAD
-=======
->>>>>>> online
->>>>>>> startLogin
     "node_modules/croact": {
       "version": "1.0.4",
       "license": "MIT",
@@ -4795,6 +4784,11 @@
         "node": "^10.12.0 || >=12.0.0"
       }
     },
+    "node_modules/file-saver": {
+      "version": "2.0.5",
+      "resolved": "https://registry.npmmirror.com/file-saver/-/file-saver-2.0.5.tgz",
+      "integrity": "sha512-P9bmyZ3h/PRG+Nzga+rbdI4OEpNDzAVyy74uVO9ATgzLK6VtAsYybF/+TOCvrc0MO793d6+42lLyZTw7/ArVzA=="
+    },
     "node_modules/filelist": {
       "version": "1.0.4",
       "license": "Apache-2.0",
@@ -5467,6 +5461,11 @@
         "node": ">=0.10.0"
       }
     },
+    "node_modules/immediate": {
+      "version": "3.0.6",
+      "resolved": "https://registry.npmmirror.com/immediate/-/immediate-3.0.6.tgz",
+      "integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ=="
+    },
     "node_modules/import-fresh": {
       "version": "3.3.0",
       "dev": true,
@@ -6391,6 +6390,49 @@
         "node": ">=0.10.0"
       }
     },
+    "node_modules/jszip": {
+      "version": "3.10.1",
+      "resolved": "https://registry.npmmirror.com/jszip/-/jszip-3.10.1.tgz",
+      "integrity": "sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==",
+      "dependencies": {
+        "lie": "~3.3.0",
+        "pako": "~1.0.2",
+        "readable-stream": "~2.3.6",
+        "setimmediate": "^1.0.5"
+      }
+    },
+    "node_modules/jszip/node_modules/isarray": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmmirror.com/isarray/-/isarray-1.0.0.tgz",
+      "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ=="
+    },
+    "node_modules/jszip/node_modules/readable-stream": {
+      "version": "2.3.8",
+      "resolved": "https://registry.npmmirror.com/readable-stream/-/readable-stream-2.3.8.tgz",
+      "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==",
+      "dependencies": {
+        "core-util-is": "~1.0.0",
+        "inherits": "~2.0.3",
+        "isarray": "~1.0.0",
+        "process-nextick-args": "~2.0.0",
+        "safe-buffer": "~5.1.1",
+        "string_decoder": "~1.1.1",
+        "util-deprecate": "~1.0.1"
+      }
+    },
+    "node_modules/jszip/node_modules/safe-buffer": {
+      "version": "5.1.2",
+      "resolved": "https://registry.npmmirror.com/safe-buffer/-/safe-buffer-5.1.2.tgz",
+      "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
+    },
+    "node_modules/jszip/node_modules/string_decoder": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmmirror.com/string_decoder/-/string_decoder-1.1.1.tgz",
+      "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
+      "dependencies": {
+        "safe-buffer": "~5.1.0"
+      }
+    },
     "node_modules/keycode": {
       "version": "2.2.1",
       "license": "MIT"
@@ -6461,6 +6503,14 @@
       "version": "0.3.2",
       "license": "ISC"
     },
+    "node_modules/lie": {
+      "version": "3.3.0",
+      "resolved": "https://registry.npmmirror.com/lie/-/lie-3.3.0.tgz",
+      "integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==",
+      "dependencies": {
+        "immediate": "~3.0.5"
+      }
+    },
     "node_modules/liftoff": {
       "version": "4.0.0",
       "dev": true,
@@ -7378,6 +7428,11 @@
         "url": "https://github.com/sponsors/sindresorhus"
       }
     },
+    "node_modules/pako": {
+      "version": "1.0.11",
+      "resolved": "https://registry.npmmirror.com/pako/-/pako-1.0.11.tgz",
+      "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw=="
+    },
     "node_modules/param-case": {
       "version": "3.0.4",
       "dev": true,
@@ -7799,6 +7854,11 @@
         "node": ">= 0.6.0"
       }
     },
+    "node_modules/process-nextick-args": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmmirror.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
+      "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag=="
+    },
     "node_modules/proxy-from-env": {
       "version": "1.1.0",
       "license": "MIT"
@@ -8401,6 +8461,11 @@
         "randombytes": "^2.1.0"
       }
     },
+    "node_modules/setimmediate": {
+      "version": "1.0.5",
+      "resolved": "https://registry.npmmirror.com/setimmediate/-/setimmediate-1.0.5.tgz",
+      "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA=="
+    },
     "node_modules/shebang-command": {
       "version": "2.0.0",
       "dev": true,
@@ -9348,7 +9413,6 @@
     },
     "node_modules/util-deprecate": {
       "version": "1.0.2",
-      "dev": true,
       "license": "MIT"
     },
     "node_modules/utrie": {
@@ -12176,20 +12240,11 @@
         "vue-demi": ">=0.14.5"
       }
     },
-<<<<<<< HEAD
-=======
-<<<<<<< HEAD
-=======
->>>>>>> startLogin
     "@xmldom/xmldom": {
       "version": "0.8.10",
       "resolved": "https://registry.npmmirror.com/@xmldom/xmldom/-/xmldom-0.8.10.tgz",
       "integrity": "sha512-2WALfTl4xo2SkGCYRt6rDTFfk9R1czmBvUQy12gK2KuRKIpWEhcbbzy8EZXtz/jkRqHX8bFEc6FC1HjX4TUWYw=="
     },
-<<<<<<< HEAD
-=======
->>>>>>> online
->>>>>>> startLogin
     "acorn": {
       "version": "8.10.0"
     },
@@ -12623,11 +12678,11 @@
         "browserslist": "^4.21.9"
       }
     },
-<<<<<<< HEAD
-=======
-<<<<<<< HEAD
-=======
->>>>>>> startLogin
+    "core-util-is": {
+      "version": "1.0.3",
+      "resolved": "https://registry.npmmirror.com/core-util-is/-/core-util-is-1.0.3.tgz",
+      "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ=="
+    },
     "cos-js-sdk-v5": {
       "version": "1.4.20",
       "resolved": "https://registry.npmmirror.com/cos-js-sdk-v5/-/cos-js-sdk-v5-1.4.20.tgz",
@@ -12636,10 +12691,6 @@
         "@xmldom/xmldom": "^0.8.6"
       }
     },
-<<<<<<< HEAD
-=======
->>>>>>> online
->>>>>>> startLogin
     "croact": {
       "version": "1.0.4",
       "requires": {
@@ -13295,6 +13346,11 @@
         "flat-cache": "^3.0.4"
       }
     },
+    "file-saver": {
+      "version": "2.0.5",
+      "resolved": "https://registry.npmmirror.com/file-saver/-/file-saver-2.0.5.tgz",
+      "integrity": "sha512-P9bmyZ3h/PRG+Nzga+rbdI4OEpNDzAVyy74uVO9ATgzLK6VtAsYybF/+TOCvrc0MO793d6+42lLyZTw7/ArVzA=="
+    },
     "filelist": {
       "version": "1.0.4",
       "requires": {
@@ -13681,6 +13737,11 @@
       "dev": true,
       "optional": true
     },
+    "immediate": {
+      "version": "3.0.6",
+      "resolved": "https://registry.npmmirror.com/immediate/-/immediate-3.0.6.tgz",
+      "integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ=="
+    },
     "import-fresh": {
       "version": "3.3.0",
       "dev": true,
@@ -14206,6 +14267,51 @@
     "jsonpointer": {
       "version": "5.0.1"
     },
+    "jszip": {
+      "version": "3.10.1",
+      "resolved": "https://registry.npmmirror.com/jszip/-/jszip-3.10.1.tgz",
+      "integrity": "sha512-xXDvecyTpGLrqFrvkrUSoxxfJI5AH7U8zxxtVclpsUtMCq4JQ290LY8AW5c7Ggnr/Y/oK+bQMbqK2qmtk3pN4g==",
+      "requires": {
+        "lie": "~3.3.0",
+        "pako": "~1.0.2",
+        "readable-stream": "~2.3.6",
+        "setimmediate": "^1.0.5"
+      },
+      "dependencies": {
+        "isarray": {
+          "version": "1.0.0",
+          "resolved": "https://registry.npmmirror.com/isarray/-/isarray-1.0.0.tgz",
+          "integrity": "sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ=="
+        },
+        "readable-stream": {
+          "version": "2.3.8",
+          "resolved": "https://registry.npmmirror.com/readable-stream/-/readable-stream-2.3.8.tgz",
+          "integrity": "sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==",
+          "requires": {
+            "core-util-is": "~1.0.0",
+            "inherits": "~2.0.3",
+            "isarray": "~1.0.0",
+            "process-nextick-args": "~2.0.0",
+            "safe-buffer": "~5.1.1",
+            "string_decoder": "~1.1.1",
+            "util-deprecate": "~1.0.1"
+          }
+        },
+        "safe-buffer": {
+          "version": "5.1.2",
+          "resolved": "https://registry.npmmirror.com/safe-buffer/-/safe-buffer-5.1.2.tgz",
+          "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g=="
+        },
+        "string_decoder": {
+          "version": "1.1.1",
+          "resolved": "https://registry.npmmirror.com/string_decoder/-/string_decoder-1.1.1.tgz",
+          "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==",
+          "requires": {
+            "safe-buffer": "~5.1.0"
+          }
+        }
+      }
+    },
     "keycode": {
       "version": "2.2.1"
     },
@@ -14252,6 +14358,14 @@
     "lib-flexible": {
       "version": "0.3.2"
     },
+    "lie": {
+      "version": "3.3.0",
+      "resolved": "https://registry.npmmirror.com/lie/-/lie-3.3.0.tgz",
+      "integrity": "sha512-UaiMJzeWRlEujzAuw5LokY1L5ecNQYZKfmyZ9L7wDHb/p5etKaxXhohBcrw0EYby+G/NA52vRSN4N39dxHAIwQ==",
+      "requires": {
+        "immediate": "~3.0.5"
+      }
+    },
     "liftoff": {
       "version": "4.0.0",
       "dev": true,
@@ -14819,6 +14933,11 @@
         "aggregate-error": "^3.0.0"
       }
     },
+    "pako": {
+      "version": "1.0.11",
+      "resolved": "https://registry.npmmirror.com/pako/-/pako-1.0.11.tgz",
+      "integrity": "sha512-4hLB8Py4zZce5s4yd9XzopqwVv/yGNhV1Bl8NTmCq1763HeK2+EwVTv+leGeL13Dnh2wfbqowVPXCIO0z4taYw=="
+    },
     "param-case": {
       "version": "3.0.4",
       "dev": true,
@@ -15060,6 +15179,11 @@
     "process": {
       "version": "0.5.2"
     },
+    "process-nextick-args": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmmirror.com/process-nextick-args/-/process-nextick-args-2.0.1.tgz",
+      "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag=="
+    },
     "proxy-from-env": {
       "version": "1.1.0"
     },
@@ -15428,6 +15552,11 @@
         "randombytes": "^2.1.0"
       }
     },
+    "setimmediate": {
+      "version": "1.0.5",
+      "resolved": "https://registry.npmmirror.com/setimmediate/-/setimmediate-1.0.5.tgz",
+      "integrity": "sha512-MATJdZp8sLqDl/68LfQmbP8zKPLQNV6BIZoIgrscFDQ+RsvK/BxeDQOgyxKKoh0y/8h3BqVFnCqQ/gd+reiIXA=="
+    },
     "shebang-command": {
       "version": "2.0.0",
       "dev": true,
@@ -15984,8 +16113,7 @@
       "version": "1.1.12"
     },
     "util-deprecate": {
-      "version": "1.0.2",
-      "dev": true
+      "version": "1.0.2"
     },
     "utrie": {
       "version": "1.0.2",

+ 2 - 0
package.json

@@ -35,7 +35,9 @@
     "dayjs": "^1.11.7",
     "echarts": "^5.4.2",
     "eventemitter3": "^5.0.1",
+    "file-saver": "^2.0.5",
     "html2canvas": "^1.4.1",
+    "jszip": "^3.10.1",
     "lib-flexible": "^0.3.2",
     "lodash": "^4.17.21",
     "lodash-es": "^4.17.21",

+ 5 - 0
src/api/user.ts

@@ -55,3 +55,8 @@ export const suggestMessageUnread = (params?: any) => {
     params
   });
 };
+
+/** 曲谱详情 */
+export const api_musicSheetDetail = (data: any) => {
+  return request.get(`/edu-app/musicSheet/detail/${data}`);
+};

BIN
src/common/images/icon-download.png


BIN
src/common/images/icon-sort-asc.png


BIN
src/common/images/icon-sort-default.png


BIN
src/common/images/icon-sort-desc.png


+ 21 - 5
src/components/card-type/index.module.less

@@ -104,7 +104,7 @@
     display: flex;
     align-items: center;
     justify-content: space-between;
-
+    padding: 3px 0;
   }
 
   .title {
@@ -119,7 +119,7 @@
     .titleContent {
       padding-left: 6px;
       font-size: 16px;
-      max-width: 180px;
+      max-width: 160px;
       color: #131415;
       font-weight: 600 !important;
       flex: 1;
@@ -127,15 +127,32 @@
   }
 
   // 收藏按钮
+  .btnGroup {
+    position: absolute;
+    right: 12px;
+    display: flex;
+    align-items: center;
+
+    :global {
+      .n-spin-content {
+        display: flex;
+        align-items: center;
+      }
+    }
+  }
 
 
+  .btnItem+.btnItem {
+    margin-left: 12px;
+  }
+
   .iconCollect {
     width: 34px;
     height: 34px;
     // background: url('../../common/images/icon-collect-default.png') no-repeat center;
     // background-size: contain;
-    position: absolute;
-    right: 0;
+    // position: absolute;
+    // right: 0;
     transition: transform .2s ease;
 
     &:hover {
@@ -146,7 +163,6 @@
     &.isCollect {
       cursor: pointer;
     }
-
   }
 
   .iconDiv {

+ 171 - 33
src/components/card-type/index.tsx

@@ -1,6 +1,6 @@
 import { PropType, Transition, defineComponent, ref } from 'vue';
 import styles from './index.module.less';
-import { NButton, NCard, NImage, NModal } from 'naive-ui';
+import { NButton, NCard, NImage, NModal, NSpin, useMessage } from 'naive-ui';
 import iconImage from '@common/images/icon-image.png';
 import iconVideo from '@common/images/icon-video.png';
 import iconAudio from '@common/images/icon-audio.png';
@@ -8,10 +8,14 @@ import iconMusic from '@common/images/icon-music.png';
 import iconPPT from '@common/images/icon-ppt.png';
 import iconCollectDefault from '@common/images/icon-collect-default.png';
 import iconCollectActive from '@common/images/icon-collect-active.png';
+import iconDownload from '@common/images/icon-download.png';
 import TheNoticeBar from '../TheNoticeBar';
 import AudioPlayer from './audio-player';
 import VideoPlayer from './video-player';
 import { PageEnum } from '/src/enums/pageEnum';
+import { api_musicSheetDetail } from '/src/api/user';
+import JSZip, { file } from 'jszip';
+import { saveAs } from 'file-saver';
 
 type itemType = {
   id: string | number;
@@ -75,6 +79,11 @@ export default defineComponent({
     offShelf: {
       type: Boolean,
       default: false
+    },
+    /** 是否可以下载 */
+    isDownload: {
+      type: Boolean,
+      default: false
     }
   },
   /**
@@ -85,7 +94,9 @@ export default defineComponent({
    */
   emits: ['click', 'collect', 'add', 'offShelf'],
   setup(props, { emit }) {
+    const message = useMessage();
     const isAnimation = ref(false);
+    const downloadStatus = ref(false);
     const formatType = (type: string) => {
       let typeImg = iconImage;
       switch (type) {
@@ -108,9 +119,123 @@ export default defineComponent({
       return typeImg;
     };
 
+    // 获取文件blob格式
+    const getFileBlob = (url: string) => {
+      return new Promise((resolve, reject) => {
+        const request = new XMLHttpRequest();
+        request.open('GET', url, true);
+        request.responseType = 'blob';
+        request.onload = (res: any) => {
+          if (res.target.status == 200) {
+            resolve(res.target.response);
+          } else {
+            reject(res);
+          }
+        };
+        request.send();
+      });
+    };
+
+    // 多个文件下载
+    const downLoadMultiFile = (files: any, filesName: string) => {
+      const zip = new JSZip();
+      const result = [];
+      for (const i in files) {
+        const promise = getFileBlob(files[i].url).then((res: any) => {
+          zip.file(files[i].name, res, { binary: true });
+        });
+        result.push(promise);
+      }
+      Promise.all(result)
+        .then(() => {
+          zip.generateAsync({ type: 'blob' }).then(res => {
+            saveAs(
+              res,
+              filesName
+                ? filesName + Date.now() + '.zip'
+                : `文件夹${Date.now()}.zip`
+            );
+          });
+        })
+        .catch(() => {
+          message.error('下载失败');
+        });
+
+      downloadStatus.value = false;
+    };
+
+    const downloadFile = (filename: string, fileUrl: string) => {
+      // 发起Fetch请求
+      fetch(fileUrl)
+        .then(response => response.blob())
+        .then(blob => {
+          saveAs(blob, filename);
+          setTimeout(() => {
+            downloadStatus.value = false;
+          }, 100);
+        })
+        .catch(() => {
+          message.error('下载失败');
+        });
+
+      downloadStatus.value = false;
+    };
+
+    const getFileName = (url: any) => {
+      // 使用正则表达式获取文件名
+      const tempUrl = url.split('?');
+      const fileNameRegex = /\/([^\\/]+)$/; // 匹配最后一个斜杠后的内容
+      const match = tempUrl[0].match(fileNameRegex);
+
+      if (match) {
+        return match[1];
+      } else {
+        return '';
+      }
+    };
+    const onDownload = async (e: MouseEvent) => {
+      e.stopPropagation();
+      e.preventDefault();
+      const item = props.item;
+      if (!item.content) {
+        message.error('下载失败');
+        return;
+      }
+      if (downloadStatus.value) return false;
+      downloadStatus.value = true;
+      const suffix: any = item.content?.split('.');
+      const fileName = item.title + '.' + suffix[suffix?.length - 1];
+      if (item.type === 'MUSIC') {
+        const { data } = await api_musicSheetDetail(item.content);
+        const urls = [];
+        if (data.xmlFileUrl) {
+          urls.push({
+            url: data.xmlFileUrl,
+            name: getFileName(data.xmlFileUrl)
+          });
+        }
+        if (data.background && data.background.length > 0) {
+          data.background.forEach((item: any) => {
+            urls.push({
+              url: item.audioFileUrl,
+              name: getFileName(item.audioFileUrl)
+            });
+          });
+        }
+        downLoadMultiFile(urls, item.title);
+
+        // setTimeout(() => {
+        //   downloadStatus.value = false;
+        // }, 1000);
+      } else {
+        downloadFile(fileName, item.content);
+      }
+    };
+
     return () => (
       <div
         onClick={() => emit('click', props.item)}
+        key={props.item.id}
         draggable={!props.draggable ? false : props.item.exist ? false : true}
         class={[
           styles['card-section'],
@@ -217,40 +342,53 @@ export default defineComponent({
                   </span>
                 </div>
                 {/* 收藏 */}
-                {props.isShowCollect && (
-                  <div
-                    class={[styles.iconCollect, styles.iconDiv]}
-                    onClick={(e: MouseEvent) => {
-                      e.stopPropagation();
-                      e.preventDefault();
-                      // 判断是否可以收藏
-                      if (props.isCollect) {
-                        emit('collect', props.item);
-                      }
-                    }}>
-                    <Transition name="favitor" mode="out-in">
-                      {props.item.isCollect ? (
-                        <img
-                          src={iconCollectActive}
-                          key="1"
-                          class={[
-                            styles.iconCollect,
-                            props.isCollect ? styles.isCollect : ''
-                          ]}
-                        />
-                      ) : (
+                <div class={styles.btnGroup}>
+                  {props.isDownload && (
+                    <div class={styles.btnItem} onClick={onDownload}>
+                      <NSpin show={downloadStatus.value} size={'small'}>
                         <img
-                          src={iconCollectDefault}
-                          key="2"
-                          class={[
-                            styles.iconCollect,
-                            props.isCollect ? styles.isCollect : ''
-                          ]}
+                          src={iconDownload}
+                          key="3"
+                          class={[styles.iconCollect]}
                         />
-                      )}
-                    </Transition>
-                  </div>
-                )}
+                      </NSpin>
+                    </div>
+                  )}
+                  {props.isShowCollect && (
+                    <div
+                      class={[styles.iconCollect, styles.btnItem]}
+                      onClick={(e: MouseEvent) => {
+                        e.stopPropagation();
+                        e.preventDefault();
+                        // 判断是否可以收藏
+                        if (props.isCollect) {
+                          emit('collect', props.item);
+                        }
+                      }}>
+                      <Transition name="favitor" mode="out-in">
+                        {props.item.isCollect ? (
+                          <img
+                            src={iconCollectActive}
+                            key="1"
+                            class={[
+                              styles.iconCollect,
+                              props.isCollect ? styles.isCollect : ''
+                            ]}
+                          />
+                        ) : (
+                          <img
+                            src={iconCollectDefault}
+                            key="2"
+                            class={[
+                              styles.iconCollect,
+                              props.isCollect ? styles.isCollect : ''
+                            ]}
+                          />
+                        )}
+                      </Transition>
+                    </div>
+                  )}
+                </div>
 
                 {/* 精选 */}
                 {props.item.isSelected && (

+ 1 - 1
src/components/layout/index.tsx

@@ -705,7 +705,7 @@ export default defineComponent({
                   startShowModal('iconNote');
                 }}>
                 <img src={iconNote} alt="" />
-                帮助指引
+                功能引导
               </div>
 
               <div

+ 38 - 22
src/components/layout/layoutTop.tsx

@@ -159,14 +159,23 @@ export default defineComponent({
               displayDirective="show"
               v-slots={{
                 trigger: () => (
-                  <div class={styles.optons}>
-                    <NImage src={classHistoryIcon} previewDisabled></NImage>
-                  </div>
+                  <NTooltip showArrow={false}>
+                    {{
+                      trigger: () => (
+                        <div class={styles.optons}>
+                          <NImage
+                            src={classHistoryIcon}
+                            previewDisabled></NImage>
+                        </div>
+                      ),
+                      default: '上课记录'
+                    }}
+                  </NTooltip>
                 )
               }}>
               <ClassModal />
             </NPopover>
-            <NTooltip>
+            <NTooltip showArrow={false}>
               {{
                 trigger: () => (
                   <div class={styles.optons} onClick={showOption}>
@@ -180,24 +189,31 @@ export default defineComponent({
             </NTooltip>
             {/* </div> */}
             <div onClick={() => (showImGroup.value = true)}>
-              <NBadge
-                value={noReadCount.value}
-                max={99}
-                class={[
-                  noReadCount.value > 0 ? '' : styles.messageBadgeHide,
-                  styles.messageBadge,
-                  noReadCount.value > 0 ? '' : styles.messageBadgeNo
-                ]}
-                {...{ id: 'home-2' }}
-                color={'#FF1036'}>
-                <NImage
-                  class={[
-                    styles.messageIcon,
-                    noReadCount.value > 0 ? styles.animation : ''
-                  ]}
-                  preview-disabled
-                  src={messageIcon}></NImage>
-              </NBadge>
+              <NTooltip showArrow={false}>
+                {{
+                  trigger: () => (
+                    <NBadge
+                      value={noReadCount.value}
+                      max={99}
+                      class={[
+                        noReadCount.value > 0 ? '' : styles.messageBadgeHide,
+                        styles.messageBadge,
+                        noReadCount.value > 0 ? '' : styles.messageBadgeNo
+                      ]}
+                      {...{ id: 'home-2' }}
+                      color={'#FF1036'}>
+                      <NImage
+                        class={[
+                          styles.messageIcon,
+                          noReadCount.value > 0 ? styles.animation : ''
+                        ]}
+                        preview-disabled
+                        src={messageIcon}></NImage>
+                    </NBadge>
+                  ),
+                  default: '聊天'
+                }}
+              </NTooltip>
             </div>
 
             <div class={styles.line}></div>

+ 1 - 1
src/views/attend-class/component/tools/pen.tsx

@@ -99,7 +99,7 @@ export default defineComponent({
             {/* <div class={styles.modelAttendContent}>
               {data.modalAttendMessage}
             </div> */}
-            <NSpace class={styles.btnGroupModal}>
+            <NSpace class={styles.btnGroupModal} justify="center">
               <NButton
                 type="default"
                 round

BIN
src/views/attend-class/image/right_icon1.png


BIN
src/views/attend-class/image/right_icon2.png


BIN
src/views/attend-class/image/right_icon9.png


+ 54 - 8
src/views/attend-class/index.module.less

@@ -613,6 +613,7 @@
   width: 100%;
   height: 100%;
   position: relative;
+
   .pptBox {
     position: absolute;
     width: 100%;
@@ -637,6 +638,7 @@
   align-items: center;
   padding: 10px 0;
   transition: all 0.3s;
+
   .rightItem {
     width: 54px;
     height: 54px;
@@ -647,11 +649,14 @@
     align-items: center;
     justify-content: center;
     z-index: 9;
-    &.active, &:hover {
+
+    &.active,
+    &:hover {
       background: RGBA(255, 255, 255, 0.3);
       border-radius: 8px;
       z-index: 9;
     }
+
     &::after {
       content: "";
       position: absolute;
@@ -662,12 +667,14 @@
       height: 1px;
       background: rgba(255, 255, 255, 0.2);
     }
+
     img {
       width: 34px;
       height: 34px;
       margin: auto;
       z-index: 9;
     }
+
     .rightTips {
       position: absolute;
       right: 24px;
@@ -687,66 +694,80 @@
       z-index: 1;
       opacity: 0;
     }
+
     &:hover {
       .rightTips {
         opacity: 1;
       }
     }
   }
+
   .rightItem:last-child {
     width: 54px;
     height: 36px;
+
     img {
       width: 18px;
       height: 18px;
     }
+
     &::after {
       display: none;
     }
   }
+
   .itemDisabled {
     opacity: 0.7;
     cursor: not-allowed;
   }
-} 
+}
+
 .rightColumnZ {
   z-index: -1;
 }
+
 .rightColumnHide {
-  transform: translate(80px,-50%);
+  transform: translate(80px, -50%);
+
   .rightTips {
     display: none;
   }
 }
+
 .rightHideIcon {
   width: 30px;
   height: 60px;
   position: absolute;
   right: 0;
   top: 50%;
-  transform: translate(60px,-50%);
+  transform: translate(60px, -50%);
   z-index: 10;
   cursor: pointer;
   // transition: all 0.5s;
 }
+
 .rightIconShow {
-  animation:rightIn 0.3s ease 0.3s;
+  animation: rightIn 0.3s ease 0.3s;
   animation-fill-mode: forwards;
 }
+
 @keyframes rightIn {
   0% {
-    transform: translate(60px,-50%);
+    transform: translate(60px, -50%);
   }
+
   100% {
-    transform: translate(0,-50%);
+    transform: translate(0, -50%);
   }
 }
+
 .bottomColumn {
   position: fixed;
   right: 40px;
   bottom: 26px;
   display: flex;
   z-index: 2;
+
   .bottomItem {
     width: 54px;
     height: 54px;
@@ -758,6 +779,7 @@
     margin-right: 16px;
     position: relative;
     cursor: pointer;
+
     .bottomTips {
       position: absolute;
       left: 50%;
@@ -775,22 +797,46 @@
       font-weight: 500;
       color: #FFFFFF;
     }
-    &.active, &:hover {
+
+    &.active,
+    &:hover {
       background: rgba(0, 0, 0, 0.5);
+
       .bottomTips {
         opacity: 1;
       }
     }
+
     img {
       width: 40px;
       height: 40px;
     }
   }
+
   .bottomItem:last-child {
     margin-right: 0;
   }
+
   .itemDisabled {
     opacity: 0.7;
     cursor: not-allowed;
   }
+}
+
+.selectMusicModal {
+  position: relative;
+  width: 1352px;
+
+  :global {
+    .n-card-header {
+      position: absolute;
+      top: 0;
+      left: 0;
+      right: 0;
+
+      .n-card-header__main {
+        color: #fff;
+      }
+    }
+  }
 }

+ 211 - 316
src/views/attend-class/index.tsx

@@ -62,18 +62,17 @@ import Chapter from './model/chapter';
 import { useRouter } from 'vue-router';
 import { useUserStore } from '@/store/modules/users';
 
-import iconBeatIcon from './new-image/icon-beatIcon.png';
-import iconChange from './new-image/icon-change.png';
-import iconDown from './new-image/icon-down.png';
-import iconMenu from './new-image/icon-menu.png';
+// import iconBeatIcon from './new-image/icon-beatIcon.png';
+// import iconChange from './new-image/icon-change.png';
+// import iconDown from './new-image/icon-down.png';
+// import iconMenu from './new-image/icon-menu.png';
 import iconNote from './new-image/icon-note.png';
-import iconOverClass from './new-image/icon-overclass.png';
-import iconSetTime from './new-image/icon-setTime.png';
-import iconToneIcon from './new-image/icon-toneIcon.png';
-import iconUp from './new-image/icon-up.png';
+// import iconOverClass from './new-image/icon-overclass.png';
+// import iconSetTime from './new-image/icon-setTime.png';
+// import iconToneIcon from './new-image/icon-toneIcon.png';
+// import iconUp from './new-image/icon-up.png';
 import iconWhite from './new-image/icon-white.png';
-import iconWork from './new-image/icon-work.png';
-import dayjs from 'dayjs';
+// import iconWork from './new-image/icon-work.png';
 
 import rightIconEnd from './image/right_icon1.png';
 import rightIconArrange from './image/right_icon2.png';
@@ -83,11 +82,13 @@ import rightIconMetronome from './image/right_icon5.png';
 import rightIconTuner from './image/right_icon6.png';
 import rightIconTimer from './image/right_icon7.png';
 import rightIconPackUp from './image/right_icon8.png';
+import rightIconMusic from './image/right_icon9.png';
 import bottomIconSwitch from './image/bottom_icon1.png';
 import bottomIconResource from './image/bottom_icon2.png';
 import bottomIconPre from './image/bottom_icon3.png';
 import bottomIconNext from './image/bottom_icon4.png';
 import rightHideIcon from './image/right_hide_icon.png';
+import SelectResources from '../prepare-lessons/model/select-resources';
 
 export type ToolType = 'init' | 'pen' | 'whiteboard';
 export type ToolItem = {
@@ -187,6 +188,7 @@ export default defineComponent({
       removeTitle: '',
       removeContent: '',
 
+      selectResourceStatus: false,
       videoState: 'init' as 'init' | 'play',
       videoItemRef: null as any,
       animationState: 'start' as 'start' | 'end'
@@ -244,8 +246,8 @@ export default defineComponent({
           };
         });
         setTimeout(() => {
-          data.animationState = 'end'
-        }, 500)
+          data.animationState = 'end';
+        }, 500);
       } catch {
         //
       }
@@ -314,23 +316,23 @@ export default defineComponent({
       getDetail();
       getLessonCoursewareDetail();
       if (data.type === 'preview') {
-        rightList.splice(1,1)
+        rightList.splice(1, 1);
       }
     });
 
-    const onFullScreen = () => {
-      if (data.type === 'preview') {
-        const el: any = document.querySelector('#app');
-
-        if (el.mozRequestFullScreen) {
-          el.mozRequestFullScreen();
-        } else if (el.webkitRequestFullscreen) {
-          el.webkitRequestFullscreen();
-        } else if (el.requestFullScreen) {
-          el.requestFullscreen();
-        }
-      }
-    };
+    // const onFullScreen = () => {
+    //   if (data.type === 'preview') {
+    //     const el: any = document.querySelector('#app');
+
+    //     if (el.mozRequestFullScreen) {
+    //       el.mozRequestFullScreen();
+    //     } else if (el.webkitRequestFullscreen) {
+    //       el.webkitRequestFullscreen();
+    //     } else if (el.requestFullScreen) {
+    //       el.requestFullscreen();
+    //     }
+    //   }
+    // };
 
     const popupData = reactive({
       open: false,
@@ -388,25 +390,25 @@ export default defineComponent({
       }
     };
 
-    const activeName = computed(() => {
-      let name = '';
-      // data.knowledgePointList.forEach((item: any, index: number) => {
-      //   if (popupData.activeIndex === index) {
-      //     name = item.title;
-      //   }
-      // });
-      popupData.chapterDetails.forEach((chapter: any) => {
-        if (chapter.id === data.lessonCoursewareDetailId) {
-          name = chapter.name;
-          chapter.knowledgeList?.forEach((know: any) => {
-            if (know.id === data.detailId) {
-              name += ' - ' + know.name;
-            }
-          });
-        }
-      });
-      return name;
-    });
+    // const activeName = computed(() => {
+    //   let name = '';
+    //   // data.knowledgePointList.forEach((item: any, index: number) => {
+    //   //   if (popupData.activeIndex === index) {
+    //   //     name = item.title;
+    //   //   }
+    //   // });
+    //   popupData.chapterDetails.forEach((chapter: any) => {
+    //     if (chapter.id === data.lessonCoursewareDetailId) {
+    //       name = chapter.name;
+    //       chapter.knowledgeList?.forEach((know: any) => {
+    //         if (know.id === data.detailId) {
+    //           name += ' - ' + know.name;
+    //         }
+    //       });
+    //     }
+    //   });
+    //   return name;
+    // });
 
     /**停止所有的播放 */
     const handleStop = () => {
@@ -494,16 +496,16 @@ export default defineComponent({
 
     // ppt iframe 点击事件
     // const iframeClick = () => {
-		// 	if(document.all) {
-		// 		document.getElementById("iframe-ppt")?.attachEvent("click", () => {
+    // 	if(document.all) {
+    // 		document.getElementById("iframe-ppt")?.attachEvent("click", () => {
     //       activeData.model = !activeData.model
     //     });
-		// 	} else {
-		// 		document.getElementById("iframe-ppt")?.contentWindow?.postMessage({
+    // 	} else {
+    // 		document.getElementById("iframe-ppt")?.contentWindow?.postMessage({
     //       api: 'onAttendToggleMenu'
     //     }, '*');
-		// 	}
-		// }
+    // 	}
+    // }
 
     // 切换播放
     // const togglePlay = (m: any, isPlay: boolean) => {
@@ -579,8 +581,8 @@ export default defineComponent({
     const handleSwipeChange = (index: number) => {
       // 如果是当前正在播放 或者是视频最后一个
       if (popupData.activeIndex == index) return;
-      data.animationState = 'start'
-      data.videoState = 'init'
+      data.animationState = 'start';
+      data.videoState = 'init';
       handleStop();
       clearTimeout(acitveTimer.value);
       activeData.model = true;
@@ -590,7 +592,7 @@ export default defineComponent({
       acitveTimer.value = setTimeout(
         () => {
           const item = data.itemList[index];
-          
+
           if (item) {
             if (item.type == 'MUSIC') {
               activeData.model = true;
@@ -615,7 +617,7 @@ export default defineComponent({
                   item.error = false;
                   //   item.videoEle?.onPlay();
                 }
-                data.animationState = 'end'
+                data.animationState = 'end';
               });
             }
             if (item.type === 'PPT') {
@@ -858,28 +860,28 @@ export default defineComponent({
       }
     });
 
-    const toggleListenerKeyUp = (type: string) => {
-      if (type === 'remove') {
-        document.body.removeEventListener('keyup', () => {
-          listenerKeyUpState.value = false
-        })
-      } else {
-        // 监听页面键盘事件 - 上下切换
-        document.body.addEventListener('keyup', (e: KeyboardEvent) => {
-          // console.log(e, 'e');
-          if (e.code === 'ArrowLeft') {
-            // if (popupData.activeIndex === 0) return;
-            setModalOpen();
-            handlePreAndNext('up');
-          } else if (e.code === 'ArrowRight') {
-            // if (popupData.activeIndex === data.itemList.length - 1) return;
-            setModalOpen();
-            handlePreAndNext('down');
-          }
-        });
-        listenerKeyUpState.value = true
-      }
-    }
+    // const toggleListenerKeyUp = (type: string) => {
+    //   if (type === 'remove') {
+    //     document.body.removeEventListener('keyup', () => {
+    //       listenerKeyUpState.value = false;
+    //     });
+    //   } else {
+    //     // 监听页面键盘事件 - 上下切换
+    //     document.body.addEventListener('keyup', (e: KeyboardEvent) => {
+    //       // console.log(e, 'e');
+    //       if (e.code === 'ArrowLeft') {
+    //         // if (popupData.activeIndex === 0) return;
+    //         setModalOpen();
+    //         handlePreAndNext('up');
+    //       } else if (e.code === 'ArrowRight') {
+    //         // if (popupData.activeIndex === data.itemList.length - 1) return;
+    //         setModalOpen();
+    //         handlePreAndNext('down');
+    //       }
+    //     });
+    //     listenerKeyUpState.value = true;
+    //   }
+    // };
 
     // 监听切换到ppt课件时,手动移除键盘监听器
     // watch(() => popupData.activeIndex, () => {
@@ -911,20 +913,21 @@ export default defineComponent({
 
     /** 打开教学工具 */
     const openStudyTool = (item: ToolItem) => {
-      const activeItem = data.itemList[popupData.activeIndex];
-      // 暂停视频和曲谱的播放
-      if (activeItem.type === 'VIDEO' && activeItem.videoEle) {
-        activeItem.videoEle?.pause();
-      }
-      if (activeItem.type === 'SONG' && activeItem.audioEle) {
-        activeItem.audioEle?.stop();
-      }
-      if (activeItem.type === 'MUSIC') {
-        activeItem.iframeRef?.contentWindow?.postMessage(
-          { api: 'setPlayState' },
-          '*'
-        );
-      }
+      // const activeItem = data.itemList[popupData.activeIndex];
+      // // 暂停视频和曲谱的播放
+      // if (activeItem.type === 'VIDEO' && activeItem.videoEle) {
+      //   activeItem.videoEle?.pause();
+      // }
+      // if (activeItem.type === 'SONG' && activeItem.audioEle) {
+      //   activeItem.audioEle?.stop();
+      // }
+      // if (activeItem.type === 'MUSIC') {
+      //   activeItem.iframeRef?.contentWindow?.postMessage(
+      //     { api: 'setPlayState' },
+      //     '*'
+      //   );
+      // }
+      handleStop();
       clearModel();
       popupData.toolOpen = false;
       studyData.type = item.type;
@@ -957,12 +960,6 @@ export default defineComponent({
       if (val == 'toneIcon') {
         showModalTone.value = true;
       }
-      // if (val == 'iconNote2') {
-      //   if (NPopoverRef.value) {
-      //     NPopoverRef.value.setShow(false);
-      //   }
-      //   eventGlobal.emit('teacher-guideInfo-attend-class', 'attend-class');
-      // }
     };
 
     // 是否允许上一页
@@ -974,7 +971,6 @@ export default defineComponent({
        * 4,判断当前章节上面还没有其它章节;
        * 5,判断上面章节里面课程是否有资源;
        */
-
       if (popupData.activeIndex > 0) {
         return true;
       }
@@ -1099,12 +1095,12 @@ export default defineComponent({
       return false;
     });
     const activeVideoItem = computed(() => {
-      const item = data.itemList[popupData.activeIndex]
+      const item = data.itemList[popupData.activeIndex];
       if (item && item.type && item.type.toLocaleUpperCase() === 'VIDEO') {
-        return item
+        return item;
       }
-      return {}
-    })
+      return {};
+    });
 
     // 右侧菜单栏
     const rightList = reactive([
@@ -1112,43 +1108,48 @@ export default defineComponent({
         name: '结束课程',
         name2: '结束预览',
         icon: rightIconEnd,
-        id: 1,
+        id: 1
       },
       {
         name: '布置作业',
         icon: rightIconArrange,
-        id: 2,
+        id: 2
+      },
+      {
+        name: '曲目资源',
+        icon: rightIconMusic,
+        id: 9
       },
       {
         name: '批注',
         icon: rightIconPostil,
-        id: 3,
+        id: 3
       },
       {
         name: '白板',
         icon: rightIconWhiteboard,
-        id: 4,
+        id: 4
       },
       {
         name: '节拍器',
         icon: rightIconMetronome,
-        id: 5,
+        id: 5
       },
       {
         name: '调音器',
         icon: rightIconTuner,
-        id: 6,
+        id: 6
       },
       {
         name: '计时器',
         icon: rightIconTimer,
-        id: 7,
+        id: 7
       },
       {
         name: '收起',
         icon: rightIconPackUp,
-        id: 8,
-      },
+        id: 8
+      }
     ]);
 
     // 底部菜单栏
@@ -1156,26 +1157,25 @@ export default defineComponent({
       {
         name: '切换章节',
         icon: bottomIconSwitch,
-        id: 1,
+        id: 1
       },
       {
         name: '资源列表',
         icon: bottomIconResource,
-        id: 2,
+        id: 2
       },
       {
         name: '上一张',
         icon: bottomIconPre,
-        id: 3,
+        id: 3
       },
       {
         name: '下一张',
         icon: bottomIconNext,
-        id: 4,
+        id: 4
       }
     ]);
     const rightColumnShow = ref(true);
- 
 
     // 右边栏操作
     const operateRightBtn = async (id: number) => {
@@ -1202,50 +1202,54 @@ export default defineComponent({
             rows: 99
           });
           if (res.data.rows && res.data.rows.length) {
-            data.modalAttendMessage =
-              '本节课已设置课后作业,是否布置?';
+            data.modalAttendMessage = '本节课已设置课后作业,是否布置?';
           }
-          data.modelAttendStatus = true;          
+          data.modelAttendStatus = true;
           break;
         case 3:
           openStudyTool({
             type: 'pen',
             icon: iconNote,
             name: '批注'
-          })          
+          });
           break;
         case 4:
           openStudyTool({
             type: 'whiteboard',
             icon: iconWhite,
             name: '白板'
-          })          
+          });
           break;
         case 5:
-          startShowModal('beatIcon')
+          startShowModal('beatIcon');
           break;
         case 6:
-          startShowModal('toneIcon')
+          startShowModal('toneIcon');
           break;
         case 7:
-          startShowModal('setTimeIcon')
+          startShowModal('setTimeIcon');
           break;
         case 8:
-          rightColumnShow.value = false
-          break;                                                                  
+          rightColumnShow.value = false;
+          break;
+        case 9:
+          // 选择曲目时需要暂停所有播放
+          handleStop();
+          data.selectResourceStatus = true;
+          break;
         default:
           break;
       }
-    }
+    };
 
     // 底部悬浮按钮操作
     const operateBottomBtn = (id: number) => {
       switch (id) {
         case 1:
-          popupData.chapterOpen = true
+          popupData.chapterOpen = true;
           break;
         case 2:
-          popupData.open = true
+          popupData.open = true;
           break;
         case 3:
           if (!isUpArrow.value) return;
@@ -1254,15 +1258,14 @@ export default defineComponent({
         case 4:
           if (!isDownArrow.value) return;
           handlePreAndNext('down');
-          break;      
+          break;
         default:
           break;
       }
-    }
+    };
 
     return () => (
       <div id="playContent" class={[styles.playContent, 'wrap']}>
-
         <div
           onClick={() => {
             clearTimeout(activeData.timer);
@@ -1290,23 +1293,21 @@ export default defineComponent({
                         }
                       : { opacity: 0, zIndex: -1 }
                   }
-                  class={styles.itemDiv}
-                >
+                  class={styles.itemDiv}>
                   <VideoPlay
-                    ref={(el: any) => data.videoItemRef = el}
+                    ref={(el: any) => (data.videoItemRef = el)}
                     item={activeVideoItem.value}
                     showModel={activeData.model}
                     onClose={setModelOpen}
-
                     onCanplay={() => {
-                      data.videoState = 'play'
+                      data.videoState = 'play';
                     }}
                     onPause={() => {
-                      clearTimeout(activeData.timer)
-                      activeData.model = true
+                      clearTimeout(activeData.timer);
+                      activeData.model = true;
                     }}
                     onEnded={() => {
-                      const _index = popupData.activeIndex + 1
+                      const _index = popupData.activeIndex + 1;
                       if (_index < data.itemList.length) {
                         handleSwipeChange(_index);
                       }
@@ -1364,9 +1365,12 @@ export default defineComponent({
                       }}>
                       {m.type === 'VIDEO' ? (
                         <>
-                          <img src={m.coverImg} onLoad={() => {
-                            m.isprepare = true;
-                          }} />
+                          <img
+                            src={m.coverImg}
+                            onLoad={() => {
+                              m.isprepare = true;
+                            }}
+                          />
                           {/* <VideoPlay
                             ref={(v: any) => (data.videoRefs[mIndex] = v)}
                             item={m}
@@ -1394,12 +1398,12 @@ export default defineComponent({
                             }}
                           /> */}
                           <Transition name="van-fade">
-                            { (
+                            {
                               <div class={styles.loadWrap}>
                                 <Vue3Lottie
                                   animationData={playLoadData}></Vue3Lottie>
                               </div>
-                            )}
+                            }
                           </Transition>
                         </>
                       ) : m.type === 'IMG' ? (
@@ -1432,13 +1436,19 @@ export default defineComponent({
                             }
                           }}
                         />
-                      ) 
-                      // : m.type === 'PPT' ? <div class={styles.iframePpt}>
+                      ) : // : m.type === 'PPT' ? <div class={styles.iframePpt}>
                       //   <div class={styles.pptBox}></div>
                       //   <iframe src={`https://view.officeapps.live.com/op/embed.aspx?src=${encodeURIComponent(m.content)}`} width='100%' height='100%' frameborder='1'></iframe>
                       // </div>
-                      : m.type === 'PPT' ? <iframe src={`https://view.officeapps.live.com/op/embed.aspx?src=${encodeURIComponent(m.content)}`} width='100%' height='100%' frameborder='1'></iframe>                     
-                      : (
+                      m.type === 'PPT' ? (
+                        <iframe
+                          src={`https://view.officeapps.live.com/op/embed.aspx?src=${encodeURIComponent(
+                            m.content
+                          )}`}
+                          width="100%"
+                          height="100%"
+                          frameborder="1"></iframe>
+                      ) : (
                         <MusicScore
                           activeModel={activeData.model}
                           activeStatus={popupData.activeIndex === mIndex}
@@ -1459,182 +1469,59 @@ export default defineComponent({
           </div>
         </div>
 
-        {/* 头部样式 */}
-        {/* <div
-          style={{ transform: activeData.model ? '' : 'translateY(-100%)' }}
-          class={styles.headerContainer}>
-          <div class={styles.menu}>{activeName.value}</div>
-        </div> */}
-        {/* 布置作业按钮 */}
-
-        {/* <div
-          onClick={(e: any) => {
-            e.stopPropagation();
-            if (activeData.timer){
-              setModelOpen();
-            }
-          }}
+        {/* 右边操作栏 */}
+        <div
           class={[
-            styles.switchDisplaySection,
-            activeData.model ? '' : styles.sectionAnimate
+            styles.rightColumn,
+            !rightColumnShow.value ? styles.rightColumnHide : '',
+            studyData.type !== 'init' &&
+            (studyData.penShow || studyData.whiteboardShow)
+              ? styles.rightColumnZ
+              : ''
           ]}>
-          <NSpace class={styles.switchSpace}>
-            <div
-              class={styles.btnItem}
-              onClick={async () => {
-                if (data.type === 'preview') {
-                  handleStop();
-                  data.removeVisiable = true;
-                  data.removeTitle = '结束预览';
-                  data.removeContent = '请确认是否结束预览?';
-                } else {
-                  data.removeVisiable = true;
-                  data.removeTitle = '结束课程';
-                  data.removeContent = '请确认是否结束课程?';
-                }
-              }}>
-              <img src={iconOverClass} />
-              <p>{data.type !== 'preview' ? '结束课程' : '结束预览'}</p>
-            </div>
-            {data.type !== 'preview' && (
-              <div
-                class={[
-                  styles.btnItem,
-                  data.preStudentNum <= 0 ? styles.btnsDisabled : ''
-                ]}
-                onClick={async () => {
-                  // 学生人数必须大于0,才可以布置作业
-                  if (data.preStudentNum <= 0) return;
-                  const res = await lessonPreTrainingPage({
-                    coursewareKnowledgeDetailId: data.detailId,
-                    subjectId: data.subjectId,
-                    page: 1,
-                    rows: 99
-                  });
-                  if (res.data.rows && res.data.rows.length) {
-                    data.modalAttendMessage =
-                      '本节课已设置课后作业,是否布置?';
-                  }
-                  data.modelAttendStatus = true;
-                }}>
-                <img src={iconWork} />
-                <p>布置作业</p>
-              </div>
-            )}
-
-            <div
-              class={styles.btnItem}
-              onClick={() =>
-                openStudyTool({
-                  type: 'pen',
-                  icon: iconNote,
-                  name: '批注'
-                })
-              }>
-              <img src={iconNote} />
-              <p>批注</p>
-            </div>
-            <div
-              class={styles.btnItem}
-              onClick={() =>
-                openStudyTool({
-                  type: 'whiteboard',
-                  icon: iconWhite,
-                  name: '白板'
-                })
-              }>
-              <img src={iconWhite} />
-              <p>白板</p>
-            </div>
-            <div
-              class={styles.btnItem}
-              onClick={() => startShowModal('beatIcon')}>
-              <img src={iconToneIcon} />
-              <p>节拍器</p>
-            </div>
-            <div
-              class={styles.btnItem}
-              onClick={() => startShowModal('toneIcon')}>
-              <img src={iconSetTime} />
-              <p>调音器</p>
-            </div>
-            <div
-              class={styles.btnItem}
-              onClick={() => startShowModal('setTimeIcon')}>
-              <img src={iconBeatIcon} />
-              <p>计时器</p>
-            </div>
-          </NSpace>
-          <NSpace class={styles.switchSpace}>
-            <div
-              class={styles.btnItem}
-              onClick={() => (popupData.chapterOpen = true)}>
-              <img src={iconChange} />
-              <p>切换章节</p>
-            </div>
-            <div class={styles.btnItem} onClick={() => (popupData.open = true)}>
-              <img src={iconMenu} />
-              <p>资源列表</p>
-            </div>
-            <div
-              class={[
-                styles.btnItem,
-                !isUpArrow.value ? styles.btnsDisabled : ''
-              ]}
-              onClick={() => {
-                if (!isUpArrow.value) return;
-                handlePreAndNext('up');
-              }}>
-              <img src={iconUp} />
-              <p>上一个</p>
-            </div>
+          {rightList.map((item: any, index: number) => (
             <div
               class={[
-                styles.btnItem,
-                !isDownArrow.value ? styles.btnsDisabled : ''
+                styles.rightItem,
+                item.id === 2 && data.preStudentNum <= 0
+                  ? styles.itemDisabled
+                  : ''
               ]}
-              onClick={() => {
-                if (!isDownArrow.value) return;
-                handlePreAndNext('down');
-              }}>
-              <img src={iconDown} />
-              <p>下一个</p>
-            </div>
-          </NSpace>
-        </div> */}
-
-        {/* 右边操作栏 */}
-        <div class={[styles.rightColumn, !rightColumnShow.value ? styles.rightColumnHide : '', studyData.type !== 'init' && (studyData.penShow || studyData.whiteboardShow) ? styles.rightColumnZ : '' ]}>
-          {rightList.map((item: any, index: number) => (
-              <div 
-                class={[
-                  styles.rightItem,
-                  (item.id === 2 && data.preStudentNum <= 0) ? styles.itemDisabled : ''
-                ]} 
-                onClick={() => operateRightBtn(item.id)}>
-                <img src={item.icon} />
-                <div class={styles.rightTips}>{index === 0 && data.type === 'preview' ? item.name2 : item.name}</div>
+              onClick={() => operateRightBtn(item.id)}>
+              <img src={item.icon} />
+              <div class={styles.rightTips}>
+                {index === 0 && data.type === 'preview'
+                  ? item.name2
+                  : item.name}
               </div>
-            ))}
-        </div> 
-        {
-          !rightColumnShow.value && <img 
-            class={[styles.rightHideIcon, !rightColumnShow.value ? styles.rightIconShow : '']} 
+            </div>
+          ))}
+        </div>
+        {!rightColumnShow.value && (
+          <img
+            class={[
+              styles.rightHideIcon,
+              !rightColumnShow.value ? styles.rightIconShow : ''
+            ]}
             src={rightHideIcon}
-            onClick={() => rightColumnShow.value = true } />
-        }  
+            onClick={() => (rightColumnShow.value = true)}
+          />
+        )}
         {/* 右下角悬浮按钮 */}
         <div class={styles.bottomColumn}>
           {bottomList.map((item: any, index: number) => (
-              <div 
-                class={[
-                  styles.bottomItem,
-                  ((item.id === 3 && !isUpArrow.value) || (item.id === 4 && !isDownArrow.value)) ? styles.itemDisabled : ''
-                ]} 
-                onClick={() => operateBottomBtn(item.id)}>
-                <img src={item.icon} />
-                <div class={styles.bottomTips}>{item.name}</div>
-              </div>
+            <div
+              class={[
+                styles.bottomItem,
+                (item.id === 3 && !isUpArrow.value) ||
+                (item.id === 4 && !isDownArrow.value)
+                  ? styles.itemDisabled
+                  : ''
+              ]}
+              onClick={() => operateBottomBtn(item.id)}>
+              <img src={item.icon} />
+              <div class={styles.bottomTips}>{item.name}</div>
+            </div>
           ))}
         </div>
 
@@ -1729,7 +1616,7 @@ export default defineComponent({
             {/* <div class={styles.modelAttendContent}>
               {data.modalAttendMessage}
             </div> */}
-            <NSpace class={styles.btnGroupModal}>
+            <NSpace class={styles.btnGroupModal} justify="center">
               <NButton
                 type="default"
                 round
@@ -1824,6 +1711,14 @@ export default defineComponent({
             <TimerMeter></TimerMeter>
           </div>
         </NModal>
+
+        <NModal
+          v-model:show={data.selectResourceStatus}
+          class={['modalTitle', styles.selectMusicModal]}
+          preset="card"
+          title={'选择资源'}>
+          <SelectResources from="class" />
+        </NModal>
         <NModal
           transformOrigin="center"
           v-model:show={data.removeVisiable}

BIN
src/views/attend-class/model/train-type/images/e-back-look.png


BIN
src/views/attend-class/model/train-type/images/e-delete.png


+ 0 - 23
src/views/attend-class/model/train-type/images/e-delete.svg

@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<svg width="26px" height="26px" viewBox="0 0 26 26" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
-    <title>切片</title>
-    <g id="页面-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" stroke-linecap="round" stroke-linejoin="round">
-        <g id="64、布置作业-训练设置" transform="translate(-1388.000000, -701.000000)" stroke="#F92D53" stroke-width="1.86666667">
-            <g id="编组-4" transform="translate(446.000000, 206.000000)">
-                <g id="位图备份" transform="translate(526.000000, 169.000000)">
-                    <g id="删除" transform="translate(416.000000, 326.000000)">
-                        <g transform="translate(4.000000, 4.000000)" id="编组">
-                            <line x1="11.5634909" y1="7.66953939" x2="11.5634909" y2="14.4243273" id="Stroke-1"></line>
-                            <line x1="7.02112727" y1="7.66953939" x2="7.02112727" y2="14.4243273" id="Stroke-2"></line>
-                            <g>
-                                <path d="M16.1212121,6.78787879 L16.1212121,17.1818182 C16.1212121,18.002303 15.3609697,18.6666667 14.4242424,18.6666667 L4.24242424,18.6666667 C3.30569697,18.6666667 2.54545455,18.002303 2.54545455,17.1818182 L2.54545455,6.78787879" id="Stroke-3"></path>
-                                <line x1="0" y1="4.24242424" x2="18.6666667" y2="4.24242424" id="Stroke-5"></line>
-                                <path d="M12.7272727,0 L5.93939394,0 C5.00266667,0 4.24242424,0.760242424 4.24242424,1.6969697 L4.24242424,4.24242424 L14.4242424,4.24242424 L14.4242424,1.6969697 C14.4242424,0.760242424 13.664,0 12.7272727,0 Z" id="Stroke-7"></path>
-                            </g>
-                        </g>
-                    </g>
-                </g>
-            </g>
-        </g>
-    </g>
-</svg>

BIN
src/views/attend-class/model/train-type/images/e-edit.png


+ 0 - 23
src/views/attend-class/model/train-type/images/e-edit.svg

@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<svg width="26px" height="26px" viewBox="0 0 26 26" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
-    <title>切片</title>
-    <g id="页面-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" stroke-linecap="round">
-        <g id="64、布置作业-训练设置" transform="translate(-1388.000000, -696.000000)" stroke="#F92D53" stroke-width="1.89584019">
-            <g id="编组-4" transform="translate(446.000000, 219.000000)">
-                <g id="位图备份" transform="translate(526.000000, 168.000000)">
-                    <g id="图标/通用/返回备份" transform="translate(416.000000, 309.000000)">
-                        <g id="编组" transform="translate(4.875000, 3.791667)">
-                            <path d="M16.2496553,13.0856648 L16.2498599,16.2494713 C16.249793,17.446032 15.279894,18.4161211 14.0833334,18.4164223 L12.221101,18.4166667 L12.221101,18.4166667" id="Stroke-1" stroke-linejoin="round"></path>
-                            <path d="M16.2496553,5.52567849 L16.2496553,2.16666667 C16.2496553,0.970049709 15.2796056,-2.19814969e-16 14.0829886,0 L2.16666667,0 C0.970049709,2.19814969e-16 2.97545897e-16,0.970049709 0,2.16666667 L0,16.2494713 C5.90632523e-16,17.4460883 0.970049709,18.416138 2.16666667,18.416138 L4.87491383,18.416138 L4.87491383,18.416138" id="Stroke-3" stroke-linejoin="round"></path>
-                            <g id="编组-2" transform="translate(4.333333, 3.323284)">
-                                <line x1="0" y1="1.523912" x2="7.58359185" y2="1.55171575" id="Stroke-5"></line>
-                                <line x1="0" y1="6.48380889" x2="3.79158049" y2="6.48380889" id="Stroke-9"></line>
-                            </g>
-                            <line x1="8.19289145" y1="18.4164904" x2="16.25" y2="8.23912327" id="Stroke-7"></line>
-                        </g>
-                    </g>
-                </g>
-            </g>
-        </g>
-    </g>
-</svg>

BIN
src/views/attend-class/model/train-type/images/e-message.png


BIN
src/views/attend-class/model/train-type/images/e-report-status.png


BIN
src/views/attend-class/model/train-type/images/e-report.png


+ 0 - 31
src/views/attend-class/model/train-type/images/e-tag.svg

@@ -1,31 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<svg width="28px" height="28px" viewBox="0 0 28 28" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
-    <title>专辑标签(2)</title>
-    <defs>
-        <linearGradient x1="100%" y1="50%" x2="0%" y2="50%" id="linearGradient-1">
-            <stop stop-color="#FF7B57" offset="0%"></stop>
-            <stop stop-color="#FF3460" offset="100%"></stop>
-        </linearGradient>
-        <linearGradient x1="60.2548008%" y1="8.41286024%" x2="50%" y2="100%" id="linearGradient-2">
-            <stop stop-color="#FFFFFF" offset="0%"></stop>
-            <stop stop-color="#FFFFFF" offset="100%"></stop>
-        </linearGradient>
-        <linearGradient x1="50%" y1="0%" x2="50%" y2="46.9481485%" id="linearGradient-3">
-            <stop stop-color="#FFFFFF" stop-opacity="0" offset="0%"></stop>
-            <stop stop-color="#FFFFFF" offset="100%"></stop>
-        </linearGradient>
-    </defs>
-    <g id="页面-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" opacity="0.900000036">
-        <g id="64、布置作业-训练设置" transform="translate(-992.000000, -407.000000)">
-            <g id="编组-4" transform="translate(446.000000, 219.000000)">
-                <g id="位图备份" transform="translate(526.000000, 168.000000)">
-                    <g id="专辑标签(2)" transform="translate(20.000000, 20.000000)">
-                        <rect id="矩形" fill="url(#linearGradient-1)" x="0" y="0" width="28" height="28" rx="14"></rect>
-                        <path d="M14,5.6 C14.3155402,5.6 14.6270168,5.61739826 14.9335228,5.65128781 L14.9330114,9.42661825 C14.631542,9.36544538 14.3195211,9.33333333 14,9.33333333 C11.4226712,9.33333333 9.33333333,11.4226712 9.33333333,14 C9.33333333,16.5773288 11.4226712,18.6666667 14,18.6666667 C16.5494313,18.6666667 18.6213787,16.6223149 18.6666667,14.0838855 L18.6666667,14.0838855 L18.6671259,7.01481986 C20.9180416,8.5217493 22.4,11.0878097 22.4,14 C22.4,18.6391919 18.6391919,22.4 14,22.4 C9.3608081,22.4 5.6,18.6391919 5.6,14 C5.6,9.3608081 9.3608081,5.6 14,5.6 Z" id="形状结合" fill="url(#linearGradient-2)"></path>
-                        <rect id="矩形" fill="url(#linearGradient-3)" x="18.6658869" y="0.691618793" width="3.73333333" height="13.0666667"></rect>
-                    </g>
-                </g>
-            </g>
-        </g>
-    </g>
-</svg>

BIN
src/views/attend-class/model/train-type/images/p-delete.png


+ 0 - 23
src/views/attend-class/model/train-type/images/p-delete.svg

@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<svg width="26px" height="26px" viewBox="0 0 26 26" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
-    <title>切片</title>
-    <g id="页面-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" stroke-linecap="round" stroke-linejoin="round">
-        <g id="64、布置作业-训练设置" transform="translate(-902.000000, -701.000000)" stroke="#198CFE" stroke-width="1.86666667">
-            <g id="编组-4" transform="translate(446.000000, 206.000000)">
-                <g id="位图" transform="translate(40.000000, 169.000000)">
-                    <g id="删除" transform="translate(416.000000, 326.000000)">
-                        <g transform="translate(4.000000, 4.000000)" id="编组">
-                            <line x1="11.5634909" y1="7.66953939" x2="11.5634909" y2="14.4243273" id="Stroke-1"></line>
-                            <line x1="7.02112727" y1="7.66953939" x2="7.02112727" y2="14.4243273" id="Stroke-2"></line>
-                            <g>
-                                <path d="M16.1212121,6.78787879 L16.1212121,17.1818182 C16.1212121,18.002303 15.3609697,18.6666667 14.4242424,18.6666667 L4.24242424,18.6666667 C3.30569697,18.6666667 2.54545455,18.002303 2.54545455,17.1818182 L2.54545455,6.78787879" id="Stroke-3"></path>
-                                <line x1="0" y1="4.24242424" x2="18.6666667" y2="4.24242424" id="Stroke-5"></line>
-                                <path d="M12.7272727,0 L5.93939394,0 C5.00266667,0 4.24242424,0.760242424 4.24242424,1.6969697 L4.24242424,4.24242424 L14.4242424,4.24242424 L14.4242424,1.6969697 C14.4242424,0.760242424 13.664,0 12.7272727,0 Z" id="Stroke-7"></path>
-                            </g>
-                        </g>
-                    </g>
-                </g>
-            </g>
-        </g>
-    </g>
-</svg>

BIN
src/views/attend-class/model/train-type/images/p-edit.png


+ 0 - 23
src/views/attend-class/model/train-type/images/p-edit.svg

@@ -1,23 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<svg width="26px" height="26px" viewBox="0 0 26 26" version="1.1" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink">
-    <title>切片</title>
-    <g id="页面-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd" stroke-linecap="round">
-        <g id="64、布置作业-训练设置" transform="translate(-902.000000, -696.000000)" stroke="#198CFE" stroke-width="1.89584019">
-            <g id="编组-4" transform="translate(446.000000, 219.000000)">
-                <g id="位图" transform="translate(40.000000, 168.000000)">
-                    <g id="图标/通用/返回备份" transform="translate(416.000000, 309.000000)">
-                        <g id="编组" transform="translate(4.875000, 3.791667)">
-                            <path d="M16.2496553,13.0856648 L16.2498599,16.2494713 C16.249793,17.446032 15.279894,18.4161211 14.0833334,18.4164223 L12.221101,18.4166667 L12.221101,18.4166667" id="Stroke-1" stroke-linejoin="round"></path>
-                            <path d="M16.2496553,5.52567849 L16.2496553,2.16666667 C16.2496553,0.970049709 15.2796056,-2.19814969e-16 14.0829886,0 L2.16666667,0 C0.970049709,2.19814969e-16 2.97545897e-16,0.970049709 0,2.16666667 L0,16.2494713 C5.90632523e-16,17.4460883 0.970049709,18.416138 2.16666667,18.416138 L4.87491383,18.416138 L4.87491383,18.416138" id="Stroke-3" stroke-linejoin="round"></path>
-                            <g id="编组-2" transform="translate(4.333333, 3.323284)">
-                                <line x1="0" y1="1.523912" x2="7.58359185" y2="1.55171575" id="Stroke-5"></line>
-                                <line x1="0" y1="6.48380889" x2="3.79158049" y2="6.48380889" id="Stroke-9"></line>
-                            </g>
-                            <line x1="8.19289145" y1="18.4164904" x2="16.25" y2="8.23912327" id="Stroke-7"></line>
-                        </g>
-                    </g>
-                </g>
-            </g>
-        </g>
-    </g>
-</svg>

+ 5 - 4
src/views/attend-class/model/train-type/index.module.less

@@ -206,12 +206,13 @@
 
   .operation {
     display: flex;
-    height: 26px;
+    height: 28px;
     padding: 0;
+    border-radius: 50%;
 
     img {
-      height: 26px;
-      width: 26px;
+      height: 28px;
+      width: 28px;
     }
   }
 }
@@ -303,4 +304,4 @@
       }
     }
   }
-}
+}

+ 140 - 18
src/views/attend-class/model/train-type/index.tsx

@@ -1,4 +1,4 @@
-import { PropType, defineComponent, ref } from 'vue';
+import { PropType, computed, defineComponent, ref } from 'vue';
 import styles from './index.module.less';
 import {
   NButton,
@@ -7,17 +7,22 @@ import {
   NImage,
   NTag,
   NModal,
-  NProgress
+  NProgress,
+  NTooltip
 } from 'naive-ui';
 import pTag from './images/p-tag.png';
 import eTag from './images/e-tag.png';
 import pEdit from './images/p-edit.png';
 import eEdit from './images/e-edit.png';
-import pDelete from './images/p-delete.svg';
-import eDelete from './images/e-delete.svg';
+import pDelete from './images/p-delete.png';
+import eDelete from './images/e-delete.png';
 import iconDelete from './images/icon-delete.png';
+import eBackLook from './images/e-back-look.png';
+import eReport from './images/e-report.png';
+import eMessage from './images/e-message.png';
 import { useUserStore } from '/src/store/modules/users';
 import CardPreview from '/src/components/card-preview';
+import { vaildUrl } from '/src/utils/urlUtils';
 type ItemType = {
   id: string | number;
   trainingType: 'PRACTICE' | 'EVALUATION';
@@ -74,6 +79,36 @@ export default defineComponent({
       previewShow.value = true;
     };
 
+    const reportSrc = ref('');
+    const detailVisiable = ref(false);
+    const gotoRecode = (row: any) => {
+      console.log(row.id, 'gotoRecode');
+      const tockn = userStore.getToken;
+      reportSrc.value =
+        vaildUrl() +
+        `/instrument/#/evaluat-report?id=${row.id}&Authorization=${tockn}`;
+      detailVisiable.value = true;
+    };
+
+    const isPass = computed(() => {
+      const item = props.item;
+      if (item) {
+        if (item.trainingType === 'EVALUATION') {
+          return (item.trainingTimes || 0) >= (item.allTimes || 0)
+            ? '#6CFFC1'
+            : '#FF7794';
+        } else {
+          return (item.trainingTimes
+            ? parseInt(item.trainingTimes / 60 + '')
+            : 0) >= (item.allTimes || 0)
+            ? '#6CFFC1'
+            : '#FF7794';
+        }
+      } else {
+        return false;
+      }
+    });
+
     return () => (
       <div
         class={[
@@ -139,9 +174,11 @@ export default defineComponent({
                 type="circle"
                 rail-color={'8b8b8b'}
                 color={
-                  props.item.trainingType === 'EVALUATION'
-                    ? '#FF7E65'
-                    : '#44B3FF'
+                  // props.item.trainingType === 'EVALUATION'
+                  //   ? '#FF7E65'
+                  //   : '#44B3FF'
+
+                  isPass.value ? '#6CFFC1' : '#FF7794'
                 }
                 style="width: 120px; margin: 0  0 10px;">
                 <div class={styles.BProgress}>
@@ -161,7 +198,6 @@ export default defineComponent({
                   )}
 
                   <p>
-                    {' '}
                     {props.item.trainingType === 'EVALUATION'
                       ? '学生分数'
                       : '实际练习'}{' '}
@@ -204,9 +240,20 @@ export default defineComponent({
                   e.stopPropagation();
                   emit('edit', props.item);
                 }}>
-                <img
-                  src={props.item.trainingType === 'EVALUATION' ? eEdit : pEdit}
-                />
+                <NTooltip showArrow={false}>
+                  {{
+                    trigger: () => (
+                      <img
+                        src={
+                          props.item.trainingType === 'EVALUATION'
+                            ? eEdit
+                            : pEdit
+                        }
+                      />
+                    ),
+                    default: '编辑'
+                  }}
+                </NTooltip>
               </n-button>
               {props.type === 'homework' && (
                 <n-button
@@ -218,17 +265,78 @@ export default defineComponent({
                     // onDelete();
                     removeVisiable.value = true;
                   }}>
-                  <img
-                    src={
-                      props.item.trainingType === 'EVALUATION'
-                        ? eDelete
-                        : pDelete
-                    }
-                  />
+                  <NTooltip showArrow={false}>
+                    {{
+                      trigger: () => (
+                        <img
+                          src={
+                            props.item.trainingType === 'EVALUATION'
+                              ? eDelete
+                              : pDelete
+                          }
+                        />
+                      ),
+                      default: '删除'
+                    }}
+                  </NTooltip>
                 </n-button>
               )}
             </NSpace>
           )}
+
+          {props.isDisabled && !props.isCLassWork ? (
+            <>
+              <NSpace size={6}>
+                <n-button
+                  quaternary
+                  disabled={props.isDelete}
+                  class={styles.operation}
+                  onClick={(e: MouseEvent) => {
+                    e.stopPropagation();
+                    removeVisiable.value = true;
+                  }}>
+                  <NTooltip showArrow={false}>
+                    {{
+                      trigger: () => <img src={eReport} />,
+                      default: '评测报告'
+                    }}
+                  </NTooltip>
+                </n-button>
+                <n-button
+                  quaternary
+                  disabled={props.isDelete}
+                  class={styles.operation}
+                  onClick={(e: MouseEvent) => {
+                    e.stopPropagation();
+                    removeVisiable.value = true;
+                  }}>
+                  <NTooltip showArrow={false}>
+                    {{
+                      trigger: () => <img src={eBackLook} />,
+                      default: '查看回放'
+                    }}
+                  </NTooltip>
+                </n-button>
+                {/* <n-button
+                  quaternary
+                  disabled={props.isDelete}
+                  class={styles.operation}
+                  onClick={(e: MouseEvent) => {
+                    e.stopPropagation();
+                    removeVisiable.value = true;
+                  }}>
+                  <NTooltip showArrow={false}>
+                    {{
+                      trigger: () => <img src={eMessage} />,
+                      default: '写评语'
+                    }}
+                  </NTooltip>
+                </n-button> */}
+              </NSpace>
+            </>
+          ) : (
+            ''
+          )}
         </div>
 
         {/* 判断是否下架 */}
@@ -277,6 +385,20 @@ export default defineComponent({
             title: props.item.musicName
           }}
         />
+
+        <NModal
+          v-model:show={detailVisiable.value}
+          preset="card"
+          class={['modalTitle background', styles.reportModel]}
+          title={'评测报告'}>
+          <div class={styles.reportContainer} style={{ lineHeight: 0 }}>
+            <iframe
+              width={'100%'}
+              height={'450px'}
+              frameborder="0"
+              src={reportSrc.value}></iframe>
+          </div>
+        </NModal>
       </div>
     );
   }

+ 2 - 2
src/views/classList/api.ts

@@ -131,7 +131,7 @@ export const getCourseChapter = (params: any) => {
 };
 
 /**
- * 获取学生训练详情
+ * 获取学生作业详情
  */
 
 export const getTrainingStudentDetail = (params: any) => {
@@ -151,7 +151,7 @@ export const getTrainingStat = (params: any) => {
 };
 
 /**
- * 获取班级训练详情
+ * 获取班级作业详情
  */
 export const getTrainingClassDetail = (params: any) => {
   return request.get(`/edu-app/lessonTraining/trainingDetail`, {

+ 5 - 6
src/views/classList/components/afterWork.tsx

@@ -40,7 +40,6 @@ export default defineComponent({
       getNowDateAndMonday(new Date().getTime()),
       getNowDateAndSunday(new Date().getTime())
     ]);
-    console.log(route.query, 'query');
     const state = reactive({
       upgradeFlag: props.upgradeFlag == 0 ? true : false, // 是否为历史班
       searchForm: {
@@ -126,7 +125,7 @@ export default defineComponent({
           }
         },
         {
-          title: '训练状态',
+          title: '作业状态',
           key: 'status',
           render(row: any) {
             return row.status == 0 ? (
@@ -190,7 +189,7 @@ export default defineComponent({
                   text
                   type="primary"
                   onClick={() => lookWorkDetail(row)}>
-                  训练详情
+                  作业详情
                 </NButton>
               </NSpace>
             );
@@ -231,7 +230,7 @@ export default defineComponent({
                 {...({
                   options: [
                     {
-                      label: '训练状态',
+                      label: '作业状态',
                       value: null
                     },
                     {
@@ -243,7 +242,7 @@ export default defineComponent({
                       value: 0
                     }
                   ],
-                  placeholder: '训练状态',
+                  placeholder: '作业状态',
                   clearable: true,
                   inline: true
                 } as any)}
@@ -309,7 +308,7 @@ export default defineComponent({
           v-model:show={state.detailVisiable}
           preset="card"
           class={['modalTitle background', styles.wordDetailModel]}
-          title={'训练详情'}>
+          title={'作业详情'}>
           <ClassTrainingDetails
             onClose={() => (state.detailVisiable = false)}
             activeRow={state.activeRow}></ClassTrainingDetails>

+ 3 - 3
src/views/classList/components/afterWorkDetail.tsx

@@ -280,8 +280,8 @@ export default defineComponent({
         </NButton> */}
           <div class={styles.tableWrap}>
             <NDataTable
-               v-slots={{
-                empty:()=><TheEmpty></TheEmpty>
+              v-slots={{
+                empty: () => <TheEmpty></TheEmpty>
               }}
               class={styles.classTable}
               loading={state.loading}
@@ -300,7 +300,7 @@ export default defineComponent({
           v-model:show={state.detailVisiable}
           preset="card"
           class={['modalTitle background', styles.wordDetailModel]}
-          title={'训练详情'}>
+          title={'作业详情'}>
           <TrainingDetails
             onNext={() => goToNext()}
             onPre={() => gotoPre()}

+ 88 - 28
src/views/classList/components/classStudent.tsx

@@ -5,18 +5,18 @@ import {
   NDataTable,
   NForm,
   NFormItem,
-  NImage,
-  NSelect,
+  NModal,
   NSpace,
+  NTooltip,
   useMessage
 } from 'naive-ui';
 import SearchInput from '@/components/searchInput';
 import CSelect from '@/components/CSelect';
 import Pagination from '@/components/pagination';
-import { classGroupDetail, getStudentList } from '../api';
-import add from './images/add.png';
+import { getStudentList } from '../api';
 import { useRoute, useRouter } from 'vue-router';
 import TheEmpty from '/src/components/TheEmpty';
+import UpdateStudent from '../../studentList/modals/update-student';
 export default defineComponent({
   name: 'student-studentList',
   props: {
@@ -24,21 +24,23 @@ export default defineComponent({
       type: Number
     }
   },
-  setup(props, { emit }) {
+  setup(props) {
     const message = useMessage();
     const route = useRoute();
     const router = useRouter();
 
     const state = reactive({
       upgradeFlag: props.upgradeFlag == 0 ? true : false, // 是否为历史班
-      searchForm: { keyword: '', gender: null as any },
+      searchForm: { keyword: '', gender: null as any, membership: null as any },
       loading: false,
       pagination: {
         page: 1,
         rows: 10,
         pageTotal: 4
       },
-      tableList: [] as any
+      tableList: [] as any,
+      editStatus: false,
+      activeRow: {} as any
     });
 
     watch(
@@ -108,11 +110,18 @@ export default defineComponent({
           key: 'nickname',
           render: (row: any) => {
             return (
-              <div
-                style={{ userSelect: 'all', cursor: 'pointer' }}
-                onClick={() => copyTo(row.nickname)}>
-                {row.nickname}
-              </div>
+              <NTooltip showArrow={false} placement="top-start">
+                {{
+                  trigger: () => (
+                    <div
+                      style={{ userSelect: 'all', cursor: 'pointer' }}
+                      onClick={() => copyTo(row.nickname)}>
+                      {row.nickname}
+                    </div>
+                  ),
+                  default: '点击复制'
+                }}
+              </NTooltip>
             );
           }
         },
@@ -121,11 +130,18 @@ export default defineComponent({
           key: 'phone',
           render: (row: any) => {
             return (
-              <div
-                style={{ userSelect: 'all', cursor: 'pointer' }}
-                onClick={() => copyTo(row.phone)}>
-                {row.phone}
-              </div>
+              <NTooltip showArrow={false} placement="top-start">
+                {{
+                  trigger: () => (
+                    <div
+                      style={{ userSelect: 'all', cursor: 'pointer' }}
+                      onClick={() => copyTo(row.phone)}>
+                      {row.phone}
+                    </div>
+                  ),
+                  default: '点击复制'
+                }}
+              </NTooltip>
             );
           }
         },
@@ -144,27 +160,36 @@ export default defineComponent({
             );
           }
         },
-
-        // {
-        //   title: '学生类型',
-        //   key: 'studentType',
-        //   render(row: any) {
-        //     return <>{row.studentType == 'member' ? '会员' : '普通'}</>;
-        //   }
-        // },
+        {
+          title: '学生类型',
+          key: 'vipMember',
+          render(row: any) {
+            return <>{row.vipMember ? '会员' : '普通'}</>;
+          }
+        },
         {
           title: '操作',
           key: 'id',
           render(row: any) {
             return (
-              <NButton text type="primary" onClick={() => gotoDetail(row)}>
-                详情
-              </NButton>
+              <NSpace>
+                <NButton text type="primary" onClick={() => gotoDetail(row)}>
+                  详情
+                </NButton>
+                <NButton text type="primary" onClick={() => onUpdate(row)}>
+                  修改
+                </NButton>
+              </NSpace>
             );
           }
         }
       ];
     };
+    // 修改
+    const onUpdate = (row: any) => {
+      state.editStatus = true;
+      state.activeRow = row;
+    };
     return () => (
       <div>
         <div class={styles.searchList}>
@@ -202,6 +227,29 @@ export default defineComponent({
                 } as any)}
                 v-model:value={state.searchForm.gender}></CSelect>
             </NFormItem>
+            <NFormItem>
+              <CSelect
+                {...({
+                  options: [
+                    {
+                      label: '学生类型',
+                      value: null
+                    },
+                    {
+                      label: '会员',
+                      value: true
+                    },
+                    {
+                      label: '普通',
+                      value: false
+                    }
+                  ],
+                  placeholder: '学生类型',
+                  clearable: true,
+                  inline: true
+                } as any)}
+                v-model:value={state.searchForm.membership}></CSelect>
+            </NFormItem>
 
             <NFormItem>
               <NSpace justify="end">
@@ -248,6 +296,18 @@ export default defineComponent({
             sync
           />
         </div>
+
+        <NModal
+          v-model:show={state.editStatus}
+          class={['modalTitle background', styles.updateStudent]}
+          preset="card"
+          title="修改信息">
+          <UpdateStudent
+            onClose={() => (state.editStatus = false)}
+            onConfirm={() => getList()}
+            row={state.activeRow}
+          />
+        </NModal>
       </div>
     );
   }

+ 4 - 4
src/views/classList/components/testRecode.tsx

@@ -128,7 +128,7 @@ export default defineComponent({
     const columns = () => {
       return [
         {
-          title: '姓名',
+          title: '学生姓名',
           key: 'studentName'
         },
         {
@@ -165,7 +165,7 @@ export default defineComponent({
           }
         },
         {
-          title: '练时长',
+          title: '练时长',
           key: 'studentType',
           render(row: any) {
             return (
@@ -177,7 +177,7 @@ export default defineComponent({
                       getSecend(row.practiceDuration) +
                       '秒'
                     : getSecend(row.practiceDuration) + '秒'
-                  : 0}
+                  : 0 + '秒'}
               </>
             );
           }
@@ -318,7 +318,7 @@ export default defineComponent({
                   </div>
                 </p>
-                <p class={styles.TrainDataItemsubTitle}>平均练习时长</p>
+                <p class={styles.TrainDataItemsubTitle}>平均每天练习时长</p>
               </div>
             </NGi>
           </NGrid>

+ 4 - 0
src/views/classList/index.module.less

@@ -755,4 +755,8 @@
 
 .nextWrap {
   padding: 30px 32px 0 !important;
+}
+
+.updateStudent {
+  width: 480px;
 }

+ 1 - 1
src/views/home/components/practiceData.tsx

@@ -61,7 +61,7 @@ export default defineComponent({
           }
         },
         {
-          title: '平均练习时长(分钟)',
+          title: '平均练习时长',
           key: 'practiceDuration',
           render(row: any) {
             return (

+ 108 - 22
src/views/home/components/practiceRanking.tsx

@@ -1,22 +1,13 @@
-import { defineComponent, reactive, onMounted, computed } from 'vue';
+import { defineComponent, reactive, onMounted, computed, nextTick } from 'vue';
 import styles from '../index2.module.less';
-import {
-  NButton,
-  NDataTable,
-  NForm,
-  NFormItem,
-  NImage,
-  NSelect,
-  NSpace
-} from 'naive-ui';
-import SearchInput from '@/components/searchInput';
-import CSelect from '@/components/CSelect';
+import { NDataTable, NTooltip } from 'naive-ui';
 import Pagination from '@/components/pagination';
-import add from './images/add.png';
 import { getMinutes, getSecend, getTimes } from '/src/utils/dateFormat';
 import { getTestList } from '../../classList/api';
-import dayjs from 'dayjs';
 import TheEmpty from '/src/components/TheEmpty';
+import iconSortDefault from '@/common/images/icon-sort-default.png';
+import iconSortDesc from '@/common/images/icon-sort-desc.png';
+import iconSortAsc from '@/common/images/icon-sort-asc.png';
 export default defineComponent({
   name: 'student-studentList',
   props: {
@@ -78,11 +69,20 @@ export default defineComponent({
       }
     };
     expose({ getList });
-    onMounted(() => {
-      getList();
+    onMounted(async () => {
+      await getList();
+
+      nextTick(() => {
+        // 把默认的排序删除
+        const dom = document.querySelectorAll('.n-data-table-sorter');
+        dom.forEach((item: any) => {
+          item.style.display = 'none';
+        });
+      });
     });
 
     const handleSorterChange = (sroter: any) => {
+      console.log(sroter, '12');
       if (!sroter) {
         searchForm.ase = 0;
         searchForm.sortType = 1;
@@ -123,19 +123,76 @@ export default defineComponent({
     };
 
     const practiceDaysRef = reactive({
-      title: '练习天数',
+      title() {
+        return (
+          <NTooltip showArrow={false} placement="top-start">
+            {{
+              trigger: () => (
+                <div class={styles.cell}>
+                  练习天数
+                  <img
+                    class={styles.sortIcon}
+                    src={
+                      practiceDaysRef.sortOrder === 'descend'
+                        ? iconSortDesc
+                        : practiceDaysRef.sortOrder === 'ascend'
+                        ? iconSortAsc
+                        : iconSortDefault
+                    }
+                  />
+                </div>
+              ),
+              default:
+                practiceDaysRef.sortOrder === 'descend'
+                  ? '点击升序'
+                  : practiceDaysRef.sortOrder === 'ascend'
+                  ? '取消排序'
+                  : '点击降序'
+            }}
+          </NTooltip>
+        );
+      },
       key: 'practiceDays',
       sorter: true,
-      sortOrder: false,
+      sortOrder: false as any,
       render(row: any) {
         return <>{row.practiceDays ? row.practiceDays : 0}天</>;
       }
     });
     const practiceDurationRef = reactive({
-      title: '练习总时长',
+      // title: '练习总时长',
+      title() {
+        return (
+          <NTooltip showArrow={false} placement="top-start">
+            {{
+              trigger: () => (
+                <div class={styles.cell}>
+                  练习总时长
+                  <img
+                    class={styles.sortIcon}
+                    src={
+                      practiceDurationRef.sortOrder === 'descend'
+                        ? iconSortDesc
+                        : practiceDurationRef.sortOrder === 'ascend'
+                        ? iconSortAsc
+                        : iconSortDefault
+                    }
+                  />
+                </div>
+              ),
+              default:
+                practiceDaysRef.sortOrder === 'descend'
+                  ? '点击升序'
+                  : practiceDaysRef.sortOrder === 'ascend'
+                  ? '取消排序'
+                  : '点击降序'
+            }}
+          </NTooltip>
+        );
+      },
       key: 'practiceDuration',
       sorter: true,
-      sortOrder: false,
+      sortOrder: false as any,
       render(row: any) {
         return (
           <>
@@ -152,10 +209,39 @@ export default defineComponent({
       }
     });
     const practiceDurationAvgRef = reactive({
-      title: '平均练习时长',
+      // title: '平均练习时长',
+      title() {
+        return (
+          <NTooltip showArrow={false} placement="top-start">
+            {{
+              trigger: () => (
+                <div class={styles.cell}>
+                  平均练习时长
+                  <img
+                    class={styles.sortIcon}
+                    src={
+                      practiceDurationAvgRef.sortOrder === 'descend'
+                        ? iconSortDesc
+                        : practiceDurationAvgRef.sortOrder === 'ascend'
+                        ? iconSortAsc
+                        : iconSortDefault
+                    }
+                  />
+                </div>
+              ),
+              default:
+                practiceDaysRef.sortOrder === 'descend'
+                  ? '点击升序'
+                  : practiceDaysRef.sortOrder === 'ascend'
+                  ? '取消排序'
+                  : '点击降序'
+            }}
+          </NTooltip>
+        );
+      },
       key: 'practiceDurationAvg',
       sorter: true,
-      sortOrder: false,
+      sortOrder: false as any,
       render(row: any) {
         return (
           <>

+ 11 - 0
src/views/home/index2.module.less

@@ -899,4 +899,15 @@
 
 .showUpdatePassword {
   width: 514px;
+}
+
+.cell {
+  display: flex;
+  align-items: center;
+
+  .sortIcon {
+    margin-left: 7px;
+    width: 13px;
+    height: 13px;
+  }
 }

+ 24 - 1
src/views/login/components/codeLogin.tsx

@@ -30,6 +30,13 @@ interface FormState {
 
 export default defineComponent({
   name: 'codeLogin',
+  props: {
+    phone: {
+      type: String,
+      default: ''
+    }
+  },
+  emits: ['update:phone'],
   setup(props, { emit }) {
     const router = useRouter();
     const route = useRoute();
@@ -52,6 +59,19 @@ export default defineComponent({
     if (formInlineHistory) {
       formInline.username = JSON.parse(formInlineHistory).username;
     }
+
+    if (formInline.username) {
+      if (formInline.username !== props.phone && props.phone) {
+        formInline.username = props.phone;
+      } else {
+        emit('update:phone', formInline.username);
+      }
+    } else {
+      if (props.phone) {
+        formInline.username = props.phone;
+      }
+    }
+
     const handleSubmit = async () => {
       formRef.value.validate(async (errors: any) => {
         if (!errors) {
@@ -152,7 +172,10 @@ export default defineComponent({
             <NInput
               maxlength={11}
               v-model:value={formInline.username}
-              placeholder="请输入手机号">
+              placeholder="请输入手机号"
+              onInput={(val: any) => {
+                emit('update:phone', val);
+              }}>
               {{
                 prefix: () => (
                   <img src={useIcon} class={styles.prefixIcon} alt="" />

+ 11 - 0
src/views/login/components/forgotPassword.tsx

@@ -29,6 +29,12 @@ interface FormState {
 
 export default defineComponent({
   name: 'forgotPassword',
+  props: {
+    phone: {
+      type: String,
+      default: ''
+    }
+  },
   emits: ['changType'],
   setup(props, { emit }) {
     const router = useRouter();
@@ -65,6 +71,11 @@ export default defineComponent({
       }
     };
 
+    // 初始化手机号
+    if (props.phone) {
+      formInline.mobile = props.phone;
+    }
+
     const handleSubmit = async () => {
       formRef.value.validate(async (errors: any) => {
         if (!errors) {

+ 24 - 3
src/views/login/components/pwdLogin.tsx

@@ -30,7 +30,13 @@ interface FormState {
 
 export default defineComponent({
   name: 'codeLogin',
-  emits: ['changType'],
+  props: {
+    phone: {
+      type: String,
+      default: ''
+    }
+  },
+  emits: ['changType', 'update:phone'],
   setup(props, { emit }) {
     const router = useRouter();
     const route = useRoute();
@@ -51,6 +57,19 @@ export default defineComponent({
     if (formInlineHistory) {
       formInline = reactive({ ...JSON.parse(formInlineHistory) });
     }
+
+    if (formInline.username) {
+      if (formInline.username !== props.phone && props.phone) {
+        formInline.username = props.phone;
+      } else {
+        emit('update:phone', formInline.username);
+      }
+    } else {
+      if (props.phone) {
+        formInline.username = props.phone;
+      }
+    }
+
     const handleSubmit = async () => {
       formRef.value.validate(async (errors: any) => {
         if (!errors) {
@@ -111,7 +130,10 @@ export default defineComponent({
             <NInput
               maxlength={11}
               v-model:value={formInline.username}
-              placeholder="请输入用户名">
+              placeholder="请输入用户名"
+              onInput={(val: any) => {
+                emit('update:phone', val);
+              }}>
               {{
                 prefix: () => (
                   <img src={useIcon} class={styles.prefixIcon} alt="" />
@@ -130,7 +152,6 @@ export default defineComponent({
               inputProps={{ autocomplete: 'off' }}
               class={[showPwd.value ? '' : styles['no-pwd']]}
               onKeydown={(e: KeyboardEvent) => {
-                console.log(e.code);
                 if (e.code === 'Enter' || e.code === 'NumpadEnter') {
                   handleSubmit();
                 }

+ 4 - 1
src/views/login/index.tsx

@@ -38,6 +38,7 @@ export default defineComponent({
     const forgotPasswordRef = ref();
     const popEvent = ref();
     const dialog = useDialog();
+    const userPhone = ref(); // 用户手机号
     const showModalMask = ref(false);
     const checkInstall = async (event: any) => {
       event.preventDefault();
@@ -144,6 +145,7 @@ export default defineComponent({
               justify-content="center">
               <NTabPane name="forgotPassword" tab="重置密码">
                 <ForgotPassword
+                  v-model:phone={userPhone.value}
                   onChangType={() => {
                     isForgot.value = false;
                     NavsValue.value = 'pwdLogin';
@@ -160,6 +162,7 @@ export default defineComponent({
               justify-content="center">
               <NTabPane name="pwdLogin" tab="密码登录">
                 <PwdLogin
+                  v-model:phone={userPhone.value}
                   onChangType={() => {
                     isForgot.value = true;
                     NavsValue.value = 'forgotPassword';
@@ -167,7 +170,7 @@ export default defineComponent({
                   }}></PwdLogin>
               </NTabPane>
               <NTabPane name="codeLogin" tab="短信验证">
-                <CodeLogin></CodeLogin>
+                <CodeLogin v-model:phone={userPhone.value}></CodeLogin>
               </NTabPane>
             </NTabs>
           )}

+ 1 - 1
src/views/natural-resources/components/my-collect/index.tsx

@@ -22,7 +22,7 @@ export default defineComponent({
         rows: 20
       },
       searchGroup: {
-        type: '', //
+        type: 'MUSIC', //
         name: '',
         bookVersionId: null,
         subjectId: null,

+ 5 - 5
src/views/natural-resources/components/my-collect/search-group-resources.tsx

@@ -13,7 +13,7 @@ export default defineComponent({
     const catchStore = useCatchStore();
     const resourceList = ref([] as any[]);
     const forms = reactive({
-      type: '', //
+      type: 'MUSIC', //
       name: '',
       bookVersionId: null,
       subjectId: null
@@ -70,10 +70,10 @@ export default defineComponent({
 
     onMounted(async () => {
       resourceList.value = [
-        {
-          label: '全部',
-          value: ''
-        },
+        // {
+        //   label: '全部',
+        //   value: ''
+        // },
         ...resourceTypeArray
       ];
 

+ 1 - 0
src/views/natural-resources/components/my-resources/index.tsx

@@ -212,6 +212,7 @@ export default defineComponent({
                 <div class={styles.itemWrapBox}>
                   <CardType
                     item={item}
+                    isDownload
                     disabledMouseHover={false}
                     offShelf={item.enableFlag ? false : true}
                     onOffShelf={() => {

+ 56 - 43
src/views/prepare-lessons/components/lesson-main/courseware/index.tsx

@@ -1,4 +1,4 @@
-import { defineComponent, onMounted, reactive, watch } from 'vue';
+import { defineComponent, nextTick, onMounted, reactive, watch } from 'vue';
 import styles from './index.module.less';
 import {
   NButton,
@@ -32,6 +32,7 @@ import { state } from '/src/state';
 import SubjectSync from '../../../model/subject-sync';
 import { eventGlobal } from '/src/utils';
 import iconTips from '../../../images/icon-tips.png';
+import { useElementBounding } from '@vueuse/core';
 export default defineComponent({
   name: 'courseware-modal',
   setup() {
@@ -302,27 +303,6 @@ export default defineComponent({
           'prepareLessonSubjectId'
         );
         // // 先取 上次上课声部,在取班级声部 最后取缓存
-        // const subjectId =
-        //   forms.courseScheduleSubjectId ||
-        //   forms.subjectId ||
-        //   localStorageSubjectId
-        //     ? Number(
-        //         forms.courseScheduleSubjectId ||
-        //           forms.subjectId ||
-        //           localStorageSubjectId
-        //       )
-        //     : null;
-        // // 判断浏览器上面是否有
-        // const index = subjectList.findIndex(
-        //   (subject: any) => subject.id == subjectId
-        // );
-
-        // if (subjectId && index >= 0) {
-        //   prepareStore.setSubjectId(subjectId);
-        // } else {
-        //   // 判断是否有缓存
-        //   prepareStore.setSubjectId(subjectList[0].id);
-        // }
         let subjectId = null;
         let index = -1;
         if (forms.courseScheduleSubjectId) {
@@ -385,6 +365,17 @@ export default defineComponent({
       }
     );
 
+    const isPointInsideElement = (element: any, x: number, y: number) => {
+      const rect = element.getBoundingClientRect();
+      return (
+        x >= rect.left && x <= rect.right && y >= rect.top && y <= rect.bottom
+      );
+    };
+    const isPointOnLeft = (element: any, x: number) => {
+      const rect = element.getBoundingClientRect();
+      const elementCenterX = rect.left + rect.width / 2;
+      return x < elementCenterX;
+    };
     onMounted(async () => {
       prepareStore.setClassGroupId(route.query.classGroupId as any);
       // 获取教材分类列表
@@ -393,12 +384,31 @@ export default defineComponent({
       await getList();
 
       // 动态添加数据
-      eventGlobal.on('onPrepareAddItem', (item: any) => {
+      eventGlobal.on('onPrepareAddItem', (item: any, point?: any) => {
         forms.drag = true;
-        forms.coursewareList.push(item);
-        prepareStore.setCoursewareList(forms.coursewareList);
-
         forms.isEdit = true;
+        nextTick(() => {
+          if (point) {
+            const dom = document.querySelectorAll('.row-nav');
+            dom.forEach((child: any, index: number) => {
+              const status = isPointInsideElement(child, point.x, point.y);
+              if (status) {
+                const array: any = forms.coursewareList;
+                const left = isPointOnLeft(child, point.x);
+                if (!left) {
+                  array.splice(index + 1, 0, item);
+                } else {
+                  array.splice(index, 0, item);
+                }
+                forms.coursewareList = array;
+                prepareStore.setCoursewareList(forms.coursewareList);
+              }
+            });
+          } else {
+            forms.coursewareList.push(item);
+            prepareStore.setCoursewareList(forms.coursewareList);
+          }
+        });
       });
     });
 
@@ -406,13 +416,6 @@ export default defineComponent({
       <div class={styles.coursewareModal}>
         <div class={styles.btnGroup}>
           {forms.drag ? (
-            // <div class={styles.tipsContainer}>
-            //   <div class={styles.tipsLeft}>
-            //     <img src={iconTips} class={styles.iconTips} />
-            //     <span class={styles.tips}>拖动可将资源进行排序哦~</span>
-            //   </div>
-            //   <span class={styles.btnNoTips}>不再提醒</span>
-            // </div>
             !forms.tipsStatus ? (
               <div class={styles.tipsContainer}>
                 <div class={styles.tipsLeft}>
@@ -543,20 +546,30 @@ export default defineComponent({
                 e.preventDefault();
               }}
               onDrop={(e: any) => {
+                console.log(e, 'event');
                 let dropItem = e.dataTransfer.getData('text');
                 dropItem = dropItem ? JSON.parse(dropItem) : {};
                 // 判断是否有数据
                 if (dropItem.id) {
-                  eventGlobal.emit('onPrepareAddItem', {
-                    materialId: dropItem.id,
-                    coverImg: dropItem.coverImg,
-                    type: dropItem.type,
-                    title: dropItem.title,
-                    isCollect: dropItem.isCollect,
-                    isSelected: dropItem.isSelected,
-                    content: dropItem.content,
-                    removeFlag: false
-                  });
+                  // 获取拖拽的目标元素
+
+                  eventGlobal.emit(
+                    'onPrepareAddItem',
+                    {
+                      materialId: dropItem.id,
+                      coverImg: dropItem.coverImg,
+                      type: dropItem.type,
+                      title: dropItem.title,
+                      isCollect: dropItem.isCollect,
+                      isSelected: dropItem.isSelected,
+                      content: dropItem.content,
+                      removeFlag: false
+                    },
+                    {
+                      x: e.clientX,
+                      y: e.clientY
+                    }
+                  );
                 }
               }}>
               {forms.coursewareList.length > 0 && (

+ 1 - 4
src/views/prepare-lessons/components/resource-main/components/resource-item/index.tsx

@@ -40,10 +40,7 @@ export default defineComponent({
         rows: 20
       },
       searchGroup: {
-        type:
-          props.type === 'shareResources' || props.type === 'myResources'
-            ? 'MUSIC'
-            : '', //
+        type: 'MUSIC', //
         name: '',
         bookVersionId: null,
         subjectId: null,

+ 7 - 7
src/views/prepare-lessons/components/resource-main/components/resource-item/resource-search-group/index.tsx

@@ -32,13 +32,13 @@ export default defineComponent({
     onMounted(async () => {
       // 获取教材分类列表
       await catchStore.getMusicSheetCategory();
-      if (props.type === 'myCollect') {
-        resourceType.value.push({
-          label: '全部',
-          value: ''
-        });
-        forms.type = ''; // 默认全部
-      }
+      // if (props.type === 'myCollect') {
+      //   resourceType.value.push({
+      //     label: '全部',
+      //     value: ''
+      //   });
+      //   forms.type = ''; // 默认全部
+      // }
       resourceTypeArray.forEach((item: any) => {
         // if (props.type === 'myResources') {
         //   item.value !== 'MUSIC' && resourceType.value.push(item);

+ 1 - 1
src/views/prepare-lessons/components/resource-main/components/select-music/index.tsx

@@ -165,7 +165,7 @@ export default defineComponent({
     onMounted(() => {
       getList();
 
-      eventGlobal.on('onTrainDragItem', (item: any) => {
+      eventGlobal.on('onTrainDragItem', (item: any, point?: any) => {
         console.log('onTrainDragItem', Date.now());
         onAdd(item);
       });

+ 15 - 22
src/views/prepare-lessons/model/select-resources/index.tsx

@@ -10,6 +10,11 @@ export default defineComponent({
     type: {
       type: String,
       default: 'myResources'
+    },
+    /** 从哪里使用 */
+    from: {
+      type: String,
+      default: ''
     }
   },
   emits: ['select'],
@@ -18,10 +23,10 @@ export default defineComponent({
     const tabType = ref(type.value);
 
     onMounted(() => {
-      console.log(
-        document.querySelector('.select-resource .n-tabs-nav--top'),
-        ''
-      );
+      // console.log(
+      //   document.querySelector('.select-resource .n-tabs-nav--top'),
+      //   ''
+      // );
       useResizeObserver(
         document.querySelector(
           '.select-resource .n-tabs-nav--top'
@@ -48,26 +53,14 @@ export default defineComponent({
           onUpdate:value={(val: string) => {
             tabType.value = val;
           }}>
-          <NTabPane
-            name="myResources"
-            tab="我的资源"
-            // displayDirective="show:lazy"
-          >
-            <SelectItem type="myResources" />
+          <NTabPane name="myResources" tab="我的资源">
+            <SelectItem type="myResources" from={props.from} />
           </NTabPane>
-          <NTabPane
-            name="shareResources"
-            tab="共享资源"
-            // displayDirective="show:lazy"
-          >
-            <SelectItem type="shareResources" />
+          <NTabPane name="shareResources" tab="共享资源">
+            <SelectItem type="shareResources" from={props.from} />
           </NTabPane>
-          <NTabPane
-            name="myCollect"
-            tab="我的收藏"
-            // displayDirective="show:lazy"
-          >
-            <SelectItem type="myCollect" />
+          <NTabPane name="myCollect" tab="我的收藏">
+            <SelectItem type="myCollect" from={props.from} />
           </NTabPane>
         </NTabs>
       </div>

+ 149 - 0
src/views/prepare-lessons/model/select-resources/select-item/class-search-group/index.module.less

@@ -0,0 +1,149 @@
+.searchGroup {
+  position: relative;
+  padding: 0 40px;
+
+
+  .btnType {
+    gap: 0px 24px !important;
+
+    :global {
+      .n-button {
+        height: 37px;
+        padding: 0 24px;
+        font-size: 18px;
+        color: rgba(0, 0, 0, .6);
+
+        &.n-button--primary-type {
+          font-weight: bold;
+          color: #fff;
+        }
+      }
+    }
+  }
+
+  :global {
+    .n-form {
+      position: relative;
+    }
+
+    .n-form-item {
+      .n-form-item-label {
+        font-size: max(17px, 13px);
+        font-weight: 600;
+        color: #131415;
+        line-height: 24px;
+      }
+
+      .n-button {
+        height: 32px;
+        font-size: max(17px, 13px);
+        border-radius: 8px;
+        color: rgba(0, 0, 0, 0.6);
+      }
+
+      .n-button--primary-type {
+        color: #131415;
+        background-color: #D2ECFF !important;
+      }
+    }
+
+    .n-form-item-feedback-wrapper {
+      min-height: 14px;
+    }
+  }
+
+  .inputSearch {
+    position: absolute;
+    top: 4px;
+    right: 0px;
+    width: 360px;
+    height: 42px;
+    font-size: 16px;
+    --n-height: 42px !important;
+
+    img {
+      width: 18px;
+      height: 18px;
+    }
+
+    :global {
+      .n-input-wrapper {
+        padding-left: 12px;
+        padding-right: 4px;
+        height: 42px !important;
+      }
+
+      .n-button {
+        height: 34px;
+        font-size: 15px;
+        font-weight: 500;
+        width: auto;
+      }
+    }
+  }
+
+  .searchCatatory {
+    display: flex;
+    justify-content: space-between;
+    padding-bottom: 20px;
+    border-bottom: 1px solid #F2F2F2;
+    margin-bottom: 20px;
+
+    .addTrain {
+      height: 37px;
+      border-radius: 8px;
+      font-size: 18px;
+      background-color: #E8F4FF;
+      color: #0378EC;
+
+      img {
+        width: 16px;
+        height: 16px;
+        margin-right: 8px;
+      }
+    }
+  }
+}
+
+.spaceSection {
+  width: 69%;
+  gap: 8px 12px !important;
+
+  &>div {
+    line-height: var(--n-blank-height);
+  }
+}
+
+
+.collapseWrap {
+  width: 98%;
+  display: flex;
+  flex-direction: row;
+  align-items: flex-end;
+
+}
+
+.collaoseBtn {
+  width: 32px;
+  height: 32px;
+  cursor: pointer;
+}
+
+.collaoseBtn.isStart {
+  transform: rotate(-180deg);
+}
+
+.collapsSection {
+  // padding-top: 10px;
+}
+
+.isHidden {
+  overflow: hidden;
+  transition: 1s all ease-in;
+}
+
+.hideButton {
+  visibility: hidden;
+  height: 0 !important;
+  line-height: 0 !important;
+}

+ 249 - 0
src/views/prepare-lessons/model/select-resources/select-item/class-search-group/index.tsx

@@ -0,0 +1,249 @@
+import {
+  PropType,
+  defineComponent,
+  nextTick,
+  onMounted,
+  reactive,
+  ref,
+  toRef
+} from 'vue';
+import styles from './index.module.less';
+import { NButton, NForm, NFormItem, NImage, NSpace } from 'naive-ui';
+import { resourceTypeArray } from '/src/utils/searchArray';
+import { useCatchStore } from '/src/store/modules/catchData';
+import { useThrottleFn } from '@vueuse/core';
+import TheSearch from '/src/components/TheSearch';
+import isCollaose from '/src/views/natural-resources/images/isCollaose.png';
+
+export default defineComponent({
+  name: 'resource-search-group',
+  props: {
+    type: {
+      type: String as PropType<'shareResources' | 'myResources' | 'myCollect'>,
+      default: 'shareResources'
+    },
+    subjectId: {
+      type: String,
+      default: ''
+    }
+  },
+  emits: ['search'],
+  setup(props, { emit }) {
+    const subjectId = toRef(props.subjectId);
+    const catchStore = useCatchStore();
+    const forms = reactive({
+      type: 'MUSIC', //
+      name: '',
+      subjectId: subjectId.value as any,
+      bookVersionId: null
+    });
+    const resourceType = ref([] as any);
+
+    const onSearch = () => {
+      emit('search', forms);
+    };
+
+    const throttleFn = useThrottleFn(() => onSearch(), 500);
+
+    // const collapseWrapRef = ref();
+    // const divDomList = ref([] as any);
+    // const orginHeight = ref(0);
+    // const hiddenHeight = ref(0);
+    // const line = ref(0);
+    // const isCollapse = ref(false);
+    // const loadingCollapse = ref(false); // 是否加载完成
+    // const musicCateRef = (el: any) => {
+    //   if (el?.selfElRef) {
+    //     divDomList.value.push(el.selfElRef.parentNode);
+    //   }
+    // };
+    // const setCollapse = (flag: boolean) => {
+    //   isCollapse.value = flag;
+    //   getLive();
+    // };
+    // const getLive = () => {
+    //   try {
+    //     divDomList.value = [...new Set(divDomList.value)];
+    //     let offsetLeft = -1;
+    //     divDomList.value.forEach((item: any, index: number) => {
+    //       if (index === 0) {
+    //         line.value = 1;
+    //         offsetLeft = item.offsetLeft;
+    //       } else if (item.offsetLeft === offsetLeft && index != 0) {
+    //         // 如果某个标签的offsetLeft和第一个标签的offsetLeft相等  说明增加了一行
+    //         line.value++;
+    //       }
+    //       if (!isCollapse.value) {
+    //         if (line.value > 1) {
+    //           //从第3行开始 隐藏标签
+    //           item.style.display = 'none';
+
+    //           // 显示展开按钮  class名chu是在前面动态添加的
+    //         } else {
+    //           item.style.display = 'block';
+    //         }
+    //       } else {
+    //         item.style.display = 'block';
+    //       }
+    //     });
+    //     loadingCollapse.value = true;
+    //   } catch {
+    //     //
+    //   }
+    // };
+    onMounted(async () => {
+      // if (props.type === 'myCollect') {
+      //   resourceType.value.push({
+      //     label: '全部',
+      //     value: ''
+      //   });
+      //   forms.type = ''; // 默认全部
+      // }
+      resourceTypeArray.forEach((item: any) => {
+        // if (props.type === 'myResources') {
+        //   item.value !== 'MUSIC' && resourceType.value.push(item);
+        // } else {
+        resourceType.value.push(item);
+        // }
+      });
+
+      // 获取教材分类列表
+      await catchStore.getMusicSheetCategory();
+      // 获取声部
+      await catchStore.getSubjects();
+
+      // 这里开始
+      // musicCateRef
+      // if (forms.type === 'MUSIC') {
+      //   orginHeight.value = collapseWrapRef.value?.offsetHeight;
+      //   hiddenHeight.value = collapseWrapRef.value?.offsetHeight / line.value;
+      //   // 默认隐藏
+      //   getLive();
+      // }
+    });
+    return () => (
+      <div class={styles.searchGroup}>
+        {/* <div class={styles.searchCatatory}> */}
+        {/* <NSpace size="small" class={styles.btnType}>
+            {resourceType.value.map((item: any) => (
+              <NButton
+                type={forms.type === item.value ? 'primary' : 'default'}
+                secondary={forms.type === item.value ? false : true}
+                round
+                size="small"
+                focusable={false}
+                onClick={() => {
+                  forms.type = item.value;
+                  onSearch();
+
+                  try {
+                    nextTick(() => {
+                      if (forms.type === 'MUSIC') {
+                        orginHeight.value = collapseWrapRef.value?.offsetHeight;
+                        hiddenHeight.value =
+                          collapseWrapRef.value?.offsetHeight / line.value;
+                        // 默认隐藏
+                        getLive();
+                      } else {
+                        divDomList.value = [];
+                      }
+                    });
+                  } catch {
+                    //
+                  }
+                }}>
+                {item.label}
+              </NButton>
+            ))}
+          </NSpace> */}
+
+        {/* </div> */}
+        <NForm labelAlign="left" labelPlacement="left">
+          {/* {forms.type === 'MUSIC' && props.type === 'shareResources' && (
+            <div class={styles.collapsSection}>
+              <NFormItem label="教材:">
+                <div
+                  class={[
+                    styles.collapseWrap,
+                    loadingCollapse.value ? '' : styles.hideButton,
+                    isCollapse.value ? '' : styles.isHidden
+                  ]}
+                  ref={collapseWrapRef}>
+                  <NSpace class={[styles.spaceSection]}>
+                    {catchStore.getAllMusicCategories.map((music: any) => (
+                      <NButton
+                        ref={musicCateRef}
+                        secondary={forms.bookVersionId === music.id}
+                        quaternary={forms.bookVersionId !== music.id}
+                        strong
+                        focusable={false}
+                        type={
+                          forms.bookVersionId === music.id
+                            ? 'primary'
+                            : 'default'
+                        }
+                        onClick={() => {
+                          forms.bookVersionId = music.id;
+                          onSearch();
+                        }}>
+                        {music.name}
+                      </NButton>
+                    ))}
+                    {line.value > 1 && (
+                      <div
+                        style={{
+                          height: 'var(--n-blank-height)',
+                          position: 'absolute',
+                          display: 'flex',
+                          alignItems: 'center'
+                        }}
+                        onClick={() => {
+                          setCollapse(!isCollapse.value);
+                        }}>
+                        <NImage
+                          previewDisabled
+                          src={isCollaose}
+                          class={[
+                            styles.collaoseBtn,
+                            isCollapse.value ? styles.isStart : ''
+                          ]}></NImage>
+                      </div>
+                    )}
+                  </NSpace>
+                </div>
+              </NFormItem>
+            </div>
+          )} */}
+
+          <NFormItem label="声部:">
+            <NSpace class={styles.spaceSection}>
+              {catchStore.getSubjectAllList.map((music: any) => (
+                <NButton
+                  secondary={forms.subjectId === music.id}
+                  quaternary={forms.subjectId !== music.id}
+                  strong
+                  focusable={false}
+                  type={forms.subjectId === music.id ? 'primary' : 'default'}
+                  onClick={() => {
+                    forms.subjectId = music.id;
+                    onSearch();
+                  }}>
+                  {music.name}
+                </NButton>
+              ))}
+            </NSpace>
+          </NFormItem>
+
+          <TheSearch
+            class={styles.inputSearch}
+            round
+            onSearch={(val: string) => {
+              forms.name = val;
+              throttleFn();
+            }}
+          />
+        </NForm>
+      </div>
+    );
+  }
+});

+ 21 - 58
src/views/prepare-lessons/model/select-resources/select-item/index.tsx

@@ -7,16 +7,16 @@ import {
   watch
 } from 'vue';
 import ResourceSearchGroup from './resource-search-group';
-import { NScrollbar, NSpin, useDialog, useMessage } from 'naive-ui';
+import { NScrollbar, NSpin } from 'naive-ui';
 import styles from './index.module.less';
 import CardType from '/src/components/card-type';
 import { favorite, materialQueryPage } from '/src/views/natural-resources/api';
 import TheEmpty from '/src/components/TheEmpty';
 import { usePrepareStore } from '/src/store/modules/prepareLessons';
-import { saveCourseware } from '../../../api';
 import { useDebounceFn, useResizeObserver } from '@vueuse/core';
 import CardPreview from '/src/components/card-preview';
 import { eventGlobal } from '/src/utils';
+import ClassSearchGroup from './class-search-group';
 
 const formatType = (type: string) => {
   if (type === 'shareResources') {
@@ -34,11 +34,15 @@ export default defineComponent({
     type: {
       type: String as PropType<'shareResources' | 'myResources' | 'myCollect'>,
       default: 'shareResources'
+    },
+    /** 从哪里使用 */
+    from: {
+      type: String,
+      default: ''
     }
   },
   setup(props) {
     const prepareStore = usePrepareStore();
-    const message = useMessage();
     const { type } = toRefs(props);
     const className = 'resourceSearchGroup' + +new Date();
     const state = reactive({
@@ -50,10 +54,7 @@ export default defineComponent({
         rows: 20
       },
       searchGroup: {
-        type:
-          type.value === 'shareResources' || type.value === 'myResources'
-            ? 'MUSIC'
-            : '', //
+        type: 'MUSIC', //
         name: '',
         bookVersionId: null,
         subjectId: prepareStore.getSubjectId,
@@ -102,12 +103,6 @@ export default defineComponent({
       }
     };
 
-    // const onSearch = async (item: any) => {
-    //   state.pagination.page = 1;
-    //   state.tableList = [];
-    //   state.searchGroup = Object.assign(state.searchGroup, item);
-    //   getList();
-    // };
     const throttledFnSearch = useDebounceFn(item => {
       state.pagination.page = 1;
       state.tableList = [];
@@ -117,41 +112,7 @@ export default defineComponent({
 
     // 添加资源
     const onAdd = async (item: any) => {
-      // dialog.warning({
-      //   title: '提示',
-      //   content: `是否添加"${item.title}"资源?`,
-      //   positiveText: '确定',
-      //   negativeText: '取消',
-      //   onPositiveClick: async () => {
       try {
-        // const temp: any = [];
-        // prepareStore.getCoursewareList.forEach((item: any) => {
-        //   temp.push({
-        //     materialId: item.materialId,
-        //     materialName: item.title,
-        //     materialType: item.type,
-        //     id: item.id
-        //   });
-        // });
-
-        // // 保存课件
-        // await saveCourseware({
-        //   coursewareDetailKnowledgeId: prepareStore.getSelectKey,
-        //   lessonCoursewareId: prepareStore.getLessonCoursewareId,
-        //   lessonCoursewareDetailId: prepareStore.getLessonCoursewareDetailId,
-        //   materialList: [
-        //     ...temp,
-        //     {
-        //       materialName: item.title,
-        //       materialType: item.type,
-        //       materialId: item.id
-        //     }
-        //   ],
-        //   subjectId: prepareStore.getSubjectId
-        // });
-
-        // message.success('添加成功');
-        // prepareStore.setIsAddResource(true);
         eventGlobal.emit('onPrepareAddItem', {
           materialId: item.id,
           coverImg: item.coverImg,
@@ -165,8 +126,6 @@ export default defineComponent({
       } catch {
         //
       }
-      //   }
-      // });
     };
 
     watch(
@@ -207,10 +166,6 @@ export default defineComponent({
         (entries: any) => {
           const entry = entries[0];
           const { height } = entry.contentRect;
-          // document.documentElement.style.setProperty(
-          //   '--modal-lesson-search-height',
-          //   height + 'px'
-          // );
           state.searchHeight = height + 'px';
         }
       );
@@ -218,11 +173,19 @@ export default defineComponent({
     return () => (
       <div>
         <div class={className}>
-          <ResourceSearchGroup
-            type={props.type}
-            subjectId={prepareStore.getSubjectId as any}
-            onSearch={(item: any) => throttledFnSearch(item)}
-          />
+          {props.from === 'class' ? (
+            <ClassSearchGroup
+              type={props.type}
+              subjectId={prepareStore.getSubjectId as any}
+              onSearch={(item: any) => throttledFnSearch(item)}
+            />
+          ) : (
+            <ResourceSearchGroup
+              type={props.type}
+              subjectId={prepareStore.getSubjectId as any}
+              onSearch={(item: any) => throttledFnSearch(item)}
+            />
+          )}
         </div>
         <NScrollbar
           class={styles.listContainer}

+ 7 - 7
src/views/prepare-lessons/model/select-resources/select-item/resource-search-group/index.tsx

@@ -92,13 +92,13 @@ export default defineComponent({
       }
     };
     onMounted(async () => {
-      if (props.type === 'myCollect') {
-        resourceType.value.push({
-          label: '全部',
-          value: ''
-        });
-        forms.type = ''; // 默认全部
-      }
+      // if (props.type === 'myCollect') {
+      //   resourceType.value.push({
+      //     label: '全部',
+      //     value: ''
+      //   });
+      //   forms.type = ''; // 默认全部
+      // }
       resourceTypeArray.forEach((item: any) => {
         // if (props.type === 'myResources') {
         //   item.value !== 'MUSIC' && resourceType.value.push(item);

+ 5 - 0
src/views/studentList/components/baseInfo.tsx

@@ -109,6 +109,8 @@ export default defineComponent({
       studentForm.currentClass = props.studentInfo.currentClass;
       data.disabled = true;
     };
+
+    const onlyAllowNumber = (value: string) => !value || /^\d+$/.test(value);
     return () => (
       <div class={styles.infoWrap}>
         <div class={styles.setInfo}>
@@ -124,6 +126,7 @@ export default defineComponent({
                     trigger: 'blur'
                   }}>
                   <NInput
+                    maxlength={15}
                     bordered={!data.disabled}
                     placeholder="请填写学生姓名"
                     v-model:value={studentForm.nickname}></NInput>
@@ -146,6 +149,8 @@ export default defineComponent({
                     }
                   ]}>
                   <NInput
+                    maxlength={11}
+                    allowInput={onlyAllowNumber}
                     bordered={!data.disabled}
                     placeholder="请填写学生手机号"
                     v-model:value={studentForm.phone}></NInput>

+ 9 - 6
src/views/studentList/components/evaluationRecords.tsx

@@ -10,7 +10,6 @@ import {
   NSpace,
   NTag
 } from 'naive-ui';
-import numeral from 'numeral';
 import { useECharts } from '@/hooks/web/useECharts';
 import Pagination from '/src/components/pagination';
 import { getPracticeRecordList } from '../api';
@@ -177,6 +176,7 @@ export default defineComponent({
       ];
       search();
     };
+    const iframeRef = ref();
     onMounted(() => {
       getList();
       console.log(props.studentId);
@@ -225,11 +225,14 @@ export default defineComponent({
           preset="card"
           class={['modalTitle background', styles.reportModel]}
           title={'评测报告'}>
-          <iframe
-            width={'100%'}
-            height={'450px'}
-            frameborder="0"
-            src={reportSrc.value}></iframe>
+          <div class={styles.reportContainer} style={{ lineHeight: 0 }}>
+            <iframe
+              width={'100%'}
+              height={'450px'}
+              ref={iframeRef}
+              frameborder="0"
+              src={reportSrc.value}></iframe>
+          </div>
         </NModal>
       </>
     );

+ 3 - 3
src/views/studentList/components/practiceData.tsx

@@ -348,7 +348,7 @@ export default defineComponent({
                     <i style={{ width: '4px', display: 'inline-block' }}></i>秒
                   </div>
                 </p>
-                <p class={styles.TrainDataItemsubTitle}>平均练习时长</p>
+                <p class={styles.TrainDataItemsubTitle}>平均每天练习时长</p>
               </div>
               <div class={styles.TrainDataItem}>
                 <p class={styles.TrainDataItemTitle}>
@@ -390,8 +390,8 @@ export default defineComponent({
           </div>
           <div class={styles.tableWrap}>
             <NDataTable
-               v-slots={{
-                empty:()=><TheEmpty></TheEmpty>
+              v-slots={{
+                empty: () => <TheEmpty></TheEmpty>
               }}
               class={styles.classTable}
               loading={state.loading}

+ 31 - 33
src/views/studentList/components/studentAfterWork.tsx

@@ -5,20 +5,14 @@ import {
   NDataTable,
   NForm,
   NFormItem,
-  NImage,
   NModal,
-  NSelect,
   NSpace
 } from 'naive-ui';
-import SearchInput from '@/components/searchInput';
 import CSelect from '@/components/CSelect';
 import Pagination from '@/components/pagination';
 import { getStudentAfterWork } from '../api';
-import add from './images/add.png';
 import { useRoute } from 'vue-router';
-import CBreadcrumb from '/src/components/CBreadcrumb';
 import CDatePicker from '/src/components/CDatePicker';
-import defultHeade from '@/components/layout/images/teacherIcon.png';
 import {
   getNowDateAndMonday,
   getNowDateAndSunday,
@@ -27,11 +21,10 @@ import {
 import { trainingStatusArray } from '@/utils/searchArray';
 import StudentTraomomhDetails from '../modals/studentTraomomhDetails';
 import dayjs from 'dayjs';
-import { lookup } from 'dns';
 import TheEmpty from '/src/components/TheEmpty';
 export default defineComponent({
   name: 'student-studentList',
-  setup(props, { emit }) {
+  setup() {
     const state = reactive({
       searchForm: { keyword: '', trainingStatus: null as any },
       loading: false,
@@ -52,16 +45,17 @@ export default defineComponent({
       index: 0
     });
     const timer = ref<[number, number]>([
-      getNowDateAndMonday(new Date().getTime()),
+      new Date('2023-01-01').getTime(),
+      // getNowDateAndMonday(new Date().getTime()),
       getNowDateAndSunday(new Date().getTime())
     ]);
     const TrainingDetailsRef = ref();
     const route = useRoute();
-    const routerList = ref([
-      { name: '班级管理', path: '/classList' },
-      { name: route.query.name, path: '/classDetail' },
-      { name: route.query.teacherName, path: '/afterWorkDetail' }
-    ] as any);
+    // const routerList = ref([
+    //   { name: '班级管理', path: '/classList' },
+    //   { name: route.query.name, path: '/classDetail' },
+    //   { name: route.query.teacherName, path: '/afterWorkDetail' }
+    // ] as any);
 
     const search = () => {
       state.pagination.page = 1;
@@ -156,10 +150,14 @@ export default defineComponent({
             return (
               <div>
                 {row.trainingStatus == 'UNSUBMITTED' ? (
-                  <p class={styles.nosub} style={{color:'#aaa'}}>未提交</p>
+                  <p class={styles.nosub} style={{ color: '#aaa' }}>
+                    未提交
+                  </p>
                 ) : null}
                 {row.trainingStatus == 'SUBMITTED' ? (
-                  <p style={{color:'#EA4132'}} class={styles.ison}>不合格</p>
+                  <p style={{ color: '#EA4132' }} class={styles.ison}>
+                    不合格
+                  </p>
                 ) : null}
                 {row.trainingStatus == 'TARGET' ? (
                   <p class={styles.isok}>合格</p>
@@ -187,22 +185,22 @@ export default defineComponent({
       ];
     };
 
-    const goToNext = () => {
-      ++state.index;
+    // const goToNext = () => {
+    //   ++state.index;
 
-      state.activeRow = state.tableList[state.index - 1];
+    //   state.activeRow = state.tableList[state.index - 1];
 
-      TrainingDetailsRef.value.getTrainingDetail(
-        state.activeRow.studentLessonTrainingId
-      );
-    };
-    const gotoPre = () => {
-      --state.index;
-      state.activeRow = state.tableList[state.index - 1];
-      TrainingDetailsRef.value.getTrainingDetail(
-        state.activeRow.studentLessonTrainingId
-      );
-    };
+    //   TrainingDetailsRef.value.getTrainingDetail(
+    //     state.activeRow.studentLessonTrainingId
+    //   );
+    // };
+    // const gotoPre = () => {
+    //   --state.index;
+    //   state.activeRow = state.tableList[state.index - 1];
+    //   TrainingDetailsRef.value.getTrainingDetail(
+    //     state.activeRow.studentLessonTrainingId
+    //   );
+    // };
     return () => (
       <div>
         <div>
@@ -250,8 +248,8 @@ export default defineComponent({
           </div>
           <div>
             <NDataTable
-               v-slots={{
-                empty:()=><TheEmpty></TheEmpty>
+              v-slots={{
+                empty: () => <TheEmpty></TheEmpty>
               }}
               class={styles.classTable}
               loading={state.loading}
@@ -270,7 +268,7 @@ export default defineComponent({
           v-model:show={state.detailVisiable}
           preset="card"
           class={['modalTitle background', styles.wordDetailModel]}
-          title={'训练详情'}>
+          title={'作业详情'}>
           <StudentTraomomhDetails
             // onNext={() => goToNext()}
             // onPre={() => gotoPre()}

+ 24 - 0
src/views/studentList/index.module.less

@@ -274,6 +274,25 @@
 .reportModel {
   width: 1030px;
   overflow: hidden;
+
+  //   /* 设置滚动条的宽度、颜色和轨道背景 */
+  //   iframe::-webkit-scrollbar {
+  //     width: 10px;
+  //   }
+
+  //   iframe::-webkit-scrollbar-thumb {
+  //     background-color: #888;
+  //   }
+
+  //   iframe::-webkit-scrollbar-track {
+  //     background-color: #f0f0f0;
+  //   }
+
+  //   /* Firefox */
+  //   iframe {
+  //     scrollbar-width: thin;
+  //     scrollbar-color: #888 #f0f0f0;
+  //   }
 }
 
 .addStudentWrap {
@@ -585,4 +604,9 @@
       }
     }
   }
+}
+
+
+.updateStudent {
+  width: 480px;
 }

+ 51 - 13
src/views/studentList/index.tsx

@@ -9,6 +9,7 @@ import {
   NModal,
   NSelect,
   NSpace,
+  NTooltip,
   useMessage
 } from 'naive-ui';
 import SearchInput from '@/components/searchInput';
@@ -21,8 +22,9 @@ import { classGroupList } from '@/views/classList/api';
 import AddStudentModel from './modals/addStudentModel';
 import Studentguide from '@/custom-plugins/guide-page/student-guide';
 import TheEmpty from '/src/components/TheEmpty';
-import NoticeModal from './modals/noticeModal';
+// import NoticeModal from './modals/noticeModal';
 import { useUserStore } from '/src/store/modules/users';
+import UpdateStudent from './modals/update-student';
 export default defineComponent({
   name: 'student-studentList',
   setup(props, { emit }) {
@@ -49,6 +51,7 @@ export default defineComponent({
       tableList: [] as any,
       classList: [],
       addStudentVisible: false,
+      editStatus: false,
       activeRow: {} as any
     });
     const route = useRoute();
@@ -121,11 +124,18 @@ export default defineComponent({
           key: 'nickname',
           render: (row: any) => {
             return (
-              <div
-                style={{ userSelect: 'all', cursor: 'pointer' }}
-                onClick={() => copyTo(row.nickname)}>
-                {row.nickname}
-              </div>
+              <NTooltip showArrow={false} placement="top-start">
+                {{
+                  trigger: () => (
+                    <div
+                      style={{ userSelect: 'all', cursor: 'pointer' }}
+                      onClick={() => copyTo(row.nickname)}>
+                      {row.nickname}
+                    </div>
+                  ),
+                  default: '点击复制'
+                }}
+              </NTooltip>
             );
           }
         },
@@ -134,11 +144,18 @@ export default defineComponent({
           key: 'phone',
           render: (row: any) => {
             return (
-              <div
-                style={{ userSelect: 'all', cursor: 'pointer' }}
-                onClick={() => copyTo(row.phone)}>
-                {row.phone}
-              </div>
+              <NTooltip showArrow={false} placement="top-start">
+                {{
+                  trigger: () => (
+                    <div
+                      style={{ userSelect: 'all', cursor: 'pointer' }}
+                      onClick={() => copyTo(row.phone)}>
+                      {row.phone}
+                    </div>
+                  ),
+                  default: '点击复制'
+                }}
+              </NTooltip>
             );
           }
         },
@@ -175,7 +192,7 @@ export default defineComponent({
           width: 300,
           render(row: any, index: number) {
             return (
-              <>
+              <NSpace>
                 {index == 0 ? (
                   <NButton
                     {...{ id: 'student-1' }}
@@ -189,7 +206,10 @@ export default defineComponent({
                     详情
                   </NButton>
                 )}
-              </>
+                <NButton text type="primary" onClick={() => onUpdate(row)}>
+                  修改
+                </NButton>
+              </NSpace>
             );
           }
         }
@@ -202,6 +222,12 @@ export default defineComponent({
         query: { ...route.query, studentId: row.id, studentName: row.nickname }
       });
     };
+
+    // 修改
+    const onUpdate = (row: any) => {
+      state.editStatus = true;
+      state.activeRow = row;
+    };
     return () => (
       <div class={styles.listWrap}>
         <div class={styles.searchList}>
@@ -358,6 +384,18 @@ export default defineComponent({
             onClose={() => (state.addStudentVisible = false)}
           />
         </NModal> */}
+
+        <NModal
+          v-model:show={state.editStatus}
+          class={['modalTitle background', styles.updateStudent]}
+          preset="card"
+          title="修改信息">
+          <UpdateStudent
+            onClose={() => (state.editStatus = false)}
+            onConfirm={() => getList()}
+            row={state.activeRow}
+          />
+        </NModal>
         {showGuide.value ? <Studentguide></Studentguide> : null}
       </div>
     );

+ 6 - 15
src/views/studentList/modals/studentTraomomhDetails.tsx

@@ -1,13 +1,4 @@
-import {
-  NButton,
-  NSpace,
-  useMessage,
-  NForm,
-  NFormItem,
-  NSelect,
-  NImage,
-  NScrollbar
-} from 'naive-ui';
+import { useMessage, NImage, NScrollbar } from 'naive-ui';
 import { defineComponent, onMounted, reactive, ref } from 'vue';
 import styles from '@/views/classList/index.module.less';
 import TrainType from '@/views/attend-class/model/train-type';
@@ -37,9 +28,9 @@ export default defineComponent({
   emits: ['close'],
 
   setup(props, { emit, expose }) {
-    const data = reactive({
-      uploading: false
-    });
+    // const data = reactive({
+    //   uploading: false
+    // });
     const teacherInfo = ref({
       teacherName: '',
       createTime: '',
@@ -47,8 +38,8 @@ export default defineComponent({
       teacherAvatar: '',
       studentLessonTrainingDetails: [] as any
     } as any);
-    const message = useMessage();
-    const foemsRef = ref();
+    // const message = useMessage();
+    // const foemsRef = ref();
     const typeFormat = (trainingType: string, configJson: any) => {
       let tList: string[] = [];
 

+ 16 - 0
src/views/studentList/modals/update-student/index.module.less

@@ -0,0 +1,16 @@
+.updateStudentContainer {
+  padding: 24px 30px;
+
+
+  .updateBtnGroup {
+    padding: 0;
+    justify-content: center !important;
+
+    :global {
+      .n-button {
+        height: 48px !important;
+        min-width: 156px;
+      }
+    }
+  }
+}

+ 204 - 0
src/views/studentList/modals/update-student/index.tsx

@@ -0,0 +1,204 @@
+import {
+  NButton,
+  NForm,
+  NFormItem,
+  NInput,
+  NSelect,
+  NSpace,
+  NSpin,
+  useMessage
+} from 'naive-ui';
+import { defineComponent, onMounted, reactive, ref } from 'vue';
+import styles from './index.module.less';
+import { gradeToCN } from '/src/utils/contants';
+import { classGroupList } from '/src/views/home/api';
+import { resetStudentInfo } from '../../api';
+import { classArray, getgradeNumList } from '/src/views/classList/contants';
+
+export default defineComponent({
+  name: 'update-student',
+  props: {
+    row: {
+      type: Object,
+      default: () => ({})
+    }
+  },
+  emits: ['close', 'confirm'],
+  setup(props, { emit }) {
+    const message = useMessage();
+    const state = reactive({
+      uploading: false,
+      gradeList: [] as any,
+      classList: [] as any
+    });
+    const forms = reactive({
+      phone: null,
+      nickname: null,
+      gender: null,
+      currentGradeNum: null,
+      //  expireDate: dayjs().add(7, 'day').format('YYYY-MM-DD') as any, // 默认7天
+      currentClass: null as any
+    });
+
+    const gradeNumList = ref(getgradeNumList());
+    const classArrayRef = ref([...classArray] as any);
+    const formsRef = ref();
+
+    const onSubmit = async () => {
+      formsRef.value?.validate(async (err: any) => {
+        if (err) {
+          return;
+        }
+        state.uploading = true;
+        try {
+          await resetStudentInfo({
+            ...forms,
+            id: props.row.id,
+            schoolId: props.row.schoolId,
+            tenantId: props.row.tenantId
+          });
+          message.success('修改成功');
+          emit('confirm');
+          emit('close');
+        } catch {
+          //
+        }
+        state.uploading = false;
+      });
+    };
+
+    onMounted(async () => {
+      const row = props.row;
+      if (row.id) {
+        forms.nickname = row.nickname;
+        forms.phone = row.phone;
+        forms.gender = row.gender;
+        forms.currentGradeNum = row.currentGradeNum;
+        forms.currentClass = row.currentClass;
+      }
+    });
+
+    const onlyAllowNumber = (value: string) => !value || /^\d+$/.test(value);
+    return () => (
+      <div class={styles.updateStudentContainer}>
+        <NForm
+          ref={formsRef}
+          model={forms}
+          labelAlign="right"
+          labelWidth={'auto'}
+          labelPlacement="left">
+          <NFormItem
+            label="姓名"
+            path="nickname"
+            rule={{
+              required: true,
+              message: '请填写学生姓名',
+              trigger: 'blur'
+            }}>
+            <NInput
+              maxlength={15}
+              placeholder="请填写学生姓名"
+              v-model:value={forms.nickname}></NInput>
+          </NFormItem>
+          <NFormItem
+            label="手机号"
+            path="phone"
+            rule={[
+              {
+                required: true,
+                message: '请填写学生手机号',
+                trigger: 'blur'
+              },
+              {
+                pattern: /^1[3456789]\d{9}$/,
+                message: '手机号格式不正确',
+                trigger: 'blur'
+              }
+            ]}>
+            <NInput
+              maxlength={11}
+              allowInput={onlyAllowNumber}
+              placeholder="请填写学生手机号"
+              v-model:value={forms.phone}></NInput>
+          </NFormItem>
+          <NFormItem
+            label="性别"
+            path="gender"
+            rule={[
+              {
+                required: true,
+                message: '请选择性别',
+                trigger: 'blur',
+                type: 'number'
+              }
+            ]}>
+            <NSelect
+              class={styles.select}
+              clearable
+              filterable
+              placeholder="请选择性别"
+              options={[
+                { label: '男', value: 1 },
+                { label: '女', value: 0 }
+              ]}
+              v-model:value={forms.gender}
+            />
+          </NFormItem>
+          <NFormItem
+            label="年级"
+            path="currentGradeNum"
+            rule={[
+              {
+                required: true,
+                message: '请选择年级',
+                trigger: 'change',
+                type: 'number'
+              }
+            ]}>
+            <NSelect
+              v-model:value={forms.currentGradeNum}
+              placeholder="请选择年级"
+              options={gradeNumList.value as any}
+              clearable
+              filterable
+            />
+          </NFormItem>
+          <NFormItem
+            label="班级"
+            path="currentClass"
+            rule={[
+              {
+                required: true,
+                message: '请选择班级',
+                trigger: 'change',
+                type: 'number'
+              }
+            ]}>
+            <NSelect
+              filterable
+              v-model:value={forms.currentClass}
+              placeholder="请选择班级"
+              clearable
+              options={classArrayRef.value as any}
+            />
+          </NFormItem>
+
+          <NSpace class={styles.updateBtnGroup}>
+            <NButton strong type="default" round onClick={() => emit('close')}>
+              取消
+            </NButton>
+            <NButton
+              strong
+              type="primary"
+              round
+              disabled={state.uploading}
+              loading={state.uploading}
+              onClick={onSubmit}>
+              确认
+            </NButton>
+          </NSpace>
+        </NForm>
+      </div>
+    );
+  }
+});