Browse Source

添加作品详情

lex 1 year ago
parent
commit
4d842b8f6f
48 changed files with 3443 additions and 102 deletions
  1. 443 3
      package-lock.json
  2. 1 0
      package.json
  3. 101 0
      src/components/col-video-tcplayer/index.module.less
  4. 162 0
      src/components/col-video-tcplayer/index.tsx
  5. 2 1
      src/helpers/request.ts
  6. 39 1
      src/helpers/utils.ts
  7. 21 0
      src/router/routes-common.ts
  8. 21 96
      src/router/routes-tenant.ts
  9. 60 0
      src/views/creation/api.ts
  10. 102 0
      src/views/creation/edit/index.module.less
  11. 101 0
      src/views/creation/edit/index.tsx
  12. BIN
      src/views/creation/images/audio-banner-bg.png
  13. BIN
      src/views/creation/images/audio-bg.png
  14. BIN
      src/views/creation/images/audio-pan.png
  15. BIN
      src/views/creation/images/audio-point.png
  16. BIN
      src/views/creation/images/audio-shadow.png
  17. BIN
      src/views/creation/images/audio-zhen.png
  18. BIN
      src/views/creation/images/icon-delete.png
  19. BIN
      src/views/creation/images/icon-download.png
  20. BIN
      src/views/creation/images/icon-member.png
  21. BIN
      src/views/creation/images/icon-pause.png
  22. BIN
      src/views/creation/images/icon-play.png
  23. BIN
      src/views/creation/images/icon-share.png
  24. BIN
      src/views/creation/images/icon-z.png
  25. BIN
      src/views/creation/images/icon-zan-active.png
  26. BIN
      src/views/creation/images/icon-zan.png
  27. BIN
      src/views/creation/images/wave-1.png
  28. BIN
      src/views/creation/images/wave-2.png
  29. 450 0
      src/views/creation/index-share.tsx
  30. 556 0
      src/views/creation/index.module.less
  31. 491 0
      src/views/creation/index.tsx
  32. BIN
      src/views/creation/login-model/images/icon-close.png
  33. BIN
      src/views/creation/login-model/images/icon-password.png
  34. BIN
      src/views/creation/login-model/images/icon-phone.png
  35. BIN
      src/views/creation/login-model/images/login-bg.png
  36. 74 0
      src/views/creation/login-model/index.module.less
  37. 207 0
      src/views/creation/login-model/index.tsx
  38. BIN
      src/views/creation/share-model/images/icon-download.png
  39. BIN
      src/views/creation/share-model/images/icon-friend-ring.png
  40. BIN
      src/views/creation/share-model/images/icon-friend.png
  41. BIN
      src/views/creation/share-model/images/icon-link.png
  42. BIN
      src/views/creation/share-model/images/icon-logo.png
  43. BIN
      src/views/creation/share-model/images/icon-wechat.png
  44. BIN
      src/views/creation/share-model/images/music-bg.png
  45. BIN
      src/views/creation/share-model/images/share-bg.png
  46. 195 0
      src/views/creation/share-model/index.module.less
  47. 222 0
      src/views/creation/share-model/index.tsx
  48. 195 1
      yarn.lock

+ 443 - 3
package-lock.json

@@ -29,6 +29,7 @@
         "qrcode": "^1.5.1",
         "qrcode.vue": "^3.3.3",
         "query-string": "^7.1.1",
+        "tcplayer.js": "^5.0.1",
         "umi-request": "^1.4.0",
         "vant": "^3.4.6",
         "vconsole": "^3.14.6",
@@ -2992,6 +2993,27 @@
         "@babel/core": "^7.0.0-0"
       }
     },
+    "node_modules/babel-runtime": {
+      "version": "6.26.0",
+      "resolved": "https://registry.npmmirror.com/babel-runtime/-/babel-runtime-6.26.0.tgz",
+      "integrity": "sha512-ITKNuq2wKlW1fJg9sSW52eepoYgZBggvOAHC0u/CYu/qxQ9EVzThCgR69BnSXLHjy2f7SY5zaQ4yt7H9ZVxY2g==",
+      "dependencies": {
+        "core-js": "^2.4.0",
+        "regenerator-runtime": "^0.11.0"
+      }
+    },
+    "node_modules/babel-runtime/node_modules/core-js": {
+      "version": "2.6.12",
+      "resolved": "https://registry.npmmirror.com/core-js/-/core-js-2.6.12.tgz",
+      "integrity": "sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ==",
+      "deprecated": "core-js@<3.23.3 is no longer maintained and not recommended for usage due to the number of issues. Because of the V8 engine whims, feature detection in old core-js versions could cause a slowdown up to 100x even if nothing is polyfilled. Some versions have web compatibility issues. Please, upgrade your dependencies to the actual version of core-js.",
+      "hasInstallScript": true
+    },
+    "node_modules/babel-runtime/node_modules/regenerator-runtime": {
+      "version": "0.11.1",
+      "resolved": "https://registry.npmmirror.com/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz",
+      "integrity": "sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg=="
+    },
     "node_modules/babel-walk": {
       "version": "3.0.0-canary-5",
       "resolved": "https://registry.npmmirror.com/babel-walk/-/babel-walk-3.0.0-canary-5.tgz",
@@ -3054,6 +3076,11 @@
         "readable-stream": "^3.4.0"
       }
     },
+    "node_modules/blueimp-md5": {
+      "version": "2.19.0",
+      "resolved": "https://registry.npmmirror.com/blueimp-md5/-/blueimp-md5-2.19.0.tgz",
+      "integrity": "sha512-DRQrD6gJyy8FbiE4s+bDoXS9hiW3Vbx5uCdwvcCf3zLHL+Iv7LtGHLpr+GZV8rHG8tK766FGYBwRbu8pELTt+w=="
+    },
     "node_modules/brace-expansion": {
       "version": "1.1.11",
       "resolved": "https://registry.npmmirror.com/brace-expansion/-/brace-expansion-1.1.11.tgz",
@@ -3275,6 +3302,14 @@
       "dev": true,
       "license": "MIT"
     },
+    "node_modules/charenc": {
+      "version": "0.0.2",
+      "resolved": "https://registry.npmmirror.com/charenc/-/charenc-0.0.2.tgz",
+      "integrity": "sha512-yrLQ/yVUFXkzg7EDQsPieE/53+0RlaWTs+wBrvW36cyilJ2SaDWfl4Yj7MtLTXleV9uEKefbAGUPv2/iWSooRA==",
+      "engines": {
+        "node": "*"
+      }
+    },
     "node_modules/ci-info": {
       "version": "1.6.0",
       "resolved": "https://registry.npmmirror.com/ci-info/-/ci-info-1.6.0.tgz",
@@ -3620,6 +3655,14 @@
         "node": ">= 8"
       }
     },
+    "node_modules/crypt": {
+      "version": "0.0.2",
+      "resolved": "https://registry.npmmirror.com/crypt/-/crypt-0.0.2.tgz",
+      "integrity": "sha512-mCxBlsHFYh9C+HVpiEacem8FEBnMXgU9gy4zmNC+SXAZNB/1idgp/aulFJ4FgCi7GPEVbfyng092GqL2k2rmow==",
+      "engines": {
+        "node": "*"
+      }
+    },
     "node_modules/css-line-break": {
       "version": "2.1.0",
       "resolved": "https://registry.npmmirror.com/css-line-break/-/css-line-break-2.1.0.tgz",
@@ -3810,6 +3853,11 @@
         "url": "https://github.com/fb55/entities?sponsor=1"
       }
     },
+    "node_modules/dom-walk": {
+      "version": "0.1.2",
+      "resolved": "https://registry.npmmirror.com/dom-walk/-/dom-walk-0.1.2.tgz",
+      "integrity": "sha512-6QvTW9mrGeIegrFXdtQi9pk7O/nSK6lSdXW2eqUspN5LWD7UTji2Fqw5V2YLjBpHEoU9Xl/eUWNpDeZvoyOv2w=="
+    },
     "node_modules/domelementtype": {
       "version": "2.2.0",
       "resolved": "https://registry.npmmirror.com/domelementtype/-/domelementtype-2.2.0.tgz",
@@ -3980,6 +4028,14 @@
       "dev": true,
       "license": "MIT"
     },
+    "node_modules/es5-shim": {
+      "version": "4.6.7",
+      "resolved": "https://registry.npmmirror.com/es5-shim/-/es5-shim-4.6.7.tgz",
+      "integrity": "sha512-jg21/dmlrNQI7JyyA2w7n+yifSxBng0ZralnSfVZjoCawgNTCnS+yBCyVM9DL5itm7SUnDGgv7hcq2XCZX4iRQ==",
+      "engines": {
+        "node": ">=0.4.0"
+      }
+    },
     "node_modules/esbuild": {
       "version": "0.14.54",
       "resolved": "https://registry.npmmirror.com/esbuild/-/esbuild-0.14.54.tgz",
@@ -5228,6 +5284,15 @@
         "node": ">=10.13.0"
       }
     },
+    "node_modules/global": {
+      "version": "4.3.2",
+      "resolved": "https://registry.npmmirror.com/global/-/global-4.3.2.tgz",
+      "integrity": "sha512-/4AybdwIDU4HkCUbJkZdWpe4P6vuw/CUtu+0I1YlLIPe7OlUO7KNJ+q/rO70CW2/NW6Jc6I62++Hzsf5Alu6rQ==",
+      "dependencies": {
+        "min-document": "^2.19.0",
+        "process": "~0.5.1"
+      }
+    },
     "node_modules/global-modules": {
       "version": "1.0.0",
       "resolved": "https://registry.npmmirror.com/global-modules/-/global-modules-1.0.0.tgz",
@@ -5568,6 +5633,11 @@
         "node": ">=8"
       }
     },
+    "node_modules/individual": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmmirror.com/individual/-/individual-2.0.0.tgz",
+      "integrity": "sha512-pWt8hBCqJsUWI/HtcfWod7+N9SgAqyPEaF7JQjwzjn5vGrpg6aQ5qeAFQ7dx//UH4J1O+7xqew+gCeeFt6xN/g=="
+    },
     "node_modules/inflight": {
       "version": "1.0.6",
       "resolved": "https://registry.npmmirror.com/inflight/-/inflight-1.0.6.tgz",
@@ -5852,6 +5922,11 @@
         "node": ">=0.10.0"
       }
     },
+    "node_modules/is-buffer": {
+      "version": "1.1.6",
+      "resolved": "https://registry.npmmirror.com/is-buffer/-/is-buffer-1.1.6.tgz",
+      "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w=="
+    },
     "node_modules/is-ci": {
       "version": "1.2.1",
       "resolved": "https://registry.npmmirror.com/is-ci/-/is-ci-1.2.1.tgz",
@@ -5925,6 +6000,11 @@
         "url": "https://github.com/sponsors/sindresorhus"
       }
     },
+    "node_modules/is-function": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmmirror.com/is-function/-/is-function-1.0.2.tgz",
+      "integrity": "sha512-lw7DUp0aWXYg+CBCN+JKkcE0Q2RayZnSvnZBlwgxHBQhqt5pZNVy4Ri7H9GmmXkdu7LUthszM+Tor1u/2iBcpQ=="
+    },
     "node_modules/is-glob": {
       "version": "4.0.3",
       "resolved": "https://registry.npmmirror.com/is-glob/-/is-glob-4.0.3.tgz",
@@ -6151,6 +6231,11 @@
         "js-yaml": "bin/js-yaml.js"
       }
     },
+    "node_modules/jsencrypt": {
+      "version": "3.3.2",
+      "resolved": "https://registry.npmmirror.com/jsencrypt/-/jsencrypt-3.3.2.tgz",
+      "integrity": "sha512-arQR1R1ESGdAxY7ZheWr12wCaF2yF47v5qpB76TtV64H1pyGudk9Hvw8Y9tb/FiTIaaTRUyaSnm5T/Y53Ghm/A=="
+    },
     "node_modules/jsesc": {
       "version": "2.5.2",
       "resolved": "https://registry.npmmirror.com/jsesc/-/jsesc-2.5.2.tgz",
@@ -6813,6 +6898,16 @@
         "node": ">=0.10.0"
       }
     },
+    "node_modules/md5": {
+      "version": "2.3.0",
+      "resolved": "https://registry.npmmirror.com/md5/-/md5-2.3.0.tgz",
+      "integrity": "sha512-T1GITYmFaKuO91vxyoQMFETst+O71VUPEU3ze5GNzDm0OWdP8v1ziTaAEPUr/3kLsY3Sftgz242A1SetQiDL7g==",
+      "dependencies": {
+        "charenc": "0.0.2",
+        "crypt": "0.0.2",
+        "is-buffer": "~1.1.6"
+      }
+    },
     "node_modules/merge-stream": {
       "version": "2.0.0",
       "resolved": "https://registry.npmmirror.com/merge-stream/-/merge-stream-2.0.0.tgz",
@@ -6868,6 +6963,14 @@
         "node": ">=6"
       }
     },
+    "node_modules/min-document": {
+      "version": "2.19.0",
+      "resolved": "https://registry.npmmirror.com/min-document/-/min-document-2.19.0.tgz",
+      "integrity": "sha512-9Wy1B3m3f66bPPmU5hdA4DR4PB2OfDU/+GS3yAB7IQozE3tqXaVv2zOjgla7MEGSRv95+ILmOuvhLkOK6wJtCQ==",
+      "dependencies": {
+        "dom-walk": "^0.1.0"
+      }
+    },
     "node_modules/minimatch": {
       "version": "3.0.4",
       "resolved": "https://registry.npmmirror.com/minimatch/-/minimatch-3.0.4.tgz",
@@ -7142,7 +7245,6 @@
       "version": "4.1.1",
       "resolved": "https://registry.npmmirror.com/object-assign/-/object-assign-4.1.1.tgz",
       "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
-      "dev": true,
       "license": "MIT",
       "engines": {
         "node": ">=0.10.0"
@@ -7546,6 +7648,11 @@
         "node": ">=0.8"
       }
     },
+    "node_modules/parse-headers": {
+      "version": "2.0.5",
+      "resolved": "https://registry.npmmirror.com/parse-headers/-/parse-headers-2.0.5.tgz",
+      "integrity": "sha512-ft3iAoLOB/MlwbNXgzy43SWGP6sQki2jQvAyBg/zDFAgr9bfNWZIUj42Kw2eJIl8kEi4PbgE6U1Zau/HwI75HA=="
+    },
     "node_modules/parse-node-version": {
       "version": "1.0.1",
       "resolved": "https://registry.npmmirror.com/parse-node-version/-/parse-node-version-1.0.1.tgz",
@@ -7827,6 +7934,14 @@
         "node": ">=6.0.0"
       }
     },
+    "node_modules/process": {
+      "version": "0.5.2",
+      "resolved": "https://registry.npmmirror.com/process/-/process-0.5.2.tgz",
+      "integrity": "sha512-oNpcutj+nYX2FjdEW7PGltWhXulAnFlM0My/k48L90hARCOJtvBbQXc/6itV2jDvU5xAAtonP+r6wmQgCcbAUA==",
+      "engines": {
+        "node": ">= 0.6.0"
+      }
+    },
     "node_modules/progress": {
       "version": "2.0.3",
       "resolved": "https://registry.npmmirror.com/progress/-/progress-2.0.3.tgz",
@@ -8402,6 +8517,14 @@
         "queue-microtask": "^1.2.2"
       }
     },
+    "node_modules/rust-result": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmmirror.com/rust-result/-/rust-result-1.0.0.tgz",
+      "integrity": "sha512-6cJzSBU+J/RJCF063onnQf0cDUOHs9uZI1oroSGnHOph+CQTIJ5Pp2hK5kEQq1+7yE/EEWfulSNXAQ2jikPthA==",
+      "dependencies": {
+        "individual": "^2.0.0"
+      }
+    },
     "node_modules/rxjs": {
       "version": "7.5.5",
       "resolved": "https://registry.npmmirror.com/rxjs/-/rxjs-7.5.5.tgz",
@@ -8419,6 +8542,14 @@
       "dev": true,
       "license": "MIT"
     },
+    "node_modules/safe-json-parse": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmmirror.com/safe-json-parse/-/safe-json-parse-4.0.0.tgz",
+      "integrity": "sha512-RjZPPHugjK0TOzFrLZ8inw44s9bKox99/0AZW9o/BEQVrJfhI+fIHMErnPyRa89/yRXUUr93q+tiN6zhoVV4wQ==",
+      "dependencies": {
+        "rust-result": "^1.0.0"
+      }
+    },
     "node_modules/safer-buffer": {
       "version": "2.1.2",
       "resolved": "https://registry.npmmirror.com/safer-buffer/-/safer-buffer-2.1.2.tgz",
@@ -8597,6 +8728,19 @@
         "node": ">=0.1.14"
       }
     },
+    "node_modules/store": {
+      "version": "2.0.12",
+      "resolved": "https://registry.npmmirror.com/store/-/store-2.0.12.tgz",
+      "integrity": "sha512-eO9xlzDpXLiMr9W1nQ3Nfp9EzZieIQc10zPPMP5jsVV7bLOziSFFBP0XoDXACEIFtdI+rIz0NwWVA/QVJ8zJtw==",
+      "engines": {
+        "node": "*"
+      }
+    },
+    "node_modules/store2": {
+      "version": "2.14.2",
+      "resolved": "https://registry.npmmirror.com/store2/-/store2-2.14.2.tgz",
+      "integrity": "sha512-siT1RiqlfQnGqgT/YzXVUNsom9S0H1OX+dpdGN1xkyYATo4I6sep5NmsRD/40s3IIOvlCq6akxkqG82urIZW1w=="
+    },
     "node_modules/strict-uri-encode": {
       "version": "2.0.0",
       "resolved": "https://registry.npmmirror.com/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz",
@@ -8796,6 +8940,48 @@
       "dev": true,
       "license": "MIT"
     },
