hookehuyr

feat(视频上传): 新增视频上传功能及相关组件

新增视频上传页面、弹窗组件及视频播放器自动播放配置。添加相关依赖以支持视频上传功能。
......@@ -9,6 +9,10 @@
"version": "0.0.0",
"dependencies": {
"@heroicons/vue": "^2.2.0",
"@uppy/core": "^4.4.3",
"@uppy/dashboard": "^4.3.2",
"@uppy/vue": "^2.1.1",
"@uppy/xhr-upload": "^4.3.3",
"@vant/touch-emulator": "^1.4.0",
"@vant/use": "^1.6.0",
"@videojs-player/vue": "^1.0.0",
......@@ -1192,12 +1196,22 @@
"win32"
]
},
"node_modules/@transloadit/prettier-bytes": {
"version": "0.3.5",
"resolved": "https://registry.npmjs.org/@transloadit/prettier-bytes/-/prettier-bytes-0.3.5.tgz",
"integrity": "sha512-xF4A3d/ZyX2LJWeQZREZQw+qFX4TGQ8bGVP97OLRt6sPO6T0TNHBFTuRHOJh7RNmYOBmQ9MHxpolD9bXihpuVA=="
},
"node_modules/@types/estree": {
"version": "1.0.6",
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-1.0.6.tgz",
"integrity": "sha512-AYnb1nQyY49te+VRAVgmzfcgjYS91mY5P0TKUDCLEM+gNnA+3T6rWITXRLYCpahpqSQbN5cE+gHpnPyXjHWxcw==",
"dev": true
},
"node_modules/@types/retry": {
"version": "0.12.2",
"resolved": "https://registry.npmjs.org/@types/retry/-/retry-0.12.2.tgz",
"integrity": "sha512-XISRgDJ2Tc5q4TRqvgJtzsRkFYNJzZrhTdtMoGVBttwzzQJkPnS3WWTFc7kuDRoPtPakl+T+OfdEUjYJj7Jbow=="
},
"node_modules/@types/video.js": {
"version": "7.3.58",
"resolved": "https://registry.npmjs.org/@types/video.js/-/video.js-7.3.58.tgz",
......@@ -1210,6 +1224,220 @@
"integrity": "sha512-oIQLCGWtcFZy2JW77j9k8nHzAOpqMHLQejDA48XXMWH6tjCQHz5RCFz1bzsmROyL6PUm+LLnUiI4BCn221inxA==",
"dev": true
},
"node_modules/@uppy/companion-client": {
"version": "4.4.1",
"resolved": "https://registry.npmjs.org/@uppy/companion-client/-/companion-client-4.4.1.tgz",
"integrity": "sha512-ardMacShsfzaIbqHEH48YlpzWZkBj1qhAj0Dvn3r31p9d0HA5xFUvAdLYrZ6ezKvZ0RcDbf0SB5qCrQMkjscXQ==",
"dependencies": {
"@uppy/utils": "^6.1.1",
"namespace-emitter": "^2.0.1",
"p-retry": "^6.1.0"
},
"peerDependencies": {
"@uppy/core": "^4.4.1"
}
},
"node_modules/@uppy/core": {
"version": "4.4.3",
"resolved": "https://registry.npmjs.org/@uppy/core/-/core-4.4.3.tgz",
"integrity": "sha512-Ma/v9+u0xYoxFcTajBpe0TUHI0Vjw2IKgB0AUNevhgFsBRgA03nL5n8Fac8TrC0QjPkYu7h0n2xf2EgzvyxAQA==",
"dependencies": {
"@transloadit/prettier-bytes": "^0.3.4",
"@uppy/store-default": "^4.2.0",
"@uppy/utils": "^6.1.2",
"lodash": "^4.17.21",
"mime-match": "^1.0.2",
"namespace-emitter": "^2.0.1",
"nanoid": "^5.0.9",
"preact": "^10.5.13"
}
},
"node_modules/@uppy/core/node_modules/nanoid": {
"version": "5.1.5",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-5.1.5.tgz",
"integrity": "sha512-Ir/+ZpE9fDsNH0hQ3C68uyThDXzYcim2EqcZ8zn8Chtt1iylPT9xXJB0kPCnqzgcEGikO9RxSrh63MsmVCU7Fw==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/ai"
}
],
"bin": {
"nanoid": "bin/nanoid.js"
},
"engines": {
"node": "^18 || >=20"
}
},
"node_modules/@uppy/dashboard": {
"version": "4.3.2",
"resolved": "https://registry.npmjs.org/@uppy/dashboard/-/dashboard-4.3.2.tgz",
"integrity": "sha512-6cikgcY/TMy+Fq/v03QI1BNocfm1kOxii3kuUaxnz1SFGeuZ/55+C7KKL7SP/IdeoVwH7KV+550HThT0uwIQEw==",
"dependencies": {
"@transloadit/prettier-bytes": "^0.3.4",
"@uppy/informer": "^4.2.1",
"@uppy/provider-views": "^4.4.2",
"@uppy/status-bar": "^4.1.2",
"@uppy/thumbnail-generator": "^4.1.1",
"@uppy/utils": "^6.1.2",
"classnames": "^2.2.6",
"lodash": "^4.17.21",
"memoize-one": "^6.0.0",
"nanoid": "^5.0.9",
"preact": "^10.5.13",
"shallow-equal": "^3.0.0"
},
"peerDependencies": {
"@uppy/core": "^4.4.2"
}
},
"node_modules/@uppy/dashboard/node_modules/nanoid": {
"version": "5.1.5",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-5.1.5.tgz",
"integrity": "sha512-Ir/+ZpE9fDsNH0hQ3C68uyThDXzYcim2EqcZ8zn8Chtt1iylPT9xXJB0kPCnqzgcEGikO9RxSrh63MsmVCU7Fw==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/ai"
}
],
"bin": {
"nanoid": "bin/nanoid.js"
},
"engines": {
"node": "^18 || >=20"
}
},
"node_modules/@uppy/informer": {
"version": "4.2.1",
"resolved": "https://registry.npmjs.org/@uppy/informer/-/informer-4.2.1.tgz",
"integrity": "sha512-0en8Py47pl6RMDrgUfqFoF807W5kK5AKVJNT1SkTsLiGg5anmTIMuvmNG3k6LN4cn9P/rKyEHSdGcoBBUj9u7Q==",
"dependencies": {
"@uppy/utils": "^6.1.1",
"preact": "^10.5.13"
},
"peerDependencies": {
"@uppy/core": "^4.4.1"
}
},
"node_modules/@uppy/provider-views": {
"version": "4.4.2",
"resolved": "https://registry.npmjs.org/@uppy/provider-views/-/provider-views-4.4.2.tgz",
"integrity": "sha512-YGrPJuydrksmMCjvo7Ty7/lDLNo/Y8zsOgWgWmVbXB0V5aRvqY49LeKY8HDlOXclKmn6dl5CeQFf7p46txRNGQ==",
"dependencies": {
"@uppy/utils": "^6.1.2",
"classnames": "^2.2.6",
"nanoid": "^5.0.9",
"p-queue": "^8.0.0",
"preact": "^10.5.13"
},
"peerDependencies": {
"@uppy/core": "^4.4.2"
}
},
"node_modules/@uppy/provider-views/node_modules/nanoid": {
"version": "5.1.5",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-5.1.5.tgz",
"integrity": "sha512-Ir/+ZpE9fDsNH0hQ3C68uyThDXzYcim2EqcZ8zn8Chtt1iylPT9xXJB0kPCnqzgcEGikO9RxSrh63MsmVCU7Fw==",
"funding": [
{
"type": "github",
"url": "https://github.com/sponsors/ai"
}
],
"bin": {
"nanoid": "bin/nanoid.js"
},
"engines": {
"node": "^18 || >=20"
}
},
"node_modules/@uppy/status-bar": {
"version": "4.1.2",
"resolved": "https://registry.npmjs.org/@uppy/status-bar/-/status-bar-4.1.2.tgz",
"integrity": "sha512-Z2fDXItoE940uMo3kwdDo4ZFPjTk5GY6y/C/G5+tSl6nL/IaDtWo5iVbAKHIH4s9SIRwCBgllEhxbmEPhuK7eA==",
"dependencies": {
"@transloadit/prettier-bytes": "^0.3.4",
"@uppy/utils": "^6.1.2",
"classnames": "^2.2.6",
"preact": "^10.5.13"
},
"peerDependencies": {
"@uppy/core": "^4.4.2"
}
},
"node_modules/@uppy/store-default": {
"version": "4.2.0",
"resolved": "https://registry.npmjs.org/@uppy/store-default/-/store-default-4.2.0.tgz",
"integrity": "sha512-PieFVa8yTvRHIqsNKfpO/yaJw5Ae/hT7uT58ryw7gvCBY5bHrNWxH5N0XFe8PFHMpLpLn8v3UXGx9ib9QkB6+Q=="
},
"node_modules/@uppy/thumbnail-generator": {
"version": "4.1.1",
"resolved": "https://registry.npmjs.org/@uppy/thumbnail-generator/-/thumbnail-generator-4.1.1.tgz",
"integrity": "sha512-65znkGNgVTbVte51IKOhgxOpHGSwYj9Qik2jF2ZBocMbhBY4gPkWFwqMrKQBfddA9KbUa4jVe1psxhAQTzYgiA==",
"dependencies": {
"@uppy/utils": "^6.1.1",
"exifr": "^7.0.0"
},
"peerDependencies": {
"@uppy/core": "^4.4.1"
}
},
"node_modules/@uppy/utils": {
"version": "6.1.2",
"resolved": "https://registry.npmjs.org/@uppy/utils/-/utils-6.1.2.tgz",
"integrity": "sha512-PCrw6v51M6p3hlrlB2INmcocen4Dyjun1SobjVZRBkg4wutQE8ihZfSrH5ZE8UXFelufhtO16wlaZMi0EHk84w==",
"dependencies": {
"lodash": "^4.17.21",
"preact": "^10.5.13"
}
},
"node_modules/@uppy/vue": {
"version": "2.1.1",
"resolved": "https://registry.npmjs.org/@uppy/vue/-/vue-2.1.1.tgz",
"integrity": "sha512-svmkqO3QObuPOyVE0ttTFcwjrjzsxiDGgqau0tuV27FNO0/50GGzrJcBOy+P7kkZhQ23qEBltScQzOc75s7uUQ==",
"dependencies": {
"shallow-equal": "^3.0.0"
},
"peerDependencies": {
"@uppy/core": "^4.4.1",
"@uppy/dashboard": "^4.3.1",
"@uppy/drag-drop": "^4.1.1",
"@uppy/file-input": "^4.1.1",
"@uppy/progress-bar": "^4.2.1",
"@uppy/status-bar": "^4.1.1",
"vue": ">=3.0.0"
},
"peerDependenciesMeta": {
"@uppy/dashboard": {
"optional": true
},
"@uppy/drag-drop": {
"optional": true
},
"@uppy/file-input": {
"optional": true
},
"@uppy/progress-bar": {
"optional": true
},
"@uppy/status-bar": {
"optional": true
}
}
},
"node_modules/@uppy/xhr-upload": {
"version": "4.3.3",
"resolved": "https://registry.npmjs.org/@uppy/xhr-upload/-/xhr-upload-4.3.3.tgz",
"integrity": "sha512-I7RVppwTvLRlVfoW5piMxcZKzWF42E6CwYFQ42d2LzizrkG4tVLQkQrTZlw85za3nhcSrX3o/d1eNx3pzLmsdw==",
"dependencies": {
"@uppy/companion-client": "^4.4.1",
"@uppy/utils": "^6.1.2"
},
"peerDependencies": {
"@uppy/core": "^4.4.2"
}
},
"node_modules/@vant/popperjs": {
"version": "1.3.0",
"resolved": "https://registry.npmjs.org/@vant/popperjs/-/popperjs-1.3.0.tgz",
......@@ -1798,6 +2026,11 @@
"node": ">= 6"
}
},
"node_modules/classnames": {
"version": "2.5.1",
"resolved": "https://registry.npmjs.org/classnames/-/classnames-2.5.1.tgz",
"integrity": "sha512-saHYOzhIQs6wy2sVxTM6bUDsQO4F50V9RQ22qBpEdCW+I+/Wmke2HOl6lS6dTpdxVhb88/I6+Hs+438c3lfUow=="
},
"node_modules/color-convert": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz",
......@@ -2103,6 +2336,16 @@
"resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-2.0.2.tgz",
"integrity": "sha512-Rfkk/Mp/DL7JVje3u18FxFujQlTNR2q6QfMSMB7AvCBx91NGj/ba3kCfza0f6dVDbw7YlRf/nDrn7pQrCCyQ/w=="
},
"node_modules/eventemitter3": {
"version": "5.0.1",
"resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-5.0.1.tgz",
"integrity": "sha512-GWkBvjiSZK87ELrYOSESUYeVIc9mvLLf/nXalMOS5dYrgZq9o5OVkbZAVM06CVxYsCwH9BDZFPlQTlPA1j4ahA=="
},
"node_modules/exifr": {
"version": "7.1.3",
"resolved": "https://registry.npmjs.org/exifr/-/exifr-7.1.3.tgz",
"integrity": "sha512-g/aje2noHivrRSLbAUtBPWFbxKdKhgj/xr1vATDdUXPOFYJlQ62Ft0oy+72V6XLIpDJfHs6gXLbBLAolqOXYRw=="
},
"node_modules/exsolve": {
"version": "1.0.4",
"resolved": "https://registry.npmjs.org/exsolve/-/exsolve-1.0.4.tgz",
......@@ -2459,6 +2702,17 @@
"node": ">=0.10.0"
}
},
"node_modules/is-network-error": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/is-network-error/-/is-network-error-1.1.0.tgz",
"integrity": "sha512-tUdRRAnhT+OtCZR/LxZelH/C7QtjtFrTu5tXCA8pl55eTUElUHT+GPYV8MBMBvea/j+NxQqVt3LbWMRir7Gx9g==",
"engines": {
"node": ">=16"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/is-number": {
"version": "7.0.0",
"resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz",
......@@ -2568,6 +2822,11 @@
"url": "https://github.com/sponsors/antfu"
}
},
"node_modules/lodash": {
"version": "4.17.21",
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
},
"node_modules/lru-cache": {
"version": "5.1.1",
"resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz",
......@@ -2604,6 +2863,11 @@
"node": ">= 0.4"
}
},
"node_modules/memoize-one": {
"version": "6.0.0",
"resolved": "https://registry.npmjs.org/memoize-one/-/memoize-one-6.0.0.tgz",
"integrity": "sha512-rkpe71W0N0c0Xz6QD0eJETuWAJGnJ9afsl1srmwPrI+yBCkge5EycXXbYRyvL29zZVUWQCY7InPRCv3GDXuZNw=="
},
"node_modules/merge2": {
"version": "1.4.1",
"resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz",
......@@ -2635,6 +2899,14 @@
"node": ">= 0.6"
}
},
"node_modules/mime-match": {
"version": "1.0.2",
"resolved": "https://registry.npmjs.org/mime-match/-/mime-match-1.0.2.tgz",
"integrity": "sha512-VXp/ugGDVh3eCLOBCiHZMYWQaTNUHv2IJrut+yXA6+JbLPXHglHwfS/5A5L0ll+jkCY7fIzRJcH6OIunF+c6Cg==",
"dependencies": {
"wildcard": "^1.1.0"
}
},
"node_modules/mime-types": {
"version": "2.1.35",
"resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz",
......@@ -2760,6 +3032,11 @@
"thenify-all": "^1.0.0"
}
},
"node_modules/namespace-emitter": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/namespace-emitter/-/namespace-emitter-2.0.1.tgz",
"integrity": "sha512-N/sMKHniSDJBjfrkbS/tpkPj4RAbvW3mr8UAzvlMHyun93XEm83IAvhWtJVHo+RHn/oO8Job5YN4b+wRjSVp5g=="
},
"node_modules/nanoid": {
"version": "3.3.11",
"resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.3.11.tgz",
......@@ -2831,6 +3108,48 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/p-queue": {
"version": "8.1.0",
"resolved": "https://registry.npmjs.org/p-queue/-/p-queue-8.1.0.tgz",
"integrity": "sha512-mxLDbbGIBEXTJL0zEx8JIylaj3xQ7Z/7eEVjcF9fJX4DBiH9oqe+oahYnlKKxm0Ci9TlWTyhSHgygxMxjIB2jw==",
"dependencies": {
"eventemitter3": "^5.0.1",
"p-timeout": "^6.1.2"
},
"engines": {
"node": ">=18"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/p-retry": {
"version": "6.2.1",
"resolved": "https://registry.npmjs.org/p-retry/-/p-retry-6.2.1.tgz",
"integrity": "sha512-hEt02O4hUct5wtwg4H4KcWgDdm+l1bOaEy/hWzd8xtXB9BqxTWBBhb+2ImAtH4Cv4rPjV76xN3Zumqk3k3AhhQ==",
"dependencies": {
"@types/retry": "0.12.2",
"is-network-error": "^1.0.0",
"retry": "^0.13.1"
},
"engines": {
"node": ">=16.17"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/p-timeout": {
"version": "6.1.4",
"resolved": "https://registry.npmjs.org/p-timeout/-/p-timeout-6.1.4.tgz",
"integrity": "sha512-MyIV3ZA/PmyBN/ud8vV9XzwTrNtR4jFrObymZYnZqMmW0zA8Z17vnT0rBgFE/TlohB+YCHqXMgZzb3Csp49vqg==",
"engines": {
"node": ">=14.16"
},
"funding": {
"url": "https://github.com/sponsors/sindresorhus"
}
},
"node_modules/package-json-from-dist": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz",
......@@ -3079,6 +3398,15 @@
"integrity": "sha512-1NNCs6uurfkVbeXG4S8JFT9t19m45ICnif8zWLd5oPSZ50QnwMfK+H3jv408d4jw/7Bttv5axS5IiHoLaVNHeQ==",
"dev": true
},
"node_modules/preact": {
"version": "10.26.4",
"resolved": "https://registry.npmjs.org/preact/-/preact-10.26.4.tgz",
"integrity": "sha512-KJhO7LBFTjP71d83trW+Ilnjbo+ySsaAgCfXOXUlmGzJ4ygYPWmysm77yg4emwfmoz3b22yvH5IsVFHbhUaH5w==",
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/preact"
}
},
"node_modules/process": {
"version": "0.11.10",
"resolved": "https://registry.npmjs.org/process/-/process-0.11.10.tgz",
......@@ -3190,6 +3518,14 @@
"url": "https://github.com/sponsors/ljharb"
}
},
"node_modules/retry": {
"version": "0.13.1",
"resolved": "https://registry.npmjs.org/retry/-/retry-0.13.1.tgz",
"integrity": "sha512-XQBQ3I8W1Cge0Seh+6gjj03LbmRFWuoszgK9ooCpwYIrhhoO80pfq4cUkU5DkknwfOfFteRwlZ56PYOGYyFWdg==",
"engines": {
"node": ">= 4"
}
},
"node_modules/reusify": {
"version": "1.1.0",
"resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz",
......@@ -3292,6 +3628,11 @@
"semver": "bin/semver.js"
}
},
"node_modules/shallow-equal": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/shallow-equal/-/shallow-equal-3.1.0.tgz",
"integrity": "sha512-pfVOw8QZIXpMbhBWvzBISicvToTiM5WBF1EeAUZDDSb5Dt29yl4AYbyywbJFSEsRUMr7gJaxqCdr4L3tQf9wVg=="
},
"node_modules/shebang-command": {
"version": "2.0.0",
"resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz",
......@@ -4107,6 +4448,11 @@
"node": ">= 8"
}
},
"node_modules/wildcard": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/wildcard/-/wildcard-1.1.2.tgz",
"integrity": "sha512-DXukZJxpHA8LuotRwL0pP1+rS6CS7FF2qStDDE1C7DDg2rLud2PXRMuEDYIPhgEezwnlHNL4c+N6MfMTjCGTng=="
},
"node_modules/wrap-ansi": {
"version": "8.1.0",
"resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz",
......
......@@ -24,6 +24,7 @@ declare module 'vue' {
SearchBar: typeof import('./components/ui/SearchBar.vue')['default']
SummerCampCard: typeof import('./components/ui/SummerCampCard.vue')['default']
TermsPopup: typeof import('./components/ui/TermsPopup.vue')['default']
UploadVideoPopup: typeof import('./components/ui/UploadVideoPopup.vue')['default']
VanButton: typeof import('vant/es')['Button']
VanCellGroup: typeof import('vant/es')['CellGroup']
VanCheckbox: typeof import('vant/es')['Checkbox']
......@@ -33,6 +34,7 @@ declare module 'vue' {
VanIcon: typeof import('vant/es')['Icon']
VanImage: typeof import('vant/es')['Image']
VanList: typeof import('vant/es')['List']
VanNavBar: typeof import('vant/es')['NavBar']
VanPickerGroup: typeof import('vant/es')['PickerGroup']
VanPopup: typeof import('vant/es')['Popup']
VanProgress: typeof import('vant/es')['Progress']
......
<template>
<van-popup
v-model:show="show"
position="bottom"
:style="{ height: '100%' }"
>
<div class="upload-video-popup">
<van-nav-bar
title="上传视频"
left-text="取消"
right-text="提交"
@click-left="onCancel"
@click-right="onSubmit"
/>
<div class="upload-content">
<van-uploader
:max-count="1"
:max-size="maxSize"
:before-read="beforeRead"
:after-read="afterRead"
accept="video/*"
@oversize="onOversize"
>
<van-button icon="plus" type="primary">上传视频</van-button>
</van-uploader>
<div v-if="uploadProgress > 0 && uploadProgress < 100" class="progress">
<van-progress :percentage="uploadProgress" :show-pivot="true" />
</div>
<div v-if="videoUrl" class="video-preview">
<VideoPlayer :video-url="videoUrl" :autoplay="false" />
</div>
</div>
</div>
</van-popup>
</template>
<script setup>
import { ref, defineProps, defineEmits, watch } from 'vue';
import { showToast } from 'vant';
import VideoPlayer from '@/components/ui/VideoPlayer.vue';
import { v4 as uuidv4 } from 'uuid';
const props = defineProps({
modelValue: {
type: Boolean,
required: true
}
});
const emit = defineEmits(['update:modelValue', 'submit', 'cancel']);
const show = ref(false);
watch(() => props.modelValue, (newVal) => {
show.value = newVal;
if (newVal) {
// 重置所有状态
videoUrl.value = '';
videoId.value = '';
videoName.value = '';
uploadProgress.value = 0;
}
});
watch(show, (newVal) => {
emit('update:modelValue', newVal);
});
const videoUrl = ref('');
const videoId = ref('');
const videoName = ref('');
const uploadProgress = ref(0);
const maxSize = 100 * 1024 * 1024; // 100MB
const beforeRead = (file) => {
if (!file.type.includes('video/')) {
showToast('请上传视频文件');
return false;
}
return true;
};
const afterRead = async (file) => {
const formData = new FormData();
formData.append('file', file.file);
try {
// 模拟上传进度
const timer = setInterval(() => {
uploadProgress.value += 10;
if (uploadProgress.value >= 100) {
clearInterval(timer);
// 模拟上传成功后的视频URL
videoUrl.value = URL.createObjectURL(file.file);
videoId.value = uuidv4();
videoName.value = file.file.name;
}
}, 300);
// TODO: 实际的上传逻辑
// const response = await uploadVideo(formData);
// videoUrl.value = response.data.url;
// videoId.value = uuidv4();
// videoName.value = file.file.name;
} catch (error) {
showToast('上传失败');
console.error('Upload error:', error);
}
};
const onOversize = () => {
showToast('文件大小不能超过100MB');
};
const onSubmit = () => {
if (!videoUrl.value || !videoId.value) {
showToast('请先上传视频');
return;
}
emit('submit', {
url: videoUrl.value,
id: videoId.value,
name: videoName.value
});
emit('update:modelValue', false);
};
const onCancel = () => {
emit('cancel');
emit('update:modelValue', false);
};
</script>
<style scoped>
.upload-video-popup {
display: flex;
flex-direction: column;
height: 100%;
}
.upload-content {
flex: 1;
padding: 16px;
overflow-y: auto;
}
.progress {
margin: 16px 0;
}
.video-preview {
margin-top: 16px;
width: 100%;
max-width: 600px;
}
</style>
......@@ -28,6 +28,11 @@ const props = defineProps({
type: String,
required: true,
},
autoplay: {
type: Boolean,
required: false,
default: true,
},
});
const emit = defineEmits(["onPlay", "onPause"]);
......@@ -39,7 +44,7 @@ const videoOptions = computed(() => ({
controls: true,
preload: "auto",
responsive: true,
autoplay: true,
autoplay: props.autoplay,
sources: [
{
src: props.videoUrl,
......@@ -68,7 +73,10 @@ const handleMounted = (payload) => {
state.value = payload.state;
player.value = payload.player;
if (player.value) {
player.value.play();
// TAG: 自动播放
if (props.autoplay) {
player.value.play();
}
if (!wxInfo().isPc) {
// 添加touchstart事件监听
player.value.on('touchstart', (event) => {
......
/*
* @Date: 2025-03-20 20:36:36
* @LastEditors: hookehuyr hookehuyr@gmail.com
* @LastEditTime: 2025-03-21 17:58:10
* @LastEditTime: 2025-03-25 09:55:59
* @FilePath: /mlaj/src/router/index.js
* @Description: 文件描述
*/
......@@ -175,6 +175,12 @@ const routes = [
component: () => import('../views/test.vue'),
meta: { title: 'test' },
},
{
path: '/upload_video',
name: 'upload_video',
component: () => import('../views/upload_video.vue'),
meta: { title: 'upload_video' },
},
...checkinRoutes,
]
......
<!--
* @Date: 2025-03-24 13:04:21
* @LastEditors: hookehuyr hookehuyr@gmail.com
* @LastEditTime: 2025-03-24 14:05:18
* @LastEditTime: 2025-03-25 09:56:14
* @FilePath: /mlaj/src/views/profile/SettingsPage.vue
* @Description: 用户设置页面
-->
......@@ -53,6 +53,17 @@
<ChevronRightIcon class="w-5 h-5 text-gray-400" />
</div>
</div>
<!-- 视频上传 -->
<div class="p-4" @click="router.push('/upload_video')">
<div class="flex items-center justify-between">
<div>
<h3 class="text-base font-medium text-gray-900">视频上传</h3>
<p class="text-sm text-gray-500">视频上传</p>
</div>
<ChevronRightIcon class="w-5 h-5 text-gray-400" />
</div>
</div>
</div>
</FrostedGlass>
</div>
......
<template>
<div class="upload-video-container">
<van-button icon="plus" type="primary" @click="showUploadPopup = true">上传视频</van-button>
<div class="video-list">
<div v-for="video in videos" :key="video.id" class="video-preview">
<VideoPlayer :video-url="video.url" :autoplay="false" />
<div class="video-info">
<span class="video-name">{{ video.name }}</span>
<van-button icon="delete" type="danger" size="small" class="delete-btn" @click="deleteVideo(video.id)">删除</van-button>
</div>
</div>
</div>
<UploadVideoPopup
v-model="showUploadPopup"
@submit="onVideoUploaded"
@cancel="showUploadPopup = false"
/>
</div>
</template>
<script setup>
import { ref } from 'vue';
import VideoPlayer from '@/components/ui/VideoPlayer.vue';
import UploadVideoPopup from '@/components/ui/UploadVideoPopup.vue';
const showUploadPopup = ref(false);
const videos = ref([]);
const onVideoUploaded = (videoInfo) => {
videos.value.push(videoInfo);
};
const deleteVideo = (id) => {
const index = videos.value.findIndex(video => video.id === id);
if (index !== -1) {
const newVideos = [...videos.value];
newVideos.splice(index, 1);
videos.value = newVideos;
}
};
</script>
<style scoped>
.upload-video-container {
padding: 16px;
}
.video-list {
margin-top: 16px;
display: grid;
grid-template-columns: repeat(auto-fill, minmax(300px, 1fr));
gap: 16px;
}
.video-preview {
position: relative;
width: 100%;
}
.delete-btn {
position: absolute;
top: 8px;
right: 8px;
z-index: 1;
}
</style>