From 310a501380e9ca9f082c60770ae8e2a4312a2281 Mon Sep 17 00:00:00 2001 From: jxxghp Date: Sun, 10 May 2026 22:10:30 +0800 Subject: [PATCH] feat: implement QR code generation for WechatClawBot status display --- .../cards/NotificationChannelCard.vue | 58 ++++++++++++++++++- 1 file changed, 56 insertions(+), 2 deletions(-) diff --git a/src/components/cards/NotificationChannelCard.vue b/src/components/cards/NotificationChannelCard.vue index c63086a9..38d71b18 100644 --- a/src/components/cards/NotificationChannelCard.vue +++ b/src/components/cards/NotificationChannelCard.vue @@ -4,6 +4,7 @@ import { NotificationConf } from '@/api/types' import { getLogoUrl } from '@/utils/imageUtils' import { useToast } from 'vue-toastification' import { cloneDeep } from 'lodash-es' +import QRCode from 'qrcode' import { useI18n } from 'vue-i18n' import { useDisplay } from 'vuetify' @@ -115,8 +116,57 @@ function ensureWechatClawBotConfigDefaults(notification: NotificationConf) { const wechatClawBotLoading = ref(false) const wechatClawBotActionLoading = ref(false) const wechatClawBotStatus = ref(null) +const wechatClawBotQrImage = ref('') let wechatClawBotTimer: number | null = null +function isImageSource(value?: string | null) { + if (!value) { + return false + } + const raw = value.trim() + if (!raw) { + return false + } + if (raw.toLowerCase().startsWith('data:image/')) { + return true + } + return /\.(png|jpe?g|gif|webp|svg)(\?|$)/i.test(raw) +} + +function getWechatClawBotQrText(status?: WechatClawBotStatus | null) { + const directUrl = status?.qrcode_url?.trim() + if (directUrl) { + return directUrl + } + const qrcode = status?.qrcode?.trim() + if (!qrcode) { + return '' + } + return `https://liteapp.weixin.qq.com/q/7GiQu1?qrcode=${encodeURIComponent(qrcode)}&bot_type=3` +} + +async function updateWechatClawBotQrImage(status?: WechatClawBotStatus | null) { + const directUrl = status?.qrcode_url?.trim() + if (isImageSource(directUrl)) { + wechatClawBotQrImage.value = directUrl || '' + return + } + const qrText = getWechatClawBotQrText(status) + if (!qrText) { + wechatClawBotQrImage.value = '' + return + } + try { + wechatClawBotQrImage.value = await QRCode.toDataURL(qrText, { + width: 220, + margin: 1, + }) + } catch (error) { + console.error(error) + wechatClawBotQrImage.value = '' + } +} + function getWechatClawBotRequestParams(extraParams: Record = {}) { const config = notificationInfo.value.config || {} return { @@ -206,9 +256,11 @@ async function fetchWechatClawBotStatus(autoGenerateQrcode = false) { }) if (result.success) { wechatClawBotStatus.value = result.data + await updateWechatClawBotQrImage(result.data) scheduleWechatClawBotRefresh() } else { wechatClawBotStatus.value = null + wechatClawBotQrImage.value = '' clearWechatClawBotTimer() $toast.error(result.message || t('notification.wechatclawbot.statusLoadFailed')) } @@ -232,6 +284,7 @@ async function refreshWechatClawBotQrcode() { }) if (result.success) { wechatClawBotStatus.value = result.data + await updateWechatClawBotQrImage(result.data) scheduleWechatClawBotRefresh() $toast.success(t('notification.wechatclawbot.qrcodeRefreshSuccess')) } else { @@ -327,6 +380,7 @@ function onClose() { watch(notificationInfoDialog, value => { if (!value) { clearWechatClawBotTimer() + wechatClawBotQrImage.value = '' } }) @@ -617,8 +671,8 @@ watch(notificationInfoDialog, value => {