Explorar el Código

1. 录音
2. vue-route 更新,将课节主入口改为 setup

dusenyao hace 2 años
padre
commit
3479023adc

+ 2 - 1
.vscode/settings.json

@@ -12,5 +12,6 @@
   "i18n-ally.localesPaths": ["src/locales/lang"],
   "i18n-ally.keystyle": "nested",
   "i18n-ally.sourceLanguage": "ZH",
-  "i18n-ally.displayLanguage": "ZH"
+  "i18n-ally.displayLanguage": "ZH",
+  "svg.preview.background": "white"
 }

+ 94 - 82
package-lock.json

@@ -41,21 +41,21 @@
       "dev": true
     },
     "@babel/core": {
-      "version": "7.18.10",
-      "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/core/-/core-7.18.10.tgz",
-      "integrity": "sha512-JQM6k6ENcBFKVtWvLavlvi/mPcpYZ3+R+2EySDEMSMbp7Mn4FexlbbJVrx2R7Ijhr01T8gyqrOaABWIOgxeUyw==",
+      "version": "7.18.13",
+      "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/core/-/core-7.18.13.tgz",
+      "integrity": "sha512-ZisbOvRRusFktksHSG6pjj1CSvkPkcZq/KHD45LAkVP/oiHJkNBZWfpvlLmX8OtHDG8IuzsFlVRWo08w7Qxn0A==",
       "dev": true,
       "requires": {
         "@ampproject/remapping": "^2.1.0",
         "@babel/code-frame": "^7.18.6",
-        "@babel/generator": "^7.18.10",
+        "@babel/generator": "^7.18.13",
         "@babel/helper-compilation-targets": "^7.18.9",
         "@babel/helper-module-transforms": "^7.18.9",
         "@babel/helpers": "^7.18.9",
-        "@babel/parser": "^7.18.10",
+        "@babel/parser": "^7.18.13",
         "@babel/template": "^7.18.10",
-        "@babel/traverse": "^7.18.10",
-        "@babel/types": "^7.18.10",
+        "@babel/traverse": "^7.18.13",
+        "@babel/types": "^7.18.13",
         "convert-source-map": "^1.7.0",
         "debug": "^4.1.0",
         "gensync": "^1.0.0-beta.2",
@@ -64,20 +64,20 @@
       },
       "dependencies": {
         "@babel/generator": {
-          "version": "7.18.12",
-          "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/generator/-/generator-7.18.12.tgz",
-          "integrity": "sha512-dfQ8ebCN98SvyL7IxNMCUtZQSq5R7kxgN+r8qYTGDmmSion1hX2C0zq2yo1bsCDhXixokv1SAWTZUMYbO/V5zg==",
+          "version": "7.18.13",
+          "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/generator/-/generator-7.18.13.tgz",
+          "integrity": "sha512-CkPg8ySSPuHTYPJYo7IRALdqyjM9HCbt/3uOBEFbzyGVP6Mn8bwFPB0jX6982JVNBlYzM1nnPkfjuXSOPtQeEQ==",
           "dev": true,
           "requires": {
-            "@babel/types": "^7.18.10",
+            "@babel/types": "^7.18.13",
             "@jridgewell/gen-mapping": "^0.3.2",
             "jsesc": "^2.5.1"
           }
         },
         "@babel/parser": {
-          "version": "7.18.11",
-          "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/parser/-/parser-7.18.11.tgz",
-          "integrity": "sha512-9JKn5vN+hDt0Hdqn1PiJ2guflwP+B6Ga8qbDuoF0PzzVhrzsKIJo8yGqVk6CmMHiMei9w1C1Bp9IMJSIK+HPIQ==",
+          "version": "7.18.13",
+          "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/parser/-/parser-7.18.13.tgz",
+          "integrity": "sha512-dgXcIfMuQ0kgzLB2b9tRZs7TTFFaGM2AbtA4fJgUUYukzGH4jwsS7hzQHEGs67jdehpm22vkgKwvbU+aEflgwg==",
           "dev": true
         },
         "@babel/template": {
@@ -92,27 +92,27 @@
           }
         },
         "@babel/traverse": {
-          "version": "7.18.11",
-          "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/traverse/-/traverse-7.18.11.tgz",
-          "integrity": "sha512-TG9PiM2R/cWCAy6BPJKeHzNbu4lPzOSZpeMfeNErskGpTJx6trEvFaVCbDvpcxwy49BKWmEPwiW8mrysNiDvIQ==",
+          "version": "7.18.13",
+          "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/traverse/-/traverse-7.18.13.tgz",
+          "integrity": "sha512-N6kt9X1jRMLPxxxPYWi7tgvJRH/rtoU+dbKAPDM44RFHiMH8igdsaSBgFeskhSl/kLWLDUvIh1RXCrTmg0/zvA==",
           "dev": true,
           "requires": {
             "@babel/code-frame": "^7.18.6",
-            "@babel/generator": "^7.18.10",
+            "@babel/generator": "^7.18.13",
             "@babel/helper-environment-visitor": "^7.18.9",
             "@babel/helper-function-name": "^7.18.9",
             "@babel/helper-hoist-variables": "^7.18.6",
             "@babel/helper-split-export-declaration": "^7.18.6",
-            "@babel/parser": "^7.18.11",
-            "@babel/types": "^7.18.10",
+            "@babel/parser": "^7.18.13",
+            "@babel/types": "^7.18.13",
             "debug": "^4.1.0",
             "globals": "^11.1.0"
           }
         },
         "@babel/types": {
-          "version": "7.18.10",
-          "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/types/-/types-7.18.10.tgz",
-          "integrity": "sha512-MJvnbEiiNkpjo+LknnmRrqbY1GPUUggjv+wQVjetM/AONoupqRALB7I6jGqNUAZsKcRIEu2J6FRFvsczljjsaQ==",
+          "version": "7.18.13",
+          "resolved": "https://repo.huaweicloud.com/repository/npm/@babel/types/-/types-7.18.13.tgz",
+          "integrity": "sha512-ePqfTihzW0W6XAU+aMw2ykilisStJfDnsejDCXRchCcMJ4O0+8DhPXf2YUbZ6wjBlsEmZwLK/sPweWtu8hcJYQ==",
           "dev": true,
           "requires": {
             "@babel/helper-string-parser": "^7.18.10",
@@ -3194,9 +3194,9 @@
       }
     },
     "@vue/compiler-sfc": {
-      "version": "2.7.8",
-      "resolved": "https://repo.huaweicloud.com/repository/npm/@vue/compiler-sfc/-/compiler-sfc-2.7.8.tgz",
-      "integrity": "sha512-2DK4YWKfgLnW9VDR9gnju1gcYRk3flKj8UNsms7fsRmFcg35slVTZEkqwBtX+wJBXaamFfn6NxSsZh3h12Ix/Q==",
+      "version": "2.7.10",
+      "resolved": "https://repo.huaweicloud.com/repository/npm/@vue/compiler-sfc/-/compiler-sfc-2.7.10.tgz",
+      "integrity": "sha512-55Shns6WPxlYsz4WX7q9ZJBL77sKE1ZAYNYStLs6GbhIOMrNtjMvzcob6gu3cGlfpCR4bT7NXgyJ3tly2+Hx8Q==",
       "requires": {
         "@babel/parser": "^7.18.4",
         "postcss": "^8.4.14",
@@ -3782,7 +3782,7 @@
     },
     "arrify": {
       "version": "1.0.1",
-      "resolved": "https://repo.huaweicloud.com/repository/npm/arrify/-/arrify-1.0.1.tgz",
+      "resolved": "https://registry.npmmirror.com/arrify/-/arrify-1.0.1.tgz",
       "integrity": "sha512-3CYzex9M9FGQjCGMGyi6/31c8GJbgb0qGyrx5HWxPd0aCwh4cB2YjMb2Xf9UuoogrMrlO9cTqnB5rI5GHZTcUA==",
       "dev": true
     },
@@ -5471,7 +5471,7 @@
     },
     "decamelize-keys": {
       "version": "1.1.0",
-      "resolved": "https://repo.huaweicloud.com/repository/npm/decamelize-keys/-/decamelize-keys-1.1.0.tgz",
+      "resolved": "https://registry.npmmirror.com/decamelize-keys/-/decamelize-keys-1.1.0.tgz",
       "integrity": "sha512-ocLWuYzRPoS9bfiSdDd3cxvrzovVMZnRDVEzAs+hWIVXGDbHxWMECij2OBuyB/An0FFW/nLuq6Kv1i/YC5Qfzg==",
       "dev": true,
       "requires": {
@@ -6245,9 +6245,9 @@
       }
     },
     "eslint-plugin-vue": {
-      "version": "9.3.0",
-      "resolved": "https://repo.huaweicloud.com/repository/npm/eslint-plugin-vue/-/eslint-plugin-vue-9.3.0.tgz",
-      "integrity": "sha512-iscKKkBZgm6fGZwFt6poRoWC0Wy2dQOlwUPW++CiPoQiw1enctV2Hj5DBzzjJZfyqs+FAXhgzL4q0Ww03AgSmQ==",
+      "version": "9.4.0",
+      "resolved": "https://repo.huaweicloud.com/repository/npm/eslint-plugin-vue/-/eslint-plugin-vue-9.4.0.tgz",
+      "integrity": "sha512-Nzz2QIJ8FG+rtJaqT/7/ru5ie2XgT9KCudkbN0y3uFYhQ41nuHEaboLAiqwMcK006hZPQv/rVMRhUIwEGhIvfQ==",
       "dev": true,
       "requires": {
         "eslint-utils": "^3.0.0",
@@ -7337,7 +7337,7 @@
     },
     "global-modules": {
       "version": "2.0.0",
-      "resolved": "https://repo.huaweicloud.com/repository/npm/global-modules/-/global-modules-2.0.0.tgz",
+      "resolved": "https://registry.npmmirror.com/global-modules/-/global-modules-2.0.0.tgz",
       "integrity": "sha512-NGbfmJBp9x8IxyJSd1P+otYK8vonoJactOogrVfFRIAEY1ukil8RSKDz2Yo7wh1oihl51l/r6W4epkeKJHqL8A==",
       "dev": true,
       "requires": {
@@ -7346,7 +7346,7 @@
     },
     "global-prefix": {
       "version": "3.0.0",
-      "resolved": "https://repo.huaweicloud.com/repository/npm/global-prefix/-/global-prefix-3.0.0.tgz",
+      "resolved": "https://registry.npmmirror.com/global-prefix/-/global-prefix-3.0.0.tgz",
       "integrity": "sha512-awConJSVCHVGND6x3tmMaKcQvwXLhjdkmomy2W+Goaui8YPgYgXJZewhg3fWC+DlfqqQuWg8AwqjGTD2nAPVWg==",
       "dev": true,
       "requires": {
@@ -7357,13 +7357,13 @@
       "dependencies": {
         "kind-of": {
           "version": "6.0.3",
-          "resolved": "https://repo.huaweicloud.com/repository/npm/kind-of/-/kind-of-6.0.3.tgz",
+          "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz",
           "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==",
           "dev": true
         },
         "which": {
           "version": "1.3.1",
-          "resolved": "https://repo.huaweicloud.com/repository/npm/which/-/which-1.3.1.tgz",
+          "resolved": "https://registry.npmjs.org/which/-/which-1.3.1.tgz",
           "integrity": "sha512-HxJdYWq1MTIQbJ3nw0cqssHoTNU267KlrDuGZ1WYlxDStUtKUhOaJmh112/TZmHxxUfuJqPXSOm7tDyas0OSIQ==",
           "dev": true,
           "requires": {
@@ -7402,7 +7402,7 @@
     },
     "globjoin": {
       "version": "0.1.4",
-      "resolved": "https://repo.huaweicloud.com/repository/npm/globjoin/-/globjoin-0.1.4.tgz",
+      "resolved": "https://registry.npmmirror.com/globjoin/-/globjoin-0.1.4.tgz",
       "integrity": "sha512-xYfnw62CKG8nLkZBfWbhWwDw02CHty86jfPcc2cr3ZfeuK9ysoVPPEUxf21bAD/rWAgk52SuBrLJlefNy8mvFg==",
       "dev": true
     },
@@ -7480,7 +7480,7 @@
     },
     "hard-rejection": {
       "version": "2.1.0",
-      "resolved": "https://repo.huaweicloud.com/repository/npm/hard-rejection/-/hard-rejection-2.1.0.tgz",
+      "resolved": "https://registry.npmmirror.com/hard-rejection/-/hard-rejection-2.1.0.tgz",
       "integrity": "sha512-VIZB+ibDhx7ObhAe7OVtoEbuP4h/MuOTHJ+J8h/eBXotJYl0fBgR72xDFCKgIh22OJZIOVNxBMWuhAr10r8HdA==",
       "dev": true
     },
@@ -7880,7 +7880,7 @@
     },
     "import-lazy": {
       "version": "4.0.0",
-      "resolved": "https://repo.huaweicloud.com/repository/npm/import-lazy/-/import-lazy-4.0.0.tgz",
+      "resolved": "https://registry.npmmirror.com/import-lazy/-/import-lazy-4.0.0.tgz",
       "integrity": "sha512-rKtvo6a868b5Hu3heneU+L4yEQ4jYKLtjpnPeUdK7h0yzXGmyBTypknlkCvHFBqfX9YlorEiMM6Dnq/5atfHkw==",
       "dev": true
     },
@@ -8122,7 +8122,7 @@
     },
     "is-plain-object": {
       "version": "5.0.0",
-      "resolved": "https://repo.huaweicloud.com/repository/npm/is-plain-object/-/is-plain-object-5.0.0.tgz",
+      "resolved": "https://registry.npmjs.org/is-plain-object/-/is-plain-object-5.0.0.tgz",
       "integrity": "sha512-VRSzKkbMm5jMDoKLbltAkFQ5Qr7VDiTFGXxYFXXowVj387GeGNOCsOH6Msy00SGZ3Fp84b1Naa1psqgcCIEP5Q==",
       "dev": true
     },
@@ -10563,7 +10563,7 @@
     },
     "mathml-tag-names": {
       "version": "2.1.3",
-      "resolved": "https://repo.huaweicloud.com/repository/npm/mathml-tag-names/-/mathml-tag-names-2.1.3.tgz",
+      "resolved": "https://registry.npmmirror.com/mathml-tag-names/-/mathml-tag-names-2.1.3.tgz",
       "integrity": "sha512-APMBEanjybaPzUrfqU0IMU5I0AswKMH7k8OTLs0vvV4KZpExkTkY87nR/zpbuTPj+gARop7aGUbl11pnDfW6xg==",
       "dev": true
     },
@@ -10755,7 +10755,7 @@
     },
     "min-indent": {
       "version": "1.0.1",
-      "resolved": "https://repo.huaweicloud.com/repository/npm/min-indent/-/min-indent-1.0.1.tgz",
+      "resolved": "https://registry.npmmirror.com/min-indent/-/min-indent-1.0.1.tgz",
       "integrity": "sha512-I9jwMn07Sy/IwOj3zVkVik2JTvgpaykDZEigL6Rx6N9LbMywwUSMtxET+7lVoDLLd3O3IXwJwvuuns8UB/HeAg==",
       "dev": true
     },
@@ -10830,7 +10830,7 @@
     },
     "minimist-options": {
       "version": "4.1.0",
-      "resolved": "https://repo.huaweicloud.com/repository/npm/minimist-options/-/minimist-options-4.1.0.tgz",
+      "resolved": "https://registry.npmmirror.com/minimist-options/-/minimist-options-4.1.0.tgz",
       "integrity": "sha512-Q4r8ghd80yhO/0j1O3B2BjweX3fiHg9cdOwjJd2J76Q135c+NDxGCqdYKQ1SKBuFfgWbAUzBfvYjPUEeNgqN1A==",
       "dev": true,
       "requires": {
@@ -10841,7 +10841,7 @@
       "dependencies": {
         "kind-of": {
           "version": "6.0.3",
-          "resolved": "https://repo.huaweicloud.com/repository/npm/kind-of/-/kind-of-6.0.3.tgz",
+          "resolved": "https://registry.npmjs.org/kind-of/-/kind-of-6.0.3.tgz",
           "integrity": "sha512-dcS1ul+9tmeD95T+x28/ehLgd9mENa3LsvDTtzm3vyBEO7RPptvAD+t44WVXaUjTBRcrpFeFlC8WCruUR456hw==",
           "dev": true
         }
@@ -12621,7 +12621,7 @@
     },
     "quick-lru": {
       "version": "4.0.1",
-      "resolved": "https://repo.huaweicloud.com/repository/npm/quick-lru/-/quick-lru-4.0.1.tgz",
+      "resolved": "https://registry.npmmirror.com/quick-lru/-/quick-lru-4.0.1.tgz",
       "integrity": "sha512-ARhCpm70fzdcvNQfPoy49IaanKkTlRWF2JMzqhcJbhSFRZv7nPTvZJdcY7301IPmvW+/p0RgIWnQDLJxifsQ7g==",
       "dev": true
     },
@@ -13150,9 +13150,9 @@
       "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
     },
     "sass": {
-      "version": "1.54.4",
-      "resolved": "https://repo.huaweicloud.com/repository/npm/sass/-/sass-1.54.4.tgz",
-      "integrity": "sha512-3tmF16yvnBwtlPrNBHw/H907j8MlOX8aTBnlNX1yrKx24RKcJGPyLhFUwkoKBKesR3unP93/2z14Ll8NicwQUA==",
+      "version": "1.54.5",
+      "resolved": "https://repo.huaweicloud.com/repository/npm/sass/-/sass-1.54.5.tgz",
+      "integrity": "sha512-p7DTOzxkUPa/63FU0R3KApkRHwcVZYC0PLnLm5iyZACyp15qSi32x7zVUhRdABAATmkALqgGrjCJAcWvobmhHw==",
       "dev": true,
       "requires": {
         "chokidar": ">=3.0.0 <4.0.0",
@@ -14212,7 +14212,7 @@
     },
     "style-search": {
       "version": "0.1.0",
-      "resolved": "https://repo.huaweicloud.com/repository/npm/style-search/-/style-search-0.1.0.tgz",
+      "resolved": "https://registry.npmmirror.com/style-search/-/style-search-0.1.0.tgz",
       "integrity": "sha512-Dj1Okke1C3uKKwQcetra4jSuk0DqbzbYtXipzFlFMZtowbF1x7BKJwB9AayVMyFARvU8EDrZdcax4At/452cAg==",
       "dev": true
     },
@@ -14227,14 +14227,14 @@
       }
     },
     "stylelint": {
-      "version": "14.10.0",
-      "resolved": "https://repo.huaweicloud.com/repository/npm/stylelint/-/stylelint-14.10.0.tgz",
-      "integrity": "sha512-VAmyKrEK+wNFh9R8mNqoxEFzaa4gsHGhcT4xgkQDuOA5cjF6CaNS8loYV7gpi4tIZBPUyXesotPXzJAMN8VLOQ==",
+      "version": "14.11.0",
+      "resolved": "https://repo.huaweicloud.com/repository/npm/stylelint/-/stylelint-14.11.0.tgz",
+      "integrity": "sha512-OTLjLPxpvGtojEfpESWM8Ir64Z01E89xsisaBMUP/ngOx1+4VG2DPRcUyCCiin9Rd3kPXPsh/uwHd9eqnvhsYA==",
       "dev": true,
       "requires": {
         "@csstools/selector-specificity": "^2.0.2",
         "balanced-match": "^2.0.0",
-        "colord": "^2.9.2",
+        "colord": "^2.9.3",
         "cosmiconfig": "^7.0.1",
         "css-functions-list": "^3.1.0",
         "debug": "^4.3.4",
@@ -14269,24 +14269,24 @@
         "svg-tags": "^1.0.0",
         "table": "^6.8.0",
         "v8-compile-cache": "^2.3.0",
-        "write-file-atomic": "^4.0.1"
+        "write-file-atomic": "^4.0.2"
       },
       "dependencies": {
         "balanced-match": {
           "version": "2.0.0",
-          "resolved": "https://repo.huaweicloud.com/repository/npm/balanced-match/-/balanced-match-2.0.0.tgz",
+          "resolved": "https://registry.npmmirror.com/balanced-match/-/balanced-match-2.0.0.tgz",
           "integrity": "sha512-1ugUSr8BHXRnK23KfuYS+gVMC3LB8QGH9W1iGtDPsNWoQbgtXSExkBu2aDR4epiGWZOjZsj6lDl/N/AqqTC3UA==",
           "dev": true
         },
         "camelcase": {
           "version": "5.3.1",
-          "resolved": "https://repo.huaweicloud.com/repository/npm/camelcase/-/camelcase-5.3.1.tgz",
+          "resolved": "https://registry.npmmirror.com/camelcase/-/camelcase-5.3.1.tgz",
           "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==",
           "dev": true
         },
         "camelcase-keys": {
           "version": "6.2.2",
-          "resolved": "https://repo.huaweicloud.com/repository/npm/camelcase-keys/-/camelcase-keys-6.2.2.tgz",
+          "resolved": "https://registry.npmmirror.com/camelcase-keys/-/camelcase-keys-6.2.2.tgz",
           "integrity": "sha512-YrwaA0vEKazPBkn0ipTiMpSajYDSe+KjQfrjhcBMxJt/znbvlHd8Pw/Vamaz5EB4Wfhs3SUR3Z9mwRu/P3s3Yg==",
           "dev": true,
           "requires": {
@@ -14295,9 +14295,15 @@
             "quick-lru": "^4.0.1"
           }
         },
+        "colord": {
+          "version": "2.9.3",
+          "resolved": "https://repo.huaweicloud.com/repository/npm/colord/-/colord-2.9.3.tgz",
+          "integrity": "sha512-jeC1axXpnb0/2nn/Y1LPuLdgXBLH7aDcHu4KEKfqw3CUhX7ZpfBSlPKyqXE6btIgEzfWtrX3/tyBCaCvXvMkOw==",
+          "dev": true
+        },
         "hosted-git-info": {
           "version": "4.1.0",
-          "resolved": "https://repo.huaweicloud.com/repository/npm/hosted-git-info/-/hosted-git-info-4.1.0.tgz",
+          "resolved": "https://registry.npmmirror.com/hosted-git-info/-/hosted-git-info-4.1.0.tgz",
           "integrity": "sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA==",
           "dev": true,
           "requires": {
@@ -14306,19 +14312,19 @@
         },
         "ignore": {
           "version": "5.2.0",
-          "resolved": "https://repo.huaweicloud.com/repository/npm/ignore/-/ignore-5.2.0.tgz",
+          "resolved": "https://registry.npmmirror.com/ignore/-/ignore-5.2.0.tgz",
           "integrity": "sha512-CmxgYGiEPCLhfLnpPp1MoRmifwEIOgjcHXxOBjv7mY96c+eWScsOP9c112ZyLdWHi0FxHjI+4uVhKYp/gcdRmQ==",
           "dev": true
         },
         "map-obj": {
           "version": "4.3.0",
-          "resolved": "https://repo.huaweicloud.com/repository/npm/map-obj/-/map-obj-4.3.0.tgz",
+          "resolved": "https://registry.npmmirror.com/map-obj/-/map-obj-4.3.0.tgz",
           "integrity": "sha512-hdN1wVrZbb29eBGiGjJbeP8JbKjq1urkHJ/LIP/NY48MZ1QVXUsQBV1G1zvYFHn1XE06cwjBsOI2K3Ulnj1YXQ==",
           "dev": true
         },
         "meow": {
           "version": "9.0.0",
-          "resolved": "https://repo.huaweicloud.com/repository/npm/meow/-/meow-9.0.0.tgz",
+          "resolved": "https://registry.npmmirror.com/meow/-/meow-9.0.0.tgz",
           "integrity": "sha512-+obSblOQmRhcyBt62furQqRAQpNyWXo8BuQ5bN7dG8wmwQ+vwHKp/rCFD4CrTP8CsDQD1sjoZ94K417XEUk8IQ==",
           "dev": true,
           "requires": {
@@ -14338,7 +14344,7 @@
         },
         "normalize-package-data": {
           "version": "3.0.3",
-          "resolved": "https://repo.huaweicloud.com/repository/npm/normalize-package-data/-/normalize-package-data-3.0.3.tgz",
+          "resolved": "https://registry.npmmirror.com/normalize-package-data/-/normalize-package-data-3.0.3.tgz",
           "integrity": "sha512-p2W1sgqij3zMMyRC067Dg16bfzVH+w7hyegmpIvZ4JNjqtGOVAIvLmjBx3yP7YTe9vKJgkoNOPjwQGogDoMXFA==",
           "dev": true,
           "requires": {
@@ -14350,7 +14356,7 @@
         },
         "redent": {
           "version": "3.0.0",
-          "resolved": "https://repo.huaweicloud.com/repository/npm/redent/-/redent-3.0.0.tgz",
+          "resolved": "https://registry.npmmirror.com/redent/-/redent-3.0.0.tgz",
           "integrity": "sha512-6tDA8g98We0zd0GvVeMT9arEOnTw9qM03L9cJXaCjrip1OO764RDBLBfrB4cwzNGDj5OA5ioymC9GkizgWJDUg==",
           "dev": true,
           "requires": {
@@ -14369,7 +14375,7 @@
         },
         "strip-indent": {
           "version": "3.0.0",
-          "resolved": "https://repo.huaweicloud.com/repository/npm/strip-indent/-/strip-indent-3.0.0.tgz",
+          "resolved": "https://registry.npmmirror.com/strip-indent/-/strip-indent-3.0.0.tgz",
           "integrity": "sha512-laJTa3Jb+VQpaC6DseHhF7dXVqHTfJPCRDaEbid/drOhgitgYku/letMUqOXFoWV0zIIUbjpdH2t+tYj4bQMRQ==",
           "dev": true,
           "requires": {
@@ -14378,13 +14384,13 @@
         },
         "trim-newlines": {
           "version": "3.0.1",
-          "resolved": "https://repo.huaweicloud.com/repository/npm/trim-newlines/-/trim-newlines-3.0.1.tgz",
+          "resolved": "https://registry.npmmirror.com/trim-newlines/-/trim-newlines-3.0.1.tgz",
           "integrity": "sha512-c1PTsA3tYrIsLGkJkzHF+w9F2EyxfXGo4UyJc4pFL++FMjnq0HJS69T3M7d//gKrFKwy429bouPescbjecU+Zw==",
           "dev": true
         },
         "type-fest": {
           "version": "0.18.1",
-          "resolved": "https://repo.huaweicloud.com/repository/npm/type-fest/-/type-fest-0.18.1.tgz",
+          "resolved": "https://registry.npmmirror.com/type-fest/-/type-fest-0.18.1.tgz",
           "integrity": "sha512-OIAYXk8+ISY+qTOwkHtKqzAuxchoMiD9Udx+FSGQDuiRR+PJKJHc2NJAXlbhkGwTt/4/nKZxELY1w3ReWOL8mw==",
           "dev": true
         },
@@ -15679,18 +15685,18 @@
       }
     },
     "vue": {
-      "version": "2.7.8",
-      "resolved": "https://repo.huaweicloud.com/repository/npm/vue/-/vue-2.7.8.tgz",
-      "integrity": "sha512-ncwlZx5qOcn754bCu5/tS/IWPhXHopfit79cx+uIlLMyt3vCMGcXai5yCG5y+I6cDmEj4ukRYyZail9FTQh7lQ==",
+      "version": "2.7.10",
+      "resolved": "https://repo.huaweicloud.com/repository/npm/vue/-/vue-2.7.10.tgz",
+      "integrity": "sha512-HmFC70qarSHPXcKtW8U8fgIkF6JGvjEmDiVInTkKZP0gIlEPhlVlcJJLkdGIDiNkIeA2zJPQTWJUI4iWe+AVfg==",
       "requires": {
-        "@vue/compiler-sfc": "2.7.8",
+        "@vue/compiler-sfc": "2.7.10",
         "csstype": "^3.1.0"
       }
     },
     "vue-demi": {
-      "version": "0.13.8",
-      "resolved": "https://repo.huaweicloud.com/repository/npm/vue-demi/-/vue-demi-0.13.8.tgz",
-      "integrity": "sha512-Vy1zbZhCOdsmvGR6tJhAvO5vhP7eiS8xkbYQSoVa7o6KlIy3W8Rc53ED4qI4qpeRDjv3mLfXSEpYU6Yq4pgXRg==",
+      "version": "0.13.10",
+      "resolved": "https://repo.huaweicloud.com/repository/npm/vue-demi/-/vue-demi-0.13.10.tgz",
+      "integrity": "sha512-/R4QhdqGyGqSysOfhkxmYHKwdETZq2z6HAf/fjeGErdJX9cJifX5ijHJS+VjNblGIhjXz/yQTwe/t7Cip+/aJw==",
       "dev": true
     },
     "vue-esign": {
@@ -15716,6 +15722,12 @@
         "semver": "^7.3.6"
       },
       "dependencies": {
+        "acorn": {
+          "version": "8.8.0",
+          "resolved": "https://repo.huaweicloud.com/repository/npm/acorn/-/acorn-8.8.0.tgz",
+          "integrity": "sha512-QOxyigPVrpZ2GXT+PFyZTl6TtOFc5egxHIP9IlQ+RbupQuX4RkT/Bee4/kQuC02Xkzg84JcT7oLYtDIQxp+v7w==",
+          "dev": true
+        },
         "eslint-scope": {
           "version": "7.1.1",
           "resolved": "https://registry.npmmirror.com/eslint-scope/-/eslint-scope-7.1.1.tgz",
@@ -15733,12 +15745,12 @@
           "dev": true
         },
         "espree": {
-          "version": "9.3.2",
-          "resolved": "https://repo.huaweicloud.com/repository/npm/espree/-/espree-9.3.2.tgz",
-          "integrity": "sha512-D211tC7ZwouTIuY5x9XnS0E9sWNChB7IYKX/Xp5eQj3nFXhqmiUDB9q27y76oFl8jTg3pXcQx/bpxMfs3CIZbA==",
+          "version": "9.3.3",
+          "resolved": "https://repo.huaweicloud.com/repository/npm/espree/-/espree-9.3.3.tgz",
+          "integrity": "sha512-ORs1Rt/uQTqUKjDdGCyrtYxbazf5umATSf/K4qxjmZHORR6HJk+2s/2Pqe+Kk49HHINC/xNIrGfgh8sZcll0ng==",
           "dev": true,
           "requires": {
-            "acorn": "^8.7.1",
+            "acorn": "^8.8.0",
             "acorn-jsx": "^5.3.2",
             "eslint-visitor-keys": "^3.3.0"
           }
@@ -15811,9 +15823,9 @@
       "integrity": "sha512-W+y2EAI/BxS4Vlcca9scQv8ifeBFck56DRtSwWJ2H4Cw1GLNUYxiZxUHHkuzuI5JPW/cYtL1bPO5xPyEXx4LmQ=="
     },
     "vue-router": {
-      "version": "3.5.4",
-      "resolved": "https://repo.huaweicloud.com/repository/npm/vue-router/-/vue-router-3.5.4.tgz",
-      "integrity": "sha512-x+/DLAJZv2mcQ7glH2oV9ze8uPwcI+H+GgTgTmb5I55bCgY3+vXWIsqbYUzbBSZnwFHEJku4eoaH/x98veyymQ=="
+      "version": "3.6.3",
+      "resolved": "https://repo.huaweicloud.com/repository/npm/vue-router/-/vue-router-3.6.3.tgz",
+      "integrity": "sha512-G21CKd8o/Mr3h8Xgi6zwg2ixJ5OxBG9G5w/b5McEFfLBqyQJc/7HDGsibf2FAl2enpZla+OJ3IlYipRusGN/4w=="
     },
     "vue-style-loader": {
       "version": "4.1.3",
@@ -15834,9 +15846,9 @@
       }
     },
     "vue-template-compiler": {
-      "version": "2.7.8",
-      "resolved": "https://repo.huaweicloud.com/repository/npm/vue-template-compiler/-/vue-template-compiler-2.7.8.tgz",
-      "integrity": "sha512-eQqdcUpJKJpBRPDdxCNsqUoT0edNvdt1jFjtVnVS/LPPmr0BU2jWzXlrf6BVMeODtdLewB3j8j3WjNiB+V+giw==",
+      "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==",
       "dev": true,
       "requires": {
         "de-indent": "^1.0.2",

+ 8 - 8
package.json

@@ -30,15 +30,15 @@
     "normalize.css": "^8.0.1",
     "nprogress": "^0.2.0",
     "tinymce": "^5.10.5",
-    "vue": "^2.7.8",
+    "vue": "^2.7.10",
     "vue-i18n": "^8.27.2",
     "vue-pdf": "^4.3.0",
-    "vue-router": "^3.5.4",
+    "vue-router": "^3.6.3",
     "vue-video-player": "^5.0.2",
     "vuex": "^3.6.2"
   },
   "devDependencies": {
-    "@babel/core": "^7.18.10",
+    "@babel/core": "^7.18.13",
     "@babel/eslint-parser": "^7.18.9",
     "@rushstack/eslint-patch": "^1.1.4",
     "@vue/cli-plugin-babel": "~5.0.8",
@@ -55,15 +55,15 @@
     "compression-webpack-plugin": "^6.1.1",
     "eslint": "^7.32.0",
     "eslint-plugin-prettier": "^4.2.1",
-    "eslint-plugin-vue": "^9.3.0",
+    "eslint-plugin-vue": "^9.4.0",
     "html-webpack-plugin": "^5.5.0",
     "postcss": "^8.4.16",
     "postcss-html": "^1.5.0",
     "prettier": "2.7.1",
-    "sass": "^1.54.4",
+    "sass": "^1.54.5",
     "sass-loader": "^10.3.1",
     "script-ext-html-webpack-plugin": "^2.1.5",
-    "stylelint": "14.10.0",
+    "stylelint": "14.11.0",
     "stylelint-config-prettier": "^9.0.3",
     "stylelint-config-recess-order": "^3.0.0",
     "stylelint-config-recommended-vue": "^1.4.0",
@@ -73,8 +73,8 @@
     "svg-sprite-loader": "^6.0.11",
     "svgo": "^2.8.0",
     "vue-loader": "^15.10.0",
-    "vue-template-compiler": "^2.7.8",
-    "vue-demi": "^0.13.8"
+    "vue-template-compiler": "^2.7.10",
+    "vue-demi": "^0.13.10"
   },
   "browserslist": [
     "> 1%",

+ 1 - 1
src/api/app.js

@@ -53,7 +53,7 @@ export function fileUpload(SecurityLevel, file) {
       'Content-Type': 'multipart/form-data'
     },
     data: formData,
-    onUploadProgress: progressEvent => {
+    onUploadProgress: (progressEvent) => {
       store.commit(`app/${app.SET_PERCENTAGE}`, (progressEvent.loaded / progressEvent.total) * 100 || 0);
     }
   }).finally(() => {

+ 12 - 3
src/components/common/RichText.vue

@@ -45,12 +45,23 @@ const props = defineProps({
     type: [Number, String],
     default: 148
   },
+  // 是否有边框
   isBorder: {
     type: Boolean,
     default: false
+  },
+  toolbarListIndex: {
+    type: Number,
+    default: 0
   }
 });
 
+/* eslint-disable max-len */
+const toolbarList = [
+  'undo redo | alignleft aligncenter alignright | forecolor | bold italic underline strikethrough code | bullist numlist | link image blockquote hr',
+  'undo redo | alignleft aligncenter alignright | forecolor | bold italic underline strikethrough code | bullist numlist | link image blockquote'
+];
+
 const init = {
   language_url: `/tinymce/langs/zh_CN.js`,
   language: 'zh_CN',
@@ -58,9 +69,7 @@ const init = {
   height: props.height,
   width: '100%',
   plugins: 'link lists image code hr',
-  toolbar:
-    // eslint-disable-next-line max-len
-    'undo redo | alignleft aligncenter alignright | forecolor | bold italic underline strikethrough code | bullist numlist | link image blockquote hr',
+  toolbar: toolbarList[props.toolbarListIndex],
   menubar: false,
   branding: false,
   statusbar: false

+ 7 - 0
src/icons/svg/delete-current.svg

@@ -0,0 +1,7 @@
+<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
+<path d="M3 3.33301V14.6663H13V3.33301H3Z" stroke="currentColor" stroke-width="1.5" stroke-linejoin="round"/>
+<path d="M6.66602 6.66602V10.9993" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
+<path d="M9.33301 6.66602V10.9993" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
+<path d="M1.33301 3.33301H14.6663" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/>
+<path d="M5.33301 3.33301L6.42933 1.33301H9.59205L10.6663 3.33301H5.33301Z" stroke="currentColor" stroke-width="1.5" stroke-linejoin="round"/>
+</svg>

+ 2 - 2
src/views/task_details/teacher/index.vue

@@ -101,7 +101,7 @@
               {{ $t('Key319') }}
             </el-button>
             <el-button v-if="student_list.length > 1" @click="next">
-              {{ buttonName() }} <i class="el-icon-right" />
+              {{ buttonName() }} <i class="el-icon-right"></i>
             </el-button>
           </div>
         </div>
@@ -321,7 +321,7 @@ export default {
       if (list.length <= 0) {
         return this.$message.warning(this.$i18n.t('Key325'));
       }
-      const curIndex = list.findIndex(item => item.student_id === this.curStudentId);
+      const curIndex = list.findIndex((item) => item.student_id === this.curStudentId);
       const nextList = list.length - 1 === curIndex ? list[curIndex - 1] : list[curIndex + 1];
       if (nextList) this.getTaskStudentExecuteInfo(nextList.student_id);
     }

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

@@ -2,7 +2,7 @@
   <el-dialog :visible="visible" width="610px" :before-close="handleClose" :modal="false">
     <template v-for="{ type, image, name } in taskClassify">
       <div v-if="type !== MORE_NAME" :key="type" :class="['task-classify']">
-        <div class="task-classify-item" @click="selectTaskClassify(type)">
+        <div class="task-classify-item" @click.once="selectTaskClassify(type)">
           <div class="taskClassify-name">
             {{ name }}
           </div>

+ 6 - 8
src/views/teacher/create_course/step_table/create_task/components/TaskEditor.vue

@@ -13,21 +13,19 @@
         <span>{{ (scale * 100).toFixed(0) }}%</span>
         <svg-icon icon-class="add" class-name="zoom-display-add" />
       </span>
-      <div
-        class="task-main-center-container"
-        :style="{ transform: `scale(${scale})` }"
-        @dragover="dragover"
-        @drop="drop($event, setTaskClassifyType)"
-      >
+      <div class="task-main-center-container" :style="{ transform: `scale(${scale})` }">
+        <!-- 创建任务 -->
         <template v-if="curPageType === mainPageTypeList[0]">
-          <div class="create-task">
+          <div class="create-task" @dragover="dragover" @drop="drop($event, setTaskClassifyType)">
             <svg-icon icon-class="add" class-name="create-task-add" />
             <span>创建{{ curTaskTypeObj.name }}</span>
           </div>
         </template>
+        <!-- 任务说明 -->
         <template v-else-if="curPageType === mainPageTypeList[1]">
           <TaskExplain />
         </template>
+        <!-- 任务模板 -->
         <template v-else-if="curPageType === mainPageTypeList[2]">
           <TaskTemplate :cur-task-classify-type="curTaskClassifyType" />
         </template>
@@ -47,7 +45,7 @@ import { handleDrag } from './drag.js';
 
 import TaskExplain from './TaskExplain/index.vue';
 import LeftSidebar from './LeftSidebar.vue';
-import TaskTemplate from './task_template';
+import TaskTemplate from './task_template/index.vue';
 import RightSidebar from './RightSidebar.vue';
 
 const center = ref(); // vue3 获取 refs 写法,需要同名,且传空

+ 123 - 0
src/views/teacher/create_course/step_table/create_task/components/task_template/SubTask.vue

@@ -0,0 +1,123 @@
+<template>
+  <div class="subtask">
+    <div class="subtask-title">
+      <div></div>
+      <div class="operation">
+        <span class="delete" @click="deleteSubtask">
+          <span>删除</span><svg-icon icon-class="delete-current" class-name="delete-current" />
+        </span>
+        <span class="contract" @click="handleStretch">
+          <span>{{ isUnfold ? '收起' : '展开' }}</span>
+          <i :class="[isUnfold ? 'el-icon-arrow-down' : 'el-icon-arrow-up']"></i
+        ></span>
+      </div>
+    </div>
+    <div class="subtask-content" :style="{ height: isUnfold ? '100%' : '0px' }">
+      <span>任务标题</span>
+      <el-input placeholder="点击输入" />
+      <span>任务说明</span>
+      <RichText :height="209" :is-border="true" :toolbar-list-index="1" />
+      <hr />
+      <SubtaskItem v-for="(item, i) in subtaskTypeList" :key="i" :classify="item" />
+      <div class="drag-add-subtask" @drop="drop($event, addSubtask)" @dragover="dragover">
+        拖动屏幕右侧“任务模版”到此处或点击此处以添加任务
+      </div>
+    </div>
+  </div>
+</template>
+
+<script setup>
+import { ref, defineProps, defineEmits } from 'vue';
+import { handleDrag } from '../drag';
+
+import RichText from '@/components/common/RichText.vue';
+import SubtaskItem from './SubtaskItem.vue';
+
+const props = defineProps({
+  index: {
+    type: Number,
+    required: true
+  }
+});
+
+const emit = defineEmits(['deleteSubtask']);
+
+function deleteSubtask() {
+  emit('deleteSubtask', props.index);
+}
+
+const { dragover, drop } = handleDrag();
+const isUnfold = ref(true); // 是否展开
+function handleStretch() {
+  isUnfold.value = !isUnfold.value;
+}
+
+let subtaskTypeList = ref([]); // 子任务中添加的类型列表
+function addSubtask(taskClassifyType) {
+  if (!taskClassifyType) return;
+  subtaskTypeList.value.push(taskClassifyType);
+}
+</script>
+
+<style lang="scss" scoped>
+.subtask {
+  padding: 8px 16px 0;
+  background-color: #f5f5f5;
+  border: 1px solid $border-color;
+  border-radius: 8px;
+  box-shadow: 0 2px 4px $border-color;
+
+  &-title {
+    display: flex;
+    justify-content: space-between;
+    padding: 0 8px 8px;
+    line-height: 26px;
+
+    .operation {
+      display: flex;
+      column-gap: 8px;
+
+      .delete {
+        color: #de4444;
+        cursor: pointer;
+
+        .delete-current {
+          margin-left: 6px;
+          vertical-align: -2px;
+        }
+      }
+
+      .contract,
+      .expand {
+        color: #595959;
+        cursor: pointer;
+
+        .el-icon-arrow-down,
+        .el-icon-arrow-up {
+          margin-left: 6px;
+        }
+      }
+    }
+  }
+
+  &-content {
+    display: flex;
+    flex-direction: column;
+    row-gap: 8px;
+    overflow: hidden;
+    line-height: 22px;
+    color: #2c2c2c;
+
+    .drag-add-subtask {
+      height: 284px;
+      margin-bottom: 16px;
+      line-height: 284px;
+      color: #999;
+      text-align: center;
+      background-color: #f5f9ff;
+      border: 1px dashed #2a76e8;
+      border-radius: 4px;
+    }
+  }
+}
+</style>

+ 29 - 0
src/views/teacher/create_course/step_table/create_task/components/task_template/SubtaskItem.vue

@@ -0,0 +1,29 @@
+<template>
+  <div class="subtask-item">
+    <div class="subtask-item-content">
+      <template v-if="taskClassify[0].type === classify">sdfsf </template>
+    </div>
+    <hr />
+  </div>
+</template>
+
+<script setup>
+import { useTaskClassify } from '../taskType.js';
+
+defineProps({
+  classify: {
+    type: String,
+    required: true
+  }
+});
+
+const { taskClassify } = useTaskClassify();
+</script>
+
+<style lang="scss" scoped>
+.subtask-item {
+  &-content {
+    background-color: #fff;
+  }
+}
+</style>

+ 198 - 0
src/views/teacher/create_course/step_table/create_task/components/task_template/data.js

@@ -0,0 +1,198 @@
+// 任务模板数据处理
+import { ref, computed } from 'vue';
+import { GetTeacherListByCourseID, GetCourseStudentList } from '@/api/select.js';
+import { Message } from 'element-ui';
+
+/**
+ * 录音/录影页面数据
+ */
+export function useAudioVideo() {
+  const audioAndVideo = ref([
+    {
+      type: 'audio',
+      name: '录音',
+      icon: 'microphone',
+      buttonName: '按住录音'
+    },
+    {
+      type: 'video',
+      name: '录影',
+      icon: 'videocamera',
+      buttonName: '开始录制视频'
+    }
+  ]);
+  let curRecord = ref(audioAndVideo.value[0].type);
+
+  function selectRecord(type) {
+    curRecord.value = type;
+  }
+
+  return {
+    audioAndVideo,
+    curRecord,
+    selectRecord
+  };
+}
+
+// 录音/录影功能处理
+export function useRecording(type) {
+  // 获取设备权限
+  if (!navigator.mediaDevices.getUserMedia) alert('浏览器不支持 getUserMedia');
+  const devicePreset = {
+    audio: {
+      constraints: { audio: true },
+      options: {
+        // audioBitsPerSecond: 128000
+      }
+    },
+    video: {
+      constraints: { audio: true, video: true },
+      options: {
+        // audioBitsPerSecond: 128000,
+        // videoBitsPerSecond: 2500000
+      }
+    }
+  };
+
+  let mediaRecorder = ref(null);
+
+  // 检查授权情况
+  function checkAuthorization() {
+    if (mediaRecorder.value) return false;
+    return Message({
+      type: 'warning',
+      message: '请授权!'
+    });
+  }
+
+  // 录音
+  let chunks = ref([]);
+  let blob = ref(null);
+
+  navigator.mediaDevices.getUserMedia(devicePreset[type].constraints).then(
+    (stream) => {
+      mediaRecorder.value = new MediaRecorder(stream, devicePreset[type].options);
+      mediaRecorder.value.addEventListener('stop', () => {
+        console.log(chunks.value);
+        blob.value = new Blob(chunks.value, {
+          type: 'audio/mp3;codecs=opus'
+        });
+        console.log(blob.value);
+        chunks.value = [];
+        // let audioURL = window.URL.createObjectURL(blob);
+        // audioSrc.src = audioURL;
+      });
+      mediaRecorder.value.addEventListener('dataavailable', (e) => {
+        chunks.value.push(e.data);
+      });
+    },
+    () => {
+      Message({
+        type: 'warning',
+        message: '授权失败!'
+      });
+    }
+  );
+
+  // 开始录音
+  function startRecording() {
+    if (checkAuthorization()) return;
+    if (mediaRecorder.value.state === 'inactive') {
+      mediaRecorder.value.start();
+      console.log('开始录音');
+    } else {
+      Message({
+        type: 'warning',
+        message: '录音已开启'
+      });
+    }
+  }
+
+  // 结束录音
+  function closeRecording() {
+    if (checkAuthorization()) return;
+    if (mediaRecorder.value.state === 'recording') {
+      mediaRecorder.value.stop();
+    } else {
+      Message({
+        type: 'warning',
+        message: '未开启录音'
+      });
+    }
+  }
+
+  function startVideo() {
+    console.log(2);
+  }
+
+  return { startRecording, closeRecording, blob, startVideo };
+}
+
+/**
+ * 处理表单数据
+ */
+export function useFormData(id) {
+  let value1 = ref('');
+  let duration = ref(1); // 持续时长
+  let durationList = []; // 持续时长列表
+  for (let i = 1; i <= 12; i++) {
+    durationList.push({ name: `${i}小时`, value: i });
+  }
+
+  // 选择教师
+  let selectTeacher = ref([]);
+  let teacherList = ref([]);
+  GetTeacherListByCourseID({ course_id: id }).then(({ teacher_list }) => {
+    teacherList.value = teacher_list;
+  });
+
+  // 选择学生
+  let selectStudent = ref([]);
+  let studentList = ref([]);
+  GetCourseStudentList({ course_id: id, audit_status_list: [1], pay_status: 1 }).then(({ student_list }) => {
+    studentList.value = student_list;
+  });
+
+  return {
+    value1,
+    duration,
+    durationList,
+    selectStudent,
+    selectTeacher,
+    teacherList,
+    studentList
+  };
+}
+
+/**
+ * 处理子任务
+ */
+export function useSubtask() {
+  let visible = ref(false);
+  function selectTaskType() {
+    visible.value = true;
+  }
+
+  // 子任务列表
+  let subtaskList = ref([]);
+  let isHasSubtask = computed(() => subtaskList.value.length > 0);
+  function addSubtask(type) {
+    visible.value = false;
+    subtaskList.value.push({
+      type
+    });
+  }
+  // 删除子任务
+  function deleteSubtask(index) {
+    subtaskList.value.splice(index, 1);
+  }
+
+  return {
+    visible,
+    selectTaskType,
+    subtaskList,
+    isHasSubtask,
+    addSubtask,
+    deleteSubtask
+  };
+}

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

@@ -26,7 +26,7 @@
         </div>
       </div>
       <div>执教教师</div>
-      <el-select v-model="selectTeacher" multiple collapse-tags>
+      <el-select v-model="selectTeacher" multiple>
         <el-option
           v-for="{ teacher_id, teacher_name } in teacherList"
           :key="teacher_id"
@@ -35,7 +35,7 @@
         />
       </el-select>
       <div>参加学生</div>
-      <el-select v-model="selectStudent" multiple collapse-tags>
+      <el-select v-model="selectStudent" multiple>
         <el-option
           v-for="{ student_id, student_name } in studentList"
           :key="student_id"
@@ -116,7 +116,13 @@
             </span>
           </div>
           <template v-for="{ icon, buttonName, type } in audioAndVideo">
-            <div v-if="curRecord === type" :key="icon" :class="['press-sound', icon]">
+            <div
+              v-if="curRecord === type"
+              :key="icon"
+              :class="['press-sound', icon]"
+              @mousedown="startRecording"
+              @mouseup="stopAudio"
+            >
               <svg-icon :icon-class="icon" class-name="button-icon" />{{ buttonName }}
             </div>
           </template>
@@ -170,10 +176,17 @@
       </template>
 
       <hr v-if="curTaskClassifyType !== taskClassify[0].type" />
+
+      <SubTask v-for="(item, i) in subtaskList" :key="i" :index="i" @deleteSubtask="deleteSubtask" />
+
+      <hr v-if="isHasSubtask" />
+
       <el-button class="add-subtask" @click="selectTaskType">
         <svg-icon icon-class="plus" class-name="plus" />添加子任务
       </el-button>
     </div>
+    <div class="add-task"><svg-icon icon-class="plus" class-name="task-plus" /></div>
+
     <SelectTaskClassify :visible.sync="visible" @select-task-classify="addSubtask" />
     <!-- TODO 改为课节id -->
     <SelectCourse :id="id" :dialog-visible="dialogVisible" />
@@ -187,13 +200,14 @@ export default {
 </script>
 
 <script setup>
-import { ref, inject } from 'vue';
+import { ref, inject, watch } from 'vue';
 import { useTaskClassify } from '../taskType.js';
-import { GetTeacherListByCourseID, GetCourseStudentList } from '@/api/select.js';
+import { useAudioVideo, useFormData, useSubtask, useRecording } from './data';
 
 import RichText from '@/components/common/RichText.vue';
 import SelectTaskClassify from '../SelectTaskClassify.vue';
 import SelectCourse from '@/components/select/SelectCourse.vue';
+import SubTask from './SubTask.vue';
 
 defineProps({
   curTaskClassifyType: {
@@ -205,58 +219,25 @@ defineProps({
 const { taskClassify, getTaskClassifyName } = useTaskClassify();
 
 const id = inject('id');
-let value1 = ref('');
-let duration = ref(1); // 持续时长
-let durationList = []; // 持续时长列表
-for (let i = 1; i <= 12; i++) {
-  durationList.push({ name: `${i}小时`, value: i });
-}
-let visible = ref(false);
-
-function selectTaskType() {
-  visible.value = true;
-}
-
-function addSubtask(type) {
-  console.log(type);
-}
 
 let dialogVisible = ref(false);
 
-// 选择教师
-let selectTeacher = ref([]);
-let teacherList = ref([]);
-GetTeacherListByCourseID({ course_id: id }).then(({ teacher_list }) => {
-  teacherList.value = teacher_list;
-});
+let { visible, selectTaskType, subtaskList, isHasSubtask, addSubtask, deleteSubtask } = useSubtask();
 
-// 选择学生
-let selectStudent = ref([]);
-let studentList = ref([]);
-GetCourseStudentList({ course_id: id, audit_status_list: [1], pay_status: 1 }).then(({ student_list }) => {
-  studentList.value = student_list;
-});
+let { audioAndVideo, curRecord, selectRecord } = useAudioVideo();
 
-// 录音/录影
-let audioAndVideo = ref([
-  {
-    type: 'audio',
-    name: '录音',
-    icon: 'microphone',
-    buttonName: '按住录音'
-  },
-  {
-    type: 'video',
-    name: '录影',
-    icon: 'videocamera',
-    buttonName: '开始录制视频'
-  }
-]);
-let curRecord = ref(audioAndVideo.value[0].type);
+let { value1, duration, durationList, selectStudent, selectTeacher, teacherList, studentList } = useFormData(id);
+
+let { startRecording, closeRecording, blob } = useRecording('audio');
 
-function selectRecord(type) {
-  curRecord.value = type;
+function stopAudio() {
+  console.log('stop', closeRecording());
 }
+
+watch(blob, (newVal) => {
+  console.log(newVal);
+});
+// let { startVideo } = useRecording('video');
 </script>
 
 <style lang="scss" scoped>
@@ -310,7 +291,7 @@ $tip-color: #999;
       }
     }
 
-    hr {
+    :deep hr {
       width: 100%;
       margin: 23px 0;
       border: 1px dashed $border-color;
@@ -553,5 +534,21 @@ $tip-color: #999;
       }
     }
   }
+
+  .add-task {
+    width: 32px;
+    height: 32px;
+    padding: 4px;
+    margin: 24px auto 0;
+    cursor: pointer;
+    background-color: #5498ff;
+    border-radius: 4px;
+
+    .task-plus {
+      width: 24px;
+      height: 24px;
+      text-align: center;
+    }
+  }
 }
 </style>

+ 40 - 46
src/views/teacher/create_course/step_table/create_task/index.vue

@@ -2,9 +2,13 @@
   <div class="task">
     <!-- 课节列表 -->
     <div class="class-list">
-      <div class="back"><svg-icon icon-class="back-black" class-name="back-black" />返回</div>
+      <div class="back" @click="$router.push(closeLink)">
+        <svg-icon icon-class="back-black" class-name="back-black" />返回
+      </div>
       <div class="class-item" @click="handleClickClass">
-        <svg-icon icon-class="class" class-name="class" /><span class="class-item-name nowrap-ellipsis">课节 1</span>
+        <svg-icon icon-class="class" class-name="class" /><span class="class-item-name nowrap-ellipsis">{{
+          name
+        }}</span>
       </div>
       <div :class="['class-add', isCreateClassSection ? 'active' : '']" @click="addClassSection">
         <svg-icon icon-class="add" />
@@ -36,56 +40,46 @@
   </div>
 </template>
 
-<script>
+<script setup>
+import { ref, provide } from 'vue';
+import { useRoute } from 'vue-router/composables';
 import TaskEditor from './components/TaskEditor.vue';
 import CreateClassSection from './components/createClassSection.vue';
 
-export default {
-  components: {
-    TaskEditor,
-    CreateClassSection
-  },
-  provide() {
-    return {
-      id: this.$route.params.id
-    };
-  },
-  data() {
-    const query = this.$route.query;
-    const is_template = 'is_template' in query ? query.is_template === 'true' : false;
-
-    return {
-      id: this.$route.params.id, // 课程 id
-      is_template,
-      name: '',
-      closeLink: JSON.parse(is_template) ? '/main?tab=TemplateList' : '/create_course',
-      isEditorClassName: false,
-      isCreateClassSection: false
-    };
-  },
-  methods: {
-    editorClassName(isEditor) {
-      this.isEditorClassName = isEditor;
-      const content = this.$refs.content;
-      if (isEditor) {
-        content.focus(); // 聚焦到标题
-        const selection = window.getSelection(); // 获取 range
-        selection.selectAllChildren(content); // 选择 content 下所有子内容
-        selection.collapseToEnd(); // 光标移到最后
-      } else {
-        content.blur();
-      }
-    },
+let route = useRoute();
+const id = route.params.id; // 课程 id
 
-    handleClickClass() {
-      this.isCreateClassSection = false;
-    },
+provide('id', id);
 
-    addClassSection() {
-      this.isCreateClassSection = true;
-    }
+const content = ref(); // refs
+let 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';
+
+let name = ref('课节 1');
+let isEditorClassName = ref(false); // 是否编辑课节标题中
+
+function editorClassName(isEditor) {
+  isEditorClassName.value = isEditor;
+  if (isEditor) {
+    content.value.focus(); // 聚焦到标题
+    const selection = window.getSelection(); // 获取 range
+    selection.selectAllChildren(content); // 选择 content 下所有子内容
+    selection.collapseToEnd(); // 光标移到最后
+  } else {
+    content.value.blur();
   }
-};
+}
+
+// 是否创建课节中
+let isCreateClassSection = ref(false);
+function handleClickClass() {
+  isCreateClassSection.value = false;
+}
+
+function addClassSection() {
+  isCreateClassSection.value = true;
+}
 </script>
 
 <style lang="scss" scoped>