zjk
2026-09-29 7875d64f525de4705312e00fd2391e3fbc43d9b5
fix(course-detail): 修复H5视频自动播放拦截与openinstall课程ID问题

- 为playSelectedVideo和retryPlayback添加用户手势参数以适配H5自动播放策略
- H5场景下直接操作原生video节点,绕过uni-videoContext的限制
- 修正openinstall调用的课程ID,使用resolveShareCourseId替代原有方法
- 更新所有涉及的函数调用位置
1个文件已修改
61 ■■■■■ 已修改文件
traditionalApp/pages/courseDetail/courseDetail.vue 61 ●●●●● 补丁 | 查看 | 原始文档 | blame | 历史
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