fix: QR 预览优化 — 移除虚线边框/圆角/padding,专业白底+微阴影

- 去掉 border-dashed / rounded-2xl / p-4(干扰扫描)
- 纯白容器 + shadow-sm,干净专业
- svgToDataUrl 改用 TextEncoder(替代废弃的 unescape)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
2026-06-18 11:34:05 +08:00
parent 07da35c349
commit 9f76bb31da
+20 -13
View File
@@ -1,10 +1,12 @@
import { useMemo } from 'react';
import { useQrState } from '../store/qrContext';
/** SVG 字符串转为安全的 data URL<img> 标签中浏览器会阻止 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 (
<div className="flex flex-col items-center justify-center gap-3 text-gray-400">
<div className="w-64 h-64 border-2 border-dashed border-gray-300 dark:border-gray-700 rounded-2xl flex items-center justify-center bg-white/50 dark:bg-gray-800/50">
<div className={`${containerCls} border border-gray-200`}>
{state.loading ? (
<span className="text-sm animate-pulse">...</span>
) : (
@@ -31,16 +36,18 @@ export default function QrPreview() {
return (
<div className="flex flex-col items-center gap-3">
{/* SVG 转为 data URL 通过 <img> 渲染,浏览器在 img 上下文中阻止脚本执行 */}
<img
src={svgDataUrl}
alt="QR 码"
className="w-64 h-64 border-2 border-dashed border-gray-300 dark:border-gray-700 rounded-2xl p-4 bg-white dark:bg-white qr-preview"
/>
<div className="flex gap-3 text-xs text-gray-500 dark:text-gray-400">
<span>: {state.preview!.version}</span>
{/* 纯白背景 + 微阴影,无边框/圆角干扰扫描 */}
<div className={containerCls}>
<img
src={svgDataUrl}
alt="QR 码"
className="w-60 h-60"
/>
</div>
<div className="flex gap-3 text-xs text-gray-400">
<span> {state.preview!.version}</span>
<span>{state.preview!.size}×{state.preview!.size}</span>
<span>: {state.preview!.mask}</span>
<span> {state.preview!.mask}</span>
</div>
</div>
);