178 lines
4.0 KiB
Vue
178 lines
4.0 KiB
Vue
<template>
|
||
<BasePage>
|
||
<div class="paycode-page">
|
||
<div class="paycode-wrap">
|
||
<!-- 生成后的图片 -->
|
||
<div v-if="generatedImage" class="paycode-generated">
|
||
<img :src="generatedImage" alt="收款码">
|
||
</div>
|
||
<!-- 原始元素 -->
|
||
<div v-else class="paycode-original" ref="paycodeOriginalRef">
|
||
<img src="/img/paycode.jpg" alt="" style="width: 100%; display: block;" class="bg-img" @load="onBgLoad">
|
||
<div class="user-info">
|
||
<div class="info">
|
||
<img :src="$file(shopimg)" alt="">
|
||
</div>
|
||
<div class="name">
|
||
<span>{{ shopname?.length > 8 ? shopname.slice(0, 8) + '...' : shopname }}</span>
|
||
</div>
|
||
</div>
|
||
<div class="code">
|
||
<vue-qr v-if="link" :margin="0" :text="link" backgroundColor="rgb(255,255,255)"
|
||
colorLight="rgb(255,255,255)"></vue-qr>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
<div class="save-btn">
|
||
<van-button type="primary" color="#ca2904" round block :loading="loading"> 长按图片保存 </van-button>
|
||
</div>
|
||
</div>
|
||
</BasePage>
|
||
</template>
|
||
|
||
<script>
|
||
import { toDataURL, downloadByDataURL } from '@/utils/html2image'
|
||
|
||
export default {
|
||
name: 'PayCode',
|
||
data() {
|
||
return {
|
||
shopname: '',
|
||
shopimg: '',
|
||
link: '',
|
||
loading: false,
|
||
generatedImage: '',
|
||
bgLoaded: false,
|
||
}
|
||
},
|
||
mounted() {
|
||
this.init()
|
||
},
|
||
methods: {
|
||
init() {
|
||
const id = this.$route.query.id
|
||
this.$get(`/v1/client/DShopsClient/${id}`).then(res => {
|
||
this.shopname = res.data.shopname
|
||
this.shopimg = res.data.shopimg
|
||
this.link = `${location.origin}${location.pathname}#/Checkout?id=${res.data.userid}&RecommendCode=${localStorage.getItem('cellphone')}`
|
||
this.$nextTick(() => {
|
||
this.generateImage()
|
||
})
|
||
}).catch(err => {
|
||
this.$showFailToast('加载失败')
|
||
})
|
||
},
|
||
|
||
onBgLoad() {
|
||
this.bgLoaded = true
|
||
},
|
||
|
||
|
||
async generateImage() {
|
||
if (this.loading) return
|
||
this.loading = true
|
||
try {
|
||
await this.$nextTick()
|
||
const el = this.$refs.paycodeOriginalRef
|
||
if (!el) throw new Error('Element not found')
|
||
|
||
// 等待元素内所有 <img>加载完成(覆盖背景、商家头像、vue - qr 渲染完成的 img)
|
||
const imgs = Array.from(el.querySelectorAll('img'))
|
||
await Promise.all(imgs.map(img => {
|
||
if (img.complete && img.naturalWidth > 0) return
|
||
Promise.resolve()
|
||
return new Promise(resolve => {
|
||
img.onload = img.onerror = resolve
|
||
})
|
||
}))
|
||
|
||
// 等 vue-qr 内部的 canvas 真正画出来(vue-qr 内部用 canvas 绘图)
|
||
await new Promise(resolve => setTimeout(resolve, 100))
|
||
|
||
this.generatedImage = await toDataURL(el, {
|
||
format: 'png',
|
||
pixelRatio: 3, useCORS: true
|
||
})
|
||
} catch (e) {
|
||
console.error('生成收款码失败:', e)
|
||
this.$showFailToast('生成失败')
|
||
} finally {
|
||
this.loading = false
|
||
}
|
||
}
|
||
|
||
},
|
||
}
|
||
</script>
|
||
|
||
<style lang="less" scoped>
|
||
.paycode-page {
|
||
// min-height: 100vh;
|
||
background: #f5f5f5;
|
||
padding-bottom: 20px;
|
||
}
|
||
|
||
.paycode-generated {
|
||
img {
|
||
width: 100%;
|
||
display: block;
|
||
}
|
||
}
|
||
|
||
.paycode-original {
|
||
position: relative;
|
||
width: 100%;
|
||
font-family: 'PingFang SC';
|
||
|
||
>img {
|
||
width: 100%;
|
||
display: block;
|
||
}
|
||
|
||
.user-info {
|
||
position: absolute;
|
||
left: 50%;
|
||
transform: translateX(-50%);
|
||
top: 32.27vw;
|
||
display: flex;
|
||
align-items: center;
|
||
}
|
||
|
||
.info {
|
||
display: flex;
|
||
align-items: center;
|
||
|
||
img {
|
||
width: 6.4vw;
|
||
height: 6.4vw;
|
||
// border-radius: 50%;
|
||
}
|
||
}
|
||
|
||
.name {
|
||
margin-left: 2.67vw;
|
||
// margin-bottom: 2.4vw;
|
||
|
||
span {
|
||
font-size: 4vw;
|
||
color: #333;
|
||
}
|
||
}
|
||
|
||
.code {
|
||
position: absolute;
|
||
top: 42.4vw;
|
||
left: 23.33vw;
|
||
|
||
canvas,
|
||
img {
|
||
width: 53.33vw;
|
||
height: 53.33vw;
|
||
}
|
||
}
|
||
}
|
||
|
||
.save-btn {
|
||
margin: 20px;
|
||
}
|
||
</style> |