321 lines
		
	
	
		
			7.2 KiB
		
	
	
	
		
			Vue
		
	
	
	
		
		
			
		
	
	
			321 lines
		
	
	
		
			7.2 KiB
		
	
	
	
		
			Vue
		
	
	
	
|  | <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> |