diff --git a/gui/src-frontend/src/components/QrPreview.tsx b/gui/src-frontend/src/components/QrPreview.tsx
index 36360f2..7ecc85a 100644
--- a/gui/src-frontend/src/components/QrPreview.tsx
+++ b/gui/src-frontend/src/components/QrPreview.tsx
@@ -1,10 +1,12 @@
import { useMemo } from 'react';
import { useQrState } from '../store/qrContext';
-/** 将 SVG 字符串转为安全的 data URL( 标签中浏览器会阻止 SVG 内的脚本执行) */
+/** SVG 字符串 → data URL(安全渲染,img 上下文阻止脚本执行) */
function svgToDataUrl(svg: string): string {
- const encoded = btoa(unescape(encodeURIComponent(svg)));
- return `data:image/svg+xml;base64,${encoded}`;
+ // TextEncoder 比 btoa(unescape(...)) 更可靠,正确支持 UTF-8
+ const bytes = new TextEncoder().encode(svg);
+ const binStr = Array.from(bytes, (b) => String.fromCharCode(b)).join('');
+ return `data:image/svg+xml;base64,${btoa(binStr)}`;
}
export default function QrPreview() {
@@ -15,10 +17,13 @@ export default function QrPreview() {
[state.preview?.svg],
);
+ const containerCls =
+ 'w-64 h-64 flex items-center justify-center bg-white rounded-xl shadow-sm';
+
if (!svgDataUrl) {
return (