Browse Source

基本完成任务课节编辑与预览

dusenyao 2 years ago
parent
commit
865140e792
46 changed files with 936 additions and 357 deletions
  1. 13 0
      .vscode/launch.json
  2. 112 103
      package-lock.json
  3. 9 9
      package.json
  4. 3 4
      src/components/select/SelectCourse.vue
  5. 10 7
      src/router/index.js
  6. 1 0
      src/store/data.js
  7. 4 1
      src/store/modules/app.js
  8. 15 0
      src/utils/filter.js
  9. 30 0
      src/utils/list.js
  10. 9 0
      src/utils/validate.js
  11. 10 8
      src/views/teacher/create_course/step_table/NewTask.vue
  12. 74 73
      src/views/teacher/create_course/step_table/SelectBook.vue
  13. 1 1
      src/views/teacher/create_course/step_table/create_task/components/TaskExplain/index.vue
  14. 1 1
      src/views/teacher/create_course/step_table/create_task/components/common/AudioShow.vue
  15. 2 1
      src/views/teacher/create_course/step_table/create_task/components/common/VideoShow.vue
  16. 24 12
      src/views/teacher/create_course/step_table/create_task/components/data/TaskClassify.js
  17. 11 2
      src/views/teacher/create_course/step_table/create_task/components/data/TaskData.js
  18. 2 1
      src/views/teacher/create_course/step_table/create_task/components/data/TaskType.js
  19. 42 27
      src/views/teacher/create_course/step_table/create_task/components/layouts/LeftSidebar.vue
  20. 3 3
      src/views/teacher/create_course/step_table/create_task/components/layouts/RightSidebar.vue
  21. 4 4
      src/views/teacher/create_course/step_table/create_task/components/layouts/TaskEditor.vue
  22. 10 9
      src/views/teacher/create_course/step_table/create_task/components/layouts/create_csitem/index.vue
  23. 2 2
      src/views/teacher/create_course/step_table/create_task/components/pop-up/SelectTaskClassify.vue
  24. 3 3
      src/views/teacher/create_course/step_table/create_task/components/preview/index.vue
  25. 184 3
      src/views/teacher/create_course/step_table/create_task/components/preview/mind_mapping/index.vue
  26. 1 1
      src/views/teacher/create_course/step_table/create_task/components/preview/task_preview/components/PreviewFile.vue
  27. 102 3
      src/views/teacher/create_course/step_table/create_task/components/preview/task_preview/components/PreviewMessage.vue
  28. 7 8
      src/views/teacher/create_course/step_table/create_task/components/preview/task_preview/components/SubtaskItem.vue
  29. 34 4
      src/views/teacher/create_course/step_table/create_task/components/preview/task_preview/index.vue
  30. 1 1
      src/views/teacher/create_course/step_table/create_task/components/task_template/CreateTask.vue
  31. 19 9
      src/views/teacher/create_course/step_table/create_task/components/task_template/TaskTemplate.vue
  32. 6 2
      src/views/teacher/create_course/step_table/create_task/components/task_template/components/TemplateCourseware.vue
  33. 3 7
      src/views/teacher/create_course/step_table/create_task/components/task_template/components/TemplateFile.vue
  34. 2 2
      src/views/teacher/create_course/step_table/create_task/components/task_template/components/TemplateRecording.vue
  35. 7 2
      src/views/teacher/create_course/step_table/create_task/components/task_template/components/courseware.js
  36. 3 3
      src/views/teacher/create_course/step_table/create_task/components/task_template/index.vue
  37. 10 6
      src/views/teacher/create_course/step_table/create_task/components/task_template/subtask/SubTask.vue
  38. 5 2
      src/views/teacher/create_course/step_table/create_task/components/task_template/subtask/SubtaskContainer.vue
  39. 10 6
      src/views/teacher/create_course/step_table/create_task/components/task_template/subtask/data.js
  40. 3 3
      src/views/teacher/create_course/step_table/create_task/components/utils/drag.js
  41. 3 2
      src/views/teacher/create_course/step_table/create_task/components/utils/file.js
  42. 7 2
      src/views/teacher/create_course/step_table/create_task/components/utils/mouseEvent.js
  43. 2 1
      src/views/teacher/create_course/step_table/create_task/components/utils/preview.js
  44. 0 0
      src/views/teacher/create_course/step_table/create_task/components/utils/wheel.js
  45. 118 10
      src/views/teacher/create_course/step_table/create_task/index.js
  46. 14 9
      src/views/teacher/create_course/step_table/create_task/index.vue

+ 13 - 0
.vscode/launch.json

@@ -15,6 +15,19 @@
         "webpack://gcls_sys_learn_web/src/*": "${webRoot}/*",
         "webpack://gcls_sys_learn_web/./src/*.js": "${webRoot}/*.js"
       }
+    },
+    {
+      "name": "vuejs: attach chrome",
+      "type": "chrome",
+      "request": "attach",
+      "port": 9222,
+      "webRoot": "${workspaceFolder}/src",
+      "url": "http://localhost:7878",
+      "sourceMapPathOverrides": {
+        // 对应浏览器 sources下 webpack:/// 的 .目录 和 src目录
+        "webpack://gcls_sys_learn_web/src/*": "${webRoot}/*",
+        "webpack://gcls_sys_learn_web/./src/*.js": "${webRoot}/*.js"
+      }
     }
   ]
 }

+ 112 - 103
package-lock.json

@@ -41,21 +41,21 @@
       "dev": true
     },
     "@babel/core": {
-      "version": "7.19.3",
-      "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/core/-/core-7.19.3.tgz",
-      "integrity": "sha512-WneDJxdsjEvyKtXKsaBGbDeiyOjR5vYq4HcShxnIbG0qixpoHjI3MqeZM9NDvsojNCEBItQE4juOo/bU6e72gQ==",
+      "version": "7.19.6",
+      "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/core/-/core-7.19.6.tgz",
+      "integrity": "sha512-D2Ue4KHpc6Ys2+AxpIx1BZ8+UegLLLE2p3KJEuJRKmokHOtl49jQ5ny1773KsGLZs8MQvBidAF6yWUJxRqtKtg==",
       "dev": true,
       "requires": {
         "@ampproject/remapping": "^2.1.0",
         "@babel/code-frame": "^7.18.6",
-        "@babel/generator": "^7.19.3",
+        "@babel/generator": "^7.19.6",
         "@babel/helper-compilation-targets": "^7.19.3",
-        "@babel/helper-module-transforms": "^7.19.0",
-        "@babel/helpers": "^7.19.0",
-        "@babel/parser": "^7.19.3",
+        "@babel/helper-module-transforms": "^7.19.6",
+        "@babel/helpers": "^7.19.4",
+        "@babel/parser": "^7.19.6",
         "@babel/template": "^7.18.10",
-        "@babel/traverse": "^7.19.3",
-        "@babel/types": "^7.19.3",
+        "@babel/traverse": "^7.19.6",
+        "@babel/types": "^7.19.4",
         "convert-source-map": "^1.7.0",
         "debug": "^4.1.0",
         "gensync": "^1.0.0-beta.2",
@@ -64,18 +64,18 @@
       },
       "dependencies": {
         "@babel/compat-data": {
-          "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==",
+          "version": "7.19.4",
+          "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/compat-data/-/compat-data-7.19.4.tgz",
+          "integrity": "sha512-CHIGpJcUQ5lU9KrPHTjBMhVwQG6CQjxfg36fGXl3qk/Gik1WwWachaXFuo0uCWJT/mStOKtcbFJCaVLihC1CMw==",
           "dev": true
         },
         "@babel/generator": {
-          "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==",
+          "version": "7.19.6",
+          "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/generator/-/generator-7.19.6.tgz",
+          "integrity": "sha512-oHGRUQeoX1QrKeJIKVe0hwjGqNnVYsM5Nep5zo0uE0m42sLH+Fsd2pStJ5sRM1bNyTUUoz0pe2lTeMJrb/taTA==",
           "dev": true,
           "requires": {
-            "@babel/types": "^7.19.3",
+            "@babel/types": "^7.19.4",
             "@jridgewell/gen-mapping": "^0.3.2",
             "jsesc": "^2.5.1"
           }
@@ -103,19 +103,28 @@
           }
         },
         "@babel/helper-module-transforms": {
-          "version": "7.19.0",
-          "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/helper-module-transforms/-/helper-module-transforms-7.19.0.tgz",
-          "integrity": "sha512-3HBZ377Fe14RbLIA+ac3sY4PTgpxHVkFrESaWhoI5PuyXPBBX8+C34qblV9G89ZtycGJCmCI/Ut+VUDK4bltNQ==",
+          "version": "7.19.6",
+          "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/helper-module-transforms/-/helper-module-transforms-7.19.6.tgz",
+          "integrity": "sha512-fCmcfQo/KYr/VXXDIyd3CBGZ6AFhPFy1TfSEJ+PilGVlQT6jcbqtHAM4C1EciRqMza7/TpOUZliuSH+U6HAhJw==",
           "dev": true,
           "requires": {
             "@babel/helper-environment-visitor": "^7.18.9",
             "@babel/helper-module-imports": "^7.18.6",
-            "@babel/helper-simple-access": "^7.18.6",
+            "@babel/helper-simple-access": "^7.19.4",
             "@babel/helper-split-export-declaration": "^7.18.6",
-            "@babel/helper-validator-identifier": "^7.18.6",
+            "@babel/helper-validator-identifier": "^7.19.1",
             "@babel/template": "^7.18.10",
-            "@babel/traverse": "^7.19.0",
-            "@babel/types": "^7.19.0"
+            "@babel/traverse": "^7.19.6",
+            "@babel/types": "^7.19.4"
+          }
+        },
+        "@babel/helper-simple-access": {
+          "version": "7.19.4",
+          "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/helper-simple-access/-/helper-simple-access-7.19.4.tgz",
+          "integrity": "sha512-f9Xq6WqBFqaDfbCzn2w85hwklswz5qsKlh7f08w4Y9yhJHpnNC0QemtSkK5YyOY8kPGvyiwdzZksGUhnGdaUIg==",
+          "dev": true,
+          "requires": {
+            "@babel/types": "^7.19.4"
           }
         },
         "@babel/helper-validator-identifier": {
@@ -125,9 +134,9 @@
           "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==",
+          "version": "7.19.6",
+          "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/parser/-/parser-7.19.6.tgz",
+          "integrity": "sha512-h1IUp81s2JYJ3mRkdxJgs4UvmSsRvDrx5ICSJbPvtWYv5i1nTBGcBpnog+89rAFMwvvru6E5NUHdBe01UeSzYA==",
           "dev": true
         },
         "@babel/template": {
@@ -142,30 +151,30 @@
           }
         },
         "@babel/traverse": {
-          "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==",
+          "version": "7.19.6",
+          "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/traverse/-/traverse-7.19.6.tgz",
+          "integrity": "sha512-6l5HrUCzFM04mfbG09AagtYyR2P0B71B1wN7PfSPiksDPz2k5H9CBC1tcZpz2M8OxbKTPccByoOJ22rUKbpmQQ==",
           "dev": true,
           "requires": {
             "@babel/code-frame": "^7.18.6",
-            "@babel/generator": "^7.19.3",
+            "@babel/generator": "^7.19.6",
             "@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.3",
-            "@babel/types": "^7.19.3",
+            "@babel/parser": "^7.19.6",
+            "@babel/types": "^7.19.4",
             "debug": "^4.1.0",
             "globals": "^11.1.0"
           }
         },
         "@babel/types": {
-          "version": "7.19.3",
-          "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/types/-/types-7.19.3.tgz",
-          "integrity": "sha512-hGCaQzIY22DJlDh9CH7NOxgKkFjBk0Cw9xDO1Xmh2151ti7wiGfQ3LauXzL4HP1fmFlTX6XjpRETTpUcv7wQLw==",
+          "version": "7.19.4",
+          "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/types/-/types-7.19.4.tgz",
+          "integrity": "sha512-M5LK7nAeS6+9j7hAq+b3fQs+pNfUtTGq+yFFfHnauFA8zQtLRfmuipmsKDKKLuyG+wC8ABW43A153YNawNTEtw==",
           "dev": true,
           "requires": {
-            "@babel/helper-string-parser": "^7.18.10",
+            "@babel/helper-string-parser": "^7.19.4",
             "@babel/helper-validator-identifier": "^7.19.1",
             "to-fast-properties": "^2.0.0"
           }
@@ -194,21 +203,21 @@
           }
         },
         "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==",
