<script lang="ts" setup>
|
import type { WxCpActivityApi } from '#/api/wxcp/activity';
|
|
import { onUnmounted, ref } from 'vue';
|
|
import { useVbenModal } from '@vben/common-ui';
|
import { Button, message, Spin } from 'ant-design-vue';
|
|
import {
|
getActivityQrCodeBlob,
|
getActivitySignupUrl,
|
} from '#/api/wxcp/activity';
|
|
defineOptions({ name: 'WxCpActivityQrCodeModal' });
|
|
const activity = ref<WxCpActivityApi.Activity>({});
|
const qrCodeUrl = ref('');
|
const signupUrl = ref('');
|
const loading = ref(false);
|
const loadError = ref('');
|
|
/** 释放上一次的 objectURL,避免内存泄漏 */
|
function releaseObjectUrl() {
|
if (qrCodeUrl.value) {
|
URL.revokeObjectURL(qrCodeUrl.value);
|
qrCodeUrl.value = '';
|
}
|
}
|
|
async function loadQrCode(id: number) {
|
loading.value = true;
|
loadError.value = '';
|
releaseObjectUrl();
|
try {
|
// 二维码接口返回 PNG 二进制,且 <img> 带不了请求头,因此走 blob + objectURL
|
const res = (await getActivityQrCodeBlob(id, 400)) as unknown as {
|
data?: Blob;
|
};
|
const blob = res?.data instanceof Blob ? res.data : (res as unknown as Blob);
|
if (!(blob instanceof Blob)) {
|
throw new Error('二维码返回格式异常');
|
}
|
qrCodeUrl.value = URL.createObjectURL(blob);
|
} catch (error: unknown) {
|
loadError.value = (error as Error)?.message || '二维码加载失败';
|
} finally {
|
loading.value = false;
|
}
|
}
|
|
async function loadSignupUrl(id: number) {
|
try {
|
signupUrl.value = await getActivitySignupUrl(id);
|
} catch {
|
// 报名链接取不到不影响二维码展示,静默处理
|
signupUrl.value = '';
|
}
|
}
|
|
function handleDownload() {
|
if (!qrCodeUrl.value || !activity.value.id) {
|
return;
|
}
|
const link = document.createElement('a');
|
link.href = qrCodeUrl.value;
|
link.download = `活动报名码-${activity.value.title || activity.value.id}.png`;
|
document.body.append(link);
|
link.click();
|
link.remove();
|
}
|
|
async function handleCopyLink() {
|
if (!signupUrl.value) {
|
message.warning('暂无报名链接');
|
return;
|
}
|
try {
|
await navigator.clipboard.writeText(signupUrl.value);
|
message.success('报名链接已复制');
|
} catch {
|
message.warning('复制失败,请手动选中链接复制');
|
}
|
}
|
|
const [Modal, modalApi] = useVbenModal({
|
onOpenChange(isOpen: boolean) {
|
if (!isOpen) {
|
activity.value = {};
|
signupUrl.value = '';
|
loadError.value = '';
|
releaseObjectUrl();
|
return;
|
}
|
const data = modalApi.getData<{ activity: WxCpActivityApi.Activity }>();
|
activity.value = data.activity ?? {};
|
if (activity.value.id) {
|
loadQrCode(activity.value.id);
|
loadSignupUrl(activity.value.id);
|
}
|
},
|
});
|
|
onUnmounted(releaseObjectUrl);
|
</script>
|
|
<template>
|
<Modal title="活动报名二维码" class="w-2/5">
|
<div class="flex flex-col items-center px-4">
|
<div class="mb-4 text-center text-sm text-gray-500">
|
{{ activity.title }}
|
</div>
|
|
<Spin :spinning="loading">
|
<div
|
class="flex size-70 items-center justify-center rounded border border-dashed border-gray-300"
|
>
|
<img
|
v-if="qrCodeUrl"
|
:src="qrCodeUrl"
|
alt="活动报名二维码"
|
class="size-65 object-contain"
|
/>
|
<div v-else class="px-6 text-center text-sm text-gray-400">
|
{{ loadError || '二维码加载中…' }}
|
</div>
|
</div>
|
</Spin>
|
|
<div class="mt-4 w-full text-xs text-gray-400 break-all">
|
<div v-if="signupUrl">报名链接:{{ signupUrl }}</div>
|
<div class="mt-2">
|
成员用企业微信扫一扫即可打开报名页并提交报名。
|
</div>
|
</div>
|
</div>
|
|
<template #footer>
|
<div class="flex justify-end gap-2">
|
<Button :disabled="!signupUrl" @click="handleCopyLink">
|
复制报名链接
|
</Button>
|
<Button type="primary" :disabled="!qrCodeUrl" @click="handleDownload">
|
下载二维码
|
</Button>
|
</div>
|
</template>
|
</Modal>
|
</template>
|