+    "node_modules/tcplayer.js": {
+      "version": "5.0.1",
+      "resolved": "https://registry.npmmirror.com/tcplayer.js/-/tcplayer.js-5.0.1.tgz",
+      "integrity": "sha512-CRe4eV6Jfh3USPhW5n2wI9BzUpwC2kZeBk05G9IWMxEe4OqlhFXol0uxo+WE1R21e8dXoHB9VXIg7RqQLTe9qA==",
+      "dependencies": {
+        "babel-runtime": "^6.9.2",
+        "blueimp-md5": "^2.10.0",
+        "global": "4.3.2",
+        "jsencrypt": "^3.2.0",
+        "md5": "^2.3.0",
+        "query-string": "^5.0.1",
+        "safe-json-parse": "4.0.0",
+        "store": "^2.0.12",
+        "store2": "^2.7.1",
+        "tsml": "1.0.1",
+        "videojs-font": "2.1.0",
+        "videojs-ie8": "1.1.2",
+        "videojs-vtt.js": "0.12.4",
+        "xhr": "2.4.0"
+      }
+    },
+    "node_modules/tcplayer.js/node_modules/query-string": {
+      "version": "5.1.1",
+      "resolved": "https://registry.npmmirror.com/query-string/-/query-string-5.1.1.tgz",
+      "integrity": "sha512-gjWOsm2SoGlgLEdAGt7a6slVOk9mGiXmPFMqrEhLQ68rhQuBnpfs3+EmlvqKyxnCo9/PPlF+9MtY02S1aFg+Jw==",
+      "dependencies": {
+        "decode-uri-component": "^0.2.0",
+        "object-assign": "^4.1.0",
+        "strict-uri-encode": "^1.0.0"
+      },
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
+    "node_modules/tcplayer.js/node_modules/strict-uri-encode": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmmirror.com/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz",
+      "integrity": "sha512-R3f198pcvnB+5IpnBlRkphuE9n46WyVl8I39W/ZUTZLz4nqSP/oLYUrcnJrw462Ds8he4YKMov2efsTIw1BDGQ==",
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
     "node_modules/text-segmentation": {
       "version": "1.0.3",
       "resolved": "https://registry.npmmirror.com/text-segmentation/-/text-segmentation-1.0.3.tgz",
@@ -8878,6 +9064,12 @@
       "integrity": "sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg==",
       "license": "0BSD"
     },
+    "node_modules/tsml": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmmirror.com/tsml/-/tsml-1.0.1.tgz",
+      "integrity": "sha512-3KmepnH9SUsoOVtg013CRrL7c+AK7ECaquAsJdvu4288EDJuraqBlP4PDXT/rLEJ9YDn4jqLAzRJsnFPx+V6lg==",
+      "deprecated": "no longer maintained"
+    },
     "node_modules/tsutils": {
       "version": "3.21.0",
       "resolved": "https://registry.npmmirror.com/tsutils/-/tsutils-3.21.0.tgz",
@@ -9161,6 +9353,27 @@
         "mutation-observer": "^1.0.3"
       }
     },
+    "node_modules/videojs-font": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmmirror.com/videojs-font/-/videojs-font-2.1.0.tgz",
+      "integrity": "sha512-zFqWpLrXf1q8NtYx5qtZhMC6SLUFScDmR6j+UGPogobxR21lvXShhnzcNNMdOxJUuFLiToJ/BPpFUQwX4xhpvA=="
+    },
+    "node_modules/videojs-ie8": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmmirror.com/videojs-ie8/-/videojs-ie8-1.1.2.tgz",
+      "integrity": "sha512-0Zb2T4MLkpfZbeGMK/Z93b8Lrepr+rLFoHgQV1CoDeFqXvH7b+Vsd/VHoILGxQrgCSHFQ7mAODR6oyMjuiD4/g==",
+      "dependencies": {
+        "es5-shim": "^4.5.1"
+      }
+    },
+    "node_modules/videojs-vtt.js": {
+      "version": "0.12.4",
+      "resolved": "https://registry.npmmirror.com/videojs-vtt.js/-/videojs-vtt.js-0.12.4.tgz",
+      "integrity": "sha512-JQ5eozH5SLOL5xI8ALb1aWf9HjcewQmOytf1gPIsFBTQlSgtSdJ8E8x0GO0ZEXVtFCaPDFiYWAhrjuTI125tBQ==",
+      "dependencies": {
+        "global": "^4.3.1"
+      }
+    },
     "node_modules/vite": {
       "version": "2.9.8",
       "resolved": "https://registry.npmmirror.com/vite/-/vite-2.9.8.tgz",
@@ -9765,6 +9978,25 @@
       "dev": true,
       "license": "ISC"
     },
+    "node_modules/xhr": {
+      "version": "2.4.0",
+      "resolved": "https://registry.npmmirror.com/xhr/-/xhr-2.4.0.tgz",
+      "integrity": "sha512-TUbBsdAuJbX8olk9hsDwGK8P1ri1XlV+PdEWkYw+HQQbpkiBR8PLgD1F3kQDPBs9l4Px34hP9rCYAZOCCAENbw==",
+      "dependencies": {
+        "global": "~4.3.0",
+        "is-function": "^1.0.1",
+        "parse-headers": "^2.0.0",
+        "xtend": "^4.0.0"
+      }
+    },
+    "node_modules/xtend": {
+      "version": "4.0.2",
+      "resolved": "https://registry.npmmirror.com/xtend/-/xtend-4.0.2.tgz",
+      "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==",
+      "engines": {
+        "node": ">=0.4"
+      }
+    },
     "node_modules/y18n": {
       "version": "4.0.3",
       "resolved": "https://registry.npmmirror.com/y18n/-/y18n-4.0.3.tgz",
@@ -11976,6 +12208,27 @@
         "@babel/helper-define-polyfill-provider": "^0.3.0"
       }
     },
+    "babel-runtime": {
+      "version": "6.26.0",
+      "resolved": "https://registry.npmmirror.com/babel-runtime/-/babel-runtime-6.26.0.tgz",
+      "integrity": "sha512-ITKNuq2wKlW1fJg9sSW52eepoYgZBggvOAHC0u/CYu/qxQ9EVzThCgR69BnSXLHjy2f7SY5zaQ4yt7H9ZVxY2g==",
+      "requires": {
+        "core-js": "^2.4.0",
+        "regenerator-runtime": "^0.11.0"
+      },
+      "dependencies": {
+        "core-js": {
+          "version": "2.6.12",
+          "resolved": "https://registry.npmmirror.com/core-js/-/core-js-2.6.12.tgz",
+          "integrity": "sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ=="
+        },
+        "regenerator-runtime": {
+          "version": "0.11.1",
+          "resolved": "https://registry.npmmirror.com/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz",
+          "integrity": "sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg=="
+        }
+      }
+    },
     "babel-walk": {
       "version": "3.0.0-canary-5",
       "resolved": "https://registry.npmmirror.com/babel-walk/-/babel-walk-3.0.0-canary-5.tgz",
@@ -12013,6 +12266,11 @@
         "readable-stream": "^3.4.0"
       }
     },
+    "blueimp-md5": {
+      "version": "2.19.0",
+      "resolved": "https://registry.npmmirror.com/blueimp-md5/-/blueimp-md5-2.19.0.tgz",
+      "integrity": "sha512-DRQrD6gJyy8FbiE4s+bDoXS9hiW3Vbx5uCdwvcCf3zLHL+Iv7LtGHLpr+GZV8rHG8tK766FGYBwRbu8pELTt+w=="
+    },
     "brace-expansion": {
       "version": "1.1.11",
       "resolved": "https://registry.npmmirror.com/brace-expansion/-/brace-expansion-1.1.11.tgz",
@@ -12159,6 +12417,11 @@
       "integrity": "sha512-mT8iDcrh03qDGRRmoA2hmBJnxpllMR+0/0qlzjqZES6NdiWDcZkCNAk4rPFZ9Q85r27unkiNNg8ZOiwZXBHwcA==",
       "dev": true
     },
+    "charenc": {
+      "version": "0.0.2",
+      "resolved": "https://registry.npmmirror.com/charenc/-/charenc-0.0.2.tgz",
+      "integrity": "sha512-yrLQ/yVUFXkzg7EDQsPieE/53+0RlaWTs+wBrvW36cyilJ2SaDWfl4Yj7MtLTXleV9uEKefbAGUPv2/iWSooRA=="
+    },
     "ci-info": {
       "version": "1.6.0",
       "resolved": "https://registry.npmmirror.com/ci-info/-/ci-info-1.6.0.tgz",
@@ -12405,6 +12668,11 @@
         "which": "^2.0.1"
       }
     },
+    "crypt": {
+      "version": "0.0.2",
+      "resolved": "https://registry.npmmirror.com/crypt/-/crypt-0.0.2.tgz",
+      "integrity": "sha512-mCxBlsHFYh9C+HVpiEacem8FEBnMXgU9gy4zmNC+SXAZNB/1idgp/aulFJ4FgCi7GPEVbfyng092GqL2k2rmow=="
+    },
     "css-line-break": {
       "version": "2.1.0",
       "resolved": "https://registry.npmmirror.com/css-line-break/-/css-line-break-2.1.0.tgz",
@@ -12541,6 +12809,11 @@
         }
       }
     },
+    "dom-walk": {
+      "version": "0.1.2",
+      "resolved": "https://registry.npmmirror.com/dom-walk/-/dom-walk-0.1.2.tgz",
+      "integrity": "sha512-6QvTW9mrGeIegrFXdtQi9pk7O/nSK6lSdXW2eqUspN5LWD7UTji2Fqw5V2YLjBpHEoU9Xl/eUWNpDeZvoyOv2w=="
+    },
     "domelementtype": {
       "version": "2.2.0",
       "resolved": "https://registry.npmmirror.com/domelementtype/-/domelementtype-2.2.0.tgz",
@@ -12667,6 +12940,11 @@
       "integrity": "sha512-1HQ2M2sPtxwnvOvT1ZClHyQDiggdNjURWpY2we6aMKCQiUVxTmVs2UYPLIrD84sS+kMdUwfBSylbJPwNnBrnHQ==",
       "dev": true
     },
+    "es5-shim": {
+      "version": "4.6.7",
+      "resolved": "https://registry.npmmirror.com/es5-shim/-/es5-shim-4.6.7.tgz",
+      "integrity": "sha512-jg21/dmlrNQI7JyyA2w7n+yifSxBng0ZralnSfVZjoCawgNTCnS+yBCyVM9DL5itm7SUnDGgv7hcq2XCZX4iRQ=="
+    },
     "esbuild": {
       "version": "0.14.54",
       "resolved": "https://registry.npmmirror.com/esbuild/-/esbuild-0.14.54.tgz",
@@ -13437,6 +13715,15 @@
         "is-glob": "^4.0.3"
       }
     },
+    "global": {
+      "version": "4.3.2",
+      "resolved": "https://registry.npmmirror.com/global/-/global-4.3.2.tgz",
+      "integrity": "sha512-/4AybdwIDU4HkCUbJkZdWpe4P6vuw/CUtu+0I1YlLIPe7OlUO7KNJ+q/rO70CW2/NW6Jc6I62++Hzsf5Alu6rQ==",
+      "requires": {
+        "min-document": "^2.19.0",
+        "process": "~0.5.1"
+      }
+    },
     "global-modules": {
       "version": "1.0.0",
       "resolved": "https://registry.npmmirror.com/global-modules/-/global-modules-1.0.0.tgz",
@@ -13652,6 +13939,11 @@
       "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==",
       "dev": true
     },
+    "individual": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmmirror.com/individual/-/individual-2.0.0.tgz",
+      "integrity": "sha512-pWt8hBCqJsUWI/HtcfWod7+N9SgAqyPEaF7JQjwzjn5vGrpg6aQ5qeAFQ7dx//UH4J1O+7xqew+gCeeFt6xN/g=="
+    },
     "inflight": {
       "version": "1.0.6",
       "resolved": "https://registry.npmmirror.com/inflight/-/inflight-1.0.6.tgz",
@@ -13846,6 +14138,11 @@
         "is-windows": "^1.0.1"
       }
     },
+    "is-buffer": {
+      "version": "1.1.6",
+      "resolved": "https://registry.npmmirror.com/is-buffer/-/is-buffer-1.1.6.tgz",
+      "integrity": "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w=="
+    },
     "is-ci": {
       "version": "1.2.1",
       "resolved": "https://registry.npmmirror.com/is-ci/-/is-ci-1.2.1.tgz",
@@ -13894,6 +14191,11 @@
       "integrity": "sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ==",
       "dev": true
     },
+    "is-function": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmmirror.com/is-function/-/is-function-1.0.2.tgz",
+      "integrity": "sha512-lw7DUp0aWXYg+CBCN+JKkcE0Q2RayZnSvnZBlwgxHBQhqt5pZNVy4Ri7H9GmmXkdu7LUthszM+Tor1u/2iBcpQ=="
+    },
     "is-glob": {
       "version": "4.0.3",
       "resolved": "https://registry.npmmirror.com/is-glob/-/is-glob-4.0.3.tgz",
@@ -14039,6 +14341,11 @@
         "argparse": "^2.0.1"
       }
     },
+    "jsencrypt": {
+      "version": "3.3.2",
+      "resolved": "https://registry.npmmirror.com/jsencrypt/-/jsencrypt-3.3.2.tgz",
+      "integrity": "sha512-arQR1R1ESGdAxY7ZheWr12wCaF2yF47v5qpB76TtV64H1pyGudk9Hvw8Y9tb/FiTIaaTRUyaSnm5T/Y53Ghm/A=="
+    },
     "jsesc": {
       "version": "2.5.2",
       "resolved": "https://registry.npmmirror.com/jsesc/-/jsesc-2.5.2.tgz",
@@ -14500,6 +14807,16 @@
       "integrity": "sha512-8y/eV9QQZCiyn1SprXSrCmqJN0yNRATe+PO8ztwqrvrbdRLA3eYJF0yaR0YayLWkMbsQSKWS9N2gPcGEc4UsZg==",
       "dev": true
     },
+    "md5": {
+      "version": "2.3.0",
+      "resolved": "https://registry.npmmirror.com/md5/-/md5-2.3.0.tgz",
+      "integrity": "sha512-T1GITYmFaKuO91vxyoQMFETst+O71VUPEU3ze5GNzDm0OWdP8v1ziTaAEPUr/3kLsY3Sftgz242A1SetQiDL7g==",
+      "requires": {
+        "charenc": "0.0.2",
+        "crypt": "0.0.2",
+        "is-buffer": "~1.1.6"
+      }
+    },
     "merge-stream": {
       "version": "2.0.0",
       "resolved": "https://registry.npmmirror.com/merge-stream/-/merge-stream-2.0.0.tgz",
@@ -14535,6 +14852,14 @@
       "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==",
       "dev": true
     },
+    "min-document": {
+      "version": "2.19.0",
+      "resolved": "https://registry.npmmirror.com/min-document/-/min-document-2.19.0.tgz",
+      "integrity": "sha512-9Wy1B3m3f66bPPmU5hdA4DR4PB2OfDU/+GS3yAB7IQozE3tqXaVv2zOjgla7MEGSRv95+ILmOuvhLkOK6wJtCQ==",
+      "requires": {
+        "dom-walk": "^0.1.0"
+      }
+    },
     "minimatch": {
       "version": "3.0.4",
       "resolved": "https://registry.npmmirror.com/minimatch/-/minimatch-3.0.4.tgz",
@@ -14733,8 +15058,7 @@
     "object-assign": {
       "version": "4.1.1",
       "resolved": "https://registry.npmmirror.com/object-assign/-/object-assign-4.1.1.tgz",
-      "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==",
-      "dev": true
+      "integrity": "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg=="
     },
     "object-inspect": {
       "version": "1.11.0",
@@ -14996,6 +15320,11 @@
         "path-root": "^0.1.1"
       }
     },
+    "parse-headers": {
+      "version": "2.0.5",
+      "resolved": "https://registry.npmmirror.com/parse-headers/-/parse-headers-2.0.5.tgz",
+      "integrity": "sha512-ft3iAoLOB/MlwbNXgzy43SWGP6sQki2jQvAyBg/zDFAgr9bfNWZIUj42Kw2eJIl8kEi4PbgE6U1Zau/HwI75HA=="
+    },
     "parse-node-version": {
       "version": "1.0.1",
       "resolved": "https://registry.npmmirror.com/parse-node-version/-/parse-node-version-1.0.1.tgz",
@@ -15179,6 +15508,11 @@
         "fast-diff": "^1.1.2"
       }
     },
+    "process": {
+      "version": "0.5.2",
+      "resolved": "https://registry.npmmirror.com/process/-/process-0.5.2.tgz",
+      "integrity": "sha512-oNpcutj+nYX2FjdEW7PGltWhXulAnFlM0My/k48L90hARCOJtvBbQXc/6itV2jDvU5xAAtonP+r6wmQgCcbAUA=="
+    },
     "progress": {
       "version": "2.0.3",
       "resolved": "https://registry.npmmirror.com/progress/-/progress-2.0.3.tgz",
@@ -15591,6 +15925,14 @@
         "queue-microtask": "^1.2.2"
       }
     },
+    "rust-result": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmmirror.com/rust-result/-/rust-result-1.0.0.tgz",
+      "integrity": "sha512-6cJzSBU+J/RJCF063onnQf0cDUOHs9uZI1oroSGnHOph+CQTIJ5Pp2hK5kEQq1+7yE/EEWfulSNXAQ2jikPthA==",
+      "requires": {
+        "individual": "^2.0.0"
+      }
+    },
     "rxjs": {
       "version": "7.5.5",
       "resolved": "https://registry.npmmirror.com/rxjs/-/rxjs-7.5.5.tgz",
@@ -15606,6 +15948,14 @@
       "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==",
       "dev": true
     },
+    "safe-json-parse": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmmirror.com/safe-json-parse/-/safe-json-parse-4.0.0.tgz",
+      "integrity": "sha512-RjZPPHugjK0TOzFrLZ8inw44s9bKox99/0AZW9o/BEQVrJfhI+fIHMErnPyRa89/yRXUUr93q+tiN6zhoVV4wQ==",
+      "requires": {
+        "rust-result": "^1.0.0"
+      }
+    },
     "safer-buffer": {
       "version": "2.1.2",
       "resolved": "https://registry.npmmirror.com/safer-buffer/-/safer-buffer-2.1.2.tgz",
@@ -15730,6 +16080,16 @@
       "resolved": "https://registry.npmmirror.com/stackblur-canvas/-/stackblur-canvas-2.6.0.tgz",
       "integrity": "sha512-8S1aIA+UoF6erJYnglGPug6MaHYGo1Ot7h5fuXx4fUPvcvQfcdw2o/ppCse63+eZf8PPidSu4v1JnmEVtEDnpg=="
     },
+    "store": {
+      "version": "2.0.12",
+      "resolved": "https://registry.npmmirror.com/store/-/store-2.0.12.tgz",
+      "integrity": "sha512-eO9xlzDpXLiMr9W1nQ3Nfp9EzZieIQc10zPPMP5jsVV7bLOziSFFBP0XoDXACEIFtdI+rIz0NwWVA/QVJ8zJtw=="
+    },
+    "store2": {
+      "version": "2.14.2",
+      "resolved": "https://registry.npmmirror.com/store2/-/store2-2.14.2.tgz",
+      "integrity": "sha512-siT1RiqlfQnGqgT/YzXVUNsom9S0H1OX+dpdGN1xkyYATo4I6sep5NmsRD/40s3IIOvlCq6akxkqG82urIZW1w=="
+    },
     "strict-uri-encode": {
       "version": "2.0.0",
       "resolved": "https://registry.npmmirror.com/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz",
@@ -15850,6 +16210,44 @@
       "integrity": "sha512-hqTN6kW+pN6/qro6G9OZ7ceDQOcYno020zBQKpZQLsJhYTDMCMNfXi/Y8duF5iW+4WWZr42ry0MMkcRGpbwG2A==",
       "dev": true
     },
