Compare commits
	
		
			3 Commits
		
	
	
		
			ceaaf8c81e
			...
			097dfd1939
		
	
	| Author | SHA1 | Date | 
|---|---|---|
| 
							
							
								 | 
						097dfd1939 | |
| 
							
							
								 | 
						c330e4e12e | |
| 
							
							
								 | 
						998396fc31 | 
							
								
								
									
										2
									
								
								App.vue
								
								
								
								
							
							
						
						
									
										2
									
								
								App.vue
								
								
								
								
							| 
						 | 
					@ -1,5 +1,7 @@
 | 
				
			||||||
<script>
 | 
					<script>
 | 
				
			||||||
 | 
					// import VConsole from 'vconsole'
 | 
				
			||||||
import { setToken } from '@/config/auth.js'
 | 
					import { setToken } from '@/config/auth.js'
 | 
				
			||||||
 | 
					// const vConsole = new VConsole()
 | 
				
			||||||
export default {
 | 
					export default {
 | 
				
			||||||
  onLaunch: function (options) {
 | 
					  onLaunch: function (options) {
 | 
				
			||||||
    // 分享注册
 | 
					    // 分享注册
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,320 @@
 | 
				
			||||||
 | 
					<template>
 | 
				
			||||||
 | 
					  <view class="default-share-page">
 | 
				
			||||||
 | 
					    <!-- 背景图片,替代CSS background -->
 | 
				
			||||||
 | 
					    <image
 | 
				
			||||||
 | 
					      class="share-bg-image"
 | 
				
			||||||
 | 
					      src="/static/images/share-bg.jpg"
 | 
				
			||||||
 | 
					      mode="scaleToFill"
 | 
				
			||||||
 | 
					      crossorigin="anonymous"
 | 
				
			||||||
 | 
					      @load="onBackgroundImageLoad"
 | 
				
			||||||
 | 
					      @error="onBackgroundImageError"
 | 
				
			||||||
 | 
					    ></image>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <view class="share-wrapper">
 | 
				
			||||||
 | 
					      <view class="portal-frame" :class="{ 'is-loaded': isLoaded }">
 | 
				
			||||||
 | 
					        <!-- 二维码样式区域 - 用户自定义样式位置 -->
 | 
				
			||||||
 | 
					        <view class="qr-code-outer">
 | 
				
			||||||
 | 
					          <image
 | 
				
			||||||
 | 
					            class="qr-code"
 | 
				
			||||||
 | 
					            :src="qrCodeImage"
 | 
				
			||||||
 | 
					            mode="aspectFit"
 | 
				
			||||||
 | 
					            v-if="qrCodeImage"
 | 
				
			||||||
 | 
					          ></image>
 | 
				
			||||||
 | 
					          <view v-else class="qr-code-placeholder">
 | 
				
			||||||
 | 
					            <view class="loader"></view>
 | 
				
			||||||
 | 
					          </view>
 | 
				
			||||||
 | 
					        </view>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        <!-- 会员编号样式区域 - 用户自定义样式位置 -->
 | 
				
			||||||
 | 
					        <text
 | 
				
			||||||
 | 
					          class="member-code-text"
 | 
				
			||||||
 | 
					          style="
 | 
				
			||||||
 | 
					            font-size: 30rpx;
 | 
				
			||||||
 | 
					            color: #fff;
 | 
				
			||||||
 | 
					            font-weight: bold;
 | 
				
			||||||
 | 
					            margin-top: 20rpx;
 | 
				
			||||||
 | 
					          "
 | 
				
			||||||
 | 
					          >{{ desensitization(userInfo.memberCode) }}</text
 | 
				
			||||||
 | 
					        >
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        <!-- 下载按钮 - 仅在非微信环境显示 -->
 | 
				
			||||||
 | 
					        <button
 | 
				
			||||||
 | 
					          v-if="!isWechat && shareButtonShow"
 | 
				
			||||||
 | 
					          class="share-button"
 | 
				
			||||||
 | 
					          @click="sharePage"
 | 
				
			||||||
 | 
					        >
 | 
				
			||||||
 | 
					          保存图片并分享
 | 
				
			||||||
 | 
					        </button>
 | 
				
			||||||
 | 
					      </view>
 | 
				
			||||||
 | 
					    </view>
 | 
				
			||||||
 | 
					  </view>
 | 
				
			||||||
 | 
					</template>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<script>
 | 
				
			||||||
 | 
					import html2canvas from 'html2canvas'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default {
 | 
				
			||||||
 | 
					  name: 'DefaultSharePage',
 | 
				
			||||||
 | 
					  props: {
 | 
				
			||||||
 | 
					    qrCodeImage: {
 | 
				
			||||||
 | 
					      type: String,
 | 
				
			||||||
 | 
					      default: '',
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    userInfo: {
 | 
				
			||||||
 | 
					      type: Object,
 | 
				
			||||||
 | 
					      default: () => ({}),
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    isWechat: {
 | 
				
			||||||
 | 
					      type: Boolean,
 | 
				
			||||||
 | 
					      default: false,
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    isLoaded: {
 | 
				
			||||||
 | 
					      type: Boolean,
 | 
				
			||||||
 | 
					      default: false,
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  data() {
 | 
				
			||||||
 | 
					    return {
 | 
				
			||||||
 | 
					      backgroundImageLoaded: false,
 | 
				
			||||||
 | 
					      shareButtonShow: true,
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  methods: {
 | 
				
			||||||
 | 
					    desensitization(str) {
 | 
				
			||||||
 | 
					      if (!str) return ''
 | 
				
			||||||
 | 
					      if (str.length <= 8) return str.slice(0, 4) + '****'
 | 
				
			||||||
 | 
					      const len = str.length - 6
 | 
				
			||||||
 | 
					      const placeholder = '*'.repeat(len)
 | 
				
			||||||
 | 
					      return str.slice(0, 4) + placeholder + str.slice(-2)
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // 背景图片加载成功
 | 
				
			||||||
 | 
					    onBackgroundImageLoad() {
 | 
				
			||||||
 | 
					      this.backgroundImageLoaded = true
 | 
				
			||||||
 | 
					      console.log('默认场景背景图片加载成功')
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // 背景图片加载失败
 | 
				
			||||||
 | 
					    onBackgroundImageError(e) {
 | 
				
			||||||
 | 
					      console.error('默认场景背景图片加载失败:', e)
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    async sharePage() {
 | 
				
			||||||
 | 
					      if (!this.qrCodeImage) {
 | 
				
			||||||
 | 
					        uni.showToast({
 | 
				
			||||||
 | 
					          title: '二维码尚未生成',
 | 
				
			||||||
 | 
					          icon: 'none',
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
 | 
					        return
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      uni.showLoading({ title: '加载中...' })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      try {
 | 
				
			||||||
 | 
					        this.shareButtonShow = false
 | 
				
			||||||
 | 
					        await this.$nextTick()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        await this.capturePageWithHtml2Canvas()
 | 
				
			||||||
 | 
					      } catch (error) {
 | 
				
			||||||
 | 
					        uni.hideLoading()
 | 
				
			||||||
 | 
					        uni.showToast({ title: '图片生成失败,请稍后重试', icon: 'none' })
 | 
				
			||||||
 | 
					        console.error('sharePage error:', error)
 | 
				
			||||||
 | 
					      } finally {
 | 
				
			||||||
 | 
					        // 恢复按钮显示
 | 
				
			||||||
 | 
					        this.shareButtonShow = true
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // 使用html2canvas截取整个页面
 | 
				
			||||||
 | 
					    async capturePageWithHtml2Canvas() {
 | 
				
			||||||
 | 
					      console.log('开始使用html2canvas截取页面')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      return new Promise((resolve, reject) => {
 | 
				
			||||||
 | 
					        // 确保所有图片都已加载完成
 | 
				
			||||||
 | 
					        const waitForImages = () => {
 | 
				
			||||||
 | 
					          if (!this.backgroundImageLoaded || !this.qrCodeImage) {
 | 
				
			||||||
 | 
					            setTimeout(waitForImages, 100)
 | 
				
			||||||
 | 
					            return
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          // 额外等待确保渲染完成
 | 
				
			||||||
 | 
					          setTimeout(() => {
 | 
				
			||||||
 | 
					            const element = this.$el
 | 
				
			||||||
 | 
					            if (!element) {
 | 
				
			||||||
 | 
					              reject(new Error('找不到组件容器'))
 | 
				
			||||||
 | 
					              return
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            console.log(
 | 
				
			||||||
 | 
					              '开始html2canvas截取,容器尺寸:',
 | 
				
			||||||
 | 
					              element.offsetWidth,
 | 
				
			||||||
 | 
					              'x',
 | 
				
			||||||
 | 
					              element.offsetHeight
 | 
				
			||||||
 | 
					            )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            html2canvas(element, {
 | 
				
			||||||
 | 
					              useCORS: true,
 | 
				
			||||||
 | 
					              allowTaint: true,
 | 
				
			||||||
 | 
					              backgroundColor: null,
 | 
				
			||||||
 | 
					              scale: 2,
 | 
				
			||||||
 | 
					              dpi: 400,
 | 
				
			||||||
 | 
					              logging: true,
 | 
				
			||||||
 | 
					              width: element.offsetWidth,
 | 
				
			||||||
 | 
					              height: element.offsetHeight,
 | 
				
			||||||
 | 
					              windowWidth: element.offsetWidth,
 | 
				
			||||||
 | 
					              windowHeight: element.offsetHeight,
 | 
				
			||||||
 | 
					              scrollX: 0,
 | 
				
			||||||
 | 
					              scrollY: 0,
 | 
				
			||||||
 | 
					              x: 0,
 | 
				
			||||||
 | 
					              y: 0,
 | 
				
			||||||
 | 
					            })
 | 
				
			||||||
 | 
					              .then(canvas => {
 | 
				
			||||||
 | 
					                const dataUrl = canvas.toDataURL('image/jpeg', 1)
 | 
				
			||||||
 | 
					                this.$emit('share-generated', dataUrl)
 | 
				
			||||||
 | 
					                resolve()
 | 
				
			||||||
 | 
					              })
 | 
				
			||||||
 | 
					              .catch(err => {
 | 
				
			||||||
 | 
					                console.error('html2canvas截取失败:', err)
 | 
				
			||||||
 | 
					                reject(err)
 | 
				
			||||||
 | 
					              })
 | 
				
			||||||
 | 
					          }, 1000)
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // 开始等待图片加载
 | 
				
			||||||
 | 
					        waitForImages()
 | 
				
			||||||
 | 
					      })
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					</script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<style lang="scss" scoped>
 | 
				
			||||||
 | 
					.default-share-page {
 | 
				
			||||||
 | 
					  position: relative;
 | 
				
			||||||
 | 
					  width: 100%;
 | 
				
			||||||
 | 
					  height: 100%;
 | 
				
			||||||
 | 
					  display: flex;
 | 
				
			||||||
 | 
					  flex-direction: column;
 | 
				
			||||||
 | 
					  align-items: center;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* 背景图片样式 */
 | 
				
			||||||
 | 
					.share-bg-image {
 | 
				
			||||||
 | 
					  position: absolute;
 | 
				
			||||||
 | 
					  top: 0;
 | 
				
			||||||
 | 
					  left: 0;
 | 
				
			||||||
 | 
					  width: 100%;
 | 
				
			||||||
 | 
					  height: 100%;
 | 
				
			||||||
 | 
					  z-index: 1;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.share-wrapper {
 | 
				
			||||||
 | 
					  position: absolute;
 | 
				
			||||||
 | 
					  z-index: 2;
 | 
				
			||||||
 | 
					  bottom: 22%;
 | 
				
			||||||
 | 
					  display: flex;
 | 
				
			||||||
 | 
					  flex-direction: column;
 | 
				
			||||||
 | 
					  align-items: center;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.portal-frame {
 | 
				
			||||||
 | 
					  padding: 32rpx;
 | 
				
			||||||
 | 
					  width: 520rpx;
 | 
				
			||||||
 | 
					  border-radius: 40rpx;
 | 
				
			||||||
 | 
					  display: flex;
 | 
				
			||||||
 | 
					  box-sizing: border-box;
 | 
				
			||||||
 | 
					  flex-direction: column;
 | 
				
			||||||
 | 
					  align-items: center;
 | 
				
			||||||
 | 
					  margin: 0 auto;
 | 
				
			||||||
 | 
					  opacity: 0;
 | 
				
			||||||
 | 
					  transform: translateY(20rpx);
 | 
				
			||||||
 | 
					  transition: all 0.3s ease;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.portal-frame.is-loaded {
 | 
				
			||||||
 | 
					  opacity: 1;
 | 
				
			||||||
 | 
					  transform: translateY(0);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* 二维码样式区域 - 用户自定义样式位置 */
 | 
				
			||||||
 | 
					.qr-code-outer {
 | 
				
			||||||
 | 
					  width: 400rpx;
 | 
				
			||||||
 | 
					  height: 400rpx;
 | 
				
			||||||
 | 
					  background: rgba(255, 255, 255, 0.98);
 | 
				
			||||||
 | 
					  border-radius: 20rpx;
 | 
				
			||||||
 | 
					  box-shadow: 0px 8rpx 20rpx rgba(50, 50, 90, 0.06);
 | 
				
			||||||
 | 
					  border: 1px solid #f0f0f0;
 | 
				
			||||||
 | 
					  display: flex;
 | 
				
			||||||
 | 
					  align-items: center;
 | 
				
			||||||
 | 
					  justify-content: center;
 | 
				
			||||||
 | 
					  padding: 12rpx;
 | 
				
			||||||
 | 
					  box-sizing: border-box;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.qr-code,
 | 
				
			||||||
 | 
					.qr-code-placeholder {
 | 
				
			||||||
 | 
					  width: 100%;
 | 
				
			||||||
 | 
					  height: 100%;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.qr-code {
 | 
				
			||||||
 | 
					  border-radius: 16rpx;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.qr-code-placeholder {
 | 
				
			||||||
 | 
					  display: flex;
 | 
				
			||||||
 | 
					  align-items: center;
 | 
				
			||||||
 | 
					  justify-content: center;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.loader {
 | 
				
			||||||
 | 
					  width: 100rpx;
 | 
				
			||||||
 | 
					  height: 100rpx;
 | 
				
			||||||
 | 
					  border: 8rpx solid rgba(0, 0, 0, 0.1);
 | 
				
			||||||
 | 
					  border-left-color: #0072ff;
 | 
				
			||||||
 | 
					  border-radius: 50%;
 | 
				
			||||||
 | 
					  animation: spin 1s linear infinite;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@keyframes spin {
 | 
				
			||||||
 | 
					  to {
 | 
				
			||||||
 | 
					    transform: rotate(360deg);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* 会员编号样式区域 - 用户自定义样式位置 */
 | 
				
			||||||
 | 
					.member-code-text {
 | 
				
			||||||
 | 
					  /* 用户可以在这里自定义会员编号的样式 */
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* 下载按钮样式 */
 | 
				
			||||||
 | 
					.share-button {
 | 
				
			||||||
 | 
					  margin-top: 28rpx;
 | 
				
			||||||
 | 
					  width: 280rpx;
 | 
				
			||||||
 | 
					  height: 72rpx;
 | 
				
			||||||
 | 
					  line-height: 72rpx;
 | 
				
			||||||
 | 
					  color: #fff;
 | 
				
			||||||
 | 
					  border-radius: 36rpx;
 | 
				
			||||||
 | 
					  font-size: 26rpx;
 | 
				
			||||||
 | 
					  font-weight: 500;
 | 
				
			||||||
 | 
					  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
 | 
				
			||||||
 | 
					  box-shadow: 0 5rpx 12rpx 0 rgba(102, 126, 234, 0.2);
 | 
				
			||||||
 | 
					  border: none;
 | 
				
			||||||
 | 
					  padding: 0;
 | 
				
			||||||
 | 
					  text-align: center;
 | 
				
			||||||
 | 
					  transition: all 0.3s ease;
 | 
				
			||||||
 | 
					  letter-spacing: 1rpx;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					button.share-button {
 | 
				
			||||||
 | 
					  padding: 0;
 | 
				
			||||||
 | 
					  line-height: 72rpx;
 | 
				
			||||||
 | 
					  border: none;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.share-button:active {
 | 
				
			||||||
 | 
					  transform: translateY(1rpx);
 | 
				
			||||||
 | 
					  box-shadow: 0 4rpx 10rpx rgba(102, 126, 234, 0.4);
 | 
				
			||||||
 | 
					  background: linear-gradient(135deg, #5a67d8 0%, #6b46c1 100%);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					</style>
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,178 @@
 | 
				
			||||||
 | 
					# 分享组件说明
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					## SpecialSharePage 组件
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					特殊场景分享页面组件,通过接口获取base64格式的背景图片。
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### 主要特性
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					- 📱 **响应式**:适配各种屏幕尺寸
 | 
				
			||||||
 | 
					- 🎨 **可定制**:支持样式自定义
 | 
				
			||||||
 | 
					- 🔄 **重试机制**:支持接口调用失败时的重试
 | 
				
			||||||
 | 
					- 🌐 **接口获取**:自动通过接口获取背景图片
 | 
				
			||||||
 | 
					- 🖼️ **Base64支持**:接口返回base64格式的背景图片
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### 使用方法
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```vue
 | 
				
			||||||
 | 
					<template>
 | 
				
			||||||
 | 
					  <SpecialSharePage
 | 
				
			||||||
 | 
					    :qrCodeImage="base64QrCodeImage"
 | 
				
			||||||
 | 
					    :userInfo="userInfo"
 | 
				
			||||||
 | 
					    :isLoaded="true"
 | 
				
			||||||
 | 
					    @share-generated="handleShareGenerated"
 | 
				
			||||||
 | 
					    @background-image-error="handleError"
 | 
				
			||||||
 | 
					    @background-image-retry="handleRetry"
 | 
				
			||||||
 | 
					  />
 | 
				
			||||||
 | 
					</template>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<script>
 | 
				
			||||||
 | 
					import SpecialSharePage from '@/components/share/SpecialSharePage.vue'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					export default {
 | 
				
			||||||
 | 
					  components: {
 | 
				
			||||||
 | 
					    SpecialSharePage,
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  data() {
 | 
				
			||||||
 | 
					    return {
 | 
				
			||||||
 | 
					      base64QrCodeImage:
 | 
				
			||||||
 | 
					        'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAQAAAAEAAQAAAADK...',
 | 
				
			||||||
 | 
					      userInfo: {
 | 
				
			||||||
 | 
					        memberCode: '12345678',
 | 
				
			||||||
 | 
					      },
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  methods: {
 | 
				
			||||||
 | 
					    handleShareGenerated(dataUrl) {
 | 
				
			||||||
 | 
					      console.log('生成的分享图片:', dataUrl)
 | 
				
			||||||
 | 
					      // 处理生成的图片
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    handleError(error) {
 | 
				
			||||||
 | 
					      console.error('背景图片错误:', error)
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    handleRetry(retryCount) {
 | 
				
			||||||
 | 
					      console.log('背景图片重试:', retryCount)
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					</script>
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Props
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					| 参数                    | 类型    | 默认值 | 说明                     |
 | 
				
			||||||
 | 
					| ----------------------- | ------- | ------ | ------------------------ |
 | 
				
			||||||
 | 
					| `qrCodeImage`           | String  | ''     | 二维码图片(base64格式) |
 | 
				
			||||||
 | 
					| `userInfo`              | Object  | {}     | 用户信息对象             |
 | 
				
			||||||
 | 
					| `isWechat`              | Boolean | false  | 是否为微信环境           |
 | 
				
			||||||
 | 
					| `isLoaded`              | Boolean | false  | 是否加载完成             |
 | 
				
			||||||
 | 
					| `skipImageVerification` | Boolean | false  | 跳过图片验证(调试用)   |
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### Events
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					| 事件名                   | 参数       | 说明             |
 | 
				
			||||||
 | 
					| ------------------------ | ---------- | ---------------- |
 | 
				
			||||||
 | 
					| `share-generated`        | dataUrl    | 分享图片生成成功 |
 | 
				
			||||||
 | 
					| `background-image-error` | error      | 背景图片加载错误 |
 | 
				
			||||||
 | 
					| `background-image-retry` | retryCount | 背景图片重试     |
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### 方法
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					| 方法名                          | 说明                                 |
 | 
				
			||||||
 | 
					| ------------------------------- | ------------------------------------ |
 | 
				
			||||||
 | 
					| `generateShareImage()`          | 生成分享图片                         |
 | 
				
			||||||
 | 
					| `retryGetBackgroundImage()`     | 手动重试获取背景图                   |
 | 
				
			||||||
 | 
					| `getStatusInfo()`               | 获取当前状态信息(调试用)           |
 | 
				
			||||||
 | 
					| `handleShareGenerated(dataUrl)` | 处理图片生成成功后的逻辑(内部方法) |
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### 接口配置
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					组件使用 `getSharedImg` 接口获取背景图片:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```javascript
 | 
				
			||||||
 | 
					// 在 config/login.js 中配置接口
 | 
				
			||||||
 | 
					export const getSharedImg = () => {
 | 
				
			||||||
 | 
					  return http.get('/api/share/background')
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					接口应返回以下格式:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```javascript
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  code: 200,
 | 
				
			||||||
 | 
					  data: 'data:image/jpeg;base64,/9j/4AAQSkZJRgABAQAAAQABAAD...',
 | 
				
			||||||
 | 
					  message: 'success'
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### 调试
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```javascript
 | 
				
			||||||
 | 
					// 生成分享图片
 | 
				
			||||||
 | 
					this.$refs.specialSharePage.generateShareImage()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// 手动重试获取背景图
 | 
				
			||||||
 | 
					this.$refs.specialSharePage.retryGetBackgroundImage()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// 获取当前状态信息
 | 
				
			||||||
 | 
					const status = this.$refs.specialSharePage.getStatusInfo()
 | 
				
			||||||
 | 
					console.log('组件状态:', status)
 | 
				
			||||||
 | 
					/*
 | 
				
			||||||
 | 
					输出示例:
 | 
				
			||||||
 | 
					{
 | 
				
			||||||
 | 
					  specialBackgroundImage: '123456 bytes',
 | 
				
			||||||
 | 
					  qrCodeImage: 'data:image/png;base64,iVBORw0KGgoAAAANSUhEU...',
 | 
				
			||||||
 | 
					  backgroundImageLoaded: true,
 | 
				
			||||||
 | 
					  isLoadingBackground: false,
 | 
				
			||||||
 | 
					  imageLoadRetryCount: 0,
 | 
				
			||||||
 | 
					  isReady: true
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					*/
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### 工作流程
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					1. **组件挂载**:自动调用 `getSharedImg` 接口获取背景图
 | 
				
			||||||
 | 
					2. **显示加载**:显示"正在获取背景图片..."状态
 | 
				
			||||||
 | 
					3. **接口响应**:处理接口返回的base64背景图数据
 | 
				
			||||||
 | 
					4. **准备就绪**:背景图和二维码都准备好后可以生成分享图片
 | 
				
			||||||
 | 
					5. **生成图片**:使用html2canvas截取页面生成分享图片
 | 
				
			||||||
 | 
					6. **处理结果**:
 | 
				
			||||||
 | 
					   - 关闭loading状态
 | 
				
			||||||
 | 
					   - 显示成功提示("图片生成成功,请长按保存")
 | 
				
			||||||
 | 
					   - 调用内部 `handleShareGenerated` 方法处理
 | 
				
			||||||
 | 
					   - 通过 `share-generated` 事件通知父组件
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### 图片生成流程
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					用户点击生成按钮
 | 
				
			||||||
 | 
					       ↓
 | 
				
			||||||
 | 
					检查前置条件(二维码、背景图)
 | 
				
			||||||
 | 
					       ↓
 | 
				
			||||||
 | 
					显示 "生成图片中..." loading
 | 
				
			||||||
 | 
					       ↓
 | 
				
			||||||
 | 
					使用 html2canvas 截取页面
 | 
				
			||||||
 | 
					       ↓
 | 
				
			||||||
 | 
					生成成功 → 关闭loading → 显示成功提示 → 发送事件
 | 
				
			||||||
 | 
					       ↓
 | 
				
			||||||
 | 
					生成失败 → 关闭loading → 显示错误提示
 | 
				
			||||||
 | 
					```
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### 错误处理
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					- **接口调用失败**:自动重试最多3次
 | 
				
			||||||
 | 
					- **重试机制**:通过 `background-image-retry` 事件通知重试状态
 | 
				
			||||||
 | 
					- **最终失败**:通过 `background-image-error` 事件通知错误
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					### 注意事项
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					1. 确保 `getSharedImg` 接口返回完整的base64格式图片
 | 
				
			||||||
 | 
					2. 组件会在mounted生命周期自动调用接口
 | 
				
			||||||
 | 
					3. 支持手动重试机制,适用于网络不稳定的情况
 | 
				
			||||||
 | 
					4. 生成的图片为JPEG格式,质量为100%
 | 
				
			||||||
 | 
					5. 组件支持长按保存分享图片
 | 
				
			||||||
 | 
					6. 接口失败时会显示相应的错误状态
 | 
				
			||||||
 | 
					7. **图片生成成功后会自动显示提示信息**,无需在父组件中重复处理
 | 
				
			||||||
 | 
					8. **组件内部和外部都会收到图片生成事件**,可根据需要在父组件中添加额外逻辑
 | 
				
			||||||
| 
						 | 
					@ -0,0 +1,509 @@
 | 
				
			||||||
 | 
					<template>
 | 
				
			||||||
 | 
					  <view ref="specialSharePage" class="special-share-page">
 | 
				
			||||||
 | 
					    <!-- 特殊场景背景图片 -->
 | 
				
			||||||
 | 
					    <image
 | 
				
			||||||
 | 
					      class="share-bg-image"
 | 
				
			||||||
 | 
					      :src="specialBackgroundImage"
 | 
				
			||||||
 | 
					      mode="scaleToFill"
 | 
				
			||||||
 | 
					      crossorigin="anonymous"
 | 
				
			||||||
 | 
					      @load="onBackgroundImageLoad"
 | 
				
			||||||
 | 
					      @error="onBackgroundImageError"
 | 
				
			||||||
 | 
					      v-if="specialBackgroundImage"
 | 
				
			||||||
 | 
					    ></image>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <!-- 加载状态 -->
 | 
				
			||||||
 | 
					    <view
 | 
				
			||||||
 | 
					      class="loading-container"
 | 
				
			||||||
 | 
					      v-if="!specialBackgroundImage || isLoadingBackground"
 | 
				
			||||||
 | 
					    >
 | 
				
			||||||
 | 
					      <view class="loader"></view>
 | 
				
			||||||
 | 
					      <text class="loading-text">{{
 | 
				
			||||||
 | 
					        isLoadingBackground ? '正在获取背景图片...' : '正在加载分享背景...'
 | 
				
			||||||
 | 
					      }}</text>
 | 
				
			||||||
 | 
					    </view>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    <view
 | 
				
			||||||
 | 
					      class="share-wrapper"
 | 
				
			||||||
 | 
					      v-if="specialBackgroundImage && !isLoadingBackground"
 | 
				
			||||||
 | 
					    >
 | 
				
			||||||
 | 
					      <view class="portal-frame" :class="{ 'is-loaded': isLoaded }">
 | 
				
			||||||
 | 
					        <view class="qr-code-outer special-qr-style">
 | 
				
			||||||
 | 
					          <image
 | 
				
			||||||
 | 
					            class="qr-code"
 | 
				
			||||||
 | 
					            :src="qrCodeImage"
 | 
				
			||||||
 | 
					            mode="aspectFit"
 | 
				
			||||||
 | 
					            v-if="qrCodeImage"
 | 
				
			||||||
 | 
					          ></image>
 | 
				
			||||||
 | 
					          <view v-else class="qr-code-placeholder">
 | 
				
			||||||
 | 
					            <view class="loader"></view>
 | 
				
			||||||
 | 
					          </view>
 | 
				
			||||||
 | 
					        </view>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        <text
 | 
				
			||||||
 | 
					          class="member-code-text special-member-code-style"
 | 
				
			||||||
 | 
					          style="
 | 
				
			||||||
 | 
					            font-size: 30rpx;
 | 
				
			||||||
 | 
					            color: #005bac;
 | 
				
			||||||
 | 
					            font-weight: bold;
 | 
				
			||||||
 | 
					            margin-top: 20rpx;
 | 
				
			||||||
 | 
					          "
 | 
				
			||||||
 | 
					          >{{ desensitization(userInfo.memberCode) }}</text
 | 
				
			||||||
 | 
					        >
 | 
				
			||||||
 | 
					      </view>
 | 
				
			||||||
 | 
					    </view>
 | 
				
			||||||
 | 
					  </view>
 | 
				
			||||||
 | 
					</template>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<script>
 | 
				
			||||||
 | 
					import { snapdom } from '@zumer/snapdom'
 | 
				
			||||||
 | 
					import html2canvas from 'html2canvas'
 | 
				
			||||||
 | 
					import { getSharedImg } from '@/config/login'
 | 
				
			||||||
 | 
					export default {
 | 
				
			||||||
 | 
					  name: 'SpecialSharePage',
 | 
				
			||||||
 | 
					  props: {
 | 
				
			||||||
 | 
					    qrCodeImage: {
 | 
				
			||||||
 | 
					      type: String,
 | 
				
			||||||
 | 
					      default: '',
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    userInfo: {
 | 
				
			||||||
 | 
					      type: Object,
 | 
				
			||||||
 | 
					      default: () => ({}),
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    isWechat: {
 | 
				
			||||||
 | 
					      type: Boolean,
 | 
				
			||||||
 | 
					      default: false,
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    isLoaded: {
 | 
				
			||||||
 | 
					      type: Boolean,
 | 
				
			||||||
 | 
					      default: false,
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    // 移除specialBackgroundImage prop,改为接口获取
 | 
				
			||||||
 | 
					    // 调试用:跳过图片验证
 | 
				
			||||||
 | 
					    skipImageVerification: {
 | 
				
			||||||
 | 
					      type: Boolean,
 | 
				
			||||||
 | 
					      default: false,
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  data() {
 | 
				
			||||||
 | 
					    return {
 | 
				
			||||||
 | 
					      backgroundImageLoaded: false,
 | 
				
			||||||
 | 
					      imageLoadRetryCount: 0,
 | 
				
			||||||
 | 
					      maxRetryCount: 3,
 | 
				
			||||||
 | 
					      specialBackgroundImage: '', // 存储接口返回的背景图
 | 
				
			||||||
 | 
					      isLoadingBackground: false, // 是否正在加载背景图
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  created() {
 | 
				
			||||||
 | 
					    this.getBackgroundImage()
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					  methods: {
 | 
				
			||||||
 | 
					    desensitization(str) {
 | 
				
			||||||
 | 
					      if (!str) return ''
 | 
				
			||||||
 | 
					      if (str.length <= 8) return str.slice(0, 4) + '****'
 | 
				
			||||||
 | 
					      const len = str.length - 6
 | 
				
			||||||
 | 
					      const placeholder = '*'.repeat(len)
 | 
				
			||||||
 | 
					      return str.slice(0, 4) + placeholder + str.slice(-2)
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // 背景图片加载成功
 | 
				
			||||||
 | 
					    onBackgroundImageLoad() {
 | 
				
			||||||
 | 
					      this.backgroundImageLoaded = true
 | 
				
			||||||
 | 
					      console.log('特殊场景背景图片加载成功')
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // 背景图片加载失败
 | 
				
			||||||
 | 
					    onBackgroundImageError(e) {
 | 
				
			||||||
 | 
					      console.error('特殊场景背景图片加载失败:', e)
 | 
				
			||||||
 | 
					      this.backgroundImageLoaded = false
 | 
				
			||||||
 | 
					      this.imageLoadRetryCount++
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      // 如果重试次数未达到上限,可以通知父组件重试
 | 
				
			||||||
 | 
					      if (this.imageLoadRetryCount < this.maxRetryCount) {
 | 
				
			||||||
 | 
					        console.log(
 | 
				
			||||||
 | 
					          `图片加载失败,重试中 (${this.imageLoadRetryCount}/${this.maxRetryCount})`
 | 
				
			||||||
 | 
					        )
 | 
				
			||||||
 | 
					        this.$emit('background-image-retry', this.imageLoadRetryCount)
 | 
				
			||||||
 | 
					      } else {
 | 
				
			||||||
 | 
					        this.$emit('background-image-error', e)
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    // 处理分享图片生成完成
 | 
				
			||||||
 | 
					    handleShareGenerated(dataUrl) {
 | 
				
			||||||
 | 
					      uni.showToast({
 | 
				
			||||||
 | 
					        title: '图片生成成功,请长按保存',
 | 
				
			||||||
 | 
					        icon: 'success',
 | 
				
			||||||
 | 
					        duration: 2000,
 | 
				
			||||||
 | 
					      })
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // 获取特殊场景背景图片
 | 
				
			||||||
 | 
					    async getBackgroundImage() {
 | 
				
			||||||
 | 
					      if (this.skipImageVerification) {
 | 
				
			||||||
 | 
					        this.specialBackgroundImage = '' // 跳过验证时清空背景图
 | 
				
			||||||
 | 
					        this.backgroundImageLoaded = true
 | 
				
			||||||
 | 
					        return
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      if (this.specialBackgroundImage) {
 | 
				
			||||||
 | 
					        // 如果背景图已加载,则直接使用
 | 
				
			||||||
 | 
					        this.backgroundImageLoaded = true
 | 
				
			||||||
 | 
					        return
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      this.isLoadingBackground = true
 | 
				
			||||||
 | 
					      try {
 | 
				
			||||||
 | 
					        const result = await getSharedImg()
 | 
				
			||||||
 | 
					        if (result && result.code === 200 && result.data) {
 | 
				
			||||||
 | 
					          this.specialBackgroundImage =
 | 
				
			||||||
 | 
					            'data:image/png;base64,' + result.data.base64
 | 
				
			||||||
 | 
					          this.backgroundImageLoaded = true
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					          this.backgroundImageLoaded = false
 | 
				
			||||||
 | 
					          this.imageLoadRetryCount++
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      } catch (error) {
 | 
				
			||||||
 | 
					        this.backgroundImageLoaded = false
 | 
				
			||||||
 | 
					        this.imageLoadRetryCount++
 | 
				
			||||||
 | 
					        if (this.imageLoadRetryCount < this.maxRetryCount) {
 | 
				
			||||||
 | 
					          this.$emit('background-image-retry', this.imageLoadRetryCount)
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					          this.$emit('background-image-error', error)
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      } finally {
 | 
				
			||||||
 | 
					        this.isLoadingBackground = false
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					      return this.specialBackgroundImage
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    async generateShareImage() {
 | 
				
			||||||
 | 
					      // await this.getBackgroundImage()
 | 
				
			||||||
 | 
					      try {
 | 
				
			||||||
 | 
					        this.$nextTick(() => {
 | 
				
			||||||
 | 
					          const info = uni.getSystemInfoSync()
 | 
				
			||||||
 | 
					          console.log(info.platform, '.....info')
 | 
				
			||||||
 | 
					          if (info.platform === 'ios') {
 | 
				
			||||||
 | 
					            console.log('iOS detected, using html2canvas for capturing.')
 | 
				
			||||||
 | 
					            this.capturePageWithHtml2Canvas()
 | 
				
			||||||
 | 
					          } else {
 | 
				
			||||||
 | 
					            console.log('Non-iOS detected, using snapdom for capturing.')
 | 
				
			||||||
 | 
					            this.capturePageWithSnapDom()
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					        })
 | 
				
			||||||
 | 
					      } catch (error) {
 | 
				
			||||||
 | 
					        uni.hideLoading()
 | 
				
			||||||
 | 
					        uni.showToast({ title: '图片生成失败,请稍后重试', icon: 'none' })
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // 新增:使用html2canvas截取整个页面 (For iOS)
 | 
				
			||||||
 | 
					    async capturePageWithHtml2Canvas() {
 | 
				
			||||||
 | 
					      return new Promise(async (resolve, reject) => {
 | 
				
			||||||
 | 
					        // 确保所有图片都已加载完成
 | 
				
			||||||
 | 
					        const waitForImages = async () => {
 | 
				
			||||||
 | 
					          // 检查是否正在加载背景图片
 | 
				
			||||||
 | 
					          if (this.isLoadingBackground) {
 | 
				
			||||||
 | 
					            console.log('等待背景图片接口调用完成...')
 | 
				
			||||||
 | 
					            setTimeout(waitForImages, 100)
 | 
				
			||||||
 | 
					            return
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          // 检查背景图片是否加载完成
 | 
				
			||||||
 | 
					          if (!this.backgroundImageLoaded) {
 | 
				
			||||||
 | 
					            console.log('等待背景图片加载完成...')
 | 
				
			||||||
 | 
					            setTimeout(waitForImages, 100)
 | 
				
			||||||
 | 
					            return
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          // 检查二维码是否存在
 | 
				
			||||||
 | 
					          if (!this.qrCodeImage) {
 | 
				
			||||||
 | 
					            console.log('等待二维码生成...')
 | 
				
			||||||
 | 
					            setTimeout(waitForImages, 100)
 | 
				
			||||||
 | 
					            return
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          // 检查特殊背景图片参数是否存在
 | 
				
			||||||
 | 
					          if (!this.specialBackgroundImage) {
 | 
				
			||||||
 | 
					            console.log('等待特殊背景图片参数...')
 | 
				
			||||||
 | 
					            setTimeout(waitForImages, 100)
 | 
				
			||||||
 | 
					            return
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          console.log('所有图片准备就绪,开始使用html2canvas截图...')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          // 等待确保渲染完成
 | 
				
			||||||
 | 
					          setTimeout(() => {
 | 
				
			||||||
 | 
					            const element = this.$el
 | 
				
			||||||
 | 
					            if (!element) {
 | 
				
			||||||
 | 
					              uni.hideLoading()
 | 
				
			||||||
 | 
					              reject(new Error('找不到组件容器'))
 | 
				
			||||||
 | 
					              return
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            html2canvas(element, {
 | 
				
			||||||
 | 
					              useCORS: true,
 | 
				
			||||||
 | 
					              allowTaint: true,
 | 
				
			||||||
 | 
					              backgroundColor: null,
 | 
				
			||||||
 | 
					              scale: 2, // 提高清晰度
 | 
				
			||||||
 | 
					              dpi: 300,
 | 
				
			||||||
 | 
					              width: element.offsetWidth,
 | 
				
			||||||
 | 
					              height: element.offsetHeight,
 | 
				
			||||||
 | 
					            })
 | 
				
			||||||
 | 
					              .then(canvas => {
 | 
				
			||||||
 | 
					                const dataUrl = canvas.toDataURL('image/jpeg', 1.0)
 | 
				
			||||||
 | 
					                uni.hideLoading()
 | 
				
			||||||
 | 
					                this.handleShareGenerated(dataUrl)
 | 
				
			||||||
 | 
					                this.$emit('share-generated', dataUrl)
 | 
				
			||||||
 | 
					                resolve()
 | 
				
			||||||
 | 
					              })
 | 
				
			||||||
 | 
					              .catch(err => {
 | 
				
			||||||
 | 
					                uni.hideLoading()
 | 
				
			||||||
 | 
					                console.error('html2canvas截取失败:', err)
 | 
				
			||||||
 | 
					                uni.showToast({
 | 
				
			||||||
 | 
					                  title: '图片生成失败,请稍后重试',
 | 
				
			||||||
 | 
					                  icon: 'none',
 | 
				
			||||||
 | 
					                })
 | 
				
			||||||
 | 
					                reject(err)
 | 
				
			||||||
 | 
					              })
 | 
				
			||||||
 | 
					          }, 1000)
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // 开始等待图片加载
 | 
				
			||||||
 | 
					        waitForImages()
 | 
				
			||||||
 | 
					      })
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // 使用snapDOM截取整个页面
 | 
				
			||||||
 | 
					    async capturePageWithSnapDom() {
 | 
				
			||||||
 | 
					      return new Promise(async (resolve, reject) => {
 | 
				
			||||||
 | 
					        // 确保所有图片都已加载完成
 | 
				
			||||||
 | 
					        const waitForImages = async () => {
 | 
				
			||||||
 | 
					          // 检查是否正在加载背景图片
 | 
				
			||||||
 | 
					          if (this.isLoadingBackground) {
 | 
				
			||||||
 | 
					            console.log('等待背景图片接口调用完成...')
 | 
				
			||||||
 | 
					            setTimeout(waitForImages, 100)
 | 
				
			||||||
 | 
					            return
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          // 检查背景图片是否加载完成
 | 
				
			||||||
 | 
					          if (!this.backgroundImageLoaded) {
 | 
				
			||||||
 | 
					            console.log('等待背景图片加载完成...')
 | 
				
			||||||
 | 
					            setTimeout(waitForImages, 100)
 | 
				
			||||||
 | 
					            return
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          // 检查二维码是否存在
 | 
				
			||||||
 | 
					          if (!this.qrCodeImage) {
 | 
				
			||||||
 | 
					            console.log('等待二维码生成...')
 | 
				
			||||||
 | 
					            setTimeout(waitForImages, 100)
 | 
				
			||||||
 | 
					            return
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          // 检查特殊背景图片参数是否存在
 | 
				
			||||||
 | 
					          if (!this.specialBackgroundImage) {
 | 
				
			||||||
 | 
					            console.log('等待特殊背景图片参数...')
 | 
				
			||||||
 | 
					            setTimeout(waitForImages, 100)
 | 
				
			||||||
 | 
					            return
 | 
				
			||||||
 | 
					          }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          console.log('所有图片准备就绪,开始截图...')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					          // 等待确保渲染完成
 | 
				
			||||||
 | 
					          setTimeout(async () => {
 | 
				
			||||||
 | 
					            const element = this.$el
 | 
				
			||||||
 | 
					            if (!element) {
 | 
				
			||||||
 | 
					              reject(new Error('找不到组件容器'))
 | 
				
			||||||
 | 
					              return
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            try {
 | 
				
			||||||
 | 
					              // 获取元素的所有可能尺寸
 | 
				
			||||||
 | 
					              const rect = element.getBoundingClientRect()
 | 
				
			||||||
 | 
					              const offsetWidth = element.offsetWidth
 | 
				
			||||||
 | 
					              const offsetHeight = element.offsetHeight
 | 
				
			||||||
 | 
					              const scrollWidth = element.scrollWidth
 | 
				
			||||||
 | 
					              const scrollHeight = element.scrollHeight
 | 
				
			||||||
 | 
					              const clientWidth = element.clientWidth
 | 
				
			||||||
 | 
					              const clientHeight = element.clientHeight
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					              // 计算实际需要的尺寸(取最大值确保完整)
 | 
				
			||||||
 | 
					              const elementWidth = Math.max(
 | 
				
			||||||
 | 
					                offsetWidth,
 | 
				
			||||||
 | 
					                scrollWidth,
 | 
				
			||||||
 | 
					                clientWidth,
 | 
				
			||||||
 | 
					                rect.width
 | 
				
			||||||
 | 
					              )
 | 
				
			||||||
 | 
					              const elementHeight = Math.max(
 | 
				
			||||||
 | 
					                offsetHeight,
 | 
				
			||||||
 | 
					                scrollHeight,
 | 
				
			||||||
 | 
					                clientHeight,
 | 
				
			||||||
 | 
					                rect.height
 | 
				
			||||||
 | 
					              )
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					              setTimeout(async () => {
 | 
				
			||||||
 | 
					                try {
 | 
				
			||||||
 | 
					                  const result = await snapdom(element, {
 | 
				
			||||||
 | 
					                    width: elementWidth,
 | 
				
			||||||
 | 
					                    height: elementHeight,
 | 
				
			||||||
 | 
					                    quality: 1,
 | 
				
			||||||
 | 
					                    compress: false,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                    // useProxy: true,
 | 
				
			||||||
 | 
					                  })
 | 
				
			||||||
 | 
					                  const canvas = await result.toCanvas()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                  const dataUrl = canvas.toDataURL('image/jpeg')
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					                  uni.hideLoading()
 | 
				
			||||||
 | 
					                  this.handleShareGenerated(dataUrl)
 | 
				
			||||||
 | 
					                  this.$emit('share-generated', dataUrl)
 | 
				
			||||||
 | 
					                  resolve()
 | 
				
			||||||
 | 
					                } catch (err) {
 | 
				
			||||||
 | 
					                  uni.hideLoading()
 | 
				
			||||||
 | 
					                  console.error('snapDOM截取失败:', err)
 | 
				
			||||||
 | 
					                  uni.showToast({
 | 
				
			||||||
 | 
					                    title: '图片生成失败,请稍后重试',
 | 
				
			||||||
 | 
					                    icon: 'none',
 | 
				
			||||||
 | 
					                  })
 | 
				
			||||||
 | 
					                  reject(err)
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					              }, 500)
 | 
				
			||||||
 | 
					            } catch (err) {
 | 
				
			||||||
 | 
					              uni.hideLoading()
 | 
				
			||||||
 | 
					              console.error('snapDOM初始化失败:', err)
 | 
				
			||||||
 | 
					              uni.showToast({
 | 
				
			||||||
 | 
					                title: '图片生成失败,请稍后重试',
 | 
				
			||||||
 | 
					                icon: 'none',
 | 
				
			||||||
 | 
					              })
 | 
				
			||||||
 | 
					              reject(err)
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
 | 
					          }, 1000)
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // 开始等待图片加载
 | 
				
			||||||
 | 
					        waitForImages()
 | 
				
			||||||
 | 
					      })
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					</script>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					<style lang="scss" scoped>
 | 
				
			||||||
 | 
					.special-share-page {
 | 
				
			||||||
 | 
					  position: relative;
 | 
				
			||||||
 | 
					  width: 100%;
 | 
				
			||||||
 | 
					  height: 100%;
 | 
				
			||||||
 | 
					  display: flex;
 | 
				
			||||||
 | 
					  flex-direction: column;
 | 
				
			||||||
 | 
					  align-items: center;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* 背景图片样式 */
 | 
				
			||||||
 | 
					.share-bg-image {
 | 
				
			||||||
 | 
					  position: absolute;
 | 
				
			||||||
 | 
					  top: 0;
 | 
				
			||||||
 | 
					  left: 0;
 | 
				
			||||||
 | 
					  width: 100%;
 | 
				
			||||||
 | 
					  height: 100%;
 | 
				
			||||||
 | 
					  z-index: 1;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* 加载状态 */
 | 
				
			||||||
 | 
					.loading-container {
 | 
				
			||||||
 | 
					  position: absolute;
 | 
				
			||||||
 | 
					  top: 50%;
 | 
				
			||||||
 | 
					  left: 50%;
 | 
				
			||||||
 | 
					  transform: translate(-50%, -50%);
 | 
				
			||||||
 | 
					  display: flex;
 | 
				
			||||||
 | 
					  flex-direction: column;
 | 
				
			||||||
 | 
					  align-items: center;
 | 
				
			||||||
 | 
					  z-index: 2;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.loading-text {
 | 
				
			||||||
 | 
					  margin-top: 20rpx;
 | 
				
			||||||
 | 
					  font-size: 28rpx;
 | 
				
			||||||
 | 
					  color: #666;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.share-wrapper {
 | 
				
			||||||
 | 
					  position: absolute;
 | 
				
			||||||
 | 
					  z-index: 2;
 | 
				
			||||||
 | 
					  bottom: 22%;
 | 
				
			||||||
 | 
					  display: flex;
 | 
				
			||||||
 | 
					  flex-direction: column;
 | 
				
			||||||
 | 
					  align-items: center;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.portal-frame {
 | 
				
			||||||
 | 
					  padding: 32rpx;
 | 
				
			||||||
 | 
					  width: 520rpx;
 | 
				
			||||||
 | 
					  border-radius: 40rpx;
 | 
				
			||||||
 | 
					  display: flex;
 | 
				
			||||||
 | 
					  box-sizing: border-box;
 | 
				
			||||||
 | 
					  flex-direction: column;
 | 
				
			||||||
 | 
					  align-items: center;
 | 
				
			||||||
 | 
					  margin: 0 auto;
 | 
				
			||||||
 | 
					  opacity: 0;
 | 
				
			||||||
 | 
					  transform: translateY(20rpx);
 | 
				
			||||||
 | 
					  transition: all 0.3s ease;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.portal-frame.is-loaded {
 | 
				
			||||||
 | 
					  opacity: 1;
 | 
				
			||||||
 | 
					  transform: translateY(0);
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* 特殊场景二维码样式区域 - 用户自定义样式位置 */
 | 
				
			||||||
 | 
					.qr-code-outer {
 | 
				
			||||||
 | 
					  width: 400rpx;
 | 
				
			||||||
 | 
					  height: 400rpx;
 | 
				
			||||||
 | 
					  border-radius: 20rpx;
 | 
				
			||||||
 | 
					  display: flex;
 | 
				
			||||||
 | 
					  align-items: center;
 | 
				
			||||||
 | 
					  justify-content: center;
 | 
				
			||||||
 | 
					  padding: 12rpx;
 | 
				
			||||||
 | 
					  box-sizing: border-box;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.qr-code,
 | 
				
			||||||
 | 
					.qr-code-placeholder {
 | 
				
			||||||
 | 
					  width: 100%;
 | 
				
			||||||
 | 
					  height: 100%;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.qr-code {
 | 
				
			||||||
 | 
					  border-radius: 16rpx;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.qr-code-placeholder {
 | 
				
			||||||
 | 
					  display: flex;
 | 
				
			||||||
 | 
					  align-items: center;
 | 
				
			||||||
 | 
					  justify-content: center;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.loader {
 | 
				
			||||||
 | 
					  width: 100rpx;
 | 
				
			||||||
 | 
					  height: 100rpx;
 | 
				
			||||||
 | 
					  border: 8rpx solid rgba(0, 0, 0, 0.1);
 | 
				
			||||||
 | 
					  border-left-color: #0072ff;
 | 
				
			||||||
 | 
					  border-radius: 50%;
 | 
				
			||||||
 | 
					  animation: spin 1s linear infinite;
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@keyframes spin {
 | 
				
			||||||
 | 
					  to {
 | 
				
			||||||
 | 
					    transform: rotate(360deg);
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* 特殊场景会员编号样式区域 - 用户自定义样式位置 */
 | 
				
			||||||
 | 
					.member-code-text {
 | 
				
			||||||
 | 
					  /* 用户可以在这里自定义会员编号的样式 */
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					/* 特殊场景下的会员编号样式 */
 | 
				
			||||||
 | 
					.special-member-code-style {
 | 
				
			||||||
 | 
					  /* 用户可以在这里自定义特殊场景下的会员编号样式 */
 | 
				
			||||||
 | 
					  /* 例如:不同的位置、颜色、字体等 */
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					</style>
 | 
				
			||||||
| 
						 | 
					@ -147,3 +147,7 @@ export const agreement_expire = params =>
 | 
				
			||||||
//创客空间收益
 | 
					//创客空间收益
 | 
				
			||||||
export const markBonus = params =>
 | 
					export const markBonus = params =>
 | 
				
			||||||
  http.post('/bonus/api/bonus/query-mark-bonus-detail', params)
 | 
					  http.post('/bonus/api/bonus/query-mark-bonus-detail', params)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					//获取分享图片
 | 
				
			||||||
 | 
					export const getSharedImg = params =>
 | 
				
			||||||
 | 
					  http.get('/retail-member/api/retail-member/get-shared-image', { params })
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -20,7 +20,7 @@ module.exports = vm => {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    //#ifdef DEV_SERVER
 | 
					    //#ifdef DEV_SERVER
 | 
				
			||||||
    console.log('DEV_SERVER')
 | 
					    console.log('DEV_SERVER')
 | 
				
			||||||
    config.baseURL = '/prod-api'
 | 
					    config.baseURL = 'http://192.168.0.86:8080'
 | 
				
			||||||
    //#endif
 | 
					    //#endif
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    //#ifdef QA_SERVER
 | 
					    //#ifdef QA_SERVER
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -13,10 +13,14 @@ export const getPhoneCode = params =>
 | 
				
			||||||
  http.get('/member/api/share/share-sms-code', { params })
 | 
					  http.get('/member/api/share/share-sms-code', { params })
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// 注册
 | 
					// 注册
 | 
				
			||||||
 | 
					 | 
				
			||||||
export const getRegister = data =>
 | 
					export const getRegister = data =>
 | 
				
			||||||
  http.post('/member/api/share/share-register', data)
 | 
					  http.post('/member/api/share/share-register', data)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// 自动登录
 | 
					// 自动登录
 | 
				
			||||||
export const autoLogin = data =>
 | 
					export const autoLogin = data =>
 | 
				
			||||||
  http.post('/retail-member/api/retail-auth/auto-login', data)
 | 
					  http.post('/retail-member/api/retail-auth/auto-login', data)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// // 获取特殊场景背景图
 | 
				
			||||||
 | 
					// // TODO: 用户需要根据实际接口修改路径和参数
 | 
				
			||||||
 | 
					// export const getSpecialBackground = params =>
 | 
				
			||||||
 | 
					//   http.get('/member/api/share/get-special-background', { params })
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -9,6 +9,7 @@
 | 
				
			||||||
      "version": "1.0.0",
 | 
					      "version": "1.0.0",
 | 
				
			||||||
      "license": "ISC",
 | 
					      "license": "ISC",
 | 
				
			||||||
      "dependencies": {
 | 
					      "dependencies": {
 | 
				
			||||||
 | 
					        "@zumer/snapdom": "^1.9.5",
 | 
				
			||||||
        "axios": "^1.5.0",
 | 
					        "axios": "^1.5.0",
 | 
				
			||||||
        "dayjs": "^1.11.13",
 | 
					        "dayjs": "^1.11.13",
 | 
				
			||||||
        "echarts": "^4.9.0",
 | 
					        "echarts": "^4.9.0",
 | 
				
			||||||
| 
						 | 
					@ -768,6 +769,12 @@
 | 
				
			||||||
      "dev": true,
 | 
					      "dev": true,
 | 
				
			||||||
      "peer": true
 | 
					      "peer": true
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
 | 
					    "node_modules/@zumer/snapdom": {
 | 
				
			||||||
 | 
					      "version": "1.9.5",
 | 
				
			||||||
 | 
					      "resolved": "https://registry.npmjs.org/@zumer/snapdom/-/snapdom-1.9.5.tgz",
 | 
				
			||||||
 | 
					      "integrity": "sha512-p+9C86evbQceSlDBhRA2+BJSotBPO7YFh6b7LeUDPowt/iAx5kzZomO3Fme7mYqfpn990TDKFHn0XascDvUODw==",
 | 
				
			||||||
 | 
					      "license": "MIT"
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
    "node_modules/acorn": {
 | 
					    "node_modules/acorn": {
 | 
				
			||||||
      "version": "6.4.2",
 | 
					      "version": "6.4.2",
 | 
				
			||||||
      "resolved": "https://mirrors.cloud.tencent.com/npm/acorn/-/acorn-6.4.2.tgz",
 | 
					      "resolved": "https://mirrors.cloud.tencent.com/npm/acorn/-/acorn-6.4.2.tgz",
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -4,6 +4,7 @@
 | 
				
			||||||
  "description": "h5",
 | 
					  "description": "h5",
 | 
				
			||||||
  "main": "main.js",
 | 
					  "main": "main.js",
 | 
				
			||||||
  "dependencies": {
 | 
					  "dependencies": {
 | 
				
			||||||
 | 
					    "@zumer/snapdom": "^1.9.5",
 | 
				
			||||||
    "axios": "^1.5.0",
 | 
					    "axios": "^1.5.0",
 | 
				
			||||||
    "dayjs": "^1.11.13",
 | 
					    "dayjs": "^1.11.13",
 | 
				
			||||||
    "echarts": "^4.9.0",
 | 
					    "echarts": "^4.9.0",
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -4,64 +4,44 @@
 | 
				
			||||||
    style="display: flex; flex-direction: column; height: 100vh"
 | 
					    style="display: flex; flex-direction: column; height: 100vh"
 | 
				
			||||||
  >
 | 
					  >
 | 
				
			||||||
    <view id="shareContainer" class="share-container">
 | 
					    <view id="shareContainer" class="share-container">
 | 
				
			||||||
      <!-- 微信环境:只有在未生成分享图时才显示原始内容 -->
 | 
					      <!-- 默认场景 -->
 | 
				
			||||||
      <template v-if="!isWechat || (isWechat && !generatedImageUrl)">
 | 
					      <DefaultSharePage
 | 
				
			||||||
        <!-- 背景图片,替代CSS background -->
 | 
					        v-if="isDefaultScene"
 | 
				
			||||||
        <image
 | 
					        :qrCodeImage="qrCodeImage"
 | 
				
			||||||
          class="share-bg-image"
 | 
					        :userInfo="userInfo"
 | 
				
			||||||
          src="/static/images/share-bg.jpg"
 | 
					        :isWechat="isWechat"
 | 
				
			||||||
          mode="scaleToFill"
 | 
					        :isLoaded="isLoaded"
 | 
				
			||||||
          crossorigin="anonymous"
 | 
					        @share-generated="handleShareGenerated"
 | 
				
			||||||
          @load="onBackgroundImageLoad"
 | 
					        ref="defaultSharePage"
 | 
				
			||||||
          @error="onBackgroundImageError"
 | 
					      />
 | 
				
			||||||
        ></image>
 | 
					 | 
				
			||||||
        <view class="share-wrapper">
 | 
					 | 
				
			||||||
          <view class="portal-frame" :class="{ 'is-loaded': isLoaded }">
 | 
					 | 
				
			||||||
            <view class="qr-code-outer">
 | 
					 | 
				
			||||||
              <image
 | 
					 | 
				
			||||||
                class="qr-code"
 | 
					 | 
				
			||||||
                :src="qrCodeImage"
 | 
					 | 
				
			||||||
                mode="aspectFit"
 | 
					 | 
				
			||||||
                v-if="qrCodeImage"
 | 
					 | 
				
			||||||
              ></image>
 | 
					 | 
				
			||||||
              <view v-else class="qr-code-placeholder">
 | 
					 | 
				
			||||||
                <view class="loader"></view>
 | 
					 | 
				
			||||||
              </view>
 | 
					 | 
				
			||||||
            </view>
 | 
					 | 
				
			||||||
            <text
 | 
					 | 
				
			||||||
              style="
 | 
					 | 
				
			||||||
                font-size: 30rpx;
 | 
					 | 
				
			||||||
                color: #fff;
 | 
					 | 
				
			||||||
                font-weight: bold;
 | 
					 | 
				
			||||||
                margin-top: 20rpx;
 | 
					 | 
				
			||||||
              "
 | 
					 | 
				
			||||||
              >{{ desensitization(userInfo.memberCode) }}</text
 | 
					 | 
				
			||||||
            >
 | 
					 | 
				
			||||||
            <button v-if="!isWechat" class="share-button" @click="sharePage">
 | 
					 | 
				
			||||||
              {{ isWechat ? '长按保存图片' : '保存图片并分享' }}
 | 
					 | 
				
			||||||
            </button>
 | 
					 | 
				
			||||||
          </view>
 | 
					 | 
				
			||||||
          <!-- <image
 | 
					 | 
				
			||||||
            class="share-bg-logo"
 | 
					 | 
				
			||||||
            src="/static/images/share-logo.png"
 | 
					 | 
				
			||||||
            mode="scaleToFill"
 | 
					 | 
				
			||||||
          ></image> -->
 | 
					 | 
				
			||||||
        </view>
 | 
					 | 
				
			||||||
      </template>
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      <!-- 特殊场景 -->
 | 
				
			||||||
 | 
					      <SpecialSharePage
 | 
				
			||||||
 | 
					        v-if="isSpecialScene && sourceVisible"
 | 
				
			||||||
 | 
					        :qrCodeImage="qrCodeImage"
 | 
				
			||||||
 | 
					        :specialBackgroundImage="userInfo.sharePosterImage"
 | 
				
			||||||
 | 
					        :userInfo="userInfo"
 | 
				
			||||||
 | 
					        :isWechat="isWechat"
 | 
				
			||||||
 | 
					        :isLoaded="isLoaded"
 | 
				
			||||||
 | 
					        @share-generated="handleShareGenerated"
 | 
				
			||||||
 | 
					        ref="specialSharePage"
 | 
				
			||||||
 | 
					      />
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      <!-- 微信环境全屏图片显示 -->
 | 
				
			||||||
      <view
 | 
					      <view
 | 
				
			||||||
        class="wechat-fullscreen-overlay"
 | 
					        class="wechat-fullscreen-overlay"
 | 
				
			||||||
        v-show="isWechat && generatedImageUrl"
 | 
					        v-show="(isWechat || userInfo.sharePosterImage) && generatedImageUrl"
 | 
				
			||||||
        @click="closeFullscreenImage"
 | 
					        @click="closeFullscreenImage"
 | 
				
			||||||
      >
 | 
					      >
 | 
				
			||||||
        <image
 | 
					        <img
 | 
				
			||||||
          class="fullscreen-image"
 | 
					          class="fullscreen-image"
 | 
				
			||||||
          :src="generatedImageUrl"
 | 
					          :src="generatedImageUrl"
 | 
				
			||||||
          mode="scaleToFill"
 | 
					          width="100%"
 | 
				
			||||||
 | 
					          height="100%"
 | 
				
			||||||
          @load="onGeneratedImageLoad"
 | 
					          @load="onGeneratedImageLoad"
 | 
				
			||||||
          @error="onGeneratedImageError"
 | 
					          @error="onGeneratedImageError"
 | 
				
			||||||
          @click.stop=""
 | 
					          @click.stop=""
 | 
				
			||||||
        ></image>
 | 
					        />
 | 
				
			||||||
      </view>
 | 
					      </view>
 | 
				
			||||||
    </view>
 | 
					    </view>
 | 
				
			||||||
    <cl-tabbar class="tabbar" :current="2" />
 | 
					    <cl-tabbar class="tabbar" :current="2" />
 | 
				
			||||||
| 
						 | 
					@ -69,33 +49,42 @@
 | 
				
			||||||
</template>
 | 
					</template>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
<script>
 | 
					<script>
 | 
				
			||||||
import html2canvas from 'html2canvas'
 | 
					 | 
				
			||||||
import { getShareCode } from '@/config/share'
 | 
					import { getShareCode } from '@/config/share'
 | 
				
			||||||
import clTabbar from '@/components/cl-tabbar.vue'
 | 
					import clTabbar from '@/components/cl-tabbar.vue'
 | 
				
			||||||
 | 
					import DefaultSharePage from '@/components/share/DefaultSharePage.vue'
 | 
				
			||||||
 | 
					import SpecialSharePage from '@/components/share/SpecialSharePage.vue'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
export default {
 | 
					export default {
 | 
				
			||||||
  name: 'ShareQRCode',
 | 
					  name: 'ShareQRCode',
 | 
				
			||||||
  components: {
 | 
					  components: {
 | 
				
			||||||
    'cl-tabbar': clTabbar,
 | 
					    'cl-tabbar': clTabbar,
 | 
				
			||||||
 | 
					    DefaultSharePage,
 | 
				
			||||||
 | 
					    SpecialSharePage,
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  data() {
 | 
					  data() {
 | 
				
			||||||
    return {
 | 
					    return {
 | 
				
			||||||
      qrCodeImage: '',
 | 
					      qrCodeImage: '',
 | 
				
			||||||
      // Set canvas dimensions. It's better to get device screen width for this.
 | 
					 | 
				
			||||||
      canvasWidth: 375,
 | 
					      canvasWidth: 375,
 | 
				
			||||||
      canvasHeight: 800,
 | 
					      canvasHeight: 800,
 | 
				
			||||||
      isLoaded: false,
 | 
					      isLoaded: false,
 | 
				
			||||||
      shareButtonShow: true,
 | 
					      isWechat: false,
 | 
				
			||||||
      isWechat: false, // 是否微信环境
 | 
					      generatedImageUrl: '',
 | 
				
			||||||
      generatedImageUrl: '', // 生成的图片URL,用于微信长按保存
 | 
					 | 
				
			||||||
      backgroundImageLoaded: false, // 背景图片是否加载完成
 | 
					 | 
				
			||||||
      userInfo: uni.getStorageSync('User'),
 | 
					      userInfo: uni.getStorageSync('User'),
 | 
				
			||||||
 | 
					      sourceVisible: true,
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
 | 
					  computed: {
 | 
				
			||||||
 | 
					    isSpecialScene() {
 | 
				
			||||||
 | 
					      return this.userInfo && this.userInfo.sharePosterImage
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					    isDefaultScene() {
 | 
				
			||||||
 | 
					      return !this.isSpecialScene
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
  onLoad() {
 | 
					  onLoad() {
 | 
				
			||||||
    this.checkWechatEnvironment()
 | 
					    this.checkWechatEnvironment()
 | 
				
			||||||
    this.handleGetShareCode()
 | 
					    this.handleGetShareCode()
 | 
				
			||||||
    // Get screen width to set canvas width dynamically
 | 
					    // 获取屏幕尺寸
 | 
				
			||||||
    uni.getSystemInfo({
 | 
					    uni.getSystemInfo({
 | 
				
			||||||
      success: res => {
 | 
					      success: res => {
 | 
				
			||||||
        this.canvasWidth = res.windowWidth
 | 
					        this.canvasWidth = res.windowWidth
 | 
				
			||||||
| 
						 | 
					@ -104,19 +93,12 @@ export default {
 | 
				
			||||||
    })
 | 
					    })
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  onReady() {
 | 
					  onReady() {
 | 
				
			||||||
    // Use a short timeout to ensure the initial render is complete before animation
 | 
					    // 短暂延迟后启用加载动画
 | 
				
			||||||
    setTimeout(() => {
 | 
					    setTimeout(() => {
 | 
				
			||||||
      this.isLoaded = true
 | 
					      this.isLoaded = true
 | 
				
			||||||
    }, 100)
 | 
					    }, 100)
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
  methods: {
 | 
					  methods: {
 | 
				
			||||||
    desensitization(str) {
 | 
					 | 
				
			||||||
      if (!str) return ''
 | 
					 | 
				
			||||||
      if (str.length <= 8) return str.slice(0, 4) + '****'
 | 
					 | 
				
			||||||
      const len = str.length - 6
 | 
					 | 
				
			||||||
      const placeholder = '*'.repeat(len)
 | 
					 | 
				
			||||||
      return str.slice(0, 4) + placeholder + str.slice(-2)
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
    // 检测微信环境
 | 
					    // 检测微信环境
 | 
				
			||||||
    checkWechatEnvironment() {
 | 
					    checkWechatEnvironment() {
 | 
				
			||||||
      const ua = navigator.userAgent.toLowerCase()
 | 
					      const ua = navigator.userAgent.toLowerCase()
 | 
				
			||||||
| 
						 | 
					@ -124,18 +106,14 @@ export default {
 | 
				
			||||||
      console.log('微信环境检测:', this.isWechat)
 | 
					      console.log('微信环境检测:', this.isWechat)
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // 获取分享二维码
 | 
				
			||||||
    handleGetShareCode() {
 | 
					    handleGetShareCode() {
 | 
				
			||||||
      // Don't show loading toast, use the placeholder loader instead
 | 
					 | 
				
			||||||
      // uni.showLoading({ title: '加载中...' })
 | 
					 | 
				
			||||||
      getShareCode()
 | 
					      getShareCode()
 | 
				
			||||||
        .then(res => {
 | 
					        .then(res => {
 | 
				
			||||||
          // The screenshot shows the base64 string is in data.datStr
 | 
					 | 
				
			||||||
          if (res.code === 200 && res.data && res.data.dataStr) {
 | 
					          if (res.code === 200 && res.data && res.data.dataStr) {
 | 
				
			||||||
            this.qrCodeImage = 'data:image/png;base64,' + res.data.dataStr
 | 
					            this.qrCodeImage = 'data:image/png;base64,' + res.data.dataStr
 | 
				
			||||||
            this.$nextTick(() => {
 | 
					            this.$nextTick(() => {
 | 
				
			||||||
              if (this.isWechat) {
 | 
					              this.generateShareImage()
 | 
				
			||||||
                this.sharePage()
 | 
					 | 
				
			||||||
              }
 | 
					 | 
				
			||||||
            })
 | 
					            })
 | 
				
			||||||
          } else {
 | 
					          } else {
 | 
				
			||||||
            uni.showToast({
 | 
					            uni.showToast({
 | 
				
			||||||
| 
						 | 
					@ -153,33 +131,47 @@ export default {
 | 
				
			||||||
        })
 | 
					        })
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    async sharePage() {
 | 
					    // 生成分享图片
 | 
				
			||||||
      if (!this.qrCodeImage) {
 | 
					    async generateShareImage() {
 | 
				
			||||||
        uni.showToast({
 | 
					 | 
				
			||||||
          title: '二维码尚未生成',
 | 
					 | 
				
			||||||
          icon: 'none',
 | 
					 | 
				
			||||||
        })
 | 
					 | 
				
			||||||
        return
 | 
					 | 
				
			||||||
      }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      // 统一使用html2canvas生成图片
 | 
					 | 
				
			||||||
      uni.showLoading({ title: '加载中...' })
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      try {
 | 
					      try {
 | 
				
			||||||
        // 隐藏按钮,等待DOM更新
 | 
					        if (this.isSpecialScene) {
 | 
				
			||||||
        this.shareButtonShow = false
 | 
					          await this.$refs.specialSharePage.generateShareImage()
 | 
				
			||||||
        await this.$nextTick()
 | 
					        } else {
 | 
				
			||||||
 | 
					          // 默认场景生成图片
 | 
				
			||||||
        // 使用html2canvas截取页面
 | 
					          await this.$refs.defaultSharePage.sharePage()
 | 
				
			||||||
        await this.capturePageWithHtml2Canvas()
 | 
					 | 
				
			||||||
      } catch (error) {
 | 
					 | 
				
			||||||
        uni.hideLoading()
 | 
					 | 
				
			||||||
        uni.showToast({ title: '图片生成失败,请稍后重试', icon: 'none' })
 | 
					 | 
				
			||||||
        console.error('sharePage error:', error)
 | 
					 | 
				
			||||||
      } finally {
 | 
					 | 
				
			||||||
        // 恢复按钮显示
 | 
					 | 
				
			||||||
        this.shareButtonShow = true
 | 
					 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					      } catch (error) {
 | 
				
			||||||
 | 
					        console.error('生成分享图片失败:', error)
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // 处理分享图片生成完成
 | 
				
			||||||
 | 
					    handleShareGenerated(dataUrl) {
 | 
				
			||||||
 | 
					      this.$nextTick(() => {
 | 
				
			||||||
 | 
					        this.sourceVisible = false
 | 
				
			||||||
 | 
					      })
 | 
				
			||||||
 | 
					      uni.hideLoading()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					      if (this.isWechat) {
 | 
				
			||||||
 | 
					        // 微信环境:设置图片供长按保存
 | 
				
			||||||
 | 
					        this.generatedImageUrl = dataUrl
 | 
				
			||||||
 | 
					      } else {
 | 
				
			||||||
 | 
					        // 普通浏览器环境
 | 
				
			||||||
 | 
					        if (this.isSpecialScene) {
 | 
				
			||||||
 | 
					          // 特殊场景:设置图片供长按保存(没有下载按钮)
 | 
				
			||||||
 | 
					          this.generatedImageUrl = dataUrl
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					      }
 | 
				
			||||||
 | 
					    },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    // 下载图片
 | 
				
			||||||
 | 
					    downloadImage(dataUrl) {
 | 
				
			||||||
 | 
					      const link = document.createElement('a')
 | 
				
			||||||
 | 
					      link.href = dataUrl
 | 
				
			||||||
 | 
					      link.download = `share_page_${Date.now()}.png`
 | 
				
			||||||
 | 
					      document.body.appendChild(link)
 | 
				
			||||||
 | 
					      link.click()
 | 
				
			||||||
 | 
					      document.body.removeChild(link)
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // 生成的图片加载成功
 | 
					    // 生成的图片加载成功
 | 
				
			||||||
| 
						 | 
					@ -201,125 +193,6 @@ export default {
 | 
				
			||||||
      this.generatedImageUrl = ''
 | 
					      this.generatedImageUrl = ''
 | 
				
			||||||
      console.log('关闭全屏图片')
 | 
					      console.log('关闭全屏图片')
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
 | 
					 | 
				
			||||||
    // 背景图片加载成功
 | 
					 | 
				
			||||||
    onBackgroundImageLoad() {
 | 
					 | 
				
			||||||
      this.backgroundImageLoaded = true
 | 
					 | 
				
			||||||
      console.log('背景图片加载成功')
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    // 背景图片加载失败
 | 
					 | 
				
			||||||
    onBackgroundImageError(e) {
 | 
					 | 
				
			||||||
      console.error('背景图片加载失败:', e)
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    // 使用html2canvas截取整个页面
 | 
					 | 
				
			||||||
    async capturePageWithHtml2Canvas() {
 | 
					 | 
				
			||||||
      console.log('开始使用html2canvas截取页面')
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
      return new Promise((resolve, reject) => {
 | 
					 | 
				
			||||||
        // 确保所有图片都已加载完成
 | 
					 | 
				
			||||||
        const waitForImages = () => {
 | 
					 | 
				
			||||||
          if (!this.backgroundImageLoaded || !this.qrCodeImage) {
 | 
					 | 
				
			||||||
            setTimeout(waitForImages, 100)
 | 
					 | 
				
			||||||
            return
 | 
					 | 
				
			||||||
          }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
          // 额外等待确保渲染完成
 | 
					 | 
				
			||||||
          setTimeout(() => {
 | 
					 | 
				
			||||||
            const element = document.getElementById('shareContainer')
 | 
					 | 
				
			||||||
            if (!element) {
 | 
					 | 
				
			||||||
              reject(new Error('找不到页面容器'))
 | 
					 | 
				
			||||||
              return
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            console.log(
 | 
					 | 
				
			||||||
              '开始html2canvas截取,容器尺寸:',
 | 
					 | 
				
			||||||
              element.offsetWidth,
 | 
					 | 
				
			||||||
              'x',
 | 
					 | 
				
			||||||
              element.offsetHeight
 | 
					 | 
				
			||||||
            )
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
            html2canvas(element, {
 | 
					 | 
				
			||||||
              useCORS: true,
 | 
					 | 
				
			||||||
              allowTaint: true,
 | 
					 | 
				
			||||||
              backgroundColor: null,
 | 
					 | 
				
			||||||
              scale: 2,
 | 
					 | 
				
			||||||
              dpi: 400,
 | 
					 | 
				
			||||||
              logging: true, // 开启日志便于调试
 | 
					 | 
				
			||||||
              width: element.offsetWidth,
 | 
					 | 
				
			||||||
              height: element.offsetHeight,
 | 
					 | 
				
			||||||
              windowWidth: element.offsetWidth,
 | 
					 | 
				
			||||||
              windowHeight: element.offsetHeight,
 | 
					 | 
				
			||||||
              scrollX: 0,
 | 
					 | 
				
			||||||
              scrollY: 0,
 | 
					 | 
				
			||||||
              x: 0,
 | 
					 | 
				
			||||||
              y: 0,
 | 
					 | 
				
			||||||
            })
 | 
					 | 
				
			||||||
              .then(canvas => {
 | 
					 | 
				
			||||||
                const dataUrl = canvas.toDataURL('image/jpeg', 1)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                // 根据环境处理结果
 | 
					 | 
				
			||||||
                if (this.isWechat) {
 | 
					 | 
				
			||||||
                  // 微信环境:设置图片供长按保存
 | 
					 | 
				
			||||||
                  this.generatedImageUrl = dataUrl
 | 
					 | 
				
			||||||
                  uni.hideLoading()
 | 
					 | 
				
			||||||
                } else {
 | 
					 | 
				
			||||||
                  // 普通浏览器:直接下载图片
 | 
					 | 
				
			||||||
                  this.downloadImage(dataUrl)
 | 
					 | 
				
			||||||
                  uni.hideLoading()
 | 
					 | 
				
			||||||
                  uni.showToast({
 | 
					 | 
				
			||||||
                    title: '图片已开始下载',
 | 
					 | 
				
			||||||
                    icon: 'success',
 | 
					 | 
				
			||||||
                  })
 | 
					 | 
				
			||||||
                }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
                resolve()
 | 
					 | 
				
			||||||
              })
 | 
					 | 
				
			||||||
              .catch(err => {
 | 
					 | 
				
			||||||
                console.error('html2canvas截取失败:', err)
 | 
					 | 
				
			||||||
                reject(err)
 | 
					 | 
				
			||||||
              })
 | 
					 | 
				
			||||||
          }, 1000) // 增加等待时间到1000ms
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        // 开始等待图片加载
 | 
					 | 
				
			||||||
        waitForImages()
 | 
					 | 
				
			||||||
      })
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    // 原生图片加载器
 | 
					 | 
				
			||||||
    loadImage(src) {
 | 
					 | 
				
			||||||
      return new Promise((resolve, reject) => {
 | 
					 | 
				
			||||||
        const img = new Image()
 | 
					 | 
				
			||||||
        img.crossOrigin = 'anonymous'
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        img.onload = () => {
 | 
					 | 
				
			||||||
          console.log(
 | 
					 | 
				
			||||||
            `图片加载成功: ${src.substring(0, 50)}...`,
 | 
					 | 
				
			||||||
            `${img.width}x${img.height}`
 | 
					 | 
				
			||||||
          )
 | 
					 | 
				
			||||||
          resolve(img)
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        img.onerror = error => {
 | 
					 | 
				
			||||||
          console.error(`图片加载失败: ${src}`, error)
 | 
					 | 
				
			||||||
          reject(new Error(`图片加载失败: ${src}`))
 | 
					 | 
				
			||||||
        }
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
        img.src = src
 | 
					 | 
				
			||||||
      })
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
    // 下载图片
 | 
					 | 
				
			||||||
    downloadImage(dataUrl) {
 | 
					 | 
				
			||||||
      const link = document.createElement('a')
 | 
					 | 
				
			||||||
      link.href = dataUrl
 | 
					 | 
				
			||||||
      link.download = `share_page_${Date.now()}.png`
 | 
					 | 
				
			||||||
      document.body.appendChild(link)
 | 
					 | 
				
			||||||
      link.click()
 | 
					 | 
				
			||||||
      document.body.removeChild(link)
 | 
					 | 
				
			||||||
    },
 | 
					 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
</script>
 | 
					</script>
 | 
				
			||||||
| 
						 | 
					@ -336,190 +209,33 @@ export default {
 | 
				
			||||||
  align-items: center;
 | 
					  align-items: center;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
/* 背景图片样式 */
 | 
					 | 
				
			||||||
.share-bg-image {
 | 
					 | 
				
			||||||
  position: absolute;
 | 
					 | 
				
			||||||
  top: 0;
 | 
					 | 
				
			||||||
  left: 0;
 | 
					 | 
				
			||||||
  width: 100%;
 | 
					 | 
				
			||||||
  height: 100%;
 | 
					 | 
				
			||||||
  z-index: 1;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
.share-bg {
 | 
					 | 
				
			||||||
  width: 100%;
 | 
					 | 
				
			||||||
  height: 100%;
 | 
					 | 
				
			||||||
  img {
 | 
					 | 
				
			||||||
    width: 100%;
 | 
					 | 
				
			||||||
    height: 100%;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
.share-wrapper {
 | 
					 | 
				
			||||||
  position: absolute;
 | 
					 | 
				
			||||||
  z-index: 2;
 | 
					 | 
				
			||||||
  bottom: 22%;
 | 
					 | 
				
			||||||
  display: flex;
 | 
					 | 
				
			||||||
  flex-direction: column;
 | 
					 | 
				
			||||||
  align-items: center;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
.share-bg-logo {
 | 
					 | 
				
			||||||
  width: 100%;
 | 
					 | 
				
			||||||
  height: 360rpx;
 | 
					 | 
				
			||||||
  // margin-top: -60rpx;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
.portal-frame {
 | 
					 | 
				
			||||||
  padding: 32rpx;
 | 
					 | 
				
			||||||
  width: 520rpx;
 | 
					 | 
				
			||||||
  border-radius: 40rpx;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
  display: flex;
 | 
					 | 
				
			||||||
  box-sizing: border-box;
 | 
					 | 
				
			||||||
  flex-direction: column;
 | 
					 | 
				
			||||||
  align-items: center;
 | 
					 | 
				
			||||||
  margin: 0 auto;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
.portal-frame.is-loaded {
 | 
					 | 
				
			||||||
  opacity: 1;
 | 
					 | 
				
			||||||
  transform: translateY(0);
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
.title {
 | 
					 | 
				
			||||||
  font-size: 44rpx;
 | 
					 | 
				
			||||||
  font-weight: 500;
 | 
					 | 
				
			||||||
  color: #1e1e1e;
 | 
					 | 
				
			||||||
  margin-bottom: 60rpx;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/* The single white card for the QR code */
 | 
					 | 
				
			||||||
.qr-code-outer {
 | 
					 | 
				
			||||||
  width: 400rpx; /* 从400rpx缩小到320rpx */
 | 
					 | 
				
			||||||
  height: 400rpx; /* 从400rpx缩小到320rpx */
 | 
					 | 
				
			||||||
  background: rgba(255, 255, 255, 0.98);
 | 
					 | 
				
			||||||
  border-radius: 20rpx; /* 从24rpx减小到20rpx */
 | 
					 | 
				
			||||||
  box-shadow: 0px 8rpx 20rpx rgba(50, 50, 90, 0.06);
 | 
					 | 
				
			||||||
  border: 1px solid #f0f0f0;
 | 
					 | 
				
			||||||
  display: flex;
 | 
					 | 
				
			||||||
  align-items: center;
 | 
					 | 
				
			||||||
  justify-content: center;
 | 
					 | 
				
			||||||
  padding: 12rpx; /* 从16rpx减小到12rpx */
 | 
					 | 
				
			||||||
  box-sizing: border-box;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/* The image and the placeholder both live inside the outer card */
 | 
					 | 
				
			||||||
.qr-code,
 | 
					 | 
				
			||||||
.qr-code-placeholder {
 | 
					 | 
				
			||||||
  width: 100%;
 | 
					 | 
				
			||||||
  height: 100%;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
.qr-code {
 | 
					 | 
				
			||||||
  border-radius: 16rpx;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
.qr-code-placeholder {
 | 
					 | 
				
			||||||
  display: flex;
 | 
					 | 
				
			||||||
  align-items: center;
 | 
					 | 
				
			||||||
  justify-content: center;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
.loader {
 | 
					 | 
				
			||||||
  width: 100rpx;
 | 
					 | 
				
			||||||
  height: 100rpx;
 | 
					 | 
				
			||||||
  border: 8rpx solid rgba(0, 0, 0, 0.1);
 | 
					 | 
				
			||||||
  border-left-color: #0072ff;
 | 
					 | 
				
			||||||
  border-radius: 50%;
 | 
					 | 
				
			||||||
  animation: spin 1s linear infinite;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
@keyframes spin {
 | 
					 | 
				
			||||||
  to {
 | 
					 | 
				
			||||||
    transform: rotate(360deg);
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
.tip {
 | 
					 | 
				
			||||||
  font-size: 30rpx;
 | 
					 | 
				
			||||||
  color: #888;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/* Unified style for both the real button and the fake one (which is a view) */
 | 
					 | 
				
			||||||
.share-button {
 | 
					 | 
				
			||||||
  margin-top: 28rpx; /* 从32rpx减小到28rpx */
 | 
					 | 
				
			||||||
  width: 280rpx; /* 设置固定宽度 */
 | 
					 | 
				
			||||||
  height: 72rpx; /* 从88rpx减小到72rpx */
 | 
					 | 
				
			||||||
  line-height: 72rpx;
 | 
					 | 
				
			||||||
  color: #fff;
 | 
					 | 
				
			||||||
  border-radius: 36rpx; /* 从44rpx减小到36rpx */
 | 
					 | 
				
			||||||
  font-size: 26rpx; /* 从30rpx减小到26rpx */
 | 
					 | 
				
			||||||
  font-weight: 500;
 | 
					 | 
				
			||||||
  /* 更精细的渐变 */
 | 
					 | 
				
			||||||
  background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
 | 
					 | 
				
			||||||
  box-shadow: 0 5rpx 12rpx 0 rgba(102, 126, 234, 0.2);
 | 
					 | 
				
			||||||
  border: none;
 | 
					 | 
				
			||||||
  padding: 0;
 | 
					 | 
				
			||||||
  text-align: center;
 | 
					 | 
				
			||||||
  transition: all 0.3s ease;
 | 
					 | 
				
			||||||
  letter-spacing: 1rpx;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/* The real button needs to override some uni-app defaults */
 | 
					 | 
				
			||||||
button.share-button {
 | 
					 | 
				
			||||||
  padding: 0;
 | 
					 | 
				
			||||||
  line-height: 72rpx; /* 更新行高到72rpx */
 | 
					 | 
				
			||||||
  border: none;
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
.share-button:active {
 | 
					 | 
				
			||||||
  transform: translateY(1rpx);
 | 
					 | 
				
			||||||
  box-shadow: 0 4rpx 10rpx rgba(102, 126, 234, 0.4);
 | 
					 | 
				
			||||||
  background: linear-gradient(
 | 
					 | 
				
			||||||
    135deg,
 | 
					 | 
				
			||||||
    #5a67d8 0%,
 | 
					 | 
				
			||||||
    #6b46c1 100%
 | 
					 | 
				
			||||||
  ); /* 按下时的渐变 */
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
/* 微信环境全屏覆盖样式 */
 | 
					/* 微信环境全屏覆盖样式 */
 | 
				
			||||||
.wechat-fullscreen-overlay {
 | 
					.wechat-fullscreen-overlay {
 | 
				
			||||||
  position: absolute;
 | 
					  position: absolute;
 | 
				
			||||||
  top: 0;
 | 
					  top: 0;
 | 
				
			||||||
  left: 0;
 | 
					  left: 0;
 | 
				
			||||||
  width: 100vw;
 | 
					  width: 100vw;
 | 
				
			||||||
  height: 100%; /* 减去tab栏高度 */
 | 
					  height: 100%;
 | 
				
			||||||
 | 
					  background-color: transparent;
 | 
				
			||||||
  background-color: transparent; /* 移除背景色 */
 | 
					 | 
				
			||||||
  z-index: 999;
 | 
					  z-index: 999;
 | 
				
			||||||
  display: flex;
 | 
					  display: flex;
 | 
				
			||||||
  flex-direction: column;
 | 
					  flex-direction: column;
 | 
				
			||||||
  align-items: stretch; /* 拉伸对齐 */
 | 
					  align-items: stretch;
 | 
				
			||||||
  justify-content: stretch; /* 拉伸对齐 */
 | 
					  justify-content: stretch;
 | 
				
			||||||
  padding: 0;
 | 
					  padding: 0;
 | 
				
			||||||
  margin: 0;
 | 
					  margin: 0;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.fullscreen-image {
 | 
					.fullscreen-image {
 | 
				
			||||||
  width: 100vw;
 | 
					  width: 100vw;
 | 
				
			||||||
  height: 100%; /* 减去tab栏高度 */
 | 
					  height: 100%;
 | 
				
			||||||
  object-fit: cover; /* 覆盖整个容器,可能会裁剪 */
 | 
					  object-fit: cover;
 | 
				
			||||||
  margin: 0;
 | 
					  margin: 0;
 | 
				
			||||||
  padding: 0;
 | 
					  padding: 0;
 | 
				
			||||||
  border: none;
 | 
					  border: none;
 | 
				
			||||||
  display: block;
 | 
					  display: block;
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@media screen and (max-height: 667px) {
 | 
					 | 
				
			||||||
  .tabbar {
 | 
					 | 
				
			||||||
    height: 70px !important;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
@media screen and (min-height: 812px) {
 | 
					 | 
				
			||||||
  /* iPhone X及以上机型 */
 | 
					 | 
				
			||||||
  .tabbar {
 | 
					 | 
				
			||||||
    height: 80px !important;
 | 
					 | 
				
			||||||
  }
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
.tabbar {
 | 
					.tabbar {
 | 
				
			||||||
  position: static;
 | 
					  position: static;
 | 
				
			||||||
  bottom: 0;
 | 
					  bottom: 0;
 | 
				
			||||||
| 
						 | 
					@ -529,8 +245,17 @@ button.share-button {
 | 
				
			||||||
  :v-deep .u-tabbar--fixed {
 | 
					  :v-deep .u-tabbar--fixed {
 | 
				
			||||||
    height: 100px !important;
 | 
					    height: 100px !important;
 | 
				
			||||||
  }
 | 
					  }
 | 
				
			||||||
  // ::v-deep .u-safe-area-inset-bottom {
 | 
					}
 | 
				
			||||||
  //   display: none !important;
 | 
					
 | 
				
			||||||
  // }
 | 
					@media screen and (max-height: 667px) {
 | 
				
			||||||
 | 
					  .tabbar {
 | 
				
			||||||
 | 
					    height: 70px !important;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					@media screen and (min-height: 812px) {
 | 
				
			||||||
 | 
					  .tabbar {
 | 
				
			||||||
 | 
					    height: 80px !important;
 | 
				
			||||||
 | 
					  }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
</style>
 | 
					</style>
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
| 
						 | 
					@ -709,7 +709,8 @@ import {
 | 
				
			||||||
  REPURCHASE_AREA,
 | 
					  REPURCHASE_AREA,
 | 
				
			||||||
  REISSUE_AREA,
 | 
					  REISSUE_AREA,
 | 
				
			||||||
} from '@/util/specialAreaMap'
 | 
					} from '@/util/specialAreaMap'
 | 
				
			||||||
var payStatus
 | 
					let payStatus
 | 
				
			||||||
 | 
					let registerFlag = null
 | 
				
			||||||
export default {
 | 
					export default {
 | 
				
			||||||
  components: {
 | 
					  components: {
 | 
				
			||||||
    successDialog,
 | 
					    successDialog,
 | 
				
			||||||
| 
						 | 
					@ -784,6 +785,10 @@ export default {
 | 
				
			||||||
      this.isQuickSelected = false
 | 
					      this.isQuickSelected = false
 | 
				
			||||||
    },
 | 
					    },
 | 
				
			||||||
  },
 | 
					  },
 | 
				
			||||||
 | 
					  onUnload() {
 | 
				
			||||||
 | 
					    clearInterval(payStatus)
 | 
				
			||||||
 | 
					    clearInterval(this?.clockTime)
 | 
				
			||||||
 | 
					  },
 | 
				
			||||||
  async onLoad(options) {
 | 
					  async onLoad(options) {
 | 
				
			||||||
    this.paramsPost = JSON.parse(options.paramsPost)
 | 
					    this.paramsPost = JSON.parse(options.paramsPost)
 | 
				
			||||||
    // 获取支付配置
 | 
					    // 获取支付配置
 | 
				
			||||||
| 
						 | 
					@ -1461,18 +1466,17 @@ export default {
 | 
				
			||||||
        if (that.sucPay == 1) {
 | 
					        if (that.sucPay == 1) {
 | 
				
			||||||
          // 清除定时器
 | 
					          // 清除定时器
 | 
				
			||||||
          clearInterval(payStatus)
 | 
					          clearInterval(payStatus)
 | 
				
			||||||
          if (
 | 
					 | 
				
			||||||
            [REGIEST_AREA.id, UPGRADE_AREA.id].includes(
 | 
					 | 
				
			||||||
              Number(this.specialArea)
 | 
					 | 
				
			||||||
            )
 | 
					 | 
				
			||||||
          ) {
 | 
					 | 
				
			||||||
            api.registerInfo(this.orderCode).then(res => {
 | 
					 | 
				
			||||||
              this.$refs.successDialog.showSuccess(res.data)
 | 
					 | 
				
			||||||
            })
 | 
					 | 
				
			||||||
          } else {
 | 
					 | 
				
			||||||
          this.wxPopup = false
 | 
					          this.wxPopup = false
 | 
				
			||||||
          this.aliQrCodeVisible = false
 | 
					          this.aliQrCodeVisible = false
 | 
				
			||||||
          this.showSucce = true
 | 
					          this.showSucce = true
 | 
				
			||||||
 | 
					          if ([REGIEST_AREA.id].includes(Number(this.specialArea))) {
 | 
				
			||||||
 | 
					            registerFlag = setTimeout(() => {
 | 
				
			||||||
 | 
					              api.registerInfo(this.orderCode).then(res => {
 | 
				
			||||||
 | 
					                if (res.data) {
 | 
				
			||||||
 | 
					                  this?.$refs?.successDialog?.showSuccess(res.data)
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					              })
 | 
				
			||||||
 | 
					            }, 3000)
 | 
				
			||||||
          }
 | 
					          }
 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
          api.payStatus(data).then(res => {
 | 
					          api.payStatus(data).then(res => {
 | 
				
			||||||
| 
						 | 
					
 | 
				
			||||||
		Loading…
	
		Reference in New Issue