“mitao”
2026-04-20 ed527bae283588de0a9848390348e93c3820fef9
traditionalApp/pages/watchLive/watchLive.vue
@@ -90,10 +90,11 @@
               <!-- 底部按钮区域 -->
               <view class="submit-action-area">
                  <view class="btn cancel-btn" @tap="closeAnswer">稍后可答题</view>
                  <view class="btn confirm-btn" :class="{ disabled: quizSubmitting }" @tap="submitAnswer">
                     {{ quizSubmitting ? '提交中...' : '提交' }}
                  <view class="btn confirm-btn" :class="{ disabled: submitButtonDisabled }" @tap="submitAnswer">
                     {{ submitButtonText }}
                  </view>
               </view>
               <view class="submit-hint" v-if="submitHintText">{{ submitHintText }}</view>
            </template>
            <!-- 答题完成结果区域 -->
@@ -240,6 +241,7 @@
const loadFailed = ref(false)
const quizLoading = ref(false)
const quizSubmitting = ref(false)
const quizNowTimestamp = ref(Date.now())
const liveDetail = ref<LiveDetailResponse>(createEmptyLiveDetail())
const quizState = ref<QuizStatusResponse>(createEmptyQuizState())
const questions = ref<QuizQuestionItem[]>([])
@@ -254,6 +256,7 @@
const instance = getCurrentInstance()
let videoContext: ReturnType<typeof uni.createVideoContext> | null = null
let playbackStartupTimer: ReturnType<typeof setTimeout> | null = null
let quizAvailabilityTimer: ReturnType<typeof setInterval> | null = null
/**
 * 生成观看页默认直播详情,避免模板访问空对象。
@@ -404,6 +407,13 @@
})
const watchCountText = computed(() => formatCount(liveDetail.value.watchCount))
const rewardPointsText = computed(() => String(quizState.value.pointsReward || liveDetail.value.quizPoints || 0))
const hasQuizQuestions = computed(() => questions.value.length > 0)
const isQuizTimeReached = computed(() => {
   if (quizState.value.quizEnabled) {
      return true
   }
   return isQuizAvailableByTime(quizState.value.quizAvailableAt)
})
const answerButtonText = computed(() => {
   if (quizState.value.quizPointsAwarded) {
      return '已得分'
@@ -414,6 +424,14 @@
   return '答题'
})
const answerButtonDisabled = computed(() => !activeSessionId.value || quizSubmitting.value)
const isSubmitEnabled = computed(() => {
   if (!activeSessionId.value || quizState.value.quizSubmitted) {
      return false
   }
   return hasQuizQuestions.value && isQuizTimeReached.value
})
const submitButtonDisabled = computed(() => !isSubmitEnabled.value || quizSubmitting.value)
const submitButtonText = computed(() => (quizSubmitting.value ? '提交中...' : '提交'))
const answerViewState = computed<QuizViewState>(() => {
   if (quizState.value.quizSubmitted) {
      if (quizState.value.quizPointsAwarded) {
@@ -425,7 +443,7 @@
      }
      return 'wrong'
   }
   if (quizState.value.quizEnabled) {
   if (hasQuizQuestions.value) {
      return 'answering'
   }
   return 'unopened'
@@ -444,6 +462,18 @@
      return `开放时间:${formatDateLabel(quizState.value.quizAvailableAt)}`
   }
   return ''
})
const submitHintText = computed(() => {
   if (quizState.value.quizSubmitted || !hasQuizQuestions.value) {
      return ''
   }
   if (isQuizTimeReached.value) {
      return ''
   }
   if (quizState.value.quizAvailableAt) {
      return `答题开放时间:${formatDateLabel(quizState.value.quizAvailableAt)}`
   }
   return '答题暂未开放'
})
const videoPlaceholderText = computed(() => {
   if (loading.value) {
@@ -481,6 +511,7 @@
 */