+    "tcplayer.js": {
+      "version": "5.0.1",
+      "resolved": "https://registry.npmmirror.com/tcplayer.js/-/tcplayer.js-5.0.1.tgz",
+      "integrity": "sha512-CRe4eV6Jfh3USPhW5n2wI9BzUpwC2kZeBk05G9IWMxEe4OqlhFXol0uxo+WE1R21e8dXoHB9VXIg7RqQLTe9qA==",
+      "requires": {
+        "babel-runtime": "^6.9.2",
+        "blueimp-md5": "^2.10.0",
+        "global": "4.3.2",
+        "jsencrypt": "^3.2.0",
+        "md5": "^2.3.0",
+        "query-string": "^5.0.1",
+        "safe-json-parse": "4.0.0",
+        "store": "^2.0.12",
+        "store2": "^2.7.1",
+        "tsml": "1.0.1",
+        "videojs-font": "2.1.0",
+        "videojs-ie8": "1.1.2",
+        "videojs-vtt.js": "0.12.4",
+        "xhr": "2.4.0"
+      },
+      "dependencies": {
+        "query-string": {
+          "version": "5.1.1",
+          "resolved": "https://registry.npmmirror.com/query-string/-/query-string-5.1.1.tgz",
+          "integrity": "sha512-gjWOsm2SoGlgLEdAGt7a6slVOk9mGiXmPFMqrEhLQ68rhQuBnpfs3+EmlvqKyxnCo9/PPlF+9MtY02S1aFg+Jw==",
+          "requires": {
+            "decode-uri-component": "^0.2.0",
+            "object-assign": "^4.1.0",
+            "strict-uri-encode": "^1.0.0"
+          }
+        },
+        "strict-uri-encode": {
+          "version": "1.1.0",
+          "resolved": "https://registry.npmmirror.com/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz",
+          "integrity": "sha512-R3f198pcvnB+5IpnBlRkphuE9n46WyVl8I39W/ZUTZLz4nqSP/oLYUrcnJrw462Ds8he4YKMov2efsTIw1BDGQ=="
+        }
+      }
+    },
     "text-segmentation": {
       "version": "1.0.3",
       "resolved": "https://registry.npmmirror.com/text-segmentation/-/text-segmentation-1.0.3.tgz",
@@ -15914,6 +16312,11 @@
       "resolved": "https://registry.npmmirror.com/tslib/-/tslib-2.3.0.tgz",
       "integrity": "sha512-N82ooyxVNm6h1riLCoyS9e3fuJ3AMG2zIZs2Gd1ATcSFjSA23Q0fzjjZeh0jbJvWVDZ0cJT8yaNNaaXHzueNjg=="
     },
+    "tsml": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmmirror.com/tsml/-/tsml-1.0.1.tgz",
+      "integrity": "sha512-3KmepnH9SUsoOVtg013CRrL7c+AK7ECaquAsJdvu4288EDJuraqBlP4PDXT/rLEJ9YDn4jqLAzRJsnFPx+V6lg=="
+    },
     "tsutils": {
       "version": "3.21.0",
       "resolved": "https://registry.npmmirror.com/tsutils/-/tsutils-3.21.0.tgz",
@@ -16102,6 +16505,27 @@
         "mutation-observer": "^1.0.3"
       }
     },
+    "videojs-font": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmmirror.com/videojs-font/-/videojs-font-2.1.0.tgz",
+      "integrity": "sha512-zFqWpLrXf1q8NtYx5qtZhMC6SLUFScDmR6j+UGPogobxR21lvXShhnzcNNMdOxJUuFLiToJ/BPpFUQwX4xhpvA=="
+    },
+    "videojs-ie8": {
+      "version": "1.1.2",
+      "resolved": "https://registry.npmmirror.com/videojs-ie8/-/videojs-ie8-1.1.2.tgz",
+      "integrity": "sha512-0Zb2T4MLkpfZbeGMK/Z93b8Lrepr+rLFoHgQV1CoDeFqXvH7b+Vsd/VHoILGxQrgCSHFQ7mAODR6oyMjuiD4/g==",
+      "requires": {
+        "es5-shim": "^4.5.1"
+      }
+    },
+    "videojs-vtt.js": {
+      "version": "0.12.4",
+      "resolved": "https://registry.npmmirror.com/videojs-vtt.js/-/videojs-vtt.js-0.12.4.tgz",
+      "integrity": "sha512-JQ5eozH5SLOL5xI8ALb1aWf9HjcewQmOytf1gPIsFBTQlSgtSdJ8E8x0GO0ZEXVtFCaPDFiYWAhrjuTI125tBQ==",
+      "requires": {
+        "global": "^4.3.1"
+      }
+    },
     "vite": {
       "version": "2.9.8",
       "resolved": "https://registry.npmmirror.com/vite/-/vite-2.9.8.tgz",
@@ -16527,6 +16951,22 @@
       "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==",
       "dev": true
     },
