feat(admin): 添加答题前3秒倒计时动画和音效
在用户点击"开始答题"后,增加3秒倒计时动画和音效提示,提升答题体验 - 添加倒计时遮罩层和动画效果 - 播放倒计时音效文件 - 修改开始答题按钮状态为"准备中..." - 调整环境配置中的API地址
This commit is contained in:
@ -5,8 +5,8 @@
|
||||
|
||||
# VITE_SERVICE_BASE_URL=http://api.ydzx.qyzhjy.com
|
||||
|
||||
# VITE_SERVICE_BASE_URL=http://api.ydzx.qyzhjy.com/
|
||||
VITE_SERVICE_BASE_URL=http://172.16.10.130:5000
|
||||
VITE_SERVICE_BASE_URL=http://api.ydzx.qyzhjy.com/
|
||||
# VITE_SERVICE_BASE_URL=http://172.16.10.130:5000
|
||||
|
||||
VITE_OTHER_SERVICE_BASE_URL= `{
|
||||
"demo": "http://localhost:9528"
|
||||
|
||||
BIN
apps/admin/src/assets/audio/timeout3.mp3
Normal file
BIN
apps/admin/src/assets/audio/timeout3.mp3
Normal file
Binary file not shown.
@ -2,6 +2,7 @@
|
||||
<script setup lang="ts">
|
||||
import { storeToRefs } from 'pinia'
|
||||
import { computed, onMounted, onUnmounted, ref, watch } from 'vue'
|
||||
import start3Audio from '@/assets/audio/timeout3.mp3'
|
||||
import CompetitionLayout from '@/components/custom/user/CompetitionLayout.vue'
|
||||
import { useRouterPush } from '@/hooks/common/router'
|
||||
import { fetchGetGameStatistics, fetchGetQuestionDetail, fetchSubmitQuestionResult } from '@/service/api/game'
|
||||
@ -26,6 +27,9 @@ const loading = ref(false)
|
||||
|
||||
// 本地倒计时控制(为了在页面上显示“开始答题”还是倒计时)
|
||||
const isStarted = ref(false)
|
||||
const isPreparing = ref(false) // 是否正在准备中(321倒计时)
|
||||
const showCountdown = ref(false) // 控制倒计时弹窗显示
|
||||
const countdownNum = ref(3) // 倒计时数字
|
||||
const pollingTimer = ref<number | null>(null) // 轮询定时器
|
||||
|
||||
console.log(activityInfo.value, 'activityInfo.value')
|
||||
@ -130,19 +134,10 @@ async function fetchGetQuestionDetailData() {
|
||||
|
||||
async function handleNext() {
|
||||
// 点击开始答题
|
||||
if (!isStarted.value) {
|
||||
isStarted.value = true
|
||||
if (!isStarted.value && !isPreparing.value) {
|
||||
isPreparing.value = true
|
||||
|
||||
// 获取题目时间,优先从 currentQuestionInfo 获取,兼容大小写
|
||||
// 并在获取失败时尝试从 detail 获取(如果后端在详情接口也返回了时间)
|
||||
const infoTime = store.currentQuestionInfo?.QuestionTime || (store.currentQuestionInfo as any)?.questionTime
|
||||
const detailTime = (store.currentQuestionDetail as any)?.QuestionTime || (store.currentQuestionDetail as any)?.questionTime
|
||||
const duration = Number(infoTime || detailTime || 120)
|
||||
|
||||
console.log('开始倒计时,时长:', duration, 'InfoTime:', infoTime, 'DetailTime:', detailTime)
|
||||
store.startTimer(duration)
|
||||
|
||||
// 提交题目使用记录
|
||||
// 提交题目使用记录 (调用开始接口)
|
||||
try {
|
||||
if (store.currentQuestionInfo && store.currentQuestionDetail) {
|
||||
const params: Api.Competition.QuestionAddParams = {
|
||||
@ -153,19 +148,45 @@ async function handleNext() {
|
||||
QuestionDetaiID: store.currentQuestionDetail.Id || 0, // 详情 ID
|
||||
TeamGroupQuestionID: store.currentQuestionMainInfo?.TeamGroup_QuestionID || 0, // 题目在分组中的 ID
|
||||
}
|
||||
console.log(params, 'params')
|
||||
await fetchSubmitQuestionResult(params)
|
||||
await fetchSubmitQuestionResult(params) // 提交题目使用记录
|
||||
|
||||
// 调用接口成功后,显示 321 倒计时弹窗并播放音乐
|
||||
showCountdown.value = true
|
||||
countdownNum.value = 3
|
||||
|
||||
const audio = new Audio(start3Audio)
|
||||
audio.play().catch(e => console.error('Play audio failed', e))
|
||||
|
||||
const timer = setInterval(() => {
|
||||
countdownNum.value--
|
||||
if (countdownNum.value === 0) {
|
||||
clearInterval(timer)
|
||||
showCountdown.value = false
|
||||
isPreparing.value = false
|
||||
isStarted.value = true
|
||||
|
||||
// 并在获取失败时尝试从 detail 获取(如果后端在详情接口也返回了时间)
|
||||
const infoTime = store.currentQuestionInfo?.QuestionTime || (store.currentQuestionInfo as any)?.questionTime
|
||||
const detailTime = (store.currentQuestionDetail as any)?.QuestionTime || (store.currentQuestionDetail as any)?.questionTime
|
||||
const duration = Number(infoTime || detailTime || 120)
|
||||
|
||||
console.log('开始倒计时,时长:', duration, 'InfoTime:', infoTime, 'DetailTime:', detailTime)
|
||||
store.startTimer(duration)
|
||||
|
||||
// 立即拉取一次,然后开启轮询
|
||||
await fetchGetGameStatisticsData(params)
|
||||
fetchGetGameStatisticsData(params)
|
||||
startPolling(params)
|
||||
}
|
||||
}, 1000)
|
||||
}
|
||||
}
|
||||
catch (error) {
|
||||
console.error('Failed to submit question result', error)
|
||||
isPreparing.value = false
|
||||
// 不阻塞流程,仅记录错误
|
||||
}
|
||||
}
|
||||
else {
|
||||
else if (isStarted.value) {
|
||||
// 备用逻辑:如果已经在答题中(理论上不会触发,因为上面已经跳转),直接跳转
|
||||
store.stopTimer()
|
||||
stopPolling()
|
||||
@ -255,8 +276,8 @@ const titleBgTypeNum = computed(() => RoundType.value === 0 ? 3 : 4) || 3
|
||||
<CompetitionLayout
|
||||
action-position="top" :show-back="false" :show-next="true" :title="mainTitle || ''"
|
||||
:title-bg-type="titleBgTypeNum" title-text-color="#ffffff" back-btn-text="返回"
|
||||
:next-btn-text="isStarted ? formattedTime : '开始答题'" :next-disabled="isStarted" btn-theme="light" @back="handleBack"
|
||||
@next="handleNext"
|
||||
:next-btn-text="(isStarted || isPreparing) ? (isPreparing ? '准备中...' : formattedTime) : '开始答题'"
|
||||
:next-disabled="isStarted || isPreparing" btn-theme="light" @back="handleBack" @next="handleNext"
|
||||
>
|
||||
<div class="relative h-full w-full flex flex-col items-center overflow-hidden px-12 font-sans">
|
||||
<!-- 题目说明 -->
|
||||
@ -270,6 +291,18 @@ const titleBgTypeNum = computed(() => RoundType.value === 0 ? 3 : 4) || 3
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 321 倒计时遮罩层 -->
|
||||
<div
|
||||
v-if="showCountdown"
|
||||
class="fixed inset-0 z-[9999] flex items-center justify-center bg-black/60 backdrop-blur-sm"
|
||||
>
|
||||
<Transition name="scale" mode="out-in">
|
||||
<div :key="countdownNum" class="text-[300px] color-#dd0315 font-bold">
|
||||
{{ countdownNum }}
|
||||
</div>
|
||||
</Transition>
|
||||
</div>
|
||||
|
||||
<!-- 隐藏的 Next 按钮 (方便演示:右上角小区域) -->
|
||||
<div class="absolute right-0 top-0 z-50 h-20 w-20 cursor-pointer" title="Next Step (Debug)" @click="handleNext">
|
||||
<div class="h-full w-full flex items-center justify-center">
|
||||
@ -285,7 +318,10 @@ const titleBgTypeNum = computed(() => RoundType.value === 0 ? 3 : 4) || 3
|
||||
>
|
||||
<!-- 图表标题栏 -->
|
||||
<div v-if="isStarted" class="z-index-999 relative mb-4">
|
||||
<img src="@/assets/imgs/user/rank-bg-kuang.png" alt="答题进度" class="z-index-999 absolute left--30px top--25px h-auto max-h-16 w-auto">
|
||||
<img
|
||||
src="@/assets/imgs/user/rank-bg-kuang.png" alt="答题进度"
|
||||
class="z-index-999 absolute left--30px top--25px h-auto max-h-16 w-auto"
|
||||
>
|
||||
</div>
|
||||
|
||||
<!-- 柱状图(ECharts) -->
|
||||
@ -303,4 +339,16 @@ const titleBgTypeNum = computed(() => RoundType.value === 0 ? 3 : 4) || 3
|
||||
/* red-500 */
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
/* 321 倒计时动画 */
|
||||
.scale-enter-active,
|
||||
.scale-leave-active {
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
.scale-enter-from,
|
||||
.scale-leave-to {
|
||||
opacity: 0;
|
||||
transform: scale(0.5);
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user