+          "version": "1.0.30001420",
+          "resolved": "https://repo.huaweicloud.com/repository/npm/caniuse-lite/-/caniuse-lite-1.0.30001420.tgz",
+          "integrity": "sha512-OnyeJ9ascFA9roEj72ok2Ikp7PHJTKubtEJIQ/VK3fdsS50q4KWy+Z5X0A1/GswEItKX0ctAp8n4SYDE7wTu6A==",
           "dev": true
         },
         "electron-to-chromium": {
-          "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==",
+          "version": "1.4.283",
+          "resolved": "https://repo.huaweicloud.com/repository/npm/electron-to-chromium/-/electron-to-chromium-1.4.283.tgz",
+          "integrity": "sha512-g6RQ9zCOV+U5QVHW9OpFR7rdk/V7xfopNXnyAamdpFgCHgZ1sjI8VuR1+zG2YG/TZk+tQ8mpNkug4P8FU0fuOA==",
           "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==",
+          "version": "1.0.10",
+          "resolved": "https://repo.huaweicloud.com/repository/npm/update-browserslist-db/-/update-browserslist-db-1.0.10.tgz",
+          "integrity": "sha512-OztqDenkfFkbSG+tRxBeAnCVPckDBcvibKd35yDONx6OU8N7sqgwc7rCbkJ/WcYtVRZ4ba68d6byhC21GFh7sQ==",
           "dev": true,
           "requires": {
             "escalade": "^3.1.1",
@@ -552,9 +561,9 @@
       }
     },
     "@babel/helper-string-parser": {
-      "version": "7.18.10",
-      "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/helper-string-parser/-/helper-string-parser-7.18.10.tgz",
-      "integrity": "sha512-XtIfWmeNY3i4t7t4D2t02q50HvqHybPqW2ki1kosnvWCwuCMeo81Jf0gwr85jy/neUdg5XDdeFE/80DXiO+njw==",
+      "version": "7.19.4",
+      "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/helper-string-parser/-/helper-string-parser-7.19.4.tgz",
+      "integrity": "sha512-nHtDoQcuqFmwYNYPz3Rah5ph2p8PFeFCsZk9A/48dPc/rGocJ5J3hAAZ7pb76VWX3fZKu+uEr/FhH5jLx7umrw==",
       "dev": true
     },
     "@babel/helper-validator-identifier": {
@@ -594,23 +603,23 @@
       }
     },
     "@babel/helpers": {
-      "version": "7.19.0",
-      "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/helpers/-/helpers-7.19.0.tgz",
-      "integrity": "sha512-DRBCKGwIEdqY3+rPJgG/dKfQy9+08rHIAJx8q2p+HSWP87s2HCrQmaAMMyMll2kIXKCW0cO1RdQskx15Xakftg==",
+      "version": "7.19.4",
+      "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/helpers/-/helpers-7.19.4.tgz",
+      "integrity": "sha512-G+z3aOx2nfDHwX/kyVii5fJq+bgscg89/dJNWpYeKeBv3v9xX8EIabmx1k6u9LS04H7nROFVRVK+e3k0VHp+sw==",
       "dev": true,
       "requires": {
         "@babel/template": "^7.18.10",
-        "@babel/traverse": "^7.19.0",
-        "@babel/types": "^7.19.0"
+        "@babel/traverse": "^7.19.4",
+        "@babel/types": "^7.19.4"
       },
       "dependencies": {
         "@babel/generator": {
-          "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==",
+          "version": "7.19.5",
+          "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/generator/-/generator-7.19.5.tgz",
+          "integrity": "sha512-DxbNz9Lz4aMZ99qPpO1raTbcrI1ZeYh+9NR9qhfkQIbFtVEqotHojEBxHzmxhVONkGt6VyrqVQcgpefMy9pqcg==",
           "dev": true,
           "requires": {
-            "@babel/types": "^7.19.3",
+            "@babel/types": "^7.19.4",
             "@jridgewell/gen-mapping": "^0.3.2",
             "jsesc": "^2.5.1"
           }
@@ -632,9 +641,9 @@
           "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==",
+          "version": "7.19.4",
+          "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/parser/-/parser-7.19.4.tgz",
+          "integrity": "sha512-qpVT7gtuOLjWeDTKLkJ6sryqLliBaFpAtGeqw5cs5giLldvh+Ch0plqnUMKoVAUS6ZEueQQiZV+p5pxtPitEsA==",
           "dev": true
         },
         "@babel/template": {
@@ -649,30 +658,30 @@
           }
         },
         "@babel/traverse": {
-          "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==",
+          "version": "7.19.4",
+          "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/traverse/-/traverse-7.19.4.tgz",
+          "integrity": "sha512-w3K1i+V5u2aJUOXBFFC5pveFLmtq1s3qcdDNC2qRI6WPBQIDaKFqXxDEqDO/h1dQ3HjsZoZMyIy6jGLq0xtw+g==",
           "dev": true,
           "requires": {
             "@babel/code-frame": "^7.18.6",
-            "@babel/generator": "^7.19.3",
+            "@babel/generator": "^7.19.4",
             "@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.3",
-            "@babel/types": "^7.19.3",
+            "@babel/parser": "^7.19.4",
+            "@babel/types": "^7.19.4",
             "debug": "^4.1.0",
             "globals": "^11.1.0"
           }
         },
         "@babel/types": {
-          "version": "7.19.3",
-          "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/types/-/types-7.19.3.tgz",
-          "integrity": "sha512-hGCaQzIY22DJlDh9CH7NOxgKkFjBk0Cw9xDO1Xmh2151ti7wiGfQ3LauXzL4HP1fmFlTX6XjpRETTpUcv7wQLw==",
+          "version": "7.19.4",
+          "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/types/-/types-7.19.4.tgz",
+          "integrity": "sha512-M5LK7nAeS6+9j7hAq+b3fQs+pNfUtTGq+yFFfHnauFA8zQtLRfmuipmsKDKKLuyG+wC8ABW43A153YNawNTEtw==",
           "dev": true,
           "requires": {
-            "@babel/helper-string-parser": "^7.18.10",
+            "@babel/helper-string-parser": "^7.19.4",
             "@babel/helper-validator-identifier": "^7.19.1",
             "to-fast-properties": "^2.0.0"
           }
@@ -3360,9 +3369,9 @@
       }
     },
     "@vue/compiler-sfc": {
-      "version": "2.7.10",
-      "resolved": "https://repo.huaweicloud.com/repository/npm/@vue/compiler-sfc/-/compiler-sfc-2.7.10.tgz",
-      "integrity": "sha512-55Shns6WPxlYsz4WX7q9ZJBL77sKE1ZAYNYStLs6GbhIOMrNtjMvzcob6gu3cGlfpCR4bT7NXgyJ3tly2+Hx8Q==",
+      "version": "2.7.13",
+      "resolved": "https://repo.huaweicloud.com/repository/npm/@vue/compiler-sfc/-/compiler-sfc-2.7.13.tgz",
+      "integrity": "sha512-zzu2rLRZlgIU+OT3Atbr7Y6PG+LW4wVQpPfNRrGDH3dM9PsrcVfa+1pKb8bW467bGM3aDOvAnsYLWVpYIv3GRg==",
       "requires": {
         "@babel/parser": "^7.18.4",
         "postcss": "^8.4.14",
@@ -5253,9 +5262,9 @@
       }
     },
     "core-js": {
-      "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=="
+      "version": "3.25.5",
+      "resolved": "https://repo.huaweicloud.com/repository/npm/core-js/-/core-js-3.25.5.tgz",
+      "integrity": "sha512-nbm6eZSjm+ZuBQxCUPQKQCoUEfFOXjUZ8dTTyikyKaWrTYmAVbykQfwsKE5dBK88u3QCkCrzsx/PPlKfhsvgpw=="
     },
     "core-js-compat": {
       "version": "3.24.0",
@@ -5534,9 +5543,9 @@
       }
     },
     "csstype": {
-      "version": "3.1.0",
-      "resolved": "https://registry.npmjs.org/csstype/-/csstype-3.1.0.tgz",
-      "integrity": "sha512-uX1KG+x9h5hIJsaKR9xHUeUraxf8IODOwq9JLNPq6BwB04a/xgpq3rcx47l5BZu5zBPlgD342tdke3Hom/nJRA=="
+      "version": "3.1.1",
+      "resolved": "https://repo.huaweicloud.com/repository/npm/csstype/-/csstype-3.1.1.tgz",
+      "integrity": "sha512-DJR/VvkAvSZW9bTouZue2sSxDwdTN92uHjqeKVm+0dAqdfNykRzQ95tay8aXMBAAPpUiq4Qcug2L7neoRh2Egw=="
     },
     "currently-unhandled": {
       "version": "0.4.1",
@@ -5611,9 +5620,9 @@
       }
     },
     "dayjs": {
-      "version": "1.11.5",
-      "resolved": "https://repo.huaweicloud.com/repository/npm/dayjs/-/dayjs-1.11.5.tgz",
-      "integrity": "sha512-CAdX5Q3YW3Gclyo5Vpqkgpj8fSdLQcRuzfX6mC6Phy0nfJ0eGYOeS7m4mt2plDWLAtA4TqTakvbboHvUxfe4iA=="
+      "version": "1.11.6",
+      "resolved": "https://repo.huaweicloud.com/repository/npm/dayjs/-/dayjs-1.11.6.tgz",
+      "integrity": "sha512-zZbY5giJAinCG+7AGaw0wIhNZ6J8AhWuSXKvuc1KAyMiRsvGQWqh4L+MomvhdAYjN+lqvVCMq1I41e3YHvXkyQ=="
     },
     "de-indent": {
       "version": "1.0.2",
@@ -6411,9 +6420,9 @@
       }
     },
     "eslint-plugin-vue": {
-      "version": "9.5.1",
-      "resolved": "https://repo.huaweicloud.com/repository/npm/eslint-plugin-vue/-/eslint-plugin-vue-9.5.1.tgz",
-      "integrity": "sha512-Y0sL2RY7Xc9S8kNih9lbwHIDmewUg9bfas6WSzsOWRgDXhIHKxRBZYNAnVcXBFfE+bMWHUA5GLChl7TcTYUI8w==",
+      "version": "9.6.0",
+      "resolved": "https://repo.huaweicloud.com/repository/npm/eslint-plugin-vue/-/eslint-plugin-vue-9.6.0.tgz",
+      "integrity": "sha512-zzySkJgVbFCylnG2+9MDF7N+2Rjze2y0bF8GyUNpFOnT8mCMfqqtLDJkHBuYu9N/psW1A6DVbQhPkP92E+qakA==",
       "dev": true,
       "requires": {
         "eslint-utils": "^3.0.0",
@@ -7405,7 +7414,7 @@
     },
     "gensync": {
       "version": "1.0.0-beta.2",
-      "resolved": "https://registry.npmmirror.com/gensync/-/gensync-1.0.0-beta.2.tgz",
+      "resolved": "https://repo.huaweicloud.com/repository/npm/gensync/-/gensync-1.0.0-beta.2.tgz",
       "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==",
       "dev": true
     },
@@ -12098,9 +12107,9 @@
       "dev": true
     },
     "postcss": {
-      "version": "8.4.17",
-      "resolved": "https://repo.huaweicloud.com/repository/npm/postcss/-/postcss-8.4.17.tgz",
-      "integrity": "sha512-UNxNOLQydcOFi41yHNMcKRZ39NeXlr8AxGuZJsdub8vIb12fHzcq37DTU/QtbI6WLxNg2gF9Z+8qtRwTj1UI1Q==",
+      "version": "8.4.18",
+      "resolved": "https://repo.huaweicloud.com/repository/npm/postcss/-/postcss-8.4.18.tgz",
+      "integrity": "sha512-Wi8mWhncLJm11GATDaQKobXSNEYGUHeQLiQqDFG1qQ5UTDPTEvKw0Xt5NsTpktGTwLps3ByrWsBrG0rB8YQ9oA==",
       "requires": {
         "nanoid": "^3.3.4",
         "picocolors": "^1.0.0",
@@ -14393,9 +14402,9 @@
       }
     },
     "stylelint": {
-      "version": "14.13.0",
-      "resolved": "https://repo.huaweicloud.com/repository/npm/stylelint/-/stylelint-14.13.0.tgz",
-      "integrity": "sha512-NJSAdloiAB/jgVJKxMR90mWlctvmeBFGFVUvyKngi9+j/qPSJ5ZB+u8jOmGbLTnS7OHrII9NFGehPRyar8U5vg==",
+      "version": "14.14.0",
+      "resolved": "https://repo.huaweicloud.com/repository/npm/stylelint/-/stylelint-14.14.0.tgz",
+      "integrity": "sha512-yUI+4xXfPHVnueYddSQ/e1GuEA/2wVhWQbGj16AmWLtQJtn28lVxfS4b0CsWyVRPgd3Auzi0NXOthIEUhtQmmA==",
       "dev": true,
       "requires": {
         "@csstools/selector-specificity": "^2.0.2",
@@ -14421,7 +14430,7 @@
         "micromatch": "^4.0.5",
         "normalize-path": "^3.0.0",
         "picocolors": "^1.0.0",
-        "postcss": "^8.4.16",
+        "postcss": "^8.4.17",
         "postcss-media-query-parser": "^0.2.3",
         "postcss-resolve-nested-selector": "^0.1.1",
         "postcss-safe-parser": "^6.0.0",
@@ -14559,9 +14568,9 @@
           }
         },
         "semver": {
-          "version": "7.3.7",
-          "resolved": "https://repo.huaweicloud.com/repository/npm/semver/-/semver-7.3.7.tgz",
-          "integrity": "sha512-QlYTucUYOews+WeEujDoEGziz4K6c47V/Bd+LjSSYcA94p+DmINdf7ncaUinThfvZyu13lN9OY1XDxt8C0Tw0g==",
+          "version": "7.3.8",
+          "resolved": "https://repo.huaweicloud.com/repository/npm/semver/-/semver-7.3.8.tgz",
+          "integrity": "sha512-NB1ctGL5rlHrPJtFDVIVzTyQylMLu9N9VICA6HSFJo8MCGVTMW6gfpicwKmmK/dAjTOrqu5l63JJOpDSrAis3A==",
           "dev": true,
           "requires": {
             "lru-cache": "^6.0.0"
@@ -15898,11 +15907,11 @@
       }
     },
     "vue": {
-      "version": "2.7.10",
-      "resolved": "https://repo.huaweicloud.com/repository/npm/vue/-/vue-2.7.10.tgz",
-      "integrity": "sha512-HmFC70qarSHPXcKtW8U8fgIkF6JGvjEmDiVInTkKZP0gIlEPhlVlcJJLkdGIDiNkIeA2zJPQTWJUI4iWe+AVfg==",
+      "version": "2.7.13",
+      "resolved": "https://repo.huaweicloud.com/repository/npm/vue/-/vue-2.7.13.tgz",
+      "integrity": "sha512-QnM6ULTNnPmn71eUO+4hdjfBIA3H0GLsBnchnI/kS678tjI45GOUZhXd0oP/gX9isikXz1PAzSnkPspp9EUNfQ==",
       "requires": {
-        "@vue/compiler-sfc": "2.7.10",
+        "@vue/compiler-sfc": "2.7.13",
         "csstype": "^3.1.0"
       }
     },
