feat: 更新用户转账二维码

This commit is contained in:
2026-01-12 00:28:26 +07:00
parent 405ca17664
commit 4902a155a1
7 changed files with 260 additions and 95 deletions

View File

@@ -1,5 +1,5 @@
<script lang='ts' setup>
import { cardOutline, documentOutline, documentTextOutline } from "ionicons/icons";
import { cardOutline, documentOutline, documentTextOutline, swapHorizontal } from "ionicons/icons";
const { t } = useI18n();
</script>
@@ -28,6 +28,12 @@ const { t } = useI18n();
{{ t('trade.settings.myIssues') }}
</div>
</div>
<div class="col-span-1 flex-col-center gap-2" @click="$router.push('/trade-settings/my-issues')">
<ion-icon :icon="swapHorizontal" />
<div class="text-xs">
发起转账
</div>
</div>
</div>
</div>
</template>

View File

@@ -1,12 +1,16 @@
<script lang='ts' setup>
import { Directory, Filesystem } from "@capacitor/filesystem";
import { Share } from "@capacitor/share";
import { loadingController, toastController } from "@ionic/vue";
import { useQRCode } from "@vueuse/integrations/useQRCode";
import html2canvas from "html2canvas";
import MaterialSymbolsDownload from "~icons/material-symbols/download";
import MaterialSymbolsUpload from "~icons/material-symbols/upload";
const userStore = useUserStore();
const { userProfile } = storeToRefs(userStore);
const url = computed(() => {
return `riwaapp://onchain-address?user=${userProfile.value?.user.email}`;
return `/transfer_to_user/${userProfile.value?.uid}`;
});
const qrcode = useQRCode(url, {
type: "image/webp",
@@ -14,6 +18,167 @@ const qrcode = useQRCode(url, {
quality: 1,
},
});
const { share, isSupported } = useShare();
const platform = usePlatform();
const qrcodeContainer = ref<HTMLElement>();
async function captureQRCode(): Promise<string> {
if (!qrcodeContainer.value) {
throw new Error("QR code container not found");
}
// 克隆元素避免修改原始DOM
const clone = qrcodeContainer.value.cloneNode(true) as HTMLElement;
// 设置固定尺寸和简单样式,避免 oklch 颜色问题
clone.style.width = "400px";
clone.style.height = "400px";
clone.style.backgroundColor = "#ffffff";
clone.style.padding = "20px";
clone.style.borderRadius = "14px";
clone.style.border = "1px solid #e5e7eb";
clone.style.position = "absolute";
clone.style.left = "-9999px";
clone.style.top = "0";
// 临时添加到 DOM
document.body.appendChild(clone);
try {
const canvas = await html2canvas(clone, {
backgroundColor: "#ffffff",
scale: 2,
logging: false,
useCORS: true,
allowTaint: true,
});
return canvas.toDataURL("image/png");
}
finally {
// 清理克隆的元素
document.body.removeChild(clone);
}
}
async function handleShare() {
const loading = await loadingController.create({
message: "准备分享...",
});
await loading.present();
try {
const dataUrl = await captureQRCode();
if (platform === "browser") {
// 浏览器环境使用 Web Share API
if (isSupported.value) {
// 将 base64 转换为 blob
const blob = await (await fetch(dataUrl)).blob();
const file = new File([blob], "qrcode.png", { type: "image/png" });
await share({
title: "我的转账二维码",
text: `我的 ID: ${userProfile.value?.uid}`,
files: [file],
});
}
else {
// 不支持则提示复制链接
await navigator.clipboard.writeText(url.value);
const toast = await toastController.create({
message: "链接已复制到剪贴板",
duration: 2000,
color: "success",
});
await toast.present();
}
}
else {
// 原生环境使用 Capacitor Share
// 先保存到临时目录
const fileName = `qrcode-${Date.now()}.png`;
const base64Data = dataUrl.split(",")[1];
const result = await Filesystem.writeFile({
path: fileName,
data: base64Data,
directory: Directory.Cache,
});
await Share.share({
title: "我的转账二维码",
text: `我的 ID: ${userProfile.value?.uid}`,
url: result.uri,
dialogTitle: "分享二维码",
});
}
}
catch (error) {
console.error("Share error:", error);
const toast = await toastController.create({
message: "分享失败",
duration: 2000,
color: "danger",
});
await toast.present();
}
finally {
await loading.dismiss();
}
}
/**
* 下载二维码
*/
async function handleDownload() {
const loading = await loadingController.create({
message: "正在保存...",
});
await loading.present();
try {
const dataUrl = await captureQRCode();
if (platform === "browser") {
// 浏览器环境直接下载
const link = document.createElement("a");
link.download = `qrcode-${userProfile.value?.uid}.png`;
link.href = dataUrl;
link.click();
}
else {
// 原生环境保存到文档目录
const base64Data = dataUrl.split(",")[1];
const fileName = `riwa-qrcode-${Date.now()}.png`;
await Filesystem.writeFile({
path: fileName,
data: base64Data,
directory: Directory.Documents, // iOS 使用 Documents 目录
});
const toast = await toastController.create({
message: "二维码已保存到文件",
duration: 2000,
color: "success",
});
await toast.present();
}
}
catch (error) {
console.error("Download error:", error);
const toast = await toastController.create({
message: "保存失败",
duration: 2000,
color: "danger",
});
await toast.present();
}
finally {
await loading.dismiss();
}
}
</script>
<template>
@@ -23,12 +188,15 @@ const qrcode = useQRCode(url, {
<ion-buttons slot="start">
<ui-back-button />
</ion-buttons>
<ion-title>我的二维码</ion-title>
<ion-title>转账二维码</ion-title>
</IonToolbar>
</IonHeader>
<IonContent :fullscreen="true" class="ion-padding">
<div class="flex justify-center items-center flex-col mt-20">
<div class="w-[80vw] h-[80vw] bg-gray-50 max-w-130 max-h-130 rounded-[14px] p-4 flex flex-col justify-center items-center relative border border-text-900">
<div
ref="qrcodeContainer"
class="w-[80vw] h-[80vw] bg-gray-50 max-w-130 max-h-130 rounded-[14px] p-4 flex flex-col justify-center items-center relative border border-text-900"
>
<div class="absolute top-0 left-1/2 -translate-x-1/2 -translate-y-1/2 flex flex-col items-center">
<div class="p-1 rounded-full w-fit">
<ui-avatar class="size-18 border border-text-900" />
@@ -41,13 +209,13 @@ const qrcode = useQRCode(url, {
</div>
<div class="mt-4">
<ion-text color="secondary">
<div class="text-sm text-text-400">
Onchain address
<div class="text-sm text-text-400 font-semibold">
我的 ID
</div>
</ion-text>
<ion-text>
<div class="mt-4px text-md font-semibold break-all">
0x1234567890abcdef1234
{{ userProfile?.uid }}
</div>
</ion-text>
</div>
@@ -55,7 +223,7 @@ const qrcode = useQRCode(url, {
</div>
<div class="ion-text-center flex justify-between gap-6 w-[50%] mt-14">
<div class="button">
<div class="button" @click="handleShare">
<div class="icon">
<MaterialSymbolsUpload class="text-xl" />
</div>
@@ -63,12 +231,12 @@ const qrcode = useQRCode(url, {
Share
</div>
</div>
<div class="button">
<div class="button" @click="handleDownload">
<div class="icon">
<MaterialSymbolsDownload class="text-xl" />
</div>
<div class="text">
Copy
Download
</div>
</div>
</div>
@@ -81,7 +249,7 @@ const qrcode = useQRCode(url, {
@reference "tailwindcss";
.button {
@apply flex flex-col items-center gap-2 text-[text-500] font-medium;
@apply flex flex-col items-center gap-2 text-[text-500] font-medium cursor-pointer;
}
.icon {
@apply p-2.5 bg-(--ion-color-faint) rounded-full;