Sfoglia il codice sorgente

完成预览中文件部分

dusenyao 2 anni fa
parent
commit
dd19ec9b99
42 ha cambiato i file con 894 aggiunte e 232 eliminazioni
  1. 123 95
      package-lock.json
  2. 4 4
      package.json
  3. 13 0
      src/api/app.js
  4. BIN
      src/assets/file/execl.png
  5. BIN
      src/assets/file/pdf.png
  6. BIN
      src/assets/file/ppt.png
  7. BIN
      src/assets/file/txt.png
  8. BIN
      src/assets/file/word.png
  9. 56 52
      src/common/show_file/index.vue
  10. 68 0
      src/components/course/CoursewareView.vue
  11. 74 0
      src/components/course/courseware.js
  12. 1 1
      src/icons/svg/class-grey.svg
  13. 1 1
      src/icons/svg/class.svg
  14. 5 0
      src/icons/svg/course/course-download.svg
  15. 4 0
      src/icons/svg/course/course-preview.svg
  16. 1 1
      src/icons/svg/delete-current.svg
  17. 1 1
      src/icons/svg/microphone.svg
  18. 1 4
      src/icons/svg/play.svg
  19. 1 1
      src/icons/svg/preview.svg
  20. 1 4
      src/icons/svg/record-stop.svg
  21. 1 4
      src/icons/svg/recording.svg
  22. 24 0
      src/utils/common.js
  23. 1 0
      src/utils/filter.js
  24. 1 2
      src/utils/request.js
  25. 19 0
      src/utils/switch.js
  26. 4 7
      src/views/task_details/student/index.vue
  27. 4 7
      src/views/task_details/teacher/index.vue
  28. 0 3
      src/views/teacher/create_course/step_table/SelectBook.vue
  29. 7 8
      src/views/teacher/create_course/step_table/create_task/components/TaskExplain/ExpandContract.vue
  30. 1 1
      src/views/teacher/create_course/step_table/create_task/components/TaskExplain/index.vue
  31. 8 2
      src/views/teacher/create_course/step_table/create_task/components/common/file.js
  32. 1 1
      src/views/teacher/create_course/step_table/create_task/components/data/TaskClassify.js
  33. 6 6
      src/views/teacher/create_course/step_table/create_task/components/data/TaskData.js
  34. 1 1
      src/views/teacher/create_course/step_table/create_task/components/preview/index.vue
  35. 150 0
      src/views/teacher/create_course/step_table/create_task/components/preview/task_preview/SubtaskItem.vue
  36. 245 0
      src/views/teacher/create_course/step_table/create_task/components/preview/task_preview/components/PreviewFile.vue
  37. 7 0
      src/views/teacher/create_course/step_table/create_task/components/preview/task_preview/components/PreviewMessage.vue
  38. 35 12
      src/views/teacher/create_course/step_table/create_task/components/preview/task_preview/index.vue
  39. 2 2
      src/views/teacher/create_course/step_table/create_task/components/task_template/components/TemplateFile.vue
  40. 4 0
      src/views/teacher/create_course/step_table/create_task/components/task_template/components/courseware.js
  41. 7 9
      src/views/teacher/create_course/step_table/create_task/components/task_template/subtask/SubTask.vue
  42. 12 3
      src/views/teacher/create_course/step_table/create_task/index.js

+ 123 - 95
package-lock.json

@@ -41,21 +41,21 @@
       "dev": true
     },
     "@babel/core": {
-      "version": "7.19.1",
-      "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/core/-/core-7.19.1.tgz",
-      "integrity": "sha512-1H8VgqXme4UXCRv7/Wa1bq7RVymKOzC7znjyFM8KiEzwFqcKUKYNoQef4GhdklgNvoBXyW4gYhuBNCM5o1zImw==",
+      "version": "7.19.3",
+      "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/core/-/core-7.19.3.tgz",
+      "integrity": "sha512-WneDJxdsjEvyKtXKsaBGbDeiyOjR5vYq4HcShxnIbG0qixpoHjI3MqeZM9NDvsojNCEBItQE4juOo/bU6e72gQ==",
       "dev": true,
       "requires": {
         "@ampproject/remapping": "^2.1.0",
         "@babel/code-frame": "^7.18.6",
-        "@babel/generator": "^7.19.0",
-        "@babel/helper-compilation-targets": "^7.19.1",
+        "@babel/generator": "^7.19.3",
+        "@babel/helper-compilation-targets": "^7.19.3",
         "@babel/helper-module-transforms": "^7.19.0",
         "@babel/helpers": "^7.19.0",
-        "@babel/parser": "^7.19.1",
+        "@babel/parser": "^7.19.3",
         "@babel/template": "^7.18.10",
-        "@babel/traverse": "^7.19.1",
-        "@babel/types": "^7.19.0",
+        "@babel/traverse": "^7.19.3",
+        "@babel/types": "^7.19.3",
         "convert-source-map": "^1.7.0",
         "debug": "^4.1.0",
         "gensync": "^1.0.0-beta.2",
@@ -64,29 +64,29 @@
       },
       "dependencies": {
         "@babel/compat-data": {
-          "version": "7.19.1",
-          "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/compat-data/-/compat-data-7.19.1.tgz",
-          "integrity": "sha512-72a9ghR0gnESIa7jBN53U32FOVCEoztyIlKaNoU05zRhEecduGK9L9c3ww7Mp06JiR+0ls0GBPFJQwwtjn9ksg==",
+          "version": "7.19.3",
+          "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/compat-data/-/compat-data-7.19.3.tgz",
+          "integrity": "sha512-prBHMK4JYYK+wDjJF1q99KK4JLL+egWS4nmNqdlMUgCExMZ+iZW0hGhyC3VEbsPjvaN0TBhW//VIFwBrk8sEiw==",
           "dev": true
         },
         "@babel/generator": {
-          "version": "7.19.0",
-          "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/generator/-/generator-7.19.0.tgz",
-          "integrity": "sha512-S1ahxf1gZ2dpoiFgA+ohK9DIpz50bJ0CWs7Zlzb54Z4sG8qmdIrGrVqmy1sAtTVRb+9CU6U8VqT9L0Zj7hxHVg==",
+          "version": "7.19.3",
+          "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/generator/-/generator-7.19.3.tgz",
+          "integrity": "sha512-fqVZnmp1ncvZU757UzDheKZpfPgatqY59XtW2/j/18H7u76akb8xqvjw82f+i2UKd/ksYsSick/BCLQUUtJ/qQ==",
           "dev": true,
           "requires": {
-            "@babel/types": "^7.19.0",
+            "@babel/types": "^7.19.3",
             "@jridgewell/gen-mapping": "^0.3.2",
             "jsesc": "^2.5.1"
           }
         },
         "@babel/helper-compilation-targets": {
-          "version": "7.19.1",
-          "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/helper-compilation-targets/-/helper-compilation-targets-7.19.1.tgz",
-          "integrity": "sha512-LlLkkqhCMyz2lkQPvJNdIYU7O5YjWRgC2R4omjCTpZd8u8KMQzZvX4qce+/BluN1rcQiV7BoGUpmQ0LeHerbhg==",
+          "version": "7.19.3",
+          "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/helper-compilation-targets/-/helper-compilation-targets-7.19.3.tgz",
+          "integrity": "sha512-65ESqLGyGmLvgR0mst5AdW1FkNlj9rQsCKduzEoEPhBCDFGXvz2jW6bXFG6i0/MrV2s7hhXjjb2yAzcPuQlLwg==",
           "dev": true,
           "requires": {
-            "@babel/compat-data": "^7.19.1",
+            "@babel/compat-data": "^7.19.3",
             "@babel/helper-validator-option": "^7.18.6",
             "browserslist": "^4.21.3",
             "semver": "^6.3.0"
@@ -118,10 +118,16 @@
             "@babel/types": "^7.19.0"
           }
         },
-        "@babel/parser": {
+        "@babel/helper-validator-identifier": {
           "version": "7.19.1",
-          "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/parser/-/parser-7.19.1.tgz",
-          "integrity": "sha512-h7RCSorm1DdTVGJf3P2Mhj3kdnkmF/EiysUkzS2TdgAYqyjFdMQJbVuXOBej2SBJaXan/lIVtT6KkGbyyq753A==",
+          "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz",
+          "integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==",
+          "dev": true
+        },
+        "@babel/parser": {
+          "version": "7.19.3",
+          "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/parser/-/parser-7.19.3.tgz",
+          "integrity": "sha512-pJ9xOlNWHiy9+FuFP09DEAFbAn4JskgRsVcc169w2xRBC3FRGuQEwjeIMMND9L2zc0iEhO/tGv4Zq+km+hxNpQ==",
           "dev": true
         },
         "@babel/template": {
@@ -136,31 +142,31 @@
           }
         },
         "@babel/traverse": {
-          "version": "7.19.1",
-          "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/traverse/-/traverse-7.19.1.tgz",
-          "integrity": "sha512-0j/ZfZMxKukDaag2PtOPDbwuELqIar6lLskVPPJDjXMXjfLb1Obo/1yjxIGqqAJrmfaTIY3z2wFLAQ7qSkLsuA==",
+          "version": "7.19.3",
+          "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/traverse/-/traverse-7.19.3.tgz",
+          "integrity": "sha512-qh5yf6149zhq2sgIXmwjnsvmnNQC2iw70UFjp4olxucKrWd/dvlUsBI88VSLUsnMNF7/vnOiA+nk1+yLoCqROQ==",
           "dev": true,
           "requires": {
             "@babel/code-frame": "^7.18.6",
-            "@babel/generator": "^7.19.0",
+            "@babel/generator": "^7.19.3",
             "@babel/helper-environment-visitor": "^7.18.9",
             "@babel/helper-function-name": "^7.19.0",
             "@babel/helper-hoist-variables": "^7.18.6",
             "@babel/helper-split-export-declaration": "^7.18.6",
-            "@babel/parser": "^7.19.1",
-            "@babel/types": "^7.19.0",
+            "@babel/parser": "^7.19.3",
+            "@babel/types": "^7.19.3",
             "debug": "^4.1.0",
             "globals": "^11.1.0"
           }
         },
         "@babel/types": {
-          "version": "7.19.0",
-          "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/types/-/types-7.19.0.tgz",
-          "integrity": "sha512-YuGopBq3ke25BVSiS6fgF49Ul9gH1x70Bcr6bqRLjWCkcX8Hre1/5+z+IiWOIerRMSSEfGZVB9z9kyq7wVs9YA==",
+          "version": "7.19.3",
+          "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/types/-/types-7.19.3.tgz",
+          "integrity": "sha512-hGCaQzIY22DJlDh9CH7NOxgKkFjBk0Cw9xDO1Xmh2151ti7wiGfQ3LauXzL4HP1fmFlTX6XjpRETTpUcv7wQLw==",
           "dev": true,
           "requires": {
             "@babel/helper-string-parser": "^7.18.10",
-            "@babel/helper-validator-identifier": "^7.18.6",
+            "@babel/helper-validator-identifier": "^7.19.1",
             "to-fast-properties": "^2.0.0"
           }
         },
@@ -176,22 +182,38 @@
           }
         },
         "browserslist": {
-          "version": "4.21.3",
-          "resolved": "https://repo.huaweicloud.com/repository/npm/browserslist/-/browserslist-4.21.3.tgz",
-          "integrity": "sha512-898rgRXLAyRkM1GryrrBHGkqA5hlpkV5MhtZwg9QXeiyLUYs2k00Un05aX5l2/yJIOObYKOpS2JNo8nJDE7fWQ==",
+          "version": "4.21.4",
+          "resolved": "https://repo.huaweicloud.com/repository/npm/browserslist/-/browserslist-4.21.4.tgz",
+          "integrity": "sha512-CBHJJdDmgjl3daYjN5Cp5kbTf1mUhZoS+beLklHIvkOWscs83YAhLlF3Wsh/lciQYAcbBJgTOD44VtG31ZM4Hw==",
           "dev": true,
           "requires": {
-            "caniuse-lite": "^1.0.30001370",
-            "electron-to-chromium": "^1.4.202",
+            "caniuse-lite": "^1.0.30001400",
+            "electron-to-chromium": "^1.4.251",
             "node-releases": "^2.0.6",
-            "update-browserslist-db": "^1.0.5"
+            "update-browserslist-db": "^1.0.9"
           }
         },
