PosterPage.vue 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150
  1. <template>
  2. <ion-modal :is-open="show" ref="modalRef" class="open-modal" @didDismiss="dismiss">
  3. <div class="modal-box">
  4. <img :src="posterUrl" alt="" />
  5. <div class="btns">
  6. <van-button
  7. class="copy"
  8. @click="copyText(shareUrl)"
  9. round
  10. color="linear-gradient(180deg, #FFD1D8 , #FFEAD0 )"
  11. >複製連結</van-button
  12. >
  13. <van-button class="save" @click="save" round color="linear-gradient(180deg, #FF7340 , #FF3E3E )"
  14. >保存圖片</van-button
  15. >
  16. </div>
  17. </div>
  18. </ion-modal>
  19. </template>
  20. <script setup>
  21. import { ref, onMounted, computed } from 'vue'
  22. import VueQrcode from '@chenfengyuan/vue-qrcode'
  23. import qrcode from 'qrcode'
  24. import { Capacitor } from '@capacitor/core'
  25. import { useUserStore } from '../stores/user'
  26. import shareImg from '@/assets/png-yaoqinghaoyou-tanchuang.png'
  27. import { Clipboard as NativeClipboard } from '@capacitor/clipboard'
  28. import { useClipboard } from '@vueuse/core'
  29. import toast from '@/utils/toast'
  30. import { Filesystem, Directory, Encoding } from '@capacitor/filesystem'
  31. import { Media } from '@capacitor-community/media'
  32. const show = ref(false)
  33. function dismiss() {
  34. show.value = false
  35. }
  36. const { user } = useUserStore()
  37. async function getInviteUrl() {}
  38. const shareUrl = ref('')
  39. const posterUrl = ref('')
  40. async function onOpenInviteModal() {
  41. let qrImg = new Image()
  42. let bgImg = new Image()
  43. const url = await getInviteUrl()
  44. shareUrl.value = url
  45. function loadQR() {
  46. return new Promise((resolve, reject) => {
  47. qrcode.toDataURL(url, { width: 335, height: 335, margin: 0 }, function (err, url) {
  48. if (err) {
  49. reject(err)
  50. }
  51. qrImg.onload = () => {
  52. resolve()
  53. }
  54. qrImg.src = url
  55. })
  56. })
  57. }
  58. function loadBg() {
  59. return new Promise((resolve, reject) => {
  60. bgImg.onload = () => {
  61. resolve()
  62. }
  63. bgImg.src = shareImg
  64. })
  65. }
  66. await Promise.all([loadQR(), loadBg()])
  67. let canvas = document.createElement('canvas')
  68. canvas.width = 900
  69. canvas.height = 1200
  70. let ctx = canvas.getContext('2d')
  71. ctx.drawImage(bgImg, 0, 0)
  72. ctx.drawImage(qrImg, 230, 420, 450, 450)
  73. let dataUrl = canvas.toDataURL('image/png')
  74. posterUrl.value = dataUrl
  75. show.value = true
  76. }
  77. defineExpose({ onOpenInviteModal })
  78. const { copy } = useClipboard({ legacy: true })
  79. async function copyText(text) {
  80. if (Capacitor.isNativePlatform()) {
  81. await NativeClipboard.write({
  82. string: text
  83. })
  84. } else {
  85. copy(text)
  86. }
  87. toast.success('复制成功')
  88. }
  89. async function save() {
  90. if (Capacitor.isNativePlatform()) {
  91. //
  92. try {
  93. const savedFile = await Filesystem.writeFile({
  94. path: 'share.png',
  95. data: posterUrl.value,
  96. directory: Directory.Data
  97. })
  98. await Media.savePhoto({
  99. path: savedFile.uri,
  100. album: 'FirstCash'
  101. })
  102. toast.success('保存成功')
  103. } catch (e) {
  104. console.log(e)
  105. }
  106. } else {
  107. var link = document.createElement('a')
  108. link.setAttribute('download', 'Share.png')
  109. link.setAttribute('href', posterUrl.value.replace('image/png', 'image/octet-stream'))
  110. link.click()
  111. }
  112. }
  113. </script>
  114. <style lang="less" scoped>
  115. .open-modal {
  116. --height: fit-content;
  117. --border-radius: 8px;
  118. --width: 300px;
  119. --background: transparent;
  120. }
  121. .modal-box {
  122. position: relative;
  123. .btns {
  124. display: flex;
  125. justify-content: space-between;
  126. padding: 40px 0;
  127. .copy {
  128. width: 100px;
  129. color: #ff4034 !important;
  130. font-family: AlimamaShuHeiTi;
  131. text-shadow: 0px 2px 1px #ffffff;
  132. }
  133. .save {
  134. width: 184px;
  135. font-family: AlimamaShuHeiTi;
  136. text-shadow: 0px 2px 1px #e71d0c;
  137. }
  138. }
  139. }
  140. </style>