vending-machine/mobile/src/pages/index/QrcodePopup.vue
2026-04-03 16:35:38 +08:00

111 lines
2.9 KiB
Vue

<template>
<!-- 会员二维码弹窗 -->
<view v-if="visible" class="popup-mask" @click="close">
<view class="popup-content" @click.stop>
<view class="popup-header">
<text class="popup-title">{{ t('home.qrcode') }}</text>
<text class="popup-close" @click="close">✕</text>
</view>
<view class="qrcode-area">
<view v-if="loading" class="qrcode-loading">
<text>{{ t('common.loading') }}</text>
</view>
<view v-else class="qrcode-box">
<!-- 动态二维码展示区域,实际使用 canvas 或图片渲染 -->
<image
v-if="qrcodeUrl"
:src="qrcodeUrl"
class="qrcode-image"
mode="aspectFit"
/>
<text v-else class="qrcode-placeholder">{{ qrcodeToken }}</text>
</view>
<text class="qrcode-tip">{{ t('home.qrcodeTip') }}</text>
</view>
</view>
</view>
</template>
<script setup lang="ts">
import { ref, watch } from 'vue'
import { useI18n } from 'vue-i18n'
import { generateQrcode as apiGenerateQrcode } from '@/api/vending'
const { t } = useI18n()
const props = defineProps<{
visible: boolean
userId: string
}>()
const emit = defineEmits<{
(e: 'update:visible', val: boolean): void
}>()
const loading = ref(false)
const qrcodeToken = ref('')
const qrcodeUrl = ref('')
// 监听弹窗打开,生成二维码
watch(() => props.visible, async (val) => {
if (val && props.userId) {
await generateQrcode()
}
})
// 生成动态二维码(调用后端接口)
async function generateQrcode() {
loading.value = true
try {
const { token, expiresAt } = await apiGenerateQrcode()
qrcodeToken.value = token
// 实际项目中使用二维码生成库将 token 渲染为二维码图片
qrcodeUrl.value = ''
} catch {
// 错误已由 request 层处理
} finally {
loading.value = false
}
}
function close() {
emit('update:visible', false)
}
</script>
<style scoped>
.popup-mask {
position: fixed;
top: 0; left: 0; right: 0; bottom: 0;
background: rgba(0, 0, 0, 0.5);
display: flex;
align-items: center;
justify-content: center;
z-index: 999;
}
.popup-content {
background: #fff;
border-radius: 16rpx;
width: 600rpx;
padding: 40rpx;
}
.popup-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 30rpx;
}
.popup-title { font-size: 34rpx; font-weight: bold; }
.popup-close { font-size: 36rpx; color: #999; padding: 10rpx; }
.qrcode-area { display: flex; flex-direction: column; align-items: center; }
.qrcode-loading { padding: 60rpx 0; }
.qrcode-box {
width: 400rpx; height: 400rpx;
display: flex; align-items: center; justify-content: center;
border: 1rpx solid #eee; border-radius: 8rpx;
}
.qrcode-image { width: 380rpx; height: 380rpx; }
.qrcode-placeholder { font-size: 24rpx; color: #999; word-break: break-all; padding: 20rpx; }
.qrcode-tip { font-size: 24rpx; color: #999; margin-top: 20rpx; }
</style>