+    "xhr": {
+      "version": "2.4.0",
+      "resolved": "https://registry.npmmirror.com/xhr/-/xhr-2.4.0.tgz",
+      "integrity": "sha512-TUbBsdAuJbX8olk9hsDwGK8P1ri1XlV+PdEWkYw+HQQbpkiBR8PLgD1F3kQDPBs9l4Px34hP9rCYAZOCCAENbw==",
+      "requires": {
+        "global": "~4.3.0",
+        "is-function": "^1.0.1",
+        "parse-headers": "^2.0.0",
+        "xtend": "^4.0.0"
+      }
+    },
+    "xtend": {
+      "version": "4.0.2",
+      "resolved": "https://registry.npmmirror.com/xtend/-/xtend-4.0.2.tgz",
+      "integrity": "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ=="
+    },
     "y18n": {
       "version": "4.0.3",
       "resolved": "https://registry.npmmirror.com/y18n/-/y18n-4.0.3.tgz",

+ 1 - 0
package.json

@@ -42,6 +42,7 @@
     "qrcode": "^1.5.1",
     "qrcode.vue": "^3.3.3",
     "query-string": "^7.1.1",
+    "tcplayer.js": "^5.0.1",
     "umi-request": "^1.4.0",
     "vant": "^3.4.6",
     "vconsole": "^3.14.6",

+ 101 - 0
src/components/col-video-tcplayer/index.module.less

@@ -0,0 +1,101 @@
+.video-container {
+  position: relative;
+  width: 100%;
+  --plyr-color-main: #FF8057;
+  height: 210px;
+
+  video {
+    width: 100%;
+    // object-fit: cover;
+  }
+
+  :global {
+    .video-back {
+      position: absolute;
+      left: 20px;
+      top: 20px;
+      color: #fff;
+      z-index: 99;
+      font-size: 24px;
+      width: 30px;
+      height: 30px;
+      background-color: rgba(0, 0, 0, 0.5);
+      border-radius: 50%;
+      padding: 4px 5px 4px 3px;
+    }
+
+    .plyr__poster {
+      background-size: cover;
+    }
+
+    .plyr__control--overlaid {
+      border: 1px solid #fff;
+      background-color: rgba(0, 0, 0, 0.2) !important;
+    }
+
+    .plyr--video .plyr__control:hover {
+      background-color: transparent !important;
+    }
+
+    .video-js {
+      width: 100%;
+      height: 100%;
+    }
+
+    .tcp-skin .vjs-play-progress {
+      background-color: var(--van-primary) !important;
+    }
+  }
+
+  .video {
+    position: relative;
+  }
+
+}
+
+.loadingVideo {
+  position: absolute;
+  top: 0;
+  left: 0;
+  bottom: 0;
+  right: 0;
+  background: rgba(0, 0, 0, 0.9);
+  z-index: 10;
+}
+
+.playOver {
+  background: rgba(0, 0, 0, 0.5);
+  color: #fff;
+  display: flex;
+  align-items: center;
+  justify-content: center;
+  flex-direction: column;
+
+  .tips {
+    font-size: 15px;
+    color: #ffffff;
+  }
+
+  .btn {
+    margin: 10px 0;
+    min-width: 94px;
+    font-size: 14px;
+    height: 28px;
+    line-height: 28px;
+  }
+
+  .replay {
+    padding-top: 12px;
+  }
+}
+
+.freeTxt {
+  font-size: 15px;
+  color: #ffffff;
+  line-height: 21px;
+  padding-top: 10px;
+}
+
+.freeRate {
+  color: #32ffd8;
+}

+ 162 - 0
src/components/col-video-tcplayer/index.tsx

@@ -0,0 +1,162 @@
+import { defineComponent, PropType } from 'vue'
+import styles from './index.module.less'
+
+import TCPlayer from 'tcplayer.js'
+import 'tcplayer.js/dist/tcplayer.css'
+// import iconVideoPlay from '@/common/images/icon_video_play.png';
+// import { browser } from '@/helpers/utils';
+export default defineComponent({
+  name: 'o-video',
+  props: {
+    setting: {
+      type: Object,
+      default: () => {}
+    },
+    controls: {
+      type: Boolean,
+      default: true
+    },
+    height: String,
+    src: {
+      type: String,
+      default: ''
+    },
+    poster: {
+      type: String,
+      default: ''
+    },
+    styleValue: {
+      type: Object,
+      default: () => ({})
+    },
+    preload: {
+      type: String as PropType<'auto' | 'metadata' | 'none'>,
+      default: 'auto'
+    },
+    currentTime: {
+      type: Boolean,
+      default: true
+    },
+    playsinline: {
+      type: Boolean,
+      default: true
+    },
+    onPlay: {
+      type: Function,
+      default: () => {}
+    }
+  },
+  emits: ['exitfullscreen'],
+  data() {
+    return {
+      videoID: 'video' + Date.now() + Math.floor(Math.random() * 100),
+      player: null as any,
+      loading: true // 首次进入加载中
+    }
+  },
+  mounted() {
+    this._init()
+  },
+  methods: {
+    _init() {
+      const Button = TCPlayer.getComponent('Button')
+      const BigPlayButton = TCPlayer.getComponent('BigPlayButton')
+      BigPlayButton.prototype.createEl = function () {
+        const el = Button.prototype.createEl.call(this)
+        const _html =
+          '<button><svg width="41px"height="41px"viewBox="0 0 41 41"version="1.1"xmlns="http://www.w3.org/2000/svg"xmlns:xlink="http://www.w3.org/1999/xlink"><g stroke="none"stroke-width="1"fill="none"fill-rule="evenodd"><g transform="translate(-167.000000, -155.000000)"><g transform="translate(0.000000, 85.000000)"><g transform="translate(158.000000, 70.000000)"><g transform="translate(9.000000, 0.000000)"><circle id="椭圆形"stroke="#FFFFFF"fill-opacity="0.1"fill="#D8D8D8"cx="20.5"cy="20.5"r="20"></circle><path d="M14.5483871,27.6859997 L14.5483871,13.4342349 C14.5480523,12.8729571 14.8729597,12.356555 15.3949624,12.0887034 C15.9169651,11.8208518 16.5522696,11.8445472 17.0503046,12.1504437 L28.6530473,19.2778563 C29.1119763,19.5602271 29.3887725,20.0426422 29.3887725,20.5601173 C29.3887725,21.0775924 29.1119763,21.5600075 28.6530473,21.8423783 L17.0503046,28.9697909 C16.5522696,29.2756874 15.9169651,29.2993828 15.3949624,29.0315312 C14.8729597,28.7636796 14.5480523,28.2472775 14.5483871,27.6859997 Z"id="路径"fill="#FFFFFF"fill-rule="nonzero"></path></g></g></g></g></g></svg></button>'
+
+        el.appendChild(
+          TCPlayer.dom.createEl('div', {
+            className: 'vjs-button-icon',
+            innerHTML: _html
+          })
+        )
+        return el
+      }
+      this.player = TCPlayer(this.videoID, {
+        appID: '',
+        controls: this.controls
+      }) // player-container-id 为播放器容器 ID,必须与 html 中一致
+      if (this.player) {
+        this.player.src(this.src) // url 播放地址
+        this.player.poster(this.poster || '')
+
+        if (this.preload === 'none') {
+          this.loading = false
+        }
+        this.player.on('loadstart', () => {
+          this.loading = false
+          this.domPlayVisibility(false)
+        })
+        this.player.on('play', () => {
+          this.onPlay && this.onPlay(this.player)
+        })
+
+        this.player.on('fullscreenchange', () => {
+          if (this.player.isFullscreen()) {
+            console.log('fullscreen')
+            const i = document.createElement('i')
+            i.id = 'fullscreen-back'
+            i.className = 'van-icon van-icon-arrow-left video-back'
+            i.addEventListener('click', () => {
+              this.player.exitFullscreen()
+            })
+            // console.log(document.getElementsByClassName('video-js'))
+            document.getElementsByClassName('video-js')[0].appendChild(i)
+          } else {
+            console.log('exitfullscreen')
+            const i = document.getElementById('fullscreen-back')
+            i && i.remove()
+          }
+        })
+      }
+    },
+    // 操作功能
+    domPlayVisibility(hide = true) {
+      const controls = document.querySelector('.vjs-big-play-button')
+      const controls2 = document.querySelector('.vjs-control-bar')
+      if (hide) {
+        controls?.setAttribute('style', 'display:none')
+        controls2?.setAttribute('style', 'display:none')
+      } else {
+        controls?.removeAttribute('style')
+        setTimeout(() => {
+          controls2?.removeAttribute('style')
+        }, 200)
+      }
+    },
+
+    onReplay() {
+      this.player.currentTime(0)
+      this.player.play()
+      this.domPlayVisibility(false)
+    },
+    onStop() {
+      this.player.currentTime(0)
+      this.player.pause()
+    }
+  },
+  unmounted() {
+    this.player?.pause()
+    this.player?.src('')
+    this.player?.dispose()
+  },
+  render() {
+    return (
+      <div class={styles['video-container']}>
+        <video
+          ref="video"
+          id={this.videoID}
+          class={styles['video']}
+          src={this.src}
+          playsinline={this.playsinline}
+          poster={this.poster}
+          preload={this.preload}
+          style={{ ...this.styleValue }}
+        ></video>
+        {/* 加载视频使用 */}
+      </div>
+    )
+  }
+})

+ 2 - 1
src/helpers/request.ts

@@ -85,7 +85,8 @@ request.interceptors.response.use(
       throw new Error(msg)
     }
     const data = await res.clone().json()
-    if (data.code !== 200 && data.errCode !== 0 && data.code !== 5004) {
+    const otherCode = [200, 0, 999, 5004]
+    if (!otherCode.includes(data.code)) {
       let msg = data.msg || data.message || '处理失败,请重试'
       if (initRequest) {
         if (data.code === 403 || data.code === 401) {

+ 39 - 1
src/helpers/utils.ts

@@ -118,6 +118,12 @@ export const getRandomKey = () => {
   return key
 }
 
+export function checkPhone(phone: string) {
+  const phoneRule =
+    /^((13[0-9])|(14(0|[5-7]|9))|(15([0-3]|[5-9]))|(16(2|[5-7]))|(17[0-8])|(18[0-9])|(19([0-3]|[5-9])))\d{8}$/
+  return phoneRule.test(phone)
+}
+
 /**
  * 删除token
  */
@@ -138,7 +144,7 @@ export const setAuth = (token: any) => {
  * 获取token
  */
 export const getAuth = () => {
-  sessionStorage.getItem('Authorization')
+  return sessionStorage.getItem('Authorization')
 }
 
 /**
@@ -233,3 +239,35 @@ export const getUrlCode = (name = 'code') => {
   }
   return search[name]
 }
+
+export const getGradeCh = (grade: number) => {
+  const template = [
+    '一年级',
+    '二年级',
+    '三年级',
+    '四年级',
+    '五年级',
+    '六年级',
+    '七年级',
+    '八年级',
+    '九年级'
+  ]
+
+  return template[grade]
+}
+
+// 秒转分
+export const getSecondRPM = (second: number, type?: string) => {
+  if (isNaN(second)) return '00:00'
+  const mm = Math.floor(second / 60)
+    .toString()
+    .padStart(2, '0')
+  const dd = Math.floor(second % 60)
+    .toString()
+    .padStart(2, '0')
+  if (type === 'cn') {
+    return mm + '分' + dd + '秒'
+  } else {
+    return mm + ':' + dd
+  }
+}

+ 21 - 0
src/router/routes-common.ts

@@ -234,6 +234,20 @@ export const router = [
     meta: {
       title: '活动奖品'
     }
+  },
+  {
+    path: '/creation',
+    component: () => import('@/views/creation/index'),
+    meta: {
+      title: '作品详情'
+    }
+  },
+  {
+    path: '/creation-edit',
+    component: () => import('@/views/creation/edit/index'),
+    meta: {
+      title: '作品详情'
+    }
   }
 ]
 
@@ -342,5 +356,12 @@ export const rootRouter = [
     meta: {
       title: '机构老师注册'
     }
+  },
+  {
+    path: '/shareCreation',
+    component: () => import('@/views/creation/index-share'),
+    meta: {
+      title: '作品详情'
+    }
   }
 ]

+ 21 - 96
src/router/routes-tenant.ts

@@ -63,6 +63,13 @@ const noLoginRouter = [
     path: '/helpCenterDetail',
     name: 'helpCenterDetail',
     component: () => import('@/views/article-center/help-center-detail')
+  },
+  {
+    path: '/shareCreation',
+    component: () => import('@/views/creation/index-share'),
+    meta: {
+      title: '作品详情'
+    }
   }
 ]
 
@@ -234,46 +241,6 @@ export default [
           }
         ]
       },
-      // {
-      //   path: '/practiceClass',
-      //   name: 'practiceClass',
-      //   component: () => import('@/student/practice-class/index'),
-      //   meta: {
-      //     title: '陪练课'
-      //   }
-      // },
-      // {
-      //   path: '/videoDetail',
-      //   name: 'videoDetail',
-      //   component: () => import('@/student/video-class/video-detail'),
-      //   meta: {
-      //     title: '视频课'
-      //   }
-      // },
-      // {
-      //   path: '/videoClassDetail',
-      //   name: 'videoClassDetail',
-      //   component: () => import('@/student/video-class/video-class-detail'),
-      //   meta: {
-      //     title: '视频课详情'
-      //   }
-      // },
-      // {
-      //   path: '/liveDetail',
-      //   name: 'liveDetail',
-      //   component: () => import('@/student/live-class/live-detail'),
-      //   meta: {
-      //     title: '直播课详情'
-      //   }
-      // },
-      // {
-      //   path: '/memberActive',
-      //   name: 'memberActive',
-      //   component: () => import('@/student/member-center/member-active'),
-      //   meta: {
-      //     title: '小酷Ai会员大放价'
-      //   }
-      // },
       {
         path: '/memberRecord',
         name: 'memberRecord',
@@ -281,63 +248,21 @@ export default [
         meta: {
           title: '训练统计'
         }
+      },
+      {
+        path: '/creation',
+        component: () => import('@/views/creation/index'),
+        meta: {
+          title: '作品详情'
+        }
+      },
+      {
+        path: '/creation-edit',
+        component: () => import('@/views/creation/edit/index'),
+        meta: {
+          title: '作品详情'
+        }
       }
-      // {
-      //   path: '/tradeRecord',
-      //   name: 'tradeRecord',
-      //   component: () => import('@/student/trade/index'),
-      //   meta: {
-      //     title: '交易记录'
-      //   }
-      // },
-      // {
-      //   path: '/teacherHome',
-      //   name: 'teacherHome',
-      //   component: () => import('@/student/teacher-dependent/teacher-home'),
-      //   meta: {
-      //     title: '老师主页'
-      //   }
-      // },
-      // {
-      //   path: '/teacherElegant',
-      //   name: 'teacherElegant',
-      //   component: () => import('@/student/teacher-dependent/teacher-elegant'),
-      //   meta: {
-      //     title: '老师风采'
-      //   }
-      // },
-      // {
-      //   path: '/music-upload',
-      //   component: () => import('@/teacher/music/upload'),
-      //   meta: {
-      //     title: '上传曲谱'
-      //   }
-      // },
-      // {
-      //   path: '/teacherFollow',
-      //   component: () => import('@/student/teacher-dependent/teacher-follow'),
-      //   meta: {
-      //     title: '我的关注'
-      //   }
-      // },
-      // {
-      //   path: '/track-review-activity',
-      //   component: () =>
-      //     import('@/student/share-active/track-review-activity/index'),
-      //   meta: {
-      //     title: '曲目评测活动',
-      //     isExternal: true // 是否外部浏览器可以打开
-      //   }
-      // },
-
-      // {
-      //   path: '/track-song',
-      //   component: () =>
-      //     import('@/student/share-active/track-review-activity/track-song'),
-      //   meta: {
-      //     title: '评测曲目'
-      //   }
-      // }
     ]
   },
   ...noLoginRouter,

+ 60 - 0
src/views/creation/api.ts

@@ -0,0 +1,60 @@
+// userMusic/detail/1698984095609106434
+import request from '@/helpers/request'
+import { state } from '@/state'
+const apiFix =
+  state.platformType === 'STUDENT' ? '/api-student' : '/api-teacher'
+
+/** 详情 */
+export const api_userMusicDetail = (params: any): Promise<any> => {
+  return request.get(apiFix + `/userMusic/detail/${params}`)
+}
+
+/** 开放详情 */
+export const api_openUserMusicDetail = (params: any): Promise<any> => {
+  return request.get(apiFix + `/open/userMusic/detail/${params}`)
+}
+
+/**  点赞分页 */
+export const api_userMusicStarPage = (params: any): Promise<any> => {
+  return request.post(apiFix + `/userMusicStar/page`, {
+    data: params
+  })
+}
+
+/**  分享推荐作品 */
+export const api_openUserMusicPage = (params: any): Promise<any> => {
+  return request.post(apiFix + `/open/userMusic/page`, {
+    data: params
+  })
+}
+
+/** 点赞 */
+export const api_userMusicStar = (params: any): Promise<any> => {
+  return request.post(apiFix + '/userMusicStar/star', {
+    data: params
+  })
+}
+
+/** 保存草稿/发布作品 */
+export const api_userMusicSave = (params: any): Promise<any> => {
+  return request.post(apiFix + '/userMusic/save', {
+    data: params
+  })
+}
+
+/** 删除 */
+export const api_userMusicRemove = (params: any): Promise<any> => {
+  return request.post(apiFix + '/userMusic/remove', {
+    requestType: 'form',
+    params
+  })
+}
+
+/** 授权验证 */
+export const api_verification = (params: any): Promise<any> => {
+  return request.get('/api-auth/checkToken', {
+    headers: {
+      'content-type': 'application/x-www-form-urlencoded'
+    }
+  })
+}

+ 102 - 0
src/views/creation/edit/index.module.less

@@ -0,0 +1,102 @@
+.section {
+  margin: 12px 13px;
+  background: #FFFFFF;
+  border-radius: 10px;
+  overflow: hidden;
+  font-size: 16px;
+
+  :global {
+    .van-field__control::placeholder {
+      color: #aaa;
+    }
+
+  }
+}
+
+.sectionFile {
+  padding: 12px;
+  display: flex;
+
+  :global {
+    .van-uploader {
+      --upload-file-size: 62px;
+      border-radius: 8px;
+      overflow: hidden;
+    }
+
+    .van-uploader__upload {
+      margin: 0;
+    }
+  }
+
+  .muploader {
+    position: relative;
+    z-index: 9;
+    width: 62px;
+    height: 62px;
+    border: none;
+    margin: 0;
+    border-radius: 8px;
+    background-color: #FFFFFF;
+  }
+
+  .uploadImg {
+    position: relative;
+    border-radius: 8px;
+    margin-right: 16px;
+
+    .tip {
+      position: absolute;
+      bottom: 0;
+      left: 0;
+      right: 0;
+      z-index: 10;
+      background: #000000;
+      opacity: 0.37;
+      font-size: 13px;
+      color: #FFFFFF;
+      line-height: 18px;
+      text-align: center;
+      border-radius: 0 0 8px 8px;
+      pointer-events: none;
+    }
+
+    &::before {
+      content: '';
+      background: url('../images/audio-pan.png') no-repeat center;
+      background-size: contain;
+      position: absolute;
+      top: 0;
+      right: -6px;
+      z-index: 1;
+      width: 60px;
+      height: 60px;
+    }
+  }
+
+  .musicDetail {
+    display: flex;
+    justify-content: center;
+    flex-direction: column;
+
+    .musicName {
+      font-size: 16px;
+      font-weight: 600;
+      color: #131415;
+      line-height: 22px;
+    }
+
+    .username {
+      padding-top: 4px;
+      font-size: 14px;
+      color: #777777;
+      line-height: 20px;
+    }
+  }
+}
+
+.btnGroup {
+  margin: 32px 24px 12px;
+  font-weight: 500;
+  font-size: 16px;
+}

+ 101 - 0
src/views/creation/edit/index.tsx

@@ -0,0 +1,101 @@
+import MHeader from '@/components/col-header'
+import MSticky from '@/components/col-sticky'
+import { defineComponent, onMounted, reactive } from 'vue'
+import styles from './index.module.less'
+import { Button, Field } from 'vant'
+import MUploader from '@/components/col-upload'
+import { api_userMusicDetail, api_userMusicSave } from '../api'
+import { useRoute, useRouter } from 'vue-router'
+
+export default defineComponent({
+  name: 'creation-edit',
+  setup() {
+    const route = useRoute()
+    const router = useRouter()
+    const state = reactive({
+      id: route.query.id,
+      musicDetail: {} as any,
+      desc: '',
+      videoImg: '', // 视频封面
+      img: ''
+    })
+
+    const onSubmit = async () => {
+      console.log({
+        id: state.id,
+        img: state.img,
+        videoImg: state.videoImg,
+        desc: state.desc || '我发布了一首演奏作品,快来听听吧~',
+        musicPracticeRecordId: state.musicDetail.musicPracticeRecordId,
+        type: 'FORMAL'
+      })
+      try {
+        await api_userMusicSave({
+          id: state.id,
+          img: state.img,
+          videoImg: state.videoImg,
+          desc: state.desc || '我发布了一首演奏作品,快来听听吧~',
+          musicPracticeRecordId: state.musicDetail.musicPracticeRecordId,
+          type: 'FORMAL'
+        })
+
+        router.back()
+      } catch {
+        //
+      }
+    }
+    onMounted(async () => {
+      try {
+        const { data } = await api_userMusicDetail(state.id)
+        state.musicDetail = data
+        state.desc = data.desc
+        state.videoImg = data.videoImg
+        state.img = data.img
+      } catch {
+        //
+      }
+    })
+    return () => (
+      <div>
+        <MSticky position="top">
+          <MHeader border={false} />
+        </MSticky>
+
+        <div class={styles.section}>
+          <Field
+            rows={4}
+            autosize
+            type="textarea"
+            maxlength={150}
+            placeholder="我发布了一首演奏作品,快来听听吧~"
+            showWordLimit
+            v-model={state.desc}
+          />
+        </div>
+
+        <div class={[styles.section, styles.sectionFile]}>
+          <div class={styles.uploadImg}>
+            <MUploader
+              class={styles.muploader}
+              // native
+              tips={''}
+              deletable={false}
+              v-model:modelValue={state.img}
+            />
+            <div class={styles.tip}>选封面</div>
+          </div>
+          <div class={styles.musicDetail}>
+            <p class={styles.musicName}>{state.musicDetail.musicSheetName}</p>
+            <p class={styles.username}>{state.musicDetail.username}</p>
+          </div>
+        </div>
+
+        <div class={styles.btnGroup}>
+          <Button type="primary" round block color="#2DC7AA" onClick={onSubmit}>
+            {state.musicDetail.type === 'FORMAL' ? '保存' : '发布'}
+          </Button>
+        </div>
+      </div>
+    )
+  }
+})

BIN
src/views/creation/images/audio-banner-bg.png


BIN
src/views/creation/images/audio-bg.png


BIN
src/views/creation/images/audio-pan.png


BIN
src/views/creation/images/audio-point.png


BIN
src/views/creation/images/audio-shadow.png


BIN
src/views/creation/images/audio-zhen.png


BIN
src/views/creation/images/icon-delete.png


BIN
src/views/creation/images/icon-download.png


BIN
src/views/creation/images/icon-member.png


BIN
src/views/creation/images/icon-pause.png


BIN
src/views/creation/images/icon-play.png


BIN
src/views/creation/images/icon-share.png


BIN
src/views/creation/images/icon-z.png


BIN
src/views/creation/images/icon-zan-active.png


BIN
src/views/creation/images/icon-zan.png


BIN
src/views/creation/images/wave-1.png


BIN
src/views/creation/images/wave-2.png


+ 450 - 0
src/views/creation/index-share.tsx

@@ -0,0 +1,450 @@
+import {
+  defineComponent,
+  onMounted,
+  onUnmounted,
+  reactive,
+  ref,
+  watch
+} from 'vue'
+// import WaveSurfer from 'wavesurfer.js';
+// import Regions from 'wavesurfer.js/dist/plugins/regions.js';
+import styles from './index.module.less'
+import { Cell, Image, List, Popup, Slider } from 'vant'
+import iconMember from './images/icon-member.png'
+import iconZan from './images/icon-zan.png'
+import iconZanActive from './images/icon-zan-active.png'
+import iconZ from './images/icon-z.png'
+import iconPlay from './images/icon-play.png'
+import iconPause from './images/icon-pause.png'
+import {
+  browser,
+  getAuth,
+  getGradeCh,
+  getSecondRPM,
+  removeAuth
+} from '@/helpers/utils'
+import { onBeforeRouteUpdate, useRoute, useRouter } from 'vue-router'
+import {
+  api_openUserMusicDetail,
+  api_openUserMusicPage,
+  api_userMusicStar,
+  api_verification
+} from './api'
+import MEmpty from '@/components/col-result'
+import { nextTick } from 'process'
+import MVideo from '@/components/col-video-tcplayer'
+import LoginModel from './login-model'
+import { setLogout } from '@/state'
+import MWxTip from '@/components/the-wx-tip'
+import { usePageVisibility } from '@vant/use'
+
+export default defineComponent({
+  name: 'creation-detail',
+  setup() {
+    const route = useRoute()
+    const router = useRouter()
+
+    const state = reactive({
+      id: route.query.id,
+      loginTag: false, // 是否登录标识
+      loginStatus: false,
+      playType: '' as 'Audio' | 'Video' | '', // 播放类型
+      musicDetail: {} as any,
+      timer: null as any,
+      paused: true,
+      audioWidth: 0,
+      currentTime: 0,
+      duration: 0.1,
+      loop: false,
+      dragStatus: false, // 是否开始拖动
+      isClick: false,
+      list: [] as any,
+      listState: {
+        dataShow: true, // 判断是否有数据
+        loading: false,
+        finished: false
+      },
+      params: {
+        page: 1,
+        rows: 20
+      },
+      messageStatus: false,
+      message: ''
+    })
+    // window.AudioContext = window.AudioContext || window.webkitAudioContext;
+    const audioDom = new Audio()
+    audioDom.controls = true
+    audioDom.style.width = '100%'
+    audioDom.className = styles.audio
+
+    /** 改变播放时间 */
+    const handleChangeTime = (val: number) => {
+      state.currentTime = val
+      clearTimeout(state.timer)
+      state.timer = setTimeout(() => {
+        audioDom.currentTime = val
+        state.timer = null
+      }, 60)
+    }
+
+    // 切换音频播放
+    const onToggleAudio = (e: any) => {
+      e.stopPropagation()
+      if (audioDom.paused) {
+        audioDom.play()
+      } else {
+        audioDom.pause()
+      }
+
+      state.paused = audioDom.paused
+    }
+
+    // 点赞
+    const onStarChange = async () => {
+      // 是否登录
+      if (!state.loginTag) {
+        state.loginStatus = true
+        return
+      }
+      try {
+        await api_userMusicStar({
+          userMusicId: state.id,
+          star: !state.musicDetail.starFlag
+        })
+
+        state.musicDetail.starFlag = !state.musicDetail.starFlag
+        if (state.musicDetail.starFlag) {
+          state.musicDetail.likeNum += 1
+        } else {
+          state.musicDetail.likeNum -= 1
+        }
+      } catch {
+        //
+      }
+    }
+
+    // 获取列表
+    const getList = async () => {
+      try {
+        if (state.isClick) return
+        state.isClick = true
+        const res = await api_openUserMusicPage({
+          type: 'FORMAL',
+          exclusionId: state.id,
+          sort: 1,
+          ...state.params
+        })
+        state.listState.loading = false
+        const result = res.data || {}
+        // 处理重复请求数据
+        if (state.list.length > 0 && result.current === 1) {
+          return
+        }
+        state.list = state.list.concat(result.rows || [])
+        state.listState.finished = result.current >= result.pages
+        state.params.page = result.current + 1
+        state.listState.dataShow = state.list.length > 0
+        state.isClick = false
+      } catch {
+        state.listState.dataShow = false
+        state.listState.finished = true
+        state.isClick = false
+      }
+    }
+
+    const onDetail = (item: any) => {
+      router.push({
+        path: '/shareCreation',
+        query: {
+          id: item.id
+        }
+      })
+    }
+
+    const initAudio = () => {
+      try {
+        audioDom.src = state.musicDetail.videoUrl
+        audioDom.load()
+        audioDom.oncanplaythrough = () => {
+          state.paused = audioDom.paused
+          state.duration = audioDom.duration
+        }
+        // 播放时监听
+        audioDom.addEventListener('timeupdate', () => {
+          state.duration = audioDom.duration
+          state.currentTime = audioDom.currentTime
+          const rate = (state.currentTime / state.duration) * 100
+          state.audioWidth = rate > 100 ? 100 : rate
+        })
+        audioDom.addEventListener('ended', () => {
+          state.paused = audioDom.paused
+        })
+      } catch (e) {
+        //
+        console.log(e)
+      }
+    }
+
+    const __init = async () => {
+      try {
+        // 判断是否登录
+        const Authorization = getAuth() // storage.get(ACCESS_TOKEN) || ''
+        if (Authorization) {
+          await api_verification({
+            token: Authorization
+          })
+          state.loginTag = true
+          // if (!res.data) {
+          //   removeAuth()
+          //   setLogout()
+          // }
+        }
+      } catch (e: any) {
+        // 登录是否有效
+        state.loginTag = false
+        removeAuth()
+        setLogout()
+      }
+      try {
+        const res = await api_openUserMusicDetail(state.id)
+
+        if (res.code === 999) {
+          state.message = res.msg
+          state.messageStatus = true
+          return
+        } else {
+          state.musicDetail = res.data
+          getList()
+          // 判断是视频还是音频
+
+          if (res.data.videoUrl.lastIndexOf('mp4') !== -1) {
+            state.playType = 'Video'
+          } else {
+            state.playType = 'Audio'
+            // 初始化
+            nextTick(() => {
+              initAudio()
+            })
+          }
+        }
+      } catch (err) {
+        //
+        state.listState.dataShow = false
+      }
+    }
+
+    const pageVisibility = usePageVisibility()
+    watch(pageVisibility, value => {
+      console.log(value)
+      if (value === 'hidden') {
+        if (audioDom) {
+          audioDom.pause()
+          state.paused = audioDom.paused
+        }
+      }
+    })
+    onMounted(async () => {
+      __init()
+    })
+
+    onUnmounted(() => {
+      if (audioDom) {
+        audioDom.pause()
+        state.paused = audioDom.paused
+      }
+    })
+
+    onBeforeRouteUpdate((to: any) => {
+      state.id = to.query.id
+      state.playType = ''
+      state.params.page = 1
+      if (audioDom) {
+        audioDom.currentTime = 0
+        audioDom.pause()
+        state.paused = audioDom.paused
+      }
+      state.list = []
+      __init()
+    })
+    return () => (
+      <div class={styles.creation}>
+        <div class={styles.playSection}>
+          {state.playType === 'Video' && (
+            <MVideo
+              src={state.musicDetail.videoUrl}
+              poster={state.musicDetail.img}
+            />
+          )}
+          {state.playType === 'Audio' && (
+            <div class={styles.audioSection}>
+              <div class={styles.audioContainer}>
+                <div
+                  class={styles.waveActive}
+                  style={{
+                    width: state.audioWidth + '%'
+                  }}
+                ></div>
+                <div class={styles.waveDefault}></div>
+              </div>
+
+              <div class={styles.audioBox}>
+                <div
+                  class={[styles.audioPan, state.paused && styles.imgRotate]}
+                >
+                  <Image class={styles.audioImg} src={state.musicDetail?.img} />
+                </div>
+                <i class={styles.audioPoint}></i>
+                <i
+                  class={[styles.audioZhen, state.paused && styles.active]}
+                ></i>
+              </div>
+              <div
+                class={[styles.controls]}
+                onClick={(e: Event) => {
+                  e.stopPropagation()
+                }}
+              >
+                <div class={styles.actions}>
+                  <div class={styles.actionBtn} onClick={onToggleAudio}>
+                    <img src={state.paused ? iconPlay : iconPause} />
+                  </div>
+                </div>
+                <div class={[styles.slider]}>
+                  <Slider
+                    step={0.01}
+                    class={styles.timeProgress}
+                    v-model={state.currentTime}
+                    max={state.duration}
+                    onUpdate:modelValue={val => {
+                      handleChangeTime(val)
+                    }}
+                    onDrag-start={() => {
+                      state.dragStatus = true
+                      console.log('onDragStart')
+                    }}
+                    onDrag-end={() => {
+                      state.dragStatus = false
+                      console.log('onDragEnd')
+                    }}
+                  />
+                </div>
+                <div class={styles.time}>
+                  <div>{getSecondRPM(state.currentTime)}</div>
+                  <span>/</span>
+                  <div>{getSecondRPM(state.duration)}</div>
+                </div>
+              </div>
+            </div>
+          )}
+        </div>
+
+        <Cell class={styles.userSection} center border={false}>
+          {{
+            icon: () => (
+              <Image class={styles.userLogo} src={state.musicDetail.avatar} />
+            ),
+            title: () => (
+              <div class={styles.userInfo}>
+                <p class={styles.name}>
+                  <span>{state.musicDetail.username}</span>
+                  {state.musicDetail.vipFlag && (
+                    <img src={iconMember} class={styles.iconMember} />
+                  )}
+                </p>
+                <p class={styles.sub}>
+                  {state.musicDetail.subjectName}{' '}
+                  {getGradeCh(state.musicDetail.currentGradeNum - 1)}
+                </p>
+              </div>
+            ),
+            value: () => (
+              <div
+                class={[
+                  styles.zan,
+                  state.musicDetail.starFlag && styles.zanActive
+                ]}
+                onClick={onStarChange}
+              >
+                <img
+                  src={state.musicDetail.starFlag ? iconZanActive : iconZan}
+                  class={styles.iconZan}
+                />
+                {state.musicDetail.likeNum}
+              </div>
+            )
+          }}
+        </Cell>
+
+        <div class={styles.musicSection}>
+          <div class={styles.musicName}>
+            <span class={styles.musicTag}>曲目名称</span>
+            {state.musicDetail?.musicSheetName}
+          </div>
+          {state.musicDetail?.desc && (
+            <div class={styles.musicDesc}>{state.musicDetail?.desc}</div>
+          )}
+        </div>
+
+        <div class={styles.likeSection}>
+          <div class={styles.likeTitle}>推荐作品</div>
+
+          {state.listState.dataShow ? (
+            <List
+              finished={state.listState.finished}
+              finishedText=" "
+              class={[styles.container, styles.containerInformation]}
+              onLoad={getList}
+              immediateCheck={false}
+            >
+              <div class={styles.cellGroup}>
+                {state.list.map((item: any) => (
+                  <div class={styles.cell} onClick={() => onDetail(item)}>
+                    <div class={styles.cellImg}>
+                      <Image
+                        class={styles.cellImage}
+                        src={item.img}
+                        fit="cover"
+                      />
+
+                      <div class={styles.iconZan}>{item.likeNum}</div>
+                    </div>
+                    <div class={[styles.cellTitle, 'van-ellipsis']}>
+                      {item.musicSheetName}
+                    </div>
+                    <div class={styles.users}>
+                      <Image src={item.avatar} class={styles.userImg} />
+                      <span class={styles.name}>{item.username}</span>
+                    </div>
+                  </div>
+                ))}
+              </div>
+            </List>
+          ) : (
+            <MEmpty tips="暂无数据" btnStatus={false} />
+          )}
+        </div>
+
+        <Popup
+          v-model:show={state.loginStatus}
+          style={{ background: 'transparent', overflow: 'inherit' }}
+        >
+          <LoginModel
+            onClose={() => (state.loginStatus = false)}
+            onConfirm={async (val: boolean) => {
+              state.loginTag = val
+              state.loginStatus = false
+              const { data } = await api_openUserMusicDetail(state.id)
+              state.musicDetail = data
+            }}
+          />
+        </Popup>
+
+        <MWxTip
+          v-model:show={state.messageStatus}
+          message={state.message}
+          showButton={false}
+        />
+      </div>
+    )
+  }
+})

+ 556 - 0
src/views/creation/index.module.less

@@ -0,0 +1,556 @@
+.playSection {
+  min-height: 175px;
+}
+
+@keyframes rotateImg {
+  100% {
+    transform: rotate(360deg);
+  }
+}
+
+.audioSection {
+  position: relative;
+  background: url('./images/audio-banner-bg.png') no-repeat top center;
+  background-size: cover;
+  height: 175px;
+
+  .audioContainer {
+    position: absolute;
+    top: 0;
+    left: 50%;
+    width: 196px;
+    height: 35px;
+    transform: translate(-50%, 60px);
+
+    .waveActive,
+    .waveDefault {
+      width: 100%;
+      height: 100%;
+    }
+
+    .waveDefault {
+      position: absolute;
+      top: 0;
+      left: 0;
+      background: url('./images/wave-1.png')no-repeat center left;
+      background-size: cover;
+    }
+
+    .waveActive {
+      position: absolute;
+      top: 0;
+      left: 0;
+      z-index: 1;
+      background: url('./images/wave-2.png')no-repeat center left;
+      background-size: cover;
+    }
+  }
+
+
+  .audioBox {
+    position: absolute;
+    left: 50%;
+    transform: translate(-50%, 50%);
+    z-index: 2;
+    width: 74px;
+    height: 75px;
+    background: url('./images/audio-bg.png') no-repeat center;
+    background-size: contain;
+
+    // &::after {
+    //   content: '';
+    //   width: 134px;
+    //   height: 73px;
+    //   position: absolute;
+    //   left: 50%;
+    //   transform: translate(-50%, 50%);
+    //   background: url('./images/audio-shadow.png') no-repeat center;
+    //   background-size: contain;
+    //   z-index: -1;
+    // }
+    .audioPan {
+      position: absolute;
+      left: 8px;
+      top: 6px;
+      z-index: 8;
+      width: 59px;
+      height: 60px;
+      background: url('./images/audio-pan.png') no-repeat center;
+      background-size: contain;
+      display: flex;
+      align-items: center;
+      justify-content: center;
+
+      animation: rotateImg 6s linear infinite;
+
+      &.imgRotate {
+        animation-play-state: paused;
+      }
+    }
+
+    .audioImg {
+      width: 32px;
+      height: 32px;
+      border-radius: 50%;
+      overflow: hidden;
+    }
+
+    .audioPoint {
+      position: absolute;
+      z-index: 9;
+      left: 50%;
+      top: 50%;
+      transform: translate(-50%, -50%);
+      width: 8px;
+      height: 8px;
+      background: url('./images/audio-point.png') no-repeat center;
+      background-size: contain;
+    }
+
+    .audioZhen {
+      position: absolute;
+      z-index: 9;
+      right: -4px;
+      top: -33px;
+      width: 26px;
+      height: 87px;
+      background: url('./images/audio-zhen.png') no-repeat center;
+      background-size: contain;
+      transition: transform .5s ease-in-out;
+
+      &.active {
+        transform: rotate(92deg) translate3d(0, 0, 3px);
+        transition: transform .5s ease-in-out;
+      }
+    }
+  }
+
+}
+
+.controls {
+  position: absolute;
+  left: 0;
+  bottom: 0;
+  right: 0;
+  height: 44px;
+  display: flex;
+  flex-direction: column;
+  justify-content: space-between;
+  flex-direction: row;
+  transition: all 0.5s;
+  padding: 0 12px;
+
+  &>div {
+    display: flex;
+    align-items: center;
+  }
+
+  &.hide {
+    transform: translateY(100%);
+  }
+
+
+
+  .actionBtn {
+    line-height: 0;
+    margin-right: 4px;
+
+    img {
+      width: 14px;
+      height: 14px;
+      margin-bottom: -2px;
+    }
+  }
+
+  .time {
+    display: flex;
+    justify-content: space-between;
+    flex: 1;
+    min-width: 86px;
+    font-size: 12px;
+    color: #131415;
+    line-height: 20px;
+
+    span {
+      font-size: 12px;
+      padding: 0 1px;
+    }
+  }
+
+  .slider {
+    width: 100%;
+    margin: 0 12px;
+    --van-slider-bar-height: 4px;
+    --van-slider-button-width: 13px !important;
+    --van-slider-button-height: 13px !important;
+    --van-slider-inactive-background: #fff;
+    --van-slider-inactive-background-color: #fff;
+    --van-slider-active-background: #2DC7AA !important;
+
+    :global {
+
+      .van-loading {
+        width: 100%;
+        height: 100%;
+      }
+
+    }
+  }
+
+}
+
+.userSection {
+  padding: 15px 12px !important;
+  background-color: transparent !important;
+
+  .userLogo {
+    width: 44px;
+    height: 44px;
+    border: 1px solid #FFFFFF;
+    margin-right: 10px;
+    border-radius: 50%;
+    overflow: hidden;
+  }
+
+  .userInfo {
+    .name {
+      display: flex;
+      align-items: center;
+      font-size: 16px;
+      font-weight: 500;
+      color: #333333;
+      line-height: 22px;
+
+      span {
+        display: inline-block;
+        white-space: nowrap;
+        overflow: hidden;
+        text-overflow: ellipsis;
+        max-width: 100px;
+      }
+    }
+
+    .sub {
+      padding-top: 2px;
+      font-size: 12px;
+      color: #777777;
+      line-height: 17px;
+    }
+
+    .iconMember {
+      margin-left: 6px;
+      width: 14px;
+      height: 14px;
+    }
+  }
+
+  .zan {
+    background: #FFFFFF;
+    border-radius: 13px;
+    font-size: 14px;
+    color: #777777;
+    line-height: 20px;
+    padding: 4px 9px 3px;
+    display: inline-flex;
+    align-items: center;
+
+    &.zanActive {
+      background: #F7EEEE;
+      color: #FF6A6A;
+    }
+
+    .iconZan {
+      width: 18px;
+      height: 18px;
+      margin-right: 2px;
+    }
+  }
+}
+
+.musicSection {
+  margin: 0 13px 12px;
+  padding: 14px 12px;
+  background: #FFFFFF;
+  border-radius: 10px;
+
+  .musicName {
+    font-size: 15px;
+    font-weight: 500;
+    color: #333333;
+    line-height: 21px;
+    // display: flex;
+    // align-items: center;
+    overflow: hidden;
+    text-overflow: ellipsis;
+    white-space: nowrap;
+    max-width: 100%;
+
+    .musicTag {
+      margin-right: 6px;
+      padding: 1px 6px;
+      font-size: 12px;
+      color: #FF7B31;
+      line-height: 17px;
+      background: rgba(255, 166, 115, 0.07);
+      border-radius: 9px;
+      border: 1px solid #FFBF9A;
+      font-weight: 400;
+      vertical-align: text-bottom;
+    }
+  }
+
+  .musicDesc {
+    padding-top: 8px;
+    font-size: 14px;
+    color: #777777;
+    line-height: 20px;
+  }
+}
+
+.likeSection {
+  margin: 0 13px 12px;
+  background: #FFFFFF;
+  border-radius: 10px;
+  padding: 10px 12px;
+
+  .likeTitle {
+    display: flex;
+    align-items: center;
+    font-size: 17px;
+    font-weight: 600;
+    color: #333333;
+    line-height: 24px;
+    padding-bottom: 8px;
+
+    &::before {
+      display: inline-block;
+      content: '';
+      width: 4px;
+      height: 14px;
+      border-radius: 2px;
+      background: linear-gradient(to bottom, #59E5D5, #2DC7AA);
+      margin-right: 6px;
+    }
+  }
+}
+
+.likeItem {
+  padding: 16px 0;
+
+  .userLogo {
+    border-radius: 50%;
+    overflow: hidden;
+    width: 42px;
+    height: 42px;
+    margin-right: 7px;
+  }
+
+  .userInfo {
+    .name {
+      font-size: 16px;
+      font-weight: 500;
+      color: #333333;
+      line-height: 22px;
+    }
+
+    .sub {
+      padding-top: 2px;
+      font-size: 13px;
+      color: #777777;
+      line-height: 18px;
+    }
+  }
+
+  .time {
+    font-size: 13px;
+    color: #777777;
+    line-height: 18px;
+  }
+}
+
+
+.bottomSection {
+  display: flex;
+  align-items: center;
+  justify-content: space-between;
+  background-color: #fff;
+  padding: 15px 12px;
+
+  .bottomShare {
+    display: flex;
+    align-items: center;
+
+    p {
+      padding: 0 15px;
+      text-align: center;
+      line-height: 0;
+
+      &:first-child {
+        padding-left: 5px;
+      }
+    }
+
+    img {
+      width: 18px;
+      height: 18px;
+    }
+
+    span {
+      padding-top: 8px;
+      font-size: 12px;
+      color: #333333;
+      line-height: 17px;
+      display: block;
+    }
+  }
+
+  .btnEdit {
+    font-size: 14px;
+    font-weight: 500;
+    background: #2DC7AA;
+    color: #FFFFFF;
+    line-height: 22px;
+    min-width: 80px;
+    height: 30px;
+    border: none;
+  }
+}
+
+.popupContainer {
+  width: 80%;
+
+
+  .popupContent {
+    padding: 29px 0 25px;
+    text-align: center;
+    font-size: 18px;
+    font-weight: 500;
+    color: #333333;
+    line-height: 25px;
+  }
+
+  .popupBtnGroup {
+    text-align: center;
+    margin-bottom: 22px;
+
+    :global {
+      .van-button {
+        height: 40px;
+        font-size: 16px;
+        font-weight: 400 !important;
+        line-height: 22px;
+        min-width: 122px;
+
+        &:last-child {
+          margin-left: 10px;
+          background: #2DC7AA;
+          border: none;
+        }
+      }
+    }
+  }
+}
+
+.cellGroup {
+  display: flex;
+  flex-wrap: wrap;
+}
+
+.cell {
+  // display: flex;
+  // flex-direction: column;
+  width: 96px;
+  margin-right: 18px;
+  margin-bottom: 18px;
+
+  &:nth-child(3n + 3) {
+    margin-right: 0;
+  }
+
+  .cellImg {
+    position: relative;
+
+    &::before {
+      content: '';
+      position: absolute;
+      right: -8px;
+      top: 3px;
+      z-index: 8;
+      width: 84px;
+      height: 84px;
+      background: url('./images/audio-pan.png') no-repeat center;
+      background-size: contain;
+      display: flex;
+      align-items: center;
+      justify-content: center;
+    }
+
+    .iconZan {
+      position: absolute;
+      bottom: 4px;
+      left: 4px;
+      z-index: 10;
+      padding: 3px;
+      background: rgba(67, 67, 67, 0.3);
+      border-radius: 8px;
+      backdrop-filter: blur(4px);
+
+      font-size: 9px;
+      font-weight: 500;
+      color: #FFFFFF;
+      line-height: 13px;
+      display: flex;
+      align-items: center;
+
+      &::before {
+        content: '';
+        display: inline-block;
+        width: 12px;
+        height: 12px;
+        background: url('./images/icon-z.png') no-repeat center;
+        background-size: contain;
+      }
+    }
+  }
+
+  .cellImage {
+    position: relative;
+    width: 88px;
+    height: 88px;
+    border-radius: 12px;
+    z-index: 9;
+
+    :global {
+      img {
+        border-radius: 12px;
+      }
+    }
+  }
+
+  .cellTitle {
+    font-size: 13px;
+    color: #131415;
+    line-height: 18px;
+    margin: 8px 0 6px;
+  }
+
+  .users {
+    display: flex;
+    align-items: center;
+
+    .userImg {
+      width: 20px;
+      height: 20px;
+      border-radius: 50%;
+      overflow: hidden;
+      margin-right: 4px;
+      flex-shrink: 0;
+    }
+
+    .name {
+      font-size: 12px;
+      color: #402424;
+      line-height: 14px;
+    }
+  }
+}

+ 491 - 0
src/views/creation/index.tsx

@@ -0,0 +1,491 @@
+import { defineComponent, onMounted, onUnmounted, reactive, watch } from 'vue'
+// import WaveSurfer from 'wavesurfer.js';
+import styles from './index.module.less'
+import MSticky from '@/components/col-sticky'
+import MHeader from '@/components/col-header'
+import { Button, Cell, Dialog, Image, List, Popup, Slider, Toast } from 'vant'
+import iconDownload from './images/icon-download.png'
+import iconShare from './images/icon-share.png'
+import iconDelete from './images/icon-delete.png'
+import iconMember from './images/icon-member.png'
+import iconZan from './images/icon-zan.png'
+import iconZanActive from './images/icon-zan-active.png'
+import iconPlay from './images/icon-play.png'
+import iconPause from './images/icon-pause.png'
+import { postMessage, promisefiyPostMessage } from '@/helpers/native-message'
+import { browser, getGradeCh, getSecondRPM } from '@/helpers/utils'
+import { useRoute, useRouter } from 'vue-router'
+import {
+  api_userMusicDetail,
+  api_userMusicRemove,
+  api_userMusicStarPage
+} from './api'
+import MEmpty from '@/components/col-result'
+import dayjs from 'dayjs'
+import { nextTick } from 'process'
+import MVideo from '@/components/col-video-tcplayer'
+import ShareModel from './share-model'
+import { usePageVisibility } from '@vant/use'
+
+export default defineComponent({
+  name: 'creation-detail',
+  setup() {
+    const route = useRoute()
+    const router = useRouter()
+    const audioId = 'a' + +Date.now() + Math.floor(Math.random() * 100)
+
+    const state = reactive({
+      id: route.query.id,
+      deleteStatus: false,
+      shareStatus: false,
+      playType: '' as 'Audio' | 'Video' | '', // 播放类型
+      musicDetail: {} as any,
+      timer: null as any,
+      audioWidth: 0,
+      paused: true,
+      currentTime: 0,
+      duration: 0.1,
+      loop: false,
+      dragStatus: false, // 是否开始拖动
+      isClick: false,
+      list: [] as any,
+      listState: {
+        dataShow: true, // 判断是否有数据
+        loading: false,
+        finished: false
+      },
+      params: {
+        page: 1,
+        rows: 20
+      }
+    })
+    const audioDom = new Audio()
+    audioDom.controls = true
+    audioDom.style.width = '100%'
+    audioDom.className = styles.audio
+
+    /** 改变播放时间 */
+    const handleChangeTime = (val: number) => {
+      state.currentTime = val
+      clearTimeout(state.timer)
+      state.timer = setTimeout(() => {
+        // audioRef.value.currentTime = val;
+        audioDom.currentTime = val
+        state.timer = null
+      }, 60)
+    }
+
+    // 切换音频播放
+    const onToggleAudio = (e: any) => {
+      e.stopPropagation()
+      if (audioDom.paused) {
+        audioDom.play()
+      } else {
+        audioDom.pause()
+      }
+
+      state.paused = audioDom.paused
+    }
+
+    // 获取列表
+    const getStarList = async () => {
+      try {
+        if (state.isClick) return
+        state.isClick = true
+        const res = await api_userMusicStarPage({
+          userMusicId: state.id,
+          ...state.params
+        })
+        state.listState.loading = false
+        const result = res.data || {}
+        // 处理重复请求数据
+        if (state.list.length > 0 && result.current === 1) {
+          return
+        }
+        state.list = state.list.concat(result.rows || [])
+        state.listState.finished = result.current >= result.pages
+        state.params.page = result.current + 1
+        state.listState.dataShow = state.list.length > 0
+        state.isClick = false
+      } catch {
+        state.listState.dataShow = false
+        state.listState.finished = true
+        state.isClick = false
+      }
+    }
+
+    const initAudio = () => {
+      audioDom.src = state.musicDetail.videoUrl
+      audioDom.load()
+      audioDom.oncanplaythrough = () => {
+        state.paused = audioDom.paused
+        state.duration = audioDom.duration
+      }
+      // 播放时监听
+      audioDom.addEventListener('timeupdate', () => {
+        state.duration = audioDom.duration
+        state.currentTime = audioDom.currentTime
+        const rate = (state.currentTime / state.duration) * 100
+        state.audioWidth = rate > 100 ? 100 : rate
+      })
+      audioDom.addEventListener('ended', () => {
+        state.paused = audioDom.paused
+      })
+      // const wavesurfer = WaveSurfer.create({
+      //   container: document.querySelector(`#${audioId}`) as HTMLElement,
+      //   waveColor: '#fff',
+      //   progressColor: '#2FA1FD',
+      //   url: state.musicDetail.videoUrl,
+      //   cursorWidth: 0,
+      //   height: 35,
+      //   width: 'auto',
+      //   normalize: true,
+      //   // Set a bar width
+      //   barWidth: 2,
+      //   // Optionally, specify the spacing between bars
+      //   barGap: 2,
+      //   // And the bar radius
+      //   barRadius: 4,
+      //   barHeight: 0.6,
+      //   autoScroll: true,
+      //   /** If autoScroll is enabled, keep the cursor in the center of the waveform during playback */
+      //   autoCenter: true,
+      //   hideScrollbar: false,
+      //   media: audioDom
+      // });
+
+      // wavesurfer.once('interaction', () => {
+      //   // wavesurfer.play();
+      // });
+      // wavesurfer.once('ready', () => {
+      //   state.paused = audioDom.paused;
+      //   state.duration = audioDom.duration;
+      // });
+
+      // wavesurfer.on('finish', () => {
+      //   state.paused = true;
+      // });
+
+      // // 播放时监听
+      // audioDom.addEventListener('timeupdate', () => {
+      //   state.currentTime = audioDom.currentTime;
+      // });
+    }
+
+    // 删除作品
+    const onDelete = async () => {
+      try {
+        await api_userMusicRemove({ id: state.id })
+
+        setTimeout(() => {
+          state.deleteStatus = false
+          Toast('删除成功')
+        }, 100)
+
+        setTimeout(() => {
+          if (browser().isApp) {
+            postMessage({
+              api: 'goBack'
+            })
+          } else {
+            router.back()
+          }
+        }, 1200)
+      } catch {
+        //
+      }
+    }
+
+    // 下载
+    const onDownload = async () => {
+      await promisefiyPostMessage({
+        api: 'saveFile',
+        content: {
+          url: state.musicDetail.videoUrl
+        }
+      })
+    }
+
+    const pageVisibility = usePageVisibility()
+    watch(pageVisibility, value => {
+      if (value === 'hidden') {
+        if (audioDom) {
+          audioDom.pause()
+          state.paused = audioDom.paused
+        }
+      }
+    })
+    onMounted(async () => {
+      try {
+        const res = await api_userMusicDetail(state.id)
+        // console.log(res);
+        if (res.code === 999) {
+          Dialog.alert({
+            message: res.msg,
+            theme: 'round-button',
+            confirmButtonColor: '#2DC7AA'
+          }).then(() => {
+            if (browser().isApp) {
+              postMessage({
+                api: 'goBack'
+              })
+            } else {
+              router.back()
+            }
+          })
+          return
+        }
+        state.musicDetail = res.data || {}
+        getStarList()
+        // 判断是视频还是音频
+        if (res.data.videoUrl.lastIndexOf('mp4') !== -1) {
+          state.playType = 'Video'
+        } else {
+          state.playType = 'Audio'
+          // 初始化
+          nextTick(() => {
+            initAudio()
+          })
+        }
+      } catch {
+        //
+      }
+    })
+
+    onUnmounted(() => {
+      if (audioDom) {
+        audioDom.pause()
+        state.paused = audioDom.paused
+      }
+    })
+    return () => (
+      <div class={styles.creation}>
+        <MSticky position="top">
+          <MHeader
+            border={false}
+            isBack={route.query.platformType != 'ANALYSIS'}
+          />
+        </MSticky>
+        <div class={styles.playSection}>
+          {state.playType === 'Video' && (
+            <MVideo
+              src={state.musicDetail?.videoUrl}
+              poster={state.musicDetail?.img}
+            />
+          )}
+          {state.playType === 'Audio' && (
+            <div class={styles.audioSection}>
+              <div class={styles.audioContainer}>
+                {/* <div
+                  id={audioId}
+                  onClick={(e: MouseEvent) => {
+                    e.stopPropagation();
+                  }}></div> */}
+                <div
+                  class={styles.waveActive}
+                  style={{
+                    width: state.audioWidth + '%'
+                  }}
+                ></div>
+                <div class={styles.waveDefault}></div>
+              </div>
+
+              <div class={styles.audioBox}>
+                <div
+                  class={[styles.audioPan, state.paused && styles.imgRotate]}
+                >
+                  <Image class={styles.audioImg} src={state.musicDetail?.img} />
+                </div>
+                <i class={styles.audioPoint}></i>
+                <i
+                  class={[styles.audioZhen, state.paused && styles.active]}
+                ></i>
+              </div>
+              <div
+                class={[styles.controls]}
+                onClick={(e: Event) => {
+                  e.stopPropagation()
+                }}
+                onTouchmove={(e: TouchEvent) => {
+                  // emit('close');
+                }}
+              >
+                <div class={styles.actions}>
+                  <div class={styles.actionBtn} onClick={onToggleAudio}>
+                    <img src={state.paused ? iconPlay : iconPause} />
+                  </div>
+                </div>
+                <div class={[styles.slider]}>
+                  <Slider
+                    step={0.01}
+                    class={styles.timeProgress}
+                    v-model={state.currentTime}
+                    max={state.duration}
+                    onUpdate:modelValue={val => {
+                      handleChangeTime(val)
+                    }}
+                    onDrag-start={() => {
+                      state.dragStatus = true
+                      console.log('onDragStart')
+                    }}
+                    onDrag-end={() => {
+                      state.dragStatus = false
+                      console.log('onDragEnd')
+                    }}
+                  />
+                </div>
+                <div class={styles.time}>
+                  <div>{getSecondRPM(state.currentTime)}</div>
+                  <span>/</span>
+                  <div>{getSecondRPM(state.duration)}</div>
+                </div>
+              </div>
+            </div>
+          )}
+        </div>
+
+        <Cell class={styles.userSection} center border={false}>
+          {{
+            icon: () => (
+              <Image class={styles.userLogo} src={state.musicDetail.avatar} />
+            ),
+            title: () => (
+              <div class={styles.userInfo}>
+                <p class={styles.name}>
+                  <span>{state.musicDetail?.username}</span>
+                  {state.musicDetail.vipFlag && (
+                    <img src={iconMember} class={styles.iconMember} />
+                  )}
+                </p>
+                <p class={styles.sub}>
+                  {state.musicDetail.subjectName}{' '}
+                  {getGradeCh(state.musicDetail.currentGradeNum - 1)}
+                </p>
+              </div>
+            ),
+            value: () => (
+              <div class={[styles.zan, styles.zanActive]}>
+                <img src={iconZanActive} class={styles.iconZan} />
+                {state.musicDetail.likeNum}
+              </div>
+            )
+          }}
+        </Cell>
+
+        <div class={styles.musicSection}>
+          <div class={styles.musicName}>
+            <span class={styles.musicTag}>曲目名称</span>
+            {state.musicDetail?.musicSheetName}
+          </div>
+          {state.musicDetail.desc && (
+            <div class={styles.musicDesc}>{state.musicDetail.desc}</div>
+          )}
+        </div>
+
+        <div class={styles.likeSection}>
+          <div class={styles.likeTitle}>点赞记录</div>
+
+          {state.listState.dataShow ? (
+            <List
+              finished={state.listState.finished}
+              finishedText=" "
+              class={[styles.container, styles.containerInformation]}
+              onLoad={getStarList}
+              immediateCheck={false}
+            >
+              {state.list.map((item: any, index: number) => (
+                <Cell
+                  class={styles.likeItem}
+                  border={state.list.length - 1 == index ? false : true}
+                >
+                  {{
+                    icon: () => (
+                      <Image src={item.userAvatar} class={styles.userLogo} />
+                    ),
+                    title: () => (
+                      <div class={styles.userInfo}>
+                        <p class={styles.name}>{item.userName}</p>
+                        <p class={styles.sub}>
+                          {item.subjectName}{' '}
+                          {getGradeCh(item.currentGradeNum - 1)}
+                        </p>
+                      </div>
+                    ),
+                    value: () => (
+                      <div class={styles.time}>
+                        {dayjs(item.createTime).format('YYYY-MM-DD HH:mm')}
+                      </div>
+                    )
+                  }}
+                </Cell>
+              ))}
+            </List>
+          ) : (
+            <MEmpty btnStatus={false} tips="暂无数据" />
+          )}
+        </div>
+
+        <MSticky position="bottom">
+          <div class={styles.bottomSection}>
+            <div class={styles.bottomShare}>
+              <p onClick={onDownload}>
+                <img src={iconDownload} />
+                <span>下载</span>
+              </p>
+              <p onClick={() => (state.shareStatus = true)}>
+                <img src={iconShare} />
+                <span>分享</span>
+              </p>
+              <p onClick={() => (state.deleteStatus = true)}>
+                <img src={iconDelete} />
+                <span>删除</span>
+              </p>
+            </div>
+            <Button
+              round
+              class={styles.btnEdit}
+              type="primary"
+              onClick={() => {
+                router.push({
+                  path: '/creation-edit',
+                  query: {
+                    id: state.id
+                  }
+                })
+              }}
+            >
+              编辑
+            </Button>
+          </div>
+        </MSticky>
+
+        <Popup
+          v-model:show={state.deleteStatus}
+          round
+          class={styles.popupContainer}
+        >
+          <p class={styles.popupContent}>确定删除吗?</p>
+          <div class={styles.popupBtnGroup}>
+            <Button round onClick={() => (state.deleteStatus = false)}>
+              取消
+            </Button>
+            <Button round type="primary" onClick={onDelete}>
+              确定
+            </Button>
+          </div>
+        </Popup>
+
+        <Popup
+          position="bottom"
+          v-model:show={state.shareStatus}
+          style={{ background: 'transparent' }}
+        >
+          <ShareModel
+            musicDetail={state.musicDetail}
+            onClose={() => (state.shareStatus = false)}
+          />
+        </Popup>
+      </div>
+    )
+  }
+})

BIN
src/views/creation/login-model/images/icon-close.png


BIN
src/views/creation/login-model/images/icon-password.png


BIN
src/views/creation/login-model/images/icon-phone.png


BIN
src/views/creation/login-model/images/login-bg.png


+ 74 - 0
src/views/creation/login-model/index.module.less

@@ -0,0 +1,74 @@
+.loginModel {
+  background: url('./images/login-bg.png') no-repeat center;
+  background-size: contain;
+  width: 306px;
+  height: 370px;
+
+  .iconClose {
+    width: 24px;
+    height: 24px;
+    position: absolute;
+    top: 30px;
+    right: -12px;
+    background: url('./images/icon-close.png') no-repeat center;
+    background-size: contain;
+    z-index: 1;
+  }
+}
+
+.loginSection {
+  padding: 126px 16px 0;
+}
+
+.fieldSection {
+  padding-bottom: 18px;
+
+  :global {
+    .van-field {
+      background: #F2F4F8;
+      border-radius: 24px;
+      padding: 11px 16px;
+      margin-bottom: 12px;
+    }
+
+    .van-field__left-icon {
+      display: flex;
+      align-items: center;
+    }
+
+    .van-field__control::placeholder {
+      color: #97B9C3;
+    }
+  }
+
+  .icon {
+    width: 20px;
+    height: 20px;
+  }
+
+  .codeText {
+    font-size: 14px;
+    color: #2DC7AA;
+  }
+}
+
+.btnGroup {
+  :global {
+    .van-button {
+      font-size: 16px;
+      font-weight: 600;
+      color: #FFFFFF;
+    }
+  }
+
+  .btnText {
+    display: block;
+    padding-top: 16px;
+    text-align: center;
+    font-size: 16px;
+    font-weight: 500;
+    color: #74949E;
+    line-height: 22px;
+    text-align: center;
+  }
+}

+ 207 - 0
src/views/creation/login-model/index.tsx

@@ -0,0 +1,207 @@
+import { Teleport, defineComponent, reactive, ref } from 'vue'
+import styles from './index.module.less'
+import { Button, CountDown, Field, Toast } from 'vant'
+import iconPhone from './images/icon-phone.png'
+import iconPassword from './images/icon-password.png'
+import { checkPhone, setAuth } from '@/helpers/utils'
+import request from '@/helpers/request'
+import { setLogin, state } from '@/state'
+import MImgCode from '@/components/col-img-code'
+import { nextTick } from 'process'
+
+type loginType = 'PWD' | 'SMS'
+export default defineComponent({
+  name: 'login-model',
+  props: {
+    /** 是否支持短信验证码注册 */
+    isRegister: {
+      type: Boolean,
+      default: false
+    }
+  },
+  emits: ['close', 'confirm'],
+  setup(props, { emit }) {
+    const countDownRef = ref()
+    const state = reactive({
+      loginType: 'PWD' as loginType,
+      imgCodeStatus: false,
+      username: '',
+      password: '',
+      smsCode: '',
+      countDownStatus: true,
+      countDownTime: 1000 * 120 // 倒计时时间
+    })
+
+    const onLogin = async () => {
+      try {
+        if (!checkPhone(state.username)) {
+          return Toast('请输入正确的手机号码')
+        }
+        if (state.loginType === 'PWD') {
+          const { data } = await request.post('/api-auth/usernameLogin', {
+            requestType: 'form',
+            data: {
+              username: state.username,
+              password: state.password,
+              clientId: 'student',
+              clientSecret: 'student'
+            }
+          })
+          setAuth(
+            data.authentication.token_type +
+              ' ' +
+              data.authentication.access_token
+          )
+        } else {
+          const { data } = await request.post('/api-auth/smsLogin', {
+            requestType: 'form',
+            data: {
+              clientId: 'student',
+              clientSecret: 'student',
+              phone: state.username,
+              smsCode: state.smsCode,
+              isSurportRegister: props.isRegister
+            }
+          })
+          setAuth(
+            data.authentication.token_type +
+              ' ' +
+              data.authentication.access_token
+          )
+        }
+
+        const userCash = await request.get(
+          '/api-student/student/queryUserInfo',
+          {
+            initRequest: true // 初始化接口
+          }
+        )
+        setLogin(userCash.data)
+
+        emit('confirm', true)
+      } catch (e: any) {
+        //
+        console.log(e)
+      }
+    }
+
+    const onSendCode = () => {
+      // 发送验证码
+      if (!checkPhone(state.username)) {
+        return Toast('请输入正确的手机号码')
+      }
+      state.imgCodeStatus = true
+    }
+
+    const onCodeSend = () => {
+      state.countDownStatus = false
+      nextTick(() => {
+        console.log(countDownRef.value, 'countDownRef.value')
+        countDownRef.value.start()
+      })
+    }
+    const onFinished = () => {
+      state.countDownStatus = true
+      countDownRef.value.reset()
+    }
+    return () => (
+      <div class={styles.loginModel}>
+        <i class={styles.iconClose} onClick={() => emit('close')}></i>
+        <div class={styles.loginSection}>
+          <div class={styles.fieldSection}>
+            <Field
+              v-model={state.username}
+              placeholder="请输入手机号"
+              autocomplete="off"
+              border={false}
+              type="tel"
+              maxlength={11}
+            >
+              {{
+                'left-icon': () => <img src={iconPhone} class={styles.icon} />
+              }}
+            </Field>
+
+            {state.loginType === 'PWD' ? (
+              <Field
+                v-model={state.password}
+                placeholder="请输入密码"
+                autocomplete="off"
+                border={false}
+                type="password"
+              >
+                {{
+                  'left-icon': () => (
+                    <img src={iconPassword} class={styles.icon} />
+                  )
+                }}
+              </Field>
+            ) : (
+              <Field
+                v-model={state.smsCode}
+                placeholder="请输入验证码"
+                autocomplete="off"
+                maxlength={6}
+                type="tel"
+                border={false}
+              >
+                {{
+                  'left-icon': () => (
+                    <img src={iconPassword} class={styles.icon} />
+                  ),
+                  button: () =>
+                    state.countDownStatus ? (
+                      <span class={styles.codeText} onClick={onSendCode}>
+                        获取验证码
+                      </span>
+                    ) : (
+                      <CountDown
+                        ref={(el: any) => (countDownRef.value = el)}
+                        auto-start={false}
+                        time={state.countDownTime}
+                        onFinish={onFinished}
+                        format="ss秒"
+                      />
+                    )
+                }}
+              </Field>
+            )}
+          </div>
+
+          <div class={styles.btnGroup}>
+            <Button round block color="#2DC7AA" onClick={onLogin}>
+              登录
+            </Button>
+
+            <span
+              class={styles.btnText}
+              onClick={() => {
+                if (state.loginType === 'PWD') {
+                  state.countDownStatus = true
+                  state.loginType = 'SMS'
+                } else {
+                  state.loginType = 'PWD'
+                }
+              }}
+            >
+              {state.loginType === 'PWD' ? '验证码登录' : '密码登录'}
+            </span>
+          </div>
+        </div>
+
+        {state.imgCodeStatus ? (
+          <Teleport to={'body'}>
+            <MImgCode
+              v-model:value={state.imgCodeStatus}
+              phone={state.username}
+              onClose={() => {
+                state.imgCodeStatus = false
+              }}
+              onSendCode={onCodeSend}
+            />
+          </Teleport>
+        ) : null}
+      </div>
+    )
+  }
+})

BIN
src/views/creation/share-model/images/icon-download.png


BIN
src/views/creation/share-model/images/icon-friend-ring.png


BIN
src/views/creation/share-model/images/icon-friend.png


BIN
src/views/creation/share-model/images/icon-link.png


BIN
src/views/creation/share-model/images/icon-logo.png


BIN
src/views/creation/share-model/images/icon-wechat.png


BIN
src/views/creation/share-model/images/music-bg.png


BIN
src/views/creation/share-model/images/share-bg.png


+ 195 - 0
src/views/creation/share-model/index.module.less

@@ -0,0 +1,195 @@
+.shareContent {
+  position: relative;
+  width: 335px;
+  height: 355px;
+  margin: 0 auto 76px;
+
+  .shareBg {
+    position: absolute;
+    top: 0;
+    left: 0;
+    z-index: 1;
+    width: 335px;
+    height: 355px;
+  }
+
+  .share_content__title {
+    position: relative;
+    z-index: 3;
+    padding-top: 54px;
+    padding-left: 14px;
+    color: #FFFFFF;
+    text-shadow: 0px 2px 4px rgba(10, 181, 192, 0.7);
+    width: 170px;
+
+    .large {
+      font-size: 19px;
+      font-weight: 600;
+      line-height: 26px;
+    }
+
+    .small {
+      padding-top: 6px;
+      font-size: 14px;
+      line-height: 20px;
+    }
+  }
+}
+
+.sectionFile {
+  position: relative;
+  z-index: 3;
+  margin: 25px 15px 14px;
+  padding: 12px;
+  display: flex;
+  background: rgba(255, 255, 255, 0.79);
+  border-radius: 10px;
+
+  .muploader {
+    position: relative;
+    z-index: 9;
+    width: 56px;
+    height: 56px;
+    object-fit: cover;
+    border-radius: 6px;
+  }
+
+  .uploadImg {
+    position: relative;
+    border-radius: 6px;
+    margin-right: 16px;
+
+
+    .audioPan {
+      position: absolute;
+      top: 0;
+      right: -6px;
+      z-index: 1;
+      width: 56px;
+      height: 56px;
+    }
+
+
+  }
+
+  .musicDetail {
+    display: flex;
+    justify-content: center;
+    flex-direction: column;
+
+    .musicName {
+      font-size: 16px;
+      font-weight: 600;
+      color: #131415;
+      line-height: 22px;
+      max-width: 200px;
+    }
+
+    .username {
+      padding-top: 4px;
+      font-size: 13px;
+      color: #aaa;
+      line-height: 20px;
+    }
+  }
+}
+
+.downloadSection {
+  display: flex;
+  align-items: center;
+  margin: 0 20px;
+  position: relative;
+  z-index: 3;
+
+  .qrcode {
+    position: relative;
+    width: 84px;
+    height: 84px;
+    padding: 3px;
+    border-radius: 4px;
+    // opacity: 0.53;
+    border: 1px solid #2BD1B2;
+
+    flex-shrink: 0;
+    overflow: hidden;
+
+    .qrcodeCanvas {
+      width: 100% !important;
+      height: 100% !important;
+    }
+
+    .qrcodeLogo {
+      position: absolute;
+      top: 50%;
+      left: 50%;
+      z-index: 10;
+      margin-left: -8px;
+      margin-top: -8px;
+      width: 16px;
+      height: 16px;
+      border-radius: 4px;
+    }
+  }
+
+  .qrtips {
+    margin-left: 10px;
+    padding-left: 9px;
+    border-left: 1px dashed #D8D8D8;
+
+    .tip {
+      font-size: 12px;
+      color: #777777;
+      line-height: 17px;
+    }
+
+    .iconLogo {
+      width: 68px;
+      height: 21px;
+      margin: 12px 0 4px;
+    }
+
+    .downTip {
+      font-size: 12px;
+      font-weight: 500;
+      color: #333333;
+      line-height: 17px;
+    }
+  }
+}
+
+
+.shareBottom {
+  position: relative;
+  background-color: #fff;
+  border-radius: 12px 12px 0px 0px;
+  padding-bottom: env(safe-area-inset-bottom);
+
+  .iconClose {
+    position: absolute;
+    top: 12px;
+    right: 12px;
+    font-size: 16px;
+    color: #999999;
+  }
+}
+
+.share__header {
+  padding: 12px 15px 8px;
+  font-size: 16px;
+  color: #333333;
+  line-height: 22px;
+}
+
+.gridSection {
+  --van-grid-item-icon-size: 44px;
+  --van-grid-item-text-font-size: 12px;
+  --van-grid-item-text-color: #646566;
+  --van-grid-item-content-padding: 16px 12px;
+}
+
+.btn {
+  font-size: 16px;
+  color: #AAAAAA;
+  line-height: 55px;
+  text-align: center;
+}

+ 222 - 0
src/views/creation/share-model/index.tsx

@@ -0,0 +1,222 @@
+import { defineComponent, onMounted, reactive, ref } from 'vue'
+import styles from './index.module.less'
+import { Grid, GridItem, Icon, Toast } from 'vant'
+import iconDownload from './images/icon-download.png'
+// import iconFirend from './images/icon-friend.png'
+import iconWeChat from './images/icon-wechat.png'
+import iconFriendRing from './images/icon-friend-ring.png'
+// import iconLink from './images/icon-link.png';
+import iconLogo from '@common/images/logo.png'
+import shareBg from './images/share-bg.png'
+import audioPan from '../images/audio-pan.png'
+import smallLogo from '@common/images/icon_logo.png'
+import musicBg from './images/music-bg.png'
+import QRCode from 'qrcode'
+import { promisefiyPostMessage } from '@/helpers/native-message'
+import html2canvas from 'html2canvas'
+
+export default defineComponent({
+  name: 'sahre-model',
+  props: {
+    musicDetail: {
+      type: Object,
+      default: () => {}
+    }
+  },
+  emits: ['close'],
+  setup(props, { emit }) {
+    const canvasRef = ref()
+    const state = reactive({
+      saveLoading: false,
+      image: null as any
+    })
+    const saveImg = async () => {
+      Toast.loading({
+        message: '图片生成中...',
+        forbidClick: true
+      })
+      setTimeout(() => {
+        state.saveLoading = false
+      }, 100)
+      const res = await promisefiyPostMessage({
+        api: 'savePicture',
+        content: {
+          base64: state.image
+        }
+      })
+      if (res?.content?.status === 'success') {
+        Toast.success('保存至相册')
+      } else {
+        Toast.fail('保存失败')
+      }
+    }
+
+    const onSaveWe = async (type: string) => {
+      Toast.loading({
+        message: '图片生成中...',
+        forbidClick: true
+      })
+      setTimeout(() => {
+        state.saveLoading = false
+      }, 100)
+      const res = await promisefiyPostMessage({
+        api: 'shareTripartite',
+        content: {
+          title: '',
+          desc: '',
+          image: state.image,
+          video: '',
+          type: 'image',
+          shareType: type
+        }
+      })
+      if (res?.content?.status) {
+        Toast.success('分享成功')
+      } else {
+        Toast.fail('分享失败')
+      }
+    }
+
+    const onSavePath = (type: string) => {
+      // 判断是否在保存中...
+      if (state.saveLoading) {
+        return
+      }
+      state.saveLoading = true
+      // 判断是否已经生成图片
+      if (state.image) {
+        if (type) {
+          onSaveWe(type)
+        } else {
+          saveImg()
+        }
+      } else {
+        const container: any = document.getElementById('shareContent')
+        html2canvas(container, {
+          allowTaint: true,
+          useCORS: true,
+          backgroundColor: null
+        })
+          .then(async canvas => {
+            const url = canvas.toDataURL('image/png')
+            state.image = url
+            if (type) {
+              onSaveWe(type)
+            } else {
+              saveImg()
+            }
+          })
+          .catch(() => {
+            Toast.clear()
+            state.saveLoading = false
+          })
+      }
+    }
+
+    onMounted(() => {
+      const canvas = canvasRef.value
+      QRCode.toCanvas(
+        canvas,
+        location.origin +
+          location.pathname +
+          '#/shareCreation?id=' +
+          props.musicDetail.id,
+        {
+          margin: 1
+        },
+        (error: any) => {
+          if (error) console.log(error)
+          console.log('success')
+        }
+      )
+    })
+
+    return () => (
+      <div class={styles.shareModel}>
+        <div class={styles.shareContent} id="shareContent">
+          <img src={shareBg} class={styles.shareBg} />
+          <div class={styles.share_content__title}>
+            <p class={styles.large}>我在酷乐秀发布了演奏作品</p>
+            <p class={styles.small}>赶快扫码看看吧!</p>
+          </div>
+
+          <div class={[styles.share_music__container, styles.sectionFile]}>
+            <div class={styles.uploadImg}>
+              <img
+                src={audioPan}
+                class={styles.audioPan}
+                crossorigin="anonymous"
+              />
+              <img
+                src={
+                  props.musicDetail.img
+                    ? props.musicDetail.img + '?t' + new Date().getTime()
+                    : musicBg
+                }
+                class={styles.muploader}
+                crossorigin="anonymous"
+              />
+            </div>
+            <div class={styles.musicDetail}>
+              <p class={[styles.musicName, 'van-ellipsis']}>
+                {props.musicDetail.musicSheetName}
+              </p>
+              <p class={styles.username}>
+                演奏者:{props.musicDetail.username}
+              </p>
+            </div>
+          </div>
+
+          <div class={styles.downloadSection}>
+            <div class={styles.qrcode}>
+              <canvas ref={canvasRef} class={styles.qrcodeCanvas}></canvas>
+              <img src={smallLogo} class={styles.qrcodeLogo} />
+            </div>
+            <div class={styles.qrtips}>
+              <p class={styles.tip}>
+                温馨提示:保存图片到相册或长按识别二维码进入查看喔~
+              </p>
+              <img src={iconLogo} class={styles.iconLogo} />
+              {/* <p class={styles.downTip}>扫码下载音乐数字课堂App</p> */}
+            </div>
+          </div>
+        </div>
+
+        <div class={styles.shareBottom}>
+          <Icon
+            name="cross"
+            class={styles.iconClose}
+            onClick={() => emit('close')}
+          />
+          <div class={styles.share__header}>海报已生成!快来分享吧!</div>
+          <Grid columnNum={5} border={false} class={styles.gridSection}>
+            <GridItem
+              icon={iconDownload}
+              text="保存本地"
+              onClick={() => onSavePath('')}
+            ></GridItem>
+            {/* <GridItem icon={iconFirend} text="群聊"></GridItem> */}
+            <GridItem
+              icon={iconWeChat}
+              text="微信好友"
+              onClick={() => onSavePath('wechat')}
+            ></GridItem>
+            <GridItem
+              icon={iconFriendRing}
+              text="朋友圈"
+              onClick={() => onSavePath('wechat_circle')}
+            ></GridItem>
+            {/* <GridItem icon={iconLink} text="复制链接"></GridItem> */}
+          </Grid>
+
+          <div
+            class={[styles.btn, 'van-hairline--top']}
+            onClick={() => emit('close')}
+          >
+            取消
+          </div>
+        </div>
+      </div>
+    )
+  }
+})

+ 195 - 1
yarn.lock

@@ -1607,6 +1607,14 @@
   dependencies:
     "@babel/helper-define-polyfill-provider" "^0.3.0"
 
+"babel-runtime@^6.9.2":
+  "integrity" "sha512-ITKNuq2wKlW1fJg9sSW52eepoYgZBggvOAHC0u/CYu/qxQ9EVzThCgR69BnSXLHjy2f7SY5zaQ4yt7H9ZVxY2g=="
+  "resolved" "https://registry.npmmirror.com/babel-runtime/-/babel-runtime-6.26.0.tgz"
+  "version" "6.26.0"
+  dependencies:
+    "core-js" "^2.4.0"
+    "regenerator-runtime" "^0.11.0"
+
 "babel-walk@3.0.0-canary-5":
   "integrity" "sha512-GAwkz0AihzY5bkwIY5QDR+LvsRQgB/B+1foMPvi0FZPMl5fjD7ICiznUiBdLYMH1QYe6vqu4gWYytZOccLouFw=="
   "resolved" "https://registry.npmmirror.com/babel-walk/-/babel-walk-3.0.0-canary-5.tgz"
@@ -1647,6 +1655,11 @@
     "inherits" "^2.0.4"
     "readable-stream" "^3.4.0"
 
+"blueimp-md5@^2.10.0":
+  "integrity" "sha512-DRQrD6gJyy8FbiE4s+bDoXS9hiW3Vbx5uCdwvcCf3zLHL+Iv7LtGHLpr+GZV8rHG8tK766FGYBwRbu8pELTt+w=="
+  "resolved" "https://registry.npmmirror.com/blueimp-md5/-/blueimp-md5-2.19.0.tgz"
+  "version" "2.19.0"
+
 "brace-expansion@^1.1.7":
   "integrity" "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA=="
   "resolved" "https://registry.npmmirror.com/brace-expansion/-/brace-expansion-1.1.11.tgz"
@@ -1813,6 +1826,11 @@
   "resolved" "https://registry.npmmirror.com/chardet/-/chardet-0.7.0.tgz"
   "version" "0.7.0"
 
+"charenc@0.0.2":
+  "integrity" "sha512-yrLQ/yVUFXkzg7EDQsPieE/53+0RlaWTs+wBrvW36cyilJ2SaDWfl4Yj7MtLTXleV9uEKefbAGUPv2/iWSooRA=="
+  "resolved" "https://registry.npmmirror.com/charenc/-/charenc-0.0.2.tgz"
+  "version" "0.0.2"
+
 "ci-info@^1.5.0":
   "integrity" "sha512-vsGdkwSCDpWmP80ncATX7iea5DWQemg1UgCW5J8tqjU3lYw4FBYuj89J0CTVomA7BEfvSZd84GmHko+MxFQU2A=="
   "resolved" "https://registry.npmmirror.com/ci-info/-/ci-info-1.6.0.tgz"
@@ -1974,6 +1992,11 @@
     "browserslist" "^4.17.6"
     "semver" "7.0.0"
 
+"core-js@^2.4.0":
+  "integrity" "sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ=="
+  "resolved" "https://registry.npmmirror.com/core-js/-/core-js-2.6.12.tgz"
+  "version" "2.6.12"
+
 "core-js@^3.11.0", "core-js@^3.21.0", "core-js@^3.22.0":
   "integrity" "sha512-oAKwkj9xcWNBAvGbT//WiCdOMpb9XQG92/Fe3ABFM/R16BsHgePG00mFOgKf7IsCtfj8tA1kHtf/VwErhriz5Q=="
   "resolved" "https://registry.npmmirror.com/core-js/-/core-js-3.23.3.tgz"
@@ -2004,6 +2027,11 @@
     "shebang-command" "^2.0.0"
     "which" "^2.0.1"
 
+"crypt@0.0.2":
+  "integrity" "sha512-mCxBlsHFYh9C+HVpiEacem8FEBnMXgU9gy4zmNC+SXAZNB/1idgp/aulFJ4FgCi7GPEVbfyng092GqL2k2rmow=="
+  "resolved" "https://registry.npmmirror.com/crypt/-/crypt-0.0.2.tgz"
+  "version" "0.0.2"
+
 "css-line-break@^2.1.0":
   "integrity" "sha512-FHcKFCZcAha3LwfVBhCQbW2nCNbkZXn7KVUJcsT5/P8YmfsVja0FMPJr0B903j/E69HUphKiV9iQArX8SDYA4w=="
   "resolved" "https://registry.npmmirror.com/css-line-break/-/css-line-break-2.1.0.tgz"
@@ -2121,6 +2149,11 @@
     "domhandler" "^4.2.0"
     "entities" "^2.0.0"
 
+"dom-walk@^0.1.0":
+  "integrity" "sha512-6QvTW9mrGeIegrFXdtQi9pk7O/nSK6lSdXW2eqUspN5LWD7UTji2Fqw5V2YLjBpHEoU9Xl/eUWNpDeZvoyOv2w=="
+  "resolved" "https://registry.npmmirror.com/dom-walk/-/dom-walk-0.1.2.tgz"
+  "version" "0.1.2"
+
 "domelementtype@^2.0.1", "domelementtype@^2.2.0":
   "integrity" "sha512-DtBMo82pv1dFtUmHyr48beiuq792Sxohr+8Hm9zoxklYPfa6n0Z3Byjj2IV7bmr2IyqClnqEQhfgHJJ5QF0R5A=="
   "resolved" "https://registry.npmmirror.com/domelementtype/-/domelementtype-2.2.0.tgz"
@@ -2227,6 +2260,11 @@
   "resolved" "https://registry.npmmirror.com/es-module-lexer/-/es-module-lexer-0.9.3.tgz"
   "version" "0.9.3"
 
+"es5-shim@^4.5.1":
+  "integrity" "sha512-jg21/dmlrNQI7JyyA2w7n+yifSxBng0ZralnSfVZjoCawgNTCnS+yBCyVM9DL5itm7SUnDGgv7hcq2XCZX4iRQ=="
+  "resolved" "https://registry.npmmirror.com/es5-shim/-/es5-shim-4.6.7.tgz"
+  "version" "4.6.7"
+
 "esbuild-darwin-64@0.14.54":
   "integrity" "sha512-jtdKWV3nBviOd5v4hOpkVmpxsBy90CGzebpbO9beiqUYVMBtSc0AL9zGftFuBon7PNDcdvNCEuQqw2x0wP9yug=="
   "resolved" "https://registry.npmmirror.com/esbuild-darwin-64/-/esbuild-darwin-64-0.14.54.tgz"
@@ -2722,6 +2760,14 @@
     "is-windows" "^1.0.1"
     "which" "^1.2.14"
 
+"global@^4.3.1", "global@~4.3.0", "global@4.3.2":
+  "integrity" "sha512-/4AybdwIDU4HkCUbJkZdWpe4P6vuw/CUtu+0I1YlLIPe7OlUO7KNJ+q/rO70CW2/NW6Jc6I62++Hzsf5Alu6rQ=="
+  "resolved" "https://registry.npmmirror.com/global/-/global-4.3.2.tgz"
+  "version" "4.3.2"
+  dependencies:
+    "min-document" "^2.19.0"
+    "process" "~0.5.1"
+
 "globals@^11.1.0":
   "integrity" "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA=="
   "resolved" "https://registry.npmmirror.com/globals/-/globals-11.12.0.tgz"
@@ -2916,6 +2962,11 @@
   "resolved" "https://registry.npmmirror.com/indent-string/-/indent-string-4.0.0.tgz"
   "version" "4.0.0"
 
+"individual@^2.0.0":
+  "integrity" "sha512-pWt8hBCqJsUWI/HtcfWod7+N9SgAqyPEaF7JQjwzjn5vGrpg6aQ5qeAFQ7dx//UH4J1O+7xqew+gCeeFt6xN/g=="
+  "resolved" "https://registry.npmmirror.com/individual/-/individual-2.0.0.tgz"
+  "version" "2.0.0"
+
 "inflight@^1.0.4":
   "integrity" "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA=="
   "resolved" "https://registry.npmmirror.com/inflight/-/inflight-1.0.6.tgz"
@@ -2967,6 +3018,11 @@
     "is-relative" "^1.0.0"
     "is-windows" "^1.0.1"
 
+"is-buffer@~1.1.6":
+  "integrity" "sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w=="
+  "resolved" "https://registry.npmmirror.com/is-buffer/-/is-buffer-1.1.6.tgz"
+  "version" "1.1.6"
+
 "is-ci@^1.0.10":
   "integrity" "sha512-s6tfsaQaQi3JNciBH6shVqEDvhGut0SUXr31ag8Pd8BBbVVlcGfWhpPmEOoM6RJ5TFhbypvf5yyRw/VXW1IiWg=="
   "resolved" "https://registry.npmmirror.com/is-ci/-/is-ci-1.2.1.tgz"
@@ -3004,6 +3060,11 @@
   "resolved" "https://registry.npmmirror.com/is-fullwidth-code-point/-/is-fullwidth-code-point-4.0.0.tgz"
   "version" "4.0.0"
 
+"is-function@^1.0.1":
+  "integrity" "sha512-lw7DUp0aWXYg+CBCN+JKkcE0Q2RayZnSvnZBlwgxHBQhqt5pZNVy4Ri7H9GmmXkdu7LUthszM+Tor1u/2iBcpQ=="
+  "resolved" "https://registry.npmmirror.com/is-function/-/is-function-1.0.2.tgz"
+  "version" "1.0.2"
+
 "is-glob@^4.0.0", "is-glob@^4.0.1", "is-glob@^4.0.3":
   "integrity" "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg=="
   "resolved" "https://registry.npmmirror.com/is-glob/-/is-glob-4.0.3.tgz"
@@ -3143,6 +3204,11 @@
   dependencies:
     "argparse" "^2.0.1"
 
+"jsencrypt@^3.2.0":
+  "integrity" "sha512-arQR1R1ESGdAxY7ZheWr12wCaF2yF47v5qpB76TtV64H1pyGudk9Hvw8Y9tb/FiTIaaTRUyaSnm5T/Y53Ghm/A=="
+  "resolved" "https://registry.npmmirror.com/jsencrypt/-/jsencrypt-3.3.2.tgz"
+  "version" "3.3.2"
+
 "jsesc@^2.5.1":
   "integrity" "sha512-OYu7XEzjkCQ3C5Ps3QIZsQfNpqoJyZZA99wd9aWd05NCtC5pWOkShK2mkL6HXQR6/Cy2lbNdPlZBpuQHXE63gA=="
   "resolved" "https://registry.npmmirror.com/jsesc/-/jsesc-2.5.2.tgz"
@@ -3422,6 +3488,15 @@
   "resolved" "https://registry.npmmirror.com/map-cache/-/map-cache-0.2.2.tgz"
   "version" "0.2.2"
 
+"md5@^2.3.0":
+  "integrity" "sha512-T1GITYmFaKuO91vxyoQMFETst+O71VUPEU3ze5GNzDm0OWdP8v1ziTaAEPUr/3kLsY3Sftgz242A1SetQiDL7g=="
+  "resolved" "https://registry.npmmirror.com/md5/-/md5-2.3.0.tgz"
+  "version" "2.3.0"
+  dependencies:
+    "charenc" "0.0.2"
+    "crypt" "0.0.2"
+    "is-buffer" "~1.1.6"
+
 "merge-stream@^2.0.0":
   "integrity" "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w=="
   "resolved" "https://registry.npmmirror.com/merge-stream/-/merge-stream-2.0.0.tgz"
@@ -3450,6 +3525,13 @@
   "resolved" "https://registry.npmmirror.com/mimic-fn/-/mimic-fn-2.1.0.tgz"
   "version" "2.1.0"
 
+"min-document@^2.19.0":
+  "integrity" "sha512-9Wy1B3m3f66bPPmU5hdA4DR4PB2OfDU/+GS3yAB7IQozE3tqXaVv2zOjgla7MEGSRv95+ILmOuvhLkOK6wJtCQ=="
+  "resolved" "https://registry.npmmirror.com/min-document/-/min-document-2.19.0.tgz"
+  "version" "2.19.0"
+  dependencies:
+    "dom-walk" "^0.1.0"
+
 "minimatch@^3.0.4":
   "integrity" "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA=="
   "resolved" "https://registry.npmmirror.com/minimatch/-/minimatch-3.0.4.tgz"
@@ -3590,7 +3672,7 @@
   "resolved" "https://registry.npmmirror.com/numeral/-/numeral-2.0.6.tgz"
   "version" "2.0.6"
 
-"object-assign@^4.1.1":
+"object-assign@^4.1.0", "object-assign@^4.1.1":
   "integrity" "sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg=="
   "resolved" "https://registry.npmmirror.com/object-assign/-/object-assign-4.1.1.tgz"
   "version" "4.1.1"
@@ -3756,6 +3838,11 @@
     "map-cache" "^0.2.0"
     "path-root" "^0.1.1"
 
+"parse-headers@^2.0.0":
+  "integrity" "sha512-ft3iAoLOB/MlwbNXgzy43SWGP6sQki2jQvAyBg/zDFAgr9bfNWZIUj42Kw2eJIl8kEi4PbgE6U1Zau/HwI75HA=="
+  "resolved" "https://registry.npmmirror.com/parse-headers/-/parse-headers-2.0.5.tgz"
+  "version" "2.0.5"
+
 "parse-node-version@^1.0.1":
   "integrity" "sha512-3YHlOa/JgH6Mnpr05jP9eDG254US9ek25LyIxZlDItp2iJtwyaXQb57lBYLdT3MowkUFYEV2XXNAYIPlESvJlA=="
   "resolved" "https://registry.npmmirror.com/parse-node-version/-/parse-node-version-1.0.1.tgz"
@@ -3910,6 +3997,11 @@
   "resolved" "https://registry.npmmirror.com/prettier/-/prettier-2.5.1.tgz"
   "version" "2.5.1"
 
+"process@~0.5.1":
+  "integrity" "sha512-oNpcutj+nYX2FjdEW7PGltWhXulAnFlM0My/k48L90hARCOJtvBbQXc/6itV2jDvU5xAAtonP+r6wmQgCcbAUA=="
+  "resolved" "https://registry.npmmirror.com/process/-/process-0.5.2.tgz"
+  "version" "0.5.2"
+
 "progress@^2.0.0":
   "integrity" "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA=="
   "resolved" "https://registry.npmmirror.com/progress/-/progress-2.0.3.tgz"
@@ -4062,6 +4154,15 @@
   dependencies:
     "side-channel" "^1.0.4"
 
+"query-string@^5.0.1":
+  "integrity" "sha512-gjWOsm2SoGlgLEdAGt7a6slVOk9mGiXmPFMqrEhLQ68rhQuBnpfs3+EmlvqKyxnCo9/PPlF+9MtY02S1aFg+Jw=="
+  "resolved" "https://registry.npmmirror.com/query-string/-/query-string-5.1.1.tgz"
+  "version" "5.1.1"
+  dependencies:
+    "decode-uri-component" "^0.2.0"
+    "object-assign" "^4.1.0"
+    "strict-uri-encode" "^1.0.0"
+
 "query-string@^7.1.1":
   "integrity" "sha512-MplouLRDHBZSG9z7fpuAAcI7aAYjDLhtsiVZsevsfaHWDS2IDdORKbSd1kWUA+V4zyva/HZoSfpwnYMMQDhb0w=="
   "resolved" "https://registry.npmmirror.com/query-string/-/query-string-7.1.1.tgz"
@@ -4117,6 +4218,11 @@
   "resolved" "https://registry.npmmirror.com/regenerate/-/regenerate-1.4.2.tgz"
   "version" "1.4.2"
 
+"regenerator-runtime@^0.11.0":
+  "integrity" "sha512-MguG95oij0fC3QV3URf4V2SDYGJhJnJGqvIIgdECeODCT98wSWDAJ94SSuVpYQUoTcGUIL6L4yNB7j1DFFHSBg=="
+  "resolved" "https://registry.npmmirror.com/regenerator-runtime/-/regenerator-runtime-0.11.1.tgz"
+  "version" "0.11.1"
+
 "regenerator-runtime@^0.13.4", "regenerator-runtime@^0.13.9":
   "integrity" "sha512-p3VT+cOEgxFsRRA9X4lkI1E+k2/CtnKtU4gcxyaCUreilL/vqI6CdZ3wxVUx3UOUg+gnUOQQcRI7BmSI656MYA=="
   "resolved" "https://registry.npmmirror.com/regenerator-runtime/-/regenerator-runtime-0.13.9.tgz"
@@ -4257,6 +4363,13 @@
   dependencies:
     "queue-microtask" "^1.2.2"
 
+"rust-result@^1.0.0":
+  "integrity" "sha512-6cJzSBU+J/RJCF063onnQf0cDUOHs9uZI1oroSGnHOph+CQTIJ5Pp2hK5kEQq1+7yE/EEWfulSNXAQ2jikPthA=="
+  "resolved" "https://registry.npmmirror.com/rust-result/-/rust-result-1.0.0.tgz"
+  "version" "1.0.0"
+  dependencies:
+    "individual" "^2.0.0"
+
 "rxjs@^7.2.0", "rxjs@^7.4.0", "rxjs@^7.5.5":
   "integrity" "sha512-sy+H0pQofO95VDmFLzyaw9xNJU4KTRSwQIGM6+iG3SypAtCiLDzpeG8sJrNCWn2Up9km+KhkvTdbkrdy+yzZdw=="
   "resolved" "https://registry.npmmirror.com/rxjs/-/rxjs-7.5.5.tgz"
@@ -4274,6 +4387,13 @@
   "resolved" "https://registry.npmmirror.com/safe-buffer/-/safe-buffer-5.2.1.tgz"
   "version" "5.2.1"
 
+"safe-json-parse@4.0.0":
+  "integrity" "sha512-RjZPPHugjK0TOzFrLZ8inw44s9bKox99/0AZW9o/BEQVrJfhI+fIHMErnPyRa89/yRXUUr93q+tiN6zhoVV4wQ=="
+  "resolved" "https://registry.npmmirror.com/safe-json-parse/-/safe-json-parse-4.0.0.tgz"
+  "version" "4.0.0"
+  dependencies:
+    "rust-result" "^1.0.0"
+
 "safer-buffer@>= 2.1.2 < 3", "safer-buffer@>= 2.1.2 < 3.0.0":
   "integrity" "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg=="
   "resolved" "https://registry.npmmirror.com/safer-buffer/-/safer-buffer-2.1.2.tgz"
@@ -4434,6 +4554,21 @@
   "resolved" "https://registry.npmmirror.com/stackblur-canvas/-/stackblur-canvas-2.6.0.tgz"
   "version" "2.6.0"
 
+"store@^2.0.12":
+  "integrity" "sha512-eO9xlzDpXLiMr9W1nQ3Nfp9EzZieIQc10zPPMP5jsVV7bLOziSFFBP0XoDXACEIFtdI+rIz0NwWVA/QVJ8zJtw=="
+  "resolved" "https://registry.npmmirror.com/store/-/store-2.0.12.tgz"
+  "version" "2.0.12"
+
+"store2@^2.7.1":
+  "integrity" "sha512-siT1RiqlfQnGqgT/YzXVUNsom9S0H1OX+dpdGN1xkyYATo4I6sep5NmsRD/40s3IIOvlCq6akxkqG82urIZW1w=="
+  "resolved" "https://registry.npmmirror.com/store2/-/store2-2.14.2.tgz"
+  "version" "2.14.2"
+
+"strict-uri-encode@^1.0.0":
+  "integrity" "sha512-R3f198pcvnB+5IpnBlRkphuE9n46WyVl8I39W/ZUTZLz4nqSP/oLYUrcnJrw462Ds8he4YKMov2efsTIw1BDGQ=="
+  "resolved" "https://registry.npmmirror.com/strict-uri-encode/-/strict-uri-encode-1.1.0.tgz"
+  "version" "1.1.0"
+
 "strict-uri-encode@^2.0.0":
   "integrity" "sha512-QwiXZgpRcKkhTj2Scnn++4PKtWsH0kpzZ62L2R6c/LUVYv7hVnZqcg2+sMuT6R7Jusu1vviK/MFsu6kNJfWlEQ=="
   "resolved" "https://registry.npmmirror.com/strict-uri-encode/-/strict-uri-encode-2.0.0.tgz"
@@ -4542,6 +4677,26 @@
   "resolved" "https://registry.npmmirror.com/systemjs/-/systemjs-6.12.1.tgz"
   "version" "6.12.1"
 
+"tcplayer.js@^5.0.1":
+  "integrity" "sha512-CRe4eV6Jfh3USPhW5n2wI9BzUpwC2kZeBk05G9IWMxEe4OqlhFXol0uxo+WE1R21e8dXoHB9VXIg7RqQLTe9qA=="
+  "resolved" "https://registry.npmmirror.com/tcplayer.js/-/tcplayer.js-5.0.1.tgz"
+  "version" "5.0.1"
+  dependencies:
+    "babel-runtime" "^6.9.2"
+    "blueimp-md5" "^2.10.0"
+    "global" "4.3.2"
+    "jsencrypt" "^3.2.0"
+    "md5" "^2.3.0"
+    "query-string" "^5.0.1"
+    "safe-json-parse" "4.0.0"
+    "store" "^2.0.12"
+    "store2" "^2.7.1"
+    "tsml" "1.0.1"
+    "videojs-font" "2.1.0"
+    "videojs-ie8" "1.1.2"
+    "videojs-vtt.js" "0.12.4"
+    "xhr" "2.4.0"
+
 "text-segmentation@^1.0.3":
   "integrity" "sha512-iOiPUo/BGnZ6+54OsWxZidGCsdU8YbE4PSpdPinp7DeMtUJNJBoJ/ouUSTJjHkh1KntHaltHl/gDs2FC4i5+Nw=="
   "resolved" "https://registry.npmmirror.com/text-segmentation/-/text-segmentation-1.0.3.tgz"
@@ -4600,6 +4755,11 @@
   "resolved" "https://registry.npmmirror.com/tslib/-/tslib-2.3.0.tgz"
   "version" "2.3.0"
 
+"tsml@1.0.1":
+  "integrity" "sha512-3KmepnH9SUsoOVtg013CRrL7c+AK7ECaquAsJdvu4288EDJuraqBlP4PDXT/rLEJ9YDn4jqLAzRJsnFPx+V6lg=="
+  "resolved" "https://registry.npmmirror.com/tsml/-/tsml-1.0.1.tgz"
+  "version" "1.0.1"
+
 "tsutils@^3.21.0":
   "integrity" "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA=="
   "resolved" "https://registry.npmmirror.com/tsutils/-/tsutils-3.21.0.tgz"
@@ -4755,6 +4915,25 @@
     "core-js" "^3.11.0"
     "mutation-observer" "^1.0.3"
 
+"videojs-font@2.1.0":
+  "integrity" "sha512-zFqWpLrXf1q8NtYx5qtZhMC6SLUFScDmR6j+UGPogobxR21lvXShhnzcNNMdOxJUuFLiToJ/BPpFUQwX4xhpvA=="
+  "resolved" "https://registry.npmmirror.com/videojs-font/-/videojs-font-2.1.0.tgz"
+  "version" "2.1.0"
+
+"videojs-ie8@1.1.2":
+  "integrity" "sha512-0Zb2T4MLkpfZbeGMK/Z93b8Lrepr+rLFoHgQV1CoDeFqXvH7b+Vsd/VHoILGxQrgCSHFQ7mAODR6oyMjuiD4/g=="
+  "resolved" "https://registry.npmmirror.com/videojs-ie8/-/videojs-ie8-1.1.2.tgz"
+  "version" "1.1.2"
+  dependencies:
+    "es5-shim" "^4.5.1"
+
+"videojs-vtt.js@0.12.4":
+  "integrity" "sha512-JQ5eozH5SLOL5xI8ALb1aWf9HjcewQmOytf1gPIsFBTQlSgtSdJ8E8x0GO0ZEXVtFCaPDFiYWAhrjuTI125tBQ=="
+  "resolved" "https://registry.npmmirror.com/videojs-vtt.js/-/videojs-vtt.js-0.12.4.tgz"
+  "version" "0.12.4"
+  dependencies:
+    "global" "^4.3.1"
+
 "vite-plugin-style-import@^1.4.0":
   "integrity" "sha512-EGAx0zVGUkwAwvDaC66zxgzXyHE0CwAXp4O1xGKnpMcrDT9L1nlojiCjjLRQzL8C3zwY1jn9ilq+m0VABKiiLg=="
   "resolved" "https://registry.npmmirror.com/vite-plugin-style-import/-/vite-plugin-style-import-1.4.0.tgz"
@@ -5065,6 +5244,21 @@
   "resolved" "https://registry.npmmirror.com/wrappy/-/wrappy-1.0.2.tgz"
   "version" "1.0.2"
 
+"xhr@2.4.0":
+  "integrity" "sha512-TUbBsdAuJbX8olk9hsDwGK8P1ri1XlV+PdEWkYw+HQQbpkiBR8PLgD1F3kQDPBs9l4Px34hP9rCYAZOCCAENbw=="
+  "resolved" "https://registry.npmmirror.com/xhr/-/xhr-2.4.0.tgz"
+  "version" "2.4.0"
+  dependencies:
+    "global" "~4.3.0"
+    "is-function" "^1.0.1"
+    "parse-headers" "^2.0.0"
+    "xtend" "^4.0.0"
+
+"xtend@^4.0.0":
+  "integrity" "sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ=="
+  "resolved" "https://registry.npmmirror.com/xtend/-/xtend-4.0.2.tgz"
+  "version" "4.0.2"
+
 "y18n@^4.0.0":
   "integrity" "sha512-JKhqTOwSrqNA1NY5lSztJ1GrBiUodLMmIZuLiDaMRJ+itFd+ABVE8XBjOvIWL+rSqNDC74LCSFmlb/U4UZ4hJQ=="
   "resolved" "https://registry.npmmirror.com/y18n/-/y18n-4.0.3.tgz"