@@ -16059,9 +16068,9 @@
       }
     },
     "vue-template-compiler": {
-      "version": "2.7.10",
-      "resolved": "https://repo.huaweicloud.com/repository/npm/vue-template-compiler/-/vue-template-compiler-2.7.10.tgz",
-      "integrity": "sha512-QO+8R9YRq1Gudm8ZMdo/lImZLJVUIAM8c07Vp84ojdDAf8HmPJc7XB556PcXV218k2AkKznsRz6xB5uOjAC4EQ==",
+      "version": "2.7.13",
+      "resolved": "https://repo.huaweicloud.com/repository/npm/vue-template-compiler/-/vue-template-compiler-2.7.13.tgz",
+      "integrity": "sha512-jYM6TClwDS9YqP48gYrtAtaOhRKkbYmbzE+Q51gX5YDr777n7tNI/IZk4QV4l/PjQPNh/FVa/E92sh/RqKMrog==",
       "dev": true,
       "requires": {
         "de-indent": "^1.0.2",

+ 9 - 9
package.json

@@ -18,8 +18,8 @@
     "awe-dnd": "^0.3.4",
     "axios": "^0.27.2",
     "book-ui": "file:../book-ui-0.3.15.tgz",
-    "core-js": "^3.25.3",
-    "dayjs": "^1.11.5",
+    "core-js": "^3.25.5",
+    "dayjs": "^1.11.6",
     "element-ui": "^2.15.10",
     "gcls-book-question-ui": "file:../gcls-book-question-ui-0.1.0.tgz",
     "jquery": "^3.6.1",
@@ -30,7 +30,7 @@
     "normalize.css": "^8.0.1",
     "nprogress": "^0.2.0",
     "tinymce": "^5.10.5",
-    "vue": "^2.7.10",
+    "vue": "^2.7.13",
     "vue-i18n": "^8.27.2",
     "vue-pdf": "^4.3.0",
     "vue-router": "^3.6.5",
@@ -38,7 +38,7 @@
     "vuex": "^3.6.2"
   },
   "devDependencies": {
-    "@babel/core": "^7.19.3",
+    "@babel/core": "^7.19.6",
     "@babel/eslint-parser": "^7.19.1",
     "@rushstack/eslint-patch": "^1.2.0",
     "@vue/cli-plugin-babel": "~5.0.8",
@@ -55,25 +55,25 @@
     "compression-webpack-plugin": "^6.1.1",
     "eslint": "^7.32.0",
     "eslint-plugin-prettier": "^4.2.1",
-    "eslint-plugin-vue": "^9.5.1",
+    "eslint-plugin-vue": "^9.6.0",
     "html-webpack-plugin": "^5.5.0",
-    "postcss": "^8.4.17",
+    "postcss": "^8.4.18",
     "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.13.0",
+    "stylelint": "^14.14.0",
     "stylelint-config-prettier": "^9.0.3",
     "stylelint-config-recess-order": "^3.0.0",
     "stylelint-config-recommended-vue": "^1.4.0",
     "stylelint-config-standard-scss": "^5.0.0",
-    "stylelint-declaration-block-no-ignored-properties": "^2.5.0",
+    "stylelint-declaration-block-no-ignored-properties": "^2.6.0",
     "stylelint-webpack-plugin": "^3.3.0",
     "svg-sprite-loader": "^6.0.11",
     "svgo": "^2.8.0",
     "vue-loader": "^15.10.0",
-    "vue-template-compiler": "^2.7.10",
+    "vue-template-compiler": "^2.7.13",
     "vue-demi": "^0.13.11"
   },
   "browserslist": [

+ 3 - 4
src/components/select/SelectCourse.vue

@@ -105,10 +105,6 @@ export default {
     curBook() {
       this.GetBookChapterStruct();
     },
-    cs_item_id(newVal) {
-      if (newVal?.length <= 0) return;
-      this.getCourseBookListByCSItemID();
-    },
     dialogVisible(newVal) {
       if (newVal && this.book_list.length === 0) {
         this.$message.warning(this.$i18n.t('Key383'));
@@ -116,6 +112,9 @@ export default {
       }
     }
   },
+  created() {
+    this.getCourseBookListByCSItemID();
+  },
   methods: {
     dialogClose() {
       this.$emit('dialogClose');

+ 10 - 7
src/router/index.js

@@ -85,18 +85,21 @@ const routes = [
         component: () =>
           import(/* webpackChunkName: 'create_course'*/ '@/views/teacher/create_course/step_table/SelectBook')
       },
+      /* 旧创建课节 开始 */
       // 分步表单 -> 第三步
-      // {
-      //   path: '/create_course_step_table/create_task/:id',
-      //   component: () =>
-      //     import(/* webpackChunkName: 'create_course'*/ '@/views/teacher/create_course/step_table/CreateTask')
-      // },
-      // 分步表单 -> 第步 -> 新建课节任务
+      {
+        path: '/create_course_step_table/old_create_task/:id',
+        component: () =>
+          import(/* webpackChunkName: 'old_create_course'*/ '@/views/teacher/create_course/step_table/CreateTask')
+      },
+      // 分步表单 -> 第步 -> 新建课节任务
       {
         path: '/create_course_step_table/new_task/:time_type/:id/:curItemID',
         component: () =>
-          import(/* webpackChunkName: 'create_course'*/ '@/views/teacher/create_course/step_table/NewTask')
+          import(/* webpackChunkName: 'old_create_course'*/ '@/views/teacher/create_course/step_table/NewTask')
       },
+      /* 旧创建课节 结束 */
+      /* 新创建课节 */
       // 分步表单 -> 第三步
       {
         path: '/create_course_step_table/create_task/:id',

+ 1 - 0
src/store/data.js

@@ -0,0 +1 @@
+export let taskModeList = ['Simple', 'Multilayer'];

+ 4 - 1
src/store/modules/app.js

@@ -1,5 +1,7 @@
 import { app } from '@/store/mutation-types';
 import { getConfig } from '@/utils/auth';
+import { taskModeList } from '../data';
+import { isAbnormalVal } from '@/utils/validate';
 
 const liveStorage = 'liveDevice';
 
@@ -25,7 +27,8 @@ const getDefaultSate = () => {
       logo_image_url_home: isHas ? token.logo_image_url_home : '',
       title_icon_url: isHas ? token.title_icon_url : '',
       sys_type: isHas ? token.sys_type : '',
-      doc_preview_service_address: isHas ? token.doc_preview_service_address : ''
+      doc_preview_service_address: isHas ? token.doc_preview_service_address : '',
+      task_mode: isHas && isAbnormalVal(token.task_mode) ? token.task_mode : taskModeList[1]
     }
   };
 };

+ 15 - 0
src/utils/filter.js

@@ -1,3 +1,4 @@
+import { isAbnormalVal } from './validate';
 /**
  * 得到文件类型
  * @param {String} fileName
@@ -6,3 +7,17 @@
 export function getFileType(fileName) {
   return fileName.slice(fileName.lastIndexOf('.') + 1, fileName.length);
 }
+
+/**
+ * @description 根据某个属性的值得到数组中对应对象中的另一个值
+ * @param {Array} arr
+ * @param {String} inputProp
+ * @param {*} inputValue
+ * @param {*} outputProp
+ */
+export function accordingArrayPropGetProp(arr, inputProp, inputValue, outputProp) {
+  if (isAbnormalVal(arr) || isAbnormalVal(inputProp) || isAbnormalVal(inputValue) || isAbnormalVal(outputProp)) {
+    return '';
+  }
+  return arr.find((item) => item[inputProp] === inputValue)[outputProp];
+}

+ 30 - 0
src/utils/list.js

@@ -0,0 +1,30 @@
+import { ref } from 'vue';
+
+/**
+ * 列表公用数据和方法
+ */
+export function useList(capacity = 10) {
+  let page_capacity = ref(capacity); // 每页容量
+  let cur_page = ref(1); // 当前页
+  let total_count = ref(0); // 总记录数
+  let list = ref([]); // 列表
+
+  function changePage(newPage, fn) {
+    cur_page.value = newPage;
+    fn();
+  }
+
+  function changePageSize(pageSize, fn) {
+    page_capacity.value = pageSize;
+    fn();
+  }
+
+  return {
+    page_capacity,
+    cur_page,
+    total_count,
+    list,
+    changePage,
+    changePageSize
+  };
+}

+ 9 - 0
src/utils/validate.js

@@ -10,6 +10,15 @@ export function isExternal(path) {
 }
 
 /**
+ * @description 是否正常的值
+ * @param {*} val
+ * @returns {Boolean}
+ */
+export function isAbnormalVal(val) {
+  return val === undefined || val === null;
+}
+
+/**
  * @description 只允许输入两位小数
  * @param {String} value
  * @returns { Number }

+ 10 - 8
src/views/teacher/create_course/step_table/NewTask.vue

@@ -230,25 +230,27 @@
     </div>
 
     <!--选择课件-->
-    <select-course
-      :id="cs_item_id"
-      :dialog-visible="dialogVisible"
-      @selectCourse="selectCourse"
-      @dialogClose="dialogClose"
-    />
+    <SelectCourse :dialog-visible="dialogVisible" @selectCourse="selectCourse" @dialogClose="dialogClose" />
   </div>
 </template>
 
 <script>
-import SelectCourse from '@/components/select/SelectCourse.vue';
 import { GetTaskTeachingTypeList, GetTaskModeList, GetTeacherListByCourseID, GetCourseStudentList } from '@/api/select';
 import { AddTaskToCSItem, GetTreeNodeInfo_BookChapterStruct, GetTaskInfo, UpdateTask, GetCSItem } from '@/api/course';
 import { fileUpload } from '@/api/app';
 import { fileTypeSizeLimit } from '@/utils/validate';
+import { taskModeList } from '@/store/data';
+
+import SelectCourse from '@/components/select/SelectCourse.vue';
 
 export default {
   name: 'NewTask',
   components: { SelectCourse },
+  provide() {
+    return {
+      cs_item_id: this.cs_item_id
+    };
+  },
   data() {
     const validateTeacher = (rule, value, callback) => {
       if (this.form.teaching_type === 10 && !value && !this.is_template) {
@@ -437,7 +439,7 @@ export default {
 
     goBack() {
       this.$router.push({
-        path: `/create_course_step_table/create_task/${this.id}?is_template=${this.is_template}&curItemID=${this.curItemID}`
+        path: `/create_course_step_table/old_create_task/${this.id}?is_template=${this.is_template}&curItemID=${this.curItemID}`
       });
     },
 

+ 74 - 73
src/views/teacher/create_course/step_table/SelectBook.vue

@@ -28,7 +28,7 @@
         <div class="book">
           <div class="book-list">
             <div
-              v-for="{ is_selected, is_buy, id: book_id, picture_url, name } in book_list"
+              v-for="{ is_selected, is_buy, id: book_id, picture_url, name } in list"
               :key="book_id"
               class="book-list-item"
             >
@@ -49,9 +49,9 @@
               :total="total_count"
               :current-page="cur_page"
               :page-size="page_capacity"
-              @prev-click="changePage"
-              @next-click="changePage"
-              @current-change="changePage"
+              @prev-click="changePage(queryBookList)"
+              @next-click="changePage(queryBookList)"
+              @current-change="changePage(queryBookList)"
             />
             <div>
               <el-button class="prev-step" @click="prevCourseInfo">
@@ -69,79 +69,80 @@
 </template>
 
 <script>
-import StepBar from '@/components/StepBar.vue';
+export default {
+  name: 'SelectBook'
+};
+</script>
+
+<script setup>
+import { inject, ref } from 'vue';
 import { PageQueryBookList_SelectBookForCourse } from '@/api/list';
 import { AddBookToCourse, RemoveBookFromCourse, GetCourseBookInfo_Brief } from '@/api/course';
+import { taskModeList } from '@/store/data';
+import { useRoute, useRouter } from 'vue-router/composables';
+import { useList } from '@/utils/list';
+import { Message } from 'element-ui';
 
-export default {
-  name: 'SelectBook',
-  components: {
-    StepBar
-  },
-  data() {
-    return {
-      id: this.$route.params.id,
-      is_template: 'is_template' in this.$route.query ? this.$route.query.is_template === 'true' : false,
-      search: '',
-      page_capacity: 14,
-      cur_page: 1,
-      total_count: 0,
-      book_list: []
-    };
-  },
-  created() {
-    this.queryBookList();
-  },
-  methods: {
-    queryBookList() {
-      PageQueryBookList_SelectBookForCourse({
-        name: this.search,
-        course_id: this.id,
-        page_capacity: this.page_capacity,
-        cur_page: this.cur_page
-      }).then(({ book_list, cur_page, total_count }) => {
-        this.cur_page = cur_page;
-        this.total_count = total_count;
-        this.book_list = book_list;
-      });
-    },
-    changePage(newPage) {
-      this.cur_page = newPage;
-      this.queryBookList();
-    },
-    addOrRemoveBookToCourse(book_id, is_selected, is_buy) {
-      if (is_selected === 'false' && is_buy === 'false') {
-        return this.$message.warning('请先购买本教材!');
-      }
-      const data = {
-        course_id: this.id,
-        book_id
-      };
-      const bookIndex = this.book_list.findIndex(({ id }) => id === book_id);
-      if (is_selected === 'true') {
-        RemoveBookFromCourse(data).then(() => {
-          this.$message.success(this.$i18n.t('Key350'));
-          this.book_list[bookIndex].is_selected = 'false';
-        });
-      } else {
-        AddBookToCourse(data).then(() => {
-          this.$message.success(this.$i18n.t('Key351'));
-          this.book_list[bookIndex].is_selected = 'true';
-        });
-      }
-    },
-    prevCourseInfo() {
-      this.$router.push(`/create_course_step_table/course_info?id=${this.id}&is_template=${this.is_template}`);
-    },
-    nextStep() {
-      GetCourseBookInfo_Brief({ course_id: this.id }).then(({ book_count }) => {
-        this.$router.push({
-          path: `/create_course_step_table/create_task/${this.id}?is_template=${this.is_template}`
-        });
-      });
-    }
+import store from '@/store';
+import StepBar from '@/components/StepBar.vue';
+
+const route = useRoute();
+const router = useRouter();
+const $t = inject('$t');
+let id = route.params.id;
+let is_template = 'is_template' in route.query ? route.query.is_template === 'true' : false;
+let search = ref('');
+const { page_capacity, cur_page, total_count, list, changePage } = useList(14);
+
+function queryBookList() {
+  PageQueryBookList_SelectBookForCourse({
+    name: search.value,
+    course_id: id,
+    page_capacity: page_capacity.value,
+    cur_page: cur_page.value
+  }).then(({ book_list, cur_page: page, total_count: total }) => {
+    cur_page.value = page;
+    total_count.value = total;
+    list.value = book_list;
+  });
+}
+queryBookList();
+
+function addOrRemoveBookToCourse(book_id, is_selected, is_buy) {
+  if (is_selected === 'false' && is_buy === 'false') {
+    return Message.warning('请先购买本教材!');
   }
-};
+  const data = {
+    course_id: id,
+    book_id
+  };
+  const bookIndex = list.value.findIndex(({ id }) => id === book_id);
+  if (is_selected === 'true') {
+    RemoveBookFromCourse(data).then(() => {
+      Message.success($t('Key350'));
+      list.value[bookIndex].is_selected = 'false';
+    });
+  } else {
+    AddBookToCourse(data).then(() => {
+      Message.success($t('Key351'));
+      list.value[bookIndex].is_selected = 'true';
+    });
+  }
+}
+
+function prevCourseInfo() {
+  router.push(`/create_course_step_table/course_info?id=${id}&is_template=${is_template}`);
+}
+
+function nextStep() {
+  GetCourseBookInfo_Brief({ course_id: id }).then(() => {
+    router.push({
+      path: `/create_course_step_table/${
+        store.state.app.config.task_mode === taskModeList[1] ? 'create_task' : 'old_create_task'
+      }/${id}?is_template=${is_template}`
+    });
+  });
+}
 </script>
 
 <style lang="scss">

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

@@ -73,7 +73,7 @@ const disabled = ref(false);
 const placeholder = computed(() => (disabled.value ? '' : '点击输入'));
 const richTextHeight = 148;
 
-let { task_intro } = taskData.value;
+let task_intro = computed(() => taskData.value.task_intro);
 </script>
 
 <style lang="scss" scoped>

+ 1 - 1
src/views/teacher/create_course/step_table/create_task/components/task_template/components/AudioShow.vue → src/views/teacher/create_course/step_table/create_task/components/common/AudioShow.vue

@@ -34,7 +34,7 @@ export default {
 <script setup>
 import { ref, watch } from 'vue';
 import { secondFormatConversion } from '@/utils/index';
-import { usePlay } from './play';
+import { usePlay } from '../task_template/components/play';
 
 const props = defineProps({
   fileId: {

+ 2 - 1
src/views/teacher/create_course/step_table/create_task/components/task_template/components/VideoShow.vue → src/views/teacher/create_course/step_table/create_task/components/common/VideoShow.vue

@@ -4,6 +4,7 @@
       ref="ref_video"
       :src="url"
       preload="metadata"
+      @click="state.playing ? pause() : ''"
       @play="onPlay"
       @pause="onPause"
       @ended="onEnded"
@@ -24,7 +25,7 @@ export default {
 
 <script setup>
 import { ref, watch } from 'vue';
-import { usePlay } from './play';
+import { usePlay } from '../task_template/components/play';
 
 const props = defineProps({
   fileId: {

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

@@ -3,7 +3,6 @@ import { addTaskList } from './TaskData';
 
 // 任务教学类型
 export const MORE_NAME = 'more';
-
 export const taskClassify = [
   {
     type: 'basic',
@@ -44,13 +43,23 @@ export const taskClassify = [
 
 /**
  * 获取教学类型某个属性对应值
- * @param {String} type
+ * @param {Number} teaching_type
  * @param {String} propName
  * @returns {String}
  */
-export function getTaskClassifyAttr(type, propName = 'name') {
-  if (!type) return '';
-  return taskClassify.find((item) => item.type === type)[propName];
+export function getTaskClassifyAttr(teaching_type, propName = 'name') {
+  if (teaching_type === undefined) return;
+  return taskClassify.find((item) => item.teaching_type === Number(teaching_type))[propName];
+}
+
+/**
+ * 得到子任务信息块类型对应的 teaching_type
+ * @param {String} info_block_type 信息块类型
+ * @returns {String | Number}
+ */
+export function getInfoBlockTypeCorrespondingTeachingType(info_block_type) {
+  if (!info_block_type || info_block_type === MORE_NAME) return '';
+  return taskClassify.find(({ type }) => type === info_block_type)['teaching_type'];
 }
 
 /**
@@ -73,19 +82,22 @@ export function useSelectTaskClassify(curTaskTypeObj) {
   });
 
   // 添加任务
-  function addTask(taskClassifyType) {
-    addTaskList(curTaskTypeObj.value.sidebarListName, taskClassifyType);
+  function addTask(teaching_type) {
+    addTaskList(curTaskTypeObj.value.sidebarListName, teaching_type);
   }
+  provide('addTask', addTask);
 
-  function selectTaskClassify(taskClassifyType) {
-    if (!taskClassifyType) return;
+  /**
+   * 选择任务教学类型
+   * @param {Number} teaching_type 任务教学类型
+   */
+  function selectTaskClassify(teaching_type) {
+    if (!teaching_type) return;
     visible.value = false;
-    selectFn ? selectFn(taskClassifyType) : addTask(taskClassifyType);
+    selectFn ? selectFn(teaching_type) : addTask(teaching_type);
     if (selectFn) selectFn = null;
   }
 
-  provide('addTask', addTask);
-
   return {
     visible,
     isAddSubtask,

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

@@ -76,7 +76,7 @@ export const subtaskInfoBlock = [
 // 互动消息列表中可能的项
 export const messageItem = [
   {
-    message_type: 'message',
+    message_type: 'text',
     text: ''
   },
   {
@@ -91,7 +91,7 @@ export const messageItem = [
 
 // 添加任务
 export function addTaskList(list_name, teaching_type) {
-  taskData.value[list_name].push(JSON.parse(JSON.stringify({ ...taskObject, teaching_type })));
+  taskData.value[list_name].push(JSON.parse(JSON.stringify({ ...taskObject, teaching_type: Number(teaching_type) })));
 }
 
 const filterList = ['child_task_list', 'info_block_list', 'message_list']; // 需要过滤的数组属性
@@ -113,6 +113,15 @@ function filterTempProps(arr) {
 
 // 保存任务列表到课节
 export function saveCSItem(id) {
+  if (
+    taskData.value.pre_task_list.some(({ begin_time }) => begin_time === null || begin_time.length <= 0) ||
+    taskData.value.mid_task_list.some(({ begin_time }) => begin_time === null || begin_time.length <= 0) ||
+    taskData.value.after_task_list.some(({ begin_time }) => begin_time === null || begin_time.length <= 0)
+  ) {
+    Message.warning('请填写开始时间');
+    return;
+  }
+
   const data = {
     cs_item_id: id,
     task_intro: taskData.value.task_intro,

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

@@ -1,5 +1,5 @@
 import { computed, ref, provide } from 'vue';
-import { useScale } from '../util/wheel';
+import { useScale } from '../utils/wheel';
 
 import TaskExplain from '../TaskExplain/index.vue';
 import TaskTemplate from '../task_template/index.vue';
@@ -50,6 +50,7 @@ export const taskTypeArray = [
     sidebarListName: 'after_task_list'
   }
 ];
+
 // 页面状态
 export const pageStateList = ['editor', 'preview'];
 export let curPageState = ref(pageStateList[0]); // 当前页面状态

+ 42 - 27
src/views/teacher/create_course/step_table/create_task/components/layouts/LeftSidebar.vue

@@ -7,19 +7,25 @@
           {{ taskNumAndSubtaskNum(type) }}
         </span>
       </div>
-      <div v-if="type !== TASK_EXPLAIN && CSItemInfo" :key="`${type}-task`" class="task-list">
-        <template v-for="({ id: taskId, name: taskName, child_task_list }, index) in CSItemInfo[sidebarListName]">
-          <div :key="taskId" :class="['task-list-item', curTaskId === taskId ? 'active' : '']">
-            <span class="task-index">{{ index + 1 }}.</span>
-            <span class="task-name" @click="setCurTaskId(taskId)">{{ taskName }}</span>
+      <!-- 导航栏任务列表 -->
+      <div v-if="type !== TASK_EXPLAIN" :key="`${type}-task`" class="task-list">
+        <template v-for="({ name: taskName, child_task_list }, index) in taskData[sidebarListName]">
+          <div
+            :key="`sidebar-task-${index}`"
+            :class="['task-list-item', curSelectMark === `${type}-${index}` ? 'active' : '']"
+          >
+            <span class="task-index" @click="setSelectMark(type, index)">{{ index + 1 }}.</span>
+            <span class="task-name" @click="setSelectMark(type, index)">{{ taskName }}</span>
           </div>
           <div
-            v-for="({ id: subtaskId, name: subtaskName }, subtask_index) in child_task_list"
-            :key="subtaskId"
-            :class="['task-list-subtask', curTaskId === subtaskId ? 'active' : '']"
+            v-for="({ name: subtaskName }, subtask_index) in child_task_list"
+            :key="`sidebar-subtask-${subtask_index}`"
+            :class="['task-list-subtask', curSelectMark === `${type}-${index}-${subtask_index}` ? 'active' : '']"
           >
-            <span class="task-index">{{ `${index + 1}.${subtask_index + 1}` }}</span>
-            <span class="task-name" @click="setCurTaskId(subtaskId)">{{ subtaskName }}</span>
+            <span class="task-index" @click="setSelectMark(type, index, subtask_index)">
+              {{ `${index + 1}.${subtask_index + 1}` }}
+            </span>
+            <span class="task-name" @click="setSelectMark(type, index, subtask_index)">{{ subtaskName }}</span>
           </div>
         </template>
       </div>
@@ -34,11 +40,11 @@ export default {
 </script>
 
 <script setup>
-import { ref } from 'vue';
+import { nextTick, ref } from 'vue';
 import { TASK_EXPLAIN, taskTypeArray } from '../data/TaskType.js';
 import { taskData } from '../data/TaskData';
 
-defineProps({
+const props = defineProps({
   curTaskType: {
     type: String,
     required: true
@@ -53,23 +59,27 @@ defineProps({
   }
 });
 
-let curTaskId = ref('');
-
-function setCurTaskId(id) {
-  curTaskId.value = id;
+let curSelectMark = ref(''); // 当前选中任务标记
+/**
+ * 处理选中任务导航事件
+ */
+function setSelectMark(type, index, subtask_index = -1) {
+  curSelectMark.value = `${type}-${index}${subtask_index >= 0 ? `-${subtask_index}` : ''}`;
+  if (type !== props.curTaskType) {
+    props.changeTaskType(type);
+  }
+  nextTick(() => {
+    document.querySelector(`.${curSelectMark.value}`)?.scrollIntoView({ behavior: 'smooth' });
+  });
 }
 
-let CSItemInfo = ref(null);
-
 // 任务数量和子任务数量显示名
 function taskNumAndSubtaskNum(taskType) {
-  let childTaskNum = 0;
-  const index = taskTypeArray.findIndex(({ type }) => type === taskType);
-  taskData.value[taskTypeArray[index].sidebarListName].forEach(({ child_task_list }) => {
-    childTaskNum += child_task_list.length;
-  });
-  let listNum = taskData.value[taskTypeArray[index].sidebarListName].length;
-  return listNum > 0 ? `${taskData.value[taskTypeArray[index].sidebarListName].length}/${childTaskNum}` : 0;
+  const sidebarList =
+    taskData.value[taskTypeArray[taskTypeArray.findIndex(({ type }) => type === taskType)]['sidebarListName']];
+  return sidebarList.length > 0
+    ? `${sidebarList.length}/${sidebarList.reduce((pre, { child_task_list }) => pre + child_task_list.length, 0)}`
+    : 0;
 }
 </script>
 
@@ -77,7 +87,7 @@ function taskNumAndSubtaskNum(taskType) {
 $basic-background-color: #f7f7f7;
 
 .left-sidebar {
-  width: 154px;
+  width: 180px;
   padding: 16px 0 16px 8px;
   overflow: auto;
   background-color: $basic-background-color;
@@ -106,7 +116,7 @@ $basic-background-color: #f7f7f7;
   .after-task {
     display: flex;
     justify-content: space-between;
-    width: 146px;
+    width: 172px;
     height: 28px;
     padding: 2px 8px;
     margin-top: 8px;
@@ -134,6 +144,10 @@ $basic-background-color: #f7f7f7;
       column-gap: 8px;
       padding: 3px 16px;
 
+      .task-index {
+        cursor: pointer;
+      }
+
       .task-name {
         word-break: break-all;
         cursor: pointer;
@@ -150,6 +164,7 @@ $basic-background-color: #f7f7f7;
 
       .task-index {
         margin-left: 16px;
+        cursor: pointer;
       }
     }
   }

+ 3 - 3
src/views/teacher/create_course/step_table/create_task/components/layouts/RightSidebar.vue

@@ -1,7 +1,7 @@
 <template>
   <div class="task-main-right">
     <div
-      v-for="{ type, name, image } in taskClassify"
+      v-for="{ type, name, image, teaching_type } in taskClassify"
       :key="type"
       :style="{ cursor: type !== MORE_NAME ? 'pointer' : 'default' }"
       class="task-classify-item"
@@ -11,7 +11,7 @@
         :src="image"
         :alt="name"
         :draggable="type !== MORE_NAME"
-        @dragstart="onDragstart($event, type)"
+        @dragstart="onDragstart($event, teaching_type)"
         @dragend="onDragend"
       />
     </div>
@@ -26,7 +26,7 @@ export default {
 
 <script setup>
 import { MORE_NAME, taskClassify } from '../data/TaskClassify';
-import { useDrag, customTypeList } from '../util/drag.js';
+import { useDrag, customTypeList } from '../utils/drag.js';
 
 const { onDragstart, onDragend } = useDrag(true, customTypeList[0]);
 </script>

+ 4 - 4
src/views/teacher/create_course/step_table/create_task/components/layouts/TaskEditor.vue

@@ -46,8 +46,8 @@ export default {
 import { computed, provide, ref } from 'vue';
 import { useTaskType } from '../data/TaskType.js';
 import { useSelectTaskClassify } from '../data/TaskClassify';
-import { useMouseEvent } from '../util/mouseEvent.js';
-import { useScale, useWheel, scale } from '../util/wheel.js';
+import { useMouseEvent } from '../utils/mouseEvent.js';
+import { useScale, useWheel, scale } from '../utils/wheel.js';
 import { taskData } from '../data/TaskData';
 import { useSelectCourseware } from '../task_template/components/courseware';
 import { videoRecording } from '../../components/task_template/components/recording.js';
@@ -79,11 +79,11 @@ useWheel(center);
 const { changeScale } = useScale();
 
 // 当前任务模板列表(课前、课中、课后)
-let curTaskTemplateList = computed(() => {
+let curTaskDataList = computed(() => {
   if (!curTaskTypeObj.value.sidebarListName) return [];
   return taskData.value[curTaskTypeObj.value.sidebarListName];
 });
-provide('curTaskTemplateList', curTaskTemplateList);
+provide('curTaskDataList', curTaskDataList);
 
 // 录像页面显示
 const { visible_video, sendVideo } = videoRecording();

+ 10 - 9
src/views/teacher/create_course/step_table/create_task/components/layouts/create_csitem/index.vue

@@ -33,20 +33,21 @@ import { inject, ref } from 'vue';
 
 import FillName from '../../pop-up/FillName.vue';
 
-const props = defineProps({
-  getCSItemList: {
-    type: Function,
-    required: true
-  }
-});
+const emits = defineEmits(['getCSItemList']);
+
 let visible = ref(false);
 
 let id = inject('id');
-
+let { beginDate, endDate } = inject('CSItemDate');
 function addCSItem(name) {
   visible.value = false;
-  AddCSItemToCourse({ course_id: id }).then(() => {
-    props.getCSItemList();
+  AddCSItemToCourse({
+    begin_time: `${beginDate.value} 00:00:00`,
+    end_time: `${endDate.value} 23:59:59`,
+    course_id: id,
+    name
+  }).then(() => {
+    emits('getCSItemList');
   });
 }
 </script>

+ 2 - 2
src/views/teacher/create_course/step_table/create_task/components/pop-up/SelectTaskClassify.vue

@@ -11,9 +11,9 @@
     "
     :modal="false"
   >
-    <template v-for="{ type, image, name } in taskClassify">
+    <template v-for="{ type, teaching_type, image, name } in taskClassify">
       <div v-if="getSubtaskIsShow(type)" :key="type" :class="['task-classify']">
-        <div class="task-classify-item" @click="selectTaskClassify(type)">
+        <div class="task-classify-item" @click="selectTaskClassify(teaching_type)">
           <div class="taskClassify-name">
             {{ name }}
           </div>

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

@@ -1,11 +1,11 @@
 <template>
   <div>
     <template v-if="isMindMapping">
-      <MindMapping />
+      <MindMapping :cur-task-data-list="curTaskDataList" />
     </template>
     <template v-else>
       <TaskPreview
-        v-for="(item, i) in curTaskTemplateList"
+        v-for="(item, i) in curTaskDataList"
         :key="`${curTaskTypeObj.sidebarListName}-${i}`"
         :list-name="curTaskTypeObj.sidebarListName"
         :task-index="i"
@@ -21,6 +21,6 @@ import { isMindMapping } from '../data/TaskType';
 import MindMapping from './mind_mapping/index.vue';
 import TaskPreview from './task_preview/index.vue';
 
-const curTaskTemplateList = inject('curTaskTemplateList');
+const curTaskDataList = inject('curTaskDataList');
 const curTaskTypeObj = inject('curTaskTypeObj');
 </script>

+ 184 - 3
src/views/teacher/create_course/step_table/create_task/components/preview/mind_mapping/index.vue

@@ -1,7 +1,188 @@
 <template>
-  <div></div>
+  <div class="mind">
+    <div class="mind-left">
+      <div
+        v-for="(li, i) in UpperHalf"
+        :key="`mind-${i}`"
+        :class="[
+          'task-item',
+          i < Math.ceil(UpperHalf.length / 2) - 1 ? 'mind-down' : '',
+          i === Math.ceil(UpperHalf.length / 2) - 1 ? 'mind-center' : '',
+          i > Math.ceil(UpperHalf.length / 2) - 1 ? 'mind-up' : ''
+        ]"
+      >
+        <span :class="['task-item-name']" :data-index="i + 1">
+          <span class="nowrap-ellipsis" :title="li.name">{{ li.name }}</span>
+        </span>
+        <span class="task-item-date">{{ previewDateTransform(li.begin_time, li.duration_second) }}</span>
+      </div>
+    </div>
+    <div class="main-wrap">
+      <div class="csitem-name">{{ curCSItemName }}</div>
+    </div>
+    <div class="mind-right">
+      <template v-for="(li, i) in LowerHalf">
+        <div :key="`mind-${i}`" class="task-item">
+          <span class="task-item-name" :data-index="i + median + 2">
+            <span>{{ li.name }}</span>
+          </span>
+          <span class="task-item-date">{{ previewDateTransform(li.begin_time, li.duration_second) }}</span>
+        </div>
+      </template>
+    </div>
+  </div>
 </template>
 
-<script setup></script>
+<script>
+export default {
+  name: 'MindMapping'
+};
+</script>
 
-<style lang="scss" scoped></style>
+<script setup>
+import { computed, inject } from 'vue';
+import { previewDateTransform } from '../../utils/preview';
+
+const props = defineProps({
+  curTaskDataList: {
+    type: Array,
+    required: true
+  }
+});
+let median = computed(() => Math.ceil(props.curTaskDataList.length / 2) - 1);
+let curCSItemName = inject('curCSItemName');
+
+let UpperHalf = computed(() => {
+  return props.curTaskDataList.filter((item, i) => i <= median.value);
+});
+let LowerHalf = computed(() => {
+  return props.curTaskDataList.filter((item, i) => i > median.value);
+});
+</script>
+
+<style lang="scss" scoped>
+$bc-color: #2a76e8;
+
+.mind {
+  display: flex;
+  column-gap: 66px;
+
+  .main-wrap {
+    max-width: 304px;
+
+    &::before {
+      display: inline-block;
+      height: 100%;
+      vertical-align: middle;
+      content: '';
+    }
+
+    .csitem-name {
+      display: inline-block;
+      padding: 8px 12px;
+      color: #fff;
+      vertical-align: middle;
+      background-color: $bc-color;
+      border-radius: 20px;
+    }
+  }
+
+  &-left,
+  &-right {
+    display: flex;
+    flex-direction: column;
+    row-gap: 66px;
+    justify-content: center;
+  }
+
+  &-left {
+    .task-item {
+      &::before,
+      &::after {
+        right: -31px;
+      }
+    }
+  }
+
+  &-right {
+    .task-item {
+      &::before,
+      &::after {
+        left: -31px;
+      }
+    }
+  }
+
+  .task-item {
+    position: relative;
+    display: flex;
+    flex-direction: column;
+    row-gap: 8px;
+    padding: 8px 12px;
+    background-color: #fff;
+    border-radius: 20px;
+
+    &::before {
+      position: absolute;
+      top: 50%;
+      width: 31px;
+      height: 3px;
+      content: '';
+      background-color: $bc-color;
+    }
+
+    &.mind-down {
+      &::after {
+        position: absolute;
+        top: 50%;
+        width: 3px;
+        height: calc(50% + 33px);
+        content: '';
+        background-color: $bc-color;
+      }
+    }
+
+    &.mind-up {
+      &::after {
+        position: absolute;
+        top: calc(-50% + 3px);
+        width: 3px;
+        height: calc(50% + 33px);
+        content: '';
+        background-color: $bc-color;
+      }
+    }
+
+    &-name {
+      display: flex;
+      align-items: center;
+      font-size: 16px;
+      font-weight: bold;
+      color: #000;
+
+      &::before {
+        display: inline-block;
+        width: 26px;
+        height: 26px;
+        margin-right: 8px;
+        font-weight: normal;
+        line-height: 26px;
+        color: #fff;
+        text-align: center;
+        content: attr(data-index);
+        background-color: #5498ff;
+        border-radius: 50%;
+      }
+    }
+
+    &-date {
+      min-width: 242px;
+      height: 22px;
+      padding: 2px 12px;
+      margin-left: 34px;
+      background-color: #ebebeb;
+      border-radius: 20px;
+    }
+  }
+}
+</style>

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

@@ -61,7 +61,7 @@ import {
   videoFormat,
   audioFormat,
   compressFormat
-} from '../../../common/file.js';
+} from '../../../utils/file.js';
 import { getFileType } from '@/utils/filter';
 import { downloadFileUrl } from '@/utils/common';
 

+ 102 - 3
src/views/teacher/create_course/step_table/create_task/components/preview/task_preview/components/PreviewMessage.vue

@@ -1,7 +1,106 @@
 <template>
-  <div>sfdsjkljkl</div>
+  <div class="preview-message">
+    <ul class="message-list">
+      <li
+        v-for="(item, i) in messageList"
+        :key="i"
+        :class="[
+          'message-list-item',
+          { text: item.message_type === messageItem[0].message_type },
+          { audio: item.message_type === messageItem[1].message_type },
+          {
+            video: item.message_type === messageItem[2].message_type
+          }
+        ]"
+      >
+        <template v-if="item.message_type === messageItem[0].message_type">
+          <div class="text">{{ item.text }}</div>
+        </template>
+        <template v-else-if="item.message_type === messageItem[1].message_type">
+          <div class="avatar">T</div>
+          <AudioShow
+            :file-id="item.file_id"
+            :stop-play="stopPlay"
+            @pauseOtherAudio="pauseOtherAudio"
+            @changeStopPlay="changeStopPlay"
+          />
+        </template>
+        <template v-else-if="item.message_type === messageItem[2].message_type">
+          <div class="avatar">T</div>
+          <VideoShow
+            :file-id="item.file_id"
+            :stop-play="stopPlay"
+            @pauseOtherAudio="pauseOtherAudio"
+            @changeStopPlay="changeStopPlay"
+          />
+        </template>
+      </li>
+    </ul>
+  </div>
 </template>
 
-<script setup></script>
+<script setup>
+import { inject } from 'vue';
+import { messageItem } from '../../../data/TaskData';
 
-<style lang="scss" scoped></style>
+import AudioShow from '../../../common/AudioShow.vue';
+import VideoShow from '../../../common/VideoShow.vue';
+
+defineProps({
+  messageList: {
+    type: Array,
+    required: true
+  }
+});
+
+const { stopPlay, changeStopPlay, pauseOtherAudio } = inject('stopPlay');
+</script>
+
+<style lang="scss" scoped>
+.preview-message {
+  padding: 16px 0;
+  background-color: #f4f4f4;
+  border: 1px solid $border-color;
+
+  .message-list {
+    width: 100%;
+    height: 100%;
+    padding: 0 16px;
+
+    &-item {
+      display: flex;
+      column-gap: 16px;
+      align-items: center;
+      white-space: pre-wrap;
+
+      .text {
+        padding: 8px 12px;
+        margin-left: 54px;
+        background-color: #fff;
+        border-radius: 4px;
+      }
+
+      .avatar {
+        display: inline-block;
+        width: 38px;
+        height: 38px;
+        line-height: 38px;
+        text-align: center;
+        background-color: #e3eeff;
+        border-radius: 50%;
+      }
+
+      &.text:not(:first-child) {
+        margin-top: 8px;
+      }
+
+      &.audio,
+      &.video {
+        &:not(:first-child) {
+          margin-top: 22px;
+        }
+      }
+    }
+  }
+}
+</style>

+ 7 - 8
src/views/teacher/create_course/step_table/create_task/components/preview/task_preview/SubtaskItem.vue → src/views/teacher/create_course/step_table/create_task/components/preview/task_preview/components/SubtaskItem.vue

@@ -4,7 +4,7 @@
     <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) }}
+          {{ getTaskClassifyAttr(getInfoBlockTypeCorrespondingTeachingType(info_block_type)) }}
         </span>
       </div>
       <div v-show="!state" class="subtask-header-name">
@@ -33,7 +33,7 @@
           />
         </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" />
+        <PreviewMessage v-if="item.info_block_type === taskClassify[3].type" :message-list="item.message_list" />
       </div>
     </div>
   </div>
@@ -46,16 +46,15 @@ export default {
 </script>
 
 <script setup>
-import { ref } from 'vue';
-import { getTaskClassifyAttr } from '../../data/TaskClassify';
+import { getTaskClassifyAttr, getInfoBlockTypeCorrespondingTeachingType } from '../../../data/TaskClassify';
 import { useSwitch } from '@/utils/switch';
-import { taskClassify } from '../../data/TaskClassify.js';
+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';
+import PreviewFile from './PreviewFile.vue';
+import PreviewMessage from './PreviewMessage.vue';
 
-const props = defineProps({
+defineProps({
   subtaskData: {
     type: Object,
     required: true

+ 34 - 4
src/views/teacher/create_course/step_table/create_task/components/preview/task_preview/index.vue

@@ -6,14 +6,37 @@
         {{ previewDateTransform(curTaskObject.begin_time, curTaskObject.duration_second) }}
       </span>
     </div>
-    <div class="preview-content">
+    <div :class="['preview-content', `${task_type}-${taskIndex}`]">
       <div class="task-name">{{ curTaskObject.name }}</div>
       <div v-html="curTaskObject.content"></div>
+      <div class="preview-content-type">
+        <template v-if="curTaskObject.teaching_type === taskClassify[1].teaching_type">
+          <CoursewareView
+            v-for="(data, i) in curTaskObject.courseware_group_selected_list"
+            :key="`preview-${i}`"
+            :courseware-id="data.courseware_id"
+            :group-id-selected-info="data.group_id_selected_info"
+          />
+        </template>
+        <PreviewFile
+          v-else-if="curTaskObject.teaching_type === taskClassify[2].teaching_type"
+          :file-id-list="curTaskObject.file_id_list"
+        />
+        <PreviewMessage
+          v-if="curTaskObject.teaching_type === taskClassify[3].teaching_type"
+          :message-list="curTaskObject.message_list"
+        />
+      </div>
       <!-- 子任务 -->
       <template v-if="curTaskObject.child_task_list.length > 0">
         <hr />
         <div class="subtask-title">子任务:</div>
-        <SubtaskItem v-for="(data, i) in curTaskObject.child_task_list" :key="`subtask-${i}`" :subtask-data="data" />
+        <SubtaskItem
+          v-for="(data, i) in curTaskObject.child_task_list"
+          :key="`subtask-${i}`"
+          :class="[`${task_type}-${taskIndex}-${i}`]"
+          :subtask-data="data"
+        />
       </template>
     </div>
 
@@ -30,10 +53,16 @@ export default {
 <script setup>
 import { ref, computed, provide } from 'vue';
 import { taskData } from '../../data/TaskData';
-import { previewDateTransform } from '../../util/preview';
+import { previewDateTransform } from '../../utils/preview';
+import { taskClassify } from '../../data/TaskClassify';
+import { taskTypeArray } from '../../data/TaskType';
+import { accordingArrayPropGetProp } from '@/utils/filter';
 
-import SubtaskItem from './SubtaskItem.vue';
+import SubtaskItem from './components/SubtaskItem.vue';
 import ShowFile from '@/common/show_file/index.vue';
+import PreviewFile from './components/PreviewFile.vue';
+import PreviewMessage from './components/PreviewMessage.vue';
+import CoursewareView from '@/components/course/CoursewareView.vue';
 
 const props = defineProps({
   listName: {
@@ -45,6 +74,7 @@ const props = defineProps({
     required: true
   }
 });
+const task_type = accordingArrayPropGetProp(taskTypeArray, 'sidebarListName', props.listName, 'type');
 
 provide('listName', props.listName);
 provide('taskIndex', props.taskIndex);

+ 1 - 1
src/views/teacher/create_course/step_table/create_task/components/task_template/CreateTask.vue

@@ -12,7 +12,7 @@
 
 <script setup>
 import { inject } from 'vue';
-import { useDrag, customTypeList } from '../util/drag';
+import { useDrag, customTypeList } from '../utils/drag';
 
 const curTaskTypeObj = inject('curTaskTypeObj');
 const addTask = inject('addTask');

+ 19 - 9
src/views/teacher/create_course/step_table/create_task/components/task_template/TaskTemplate.vue

@@ -4,7 +4,7 @@
       <span class="task-template-index">{{ index + 1 }}</span>
       <span class="task-template-taskClassify-name">{{ getTaskClassifyAttr(teaching_type) }}</span>
     </div>
-    <div class="task-template-content">
+    <div :class="['task-template-content', `${task_type}-${index}`]">
       <div>任务标题</div>
       <el-input v-model="curTaskObject.name" placeholder="点击输入" />
       <div class="task-time">
@@ -50,23 +50,29 @@
 
       <!-- 课件 直播 -->
       <TemplateCourseware
-        v-if="[taskClassify[1].type, taskClassify[4].type].includes(teaching_type)"
+        v-if="[taskClassify[1].teaching_type, taskClassify[4].teaching_type].includes(teaching_type)"
         :list-name="listName"
         :index="index"
       />
-      <hr v-if="taskClassify[4].type === teaching_type" />
+      <hr v-if="taskClassify[4].teaching_type === teaching_type" />
       <!-- 文件 直播 -->
       <TemplateFile
-        v-if="[taskClassify[2].type, taskClassify[4].type].includes(teaching_type)"
+        v-if="[taskClassify[2].teaching_type, taskClassify[4].teaching_type].includes(teaching_type)"
         :list-name="listName"
         :index="index"
       />
       <!-- 录音/录影 -->
-      <TemplateRecording v-if="taskClassify[3].type === teaching_type" :list-name="listName" :index="index" />
+      <TemplateRecording v-if="taskClassify[3].teaching_type === teaching_type" :list-name="listName" :index="index" />
 
-      <hr v-if="taskClassify[0].type !== teaching_type" />
+      <hr v-if="taskClassify[0].teaching_type !== teaching_type" />
 
-      <SubTask v-for="(item, i) in subtaskList" :key="i" :subtask-index="i" @deleteSubtask="deleteSubtask" />
+      <SubTask
+        v-for="(item, i) in subtaskList"
+        :key="i"
+        :class="[`${task_type}-${index}-${i}`]"
+        :subtask-index="i"
+        @deleteSubtask="deleteSubtask"
+      />
 
       <hr v-if="subtaskList.length > 0" />
 
@@ -91,10 +97,12 @@ import { ref, inject, computed, provide } from 'vue';
 import { getTaskClassifyAttr, taskClassify } from '../data/TaskClassify';
 import { useSubtask } from './subtask/data';
 import { taskData } from '../data/TaskData';
+import { taskTypeArray } from '../data/TaskType';
+import { accordingArrayPropGetProp } from '@/utils/filter';
 
 import TemplateFile from './components/TemplateFile.vue';
 import TemplateCourseware from './components/TemplateCourseware.vue';
-import TemplateRecording from './components/TemplateRecording';
+import TemplateRecording from './components/TemplateRecording.vue';
 import RichText from '@/components/common/RichText.vue';
 import SubTask from './subtask/SubTask.vue';
 
@@ -109,6 +117,8 @@ const props = defineProps({
   }
 });
 
+const task_type = accordingArrayPropGetProp(taskTypeArray, 'sidebarListName', props.listName, 'type');
+
 provide('listName', props.listName);
 provide('taskIndex', props.index);
 
@@ -124,7 +134,7 @@ let isLastTask = computed(() => {
   return taskData.value[props.listName].length === props.index + 1;
 });
 
-let teaching_type = curTaskObject.value.teaching_type;
+let teaching_type = ref(curTaskObject.value.teaching_type);
 
 // 子任务
 let subtaskList = curTaskObject.value.child_task_list;

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

@@ -12,8 +12,12 @@
         <div class="operation">操作</div>
       </div>
       <ul v-if="courseList.length > 0">
-        <li v-for="({ courseware_id, name }, i) in courseList" :key="courseware_id" class="courseware-list-item">
-          <span class="file-name nowrap-ellipsis">{{ name }}</span>
+        <li
+          v-for="({ courseware_id, courseware_name }, i) in courseList"
+          :key="courseware_id"
+          class="courseware-list-item"
+        >
+          <span class="file-name nowrap-ellipsis">{{ courseware_name }}</span>
           <span class="operation" @click="deleteCourse(i)">删除</span>
         </li>
       </ul>

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

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

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

@@ -89,8 +89,8 @@ import { soundRecording, useRecordingPageData, audioAndVideo } from './recording
 import { taskData, messageItem } from '../../data/TaskData';
 import { fileUploadPrimordial } from '@/api/app.js';
 
-import AudioShow from './AudioShow.vue';
-import VideoShow from './VideoShow.vue';
+import AudioShow from '../../common/AudioShow.vue';
+import VideoShow from '../../common/VideoShow.vue';
 
 const props = defineProps({
   listName: {

+ 7 - 2
src/views/teacher/create_course/step_table/create_task/components/task_template/components/courseware.js

@@ -16,8 +16,10 @@ export function useCourseware(curTemplateData) {
     () => courseware_id_list.value,
     (newVal) => {
       curTemplateData.courseware_id_list = newVal;
-    }
+    },
+    { immediate: true }
   );
+
   // 课件分组选择列表
   let courseware_group_selected_list = computed(() => {
     return courseList.value.map(({ courseware_id, group_id_selected_info }) => {
@@ -28,6 +30,9 @@ export function useCourseware(curTemplateData) {
     () => courseware_group_selected_list.value,
     (newVal) => {
       curTemplateData.courseware_group_selected_list = newVal;
+    },
+    {
+      immediate: true
     }
   );
 
@@ -67,7 +72,7 @@ export function useSelectCourseware() {
     visibleSelectCourse.value = false;
 
     GetTreeNodeInfo_BookChapterStruct({ id: course_id }).then(({ name, id }) => {
-      courseFn({ name, courseware_id: id, group_id_selected_info: previewGroupId });
+      courseFn({ courseware_name: name, courseware_id: id, group_id_selected_info: previewGroupId ?? '[]' });
     });
   }
 

+ 3 - 3
src/views/teacher/create_course/step_table/create_task/components/task_template/index.vue

@@ -1,8 +1,8 @@
 <template>
   <div>
-    <template v-if="curTaskTemplateList.length > 0">
+    <template v-if="curTaskDataList.length > 0">
       <TaskTemplate
-        v-for="(item, i) in curTaskTemplateList"
+        v-for="(item, i) in curTaskDataList"
         :key="`${curTaskTypeObj.sidebarListName}-${i}`"
         :list-name="curTaskTypeObj.sidebarListName"
         :index="i"
@@ -20,7 +20,7 @@ import { inject, ref, onActivated, onDeactivated, provide } from 'vue';
 import TaskTemplate from './TaskTemplate.vue';
 import CreateTask from './CreateTask.vue';
 
-const curTaskTemplateList = inject('curTaskTemplateList');
+const curTaskDataList = inject('curTaskDataList');
 const curTaskTypeObj = inject('curTaskTypeObj');
 
 let isActivated = ref(true); // 组件是否激活中,keep-alive 缓存状态,为了 RichText 能顺利初始化

+ 10 - 6
src/views/teacher/create_course/step_table/create_task/components/task_template/subtask/SubTask.vue

@@ -4,7 +4,7 @@
     <div class="subtask-title">
       <div class="subtask-type">
         <span v-for="({ info_block_type }, i) in info_block_list" :key="i" class="subtask-type-item">{{
-          getTaskClassifyAttr(info_block_type)
+          getTaskClassifyAttr(getInfoBlockTypeCorrespondingTeachingType(info_block_type))
         }}</span>
       </div>
       <div v-show="!state" class="subtask-title-name">
@@ -27,7 +27,7 @@
       <span>任务说明</span>
       <RichText v-model="curSubtaskObj.content" :height="209" :is-border="true" :toolbar-list-index="1" />
       <hr />
-      <SubtaskItem
+      <SubtaskContainer
         v-for="({ info_block_type }, i) in info_block_list"
         :key="i"
         :item-index="i"
@@ -58,7 +58,7 @@
           :list-name="listName"
           :index="taskIndex"
         />
-      </SubtaskItem>
+      </SubtaskContainer>
       <div
         class="drag-add-subtask"
         @click="changeVisible(true, { isAdd: true, fn: addSubtaskItem })"
@@ -73,8 +73,12 @@
 
 <script setup>
 import { ref, inject } from 'vue';
-import { useDrag, customTypeList } from '../../util/drag';
-import { taskClassify, getTaskClassifyAttr } from '../../data/TaskClassify.js';
+import { useDrag, customTypeList } from '../../utils/drag';
+import {
+  taskClassify,
+  getTaskClassifyAttr,
+  getInfoBlockTypeCorrespondingTeachingType
+} from '../../data/TaskClassify.js';
 import { taskData } from '../../data/TaskData';
 import { useSubtaskItem } from './data';
 import { useSwitch } from '@/utils/switch';
@@ -83,7 +87,7 @@ import TemplateFile from '../components/TemplateFile.vue';
 import TemplateCourseware from '../components/TemplateCourseware.vue';
 import TemplateRecording from '../components/TemplateRecording.vue';
 import RichText from '@/components/common/RichText.vue';
-import SubtaskItem from './SubtaskItem.vue';
+import SubtaskContainer from './SubtaskContainer.vue';
 
 const props = defineProps({
   subtaskIndex: {

+ 5 - 2
src/views/teacher/create_course/step_table/create_task/components/task_template/subtask/SubtaskItem.vue → src/views/teacher/create_course/step_table/create_task/components/task_template/subtask/SubtaskContainer.vue

@@ -1,8 +1,11 @@
+<!-- 子任务容器 -->
 <template>
   <div>
     <div class="subtask-item">
       <div class="subtask-item-title">
-        <span class="label-name">{{ getTaskClassifyAttr(subtaskType) }}</span>
+        <span class="label-name">{{
+          getTaskClassifyAttr(getInfoBlockTypeCorrespondingTeachingType(subtaskType))
+        }}</span>
         <span class="subtask-delete" @click="deleteSubtaskItem">
           删除<svg-icon icon-class="delete-current" class-name="delete-current" />
         </span>
@@ -16,7 +19,7 @@
 </template>
 
 <script setup>
-import { getTaskClassifyAttr } from '../../data/TaskClassify.js';
+import { getTaskClassifyAttr, getInfoBlockTypeCorrespondingTeachingType } from '../../data/TaskClassify.js';
 
 const props = defineProps({
   itemIndex: {

+ 10 - 6
src/views/teacher/create_course/step_table/create_task/components/task_template/subtask/data.js

@@ -30,19 +30,23 @@ export function useSubtask(subtaskList) {
 export function useSubtaskItem(info_block_list) {
   /**
    * 添加子任务项
-   * @param {String} taskClassifyType 任务分类
+   * @param {Number} teaching_type 任务分类
    */
-  function addSubtaskItem(taskClassifyType) {
-    if (!taskClassifyType) return;
-    if (taskClassify[0].type === taskClassifyType || taskClassify[4].type === taskClassifyType) {
+  function addSubtaskItem(teaching_type) {
+    if (teaching_type === undefined) return;
+    if (taskClassify[0].teaching_type === teaching_type || taskClassify[4].teaching_type === teaching_type) {
       Message({
         type: 'warning',
-        message: `子任务中不能添加${getTaskClassifyAttr(taskClassifyType)}类型`
+        message: `子任务中不能添加${getTaskClassifyAttr(teaching_type)}类型`
       });
       return;
     }
     info_block_list.value.push(
-      JSON.parse(JSON.stringify(subtaskInfoBlock.find(({ info_block_type }) => info_block_type === taskClassifyType)))
+      JSON.parse(
+        JSON.stringify(
+          subtaskInfoBlock.find(({ info_block_type }) => info_block_type === getTaskClassifyAttr(teaching_type, 'type'))
+        )
+      )
     );
   }
 

+ 3 - 3
src/views/teacher/create_course/step_table/create_task/components/util/drag.js → src/views/teacher/create_course/step_table/create_task/components/utils/drag.js

@@ -8,9 +8,9 @@ export let customTypeList = ['taskClassifyType']; // 自定义状态列表
  * @param {String} customType 自定义数据类型名称
  */
 export function useDrag(isCustomData = false, customType = '') {
-  function onDragstart(e, type = '') {
+  function onDragstart(e, data = '') {
     if (isCustomData) {
-      e.dataTransfer.setData(customType, type); // 设置拖拽数据
+      e.dataTransfer.setData(customType, data); // 设置拖拽数据
     }
     e.target.style.opacity = 0.5; // 使其半透明
   }
@@ -19,7 +19,7 @@ export function useDrag(isCustomData = false, customType = '') {
     e.preventDefault();
   }
 
-  let dragover = ref(false);
+  let dragover = ref(false); // 是否进入拖拽目标区域
   function onDragover(e) {
     e.preventDefault();
     dragover.value = true;

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

@@ -44,7 +44,7 @@ export function allowFileFormat(suffix) {
 
 /**
  * 上传文件
- * @param {Ref} file <input type="file" /> 的 refs
+ * @param {ref} file <input type="file" /> 的 refs
  * @param {Object} curTemplateData 当前模板数据
  */
 export function useUploadFile(file, curTemplateData) {
@@ -60,7 +60,8 @@ export function useUploadFile(file, curTemplateData) {
     () => file_id_list.value,
     () => {
       curTemplateData.file_id_list = file_id_list.value;
-    }
+    },
+    { immediate: true }
   );
 
   // 打开选择文件

+ 7 - 2
src/views/teacher/create_course/step_table/create_task/components/util/mouseEvent.js → src/views/teacher/create_course/step_table/create_task/components/utils/mouseEvent.js

@@ -46,9 +46,13 @@ export function useMouseEvent(center) {
     /*
      * 按下空格,鼠标指针变为手型,松开恢复
      */
+    let isDown = false;
     document.addEventListener('keydown', (e) => {
-      if (e.key === ' ') e.preventDefault();
-      if (e.key === ' ' && mouseOnMain && centerStyle.value.cursor !== 'grab') {
+      if (e.key === ' ' && isDown) {
+        e.preventDefault();
+      }
+      isDown = true;
+      if (e.key === ' ' && mouseOnMain.value && centerStyle.value.cursor !== 'grab') {
         centerStyle.value = {
           cursor: 'grab',
           'user-select': 'none'
@@ -61,6 +65,7 @@ export function useMouseEvent(center) {
     document.addEventListener('mouseup', centerMouseupLeft);
 
     document.addEventListener('keyup', (e) => {
+      isDown = false;
       if (e.key === ' ') {
         e.preventDefault();
         centerStyle.value = {

+ 2 - 1
src/views/teacher/create_course/step_table/create_task/components/util/preview.js → src/views/teacher/create_course/step_table/create_task/components/utils/preview.js

@@ -1,8 +1,9 @@
 import { timeZeroFill } from '@/utils/index';
 
 /**
- * 预览开始时间转换
+ * @description 预览开始时间转换
  * @param {String} date yyyy-MM-dd HH:mm:ss
+ * @param {Number} duration 持续时间
  */
 export function previewDateTransform(date, duration) {
   if (date.length <= 0) return '';

+ 0 - 0
src/views/teacher/create_course/step_table/create_task/components/util/wheel.js → src/views/teacher/create_course/step_table/create_task/components/utils/wheel.js


+ 118 - 10
src/views/teacher/create_course/step_table/create_task/index.js

@@ -1,8 +1,18 @@
-import { ref, provide, computed, watch } from 'vue';
-import { GetCSItemListByCourseID, GetCSItemTaskList } from '@/api/course';
+import { ref, provide, inject, computed, watch } from 'vue';
+import {
+  GetCSItemListByCourseID,
+  GetCSItemTaskList,
+  GetCourseInfo_ContainCSItem,
+  UpdateCSItem,
+  DeleteCSItem
+} from '@/api/course';
 import { useRoute } from 'vue-router/composables';
 import { taskData } from './components/data/TaskData';
 import { GetTeacherListByCourseID, GetCourseStudentList } from '@/api/select.js';
+import { MessageBox, Message } from 'element-ui';
+import { fileStatusList } from './components/utils/file';
+import { isAbnormalVal } from '@/utils/validate';
+
 import store from '@/store';
 
 export function useCSItem(content) {
@@ -10,25 +20,96 @@ export function useCSItem(content) {
   const id = route.params.id; // 课程 id
   provide('id', id);
 
+  let beginDate = ref('');
+  let endDate = ref('');
+  provide('CSItemDate', {
+    beginDate,
+    endDate
+  });
+  GetCourseInfo_ContainCSItem({ id }).then(({ begin_date, end_date }) => {
+    beginDate.value = begin_date;
+    endDate.value = end_date;
+  });
+
   const query = route.query;
   const is_template = 'is_template' in query ? query.is_template === 'true' : false;
-  const closeLink = JSON.parse(is_template) ? '/main?tab=TemplateList' : '/create_course';
+  const closeLink = is_template
+    ? '/create_course'
+    : `/create_course_step_table/select_book/${id}?is_template=${is_template}`;
 
   let isEditorClassName = ref(false); // 是否编辑课节标题中
 
   let csItemList = ref(null); // 课节列表
   let curCSItemIndex = ref(0); // 当前课节索引
 
+  // 当前课节名称
+  let curCSItemName = computed(() => {
+    if (isAbnormalVal(csItemList.value) || csItemList.value.length <= 0) return '';
+    return csItemList.value[curCSItemIndex.value].name;
+  });
+  provide('curCSItemName', curCSItemName);
+
   /**
    * 得到课程的课节列表
    */
   function getCSItemList() {
     GetCSItemListByCourseID({ course_id: id }).then(({ cs_item_list }) => {
-      if (cs_item_list.length > 0) return (csItemList.value = cs_item_list);
+      if (cs_item_list.length <= 0) {
+        curCSItemIndex.value = 0;
+        return;
+      }
+      if (curCSItemIndex.value >= cs_item_list.length) curCSItemIndex.value = cs_item_list.length - 1;
+      csItemList.value = cs_item_list;
     });
   }
   getCSItemList();
 
+  /**
+   * 文件转换
+   * @param {Array} list 文件列表
+   * @returns Array
+   */
+  function filesTransform(list) {
+    return list.map(({ file_id, file_name, file_size }) => {
+      return {
+        file: {
+          name: file_name,
+          size: file_size
+        },
+        status: { ...fileStatusList[3], id: file_id }
+      };
+    });
+  }
+
+  /**
+   * 转换任务列表数据格式,转换为显示格式
+   * @param {Array} list 任务列表
+   */
+  function convertDataFormatTaskList(list) {
+    list.forEach((item) => {
+      // 学生列表转换为学生id列表
+      item.custom_student_id_list = item.custom_student_list.map(({ student_id }) => student_id);
+      // 教材转换
+      item._coursewares = JSON.parse(JSON.stringify(item.courseware_list));
+      // 文件转换
+      item._files = filesTransform(item.accessory_list);
+
+      item.child_task_list.forEach(({ info_block_list }) => {
+        info_block_list.forEach(({ info_block_type, courseware_list, file_list }, i, arr) => {
+          if (info_block_type === 'courseware') {
+            arr[i]._coursewares = JSON.parse(JSON.stringify(courseware_list));
+            return;
+          }
+          if (info_block_type === 'file') {
+            arr[i]._files = filesTransform(file_list);
+            return;
+          }
+        });
+      });
+    });
+    return list;
+  }
+
   // 当前课节 id
   let cs_item_id = computed(() => {
     return csItemList?.value?.[curCSItemIndex.value].id;
@@ -42,11 +123,9 @@ export function useCSItem(content) {
         ({ 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;
+          taskData.value.pre_task_list = convertDataFormatTaskList(pre_task_list);
+          taskData.value.mid_task_list = convertDataFormatTaskList(mid_task_list);
+          taskData.value.after_task_list = convertDataFormatTaskList(after_task_list);
         }
       );
     }
@@ -62,6 +141,11 @@ export function useCSItem(content) {
       selection.collapseToEnd(); // 光标移到最后
     } else {
       csItemList.value[curCSItemIndex.value].name = content.value.textContent;
+      const { id, begin_time, end_time, name } = csItemList.value[curCSItemIndex.value];
+      UpdateCSItem({ id, begin_time, end_time, name }).then(() => {
+        Message.success('修改课节成功');
+        getCSItemList();
+      });
       content.value.blur();
     }
   }
@@ -76,6 +160,28 @@ export function useCSItem(content) {
     isCreateClassSection.value = true;
   }
 
+  function isCSItemShow(index) {
+    return curCSItemIndex.value === index && !isCreateClassSection.value;
+  }
+
+  const $t = inject('$t');
+  /**
+   * 删除课节
+   */
+  function deleteCSItem(id) {
+    MessageBox.confirm($t('Key360'), $t('Key361'), {
+      confirmButtonText: $t('Key94'),
+      cancelButtonText: $t('Key83'),
+      type: 'warning'
+    }).then(() => {
+      DeleteCSItem({ id }).then(() => {
+        curCSItemIndex.value = Math.min(curCSItemIndex.value, csItemList.value.length - 1);
+        Message.success($t('Key362'));
+        getCSItemList();
+      });
+    });
+  }
+
   return {
     closeLink,
     isCreateClassSection,
@@ -87,7 +193,9 @@ export function useCSItem(content) {
     getCSItemList,
     editorClassName,
     handleClickClass,
-    addClassSection
+    addClassSection,
+    isCSItemShow,
+    deleteCSItem
   };
 }
 

+ 14 - 9
src/views/teacher/create_course/step_table/create_task/index.vue

@@ -8,30 +8,31 @@
       <div
         v-for="({ name, id }, i) in csItemList"
         :key="id"
-        :class="['class-item', i === curCSItemIndex ? 'active' : '']"
+        :class="['class-item', isCSItemShow(i) ? 'active' : '']"
         @click="handleClickClass(i)"
       >
-        <svg-icon :icon-class="i === curCSItemIndex ? 'class' : 'class-grey'" class-name="class" />
+        <svg-icon :icon-class="isCSItemShow(i) ? 'class' : 'class-grey'" class-name="class" />
         <span class="class-item-name nowrap-ellipsis">
           {{ name }}
         </span>
+        <svg-icon v-show="isCSItemShow(i)" icon-class="delete-current" @click="deleteCSItem(id)" />
       </div>
       <div :class="['class-add', isCreateClassSection ? 'active' : '']" @click="addClassSection">
         <svg-icon icon-class="add" />
       </div>
     </div>
     <!-- 课节标题 -->
-    <div class="class-title">
+    <div v-show="!isCreateClassSection" class="class-title">
       <div class="class-title-editor">
         <div
           ref="content"
           class="class-name"
           tabindex="-1"
           :contenteditable="isEditorClassName"
+          @click="(e) => e.preventDefault()"
           @keydown.enter="editorClassName(false)"
-        >
-          {{ csItemList ? csItemList[curCSItemIndex].name : '' }}
-        </div>
+          v-text="csItemList ? csItemList[curCSItemIndex].name : ''"
+        ></div>
         <svg-icon icon-class="write" class-name="write" @click="editorClassName(true)" />
       </div>
       <div class="class-title-operation">
@@ -53,7 +54,7 @@
       </div>
     </div>
     <!-- 创建课节 -->
-    <CreateCSitem v-show="isCreateClassSection" :get-c-s-item-list="getCSItemList" />
+    <CreateCSitem v-show="isCreateClassSection" @getCSItemList="getCSItemList" />
     <!-- 课节主体内容 -->
     <TaskEditor v-show="!isCreateClassSection" ref="task" />
   </div>
@@ -79,7 +80,9 @@ const {
   editorClassName,
   handleClickClass,
   addClassSection,
-  getCSItemList
+  getCSItemList,
+  isCSItemShow,
+  deleteCSItem
 } = useCSItem(content);
 
 initData();
@@ -113,6 +116,7 @@ $basic-background-color: #f7f7f7;
 
     .class-item {
       display: flex;
+      column-gap: 8px;
       align-items: center;
       padding: 12px;
       line-height: 24px;
@@ -142,7 +146,6 @@ $basic-background-color: #f7f7f7;
       &-name {
         min-width: 40px;
         max-width: 144px;
-        margin-left: 8px;
       }
     }
 
@@ -170,12 +173,14 @@ $basic-background-color: #f7f7f7;
       width: 100%;
 
       .class-name {
+        max-width: 50%;
         padding: 4px;
         margin-right: 6px;
 
         &[contenteditable='true'] {
           color: #fff;
           background-color: #628fd3;
+          caret-color: $basic-color;
         }
       }