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.

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