You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
468 lines
12 KiB
468 lines
12 KiB
<template>
|
|
<div class="choujiang-main">
|
|
<Lottery3D ref="lottery3DRef" />
|
|
<PrizePanel :prizes="dataManager.state.basicData.prizes" />
|
|
<ControlBar
|
|
:lottery-state="lotteryState"
|
|
:is-disabled="isDisabled"
|
|
@lottery-click="handleLotteryClick"
|
|
@reset="handleReset"
|
|
@export="handleExport"
|
|
/>
|
|
<!-- <MusicPlayer ref="musicPlayerRef" /> -->
|
|
<Mascot />
|
|
|
|
<!-- 透明弹窗 -->
|
|
<div v-if="showPrizeExhaustedModal" class="prize-exhausted-modal">
|
|
<div class="modal-content">
|
|
<p class="modal-text">该礼品已抽取完毕,请揭秘下一个礼品</p>
|
|
</div>
|
|
</div>
|
|
|
|
<div v-if="showPrizeExhaustedModal1" class="prize-exhausted-modal">
|
|
<div class="modal-content">
|
|
<p class="modal-text">请先揭秘一个礼品</p>
|
|
</div>
|
|
</div>
|
|
|
|
<div v-if="showPrizeExhaustedModal2" class="prize-exhausted-modal">
|
|
<div class="modal-content">
|
|
<p class="modal-text">该礼品已抽取完毕</p>
|
|
</div>
|
|
</div>
|
|
|
|
<!-- <UserList
|
|
:lucky-users="
|
|
dataManager.state.basicData.luckyUsers[
|
|
dataManager.state.currentPrize?.type
|
|
] || []
|
|
"
|
|
:left-users="dataManager.state.basicData.leftUsers"
|
|
/> -->
|
|
<!-- <Qipao :text="qipaoText" :show="showQipao" /> -->
|
|
</div>
|
|
<audio ref="audioRef" :src="musicSrc" loop preload="auto"></audio>
|
|
<audio ref="audioRef1" :src="musicSrc1" preload="auto"></audio>
|
|
</template>
|
|
|
|
<script setup>
|
|
import Lottery3D from "./lottery/Lottery3D.vue";
|
|
import PrizePanel from "./lottery/PrizePanel.vue";
|
|
import ControlBar from "./lottery/ControlBar.vue";
|
|
// import MusicPlayer from "./lottery/MusicPlayer.vue";
|
|
import Qipao from "./lottery/Qipao.vue";
|
|
import UserList from "./lottery/UserList.vue";
|
|
import Mascot from "./lottery/Mascot.vue";
|
|
import { ref, onMounted, nextTick, computed, watch } from "vue";
|
|
import { useDataManager } from "./lottery/dataManager.js";
|
|
import { useLotteryEngine } from "./lottery/lotteryEngine.js";
|
|
|
|
import { useLotteryStore } from "../../store/lottery"; // 路径根据实际情况调整
|
|
import { drawLottery } from "../../api/API";
|
|
|
|
const qipaoText = ref("");
|
|
const showQipao = ref(false);
|
|
const showPrizeExhaustedModal = ref(false);
|
|
const showPrizeExhaustedModal1 = ref(false);
|
|
const showPrizeExhaustedModal2 = ref(false);
|
|
|
|
// const lotteryState = ref('idle'); // idle, ready, rotating, result
|
|
|
|
// 新增
|
|
const lotteryStore = useLotteryStore();
|
|
const lotteryState = computed({
|
|
get: () => lotteryStore.lotteryState,
|
|
set: (val) => lotteryStore.setLotteryState(val),
|
|
});
|
|
|
|
const lastRevealed = computed({
|
|
get: () => lotteryStore.lastRevealedIdx,
|
|
set: (val) => lotteryStore.setLastRevealedIdx(val),
|
|
});
|
|
|
|
const waitingForNextReveal = computed({
|
|
get: () => lotteryStore.waitingForNextReveal,
|
|
set: (val) => lotteryStore.setWaitingForNextReveal(val),
|
|
});
|
|
|
|
const winnerList = computed({
|
|
get: () => lotteryStore.winnerList,
|
|
set: (val) => lotteryStore.setWinnerList(val),
|
|
});
|
|
|
|
const isDisabled = ref(false);
|
|
|
|
watch(isDisabled, (newVal, oldVal) => {
|
|
console.log("isDisabled 变化:", oldVal, "->", newVal);
|
|
});
|
|
|
|
// 数据与抽奖主流程
|
|
const dataManager = useDataManager();
|
|
let lottery3DRef = ref(null);
|
|
let musicPlayerRef = ref(null);
|
|
const lotteryEngine = useLotteryEngine(dataManager, {
|
|
resetCard: (...args) => lottery3DRef.value?.resetCard?.(...args),
|
|
addHighlight: (...args) => lottery3DRef.value?.addHighlight?.(...args),
|
|
switchScreen: (...args) => lottery3DRef.value?.switchScreen?.(...args),
|
|
rotateBallStart: (...args) => lottery3DRef.value?.rotateBallStart?.(...args),
|
|
rotateBallStop: (...args) => lottery3DRef.value?.rotateBallStop?.(...args),
|
|
selectCard: (...args) => lottery3DRef.value?.selectCard?.(...args),
|
|
});
|
|
|
|
onMounted(async () => {
|
|
isDisabled.value = true;
|
|
|
|
await dataManager.getBasicData();
|
|
|
|
await dataManager.getUsers();
|
|
setTimeout(() => {
|
|
isDisabled.value = false;
|
|
}, 3800);
|
|
|
|
// 将 dataManager 挂载到 window 对象,供子组件使用
|
|
window.dataManager = dataManager;
|
|
|
|
// 预加载音频文件以减少播放延迟
|
|
preloadAudio();
|
|
});
|
|
|
|
// 预加载音频文件
|
|
function preloadAudio() {
|
|
if (audioRef.value) {
|
|
audioRef.value.load();
|
|
// 设置音频缓冲
|
|
audioRef.value.addEventListener('canplaythrough', () => {
|
|
console.log('背景音乐预加载完成');
|
|
});
|
|
}
|
|
if (audioRef1.value) {
|
|
audioRef1.value.load();
|
|
audioRef1.value.addEventListener('canplaythrough', () => {
|
|
console.log('音效预加载完成');
|
|
});
|
|
}
|
|
}
|
|
|
|
function showLotteryQipao() {
|
|
const luckys = dataManager.state.currentLuckys;
|
|
const prize = dataManager.state.currentPrize;
|
|
if (!luckys || luckys.length === 0) return;
|
|
// 适配新的数据格式,支持 jwcode 和 username
|
|
const names = luckys
|
|
.map((item) => item.username || item[1] || item.jwcode || "")
|
|
.join("、");
|
|
qipaoText.value = `恭喜${names}获得${prize?.title || ""}!`;
|
|
showQipao.value = true;
|
|
setTimeout(() => {
|
|
showQipao.value = false;
|
|
}, 3000);
|
|
}
|
|
|
|
import musicFile from "/src/assets/worldcup.mp3";
|
|
import musicFile1 from "/src/assets/dong.mp3";
|
|
const musicSrc = musicFile;
|
|
const audioRef = ref(null);
|
|
const playing = ref(false);
|
|
const musicSrc1 = musicFile1;
|
|
const audioRef1 = ref(null);
|
|
const playing1 = ref(false);
|
|
|
|
async function toggleMusic() {
|
|
if (!audioRef.value) return;
|
|
|
|
try {
|
|
if (audioRef.value.paused) {
|
|
// 确保音频已经准备好播放
|
|
if (audioRef.value.readyState < 2) {
|
|
await new Promise((resolve) => {
|
|
audioRef.value.addEventListener('canplay', resolve, { once: true });
|
|
audioRef.value.load();
|
|
});
|
|
}
|
|
|
|
await audioRef.value.play();
|
|
playing.value = true;
|
|
} else {
|
|
audioRef.value.pause();
|
|
playing.value = false;
|
|
}
|
|
} catch (error) {
|
|
console.error('播放音乐失败:', error);
|
|
}
|
|
}
|
|
|
|
async function playMusic1() {
|
|
if (!audioRef1.value) return;
|
|
|
|
try {
|
|
// 重置音频到开始位置
|
|
audioRef1.value.currentTime = 0;
|
|
|
|
// 确保音频已经准备好播放
|
|
if (audioRef1.value.readyState < 2) {
|
|
await new Promise((resolve) => {
|
|
audioRef1.value.addEventListener('canplay', resolve, { once: true });
|
|
audioRef1.value.load();
|
|
});
|
|
}
|
|
|
|
await audioRef1.value.play();
|
|
playing1.value = true;
|
|
|
|
// 监听音频播放结束事件
|
|
audioRef1.value.addEventListener('ended', () => {
|
|
playing1.value = false;
|
|
}, { once: true });
|
|
|
|
} catch (error) {
|
|
console.error('播放音效失败:', error);
|
|
}
|
|
}
|
|
|
|
function isPlaying1() {
|
|
return playing1.value;
|
|
}
|
|
// 检查是否正在播放
|
|
function isPlaying() {
|
|
return playing.value;
|
|
}
|
|
|
|
async function handleLotteryClick() {
|
|
if (isDisabled.value) return; // 2秒内不能重复点击
|
|
isDisabled.value = true;
|
|
// setTimeout(() => {
|
|
// isDisabled.value = false;
|
|
// }, 2000);
|
|
|
|
switch (lotteryState.value) {
|
|
case "idle":
|
|
setTimeout(() => {
|
|
isDisabled.value = false;
|
|
}, 2000);
|
|
// 先切换到球体布局
|
|
await lottery3DRef.value?.switchScreen?.("lottery");
|
|
|
|
console.log("lotteryState 变更前:", lotteryState.value, "-> ready");
|
|
// await new Promise((resolve) => setTimeout(resolve, 2000));
|
|
|
|
lotteryState.value = "ready";
|
|
console.log("lotteryState 变更后:", lotteryState.value);
|
|
|
|
break;
|
|
case "ready":
|
|
if (waitingForNextReveal.value) {
|
|
console.log("waitingForNextReveal.value", waitingForNextReveal.value);
|
|
// 显示弹窗提示
|
|
showPrizeExhaustedModal.value = true;
|
|
setTimeout(() => {
|
|
showPrizeExhaustedModal.value = false;
|
|
}, 1000);
|
|
isDisabled.value = false;
|
|
|
|
break;
|
|
}
|
|
|
|
if (lastRevealed.value === -1) {
|
|
console.log("lastRevealed.value", lastRevealed.value);
|
|
showPrizeExhaustedModal1.value = true;
|
|
setTimeout(() => {
|
|
showPrizeExhaustedModal1.value = false;
|
|
}, 1000);
|
|
isDisabled.value = false;
|
|
|
|
break;
|
|
}
|
|
|
|
if (
|
|
lastRevealed.value === 0 &&
|
|
dataManager.state.basicData.prizes[lastRevealed.value].remainNum === 0
|
|
) {
|
|
// 如果是最后一个奖品且剩余数量为0,则跳出
|
|
// const currentPrize = dataManager.state.basicData.prizes[lastRevealed.value];
|
|
// if (currentPrize && currentPrize.remainNum === 0) {
|
|
showPrizeExhaustedModal2.value = true;
|
|
setTimeout(() => {
|
|
showPrizeExhaustedModal2.value = false;
|
|
}, 1000);
|
|
isDisabled.value = false;
|
|
break;
|
|
|
|
// }
|
|
}
|
|
|
|
toggleMusic();
|
|
|
|
console.log("lotteryState 变更前:", lotteryState.value, "-> rotating");
|
|
lotteryState.value = "rotating";
|
|
console.log("lotteryState 变更后:", lotteryState.value);
|
|
|
|
const prize = dataManager.state.basicData.prizes[lastRevealed.value];
|
|
console.log("准备调用 drawLottery,prize:", prize);
|
|
console.log("lastRevealed.value:", lastRevealed.value);
|
|
// 先让球转起来,不等它结束
|
|
const rotatePromise = lottery3DRef.value?.rotateBallStart?.();
|
|
|
|
// 同时请求接口
|
|
try {
|
|
winnerList.value = await drawLottery({
|
|
perWin: prize.perWin,
|
|
remainNum: prize.remainNum,
|
|
gradeId: prize.gradeId,
|
|
prizeId: prize.prizeId,
|
|
});
|
|
setTimeout(() => {
|
|
isDisabled.value = false;
|
|
}, 2000);
|
|
|
|
console.log("drawLottery 调用成功,结果:", winnerList.value);
|
|
|
|
// 开奖成功后更新获奖名单数据
|
|
// if (window.dataManager && window.dataManager.updatePrizeList) {
|
|
// try {
|
|
// await window.dataManager.updatePrizeList();
|
|
// console.log("开奖后获奖名单数据已更新");
|
|
// } catch (error) {
|
|
// console.error("更新获奖名单数据失败:", error);
|
|
// }
|
|
// }
|
|
} catch (error) {
|
|
console.error("drawLottery 调用失败:", error);
|
|
}
|
|
|
|
// 如果你还需要等球转完再做别的,可以 await rotatePromise
|
|
|
|
break;
|
|
case "rotating":
|
|
setTimeout(() => {
|
|
isDisabled.value = false;
|
|
}, 2000);
|
|
|
|
// toggleMusic();
|
|
|
|
// toggleMusic();
|
|
|
|
await lottery3DRef.value?.rotateBallStop?.();
|
|
toggleMusic();
|
|
playMusic1();
|
|
|
|
await lotteryEngine.executeLottery();
|
|
|
|
await nextTick();
|
|
showLotteryQipao();
|
|
console.log("lotteryState 变更前:", lotteryState.value, "-> idle");
|
|
lotteryState.value = "result";
|
|
|
|
console.log("lotteryState 变更后:", lotteryState.value);
|
|
|
|
break;
|
|
case "result":
|
|
setTimeout(() => {
|
|
isDisabled.value = false;
|
|
}, 2800);
|
|
// result 状态下点击不做任何事,或者你可以加提示
|
|
|
|
await lottery3DRef.value?.switchScreen?.("lottery");
|
|
|
|
await new Promise((resolve) => setTimeout(resolve, 2500));
|
|
|
|
// 去除高光
|
|
lottery3DRef.value?.changeCard1?.();
|
|
|
|
//延迟2秒
|
|
|
|
lotteryState.value = "ready";
|
|
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
|
|
function handleReset() {
|
|
lotteryEngine.resetLottery();
|
|
}
|
|
function handleExport() {
|
|
dataManager.exportData();
|
|
}
|
|
|
|
function handlePrevPrize() {
|
|
if (dataManager.state.currentPrizeIndex > 0) {
|
|
dataManager.state.currentPrizeIndex--;
|
|
dataManager.state.currentPrize =
|
|
dataManager.state.basicData.prizes[dataManager.state.currentPrizeIndex];
|
|
}
|
|
}
|
|
function handleNextPrize() {
|
|
if (
|
|
dataManager.state.currentPrizeIndex <
|
|
dataManager.state.basicData.prizes.length - 1
|
|
) {
|
|
dataManager.state.currentPrizeIndex++;
|
|
dataManager.state.currentPrize =
|
|
dataManager.state.basicData.prizes[dataManager.state.currentPrizeIndex];
|
|
}
|
|
}
|
|
</script>
|
|
|
|
<style scoped>
|
|
.choujiang-main {
|
|
width: 100vw;
|
|
height: 100vh;
|
|
position: fixed;
|
|
top: 0;
|
|
left: 0;
|
|
overflow: hidden;
|
|
/* 添加背景图片 */
|
|
background: url("../../assets/bg@2x.png") ;
|
|
background-size: 100% 100%;
|
|
}
|
|
|
|
/* 透明弹窗样式 */
|
|
.prize-exhausted-modal {
|
|
position: fixed;
|
|
top: 0;
|
|
left: 0;
|
|
width: 100%;
|
|
height: 100%;
|
|
display: flex;
|
|
justify-content: center;
|
|
align-items: flex-start;
|
|
padding-top: 20vh;
|
|
z-index: 9999;
|
|
pointer-events: none;
|
|
}
|
|
|
|
.modal-content {
|
|
background: transparent;
|
|
padding: 20px 30px;
|
|
border-radius: 10px;
|
|
animation: fadeInOut 1s ease-in-out;
|
|
}
|
|
|
|
.modal-text {
|
|
position: fixed !important;
|
|
top: 300px;
|
|
left: 50% !important;
|
|
transform: translate(-50%, -50%) !important;
|
|
color: #ff0000;
|
|
font-size: 50px;
|
|
font-weight: bold;
|
|
margin: 0;
|
|
white-space: nowrap;
|
|
}
|
|
|
|
@keyframes fadeInOut {
|
|
0% {
|
|
opacity: 0;
|
|
}
|
|
20% {
|
|
opacity: 1;
|
|
}
|
|
80% {
|
|
opacity: 1;
|
|
}
|
|
100% {
|
|
opacity: 0;
|
|
}
|
|
}
|
|
</style>
|