HomeView.vue 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573
  1. <template>
  2. <div
  3. class="home"
  4. >
  5. <div
  6. v-if="!store.state.loginStatus"
  7. class="for-visitor"
  8. >
  9. <button
  10. class="login"
  11. @click="onClickLogin"
  12. >
  13. <img
  14. class=""
  15. src="@/assets/images/btn-login-bg.png"
  16. alt=""
  17. draggable="false"
  18. >
  19. <span>登录</span>
  20. </button>
  21. <button
  22. class="sign-up"
  23. @click="onClickSignUp"
  24. >
  25. <img
  26. class=""
  27. src="@/assets/images/btn-sign-up-bg.png"
  28. alt=""
  29. draggable="false"
  30. >
  31. <span>注册</span>
  32. </button>
  33. </div>
  34. <div
  35. v-else
  36. class="after-login"
  37. >
  38. <img
  39. class="avatar"
  40. src="@/assets/images/avatar.png"
  41. alt=""
  42. draggable="false"
  43. >
  44. <div class="info-wrap">
  45. <div class="user-id">
  46. <span class="title">用户ID:</span>
  47. <span
  48. class="value"
  49. :title="store.state.userInfo.userName"
  50. >{{ store.state.userInfo.userName }}</span>
  51. </div>
  52. <div class="point-bonus">
  53. <span
  54. v-show="store.state.score !== null"
  55. class="title"
  56. >当前积分:</span>
  57. <span class="value">{{ store.state.score }}</span>
  58. </div>
  59. </div>
  60. <button
  61. class="change-password"
  62. @click="router.push({
  63. name: 'ChangePassword',
  64. })"
  65. >
  66. <img
  67. class=""
  68. src="@/assets/images/icon-password.png"
  69. alt=""
  70. draggable="false"
  71. >
  72. </button>
  73. <button
  74. class="logout"
  75. @click="onClickLogout"
  76. >
  77. <img
  78. class=""
  79. src="@/assets/images/icon-exit.png"
  80. alt=""
  81. draggable="false"
  82. >
  83. </button>
  84. </div>
  85. <div class="entry-list">
  86. <!-- 乡村林场 -->
  87. <button
  88. class="game-entry plant-tree-entry"
  89. @click="onClickGameEntry(0)"
  90. >
  91. <img
  92. class=""
  93. src="@/assets/images/plant-tree-entry.png"
  94. alt=""
  95. draggable="false"
  96. >
  97. </button>
  98. <!-- 助农课堂 -->
  99. <button
  100. class="game-entry exam-paper-entry"
  101. @click="onClickGameEntry(1)"
  102. >
  103. <img
  104. class=""
  105. src="@/assets/images/exam-paper-entry.png"
  106. alt=""
  107. draggable="false"
  108. >
  109. </button>
  110. <!-- 企业翻翻看 -->
  111. <button
  112. class="game-entry pair-up-entry"
  113. @click="onClickGameEntry(2)"
  114. >
  115. <img
  116. class=""
  117. src="@/assets/images/pair-up-entry.png"
  118. alt=""
  119. draggable="false"
  120. >
  121. </button>
  122. <!-- 抗体大作战 -->
  123. <button
  124. class="game-entry antibody-battle-entry"
  125. @click="onClickGameEntry(3)"
  126. >
  127. <img
  128. class=""
  129. src="@/assets/images/antibody-battle-entry.png"
  130. alt=""
  131. draggable="false"
  132. >
  133. </button>
  134. <!-- 应急救灾 -->
  135. <button
  136. class="game-entry disaster-relief-entry"
  137. @click="onClickGameEntry(4)"
  138. >
  139. <img
  140. class=""
  141. src="@/assets/images/disaster-relief-entry.png"
  142. alt=""
  143. draggable="false"
  144. >
  145. </button>
  146. <!-- 生态保护 -->
  147. <button
  148. class="game-entry enviroment-protection"
  149. @click="onClickGameEntry(5)"
  150. >
  151. <img
  152. class=""
  153. src="@/assets/images/enviroment-protection.png"
  154. alt=""
  155. draggable="false"
  156. >
  157. </button>
  158. <!-- 找回走失儿童 -->
  159. <button
  160. class="game-entry lost-children-entry"
  161. @click="onClickGameEntry(6)"
  162. >
  163. <img
  164. class=""
  165. src="@/assets/images/lost-children-entry.png"
  166. alt=""
  167. draggable="false"
  168. >
  169. </button>
  170. </div>
  171. <!-- 悬浮按钮 -->
  172. <div class="floating-btn-group">
  173. <button
  174. v-if="store.state.loginStatus"
  175. class="redeem"
  176. @click="router.push({
  177. name: 'ShopView'
  178. })"
  179. >
  180. <img
  181. class=""
  182. src="@/assets/images/icon-present.png"
  183. alt=""
  184. draggable="false"
  185. >
  186. </button>
  187. <button
  188. class="share"
  189. :data-clipboard-text="shareText"
  190. >
  191. <img
  192. class=""
  193. src="@/assets/images/icon-share.png"
  194. alt=""
  195. draggable="false"
  196. >
  197. </button>
  198. <button
  199. class="quit"
  200. @click="onClickQuit"
  201. >
  202. <img
  203. class=""
  204. src="@/assets/images/icon_home_2.png"
  205. alt=""
  206. draggable="false"
  207. >
  208. </button>
  209. </div>
  210. <!-- 让用户选择是否登录、注册 -->
  211. <LoginChoice
  212. v-show="isShowLoginChoice"
  213. class="login-choice"
  214. @login="onClickLogin"
  215. @signUp="onClickSignUp"
  216. @visitor="onLoginChoiceVisitor"
  217. @close="onLoginChoiceClose"
  218. />
  219. <van-loading
  220. v-show="isShowLoading"
  221. class="loading"
  222. type="spinner"
  223. />
  224. </div>
  225. </template>
  226. <script setup>
  227. import { ref, computed, watch, onMounted, onUnmounted, watchEffect } from "vue"
  228. import { useRoute, useRouter } from "vue-router"
  229. import { useStore } from "vuex"
  230. import ClipboardJS from 'clipboard'
  231. import { showDialog } from 'vant'
  232. import { logout, getScore, notifyQuit } from '@/api.js'
  233. import LoginChoice from '@/components/LoginChoice.vue'
  234. const route = useRoute()
  235. const router = useRouter()
  236. const store = useStore()
  237. const {
  238. windowSizeInCssForRef,
  239. windowSizeWhenDesignForRef,
  240. } = useSizeAdapt(390, 752)
  241. const loginStatus = computed(() => {
  242. return store.state.loginStatus
  243. })
  244. const gameRuleList = computed(() => {
  245. return store.state.gameRuleList
  246. })
  247. const score = computed(() => {
  248. return store.state.score
  249. })
  250. const ifScoreLimitReached = computed(() => {
  251. return store.state.ifScoreLimitReached
  252. })
  253. const scoreLimit = computed(() => {
  254. return store.state.scoreLimit
  255. })
  256. if (loginStatus.value) {
  257. getScore().then((res) => {
  258. store.commit('setScore', res.total)
  259. store.commit('setIfScoreLimitReached', res.hasOver)
  260. })
  261. }
  262. const isShowLoading = ref(true)
  263. let hideLoadingCallback = null
  264. watchEffect(() => {
  265. if (
  266. (loginStatus.value === false && gameRuleList.value.length > 0) ||
  267. (loginStatus.value === true && gameRuleList.value.length > 0 && score.value !== undefined && ifScoreLimitReached.value !== undefined && !scoreLimit.value !== undefined)
  268. ) {
  269. isShowLoading.value = false
  270. if (hideLoadingCallback) {
  271. hideLoadingCallback()
  272. }
  273. }
  274. })
  275. function onClickLogin() {
  276. router.push({
  277. name: 'LoginView',
  278. })
  279. }
  280. function onClickSignUp() {
  281. router.push({
  282. name: 'SignUp',
  283. })
  284. }
  285. function onClickLogout() {
  286. logout()
  287. }
  288. const shareText = location.href
  289. const clipboard = new ClipboardJS('button.share')
  290. clipboard.on('success', function(e) {
  291. showDialog({
  292. message: '链接已复制',
  293. theme: 'round-button',
  294. })
  295. })
  296. // clipboard.on('error', function(e) {
  297. // showDialog({
  298. // message: '请使用浏览器原生功能进行分享',
  299. // theme: 'round-button',
  300. // })
  301. // });
  302. onUnmounted(() => {
  303. clipboard.destroy()
  304. })
  305. const entryFunctionList = [
  306. function () {
  307. router.push({
  308. name: 'PlantTree',
  309. })
  310. },
  311. function () {
  312. router.push({
  313. name: 'ExamPaper1',
  314. })
  315. },
  316. function () {
  317. router.push({
  318. name: 'PairUp',
  319. })
  320. },
  321. function () {
  322. router.push({
  323. name: 'GameByUnity',
  324. query: {
  325. gameName: 'AntibodyBattle'
  326. },
  327. })
  328. },
  329. function () {
  330. router.push({
  331. name: 'GameByUnity',
  332. query: {
  333. gameName: 'DisasterRelief'
  334. },
  335. })
  336. },
  337. function () {
  338. router.push({
  339. name: 'GameByUnity',
  340. query: {
  341. gameName: 'EnviromentProtection'
  342. },
  343. })
  344. },
  345. function () {
  346. router.push({
  347. name: 'GameByUnity',
  348. query: {
  349. gameName: 'LostChildren'
  350. },
  351. })
  352. },
  353. ]
  354. const isShowLoginChoice = ref(false)
  355. function onClickGameEntry(idx) {
  356. if (store.state.loginStatus) {
  357. entryFunctionList[idx]()
  358. } else {
  359. store.commit('setGameToPlayIdx', idx)
  360. isShowLoginChoice.value = true
  361. }
  362. }
  363. function onLoginChoiceVisitor() {
  364. const gameIdx = store.state.gameToPlayIdx
  365. store.commit('setGameToPlayIdx', null)
  366. isShowLoginChoice.value = false
  367. entryFunctionList[gameIdx]()
  368. }
  369. function onLoginChoiceClose() {
  370. store.commit('setGameToPlayIdx', null)
  371. isShowLoginChoice.value = false
  372. }
  373. // 如果是点击游戏入口触发了登录、注册,且登录、注册完毕后回到首页,需要自动进入游戏页。
  374. // 如果是点击游戏入口触发了登录,但登录、注册未成功即回到首页,则不应自动进入游戏页。
  375. if (store.state.gameToPlayIdx !== null) {
  376. const gameIdx = store.state.gameToPlayIdx
  377. store.state.gameToPlayIdx = null
  378. if (store.state.loginStatus) {
  379. entryFunctionList[gameIdx]()
  380. }
  381. }
  382. // 如果通过url要求自动进入游戏页
  383. if (route.query.gameIdx) {
  384. const gameIdx = Number(route.query.gameIdx)
  385. router.replace({
  386. name: route.name
  387. })
  388. store.commit('setIsDirectPlayGame', true)
  389. if (isShowLoading.value) {
  390. hideLoadingCallback = () => {
  391. onClickGameEntry(gameIdx)
  392. }
  393. } else {
  394. onClickGameEntry(gameIdx)
  395. }
  396. }
  397. function onClickQuit() {
  398. notifyQuit()
  399. }
  400. </script>
  401. <style lang="less" scoped>
  402. .home {
  403. width: 100%;
  404. height: 100%;
  405. overflow: auto;
  406. >.for-visitor{
  407. margin-top: calc(15 / v-bind('windowSizeWhenDesignForRef') * v-bind('windowSizeInCssForRef'));
  408. margin-bottom: calc(18 / v-bind('windowSizeWhenDesignForRef') * v-bind('windowSizeInCssForRef'));
  409. width: 100%;
  410. display: flex;
  411. justify-content: space-between;
  412. align-items: center;
  413. padding-left: calc(16 / v-bind('windowSizeWhenDesignForRef') * v-bind('windowSizeInCssForRef'));
  414. padding-right: calc(16 / v-bind('windowSizeWhenDesignForRef') * v-bind('windowSizeInCssForRef'));
  415. >button{
  416. width: calc(165 / v-bind('windowSizeWhenDesignForRef') * v-bind('windowSizeInCssForRef'));
  417. position: relative;
  418. >img{
  419. width: 100%;
  420. height: 100%;
  421. }
  422. >span{
  423. position: absolute;
  424. left: calc(95 / v-bind('windowSizeWhenDesignForRef') * v-bind('windowSizeInCssForRef'));
  425. top: calc(32 / v-bind('windowSizeWhenDesignForRef') * v-bind('windowSizeInCssForRef'));
  426. font-size: calc(24 / v-bind('windowSizeWhenDesignForRef') * v-bind('windowSizeInCssForRef'));
  427. font-family: Source Han Sans SC, Source Han Sans SC;
  428. font-weight: bold;
  429. color: #A97C46;
  430. line-height: calc(28 / v-bind('windowSizeWhenDesignForRef') * v-bind('windowSizeInCssForRef'));
  431. }
  432. }
  433. }
  434. >.after-login{
  435. margin-top: calc(25 / v-bind('windowSizeWhenDesignForRef') * v-bind('windowSizeInCssForRef'));
  436. margin-bottom: calc(15 / v-bind('windowSizeWhenDesignForRef') * v-bind('windowSizeInCssForRef'));
  437. display: flex;
  438. align-items: center;
  439. padding-left: calc(12 / v-bind('windowSizeWhenDesignForRef') * v-bind('windowSizeInCssForRef'));
  440. padding-right: calc(12 / v-bind('windowSizeWhenDesignForRef') * v-bind('windowSizeInCssForRef'));
  441. padding-top: calc(13 / v-bind('windowSizeWhenDesignForRef') * v-bind('windowSizeInCssForRef'));
  442. padding-bottom: calc(13 / v-bind('windowSizeWhenDesignForRef') * v-bind('windowSizeInCssForRef'));
  443. background: rgba(226, 201, 163, 0.5);
  444. box-shadow: inset -1px -2px 1px 0px #E2C9A3;
  445. border-radius: calc(4 / v-bind('windowSizeWhenDesignForRef') * v-bind('windowSizeInCssForRef'));
  446. margin-left: calc(16 / v-bind('windowSizeWhenDesignForRef') * v-bind('windowSizeInCssForRef'));
  447. margin-right: calc(16 / v-bind('windowSizeWhenDesignForRef') * v-bind('windowSizeInCssForRef'));
  448. >img.avatar{
  449. flex: 0 0 auto;
  450. width: calc(54 / v-bind('windowSizeWhenDesignForRef') * v-bind('windowSizeInCssForRef'));
  451. height: calc(54 / v-bind('windowSizeWhenDesignForRef') * v-bind('windowSizeInCssForRef'));
  452. border-radius: calc(27 / v-bind('windowSizeWhenDesignForRef') * v-bind('windowSizeInCssForRef'));
  453. object-fit: cover;
  454. }
  455. >.info-wrap{
  456. flex: 0 0 auto;
  457. margin-left: calc(16 / v-bind('windowSizeWhenDesignForRef') * v-bind('windowSizeInCssForRef'));
  458. font-size: calc(16 / v-bind('windowSizeWhenDesignForRef') * v-bind('windowSizeInCssForRef'));
  459. font-family: Source Han Sans SC, Source Han Sans SC;
  460. font-weight: 400;
  461. color: #A97C46;
  462. line-height: 1.5;
  463. white-space: pre;
  464. >.user-id{
  465. >.title{
  466. vertical-align: middle;
  467. display: inline-block;
  468. width: calc(69 / v-bind('windowSizeWhenDesignForRef') * v-bind('windowSizeInCssForRef'));
  469. letter-spacing: calc(4 / v-bind('windowSizeWhenDesignForRef') * v-bind('windowSizeInCssForRef'));
  470. margin-right: calc(10 / v-bind('windowSizeWhenDesignForRef') * v-bind('windowSizeInCssForRef'));
  471. }
  472. >.value{
  473. vertical-align: middle;
  474. display: inline-block;
  475. overflow: hidden;
  476. white-space: pre;
  477. text-overflow: ellipsis;
  478. width: calc(80 / v-bind('windowSizeWhenDesignForRef') * v-bind('windowSizeInCssForRef'));
  479. }
  480. }
  481. >.point-bonus{
  482. >.title{
  483. vertical-align: middle;
  484. display: inline-block;
  485. width: calc(69 / v-bind('windowSizeWhenDesignForRef') * v-bind('windowSizeInCssForRef'));
  486. margin-right: calc(10 / v-bind('windowSizeWhenDesignForRef') * v-bind('windowSizeInCssForRef'));
  487. }
  488. >.value{
  489. vertical-align: middle;
  490. display: inline-block;
  491. overflow: hidden;
  492. white-space: pre;
  493. text-overflow: ellipsis;
  494. width: calc(80 / v-bind('windowSizeWhenDesignForRef') * v-bind('windowSizeInCssForRef'));
  495. }
  496. }
  497. }
  498. >button.change-password{
  499. flex: 0 0 auto;
  500. width: calc(32 / v-bind('windowSizeWhenDesignForRef') * v-bind('windowSizeInCssForRef'));
  501. margin-left: calc(12 / v-bind('windowSizeWhenDesignForRef') * v-bind('windowSizeInCssForRef'));
  502. >img{
  503. width: 100%;
  504. }
  505. }
  506. >button.logout{
  507. flex: 0 0 auto;
  508. width: calc(32 / v-bind('windowSizeWhenDesignForRef') * v-bind('windowSizeInCssForRef'));
  509. margin-left: calc(12 / v-bind('windowSizeWhenDesignForRef') * v-bind('windowSizeInCssForRef'));
  510. >img{
  511. width: 100%;
  512. }
  513. }
  514. }
  515. >.entry-list{
  516. margin-left: calc(10 / v-bind('windowSizeWhenDesignForRef') * v-bind('windowSizeInCssForRef'));
  517. margin-right: calc(10 / v-bind('windowSizeWhenDesignForRef') * v-bind('windowSizeInCssForRef'));
  518. >button.game-entry{
  519. width: 100%;
  520. >img{
  521. width: 100%;
  522. }
  523. }
  524. }
  525. >.floating-btn-group{
  526. position: fixed;
  527. right: calc(11 / v-bind('windowSizeWhenDesignForRef') * v-bind('windowSizeInCssForRef'));
  528. bottom: calc(58 / v-bind('windowSizeWhenDesignForRef') * v-bind('windowSizeInCssForRef'));
  529. >button{
  530. width: calc(60 / v-bind('windowSizeWhenDesignForRef') * v-bind('windowSizeInCssForRef'));
  531. height: calc(60 / v-bind('windowSizeWhenDesignForRef') * v-bind('windowSizeInCssForRef'));
  532. display: block;
  533. >img{
  534. width: 100%;
  535. height: 100%;
  536. }
  537. }
  538. }
  539. >.loading{
  540. position: absolute;
  541. left: 0;
  542. top: 0;
  543. width: 100%;
  544. height: 100%;
  545. background-color: rgba(0, 0, 0, 0.5);
  546. display: flex;
  547. justify-content: center;
  548. align-items: center;
  549. }
  550. }
  551. </style>