+        "caniuse-lite": {
+          "version": "1.0.30001410",
+          "resolved": "https://repo.huaweicloud.com/repository/npm/caniuse-lite/-/caniuse-lite-1.0.30001410.tgz",
+          "integrity": "sha512-QoblBnuE+rG0lc3Ur9ltP5q47lbguipa/ncNMyyGuqPk44FxbScWAeEO+k5fSQ8WekdAK4mWqNs1rADDAiN5xQ==",
+          "dev": true
+        },
         "electron-to-chromium": {
-          "version": "1.4.240",
-          "resolved": "https://repo.huaweicloud.com/repository/npm/electron-to-chromium/-/electron-to-chromium-1.4.240.tgz",
-          "integrity": "sha512-r20dUOtZ4vUPTqAajDGonIM1uas5tf85Up+wPdtNBNvBSqGCfkpvMVvQ1T8YJzPV9/Y9g3FbUDcXb94Rafycow==",
+          "version": "1.4.260",
+          "resolved": "https://repo.huaweicloud.com/repository/npm/electron-to-chromium/-/electron-to-chromium-1.4.260.tgz",
+          "integrity": "sha512-1GxPM2Bdz1AjuNjho9/TqJfxM7KZ7R8s4vA5cbbIoVacQXfvZlV+d7Y1lu4BhGzEBfjjhakr3NXKqN0PxPXIsg==",
           "dev": true
+        },
+        "update-browserslist-db": {
+          "version": "1.0.9",
+          "resolved": "https://repo.huaweicloud.com/repository/npm/update-browserslist-db/-/update-browserslist-db-1.0.9.tgz",
+          "integrity": "sha512-/xsqn21EGVdXI3EXSum1Yckj3ZVZugqyOZQ/CxYPBD/R+ko9NSUScf8tFF4dOKY+2pvSSJA/S+5B8s4Zr4kyvg==",
+          "dev": true,
+          "requires": {
+            "escalade": "^3.1.1",
+            "picocolors": "^1.0.0"
+          }
         }
       }
     },
@@ -583,12 +605,12 @@
       },
       "dependencies": {
         "@babel/generator": {
-          "version": "7.19.0",
-          "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/generator/-/generator-7.19.0.tgz",
-          "integrity": "sha512-S1ahxf1gZ2dpoiFgA+ohK9DIpz50bJ0CWs7Zlzb54Z4sG8qmdIrGrVqmy1sAtTVRb+9CU6U8VqT9L0Zj7hxHVg==",
+          "version": "7.19.3",
+          "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/generator/-/generator-7.19.3.tgz",
+          "integrity": "sha512-fqVZnmp1ncvZU757UzDheKZpfPgatqY59XtW2/j/18H7u76akb8xqvjw82f+i2UKd/ksYsSick/BCLQUUtJ/qQ==",
           "dev": true,
           "requires": {
-            "@babel/types": "^7.19.0",
+            "@babel/types": "^7.19.3",
             "@jridgewell/gen-mapping": "^0.3.2",
             "jsesc": "^2.5.1"
           }
@@ -603,10 +625,16 @@
             "@babel/types": "^7.19.0"
           }
         },
+        "@babel/helper-validator-identifier": {
+          "version": "7.19.1",
+          "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/helper-validator-identifier/-/helper-validator-identifier-7.19.1.tgz",
+          "integrity": "sha512-awrNfaMtnHUr653GgGEs++LlAvW6w+DcPrOliSMXWCKo597CwL5Acf/wWdNkf/tfEQE3mjkeD1YOVZOUV/od1w==",
+          "dev": true
+        },
         "@babel/parser": {
-          "version": "7.19.0",
-          "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/parser/-/parser-7.19.0.tgz",
-          "integrity": "sha512-74bEXKX2h+8rrfQUfsBfuZZHzsEs6Eql4pqy/T4Nn6Y9wNPggQOqD6z6pn5Bl8ZfysKouFZT/UXEH94ummEeQw==",
+          "version": "7.19.3",
+          "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/parser/-/parser-7.19.3.tgz",
+          "integrity": "sha512-pJ9xOlNWHiy9+FuFP09DEAFbAn4JskgRsVcc169w2xRBC3FRGuQEwjeIMMND9L2zc0iEhO/tGv4Zq+km+hxNpQ==",
           "dev": true
         },
         "@babel/template": {
@@ -621,31 +649,31 @@
           }
         },
         "@babel/traverse": {
-          "version": "7.19.0",
-          "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/traverse/-/traverse-7.19.0.tgz",
-          "integrity": "sha512-4pKpFRDh+utd2mbRC8JLnlsMUii3PMHjpL6a0SZ4NMZy7YFP9aXORxEhdMVOc9CpWtDF09IkciQLEhK7Ml7gRA==",
+          "version": "7.19.3",
+          "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/traverse/-/traverse-7.19.3.tgz",
+          "integrity": "sha512-qh5yf6149zhq2sgIXmwjnsvmnNQC2iw70UFjp4olxucKrWd/dvlUsBI88VSLUsnMNF7/vnOiA+nk1+yLoCqROQ==",
           "dev": true,
           "requires": {
             "@babel/code-frame": "^7.18.6",
-            "@babel/generator": "^7.19.0",
+            "@babel/generator": "^7.19.3",
             "@babel/helper-environment-visitor": "^7.18.9",
             "@babel/helper-function-name": "^7.19.0",
             "@babel/helper-hoist-variables": "^7.18.6",
             "@babel/helper-split-export-declaration": "^7.18.6",
-            "@babel/parser": "^7.19.0",
-            "@babel/types": "^7.19.0",
+            "@babel/parser": "^7.19.3",
+            "@babel/types": "^7.19.3",
             "debug": "^4.1.0",
             "globals": "^11.1.0"
           }
         },
         "@babel/types": {
-          "version": "7.19.0",
-          "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/types/-/types-7.19.0.tgz",
-          "integrity": "sha512-YuGopBq3ke25BVSiS6fgF49Ul9gH1x70Bcr6bqRLjWCkcX8Hre1/5+z+IiWOIerRMSSEfGZVB9z9kyq7wVs9YA==",
+          "version": "7.19.3",
+          "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/types/-/types-7.19.3.tgz",
+          "integrity": "sha512-hGCaQzIY22DJlDh9CH7NOxgKkFjBk0Cw9xDO1Xmh2151ti7wiGfQ3LauXzL4HP1fmFlTX6XjpRETTpUcv7wQLw==",
           "dev": true,
           "requires": {
             "@babel/helper-string-parser": "^7.18.10",
-            "@babel/helper-validator-identifier": "^7.18.6",
+            "@babel/helper-validator-identifier": "^7.19.1",
             "to-fast-properties": "^2.0.0"
           }
         },
@@ -3920,7 +3948,7 @@
     },
     "arrify": {
       "version": "1.0.1",
-      "resolved": "https://repo.huaweicloud.com/repository/npm/arrify/-/arrify-1.0.1.tgz",
+      "resolved": "https://registry.npmmirror.com/arrify/-/arrify-1.0.1.tgz",
       "integrity": "sha512-3CYzex9M9FGQjCGMGyi6/31c8GJbgb0qGyrx5HWxPd0aCwh4cB2YjMb2Xf9UuoogrMrlO9cTqnB5rI5GHZTcUA==",
       "dev": true
     },
