work-order/work-order-uniapp/components/QueueCode.vue

253 lines
6.2 KiB
Vue
Raw Permalink Normal View History

2025-07-27 20:34:15 +08:00
<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>