510 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			Vue
		
	
	
	
			
		
		
	
	
			510 lines
		
	
	
		
			14 KiB
		
	
	
	
		
			Vue
		
	
	
	
<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>
 |