@@ -5225,9 +5253,9 @@
       }
     },
     "core-js": {
-      "version": "3.25.2",
-      "resolved": "https://repo.huaweicloud.com/repository/npm/core-js/-/core-js-3.25.2.tgz",
-      "integrity": "sha512-YB4IAT1bjEfxTJ1XYy11hJAKskO+qmhuDBM8/guIfMz4JvdsAQAqvyb97zXX7JgSrfPLG5mRGFWJwJD39ruq2A=="
+      "version": "3.25.3",
+      "resolved": "https://repo.huaweicloud.com/repository/npm/core-js/-/core-js-3.25.3.tgz",
+      "integrity": "sha512-y1hvKXmPHvm5B7w4ln1S4uc9eV/O5+iFExSRUimnvIph11uaizFR8LFMdONN8hG3P2pipUfX4Y/fR8rAEtcHcQ=="
     },
     "core-js-compat": {
       "version": "3.24.0",
@@ -5609,7 +5637,7 @@
     },
     "decamelize-keys": {
       "version": "1.1.0",
-      "resolved": "https://repo.huaweicloud.com/repository/npm/decamelize-keys/-/decamelize-keys-1.1.0.tgz",
+      "resolved": "https://registry.npmmirror.com/decamelize-keys/-/decamelize-keys-1.1.0.tgz",
       "integrity": "sha512-ocLWuYzRPoS9bfiSdDd3cxvrzovVMZnRDVEzAs+hWIVXGDbHxWMECij2OBuyB/An0FFW/nLuq6Kv1i/YC5Qfzg==",
       "dev": true,
       "requires": {
@@ -7475,7 +7503,7 @@
     },
     "global-modules": {
       "version": "2.0.0",
-      "resolved": "https://repo.huaweicloud.com/repository/npm/global-modules/-/global-modules-2.0.0.tgz",
+      "resolved": "https://registry.npmmirror.com/global-modules/-/global-modules-2.0.0.tgz",
       "integrity": "sha512-NGbfmJBp9x8IxyJSd1P+otYK8vonoJactOogrVfFRIAEY1ukil8RSKDz2Yo7wh1oihl51l/r6W4epkeKJHqL8A==",
       "dev": true,
       "requires": {
@@ -7484,7 +7512,7 @@
     },
     "global-prefix": {
       "version": "3.0.0",
-      "resolved": "https://repo.huaweicloud.com/repository/npm/global-prefix/-/global-prefix-3.0.0.tgz",
+      "resolved": "https://registry.npmmirror.com/global-prefix/-/global-prefix-3.0.0.tgz",
       "integrity": "sha512-awConJSVCHVGND6x3tmMaKcQvwXLhjdkmomy2W+Goaui8YPgYgXJZewhg3fWC+DlfqqQuWg8AwqjGTD2nAPVWg==",
       "dev": true,
       "requires": {
@@ -7495,13 +7523,13 @@
       "dependencies": {
         "kind-of": {
           "version": "6.0.3",
-          "resolved": "https://repo.huaweicloud.com/repository/npm/kind-of/-/kind-of-6.0.3.tgz",
+          "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz",
           "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==",
           "dev": true
         },
         "which": {
           "version": "1.3.1",
-          "resolved": "https://repo.huaweicloud.com/repository/npm/which/-/which-1.3.1.tgz",
+          "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz",
           "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==",
           "dev": true,
           "requires": {
@@ -7540,7 +7568,7 @@
     },
     "globjoin": {
       "version": "0.1.4",
-      "resolved": "https://repo.huaweicloud.com/repository/npm/globjoin/-/globjoin-0.1.4.tgz",
+      "resolved": "https://registry.npmmirror.com/globjoin/-/globjoin-0.1.4.tgz",
       "integrity": "sha512-xYfnw62CKG8nLkZBfWbhWwDw02CHty86jfPcc2cr3ZfeuK9ysoVPPEUxf21bAD/rWAgk52SuBrLJlefNy8mvFg==",
       "dev": true
     },
@@ -7618,7 +7646,7 @@
     },
     "hard-rejection": {
       "version": "2.1.0",
-      "resolved": "https://repo.huaweicloud.com/repository/npm/hard-rejection/-/hard-rejection-2.1.0.tgz",
+      "resolved": "https://registry.npmmirror.com/hard-rejection/-/hard-rejection-2.1.0.tgz",
       "integrity": "sha512-VIZB+ibDhx7ObhAe7OVtoEbuP4h/MuOTHJ+J8h/eBXotJYl0fBgR72xDFCKgIh22OJZIOVNxBMWuhAr10r8HdA==",
       "dev": true
     },
@@ -8018,7 +8046,7 @@
     },
     "import-lazy": {
       "version": "4.0.0",
-      "resolved": "https://repo.huaweicloud.com/repository/npm/import-lazy/-/import-lazy-4.0.0.tgz",
+      "resolved": "https://registry.npmmirror.com/import-lazy/-/import-lazy-4.0.0.tgz",
       "integrity": "sha512-rKtvo6a868b5Hu3heneU+L4yEQ4jYKLtjpnPeUdK7h0yzXGmyBTypknlkCvHFBqfX9YlorEiMM6Dnq/5atfHkw==",
       "dev": true
     },
@@ -8260,7 +8288,7 @@
     },
     "is-plain-object": {
       "version": "5.0.0",
-      "resolved": "https://repo.huaweicloud.com/repository/npm/is-plain-object/-/is-plain-object-5.0.0.tgz",
+      "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz",
       "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==",
       "dev": true
     },
@@ -10701,7 +10729,7 @@
     },
     "mathml-tag-names": {
       "version": "2.1.3",
-      "resolved": "https://repo.huaweicloud.com/repository/npm/mathml-tag-names/-/mathml-tag-names-2.1.3.tgz",
+      "resolved": "https://registry.npmmirror.com/mathml-tag-names/-/mathml-tag-names-2.1.3.tgz",
       "integrity": "sha512-APMBEanjybaPzUrfqU0IMU5I0AswKMH7k8OTLs0vvV4KZpExkTkY87nR/zpbuTPj+gARop7aGUbl11pnDfW6xg==",
       "dev": true
     },
@@ -10893,7 +10921,7 @@
     },
     "min-indent": {
       "version": "1.0.1",
-      "resolved": "https://repo.huaweicloud.com/repository/npm/min-indent/-/min-indent-1.0.1.tgz",
+      "resolved": "https://registry.npmmirror.com/min-indent/-/min-indent-1.0.1.tgz",
       "integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==",
       "dev": true
     },
@@ -10968,7 +10996,7 @@
     },
     "minimist-options": {
       "version": "4.1.0",
-      "resolved": "https://repo.huaweicloud.com/repository/npm/minimist-options/-/minimist-options-4.1.0.tgz",
+      "resolved": "https://registry.npmmirror.com/minimist-options/-/minimist-options-4.1.0.tgz",
       "integrity": "sha512-Q4r8ghd80yhO/0j1O3B2BjweX3fiHg9cdOwjJd2J76Q135c+NDxGCqdYKQ1SKBuFfgWbAUzBfvYjPUEeNgqN1A==",
       "dev": true,
       "requires": {
@@ -10979,7 +11007,7 @@
       "dependencies": {
         "kind-of": {
           "version": "6.0.3",
-          "resolved": "https://repo.huaweicloud.com/repository/npm/kind-of/-/kind-of-6.0.3.tgz",
+          "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz",
           "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==",
           "dev": true
         }
@@ -12070,9 +12098,9 @@
       "dev": true
     },
     "postcss": {
-      "version": "8.4.16",
-      "resolved": "https://repo.huaweicloud.com/repository/npm/postcss/-/postcss-8.4.16.tgz",
-      "integrity": "sha512-ipHE1XBvKzm5xI7hiHCZJCSugxvsdq2mPnsq5+UF+VHCjiBvtDrlxJfMBToWaP9D5XlgNmcFGqoHmUn0EYEaRQ==",
+      "version": "8.4.17",
+      "resolved": "https://repo.huaweicloud.com/repository/npm/postcss/-/postcss-8.4.17.tgz",
+      "integrity": "sha512-UNxNOLQydcOFi41yHNMcKRZ39NeXlr8AxGuZJsdub8vIb12fHzcq37DTU/QtbI6WLxNg2gF9Z+8qtRwTj1UI1Q==",
       "requires": {
         "nanoid": "^3.3.4",
         "picocolors": "^1.0.0",
@@ -12759,7 +12787,7 @@
     },
     "quick-lru": {
       "version": "4.0.1",
-      "resolved": "https://repo.huaweicloud.com/repository/npm/quick-lru/-/quick-lru-4.0.1.tgz",
+      "resolved": "https://registry.npmmirror.com/quick-lru/-/quick-lru-4.0.1.tgz",
       "integrity": "sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g==",
       "dev": true
     },
@@ -14350,7 +14378,7 @@
     },
     "style-search": {
       "version": "0.1.0",
-      "resolved": "https://repo.huaweicloud.com/repository/npm/style-search/-/style-search-0.1.0.tgz",
+      "resolved": "https://registry.npmmirror.com/style-search/-/style-search-0.1.0.tgz",
       "integrity": "sha512-Dj1Okke1C3uKKwQcetra4jSuk0DqbzbYtXipzFlFMZtowbF1x7BKJwB9AayVMyFARvU8EDrZdcax4At/452cAg==",
       "dev": true
     },
@@ -14365,9 +14393,9 @@
       }
     },
     "stylelint": {
-      "version": "14.12.1",
-      "resolved": "https://repo.huaweicloud.com/repository/npm/stylelint/-/stylelint-14.12.1.tgz",
-      "integrity": "sha512-ZEM4TuksChMBfuPadQsHUkbOoRySAT9QMfDvvYxdAchOJl0p+csTMBXOu6ORAAxKhwBmxqJiep8V88bXfNs3EQ==",
+      "version": "14.13.0",
+      "resolved": "https://repo.huaweicloud.com/repository/npm/stylelint/-/stylelint-14.13.0.tgz",
+      "integrity": "sha512-NJSAdloiAB/jgVJKxMR90mWlctvmeBFGFVUvyKngi9+j/qPSJ5ZB+u8jOmGbLTnS7OHrII9NFGehPRyar8U5vg==",
       "dev": true,
       "requires": {
         "@csstools/selector-specificity": "^2.0.2",
@@ -14412,19 +14440,19 @@
       "dependencies": {
         "balanced-match": {
           "version": "2.0.0",
-          "resolved": "https://repo.huaweicloud.com/repository/npm/balanced-match/-/balanced-match-2.0.0.tgz",
+          "resolved": "https://registry.npmmirror.com/balanced-match/-/balanced-match-2.0.0.tgz",
           "integrity": "sha512-1ugUSr8BHXRnK23KfuYS+gVMC3LB8QGH9W1iGtDPsNWoQbgtXSExkBu2aDR4epiGWZOjZsj6lDl/N/AqqTC3UA==",
           "dev": true
         },
         "camelcase": {
           "version": "5.3.1",
-          "resolved": "https://repo.huaweicloud.com/repository/npm/camelcase/-/camelcase-5.3.1.tgz",
+          "resolved": "https://registry.npmmirror.com/camelcase/-/camelcase-5.3.1.tgz",
           "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==",
           "dev": true
         },
         "camelcase-keys": {
           "version": "6.2.2",
-          "resolved": "https://repo.huaweicloud.com/repository/npm/camelcase-keys/-/camelcase-keys-6.2.2.tgz",
+          "resolved": "https://registry.npmmirror.com/camelcase-keys/-/camelcase-keys-6.2.2.tgz",
           "integrity": "sha512-YrwaA0vEKazPBkn0ipTiMpSajYDSe+KjQfrjhcBMxJt/znbvlHd8Pw/Vamaz5EB4Wfhs3SUR3Z9mwRu/P3s3Yg==",
           "dev": true,
           "requires": {
@@ -14469,7 +14497,7 @@
         },
         "hosted-git-info": {
           "version": "4.1.0",
-          "resolved": "https://repo.huaweicloud.com/repository/npm/hosted-git-info/-/hosted-git-info-4.1.0.tgz",
+          "resolved": "https://registry.npmmirror.com/hosted-git-info/-/hosted-git-info-4.1.0.tgz",
           "integrity": "sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA==",
           "dev": true,
           "requires": {
@@ -14478,19 +14506,19 @@
         },
         "ignore": {
           "version": "5.2.0",
-          "resolved": "https://repo.huaweicloud.com/repository/npm/ignore/-/ignore-5.2.0.tgz",
+          "resolved": "https://registry.npmmirror.com/ignore/-/ignore-5.2.0.tgz",
           "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==",
           "dev": true
         },
         "map-obj": {
           "version": "4.3.0",
-          "resolved": "https://repo.huaweicloud.com/repository/npm/map-obj/-/map-obj-4.3.0.tgz",
+          "resolved": "https://registry.npmmirror.com/map-obj/-/map-obj-4.3.0.tgz",
           "integrity": "sha512-hdN1wVrZbb29eBGiGjJbeP8JbKjq1urkHJ/LIP/NY48MZ1QVXUsQBV1G1zvYFHn1XE06cwjBsOI2K3Ulnj1YXQ==",
           "dev": true
         },
         "meow": {
           "version": "9.0.0",
-          "resolved": "https://repo.huaweicloud.com/repository/npm/meow/-/meow-9.0.0.tgz",
+          "resolved": "https://registry.npmmirror.com/meow/-/meow-9.0.0.tgz",
           "integrity": "sha512-+obSblOQmRhcyBt62furQqRAQpNyWXo8BuQ5bN7dG8wmwQ+vwHKp/rCFD4CrTP8CsDQD1sjoZ94K417XEUk8IQ==",
           "dev": true,
           "requires": {
@@ -14510,7 +14538,7 @@
         },
         "normalize-package-data": {
           "version": "3.0.3",
-          "resolved": "https://repo.huaweicloud.com/repository/npm/normalize-package-data/-/normalize-package-data-3.0.3.tgz",
+          "resolved": "https://registry.npmmirror.com/normalize-package-data/-/normalize-package-data-3.0.3.tgz",
           "integrity": "sha512-p2W1sgqij3zMMyRC067Dg16bfzVH+w7hyegmpIvZ4JNjqtGOVAIvLmjBx3yP7YTe9vKJgkoNOPjwQGogDoMXFA==",
           "dev": true,
           "requires": {
@@ -14522,7 +14550,7 @@
         },
         "redent": {
           "version": "3.0.0",
-          "resolved": "https://repo.huaweicloud.com/repository/npm/redent/-/redent-3.0.0.tgz",
+          "resolved": "https://registry.npmmirror.com/redent/-/redent-3.0.0.tgz",
           "integrity": "sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==",
           "dev": true,
           "requires": {
@@ -14541,7 +14569,7 @@
         },
         "strip-indent": {
           "version": "3.0.0",
-          "resolved": "https://repo.huaweicloud.com/repository/npm/strip-indent/-/strip-indent-3.0.0.tgz",
+          "resolved": "https://registry.npmmirror.com/strip-indent/-/strip-indent-3.0.0.tgz",
           "integrity": "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==",
           "dev": true,
           "requires": {
@@ -14569,13 +14597,13 @@
         },
         "trim-newlines": {
           "version": "3.0.1",
-          "resolved": "https://repo.huaweicloud.com/repository/npm/trim-newlines/-/trim-newlines-3.0.1.tgz",
+          "resolved": "https://registry.npmmirror.com/trim-newlines/-/trim-newlines-3.0.1.tgz",
           "integrity": "sha512-c1PTsA3tYrIsLGkJkzHF+w9F2EyxfXGo4UyJc4pFL++FMjnq0HJS69T3M7d//gKrFKwy429bouPescbjecU+Zw==",
           "dev": true
         },
         "type-fest": {
           "version": "0.18.1",
-          "resolved": "https://repo.huaweicloud.com/repository/npm/type-fest/-/type-fest-0.18.1.tgz",
+          "resolved": "https://registry.npmmirror.com/type-fest/-/type-fest-0.18.1.tgz",
           "integrity": "sha512-OIAYXk8+ISY+qTOwkHtKqzAuxchoMiD9Udx+FSGQDuiRR+PJKJHc2NJAXlbhkGwTt/4/nKZxELY1w3ReWOL8mw==",
           "dev": true
         },

+ 4 - 4
package.json

@@ -18,7 +18,7 @@
     "awe-dnd": "^0.3.4",
     "axios": "^0.27.2",
     "book-ui": "file:../book-ui-0.3.15.tgz",
-    "core-js": "^3.25.2",
+    "core-js": "^3.25.3",
     "dayjs": "^1.11.5",
     "element-ui": "^2.15.10",
     "gcls-book-question-ui": "file:../gcls-book-question-ui-0.1.0.tgz",
@@ -38,7 +38,7 @@
     "vuex": "^3.6.2"
   },
   "devDependencies": {
-    "@babel/core": "^7.19.1",
+    "@babel/core": "^7.19.3",
     "@babel/eslint-parser": "^7.19.1",
     "@rushstack/eslint-patch": "^1.2.0",
     "@vue/cli-plugin-babel": "~5.0.8",
@@ -57,13 +57,13 @@
     "eslint-plugin-prettier": "^4.2.1",
     "eslint-plugin-vue": "^9.5.1",
     "html-webpack-plugin": "^5.5.0",
-    "postcss": "^8.4.16",
+    "postcss": "^8.4.17",
     "postcss-html": "^1.5.0",
     "prettier": "2.7.1",
     "sass": "^1.55.0",
     "sass-loader": "^10.3.1",
     "script-ext-html-webpack-plugin": "^2.1.5",
-    "stylelint": "^14.12.1",
+    "stylelint": "^14.13.0",
     "stylelint-config-prettier": "^9.0.3",
     "stylelint-config-recess-order": "^3.0.0",
     "stylelint-config-recommended-vue": "^1.4.0",

+ 13 - 0
src/api/app.js

@@ -222,3 +222,16 @@ export function GetVerificationCodeImage(data) {
     data
   });
 }
+
+/**
+ * 得到文件 ID 与文件信息的映射
+ * @param {Object} data
+ */
+export function GetFileInfoMap(data) {
+  return request({
+    method: 'post',
+    url: process.env.VUE_APP_FileServer,
+    params: getRequestParams('file_store_manager-GetFileInfoMap'),
+    data
+  });
+}

BIN
src/assets/file/execl.png


BIN
src/assets/file/pdf.png


BIN
src/assets/file/ppt.png


BIN
src/assets/file/txt.png


BIN
src/assets/file/word.png


+ 56 - 52
src/common/show_file/index.vue

@@ -1,10 +1,10 @@
 <template>
-  <el-dialog class="show-file" :visible="dialogVisibleShowFile" width="1100px" @close="dialogShowFileClose">
+  <el-dialog class="show-file" :visible="visible" width="1100px" :append-to-body="true" @close="close">
     <div slot="title">{{ $t('Key322') }}【{{ fileName }}】</div>
     <div v-loading="loading">
       <iframe
         v-if="fileUrl.length > 0"
-        id="iframe"
+        id="preview-file"
         :src="`${$store.state.app.config.doc_preview_service_address}/onlinePreview?url=${fileUrl}`"
         width="100%"
         height="540px"
@@ -12,7 +12,7 @@
     </div>
 
     <div slot="footer">
-      <el-button @click="dialogShowFileClose">
+      <el-button @click="close">
         {{ $t('Key246') }}
       </el-button>
     </div>
@@ -20,63 +20,67 @@
 </template>
 
 <script>
+export default {
+  name: 'ShowFile'
+};
+</script>
+
+<script setup>
+import { ref, watch, nextTick } from 'vue';
 import { GetFileStoreInfo } from '@/api/app';
 import { encode } from 'js-base64';
 
-export default {
-  name: 'ShowFile',
-  props: {
-    fileName: {
-      default: '',
-      type: String
-    },
-    fileId: {
-      default: '',
-      type: String
-    }
+const props = defineProps({
+  fileName: {
+    type: String,
+    default: ''
   },
-  data() {
-    return {
-      loading: false,
-      dialogVisibleShowFile: false,
-      fileUrl: ''
-    };
+  fileId: {
+    type: String,
+    default: ''
   },
-  watch: {
-    fileId(newVal) {
-      if (newVal.length > 0) {
-        this.getFileStoreInfo();
-      }
-    },
-    dialogVisibleShowFile(newVal) {
-      if (!newVal) {
-        this.fileUrl = '';
-      }
+  visible: {
+    type: Boolean,
+    required: true
+  }
+});
+const emits = defineEmits(['close']);
+
+let loading = ref(false);
+let fileUrl = ref('');
+
+watch(
+  () => props.fileId,
+  (newVal) => {
+    if (newVal.length > 0) {
+      getFileStoreInfo();
     }
-  },
-  methods: {
-    getFileStoreInfo() {
-      GetFileStoreInfo({ file_id: this.fileId }).then(({ file_url_https }) => {
-        this.loading = true;
-        this.fileUrl = encodeURIComponent(encode(file_url_https));
-        this.$nextTick(() => {
-          document.getElementById('iframe').onload = () => {
-            this.loading = false;
-          };
-        });
-      });
-    },
-
-    showDialog() {
-      this.dialogVisibleShowFile = true;
-    },
-
-    dialogShowFileClose() {
-      this.dialogVisibleShowFile = false;
-      this.$emit('dialogShowFileClose');
+  }
+);
+watch(
+  () => props.visible,
+  (newVal) => {
+    if (!newVal) {
+      fileUrl.value = '';
     }
   }
-};
+);
+
+function getFileStoreInfo() {
+  GetFileStoreInfo({ file_id: props.fileId }).then(({ file_url_https }) => {
+    loading.value = true;
+    fileUrl.value = encodeURIComponent(encode(file_url_https));
+    nextTick(() => {
+      document.getElementById('preview-file').onload = () => {
+        loading.value = false;
+      };
+    });
+  });
+}
+
+function close() {
+  emits('close');
+}
 </script>
 
 <style lang="scss">

+ 68 - 0
src/components/course/CoursewareView.vue

@@ -0,0 +1,68 @@
+<template>
+  <div class="courseware-container">
+    <template v-if="category === 'OC' || category.length === 0">
+      <bookquestion :context="context" />
+    </template>
+
+    <template v-else-if="category === 'AILP'">
+      <bookailp :context="context" :ui-type="ui_type" :preview-width="720" :preview-height="405" />
+    </template>
+
+    <template v-else-if="category === 'NPC'">
+      <booknpc
+        v-if="context"
+        ref="book"
+        :context="context"
+        task-model=""
+        :is-show-save="false"
+        :theme-color="themeColor"
+        :preview-type="previewType"
+        :preview-group-id="previewGroupId"
+      />
+    </template>
+
+    <template v-if="category === 'NNPE'">
+      <booknnpe
+        v-if="context"
+        ref="book"
+        :context="context"
+        :theme-color="themeColor"
+        task-model=""
+        :is-show-save="false"
+        :is-show-title="true"
+        :preview-type="previewType"
+        :preview-group-id="previewGroupId"
+      />
+    </template>
+
+    <template v-if="category === 'RLC'">
+      <bookrlc v-if="context" :context="context" :theme-color="themeColor" :book-font-size="bookFontSize" />
+    </template>
+  </div>
+</template>
+
+<script setup>
+import { useShowCourseware } from './courseware';
+
+const props = defineProps({
+  coursewareId: {
+    type: String,
+    required: true
+  },
+  groupIdSelectedInfo: {
+    type: String,
+    required: true
+  }
+});
+
+const { context, ui_type, category, themeColor, bookFontSize, previewType, previewGroupId } = useShowCourseware(
+  props.coursewareId,
+  props.groupIdSelectedInfo
+);
+</script>
+
+<style lang="scss" scoped>
+.courseware-container {
+  overflow: auto;
+}
+</style>

+ 74 - 0
src/components/course/courseware.js

@@ -0,0 +1,74 @@
+import { ref } from 'vue';
+import { GetCoursewareContent_View } from '@/api/course';
+
+const categoryList = ['OC', 'AILP', 'NPC', 'NNPE', 'RLC'];
+
+/**
+ * 显示课件
+ * @param {String} id
+ */
+export function useShowCourseware(id, groupId = '[]') {
+  let courseId = id;
+  let context = ref(null);
+  let ui_type = ref('');
+  let category = ref('');
+  let themeColor = ref('');
+  let bookFontSize = ref('');
+  let previewType = ref('previewCheck');
+  let previewGroupId = ref(groupId);
+
+  function getCoursewareContent_View() {
+    GetCoursewareContent_View({ id: courseId }).then(({ content, category: ca, book_theme_color, book_font_size }) => {
+      if (!content) {
+        context.value = null;
+        return;
+      }
+      category.value = ca;
+      if (category.value === categoryList[0] || category.value.length === 0) {
+        context.value = {
+          id: courseId,
+          ui_type: JSON.parse(content).question.ui_type,
+          content: JSON.parse(content)
+        };
+        return;
+      }
+      if (category.value === categoryList[1]) {
+        const contents = JSON.parse(content);
+        if (contents.question && contents.question.length > 0) {
+          context.value = JSON.parse(contents.question);
+          ui_type.value = contents.ui_type ? contents.ui_type : '';
+        }
+        return;
+      }
+      if (category.value === categoryList[2]) {
+        themeColor.value = book_theme_color;
+        context.value = JSON.parse(content);
+        return;
+      }
+      if (category.value === categoryList[3]) {
+        themeColor.value = book_theme_color;
+        bookFontSize.value = book_font_size;
+        context.value = JSON.parse(content);
+        return;
+      }
+
+      if (category.value === categoryList[4]) {
+        themeColor.value = book_theme_color;
+        context.value = JSON.parse(content);
+        return;
+      }
+    });
+  }
+  getCoursewareContent_View();
+
+  return {
+    context,
+    ui_type,
+    category,
+    themeColor,
+    bookFontSize,
+    previewType,
+    previewGroupId,
+    getCoursewareContent_View
+  };
+}

+ 1 - 1
src/icons/svg/class-grey.svg

@@ -1 +1 @@
-<svg width="20" height="20" fill="none" xmlns="http://www.w3.org/2000/svg"><rect width="20" height="20" rx="3" fill="#D7D7D7"/><path d="M6 8.334a2 2 0 1 0 0-4 2 2 0 0 0 0 4ZM7.667 12.334H4.334v3.333h3.333v-3.333Z" stroke="#fff" stroke-width="1.5" stroke-linejoin="round"/><path d="M10.56 6.334H16v7.667h-5.666" stroke="#fff" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/></svg>
+<svg width="20" height="20" fill="none" xmlns="http://www.w3.org/2000/svg"><rect width="20" height="20" rx="3" fill="#D7D7D7"/><path d="M6 8.334a2 2 0 1 0 0-4 2 2 0 0 0 0 4Zm1.667 4H4.334v3.333h3.333v-3.333Z" stroke="#fff" stroke-width="1.5" stroke-linejoin="round"/><path d="M10.56 6.334H16v7.667h-5.666" stroke="#fff" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/></svg>

+ 1 - 1
src/icons/svg/class.svg

@@ -1 +1 @@
-<svg width="20" height="20" fill="none" xmlns="http://www.w3.org/2000/svg"><rect width="20" height="20" rx="3" fill="#2A76E8"/><path d="M6 8.333a2 2 0 1 0 0-4 2 2 0 0 0 0 4ZM7.667 12.333H4.333v3.333h3.334v-3.333Z" stroke="#fff" stroke-width="1.5" stroke-linejoin="round"/><path d="M10.56 6.333H16V14h-5.667" stroke="#fff" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/></svg>
+<svg width="20" height="20" fill="none" xmlns="http://www.w3.org/2000/svg"><rect width="20" height="20" rx="3" fill="#2A76E8"/><path d="M6 8.333a2 2 0 1 0 0-4 2 2 0 0 0 0 4Zm1.667 4H4.333v3.333h3.334v-3.333Z" stroke="#fff" stroke-width="1.5" stroke-linejoin="round"/><path d="M10.56 6.333H16V14h-5.667" stroke="#fff" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/></svg>

+ 5 - 0
src/icons/svg/course/course-download.svg

@@ -0,0 +1,5 @@
+<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path fill-rule="evenodd" clip-rule="evenodd" d="M7.99997 9.66667L4 5.66667H6.66663V2H9.3333V5.66667H12L7.99997 9.66667Z" stroke="currentColor" stroke-linecap="round" stroke-linejoin="round"/>
+<path d="M14 12.333H2" stroke="currentColor" stroke-linecap="round"/>
+<path d="M11.3327 14.667H4.66602" stroke="currentColor" stroke-linecap="round"/>
+</svg>

+ 4 - 0
src/icons/svg/course/course-preview.svg

@@ -0,0 +1,4 @@
+<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M8.00065 12C11.6826 12 14.6673 8 14.6673 8C14.6673 8 11.6826 4 8.00065 4C4.31875 4 1.33398 8 1.33398 8C1.33398 8 4.31875 12 8.00065 12Z" stroke="white" stroke-linejoin="round"/>
+<path d="M8.00065 9.66634C8.92112 9.66634 9.66732 8.92014 9.66732 7.99967C9.66732 7.07921 8.92112 6.33301 8.00065 6.33301C7.08018 6.33301 6.33398 7.07921 6.33398 7.99967C6.33398 8.92014 7.08018 9.66634 8.00065 9.66634Z" stroke="white" stroke-linejoin="round"/>
+</svg>

+ 1 - 1
src/icons/svg/delete-current.svg

@@ -1 +1 @@
-<svg width="16" height="16" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M3 3.333v11.333h10V3.333H3Z" stroke="currentColor" stroke-width="1.5" stroke-linejoin="round"/><path d="M6.666 6.666v4.333M9.333 6.666v4.333M1.333 3.333h13.333" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/><path d="m5.333 3.333 1.096-2h3.163l1.074 2H5.333Z" stroke="currentColor" stroke-width="1.5" stroke-linejoin="round"/></svg>
+<svg width="16" height="16" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M3 3.333v11.333h10V3.333H3Z" stroke="currentColor" stroke-width="1.5" stroke-linejoin="round"/><path d="M6.666 6.666v4.333m2.667-4.333v4.333m-8-7.666h13.333" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/><path d="m5.333 3.333 1.096-2h3.163l1.074 2H5.333Z" stroke="currentColor" stroke-width="1.5" stroke-linejoin="round"/></svg>

+ 1 - 1
src/icons/svg/microphone.svg

@@ -1 +1 @@
-<svg width="16" height="16" fill="none" xmlns="http://www.w3.org/2000/svg"><rect x="5.667" y="1.334" width="4.667" height="9" rx="2.333" stroke="#fff" stroke-width="1.5" stroke-linejoin="round"/><path d="M3 7.666a5 5 0 0 0 10 0M8 12.666v2" stroke="#fff" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/></svg>
+<svg width="16" height="16" fill="none" xmlns="http://www.w3.org/2000/svg"><rect x="5.667" y="1.334" width="4.667" height="9" rx="2.333" stroke="#fff" stroke-width="1.5" stroke-linejoin="round"/><path d="M3 7.666a5 5 0 0 0 10 0m-5 5v2" stroke="#fff" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/></svg>

+ 1 - 4
src/icons/svg/play.svg

@@ -1,4 +1 @@
-<svg width="36" height="36" viewBox="0 0 36 36" fill="none" xmlns="http://www.w3.org/2000/svg">
-<path d="M12 26L18.5 22V14L12 10V26ZM18.5 22L25 18L18.5 14V22Z" fill="white"/>
-<rect x="0.5" y="0.5" width="35" height="35" rx="17.5" stroke="white"/>
-</svg>
+<svg width="36" height="36" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="m12 26 6.5-4v-8L12 10v16Zm6.5-4 6.5-4-6.5-4v8Z" fill="#fff"/><rect x=".5" y=".5" width="35" height="35" rx="17.5" stroke="#fff"/></svg>

+ 1 - 1
src/icons/svg/preview.svg

@@ -1 +1 @@
-<svg width="14" height="14" fill="none" xmlns="http://www.w3.org/2000/svg"><path clip-rule="evenodd" d="M7 12.667c3.314 0 6-2.774 6-4.666 0-1.893-2.686-4.667-6-4.667S1 6.11 1 8.001c0 1.89 2.686 4.666 6 4.666Z" stroke="#000" stroke-width="1.5" stroke-linejoin="round"/><path d="M7 10a2 2 0 1 0 0-4 2 2 0 0 0 0 4Z" stroke="#000" stroke-width="1.5" stroke-linejoin="round"/><path d="m3.421 2.756.865 1.207M10.875 2.904l-.865 1.207M7.003 1.334v2" stroke="#000" stroke-width="1.5" stroke-linecap="round"/></svg>
+<svg width="14" height="14" fill="none" xmlns="http://www.w3.org/2000/svg"><path clip-rule="evenodd" d="M7 12.667c3.314 0 6-2.774 6-4.666 0-1.893-2.686-4.667-6-4.667S1 6.11 1 8.001c0 1.89 2.686 4.666 6 4.666Z" stroke="#000" stroke-width="1.5" stroke-linejoin="round"/><path d="M7 10a2 2 0 1 0 0-4 2 2 0 0 0 0 4Z" stroke="#000" stroke-width="1.5" stroke-linejoin="round"/><path d="m3.421 2.756.865 1.207m6.589-1.059-.865 1.207M7.003 1.334v2" stroke="#000" stroke-width="1.5" stroke-linecap="round"/></svg>

+ 1 - 4
src/icons/svg/record-stop.svg

@@ -1,4 +1 @@
-<svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg">
-<circle cx="20" cy="20" r="19" stroke="#D83434" stroke-width="2"/>
-<rect x="12" y="12" width="16" height="16" rx="4" fill="#D83434"/>
-</svg>
+<svg width="40" height="40" fill="none" xmlns="http://www.w3.org/2000/svg"><circle cx="20" cy="20" r="19" stroke="#D83434" stroke-width="2"/><rect x="12" y="12" width="16" height="16" rx="4" fill="#D83434"/></svg>

+ 1 - 4
src/icons/svg/recording.svg

@@ -1,4 +1 @@
-<svg width="40" height="40" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg">
-<circle cx="20" cy="20" r="19" stroke="#D83434" stroke-width="2"/>
-<circle cx="20" cy="20" r="12" fill="#D83434"/>
-</svg>
+<svg width="40" height="40" fill="none" xmlns="http://www.w3.org/2000/svg"><circle cx="20" cy="20" r="19" stroke="#D83434" stroke-width="2"/><circle cx="20" cy="20" r="12" fill="#D83434"/></svg>

+ 24 - 0
src/utils/common.js

@@ -1,3 +1,5 @@
+import { getToken } from '@/utils/auth';
+
 // 全屏方法兼容
 export function fullScreenCompatibility(dom) {
   if (dom.requestFullscreen) {
@@ -78,3 +80,25 @@ export function throttle(fn, interval) {
     }
   };
 }
+
+/**
+ * 点击下载文件
+ * @param {String} id 文件id
+ * @param {String} fileName 文件名称
+ */
+export function downloadFileUrl(id, fileName) {
+  const { token, isHas } = getToken();
+  const UserCode = isHas ? token.user_code : '';
+  const UserType = isHas ? token.user_type : '';
+  const SessionID = isHas ? token.session_id : '';
+
+  const link = document.createElement('a');
+  link.href = `${process.env.VUE_APP_BASE_API}/GCLSFileServer/WebFileDownload?UserCode=${UserCode}&UserType=${UserType}&SessionID=${SessionID}&FileID=${id}`;
+  link.setAttribute('download', fileName);
+  link.style.display = 'none';
+  document.body.appendChild(link);
+  setTimeout(() => {
+    link.click();
+    document.body.removeChild(link);
+  }, 66);
+}

+ 1 - 0
src/utils/filter.js

@@ -1,6 +1,7 @@
 /**
  * 得到文件类型
  * @param {String} fileName
+ * @return {String} 文件类型
  */
 export function getFileType(fileName) {
   return fileName.slice(fileName.lastIndexOf('.') + 1, fileName.length);

+ 1 - 2
src/utils/request.js

@@ -46,8 +46,7 @@ service.interceptors.response.use(
         type: 'error',
         duration: 3 * 1000
       });
-
-      return Promise.reject(new Error(res.error || 'Error'));
+      return Promise.reject(new Error(`${response.config?.params?.MethodName} ${res.error}` || 'Error'));
     }
 
     // 无效的操作用户

+ 19 - 0
src/utils/switch.js

@@ -0,0 +1,19 @@
+import { ref } from 'vue';
+
+/**
+ * 单一状态切换
+ * @param {Boolean} startStatus 开始状态
+ */
+export function useSwitch(startStatus = false) {
+  let state = ref(startStatus);
+
+  function toggle(fn) {
+    state.value = !state.value;
+    if (typeof fn === 'function') fn();
+  }
+
+  return {
+    state,
+    toggle
+  };
+}

+ 4 - 7
src/views/task_details/student/index.vue

@@ -158,12 +158,7 @@
       @dialogClose="dialogClose"
     />
 
-    <show-file
-      ref="file"
-      :file-name="showCurFileName"
-      :file-id="showCurFileId"
-      @dialogShowFileClose="dialogShowFileClose"
-    />
+    <ShowFile :visible="visible" :file-name="showCurFileName" :file-id="showCurFileId" @close="dialogShowFileClose" />
   </div>
 </template>
 
@@ -214,6 +209,7 @@ export default {
       student_remark: '',
       student_score: 0,
       loading: false,
+      visible: false,
       showCurFileName: '',
       showCurFileId: '',
       // 开启课后评价
@@ -332,10 +328,11 @@ export default {
     viewFile(fileName, fileId) {
       this.showCurFileName = fileName;
       this.showCurFileId = fileId;
-      this.$refs.file.showDialog();
+      this.visible = true;
     },
 
     dialogShowFileClose() {
+      this.visible = false;
       this.showCurFileName = '';
       this.showCurFileId = '';
     },

+ 4 - 7
src/views/task_details/teacher/index.vue

@@ -117,12 +117,7 @@
       @dialogClose="dialogClose"
     />
 
-    <show-file
-      ref="file"
-      :file-name="showCurFileName"
-      :file-id="showCurFileId"
-      @dialogShowFileClose="dialogShowFileClose"
-    />
+    <show-file :visible="visible" :file-name="showCurFileName" :file-id="showCurFileId" @close="dialogShowFileClose" />
   </div>
 </template>
 
@@ -173,6 +168,7 @@ export default {
       previewGroupId: '[]',
       loading: false,
       student_list_height: 490,
+      visible: false,
       showCurFileName: '',
       showCurFileId: '',
       // 开启课后评价
@@ -291,10 +287,11 @@ export default {
     viewFile(fileName, fileId) {
       this.showCurFileName = fileName;
       this.showCurFileId = fileId;
-      this.$refs.file.showDialog();
+      this.visible = true;
     },
 
     dialogShowFileClose() {
+      this.visible = false;
       this.showCurFileName = '';
       this.showCurFileId = '';
     },

+ 0 - 3
src/views/teacher/create_course/step_table/SelectBook.vue

@@ -135,9 +135,6 @@ export default {
     },
     nextStep() {
       GetCourseBookInfo_Brief({ course_id: this.id }).then(({ book_count }) => {
-        if (book_count <= 0) {
-          return this.$message.warning('请至少选择一本教材');
-        }
         this.$router.push({
           path: `/create_course_step_table/create_task/${this.id}?is_template=${this.is_template}`
         });

+ 7 - 8
src/views/teacher/create_course/step_table/create_task/components/TaskExplain/ExpandContract.vue

@@ -1,10 +1,10 @@
 <template>
   <div>
-    <div ref="stretch" :class="['stretch', titleClass, isUnfold ? 'unfold' : 'contract']">
+    <div ref="stretch" :class="['stretch', titleClass, state ? 'unfold' : 'contract']">
       <span>{{ title }}</span>
-      <div class="stretch-click" @click="handleStretch">
-        {{ isUnfold ? '收起' : '展开' }}
-        <i :class="[isUnfold ? 'el-icon-arrow-down' : 'el-icon-arrow-up']"></i>
+      <div class="stretch-click" @click="toggle(handleStretch)">
+        {{ state ? '收起' : '展开' }}
+        <i :class="[state ? 'el-icon-arrow-down' : 'el-icon-arrow-up']"></i>
       </div>
     </div>
     <slot></slot>
@@ -13,6 +13,7 @@
 
 <script setup>
 import { ref } from 'vue';
+import { useSwitch } from '@/utils/switch';
 
 const props = defineProps({
   title: {
@@ -30,12 +31,10 @@ const props = defineProps({
 });
 
 const stretch = ref();
-const isUnfold = ref(true); // 是否展开
+const { state, toggle } = useSwitch(true);
 
 function handleStretch() {
-  isUnfold.value = !isUnfold.value;
-
-  stretch.value.nextSibling.nextSibling.style.height = isUnfold.value ? `${props.height}px` : 0;
+  stretch.value.nextSibling.nextSibling.style.height = state.value ? `${props.height}px` : 0;
 }
 </script>
 

+ 1 - 1
src/views/teacher/create_course/step_table/create_task/components/TaskExplain/index.vue

@@ -17,7 +17,7 @@
         <div class="sub-title">班型</div>
       </div>
       <div class="aliquot">
-        <el-input v-model="task_intro.class" :placeholder="placeholder" :disabled="disabled" />
+        <el-input v-model="task_intro.class_name" :placeholder="placeholder" :disabled="disabled" />
         <el-input v-model="task_intro.teacher" :placeholder="placeholder" :disabled="disabled" />
         <el-input v-model="task_intro.class_mode" :placeholder="placeholder" :disabled="disabled" />
       </div>

+ 8 - 2
src/views/teacher/create_course/step_table/create_task/components/task_template/components/file.js → src/views/teacher/create_course/step_table/create_task/components/common/file.js

@@ -3,11 +3,17 @@ import { Message } from 'element-ui';
 import { getFileType } from '@/utils/filter';
 import { fileUploadAbort } from '@/api/app';
 
-let allowFormat = ['mp3', 'mp4', 'png', 'jpg']; // 允许的文件格式
+export const pictureFormat = ['webp', 'jpg', 'jpeg', 'gif', 'png', 'tif'];
+export const audioFormat = ['mp3', 'wma'];
+export const videoFormat = ['mp4', 'flv', 'webm', 'mpeg', 'mpg'];
+export const documentFormat = ['txt', 'doc', 'docx', 'pdf', 'ppt', 'pptx', 'xls', 'xlsx'];
+export const compressFormat = ['zip', 'rar', '7z'];
+export const allowFormat = pictureFormat.concat(audioFormat, videoFormat, documentFormat, compressFormat); // 所有允许的文件格式
+
 export let accept = `.${allowFormat.join(',.')}`;
 
 // 文件状态列表
-export let fileStatusList = [
+export const fileStatusList = [
   {
     name: '文件格式有误',
     type: 'error',

+ 1 - 1
src/views/teacher/create_course/step_table/create_task/components/data/TaskClassify.js

@@ -24,7 +24,7 @@ export const taskClassify = [
     image: require('@/assets/course/create_task/file.png')
   },
   {
-    type: 'interactive',
+    type: 'message',
     name: '录音/录影',
     teaching_type: 14,
     image: require('@/assets/course/create_task/soundRecording.png')

+ 6 - 6
src/views/teacher/create_course/step_table/create_task/components/data/TaskData.js

@@ -1,6 +1,7 @@
 import { ref } from 'vue';
 import { taskClassify } from './TaskClassify';
 import { SaveTaskListToCSItem } from '@/api/course';
+import { Message } from 'element-ui';
 
 export let taskData = ref({
   cs_item_id: '', // 课节 id
@@ -8,7 +9,7 @@ export let taskData = ref({
   task_intro: {
     title: '', // 标题
     subject: '', // 学科
-    class: '', // 班级
+    class_name: '', // 班级
     teacher: '', // 执教者
     class_mode: '', // 班型
     topic: '', // 课题
@@ -56,7 +57,7 @@ export const subtaskObj = {
 // 子任务信息块列表中可能的对象
 export const subtaskInfoBlock = [
   {
-    info_block_type: taskClassify[1].type, // 信息块类型 courseware【课件】,file【文件】,interactive【互动
+    info_block_type: taskClassify[1].type, // 信息块类型 courseware【课件】,file【文件】,message【互动消息
     courseware_id_list: [], // 课件id列表
     courseware_group_selected_list: [], // 课件分组选择列表
     _coursewares: [] // 临时保存的课件列表
@@ -75,7 +76,7 @@ export const subtaskInfoBlock = [
 // 互动消息列表中可能的项
 export const messageItem = [
   {
-    message_type: 'text',
+    message_type: 'message',
     text: ''
   },
   {
@@ -119,8 +120,7 @@ export function saveCSItem(id) {
     mid_task_list: filterTempProps(taskData.value.mid_task_list),
     after_task_list: filterTempProps(taskData.value.after_task_list)
   };
-  console.log(data);
-  SaveTaskListToCSItem(data).then((data) => {
-    console.log(data);
+  SaveTaskListToCSItem(data).then(() => {
+    Message.success('保存任务列表到课节成功');
   });
 }

+ 1 - 1
src/views/teacher/create_course/step_table/create_task/components/preview/index.vue

@@ -8,7 +8,7 @@
         v-for="(item, i) in curTaskTemplateList"
         :key="`${curTaskTypeObj.sidebarListName}-${i}`"
         :list-name="curTaskTypeObj.sidebarListName"
-        :index="i"
+        :task-index="i"
       />
     </template>
   </div>

+ 150 - 0
src/views/teacher/create_course/step_table/create_task/components/preview/task_preview/SubtaskItem.vue

@@ -0,0 +1,150 @@
+<template>
+  <div class="subtask">
+    <!-- 标题 -->
+    <div class="subtask-header">
+      <div class="subtask-type">
+        <span v-for="({ info_block_type }, i) in subtaskData.info_block_list" :key="i" class="subtask-type-item">
+          {{ getTaskClassifyAttr(info_block_type) }}
+        </span>
+      </div>
+      <div v-show="!state" class="subtask-header-name">
+        {{ subtaskData.name }}
+      </div>
+      <div class="operation">
+        <span class="contract" @click="toggle">
+          <span>{{ state ? '收起' : '展开' }}</span>
+          <i :class="[state ? 'el-icon-arrow-down' : 'el-icon-arrow-up']"></i
+        ></span>
+      </div>
+    </div>
+
+    <div class="subtask-content" :style="{ height: state ? '100%' : '0px', padding: state ? '8px 0' : '0px' }">
+      <div class="subtask-content-item basic">
+        <div class="subtask-title">{{ subtaskData.name }}</div>
+        <div v-html="subtaskData.content"></div>
+      </div>
+      <div v-for="(item, i) in subtaskData.info_block_list" :key="`preview-${i}`" class="subtask-content-item">
+        <template v-if="item.info_block_type === taskClassify[1].type">
+          <CoursewareView
+            v-for="(data, j) in item.courseware_group_selected_list"
+            :key="`course-${j}`"
+            :courseware-id="data.courseware_id"
+            :group-id-selected-info="data.group_id_selected_info"
+          />
+        </template>
+        <PreviewFile v-if="item.info_block_type === taskClassify[2].type" :file-id-list="item.file_id_list" />
+        <PreviewMessage v-if="item.info_block_type === taskClassify[3].type" :data="item" />
+      </div>
+    </div>
+  </div>
+</template>
+
+<script>
+export default {
+  name: 'SubtaskItem'
+};
+</script>
+
+<script setup>
+import { ref } from 'vue';
+import { getTaskClassifyAttr } from '../../data/TaskClassify';
+import { useSwitch } from '@/utils/switch';
+import { taskClassify } from '../../data/TaskClassify.js';
+
+import CoursewareView from '@/components/course/CoursewareView.vue';
+import PreviewFile from './components/PreviewFile.vue';
+import PreviewMessage from './components/PreviewMessage.vue';
+
+const props = defineProps({
+  subtaskData: {
+    type: Object,
+    required: true
+  }
+});
+
+const { state, toggle } = useSwitch(true);
+</script>
+
+<style lang="scss" scoped>
+.subtask {
+  padding: 8px 8px 0;
+  background-color: #f5f5f5;
+  border: 1px solid $border-color;
+  border-radius: 8px;
+  box-shadow: 0 2px 4px $border-color;
+
+  &-header {
+    display: flex;
+    column-gap: 8px;
+    justify-content: space-between;
+    padding-bottom: 8px;
+    line-height: 24px;
+
+    .subtask-type {
+      display: flex;
+      flex-wrap: wrap;
+      row-gap: 8px;
+      column-gap: 8px;
+
+      &-item {
+        height: 26px;
+        padding: 2px 12px;
+        line-height: 22px;
+        color: #fff;
+        white-space: nowrap;
+        background-color: #44c1c1;
+        border-radius: 20px;
+      }
+    }
+
+    &-name {
+      flex: 1;
+      min-width: 412px;
+    }
+
+    .operation {
+      display: flex;
+      column-gap: 8px;
+      white-space: nowrap;
+
+      .contract,
+      .expand {
+        color: #595959;
+        cursor: pointer;
+
+        .el-icon-arrow-down,
+        .el-icon-arrow-up {
+          margin-left: 6px;
+        }
+      }
+    }
+  }
+
+  &-content {
+    display: flex;
+    flex-direction: column;
+    row-gap: 8px;
+    overflow: hidden;
+    line-height: 22px;
+    color: #2c2c2c;
+
+    &-item {
+      padding: 24px;
+      overflow: hidden;
+      background-color: #fff;
+      border: 1px solid $border-color;
+      border-radius: 8px;
+
+      // 课件间隔
+      .courseware-container + .courseware-container {
+        margin-top: 12px;
+      }
+
+      .subtask-title {
+        font-size: 18px;
+        font-weight: bold;
+      }
+    }
+  }
+}
+</style>

+ 245 - 0
src/views/teacher/create_course/step_table/create_task/components/preview/task_preview/components/PreviewFile.vue

@@ -0,0 +1,245 @@
+<template>
+  <div class="preview-file">
+    <div class="preview-file-header">
+      <div class="type-list">
+        <div
+          v-for="{ type, name } in fileTypeList"
+          :key="type"
+          :class="['type-item', { active: type === curFileType }]"
+          @click="changeFileType(type)"
+        >
+          {{ name }}
+        </div>
+      </div>
+      <div class="file-download">
+        <svg-icon icon-class="course-download" />
+        全部下载
+      </div>
+    </div>
+    <div class="preview-file-content">
+      <div
+        v-for="{ id, file_name } in info"
+        v-show="curFileType === fileTypeList[0].type || curFileFormat.includes(getFileType(file_name))"
+        :key="id"
+        class="file-item"
+      >
+        <div class="file-item-image">
+          <div class="file-operation">
+            <svg-icon icon-class="course-preview" @click="showFileVisible(file_name, id)" />
+            <span class="vertical-bar"></span>
+            <svg-icon icon-class="course-download" @click="downloadFileUrl(id, file_name)" />
+          </div>
+          <img :src="getFileImage(getFileType(file_name))" />
+        </div>
+        <span class="nowrap-ellipsis">{{ file_name }}</span>
+      </div>
+    </div>
+    <hr />
+    <div class="student-download">
+      <div class="student-download-title">学生上传:</div>
+      <div class="select-file">
+        <el-button size="small">选择文件</el-button>
+        <span class="tip">支持批量上传,上传格式支持mp3; mp4; jpg; png文件</span>
+      </div>
+    </div>
+  </div>
+</template>
+
+<script>
+export default {
+  name: 'PreviewFile'
+};
+</script>
+
+<script setup>
+import { GetFileInfoMap } from '@/api/app';
+import { computed, inject, ref, watchEffect } from 'vue';
+import {
+  allowFormat,
+  documentFormat,
+  pictureFormat,
+  videoFormat,
+  audioFormat,
+  compressFormat
+} from '../../../common/file.js';
+import { getFileType } from '@/utils/filter';
+import { downloadFileUrl } from '@/utils/common';
+
+const props = defineProps({
+  fileIdList: {
+    type: Array,
+    required: true
+  }
+});
+
+const fileTypeList = [
+  {
+    name: '全部',
+    type: 'all',
+    format: allowFormat
+  },
+  {
+    name: '文档',
+    type: 'document',
+    format: documentFormat
+  },
+  {
+    name: '图片',
+    type: 'image',
+    format: pictureFormat
+  },
+  {
+    name: '视频',
+    type: 'video',
+    format: videoFormat
+  },
+  {
+    name: '音频',
+    type: 'audio',
+    format: audioFormat
+  },
+  {
+    name: '压缩包',
+    type: 'compress',
+    format: compressFormat
+  }
+];
+
+let curFileType = ref(fileTypeList[0].type);
+let curFileFormat = computed((newVal) => {
+  return fileTypeList.find(({ type }) => curFileType.value === type).format;
+});
+
+function changeFileType(type) {
+  curFileType.value = type;
+}
+
+let info = ref([]);
+
+watchEffect(() => {
+  GetFileInfoMap({ file_id_list: props.fileIdList }).then(({ info_map }) => {
+    let data = [];
+    for (const [id, value] of Object.entries(info_map)) {
+      data.push({ ...value, id });
+    }
+    info.value = data;
+  });
+});
+
+function getFileImage(type) {
+  if (type === 'txt') {
+    return require('@/assets/file/txt.png');
+  }
+  if (type === 'pdf') {
+    return require('@/assets/file/pdf.png');
+  }
+  if (/^xlsx?$/.test(type)) {
+    return require('@/assets/file/execl.png');
+  }
+  if (/^pptx?$/.test(type)) {
+    return require('@/assets/file/ppt.png');
+  }
+  if (/^docx?$/.test(type)) {
+    return require('@/assets/file/word.png');
+  }
+}
+
+const showFileVisible = inject('showFileVisible');
+</script>
+
+<style lang="scss" scoped>
+.preview-file {
+  &-header {
+    display: flex;
+    justify-content: space-between;
+    padding-bottom: 16px;
+    border-bottom: 1px dashed $border-color;
+
+    .type-list {
+      display: flex;
+      column-gap: 40px;
+
+      .type-item {
+        font-size: 16px;
+        font-weight: 500;
+        cursor: pointer;
+
+        &.active {
+          color: #1890ff;
+        }
+      }
+    }
+
+    .file-download {
+      color: #979797;
+      cursor: pointer;
+    }
+  }
+
+  &-content {
+    display: flex;
+    flex-wrap: wrap;
+    row-gap: 16px;
+    column-gap: 8px;
+    padding-top: 16px;
+
+    .file-item {
+      display: flex;
+      flex-direction: column;
+      row-gap: 8px;
+      width: 112px;
+      height: 142px;
+
+      &-image {
+        position: relative;
+        width: 112px;
+        height: 112px;
+        cursor: pointer;
+
+        .file-operation {
+          position: absolute;
+          top: 0;
+          left: 0;
+          display: none;
+          width: 100%;
+          height: 100%;
+          color: #fff;
+          background-color: rgba(0, 0, 0, 60%);
+          border-radius: 3px;
+
+          .svg-icon {
+            width: 16px;
+            height: 16px;
+          }
+
+          .vertical-bar {
+            width: 1px;
+            height: 16px;
+            background-color: #fff;
+          }
+        }
+
+        &:hover .file-operation {
+          display: flex;
+          column-gap: 16px;
+          align-items: center;
+          justify-content: center;
+        }
+      }
+    }
+  }
+
+  .student-download {
+    &-title {
+      padding-bottom: 8px;
+      font-weight: bold;
+    }
+
+    .select-file {
+      display: flex;
+      column-gap: 24px;
+      align-items: center;
+    }
+  }
+}
+</style>

+ 7 - 0
src/views/teacher/create_course/step_table/create_task/components/preview/task_preview/components/PreviewMessage.vue

@@ -0,0 +1,7 @@
+<template>
+  <div>sfdsjkljkl</div>
+</template>
+
+<script setup></script>
+
+<style lang="scss" scoped></style>

+ 35 - 12
src/views/teacher/create_course/step_table/create_task/components/preview/task_preview/index.vue

@@ -1,7 +1,7 @@
 <template>
   <div class="preview">
     <div class="preview-title">
-      <span class="preview-title-index">{{ index + 1 }}</span>
+      <span class="preview-title-index">{{ taskIndex + 1 }}</span>
       <span class="preview-title-name">
         {{ previewDateTransform(curTaskObject.begin_time, curTaskObject.duration_second) }}
       </span>
@@ -12,11 +12,12 @@
       <!-- 子任务 -->
       <template v-if="curTaskObject.child_task_list.length > 0">
         <hr />
-        <div class="subtask">
-          <div class="subtask-title">子任务:</div>
-        </div>
+        <div class="subtask-title">子任务:</div>
+        <SubtaskItem v-for="(data, i) in curTaskObject.child_task_list" :key="`subtask-${i}`" :subtask-data="data" />
       </template>
     </div>
+
+    <ShowFile :visible="visible" :file-name="curFileName" :file-id="curFileId" @close="dialogShowFileClose" />
   </div>
 </template>
 
@@ -27,29 +28,53 @@ export default {
 </script>
 
 <script setup>
-import { computed } from 'vue';
+import { ref, computed, provide } from 'vue';
 import { taskData } from '../../data/TaskData';
 import { previewDateTransform } from '../../util/preview';
 
+import SubtaskItem from './SubtaskItem.vue';
+import ShowFile from '@/common/show_file/index.vue';
+
 const props = defineProps({
   listName: {
     type: String,
     required: true
   },
-  index: {
+  taskIndex: {
     type: Number,
     required: true
   }
 });
 
+provide('listName', props.listName);
+provide('taskIndex', props.taskIndex);
+
 let curTaskObject = computed(() => {
-  return taskData.value[props.listName][props.index];
+  return taskData.value[props.listName][props.taskIndex];
 });
+
+// 文件预览
+let visible = ref(false);
+let curFileName = ref('');
+let curFileId = ref('');
+function dialogShowFileClose() {
+  visible.value = false;
+  curFileName.value = '';
+  curFileId.value = '';
+}
+
+function showFileVisible(fileName, fileId) {
+  visible.value = true;
+  curFileName.value = fileName;
+  curFileId.value = fileId;
+}
+
+provide('showFileVisible', showFileVisible);
 </script>
 
 <style lang="scss" scoped>
 .preview {
-  width: 828px;
+  width: 830px;
   margin: 0 auto;
 
   & + .preview {
@@ -103,10 +128,8 @@ let curTaskObject = computed(() => {
       border: 1px dashed $border-color;
     }
 
-    .subtask {
-      &-title {
-        font-weight: bold;
-      }
+    .subtask-title {
+      font-weight: bold;
     }
   }
 }

+ 2 - 2
src/views/teacher/create_course/step_table/create_task/components/task_template/components/TemplateFile.vue

@@ -15,7 +15,7 @@
       <ul v-if="files.length > 0">
         <li
           v-for="({ file: { name, size, lastModified }, status }, i) in files"
-          :key="`${lastModified}i`"
+          :key="`${lastModified}-${i}`"
           class="file-list-item"
         >
           <span class="file-name nowrap-ellipsis">{{ name }}</span>
@@ -47,7 +47,7 @@
 <script setup>
 import { ref } from 'vue';
 import { conversionSize } from '@/utils/common';
-import { accept, useUploadFile } from './file';
+import { accept, useUploadFile } from '../../common/file';
 import { useDrag } from '../../util/drag';
 import { taskData } from '../../data/TaskData';
 

+ 4 - 0
src/views/teacher/create_course/step_table/create_task/components/task_template/components/courseware.js

@@ -2,6 +2,10 @@ import { ref, watch, computed, provide, inject } from 'vue';
 import { Message } from 'element-ui';
 import { GetTreeNodeInfo_BookChapterStruct } from '@/api/course';
 
+/**
+ * 添加课件
+ * @param {Object} curTemplateData 当前模板数据
+ */
 export function useCourseware(curTemplateData) {
   let courseList = ref(curTemplateData['_coursewares']);
   // 课件id列表

+ 7 - 9
src/views/teacher/create_course/step_table/create_task/components/task_template/subtask/SubTask.vue

@@ -7,21 +7,21 @@
           getTaskClassifyAttr(info_block_type)
         }}</span>
       </div>
-      <div v-show="!isUnfold" class="subtask-title-name">
+      <div v-show="!state" class="subtask-title-name">
         {{ curSubtaskObj.name }}
       </div>
       <div class="operation">
         <span class="delete" @click="deleteSubtask">
           <span>删除</span><svg-icon icon-class="delete-current" class-name="delete-current" />
         </span>
-        <span class="contract" @click="handleStretch">
-          <span>{{ isUnfold ? '收起' : '展开' }}</span>
-          <i :class="[isUnfold ? 'el-icon-arrow-down' : 'el-icon-arrow-up']"></i
+        <span class="contract" @click="toggle">
+          <span>{{ state ? '收起' : '展开' }}</span>
+          <i :class="[state ? 'el-icon-arrow-down' : 'el-icon-arrow-up']"></i
         ></span>
       </div>
     </div>
     <!-- 内容 -->
-    <div class="subtask-content" :style="{ height: isUnfold ? '100%' : '0px', padding: isUnfold ? '8px' : '0px' }">
+    <div class="subtask-content" :style="{ height: state ? '100%' : '0px', padding: state ? '8px' : '0px' }">
       <span>任务标题</span>
       <el-input v-model="curSubtaskObj.name" placeholder="点击输入" />
       <span>任务说明</span>
@@ -77,6 +77,7 @@ import { useDrag, customTypeList } from '../../util/drag';
 import { taskClassify, getTaskClassifyAttr } from '../../data/TaskClassify.js';
 import { taskData } from '../../data/TaskData';
 import { useSubtaskItem } from './data';
+import { useSwitch } from '@/utils/switch';
 
 import TemplateFile from '../components/TemplateFile.vue';
 import TemplateCourseware from '../components/TemplateCourseware.vue';
@@ -103,10 +104,7 @@ function deleteSubtask() {
 }
 
 const { onDragover, onDrop } = useDrag(true, customTypeList[0]);
-const isUnfold = ref(true); // 是否展开
-function handleStretch() {
-  isUnfold.value = !isUnfold.value;
-}
+const { state, toggle } = useSwitch(true);
 
 let info_block_list = ref(curSubtaskObj.info_block_list); // 子任务中添加的类型列表
 const { addSubtaskItem, deleteSubtaskItem } = useSubtaskItem(info_block_list);

+ 12 - 3
src/views/teacher/create_course/step_table/create_task/index.js

@@ -1,6 +1,7 @@
 import { ref, provide, computed, watch } from 'vue';
 import { GetCSItemListByCourseID, GetCSItemTaskList } from '@/api/course';
 import { useRoute } from 'vue-router/composables';
+import { taskData } from './components/data/TaskData';
 import { GetTeacherListByCourseID, GetCourseStudentList } from '@/api/select.js';
 import store from '@/store';
 
@@ -37,9 +38,17 @@ export function useCSItem(content) {
     () => cs_item_id.value,
     (newVal) => {
       if (!newVal) return;
-      GetCSItemTaskList({ cs_item_id: newVal }).then((data) => {
-        console.log(data);
-      });
+      GetCSItemTaskList({ cs_item_id: newVal }).then(
+        ({ pre_task_list, mid_task_list, after_task_list, task_intro, cs_item_id }) => {
+          taskData.value.task_intro = task_intro;
+          taskData.value.cs_item_id = cs_item_id;
+          // TODO 获取的任务列表格式与显示格式对齐
+          console.log(pre_task_list[0].child_task_list[0].info_block_list);
+          // taskData.value.pre_task_list = pre_task_list;
+          // taskData.value.mid_task_list = mid_task_list;
+          // taskData.value.after_task_list = after_task_list;
+        }
+      );
     }
   );