253 lines
6.2 KiB
Vue
253 lines
6.2 KiB
Vue
<template>
|
||
<view class="queue-code">
|
||
<!-- 预览图片 -->
|
||
<image
|
||
v-if="previewUrl"
|
||
:src="previewUrl"
|
||
class="preview-image"
|
||
:style="{width: width + 'px', height: height + 'px'}"
|
||
mode="aspectFit"
|
||
/>
|
||
<!-- 用于生成图片的canvas -->
|
||
<canvas
|
||
canvas-id="queueCode"
|
||
id="queueCode"
|
||
:style="{width: width + 'px', height: height + 'px'}"
|
||
/>
|
||
</view>
|
||
</template>
|
||
|
||
<script>
|
||
import { getWxQRCode } from '@/api/common'
|
||
import { parseLocalUrl } from '@/utils'
|
||
/**
|
||
* QueueCode 队伍小程序码组件
|
||
* @description 用于展示和保存队伍小程序码,支持自定义底部文字
|
||
* @property {String} scene - 小程序码的场景值
|
||
* @property {String} title - 底部显示的队伍名称
|
||
* @property {Number} width - 画布宽度,默认300
|
||
* @property {Number} height - 画布高度,默认400
|
||
* @property {String} page - 小程序码跳转的页面路径
|
||
* @event {Function} onSave - 保存成功的回调
|
||
* @event {Function} onError - 出错的回调
|
||
*/
|
||
export default {
|
||
name: 'QueueCode',
|
||
props: {
|
||
scene: {
|
||
type: String,
|
||
required: true
|
||
},
|
||
title: {
|
||
type: String,
|
||
required: true
|
||
},
|
||
width: {
|
||
type: Number,
|
||
default: 240
|
||
},
|
||
height: {
|
||
type: Number,
|
||
default: 320
|
||
},
|
||
page: {
|
||
type: String,
|
||
default: null
|
||
}
|
||
},
|
||
data() {
|
||
return {
|
||
ctx: null,
|
||
previewUrl: '',
|
||
qrCodePath: '',
|
||
isLoading: false
|
||
}
|
||
},
|
||
watch: {
|
||
scene: {
|
||
immediate: true,
|
||
handler(val) {
|
||
if (val) {
|
||
this.init()
|
||
}
|
||
}
|
||
}
|
||
},
|
||
methods: {
|
||
async init() {
|
||
if (this.isLoading) return
|
||
this.isLoading = true
|
||
try {
|
||
// 获取小程序码
|
||
await this.getWxacode()
|
||
// 绘制画布
|
||
await this.drawCanvas()
|
||
} catch (error) {
|
||
console.error('初始化小程序码失败:', error)
|
||
this.$emit('error', error)
|
||
uni.showToast({
|
||
title: '生成小程序码失败',
|
||
icon: 'none'
|
||
})
|
||
} finally {
|
||
this.isLoading = false
|
||
}
|
||
},
|
||
|
||
// 获取小程序码
|
||
async getWxacode() {
|
||
try {
|
||
const res = await getWxQRCode(this.page, this.scene)
|
||
if (res.code === 200 && res.data) {
|
||
this.qrCodePath = parseLocalUrl(res.data)
|
||
} else {
|
||
throw new Error('获取小程序码失败')
|
||
}
|
||
} catch (error) {
|
||
console.error('获取小程序码失败:', error)
|
||
throw error
|
||
}
|
||
},
|
||
|
||
// 绘制画布
|
||
async drawCanvas() {
|
||
return new Promise((resolve, reject) => {
|
||
try {
|
||
const ctx = uni.createCanvasContext('queueCode', this)
|
||
|
||
// 绘制白色背景
|
||
ctx.fillStyle = '#ffffff'
|
||
ctx.fillRect(0, 0, this.width, this.height)
|
||
|
||
// 绘制顶部大标题
|
||
ctx.setFontSize(18)
|
||
ctx.setFillStyle('#333333')
|
||
ctx.setTextAlign('center')
|
||
ctx.fillText('微信扫码', this.width/2, 30)
|
||
|
||
// 绘制顶部小标题
|
||
ctx.setFontSize(12)
|
||
ctx.setFillStyle('#666666')
|
||
ctx.setTextAlign('center')
|
||
ctx.fillText('扫码取号·等待叫号·叫号进场', this.width/2, 55)
|
||
|
||
// 下载并绘制二维码
|
||
uni.downloadFile({
|
||
url: this.qrCodePath,
|
||
success: (res) => {
|
||
const qrSize = 180
|
||
const qrX = (this.width - qrSize) / 2
|
||
const qrY = 70
|
||
|
||
// 绘制二维码
|
||
ctx.drawImage(res.tempFilePath, qrX, qrY, qrSize, qrSize)
|
||
|
||
// 绘制底部标题背景
|
||
const titleY = qrY + qrSize + 20
|
||
const titleHeight = 50
|
||
ctx.fillStyle = '#F8F9FA'
|
||
ctx.fillRect(0, titleY, this.width, titleHeight)
|
||
|
||
// 绘制底部标题
|
||
const fontSize = 14
|
||
ctx.setFontSize(fontSize)
|
||
ctx.setFillStyle('#333333')
|
||
ctx.setTextAlign('center')
|
||
ctx.fillText(this.title, this.width/2, titleY + titleHeight/2 + fontSize/2)
|
||
|
||
ctx.draw(false, () => {
|
||
resolve()
|
||
})
|
||
},
|
||
fail: (error) => {
|
||
console.error('下载小程序码失败:', error)
|
||
reject(error)
|
||
}
|
||
})
|
||
} catch (error) {
|
||
console.error('绘制画布失败:', error)
|
||
reject(error)
|
||
}
|
||
})
|
||
},
|
||
|
||
// 保存图片
|
||
async save() {
|
||
if (this.isLoading) {
|
||
uni.showToast({
|
||
title: '正在生成小程序码,请稍后',
|
||
icon: 'none'
|
||
})
|
||
return
|
||
}
|
||
|
||
try {
|
||
// 获取用户授权
|
||
const auth = await uni.authorize({
|
||
scope: 'scope.writePhotosAlbum'
|
||
}).catch(() => null)
|
||
|
||
if (!auth) {
|
||
uni.showModal({
|
||
title: '提示',
|
||
content: '需要保存相册权限,是否去设置?',
|
||
success: (res) => {
|
||
if (res.confirm) {
|
||
uni.openSetting()
|
||
}
|
||
}
|
||
})
|
||
return
|
||
}
|
||
|
||
// 将画布内容保存为临时文件
|
||
const res = await new Promise((resolve, reject) => {
|
||
uni.canvasToTempFilePath({
|
||
canvasId: 'queueCode',
|
||
success: resolve,
|
||
fail: reject
|
||
}, this)
|
||
})
|
||
|
||
// 保存图片到相册
|
||
await uni.saveImageToPhotosAlbum({
|
||
filePath: res.tempFilePath
|
||
})
|
||
|
||
this.$emit('save')
|
||
uni.showToast({
|
||
title: '保存成功',
|
||
icon: 'success'
|
||
})
|
||
} catch (error) {
|
||
console.error('保存小程序码失败:', error)
|
||
this.$emit('error', error)
|
||
uni.showToast({
|
||
title: '保存失败',
|
||
icon: 'none'
|
||
})
|
||
}
|
||
}
|
||
}
|
||
}
|
||
</script>
|
||
|
||
<style lang="scss" scoped>
|
||
.queue-code {
|
||
position: relative;
|
||
display: flex;
|
||
justify-content: center;
|
||
align-items: center;
|
||
|
||
.preview-image {
|
||
background-color: #ffffff;
|
||
border-radius: 8rpx;
|
||
}
|
||
|
||
canvas {
|
||
background-color: #ffffff;
|
||
border-radius: 8rpx;
|
||
box-shadow: 0 4rpx 12rpx rgba(0, 0, 0, 0.05);
|
||
}
|
||
}
|
||
</style> |