3
0
Fork 0
web-store-retail-h5/pages/mine/share/index.vue

533 lines
13 KiB
Vue
Raw 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
class="share-page"
style="display: flex; flex-direction: column; height: 100vh"
>
<view id="shareContainer" class="share-container">
<!-- 微信环境只有在未生成分享图时才显示原始内容 -->
<template v-if="!isWechat || (isWechat && !generatedImageUrl)">
<!-- 背景图片替代CSS background -->
<img
class="share-bg-image"
src="/static/images/share-bg.jpg"
mode="scaleToFill"
crossorigin="anonymous"
@load="onBackgroundImageLoad"
@error="onBackgroundImageError"
/>
<view class="share-wrapper">
<view class="portal-frame" :class="{ 'is-loaded': isLoaded }">
<view class="qr-code-outer">
<img
class="qr-code"
:src="qrCodeImage"
mode="aspectFit"
v-if="qrCodeImage"
/>
<view v-else class="qr-code-placeholder">
<view class="loader"></view>
</view>
</view>
<div
style="
font-size: 26rpx;
color: #fff;
font-weight: bold;
margin-top: 10rpx;
text-align: center;
display: flex;
align-items: center;
justify-content: center;
"
>
{{ desensitization(userInfo.memberCode) }}
</div>
<div v-if="!isWechat" class="share-button" @click="sharePage">
保存图片并分享
</div>
<img
v-if="isWechat"
class="share-btn"
style="margin-top: 50rpx"
src="/static/images/share-btn.svg"
mode="scaleToFill"
/>
</view>
<!-- <image
class="share-bg-logo"
src="/static/images/share-logo.png"
mode="scaleToFill"
></image> -->
</view>
</template>
<view
class="wechat-fullscreen-overlay"
v-show="isWechat && generatedImageUrl"
@click="closeFullscreenImage"
>
<image
class="fullscreen-image"
:src="generatedImageUrl"
mode="scaleToFill"
@load="onGeneratedImageLoad"
@error="onGeneratedImageError"
@click.stop=""
></image>
</view>
</view>
<cl-tabbar class="tabbar" :current="2" />
</view>
</template>
<script>
import html2canvas from 'html2canvas'
import { getShareCode } from '@/config/share'
import clTabbar from '@/components/cl-tabbar.vue'
export default {
name: 'ShareQRCode',
components: {
'cl-tabbar': clTabbar,
},
data() {
return {
qrCodeImage: '',
// Set canvas dimensions. It's better to get device screen width for this.
canvasWidth: 375,
canvasHeight: 800,
isLoaded: false,
shareButtonShow: true,
isWechat: false, // 是否微信环境
generatedImageUrl: '', // 生成的图片URL用于微信长按保存
backgroundImageLoaded: false, // 背景图片是否加载完成
userInfo: uni.getStorageSync('User'),
}
},
onLoad() {
this.checkWechatEnvironment()
this.handleGetShareCode()
// Get screen width to set canvas width dynamically
uni.getSystemInfo({
success: res => {
this.canvasWidth = res.windowWidth
this.canvasHeight = res.windowHeight
},
})
},
onReady() {
// Use a short timeout to ensure the initial render is complete before animation
setTimeout(() => {
this.isLoaded = true
}, 100)
},
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() {
const ua = navigator.userAgent.toLowerCase()
this.isWechat = ua.includes('micromessenger')
console.log('微信环境检测:', this.isWechat)
},
handleGetShareCode() {
// Don't show loading toast, use the placeholder loader instead
// uni.showLoading({ title: '加载中...' })
getShareCode()
.then(res => {
// The screenshot shows the base64 string is in data.datStr
if (res.code === 200 && res.data && res.data.dataStr) {
this.qrCodeImage = 'data:image/png;base64,' + res.data.dataStr
this.$nextTick(() => {
if (this.isWechat) {
this.sharePage()
}
})
} else {
uni.showToast({
title: '获取分享码失败',
icon: 'none',
})
}
})
.catch(err => {
console.error('getShareCode error:', err)
uni.showToast({
title: '网络错误,请稍后再试',
icon: 'none',
})
})
},
async sharePage() {
if (!this.qrCodeImage) {
uni.showToast({
title: '二维码尚未生成',
icon: 'none',
})
return
}
// 统一使用html2canvas生成图片
uni.showLoading({ title: '加载中...' })
try {
// 隐藏按钮等待DOM更新
this.shareButtonShow = false
await this.$nextTick()
// 使用html2canvas截取页面
await this.capturePageWithHtml2Canvas()
} catch (error) {
uni.hideLoading()
uni.showToast({ title: '图片生成失败,请稍后重试', icon: 'none' })
console.error('sharePage error:', error)
} finally {
// 恢复按钮显示
this.shareButtonShow = true
}
},
// 生成的图片加载成功
onGeneratedImageLoad() {
console.log('生成的图片加载成功')
},
// 生成的图片加载失败
onGeneratedImageError(e) {
console.error('生成的图片加载失败:', e)
uni.showToast({
title: '图片显示失败',
icon: 'none',
})
},
// 关闭全屏图片
closeFullscreenImage() {
this.generatedImageUrl = ''
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>
<style lang="scss" scoped>
.share-container {
flex: 1;
height: 0;
box-sizing: border-box;
position: relative;
overflow: hidden;
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-bg {
width: 100%;
height: 100%;
img {
width: 100%;
height: 100%;
}
}
.share-wrapper {
position: absolute;
z-index: 2;
bottom: 280rpx;
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;
text-align: center;
}
.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;
}
.share-button {
margin-top: 58rpx; /* 从32rpx减小到28rpx */
width: 380rpx; /* 设置固定宽度 */
color: #fff;
border-radius: 36rpx; /* 从44rpx减小到36rpx */
font-size: 16rpx; /* 从30rpx减小到26rpx */
font-weight: 500;
padding: 12rpx 0;
display: flex;
align-items: flex-start;
justify-content: center;
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
// box-shadow: 0 5rpx 12rpx 0 rgba(102, 126, 234, 0.2);
border: none;
border-radius: 70rpx;
text-align: center;
letter-spacing: 1rpx;
}
/* 微信环境全屏覆盖样式 */
.wechat-fullscreen-overlay {
position: absolute;
top: 0;
left: 0;
width: 100vw;
height: 100%; /* 减去tab栏高度 */
background-color: transparent; /* 移除背景色 */
z-index: 999;
display: flex;
flex-direction: column;
align-items: stretch; /* 拉伸对齐 */
justify-content: stretch; /* 拉伸对齐 */
padding: 0;
margin: 0;
}
.fullscreen-image {
width: 100vw;
height: 100%; /* 减去tab栏高度 */
object-fit: cover; /* 覆盖整个容器,可能会裁剪 */
margin: 0;
padding: 0;
border: none;
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 {
position: static;
bottom: 0;
z-index: 1000;
width: 100%;
height: 50px;
:v-deep .u-tabbar--fixed {
height: 100px !important;
}
// ::v-deep .u-safe-area-inset-bottom {
// display: none !important;
// }
}
</style>