refactor(components): 重构 SimpleQRCodeModal 组件
- 移除了未使用的 QRCodeGenerator 组件 - 在 SimpleQRCodeModal组件中添加了礼品卡名称和面值的显示 - 更新了 SimpleQRCodeModal 的样式,使其更加居中 - 在 gift/detail.tsx 中传递了礼品卡名称和面值给 SimpleQRCodeModal
This commit is contained in:
@@ -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
|
|
||||||
@@ -10,13 +10,19 @@ export interface SimpleQRCodeModalProps {
|
|||||||
onClose: () => void
|
onClose: () => void
|
||||||
/** 二维码内容(礼品卡code码) */
|
/** 二维码内容(礼品卡code码) */
|
||||||
qrContent: string
|
qrContent: string
|
||||||
|
/** 礼品卡名称 */
|
||||||
|
giftName?: string
|
||||||
|
/** 礼品卡面值 */
|
||||||
|
faceValue?: string
|
||||||
}
|
}
|
||||||
|
|
||||||
const SimpleQRCodeModal: React.FC<SimpleQRCodeModalProps> = ({
|
const SimpleQRCodeModal: React.FC<SimpleQRCodeModalProps> = ({
|
||||||
visible,
|
visible,
|
||||||
onClose,
|
onClose,
|
||||||
qrContent
|
qrContent,
|
||||||
}) => {
|
giftName,
|
||||||
|
faceValue
|
||||||
|
}) => {
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
@@ -39,13 +45,28 @@ const SimpleQRCodeModal: React.FC<SimpleQRCodeModalProps> = ({
|
|||||||
</Text>
|
</Text>
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
|
{/* 礼品卡信息 */}
|
||||||
|
{(giftName || faceValue) && (
|
||||||
|
<View className="bg-gray-50 rounded-lg p-3 mb-4">
|
||||||
|
{giftName && (
|
||||||
|
<Text className="font-medium text-center mb-1">
|
||||||
|
{giftName}
|
||||||
|
</Text>
|
||||||
|
)}
|
||||||
|
{faceValue && (
|
||||||
|
<Text className="text-lg font-bold text-red-500 text-center">
|
||||||
|
¥{faceValue}
|
||||||
|
</Text>
|
||||||
|
)}
|
||||||
|
</View>
|
||||||
|
)}
|
||||||
|
|
||||||
{/* 二维码区域 */}
|
{/* 二维码区域 */}
|
||||||
<View className="text-center mb-4">
|
<View className="text-center mb-4">
|
||||||
<View className="p-4 bg-white border border-gray-200 rounded-lg">
|
<View className="p-4 bg-white border border-gray-200 rounded-lg">
|
||||||
<View className="bg-gray-100 rounded flex items-center justify-center"
|
<View className="bg-gray-100 rounded flex items-center justify-center mx-auto"
|
||||||
style={{width: '200px', height: '200px', margin: '0 auto'}}>
|
style={{width: '200px', height: '200px'}}>
|
||||||
<View className="text-center">
|
<View className="text-center">
|
||||||
<QrCode size="48" className="text-gray-400 mb-2"/>
|
|
||||||
<Text className="text-gray-500 text-sm">二维码</Text>
|
<Text className="text-gray-500 text-sm">二维码</Text>
|
||||||
<Text className="text-xs text-gray-400 mt-1">ID: {qrContent ? qrContent.slice(-6) : '------'}</Text>
|
<Text className="text-xs text-gray-400 mt-1">ID: {qrContent ? qrContent.slice(-6) : '------'}</Text>
|
||||||
</View>
|
</View>
|
||||||
|
|||||||
@@ -324,6 +324,8 @@ const GiftCardDetail = () => {
|
|||||||
visible={showQRCode}
|
visible={showQRCode}
|
||||||
onClose={() => setShowQRCode(false)}
|
onClose={() => setShowQRCode(false)}
|
||||||
qrContent={gift.code || ''}
|
qrContent={gift.code || ''}
|
||||||
|
giftName={gift.goodsName || gift.name}
|
||||||
|
faceValue={gift.faceValue}
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
</ConfigProvider>
|
</ConfigProvider>
|
||||||
|
|||||||
Reference in New Issue
Block a user