PairUp.vue 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465
  1. <template>
  2. <div class="game-view">
  3. <img
  4. class="title"
  5. src="@/assets/images/pair-up-title.png"
  6. alt=""
  7. draggable="false"
  8. >
  9. <div
  10. v-show="!isOver"
  11. class="tip"
  12. >
  13. 请翻开相同的企业logo
  14. </div>
  15. <!-- 左上角按钮 -->
  16. <div class="btn-group">
  17. <button
  18. class="return-home"
  19. @click="onClickReturnHome"
  20. />
  21. <button class="game-rule" />
  22. </div>
  23. <!-- 卡牌列表 -->
  24. <div
  25. v-show="!isOver"
  26. class="card-list"
  27. >
  28. <div
  29. v-for="(card, cardIdx) in cardList"
  30. :key="cardIdx"
  31. class="card"
  32. :class="{
  33. active: activeCardIdxList.includes(cardIdx),
  34. hide: card.isDone,
  35. }"
  36. @click="onClickCard(card, cardIdx)"
  37. >
  38. <div class="front">
  39. <img
  40. class="card-frame"
  41. src="@/assets/images/pair-up-card-front-side.png"
  42. alt=""
  43. draggable="false"
  44. >
  45. <img
  46. class="card-content"
  47. :src="require(`@/assets/images/pair-up-logos/${logoFileNameList[card.logoIdx]}`)"
  48. alt=""
  49. draggable="false"
  50. >
  51. </div>
  52. <img
  53. class="back"
  54. src="@/assets/images/pair-up-card-back-side.png"
  55. alt=""
  56. draggable="false"
  57. >
  58. </div>
  59. </div>
  60. <!-- 底部信息栏 -->
  61. <div
  62. v-show="!isOver"
  63. class="common-info-group"
  64. >
  65. <div class="info-item bonus-point">
  66. <img
  67. class="icon"
  68. src="@/assets/images/icon_bonus_point.png"
  69. alt=""
  70. draggable="false"
  71. >
  72. <span class="number">{{ bonusPointForShow }}</span>
  73. </div>
  74. <div class="info-item time-count">
  75. <img
  76. class="icon"
  77. src="@/assets/images/icon_time_count.png"
  78. alt=""
  79. draggable="false"
  80. >
  81. <span class="number">{{ timeCountForShow }}</span>
  82. </div>
  83. </div>
  84. <PairUpOver
  85. v-show="isOver"
  86. :corp-count="recognizedCorpList.length"
  87. :bonus-count="bonusPoint"
  88. @replay="replay"
  89. />
  90. </div>
  91. </template>
  92. <script setup>
  93. import useSizeAdapt from "@/useFunctions/useSizeAdapt"
  94. import { ref, computed, watch, onMounted, onBeforeUnmount, nextTick } from "vue"
  95. import { useRoute, useRouter } from "vue-router"
  96. import { useStore } from "vuex"
  97. import dayjs from 'dayjs'
  98. import { shuffle } from 'lodash'
  99. import PairUpOver from '@/components/PairUpOver.vue'
  100. import { addScore, getScore } from '@/api.js'
  101. const route = useRoute()
  102. const router = useRouter()
  103. const store = useStore()
  104. const {
  105. windowSizeInCssForRef,
  106. windowSizeWhenDesignForRef,
  107. } = useSizeAdapt(390, 752)
  108. function onClickReturnHome() {
  109. router.push({
  110. name: 'HomeView',
  111. })
  112. }
  113. const isOver = ref(false)
  114. /**
  115. * 倒计时
  116. */
  117. const timeCount = ref(store.state.gameRuleList[2].second)
  118. let timeCountIntervalId = null
  119. timeCountIntervalId = setInterval(() => {
  120. timeCount.value--
  121. if (timeCount.value === 0) {
  122. clearInterval(timeCountIntervalId)
  123. isOver.value = true
  124. }
  125. }, 1000)
  126. onBeforeUnmount(() => {
  127. clearInterval(timeCountIntervalId)
  128. })
  129. const timeCountForShow = computed(() => {
  130. return dayjs(timeCount.value * 1000).format('m:ss')
  131. })
  132. /**
  133. * 本次游戏积分
  134. */
  135. const bonusPoint = ref(0)
  136. const bonusPointForShow = computed(() => {
  137. if (!store.state.ifScoreLimitReached) {
  138. return bonusPoint.value
  139. } else {
  140. return `已达本日上限${store.state.scoreLimit}分`
  141. }
  142. })
  143. /**
  144. * 本次游戏识别企业数
  145. */
  146. const recognizedCorpList = ref([])
  147. /**
  148. * 重玩
  149. */
  150. function replay() {
  151. isOver.value = false
  152. bonusPoint.value = 0
  153. timeCount.value = store.state.gameRuleList[2].second
  154. timeCountIntervalId = setInterval(() => {
  155. timeCount.value--
  156. if (timeCount.value === 0) {
  157. clearInterval(timeCountIntervalId)
  158. isOver.value = true
  159. }
  160. }, 1000)
  161. recognizedCorpList.value = 0
  162. }
  163. watch(isOver, (vNew) => {
  164. if (vNew) {
  165. if (store.state.loginStatus && !store.state.ifScoreLimitReached) {
  166. addScore(bonusPoint.value).then(() => {
  167. getScore().then((res) => {
  168. store.commit('setScore', res.total)
  169. store.commit('setIfScoreLimitReached', res.hasOver)
  170. })
  171. })
  172. }
  173. }
  174. })
  175. /**
  176. * 具体游戏规则
  177. */
  178. const logoFileNameList = [
  179. '北京中金公益基金会.png',
  180. '创金合信基金管理有限公司.png',
  181. '德邦基金管理有限公司.png',
  182. '东方财富证券股份有限公司.png',
  183. '东海证券股份有限公司.png',
  184. '富国基金管理有限公司.png',
  185. '广发证券股份有限公司.png',
  186. '国盛证券有限责任公司.png',
  187. '国元证券股份有限公司.png',
  188. '华安基金管理有限公司.png',
  189. '华泰证券股份有限公司.png',
  190. '汇添富基金管理股份有限公司.png',
  191. '金信期货有限公司.png',
  192. '南华期货股份有限公司.png',
  193. '鹏华基金管理有限公司.png',
  194. '鹏扬基金管理有限公司.png',
  195. '平安证券股份有限公司.png',
  196. '睿远公益基金会.png',
  197. '上海东方证券资产管理有限公司.jpg',
  198. '上海证券交易所公益基金会.png',
  199. '深圳市银华公益基金会.png',
  200. '泰达宏利基金.png',
  201. '万家基金管理有限公司.png',
  202. '兴业证券股份有限公司.png',
  203. '兴证全球基金管理有限公司.png',
  204. '圆信永丰基金管理有限公司.png',
  205. '长江证券股份有限公司.png',
  206. '郑州商品交易所.png',
  207. '中国金融期货交易所.png',
  208. '中国期货市场监控中心有限责任公司.png',
  209. '中国证券登记结算有限责任公司.png',
  210. '中海基金管理有限公司.png',
  211. '中航证券有限公司.png',
  212. '中欧基金管理有限公司.png',
  213. '中信期货有限公司.png',
  214. '中信证券股份有限公司.png',
  215. '中证数据有限责任公司.png',
  216. '朱雀基金管理有限公司.png',
  217. ]
  218. const cardList = ref([])
  219. function setCardList() {
  220. cardList.value = []
  221. nextTick(() => {
  222. for (let index = 0; index < 8; index++) {
  223. const logoIdx = Math.floor(Math.random() * logoFileNameList.length)
  224. cardList.value.push({
  225. logoIdx,
  226. isDone: false,
  227. })
  228. cardList.value.push({
  229. logoIdx,
  230. isDone: false,
  231. })
  232. }
  233. if (process.env.VUE_APP_CLI_MODE !== 'dev') {
  234. cardList.value = shuffle(cardList.value)
  235. }
  236. })
  237. }
  238. setCardList()
  239. const activeCardIdxList = ref([])
  240. const activeCardIdxListRealTime = ref([])
  241. function onClickCard(card, cardIdx) {
  242. if (activeCardIdxList.value.includes(cardIdx)) {
  243. return
  244. }
  245. activeCardIdxList.value.push(cardIdx)
  246. activeCardIdxListRealTime.value.push(cardIdx)
  247. if (activeCardIdxListRealTime.value.length === 2 && cardList.value[activeCardIdxListRealTime.value[0]].logoIdx === cardList.value[activeCardIdxListRealTime.value[1]].logoIdx) {
  248. const logoIdx = cardList.value[activeCardIdxListRealTime.value[0]].logoIdx
  249. const toDeleteValue1 = activeCardIdxListRealTime.value.shift()
  250. const toDeleteValue2 = activeCardIdxListRealTime.value.shift()
  251. setTimeout(() => {
  252. cardList.value[toDeleteValue1].isDone = true
  253. cardList.value[toDeleteValue2].isDone = true
  254. if (!recognizedCorpList.value.includes(logoIdx)) {
  255. recognizedCorpList.value.push(logoIdx)
  256. }
  257. setTimeout(() => {
  258. const toDeleteIdx1 = activeCardIdxList.value.findIndex((item) => {
  259. return item === toDeleteValue1
  260. })
  261. activeCardIdxList.value.splice(toDeleteIdx1, 1)
  262. const toDeleteIdx2 = activeCardIdxList.value.findIndex((item) => {
  263. return item === toDeleteValue2
  264. })
  265. activeCardIdxList.value.splice(toDeleteIdx2, 1)
  266. if (cardList.value.every((item) => {
  267. return item.isDone
  268. })) {
  269. bonusPoint.value += store.state.gameRuleList[2].score
  270. setCardList()
  271. }
  272. }, 400)
  273. }, 400)
  274. } else if (activeCardIdxListRealTime.value.length === 3) {
  275. const toDeleteValue1 = activeCardIdxListRealTime.value.shift()
  276. const toDeleteValue2 = activeCardIdxListRealTime.value.shift()
  277. setTimeout(() => {
  278. const toDeleteIdx1 = activeCardIdxList.value.findIndex((item) => {
  279. return item === toDeleteValue1
  280. })
  281. activeCardIdxList.value.splice(toDeleteIdx1, 1)
  282. const toDeleteIdx2 = activeCardIdxList.value.findIndex((item) => {
  283. return item === toDeleteValue2
  284. })
  285. activeCardIdxList.value.splice(toDeleteIdx2, 1)
  286. }, 400)
  287. }
  288. }
  289. </script>
  290. <style lang="less" scoped>
  291. .game-view{
  292. position: absolute;
  293. left: 0;
  294. top: 0;
  295. width: 100%;
  296. height: 100%;
  297. background-image: url(@/assets/images/pair-up-bg.jpg);
  298. background-size: cover;
  299. background-repeat: no-repeat;
  300. background-position: center center;
  301. >img.title{
  302. position: absolute;
  303. left: 50%;
  304. top: calc(35 / v-bind('windowSizeWhenDesignForRef') * v-bind('windowSizeInCssForRef'));
  305. transform: translate(-50%, 0);
  306. width: calc(244 / v-bind('windowSizeWhenDesignForRef') * v-bind('windowSizeInCssForRef'));
  307. height: calc(80 / v-bind('windowSizeWhenDesignForRef') * v-bind('windowSizeInCssForRef'));
  308. }
  309. >.tip{
  310. position: absolute;
  311. left: 50%;
  312. top: calc(122 / v-bind('windowSizeWhenDesignForRef') * v-bind('windowSizeInCssForRef'));
  313. transform: translate(-50%, 0);
  314. font-size: calc(14 / v-bind('windowSizeWhenDesignForRef') * v-bind('windowSizeInCssForRef'));
  315. font-family: Source Han Sans SC, Source Han Sans SC;
  316. font-weight: 400;
  317. color: #FFFFFF;
  318. line-height: calc(16 / v-bind('windowSizeWhenDesignForRef') * v-bind('windowSizeInCssForRef'));
  319. }
  320. >.btn-group{
  321. position: absolute;
  322. top: calc(36 / v-bind('windowSizeWhenDesignForRef') * v-bind('windowSizeInCssForRef'));
  323. right: calc(12 / v-bind('windowSizeWhenDesignForRef') * v-bind('windowSizeInCssForRef'));
  324. display: flex;
  325. flex-direction: column;
  326. gap: calc(16 / v-bind('windowSizeWhenDesignForRef') * v-bind('windowSizeInCssForRef'));
  327. z-index: 1;
  328. >button.return-home{
  329. background-image: url(@/assets/images/icon_home.png);
  330. background-size: contain;
  331. background-repeat: no-repeat;
  332. background-position: center center;
  333. width: calc(40 / v-bind('windowSizeWhenDesignForRef') * v-bind('windowSizeInCssForRef'));
  334. height: calc(40 / v-bind('windowSizeWhenDesignForRef') * v-bind('windowSizeInCssForRef'));
  335. }
  336. >button.game-rule{
  337. background-image: url(@/assets/images/icon_rules.png);
  338. background-size: contain;
  339. background-repeat: no-repeat;
  340. background-position: center center;
  341. width: calc(40 / v-bind('windowSizeWhenDesignForRef') * v-bind('windowSizeInCssForRef'));
  342. height: calc(40 / v-bind('windowSizeWhenDesignForRef') * v-bind('windowSizeInCssForRef'));
  343. }
  344. }
  345. >.card-list{
  346. position: absolute;
  347. top: calc(163 / v-bind('windowSizeWhenDesignForRef') * v-bind('windowSizeInCssForRef'));
  348. left: calc(31 / v-bind('windowSizeWhenDesignForRef') * v-bind('windowSizeInCssForRef'));
  349. right: calc((31 - 9) / v-bind('windowSizeWhenDesignForRef') * v-bind('windowSizeInCssForRef'));
  350. margin-right: calc(-12 / v-bind('windowSizeWhenDesignForRef') * v-bind('windowSizeInCssForRef'));
  351. >.card{
  352. position: relative;
  353. display: inline-block;
  354. margin-right: calc(9 / v-bind('windowSizeWhenDesignForRef') * v-bind('windowSizeInCssForRef'));
  355. margin-bottom: calc(9 / v-bind('windowSizeWhenDesignForRef') * v-bind('windowSizeInCssForRef'));
  356. width: calc(75 / v-bind('windowSizeWhenDesignForRef') * v-bind('windowSizeInCssForRef'));
  357. height: calc(115 / v-bind('windowSizeWhenDesignForRef') * v-bind('windowSizeInCssForRef'));
  358. border-radius: calc(3 / v-bind('windowSizeWhenDesignForRef') * v-bind('windowSizeInCssForRef'));
  359. cursor: pointer;
  360. transition: transform 0.4s;
  361. >.front{
  362. position: absolute;
  363. left: 0;
  364. top: 0;
  365. width: 100%;
  366. height: 100%;
  367. transition: transform 0.4s;
  368. transform: rotateY(180deg);
  369. >img.card-frame{
  370. position: absolute;
  371. left: 0;
  372. top: 0;
  373. width: 100%;
  374. height: 100%;
  375. }
  376. >img.card-content{
  377. position: absolute;
  378. left: 50%;
  379. top: 50%;
  380. transform: translate(-50%, -50%);
  381. width: 70%;
  382. }
  383. }
  384. >img.back{
  385. position: absolute;
  386. left: 0;
  387. top: 0;
  388. width: 100%;
  389. height: 100%;
  390. backface-visibility: hidden;
  391. transition: transform 0.4s;
  392. }
  393. }
  394. >.card.active{
  395. >.front{
  396. transform: rotateY(0);
  397. }
  398. >img.back{
  399. transform: rotateY(180deg);
  400. }
  401. }
  402. >.card.hide{
  403. pointer-events: none;
  404. transform: scale(0) rotateZ(360deg);
  405. }
  406. }
  407. >.common-info-group{
  408. position: absolute;
  409. left: 50%;
  410. bottom: calc(35 / v-bind('windowSizeWhenDesignForRef') * v-bind('windowSizeInCssForRef'));
  411. transform: translate(-50%, 0);
  412. display: flex;
  413. gap: calc(11 / v-bind('windowSizeWhenDesignForRef') * v-bind('windowSizeInCssForRef'));
  414. >.info-item{
  415. position: relative;
  416. display: flex;
  417. justify-content: space-between;
  418. align-items: flex-end;
  419. padding-bottom: calc(3 / v-bind('windowSizeWhenDesignForRef') * v-bind('windowSizeInCssForRef'));
  420. width: calc(143 / v-bind('windowSizeWhenDesignForRef') * v-bind('windowSizeInCssForRef'));
  421. height: calc(28 / v-bind('windowSizeWhenDesignForRef') * v-bind('windowSizeInCssForRef'));
  422. padding-left: calc(5 / v-bind('windowSizeWhenDesignForRef') * v-bind('windowSizeInCssForRef'));
  423. padding-right: calc(18 / v-bind('windowSizeWhenDesignForRef') * v-bind('windowSizeInCssForRef'));
  424. background: #BB9565;
  425. border-radius: calc(14 / v-bind('windowSizeWhenDesignForRef') * v-bind('windowSizeInCssForRef'));
  426. box-shadow: #78511F calc(2 / v-bind('windowSizeWhenDesignForRef') * v-bind('windowSizeInCssForRef')) calc(3 / v-bind('windowSizeWhenDesignForRef') * v-bind('windowSizeInCssForRef')) 0 0;
  427. >.icon{
  428. }
  429. >.number{
  430. font-size: calc(20 / v-bind('windowSizeWhenDesignForRef') * v-bind('windowSizeInCssForRef'));
  431. font-family: heiti;
  432. font-weight: 400;
  433. color: #FFFFFF;
  434. line-height: calc(23 / v-bind('windowSizeWhenDesignForRef') * v-bind('windowSizeInCssForRef'));
  435. }
  436. }
  437. >.bonus-point{
  438. >.icon{
  439. width: calc(46 / v-bind('windowSizeWhenDesignForRef') * v-bind('windowSizeInCssForRef'));
  440. height: calc(41 / v-bind('windowSizeWhenDesignForRef') * v-bind('windowSizeInCssForRef'));
  441. }
  442. }
  443. >.time-count{
  444. >.icon{
  445. width: calc(37 / v-bind('windowSizeWhenDesignForRef') * v-bind('windowSizeInCssForRef'));
  446. height: calc(46 / v-bind('windowSizeWhenDesignForRef') * v-bind('windowSizeInCssForRef'));
  447. }
  448. }
  449. }
  450. }
  451. </style>