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.

282 lines
7.6 KiB

1 month ago
1 month ago
1 month ago
1 month ago
1 month ago
  1. <template>
  2. <div class="choujiang-main">
  3. <Lottery3D ref="lottery3DRef" />
  4. <PrizePanel :prizes="dataManager.state.basicData.prizes" />
  5. <ControlBar
  6. :lottery-state="lotteryState"
  7. :is-disabled="isDisabled"
  8. @lottery-click="handleLotteryClick"
  9. @reset="handleReset"
  10. @export="handleExport"
  11. />
  12. <MusicPlayer ref="musicPlayerRef" />
  13. <Mascot />
  14. <!-- 透明弹窗 -->
  15. <div v-if="showPrizeExhaustedModal" class="prize-exhausted-modal">
  16. <div class="modal-content">
  17. <p class="modal-text">该礼品已抽取完毕请揭秘下一个礼品</p>
  18. </div>
  19. </div>
  20. <!-- <UserList
  21. :lucky-users="
  22. dataManager.state.basicData.luckyUsers[
  23. dataManager.state.currentPrize?.type
  24. ] || []
  25. "
  26. :left-users="dataManager.state.basicData.leftUsers"
  27. /> -->
  28. <!-- <Qipao :text="qipaoText" :show="showQipao" /> -->
  29. </div>
  30. </template>
  31. <script setup>
  32. import Lottery3D from "./lottery/Lottery3D.vue";
  33. import PrizePanel from "./lottery/PrizePanel.vue";
  34. import ControlBar from "./lottery/ControlBar.vue";
  35. import MusicPlayer from "./lottery/MusicPlayer.vue";
  36. import Qipao from "./lottery/Qipao.vue";
  37. import UserList from "./lottery/UserList.vue";
  38. import Mascot from "./lottery/Mascot.vue";
  39. import { ref, onMounted, nextTick, computed, watch } from "vue";
  40. import { useDataManager } from "./lottery/dataManager.js";
  41. import { useLotteryEngine } from "./lottery/lotteryEngine.js";
  42. import { useLotteryStore } from "../../store/lottery"; // 路径根据实际情况调整
  43. const qipaoText = ref("");
  44. const showQipao = ref(false);
  45. const showPrizeExhaustedModal = ref(false);
  46. // const lotteryState = ref('idle'); // idle, ready, rotating, result
  47. // 新增
  48. const lotteryStore = useLotteryStore();
  49. const lotteryState = computed({
  50. get: () => lotteryStore.lotteryState,
  51. set: (val) => lotteryStore.setLotteryState(val),
  52. });
  53. const lastRevealed = computed({
  54. get: () => lotteryStore.lastRevealedIdx,
  55. set: (val) => lotteryStore.setLastRevealedIdx(val),
  56. });
  57. const waitingForNextReveal = computed({
  58. get: () => lotteryStore.waitingForNextReveal,
  59. set: (val) => lotteryStore.setWaitingForNextReveal(val),
  60. });
  61. const isDisabled = ref(false);
  62. watch(isDisabled, (newVal, oldVal) => {
  63. console.log("isDisabled 变化:", oldVal, "->", newVal);
  64. });
  65. // 数据与抽奖主流程
  66. const dataManager = useDataManager();
  67. let lottery3DRef = ref(null);
  68. let musicPlayerRef = ref(null);
  69. const lotteryEngine = useLotteryEngine(dataManager, {
  70. resetCard: (...args) => lottery3DRef.value?.resetCard?.(...args),
  71. addHighlight: (...args) => lottery3DRef.value?.addHighlight?.(...args),
  72. switchScreen: (...args) => lottery3DRef.value?.switchScreen?.(...args),
  73. rotateBallStart: (...args) => lottery3DRef.value?.rotateBallStart?.(...args),
  74. rotateBallStop: (...args) => lottery3DRef.value?.rotateBallStop?.(...args),
  75. selectCard: (...args) => lottery3DRef.value?.selectCard?.(...args),
  76. });
  77. onMounted(async () => {
  78. await dataManager.getBasicData();
  79. await dataManager.getUsers();
  80. // 将 dataManager 挂载到 window 对象,供子组件使用
  81. window.dataManager = dataManager;
  82. // 延迟一点时间确保音乐播放器组件已经加载完成
  83. setTimeout(() => {
  84. if (musicPlayerRef.value && !musicPlayerRef.value.isPlaying()) {
  85. // 触发音乐播放
  86. musicPlayerRef.value.toggleMusic();
  87. }
  88. }, 1000);
  89. });
  90. function showLotteryQipao() {
  91. const luckys = dataManager.state.currentLuckys;
  92. const prize = dataManager.state.currentPrize;
  93. if (!luckys || luckys.length === 0) return;
  94. // 适配新的数据格式,支持 jwcode 和 username
  95. const names = luckys
  96. .map((item) => item.username || item[1] || item.jwcode || "")
  97. .join("、");
  98. qipaoText.value = `恭喜${names}获得${prize?.title || ""}`;
  99. showQipao.value = true;
  100. setTimeout(() => {
  101. showQipao.value = false;
  102. }, 3000);
  103. }
  104. async function handleLotteryClick() {
  105. if (isDisabled.value) return; // 2秒内不能重复点击
  106. isDisabled.value = true;
  107. setTimeout(() => {
  108. isDisabled.value = false;
  109. }, 2000);
  110. switch (lotteryState.value) {
  111. case "idle":
  112. // 先切换到球体布局
  113. await lottery3DRef.value?.switchScreen?.("lottery");
  114. console.log("lotteryState 变更前:", lotteryState.value, "-> ready");
  115. // await new Promise((resolve) => setTimeout(resolve, 2000));
  116. lotteryState.value = "ready";
  117. console.log("lotteryState 变更后:", lotteryState.value);
  118. break;
  119. case "ready":
  120. if (waitingForNextReveal.value) {
  121. console.log("waitingForNextReveal.value", waitingForNextReveal.value);
  122. // 显示弹窗提示
  123. showPrizeExhaustedModal.value = true;
  124. setTimeout(() => {
  125. showPrizeExhaustedModal.value = false;
  126. }, 1000);
  127. break;
  128. }
  129. if (lastRevealed.value === -1) {
  130. console.log("lastRevealed.value", lastRevealed.value);
  131. break;
  132. }
  133. // 立即切换为“停止抽奖”
  134. console.log("lotteryState 变更前:", lotteryState.value, "-> rotating");
  135. lotteryState.value = "rotating";
  136. console.log("lotteryState 变更后:", lotteryState.value);
  137. // 开始转动
  138. // isRunning.value = false; // 无论如何都恢复
  139. await lottery3DRef.value?.rotateBallStart?.();
  140. break;
  141. case "rotating":
  142. // 停止转动并开奖
  143. await lottery3DRef.value?.rotateBallStop?.();
  144. await lotteryEngine.executeLottery();
  145. await nextTick();
  146. showLotteryQipao();
  147. console.log("lotteryState 变更前:", lotteryState.value, "-> idle");
  148. lotteryState.value = "result";
  149. console.log("lotteryState 变更后:", lotteryState.value);
  150. break;
  151. case "result":
  152. // result 状态下点击不做任何事,或者你可以加提示
  153. await lottery3DRef.value?.switchScreen?.("lottery");
  154. await new Promise((resolve) => setTimeout(resolve, 2500));
  155. // 去除高光
  156. lottery3DRef.value?.changeCard1?.();
  157. //延迟2秒
  158. lotteryState.value = "ready";
  159. break;
  160. default:
  161. break;
  162. }
  163. }
  164. function handleReset() {
  165. lotteryEngine.resetLottery();
  166. }
  167. function handleExport() {
  168. dataManager.exportData();
  169. }
  170. function handlePrevPrize() {
  171. if (dataManager.state.currentPrizeIndex > 0) {
  172. dataManager.state.currentPrizeIndex--;
  173. dataManager.state.currentPrize =
  174. dataManager.state.basicData.prizes[dataManager.state.currentPrizeIndex];
  175. }
  176. }
  177. function handleNextPrize() {
  178. if (
  179. dataManager.state.currentPrizeIndex <
  180. dataManager.state.basicData.prizes.length - 1
  181. ) {
  182. dataManager.state.currentPrizeIndex++;
  183. dataManager.state.currentPrize =
  184. dataManager.state.basicData.prizes[dataManager.state.currentPrizeIndex];
  185. }
  186. }
  187. </script>
  188. <style scoped>
  189. .choujiang-main {
  190. width: 100vw;
  191. height: 100vh;
  192. position: fixed;
  193. top: 0;
  194. left: 0;
  195. overflow: hidden;
  196. /* 添加背景图片 */
  197. background: url("../../assets/bg@2x.png");
  198. background-size: 1920px 980px;
  199. }
  200. /* 透明弹窗样式 */
  201. .prize-exhausted-modal {
  202. position: fixed;
  203. top: 0;
  204. left: 0;
  205. width: 100%;
  206. height: 100%;
  207. display: flex;
  208. justify-content: center;
  209. align-items: flex-start;
  210. padding-top: 20vh;
  211. z-index: 9999;
  212. pointer-events: none;
  213. }
  214. .modal-content {
  215. background: transparent;
  216. padding: 20px 30px;
  217. border-radius: 10px;
  218. animation: fadeInOut 1s ease-in-out;
  219. }
  220. .modal-text {
  221. color: #ff0000;
  222. font-size: 18px;
  223. font-weight: bold;
  224. text-align: center;
  225. margin: 0;
  226. white-space: nowrap;
  227. }
  228. @keyframes fadeInOut {
  229. 0% {
  230. opacity: 0;
  231. transform: translateY(-20px);
  232. }
  233. 20% {
  234. opacity: 1;
  235. transform: translateY(0);
  236. }
  237. 80% {
  238. opacity: 1;
  239. transform: translateY(0);
  240. }
  241. 100% {
  242. opacity: 0;
  243. transform: translateY(-20px);
  244. }
  245. }
  246. </style>