onHide(() => {
   clearPlaybackStartupTimer()
   clearQuizAvailabilityTimer()
   submitCurrentWatchSegment()
})
@@ -489,6 +520,7 @@
 */
onUnload(() => {
   clearPlaybackStartupTimer()
   clearQuizAvailabilityTimer()
   submitCurrentWatchSegment()
})
@@ -571,7 +603,9 @@
            ? response.data.pointsReward
            : Number(liveDetail.value.quizPoints || 0),
      }
      quizNowTimestamp.value = Date.now()
      questions.value = createQuizQuestions(response.data.questions)
      startQuizAvailabilityTimer()
      return true
   } finally {
      quizLoading.value = false
@@ -612,10 +646,38 @@
 * 重置答题状态,避免切换场次时沿用上一场直播数据。
 */
function resetQuizState() {
   clearQuizAvailabilityTimer()
   quizLoading.value = false
   quizSubmitting.value = false
   quizNowTimestamp.value = Date.now()
   quizState.value = createEmptyQuizState()
   questions.value = []
}
/**
 * 清理答题开放时间轮询,避免页面切换后重复计时。
 */
function clearQuizAvailabilityTimer() {
   if (quizAvailabilityTimer) {
      clearInterval(quizAvailabilityTimer)
      quizAvailabilityTimer = null
   }
}
/**
 * 在答题开放前启动本地时间轮询,到点后自动启用提交按钮。
 */
function startQuizAvailabilityTimer() {
   clearQuizAvailabilityTimer()
   if (!quizState.value.quizAvailableAt || quizState.value.quizSubmitted || isQuizTimeReached.value) {
      return
   }
   quizAvailabilityTimer = setInterval(() => {
      quizNowTimestamp.value = Date.now()
      if (isQuizTimeReached.value) {
         clearQuizAvailabilityTimer()
      }
   }, 1000)
}
/**
@@ -628,6 +690,20 @@
   const normalizedValue = value.includes('T') ? value : value.replace(' ', 'T')
   const parsedTime = new Date(normalizedValue).getTime()
   return Number.isNaN(parsedTime) ? 0 : parsedTime
}
/**
 * 根据本地当前时间判断是否已达到答题开放时间。
 */
function isQuizAvailableByTime(value?: string): boolean {
   if (!value) {
      return false
   }
   const availableTimestamp = parsePlaybackStartTime(value)
   if (!availableTimestamp) {
      return false
   }
   return quizNowTimestamp.value >= availableTimestamp
}
/**
@@ -971,7 +1047,7 @@
 * 记录当前题目的选项结果。
 */
function selectOption(qIndex: number, oIndex: number) {
   if (answerViewState.value !== 'answering') {
   if (quizState.value.quizSubmitted || !hasQuizQuestions.value) {
      return
   }
   const question = questions.value[qIndex]
@@ -986,7 +1062,7 @@
 * 提交观看页答题结果,并根据返回结果切换答题状态展示。
 */
async function submitAnswer() {
   if (answerViewState.value !== 'answering' || quizSubmitting.value) {
   if (!isSubmitEnabled.value || quizSubmitting.value) {
      return
   }
   const hasUnanswered = questions.value.some((item) => !item.selectedOption)
@@ -1009,6 +1085,7 @@
      if (response?.code !== 0 || !response.data) {
         return
      }
      clearQuizAvailabilityTimer()
      applyQuizSubmitResult(response.data)
   } finally {
      quizSubmitting.value = false
@@ -1325,7 +1402,7 @@
   .submit-action-area {
      padding: 24rpx 32rpx;
      padding-bottom: 68rpx;
      padding-bottom: 24rpx;
      display: flex;
      justify-content: space-between;
@@ -1355,6 +1432,14 @@
      }
   }
   .submit-hint {
      padding: 0 40rpx 68rpx;
      font-size: 24rpx;
      line-height: 36rpx;
      color: #999999;
      text-align: right;
   }
   .result-container {
      padding: 40rpx;
      display: flex;