From 7875d64f525de4705312e00fd2391e3fbc43d9b5 Mon Sep 17 00:00:00 2001
From: zjk <852185829@qq.com>
Date: 星期二, 29 九月 2026 14:06:46 +0800
Subject: [PATCH] fix(course-detail): 修复H5视频自动播放拦截与openinstall课程ID问题

---
 traditionalApp/pages/courseDetail/courseDetail.vue |   61 +++++++++++++++++++++++++-----
 1 files changed, 51 insertions(+), 10 deletions(-)

diff --git a/traditionalApp/pages/courseDetail/courseDetail.vue b/traditionalApp/pages/courseDetail/courseDetail.vue
index f1de5af..13f206f 100644
--- a/traditionalApp/pages/courseDetail/courseDetail.vue
+++ b/traditionalApp/pages/courseDetail/courseDetail.vue
@@ -20,7 +20,7 @@
 				<wd-watermark v-if="videoWatermarkText" class="video-watermark" :content="videoWatermarkText"
 					:full-screen="false" :width="160" :height="110" :gutter-x="36" :gutter-y="36" :size="15"
 					color="rgba(255, 255, 255, 0.24)" :rotate="-18" :opacity="1" :z-index="2" />
-				<view v-if="showPlaybackRetry" class="video-retry-mask" @tap.stop="retryPlayback">
+				<view v-if="showPlaybackRetry" class="video-retry-mask" @tap.stop="retryPlayback(true)">
 					<text class="video-retry-text">点击开始播放</text>
 				</view>
 			</view>
@@ -885,7 +885,8 @@
                 //         return
                 // }
 		if (canPlayVideo(item)) {
-			playSelectedVideo(item)
+			// 点击章节属于用户手势,需透传给播放逻辑,否则 H5 会被浏览器自动播放策略拦截
+			playSelectedVideo(item, true)
 			return
 		}
 		handlePaidVideoTap(item)
@@ -893,13 +894,14 @@
 
 	/**
 	 * 切换顶部播放器到指定小节,并在可自动播放的环境中立即开始播放。
+	 * @param fromUserGesture 是否由用户点击章节触发,H5 需要借助该手势放行自动播放
 	 */
-	async function playSelectedVideo(item : CourseVideo) {
+	async function playSelectedVideo(item : CourseVideo, fromUserGesture = false) {
 		if (!canPlayVideo(item)) {
 			return
 		}
 		if (activeVideoId.value === item.id && showVideoPlayer.value) {
-			retryPlayback()
+			retryPlayback(fromUserGesture)
 			return
 		}
 		activeVideoId.value = item.id
@@ -907,7 +909,7 @@
 		resetVideoPlaybackFlags()
 		await nextTick()
 		initVideoContext()
-		retryPlayback()
+		retryPlayback(fromUserGesture)
 	}
 
 	/**
@@ -960,13 +962,52 @@
 	}
 
 	/**
-	 * 对当前播放器执行播放重试,避免首次切换章节时无感失败。
+	 * H5 下取出播放器内部的原生 video 节点。
+	 * uni 的 videoContext.play() 在 H5 下拿不到 Promise,无法感知“被浏览器拦截”的情况,这里直接操作原生节点。
 	 */
-	function retryPlayback() {
-		if (isReadonlyDisplayMode) {
+	function resolveNativeVideoElement() {
+		// #ifdef H5
+		const container = document.getElementById(VIDEO_PLAYER_ID)
+		if (!container) {
+			return null
+		}
+		// id 可能挂在播放器外层节点上,也可能直接落在原生 video 节点上,两种结构都兼容
+		if (container instanceof HTMLVideoElement) {
+			return container
+		}
+		return container.querySelector('video')
+		// #endif
+		return null
+	}
+
+	/**
+	 * 对当前播放器执行播放重试,避免首次切换章节时无感失败。
+	 * @param fromUserGesture 是否由用户点击触发:H5 有自动播放限制,只有用户手势内调用 play 才可靠
+	 */
+	function retryPlayback(fromUserGesture = false) {
+		// H5 下非用户手势触发的自动播放会被浏览器拦截,这里直接跳过;点击章节或遮罩属于用户手势,需要真正执行
+		if (isReadonlyDisplayMode && !fromUserGesture) {
 			return
 		}
-		if (!videoContext || !currentVideoUrl.value) {
+		if (!currentVideoUrl.value) {
+			return
+		}
+		if (isReadonlyDisplayMode) {
+			const nativeVideo = resolveNativeVideoElement()
+			if (!nativeVideo) {
+				playbackFailed.value = true
+				return
+			}
+			const playPromise = nativeVideo.play()
+			if (playPromise) {
+				// 播放被拦截(如非用户手势)时展示“点击开始播放”遮罩兜底
+				playPromise.catch(() => {
+					playbackFailed.value = true
+				})
+			}
+			return
+		}
+		if (!videoContext) {
 			return
 		}
 		try {
@@ -1082,7 +1123,7 @@
                 }
 		const openinstallOpened = await wakeupOrInstallByOpeninstall({
 			page: 'courseDetail',
-			courseId: String(resolveCurrentCourseId() || '').trim()
+			courseId: String(resolveShareCourseId() || '').trim()
 		})
 		if (openinstallOpened) {
 			return

--
Gitblit v1.7.1