Browse Source
- 移除了未使用的 QRCodeGenerator 组件 - 在 SimpleQRCodeModal组件中添加了礼品卡名称和面值的显示 - 更新了 SimpleQRCodeModal 的样式,使其更加居中 - 在 gift/detail.tsx 中传递了礼品卡名称和面值给 SimpleQRCodeModalmaster
3 changed files with 30 additions and 291 deletions
@ -1,284 +0,0 @@ |
|||
import React, { useState, useEffect, useRef } from 'react' |
|||
import { View, Text, Canvas } from '@tarojs/components' |
|||
import { Button, Popup } from '@nutui/nutui-react-taro' |
|||
import { Close, Copy, Download } from '@nutui/icons-react-taro' |
|||
import Taro from '@tarojs/taro' |
|||
|
|||
export interface QRCodeGeneratorProps { |
|||
/** 是否显示弹窗 */ |
|||
visible: boolean |
|||
/** 关闭弹窗回调 */ |
|||
onClose: () => void |
|||
/** 二维码内容 */ |
|||
text: string |
|||
/** 二维码尺寸 */ |
|||
size?: number |
|||
/** 礼品卡名称 */ |
|||
title?: string |
|||
/** 礼品卡面值 */ |
|||
subtitle?: string |
|||
} |
|||
|
|||
const QRCodeGenerator: React.FC<QRCodeGeneratorProps> = ({ |
|||
visible, |
|||
onClose, |
|||
text, |
|||
size = 200, |
|||
title, |
|||
subtitle |
|||
}) => { |
|||
const [qrDataURL, setQrDataURL] = useState<string>('') |
|||
const [loading, setLoading] = useState(false) |
|||
const canvasRef = useRef<string>('qrcode-canvas') |
|||
|
|||
// 使用Canvas API生成二维码
|
|||
const generateQRCode = async () => { |
|||
if (!text || !visible) return |
|||
|
|||
setLoading(true) |
|||
try { |
|||
// 方案1: 使用在线API生成二维码
|
|||
const qrApiUrl = `https://api.qrserver.com/v1/create-qr-code/?size=${size}x${size}&data=${encodeURIComponent(text)}` |
|||
setQrDataURL(qrApiUrl) |
|||
|
|||
// 方案2: 如果需要离线生成,可以使用Canvas绘制
|
|||
// await drawQRCodeOnCanvas()
|
|||
|
|||
} catch (error) { |
|||
console.error('生成二维码失败:', error) |
|||
Taro.showToast({ |
|||
title: '生成二维码失败', |
|||
icon: 'error' |
|||
}) |
|||
} finally { |
|||
setLoading(false) |
|||
} |
|||
} |
|||
|
|||
// 使用Canvas绘制二维码(简化版本)
|
|||
const drawQRCodeOnCanvas = async () => { |
|||
const ctx = Taro.createCanvasContext(canvasRef.current) |
|||
|
|||
// 清空画布
|
|||
ctx.clearRect(0, 0, size, size) |
|||
|
|||
// 绘制白色背景
|
|||
ctx.setFillStyle('#ffffff') |
|||
ctx.fillRect(0, 0, size, size) |
|||
|
|||
// 绘制黑色边框
|
|||
ctx.setStrokeStyle('#000000') |
|||
ctx.setLineWidth(2) |
|||
ctx.strokeRect(0, 0, size, size) |
|||
|
|||
// 绘制定位点
|
|||
const drawFinderPattern = (x: number, y: number) => { |
|||
ctx.setFillStyle('#000000') |
|||
ctx.fillRect(x, y, 28, 28) |
|||
ctx.setFillStyle('#ffffff') |
|||
ctx.fillRect(x + 4, y + 4, 20, 20) |
|||
ctx.setFillStyle('#000000') |
|||
ctx.fillRect(x + 8, y + 8, 12, 12) |
|||
} |
|||
|
|||
// 三个角的定位点
|
|||
drawFinderPattern(10, 10) // 左上
|
|||
drawFinderPattern(size - 38, 10) // 右上
|
|||
drawFinderPattern(10, size - 38) // 左下
|
|||
|
|||
// 生成数据点(模拟二维码数据)
|
|||
ctx.setFillStyle('#000000') |
|||
const moduleSize = 4 |
|||
const modules = Math.floor((size - 80) / moduleSize) |
|||
|
|||
for (let i = 0; i < modules; i++) { |
|||
for (let j = 0; j < modules; j++) { |
|||
// 简单的伪随机算法,基于文本内容生成固定的图案
|
|||
const hash = text.charCodeAt(i % text.length) + text.charCodeAt(j % text.length) |
|||
if (hash % 3 === 0) { |
|||
const x = 40 + i * moduleSize |
|||
const y = 40 + j * moduleSize |
|||
ctx.fillRect(x, y, moduleSize - 1, moduleSize - 1) |
|||
} |
|||
} |
|||
} |
|||
|
|||
ctx.draw() |
|||
} |
|||
|
|||
// 复制文本内容
|
|||
const copyText = () => { |
|||
if (text) { |
|||
Taro.setClipboardData({ |
|||
data: text, |
|||
success: () => { |
|||
Taro.showToast({ |
|||
title: '内容已复制', |
|||
icon: 'success' |
|||
}) |
|||
} |
|||
}) |
|||
} |
|||
} |
|||
|
|||
// 保存二维码图片
|
|||
const saveQRCode = () => { |
|||
if (qrDataURL) { |
|||
Taro.downloadFile({ |
|||
url: qrDataURL, |
|||
success: (res) => { |
|||
Taro.saveImageToPhotosAlbum({ |
|||
filePath: res.tempFilePath, |
|||
success: () => { |
|||
Taro.showToast({ |
|||
title: '保存成功', |
|||
icon: 'success' |
|||
}) |
|||
}, |
|||
fail: () => { |
|||
Taro.showToast({ |
|||
title: '保存失败', |
|||
icon: 'error' |
|||
}) |
|||
} |
|||
}) |
|||
} |
|||
}) |
|||
} |
|||
} |
|||
|
|||
// 当弹窗打开时生成二维码
|
|||
useEffect(() => { |
|||
if (visible && text) { |
|||
generateQRCode() |
|||
} |
|||
}, [visible, text]) |
|||
|
|||
return ( |
|||
<Popup |
|||
visible={visible} |
|||
position="center" |
|||
closeable |
|||
closeIcon={<Close />} |
|||
onClose={onClose} |
|||
style={{ |
|||
width: '90%', |
|||
maxWidth: '400px', |
|||
borderRadius: '12px' |
|||
}} |
|||
> |
|||
<View className="p-6"> |
|||
{/* 标题 */} |
|||
<View className="text-center mb-4"> |
|||
<Text className="text-lg font-bold">二维码</Text> |
|||
{title && ( |
|||
<Text className="text-sm text-gray-600 mt-1">{title}</Text> |
|||
)} |
|||
</View> |
|||
|
|||
{/* 副标题信息 */} |
|||
{subtitle && ( |
|||
<View className="bg-gray-50 rounded-lg p-3 mb-4 text-center"> |
|||
<Text className="text-lg font-bold text-red-500">{subtitle}</Text> |
|||
</View> |
|||
)} |
|||
|
|||
{/* 二维码显示区域 */} |
|||
<View className="text-center mb-4"> |
|||
{loading ? ( |
|||
<View |
|||
className="bg-gray-100 rounded-lg flex items-center justify-center" |
|||
style={{ width: `${size}px`, height: `${size}px`, margin: '0 auto' }} |
|||
> |
|||
<Text className="text-gray-500">生成中...</Text> |
|||
</View> |
|||
) : qrDataURL ? ( |
|||
<View className="p-4 bg-white border border-gray-200 rounded-lg"> |
|||
<img |
|||
src={qrDataURL} |
|||
alt="二维码" |
|||
style={{ |
|||
width: `${size}px`, |
|||
height: `${size}px`, |
|||
display: 'block', |
|||
margin: '0 auto' |
|||
}} |
|||
/> |
|||
</View> |
|||
) : ( |
|||
<View className="p-4 bg-white border border-gray-200 rounded-lg"> |
|||
<Canvas |
|||
canvasId={canvasRef.current} |
|||
style={{ |
|||
width: `${size}px`, |
|||
height: `${size}px`, |
|||
border: '1px solid #e5e5e5' |
|||
}} |
|||
/> |
|||
</View> |
|||
)} |
|||
</View> |
|||
|
|||
{/* 文本内容显示 */} |
|||
<View className="bg-blue-50 rounded-lg p-3 mb-4"> |
|||
<View className="flex justify-between items-center"> |
|||
<View className="flex-1"> |
|||
<Text className="text-sm text-blue-600 mb-1">二维码内容</Text> |
|||
<Text className="text-sm text-blue-800" style={{ wordBreak: 'break-all' }}> |
|||
{text} |
|||
</Text> |
|||
</View> |
|||
<Button |
|||
size="small" |
|||
fill="outline" |
|||
icon={<Copy />} |
|||
onClick={copyText} |
|||
className="ml-2" |
|||
> |
|||
复制 |
|||
</Button> |
|||
</View> |
|||
</View> |
|||
|
|||
{/* 操作按钮 */} |
|||
<View className="flex"> |
|||
<Button |
|||
size="large" |
|||
fill="outline" |
|||
icon={<Download />} |
|||
onClick={saveQRCode} |
|||
className="flex-1 mr-3" |
|||
> |
|||
保存 |
|||
</Button> |
|||
<Button |
|||
size="large" |
|||
type="primary" |
|||
onClick={() => { |
|||
generateQRCode() |
|||
Taro.showToast({ |
|||
title: '二维码已刷新', |
|||
icon: 'success' |
|||
}) |
|||
}} |
|||
className="flex-1" |
|||
> |
|||
刷新 |
|||
</Button> |
|||
</View> |
|||
|
|||
{/* 使用说明 */} |
|||
<View className="mt-4 p-3 bg-yellow-50 rounded-lg"> |
|||
<Text className="text-sm text-yellow-800 font-medium mb-2">使用说明:</Text> |
|||
<View> |
|||
<Text className="text-xs text-yellow-700 mb-1">• 长按二维码可以识别或保存</Text> |
|||
<Text className="text-xs text-yellow-700 mb-1">• 点击保存按钮可保存到相册</Text> |
|||
<Text className="text-xs text-yellow-700">• 可以复制二维码内容进行分享</Text> |
|||
</View> |
|||
</View> |
|||
</View> |
|||
</Popup> |
|||
) |
|||
} |
|||
|
|||
export default QRCodeGenerator |
Loading…
Reference in new issue