ShopView.vue 23 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874
  1. <template>
  2. <div
  3. class="shop-view"
  4. >
  5. <menu class="tab-bar">
  6. <button
  7. :class="{
  8. active: tabIdx === 0
  9. }"
  10. @click="tabIdx = 0"
  11. >
  12. 爱心兑换
  13. </button>
  14. <button
  15. :class="{
  16. active: tabIdx === 2
  17. }"
  18. @click="tabIdx = 2"
  19. >
  20. 排行榜
  21. </button>
  22. <button
  23. :class="{
  24. active: tabIdx === 1
  25. }"
  26. @click="tabIdx = 1"
  27. >
  28. 爱心记录
  29. </button>
  30. </menu>
  31. <template v-if="tabIdx === 0">
  32. <button
  33. class="prev-page"
  34. :class="{
  35. hide: pageNumber === 0
  36. }"
  37. @click="onClickPrevPage"
  38. />
  39. <ul
  40. v-loading.fullscreen.lock="pageLoading"
  41. class="prize-list"
  42. >
  43. <li
  44. v-for="prizeItem in prizeList"
  45. :key="prizeItem.id"
  46. class="prize"
  47. :class="{
  48. disabled: prizeItem.stock === 0,
  49. }"
  50. @click="onClickPrizeItem(prizeItem)"
  51. >
  52. <img
  53. class="thumb"
  54. :src="`${$env.VUE_APP_DEPLOY_ORIGIN}${prizeItem.thumb}`"
  55. alt=""
  56. draggable="false"
  57. >
  58. <div class="prize-inner">
  59. <div class="title">
  60. {{ prizeItem.name }}
  61. </div>
  62. <div
  63. class="remaining"
  64. >
  65. <span v-if="prizeItem.id !== 1">剩余:{{ prizeItem.stock }}</span>
  66. </div>
  67. <div class="price">
  68. <span class="number">{{ prizeItem.score }}</span>
  69. <span class="unit">爱心</span>
  70. </div>
  71. <img
  72. v-show="prizeItem.stock > 0 || (prizeItem.id === 1 && !isRedeemed)"
  73. class="icon-enabled"
  74. src="@/assets/images/icon-gift.png"
  75. alt=""
  76. draggable="false"
  77. >
  78. <img
  79. v-if="prizeItem.id === 1 && isRedeemed"
  80. class="icon-enabled redeemed"
  81. src="@/assets/images/icon_complete@2x-min.png"
  82. alt=""
  83. draggable="false"
  84. >
  85. <!-- <img
  86. v-show="prizeItem.isEnabled && prizeItem.stock === 0"
  87. class="icon-no-stock"
  88. src="@/assets/images/no-stock.png"
  89. alt=""
  90. draggable="false"
  91. > -->
  92. </div>
  93. </li>
  94. <p
  95. class="product-tip"
  96. >
  97. 更多礼品敬请期待
  98. </p>
  99. </ul>
  100. <button
  101. class="next-page"
  102. :class="{
  103. hide: !haveNextPage
  104. }"
  105. @click="onClickNextPage"
  106. />
  107. </template>
  108. <div
  109. v-show="tabIdx === 1"
  110. class="redeem-record-content"
  111. >
  112. <p
  113. v-show="redeemRecordContact"
  114. class="tip"
  115. >
  116. 如有疑问,请咨询 {{ redeemRecordContact }}
  117. </p>
  118. <div class="table">
  119. <div class="row-header">
  120. <div>时间</div>
  121. <div>类型</div>
  122. <div>爱心</div>
  123. <div>说明</div>
  124. </div>
  125. <div class="splitter" />
  126. <div class="table-content">
  127. <div
  128. v-for="item in redeemRecord"
  129. :key="item.id"
  130. class="row-data"
  131. >
  132. <div>
  133. {{ item.createTime }}
  134. </div>
  135. <div>
  136. {{ item.type }}
  137. </div>
  138. <div>
  139. {{ item.score > 0 ? '+' : item.score < 0 ? '' : '' }}{{ item.score }}
  140. </div>
  141. <div>
  142. {{ item.description || `(空)` }}
  143. </div>
  144. </div>
  145. </div>
  146. </div>
  147. </div>
  148. <div
  149. v-show="tabIdx === 2"
  150. class="ranking-content"
  151. >
  152. <div
  153. v-loading="rankingLoading"
  154. class="table"
  155. >
  156. <div class="row-header">
  157. <div>排名</div>
  158. <div>用户名</div>
  159. <div>爱心</div>
  160. </div>
  161. <div class="splitter" />
  162. <div class="table-content">
  163. <div
  164. v-for="(item, idx) in rankingList"
  165. :key="item.id"
  166. :class="['row-data', {
  167. 'is-me': store.state.userInfo.userId === item.id
  168. }]"
  169. >
  170. <div :class="['ranking-sort', idx === 0 && 'is-first']">
  171. <span>{{ idx + 1 }}</span>
  172. </div>
  173. <div class="ranking-name">
  174. <img
  175. class="ranking-name__avatar"
  176. fit="cover"
  177. src="@/assets/images/default-avatar-min.png"
  178. >
  179. <span>{{ item.nickName.slice(0, 1) }}***</span>
  180. </div>
  181. <div>
  182. {{ item.pcs }}
  183. </div>
  184. </div>
  185. </div>
  186. </div>
  187. </div>
  188. <div class="coin-number">
  189. <img
  190. class="icon"
  191. src="@/assets/images/icon_money.png"
  192. alt=""
  193. draggable="false"
  194. >
  195. <span class="value">{{ myScore }}</span>
  196. </div>
  197. <PrizeRedeem
  198. v-if="isShowRedeem"
  199. :prize-data="redeemPrizeData"
  200. :score="myScore"
  201. @open-cert="openCertImg"
  202. @close="redeemPrizeData = {}, isShowRedeem = false"
  203. @submit-over="handlePrizeSubmitOver"
  204. />
  205. <div
  206. v-show="isShowCertImg"
  207. class="cert-view"
  208. >
  209. <img
  210. class="cert-img"
  211. :src="certImgUrl"
  212. alt=""
  213. draggable="false"
  214. >
  215. <a
  216. v-show="$isTablet"
  217. class="btn-download"
  218. :href="certImgUrl"
  219. download="锡善云城-证书"
  220. >
  221. 下载证书
  222. </a>
  223. <button
  224. class="close"
  225. @click="isShowCertImg = false"
  226. />
  227. </div>
  228. </div>
  229. <div
  230. ref="certCanvas"
  231. class="cert-canvas"
  232. >
  233. <p>{{ certInfo.certName }}</p>
  234. <p>{{ certInfo.createTime }}</p>
  235. </div>
  236. </template>
  237. <script setup>
  238. import { ref, computed, watch, onMounted, inject, nextTick } from "vue"
  239. import { useStore } from "vuex"
  240. import PrizeRedeem from '@/components/PrizeRedeem.vue'
  241. import {
  242. getMyScore,
  243. getShopContact,
  244. getPrizeList,
  245. getRedeemRecord,
  246. getRankingListApi,
  247. checkRedeemApi,
  248. getRedeemApi,
  249. uploadFile,
  250. } from '@/api.js'
  251. import html2canvas from 'html2canvas'
  252. import { formatDate } from '@dage/utils'
  253. import { ElMessage } from 'element-plus'
  254. import { useRouter } from 'vue-router'
  255. const store = useStore()
  256. const router = useRouter()
  257. const pageLoading = ref(false)
  258. const $env = inject('$env')
  259. const $isTablet = inject('$isTablet')
  260. const {
  261. windowSizeInCssForRef,
  262. windowSizeWhenDesignForRef,
  263. } = useSizeAdapt(1920, 972)
  264. const myScore = ref(undefined)
  265. getMyScore().then((res) => {
  266. myScore.value = res
  267. })
  268. const tabIdx = ref(0)
  269. const redeemRecordContact = ref('')
  270. getShopContact().then((res) => {
  271. if (res.display === 0) {
  272. ElMessage({
  273. message: '商城暂未开启,敬请期待',
  274. type: 'warning',
  275. })
  276. router.replace({
  277. name: 'HomeView'
  278. })
  279. return
  280. }
  281. redeemRecordContact.value = res.rtf
  282. })
  283. const handlePrizeSubmitOver = async() => {
  284. try {
  285. pageLoading.value = true
  286. await checkRedeem()
  287. const scoreRes = await getMyScore()
  288. myScore.value = scoreRes
  289. const res = await getPrizeList(pageNumber.value, 8)
  290. total.value = res.total
  291. prizeList.value = res.records
  292. } finally {
  293. pageLoading.value = false
  294. }
  295. isShowRedeem.value = false
  296. }
  297. /**
  298. * 一页页的商品
  299. */
  300. const prizeList = ref([])
  301. const pageNumber = ref(0)
  302. const total = ref(0)
  303. function onClickPrevPage() {
  304. if (pageNumber.value > 0) {
  305. pageNumber.value--
  306. }
  307. }
  308. function onClickNextPage() {
  309. if (haveNextPage.value) {
  310. pageNumber.value++
  311. }
  312. }
  313. const haveNextPage = computed(() => {
  314. return total.value > ((pageNumber.value + 1) * 8)
  315. })
  316. watch(pageNumber, (v) => {
  317. getPrizeList(v + 1, 8).then((res) => {
  318. total.value = res.total
  319. prizeList.value = res.records
  320. })
  321. }, {
  322. immediate: true,
  323. })
  324. const isShowRedeem = ref(false)
  325. const redeemPrizeData = ref({})
  326. function onClickPrizeItem(prizeItem) {
  327. if (prizeItem.id === 1 && isRedeemed.value) {
  328. openCertImg()
  329. return
  330. }
  331. if (prizeItem.score > myScore.value) {
  332. ElMessage.warning('积分不足,无法兑换')
  333. return
  334. }
  335. if (prizeItem.stock < 1 && prizeItem.id !== 1) {
  336. ElMessage.warning('库存不足,无法兑换')
  337. return
  338. }
  339. redeemPrizeData.value = prizeItem
  340. isShowRedeem.value = true
  341. }
  342. const redeemRecord = ref([])
  343. getRedeemRecord().then((res) => {
  344. redeemRecord.value = res
  345. })
  346. const rankingList = ref([])
  347. const rankingLoading = ref(false)
  348. const getRankingList = async() => {
  349. try {
  350. rankingLoading.value = true
  351. const data = await getRankingListApi()
  352. rankingList.value = data
  353. } finally {
  354. rankingLoading.value = false
  355. }
  356. }
  357. watch(tabIdx, val => {
  358. if (val === 2) {
  359. getRankingList()
  360. }
  361. })
  362. onMounted(() => {
  363. checkRedeem()
  364. })
  365. const isRedeemed = ref(false)
  366. const checkRedeem = async() => {
  367. const data = await checkRedeemApi()
  368. isRedeemed.value = data
  369. }
  370. const certCanvas = ref()
  371. // const $isSafari = inject('$isSafari')
  372. const isShowCertImg = ref(false)
  373. const certImgUrl = ref('')
  374. const openCertImg = async(certName) => {
  375. try {
  376. pageLoading.value = true
  377. if (isRedeemed.value) {
  378. await getRedeem()
  379. } else {
  380. certInfo.value = {
  381. certName,
  382. createTime: formatDate(new Date(), "YYYY年MM月DD日")
  383. }
  384. }
  385. await nextTick(async() => {
  386. const canvas = await html2canvas(certCanvas.value, {
  387. width: certCanvas.value.offsetWidth,
  388. scale: 1,
  389. allowTaint: true,
  390. useCORS: true,
  391. })
  392. canvas.toBlob(
  393. async (blob) => {
  394. if (blob) {
  395. const uploadRes = await uploadFile(blob)
  396. // certImgUrl.value = URL.createObjectURL(blob)
  397. certImgUrl.value = `${process.env.VUE_APP_DEPLOY_ORIGIN}${uploadRes}`
  398. isShowCertImg.value = true
  399. }
  400. },
  401. 'image/jpeg',
  402. 0.95,
  403. )
  404. // const dataUrl = canvas.toDataURL('image/jpeg')
  405. // certImgUrl.value = dataUrl
  406. // isShowCertImg.value = true
  407. // uploadFile(dataUrl)
  408. })
  409. } finally {
  410. pageLoading.value = false
  411. }
  412. }
  413. const certInfo = ref({
  414. certName: '',
  415. createTime: ''
  416. })
  417. const getRedeem = async() => {
  418. const data = await getRedeemApi()
  419. data.createTime = formatDate(data.createTime, "YYYY年MM月DD日")
  420. certInfo.value = data
  421. }
  422. </script>
  423. <style lang="less" scoped>
  424. .shop-view{
  425. position: absolute;
  426. left: 0;
  427. top: 0;
  428. width: 100%;
  429. height: 100%;
  430. display: flex;
  431. align-items: center;
  432. >menu{
  433. flex: 0 0 auto;
  434. display: flex;
  435. flex-direction: column;
  436. justify-content: center;
  437. align-items: center;
  438. width: calc(300 / 1920 * 100%);
  439. >button{
  440. margin: 28px 0;
  441. font-family: Source Han Sans CN, Source Han Sans CN;
  442. font-weight: 400;
  443. font-size: 28px;
  444. color: #424A4A;
  445. line-height: 33px;
  446. position: relative;
  447. &.active{
  448. font-weight: bold;
  449. color: #589498;
  450. &::after{
  451. content: '';
  452. position: absolute;
  453. left: 50%;
  454. top: 100%;
  455. transform: translate(-50%);
  456. width: 90px;
  457. height: 9px;
  458. background: #FFE794;
  459. box-shadow: 0px 1px 4px 0px rgba(193,165,64,0.5);
  460. border-radius: 4px;
  461. }
  462. }
  463. }
  464. }
  465. >button.prev-page{
  466. width: 58px;
  467. height: 58px;
  468. background-image: url(@/assets/images/icon_arrow-left-round.png);
  469. background-size: cover;
  470. background-repeat: no-repeat;
  471. background-position: center center;
  472. margin-right: calc(25 / v-bind('windowSizeWhenDesignForRef') * v-bind('windowSizeInCssForRef'));
  473. &.hide {
  474. pointer-events: none;
  475. opacity: 0;
  476. }
  477. }
  478. ul.prize-list{
  479. position: relative;
  480. flex: 0 0 auto;
  481. width: calc(1400 / v-bind('windowSizeWhenDesignForRef') * v-bind('windowSizeInCssForRef'));
  482. height: calc((393 * 2 + 25) / v-bind('windowSizeWhenDesignForRef') * v-bind('windowSizeInCssForRef'));
  483. .product-tip {
  484. position: absolute;
  485. left: 50%;
  486. bottom: -20px;
  487. transform: translateX(-50%);
  488. color: #666;
  489. }
  490. li.prize{
  491. display: inline-block;
  492. width: calc(322 / v-bind('windowSizeWhenDesignForRef') * v-bind('windowSizeInCssForRef'));
  493. height: calc(393 / v-bind('windowSizeWhenDesignForRef') * v-bind('windowSizeInCssForRef'));
  494. background: linear-gradient( 180deg, rgba(255,255,255,0.2) 0%, rgba(255,255,255,0.8) 100%);
  495. border-radius: calc(7 / v-bind('windowSizeWhenDesignForRef') * v-bind('windowSizeInCssForRef'));
  496. padding: calc(7 / v-bind('windowSizeWhenDesignForRef') * v-bind('windowSizeInCssForRef')) calc(12 / v-bind('windowSizeWhenDesignForRef') * v-bind('windowSizeInCssForRef'));
  497. box-shadow: 0px 1px 11px 0px rgba(0,0,0,0.1);
  498. position: relative;
  499. margin-right: calc(25 / v-bind('windowSizeWhenDesignForRef') * v-bind('windowSizeInCssForRef'));
  500. margin-bottom: calc(25 / v-bind('windowSizeWhenDesignForRef') * v-bind('windowSizeInCssForRef'));
  501. cursor: pointer;
  502. >img.thumb{
  503. width: calc(307 / v-bind('windowSizeWhenDesignForRef') * v-bind('windowSizeInCssForRef'));
  504. height: calc(246 / v-bind('windowSizeWhenDesignForRef') * v-bind('windowSizeInCssForRef'));
  505. object-fit: cover;
  506. border-radius: calc(4 / v-bind('windowSizeWhenDesignForRef') * v-bind('windowSizeInCssForRef'));
  507. }
  508. .prize-inner {
  509. padding: 0 calc(22 / v-bind('windowSizeWhenDesignForRef') * v-bind('windowSizeInCssForRef'));
  510. }
  511. .title{
  512. margin-top: calc(13 / v-bind('windowSizeWhenDesignForRef') * v-bind('windowSizeInCssForRef'));
  513. font-size: calc(28 / v-bind('windowSizeWhenDesignForRef') * v-bind('windowSizeInCssForRef'));
  514. font-family: Source Han Sans SC, Source Han Sans SC;
  515. color: #424A4A;
  516. line-height: calc(33 / v-bind('windowSizeWhenDesignForRef') * v-bind('windowSizeInCssForRef'));
  517. font-weight: bold;
  518. overflow: hidden;
  519. white-space: pre;
  520. text-overflow: ellipsis;
  521. }
  522. .remaining{
  523. min-height: calc(20 / v-bind('windowSizeWhenDesignForRef') * v-bind('windowSizeInCssForRef'));
  524. margin-top: calc(5 / v-bind('windowSizeWhenDesignForRef') * v-bind('windowSizeInCssForRef'));
  525. font-size: calc(16 / v-bind('windowSizeWhenDesignForRef') * v-bind('windowSizeInCssForRef'));
  526. font-family: Source Han Sans SC, Source Han Sans SC;
  527. font-weight: 400;
  528. color: rgba(0, 0, 0, 0.5);
  529. line-height: calc(19 / v-bind('windowSizeWhenDesignForRef') * v-bind('windowSizeInCssForRef'));
  530. overflow: hidden;
  531. white-space: pre;
  532. text-overflow: ellipsis;
  533. }
  534. .price{
  535. margin-top: calc(10 / v-bind('windowSizeWhenDesignForRef') * v-bind('windowSizeInCssForRef'));
  536. >.number{
  537. font-family: Source Han Sans CN, Source Han Sans CN;
  538. font-weight: bold;
  539. font-size: calc(28 / v-bind('windowSizeWhenDesignForRef') * v-bind('windowSizeInCssForRef'));
  540. color: #589498;
  541. line-height: calc(33 / v-bind('windowSizeWhenDesignForRef') * v-bind('windowSizeInCssForRef'));
  542. }
  543. >.unit{
  544. font-family: Source Han Sans CN, Source Han Sans CN;
  545. font-weight: 400;
  546. font-size: calc(16 / v-bind('windowSizeWhenDesignForRef') * v-bind('windowSizeInCssForRef'));
  547. color: #424A4A;
  548. line-height: calc(19 / v-bind('windowSizeWhenDesignForRef') * v-bind('windowSizeInCssForRef'));
  549. margin-left: 0.5em;
  550. }
  551. }
  552. img.icon-enabled{
  553. position: absolute;
  554. right: calc(29 / v-bind('windowSizeWhenDesignForRef') * v-bind('windowSizeInCssForRef'));
  555. bottom: calc(20 / v-bind('windowSizeWhenDesignForRef') * v-bind('windowSizeInCssForRef'));
  556. width: calc(57 / v-bind('windowSizeWhenDesignForRef') * v-bind('windowSizeInCssForRef'));
  557. height: calc(57 / v-bind('windowSizeWhenDesignForRef') * v-bind('windowSizeInCssForRef'));
  558. &.redeemed {
  559. width: calc(80 / v-bind('windowSizeWhenDesignForRef') * v-bind('windowSizeInCssForRef'));
  560. height: calc(80 / v-bind('windowSizeWhenDesignForRef') * v-bind('windowSizeInCssForRef'));
  561. }
  562. }
  563. }
  564. >article.prize-item.disabled{
  565. pointer-events: none;
  566. }
  567. }
  568. >button.next-page{
  569. width: 58px;
  570. height: 58px;
  571. background-image: url(@/assets/images/icon_arrow-right-round.png);
  572. background-size: cover;
  573. background-repeat: no-repeat;
  574. background-position: center center;
  575. &.hide {
  576. pointer-events: none;
  577. opacity: 0;
  578. }
  579. }
  580. .ranking-sort {
  581. position: relative;
  582. font-family: 'Alibaba PuHuiTi-Bold' !important;
  583. &.is-first {
  584. color: #70581A !important;
  585. &::after {
  586. content: '';
  587. position: absolute;
  588. top: 50%;
  589. left: 50%;
  590. width: 36px;
  591. height: 36px;
  592. background: url('@/assets/images/icon_winner@2x-min.png') no-repeat center / contain;
  593. transform: translate(-50%, -50%);
  594. }
  595. }
  596. span {
  597. position: relative;
  598. z-index: 1;
  599. }
  600. }
  601. .ranking-name {
  602. display: flex !important;
  603. align-items: center;
  604. justify-content: center;
  605. gap: 17px;
  606. &__avatar {
  607. width: 36px;
  608. height: 36px;
  609. border: 2px solid white;
  610. border-radius: 50%;
  611. }
  612. }
  613. .ranking-content{
  614. flex: 1 0 1px;
  615. height: 100%;
  616. margin: 25px;
  617. display: flex;
  618. flex-direction: column;
  619. align-items: center;
  620. .table{
  621. margin-top: 14px;
  622. flex: 1 0 1px;
  623. width: 100%;
  624. margin-bottom: 43px;
  625. background: linear-gradient( 180deg, rgba(255,255,255,0.6) 0%, rgba(255,255,255,0) 100%);
  626. box-shadow: 15px 15px 38px 0px rgba(255,255,255,0.5);
  627. border-radius: 8px 8px 8px 8px;
  628. display: flex;
  629. flex-direction: column;
  630. >.row-header{
  631. flex: 0 0 auto;
  632. height: 72px;
  633. display: flex;
  634. align-items: center;
  635. margin-right: 16px;
  636. >div{
  637. display: inline-block;
  638. width: 33.3333%;
  639. text-align: center;
  640. font-family: Source Han Sans CN, Source Han Sans CN;
  641. font-weight: bold;
  642. font-size: 20px;
  643. color: #424A4A;
  644. line-height: 23px;
  645. }
  646. }
  647. .table-content{
  648. flex: 1 0 1px;
  649. overflow: auto;
  650. margin-right: 10px;
  651. >.row-data{
  652. height: 68px;
  653. display: flex;
  654. align-items: center;
  655. &.is-me {
  656. background: linear-gradient( 90deg, rgba(88,148,152,0.1) 0%, #589498 50%, rgba(88,148,152,0.1) 100%);
  657. }
  658. >div{
  659. display: inline-block;
  660. width: 33.3333%;
  661. text-align: center;
  662. font-family: Source Han Sans CN, Source Han Sans CN;
  663. font-weight: 400;
  664. font-size: 20px;
  665. color: #424A4A;
  666. line-height: 23px;
  667. opacity: 0.8;
  668. }
  669. }
  670. }
  671. }
  672. }
  673. >.redeem-record-content{
  674. flex: 1 0 1px;
  675. height: 100%;
  676. margin: 25px;
  677. display: flex;
  678. flex-direction: column;
  679. align-items: center;
  680. >p.tip{
  681. margin-top: 23px;
  682. font-family: Source Han Sans CN, Source Han Sans CN;
  683. font-weight: 400;
  684. font-size: 16px;
  685. color: #589498;
  686. line-height: 19px;
  687. }
  688. >.table{
  689. margin-top: 14px;
  690. flex: 1 0 1px;
  691. width: 100%;
  692. margin-bottom: 43px;
  693. background: linear-gradient( 180deg, rgba(255,255,255,0.6) 0%, rgba(255,255,255,0) 100%);
  694. box-shadow: 15px 15px 38px 0px rgba(255,255,255,0.5);
  695. border-radius: 8px 8px 8px 8px;
  696. display: flex;
  697. flex-direction: column;
  698. >.row-header{
  699. flex: 0 0 auto;
  700. height: 72px;
  701. display: flex;
  702. align-items: center;
  703. margin-right: 16px;
  704. >div{
  705. display: inline-block;
  706. width: 25%;
  707. text-align: center;
  708. font-family: Source Han Sans CN, Source Han Sans CN;
  709. font-weight: bold;
  710. font-size: 20px;
  711. color: #424A4A;
  712. line-height: 23px;
  713. }
  714. }
  715. >.splitter{
  716. width: calc(100% - 30px * 2);
  717. height: 1px;
  718. background-color: #B5C2B9;
  719. margin-left: auto;
  720. margin-right: auto;
  721. }
  722. >.table-content{
  723. flex: 1 0 1px;
  724. overflow: auto;
  725. margin-right: 10px;
  726. >.row-data{
  727. height: 68px;
  728. display: flex;
  729. align-items: center;
  730. >div{
  731. display: inline-block;
  732. width: 25%;
  733. text-align: center;
  734. font-family: Source Han Sans CN, Source Han Sans CN;
  735. font-weight: 400;
  736. font-size: 20px;
  737. color: #424A4A;
  738. line-height: 23px;
  739. opacity: 0.8;
  740. }
  741. }
  742. }
  743. }
  744. }
  745. >.coin-number{
  746. position: absolute;
  747. left: 0;
  748. bottom: 43px;
  749. width: 266px;
  750. height: 56px;
  751. background: linear-gradient( 90deg, #589498 0%, rgba(88,148,152,0) 100%);
  752. display: flex;
  753. justify-content: center;
  754. align-items: center;
  755. >img.icon{
  756. position: relative;
  757. top: -15px;
  758. width: 59px;
  759. height: 59px;
  760. margin-right: 20px;
  761. }
  762. >.value{
  763. font-family: Source Han Sans CN, Source Han Sans CN;
  764. font-weight: bold;
  765. font-size: 28px;
  766. line-height: 33px;
  767. color: #fff;
  768. margin-bottom: 10px;
  769. text-shadow: 0px 1px 1px rgba(0,0,0,0.25);
  770. }
  771. }
  772. .cert-view {
  773. position: absolute;
  774. left: 0;
  775. top: 0;
  776. width: 100%;
  777. height: 100%;
  778. background-color: rgba(0, 0, 0, 0.5);
  779. backdrop-filter: blur(10px);
  780. >img.cert-img{
  781. position: absolute;
  782. left: 50%;
  783. top: 50%;
  784. transform: translate(-50%, -50%);
  785. width: 80%;
  786. height: 80%;
  787. object-fit: contain;
  788. }
  789. >a.btn-download{
  790. display: none;
  791. position: absolute;
  792. left: 50%;
  793. transform: translateX(-50%);
  794. bottom: 40px;
  795. font-family: Source Han Sans CN, Source Han Sans CN;
  796. font-weight: bold;
  797. font-size: 20px;
  798. color: #000;
  799. }
  800. >button.close{
  801. position: absolute;
  802. top: 20px;
  803. right: 20px;
  804. width: 50px;
  805. height: 50px;
  806. background-image: url(@/assets/images/icon_close.png);
  807. background-size: cover;
  808. background-repeat: no-repeat;
  809. background-position: center center;
  810. }
  811. }
  812. }
  813. .cert-canvas {
  814. position: absolute;
  815. top: calc(-200% - 1920px);
  816. left: calc(-200% - 2880px);
  817. width: 1336px;
  818. height: 919px;
  819. background: url('@/assets/images/cert-min.png') no-repeat center / contain;
  820. p:first-child {
  821. position: absolute;
  822. top: calc(50% - 8px);
  823. left: 50%;
  824. color: #CFC49E;
  825. font-size: 70px;
  826. letter-spacing: 5px;
  827. font-family: 'Source Han Sans CN-Bold';
  828. transform: translate(-50%, -50%);
  829. }
  830. p:last-child {
  831. position: absolute;
  832. left: 352px;
  833. bottom: 155px;
  834. font-size: 20px;
  835. }
  836. }
  837. </style>