318 lines
		
	
	
		
			7.0 KiB
		
	
	
	
		
			Vue
		
	
	
	
			
		
		
	
	
			318 lines
		
	
	
		
			7.0 KiB
		
	
	
	
		
			Vue
		
	
	
	
<template>
 | 
						||
  <view class="default-share-page">
 | 
						||
    <!-- 背景图片,替代CSS background -->
 | 
						||
    <img
 | 
						||
      class="share-bg-image"
 | 
						||
      src="/static/images/share-bg.jpg"
 | 
						||
      crossorigin="anonymous"
 | 
						||
      @load="onBackgroundImageLoad"
 | 
						||
      @error="onBackgroundImageError"
 | 
						||
    />
 | 
						||
    <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>
 | 
						||
    <view class="member-code-outer">
 | 
						||
      <text
 | 
						||
        class="member-code-text"
 | 
						||
        style="
 | 
						||
          font-size: 20rpx;
 | 
						||
          color: #fff;
 | 
						||
          font-weight: bold;
 | 
						||
          margin-top: 20rpx;
 | 
						||
        "
 | 
						||
        >{{ desensitization(userInfo.memberCode) }}</text
 | 
						||
      >
 | 
						||
    </view>
 | 
						||
  </view>
 | 
						||
</template>
 | 
						||
 | 
						||
<script>
 | 
						||
import html2canvas from 'html2canvas'
 | 
						||
import { snapdom } from '@zumer/snapdom'
 | 
						||
 | 
						||
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: '加载中...', mask: true })
 | 
						||
 | 
						||
      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
 | 
						||
            }
 | 
						||
 | 
						||
            const scale = 3
 | 
						||
            html2canvas(element, {
 | 
						||
              useCORS: true,
 | 
						||
              // allowTaint: true,
 | 
						||
              backgroundColor: null,
 | 
						||
              scale,
 | 
						||
              // canvas,
 | 
						||
              width: element.offsetWidth,
 | 
						||
              height: element.offsetHeight,
 | 
						||
            })
 | 
						||
              .then(canvas => {
 | 
						||
                const context = canvas.getContext('2d')
 | 
						||
                // context.scale(2, 2)
 | 
						||
                context.mozImageSmoothingEnabled = false
 | 
						||
                context.webkitImageSmoothingEnabled = false
 | 
						||
                context.msImageSmoothingEnabled = false
 | 
						||
                context.imageSmoothingEnabled = false
 | 
						||
                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: 600rpx;
 | 
						||
  height: 1296rpx;
 | 
						||
  // height: 100%;
 | 
						||
  display: flex;
 | 
						||
  flex-direction: column;
 | 
						||
  align-items: center;
 | 
						||
}
 | 
						||
 | 
						||
/* 背景图片样式 */
 | 
						||
.share-bg-image {
 | 
						||
  position: absolute;
 | 
						||
  top: 0;
 | 
						||
  left: 0;
 | 
						||
  width: 600rpx;
 | 
						||
  height: 1296rpx;
 | 
						||
  // 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: 140rpx;
 | 
						||
  height: 140rpx;
 | 
						||
  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;
 | 
						||
  position: absolute;
 | 
						||
  // bottom: 460rpx;
 | 
						||
  left: 374rpx;
 | 
						||
  top: 708rpx;
 | 
						||
  // right: 130rpx;
 | 
						||
 | 
						||
  z-index: 10;
 | 
						||
}
 | 
						||
 | 
						||
.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 {
 | 
						||
  /* 用户可以在这里自定义会员编号的样式 */
 | 
						||
}
 | 
						||
.member-code-outer {
 | 
						||
  position: absolute;
 | 
						||
  top: 1086rpx;
 | 
						||
  left: 246rpx;
 | 
						||
  // width: 300rpx;
 | 
						||
  text-align: center;
 | 
						||
  // background: rgb(217, 24, 25);
 | 
						||
  border-radius: 20rpx;
 | 
						||
  z-index: 10;
 | 
						||
}
 | 
						||
 | 
						||
/* 下载按钮样式 */
 | 
						||
.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>
 |