web-retail-h5/components/share/SpecialSharePage.vue

502 lines
14 KiB
Vue
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<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)
}
},
// 获取特殊场景背景图片
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>