index.vue 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399
  1. <script lang="ts" setup>
  2. import { storeToRefs } from 'pinia'
  3. import { ref } from 'vue'
  4. import CustomNavigationBar from '@/components/CustomNavigationBar.vue'
  5. import { useInviteCodeStore } from '@/store/inviteQCCode'
  6. import { useInviterStore } from '@/store/inviter'
  7. import { safeAreaInsets } from '@/utils'
  8. import { getImageUrl } from '@/utils/imageUtil'
  9. definePage({
  10. style: {
  11. navigationBarTitleText: '我的邀请人',
  12. navigationStyle: 'custom',
  13. },
  14. })
  15. const inviteCodeStore = useInviteCodeStore()
  16. const inviterStore = useInviterStore()
  17. const { inviter: inviterInfo } = storeToRefs(inviterStore)
  18. const title = ref('掌柜369')
  19. const descText = ref('天天领券,笔笔省!吃喝玩乐,优惠随身!')
  20. const telephoneText = ref('400-633-3016')
  21. const qrCode = ref('')
  22. async function drawQRCode() {
  23. const qrCodeImg = await inviteCodeStore.getInviteCode()
  24. if (qrCodeImg) {
  25. try {
  26. let imgSrc = qrCodeImg
  27. let tempFilePath = ''
  28. // 检查是否已经是临时文件路径
  29. const isTempFile = typeof qrCodeImg === 'string' && !qrCodeImg.startsWith('data:image')
  30. // 如果是临时文件路径,直接使用
  31. if (isTempFile) {
  32. tempFilePath = qrCodeImg
  33. }
  34. else {
  35. // 处理base64图片数据
  36. if (typeof qrCodeImg === 'string') {
  37. // 安全地尝试解码URI,避免URIError
  38. try {
  39. if (qrCodeImg.includes('%')) {
  40. imgSrc = decodeURIComponent(qrCodeImg)
  41. }
  42. }
  43. catch (uriError) {
  44. console.warn('URI解码失败,使用原始数据:', uriError)
  45. }
  46. // 确保base64数据有正确的前缀
  47. if (!imgSrc.startsWith('data:image')) {
  48. imgSrc = `data:image/png;base64,${imgSrc}`
  49. }
  50. }
  51. // 尝试使用临时文件方式
  52. try {
  53. // #ifdef MP-WEIXIN
  54. const fs = uni.getFileSystemManager()
  55. const tempFileName = `temp_qr_${Date.now()}.png`
  56. tempFilePath = `${uni.env.USER_DATA_PATH}/${tempFileName}`
  57. // 提取base64数据部分
  58. let base64Data = imgSrc
  59. if (imgSrc.includes('base64,')) {
  60. base64Data = imgSrc.split('base64,')[1]
  61. }
  62. fs.writeFileSync(tempFilePath, base64Data, 'base64')
  63. console.log('Base64已转换为临时文件:', tempFilePath)
  64. // #endif
  65. }
  66. catch (fileError) {
  67. console.warn('转换base64为临时文件失败,尝试直接使用base64:', fileError)
  68. // 清除临时文件路径,确保使用base64
  69. tempFilePath = ''
  70. }
  71. }
  72. qrCode.value = tempFilePath || imgSrc
  73. console.log('二维码绘制成功')
  74. }
  75. catch (error) {
  76. console.error('绘制二维码失败:', error)
  77. // 增加更详细的错误日志
  78. if (typeof qrCodeImg === 'string') {
  79. console.log('二维码数据源类型:', qrCodeImg.startsWith('data:image') ? 'base64' : '临时文件路径')
  80. }
  81. }
  82. }
  83. }
  84. onShow(async () => {
  85. await drawQRCode()
  86. })
  87. // 保存图片到相册
  88. async function handleSaveImage() {
  89. if (!qrCode.value) {
  90. uni.showToast({
  91. title: '请先生成分享图片',
  92. icon: 'none',
  93. })
  94. return
  95. }
  96. uni.showLoading({
  97. title: '保存中...',
  98. mask: true,
  99. })
  100. try {
  101. await uni.saveImageToPhotosAlbum({
  102. filePath: qrCode.value,
  103. })
  104. uni.hideLoading()
  105. uni.showToast({
  106. title: '保存成功',
  107. icon: 'success',
  108. })
  109. }
  110. catch (error) {
  111. uni.hideLoading()
  112. console.error('保存失败:', error)
  113. if (error.errMsg && error.errMsg.includes('auth deny')) {
  114. uni.showModal({
  115. title: '提示',
  116. content: '需要您授权保存到相册',
  117. confirmText: '去设置',
  118. success: (res) => {
  119. if (res.confirm) {
  120. uni.openSetting()
  121. }
  122. }
  123. })
  124. }
  125. else {
  126. uni.showToast({
  127. title: '保存失败,请重试',
  128. icon: 'none',
  129. })
  130. }
  131. }
  132. }
  133. </script>
  134. <template>
  135. <view class="my-inviter-container"
  136. :style="{ backgroundImage: `url(${getImageUrl('@img/me/myInviter-card-bg.png')})`, paddingTop: `calc(${safeAreaInsets.top}px + 88rpx)`, height: `calc(100vh - ${safeAreaInsets.top}px - 88rpx)` }">
  137. <custom-navigation-bar :show-back="true" title="我的邀请人" back-icon-color="#ffffff" text-color="#ffffff"
  138. background-color="transparent" :is-shadow="false" />
  139. <view />
  140. <view class="inviter-content" :style="{ backgroundImage: `url(${getImageUrl('@img/me/myInviter-bg.png')})` }">
  141. <view class="my-inviter-content">
  142. <view class="title">
  143. <view class="icon" />
  144. <text class="text">我的邀请人</text>
  145. </view>
  146. <view class="content">
  147. <up-image :src="inviterInfo.avatarUrl" width="114rpx" height="114rpx" shape="circle" />
  148. <view class="inviter-name">
  149. <text class="text">{{ inviterInfo.nickname }}</text>
  150. </view>
  151. </view>
  152. </view>
  153. <view class="my-qcCode">
  154. <view class="title">
  155. <view class="icon" />
  156. <text class="text">我的邀请码</text>
  157. </view>
  158. <view class="qcCode-content">
  159. <view class="qcCode-image">
  160. <image :src="qrCode" class="qcCode" />
  161. </view>
  162. <view class="qcCode-title">
  163. 邀请码
  164. </view>
  165. <view class="qcCode-desc">
  166. 发放优惠券、分享得佣金,下单尽享优惠,快来<br>邀请好友吧!
  167. </view>
  168. </view>
  169. </view>
  170. <view class="invite-button">
  171. <up-button class="btn" shape="circle" @tap="handleSaveImage">
  172. 保存到相册
  173. </up-button>
  174. </view>
  175. </view>
  176. <view class="bottom-content">
  177. <view class="logo">
  178. <image :src="getImageUrl('@img/index/logo.jpg')" class="logo-image" />
  179. <view class="logo-title-text">
  180. {{ title }}
  181. </view>
  182. </view>
  183. <view class="desc-text">
  184. {{ descText }}
  185. </view>
  186. <view class="telephone">
  187. <view class="telephone-text">
  188. <up-icon name="phone-fill" color="#974542" size="24rpx" />
  189. <text class="text">客服热线:{{ telephoneText }}</text>
  190. </view>
  191. </view>
  192. </view>
  193. </view>
  194. </template>
  195. <style lang="scss" scoped>
  196. .my-inviter-container {
  197. width: 100vw;
  198. background-repeat: repeat-y;
  199. background-position: right;
  200. position: relative;
  201. display: flex;
  202. flex-direction: column;
  203. align-items: center;
  204. .inviter-content {
  205. width: 634rpx;
  206. height: 943rpx;
  207. background-repeat: no-repeat;
  208. background-size: cover;
  209. margin-top: 61rpx;
  210. display: flex;
  211. flex-direction: column;
  212. .my-inviter-content {
  213. height: 194rpx;
  214. display: flex;
  215. flex-direction: column;
  216. .content {
  217. flex: 1;
  218. display: flex;
  219. flex-direction: row;
  220. flex-wrap: nowrap;
  221. padding-left: 47rpx;
  222. align-items: center;
  223. gap: 21rpx;
  224. .inviter-name {
  225. font-weight: bolder;
  226. font-size: 26rpx;
  227. color: #333333;
  228. }
  229. }
  230. }
  231. .my-qcCode {
  232. flex: 1;
  233. display: flex;
  234. flex-direction: column;
  235. .qcCode-content {
  236. flex: 1;
  237. display: flex;
  238. flex-direction: column;
  239. justify-content: space-around;
  240. align-items: center;
  241. .qcCode-image {
  242. width: 317rpx;
  243. height: 313rpx;
  244. margin-bottom: 53rpx;
  245. .qcCode {
  246. width: 100%;
  247. height: 100%;
  248. object-fit: cover;
  249. }
  250. }
  251. .qcCode-title {
  252. font-weight: 400;
  253. font-size: 26rpx;
  254. color: #333333;
  255. margin-bottom: 9rpx;
  256. }
  257. .qcCode-desc {
  258. font-weight: 400;
  259. font-size: 22rpx;
  260. color: #979797;
  261. text-align: center;
  262. }
  263. }
  264. }
  265. .invite-button {
  266. height: 145rpx;
  267. display: flex;
  268. justify-content: center;
  269. align-items: center;
  270. .btn {
  271. width: 262rpx;
  272. height: 70rpx;
  273. }
  274. }
  275. .title {
  276. display: flex;
  277. flex-direction: row;
  278. flex-wrap: nowrap;
  279. gap: 16rpx;
  280. padding: 20rpx 26rpx;
  281. .icon {
  282. width: 6rpx;
  283. height: 33rpx;
  284. background: #ed6b66;
  285. border-radius: 3rpx;
  286. }
  287. .text {
  288. height: 25rpx;
  289. font-weight: bold;
  290. font-size: 26rpx;
  291. color: #333333;
  292. }
  293. }
  294. }
  295. .bottom-content {
  296. position: absolute;
  297. width: 100%;
  298. bottom: 0;
  299. left: 0;
  300. right: 0;
  301. padding: 30rpx 40rpx;
  302. box-sizing: border-box;
  303. display: flex;
  304. flex-direction: column;
  305. align-items: center;
  306. justify-content: center;
  307. gap: 14rpx;
  308. .logo {
  309. display: flex;
  310. flex-direction: row;
  311. flex-wrap: nowrap;
  312. justify-content: center;
  313. align-items: center;
  314. gap: 16rpx;
  315. .logo-image {
  316. width: 80rpx;
  317. height: 80rpx;
  318. border-radius: 20rpx;
  319. border: 2rpx solid #ffffff;
  320. box-shadow: 0rpx 0rpx 5rpx 0rpx rgba(175, 175, 175, 0.39);
  321. }
  322. .logo-title-text {
  323. font-weight: bold;
  324. font-size: 24rpx;
  325. color: #974542;
  326. }
  327. }
  328. .desc-text {
  329. font-weight: 400;
  330. font-size: 24rpx;
  331. color: #974542;
  332. }
  333. .telephone {
  334. width: 414rpx;
  335. height: 40rpx;
  336. border-radius: 19rpx;
  337. border: 1px solid #974542;
  338. padding: 9rpx 68rpx;
  339. .telephone-text {
  340. display: flex;
  341. flex-direction: row;
  342. flex-wrap: nowrap;
  343. justify-content: center;
  344. align-items: center;
  345. .text {
  346. margin-left: 6rpx;
  347. font-weight: 400;
  348. font-size: 24rpx;
  349. color: #974542;
  350. }
  351. }
  352. }
  353. }
  354. }
  355. </style>