Files
soul-yongping/soul-admin/src/pages/qrcodes/QRCodesPage.tsx

250 lines
10 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import toast from '@/utils/toast'
import { useState, useEffect } from 'react'
import {
Card,
CardContent,
CardDescription,
CardHeader,
CardTitle,
} from '@/components/ui/card'
import { Label } from '@/components/ui/label'
import { Input } from '@/components/ui/input'
import { Textarea } from '@/components/ui/textarea'
import { Button } from '@/components/ui/button'
import { QrCode, Upload, Link, ExternalLink, Copy, Check, HelpCircle } from 'lucide-react'
import { get, post } from '@/api/client'
export function QRCodesPage() {
const [liveQRUrls, setLiveQRUrls] = useState('')
const [wechatGroupUrl, setWechatGroupUrl] = useState('')
const [copied, setCopied] = useState('')
const [config, setConfig] = useState<{
paymentMethods?: { wechat?: { groupQrCode?: string } }
liveQRCodes?: { id: string; name: string; urls: string[]; clickCount: number }[]
}>({})
const loadConfig = async () => {
try {
const data = await get<{
paymentMethods?: { wechat?: { groupQrCode?: string } }
liveQRCodes?: { id: string; name: string; urls: string[]; clickCount: number }[]
}>('/api/config')
const urls = data?.liveQRCodes?.[0]?.urls
if (Array.isArray(urls)) setLiveQRUrls(urls.join('\n'))
const group = data?.paymentMethods?.wechat?.groupQrCode
if (group) setWechatGroupUrl(group)
setConfig({
paymentMethods: data?.paymentMethods,
liveQRCodes: data?.liveQRCodes,
})
} catch (e) {
console.error(e)
}
}
useEffect(() => {
loadConfig()
}, [])
const handleCopy = (text: string, field: string) => {
navigator.clipboard.writeText(text)
setCopied(field)
setTimeout(() => setCopied(''), 2000)
}
const handleSaveLiveQR = async () => {
try {
const urls = liveQRUrls
.split('\n')
.map((u) => u.trim())
.filter(Boolean)
const updatedLiveQRCodes = [...(config.liveQRCodes || [])]
if (updatedLiveQRCodes[0]) {
updatedLiveQRCodes[0].urls = urls
} else {
updatedLiveQRCodes.push({ id: 'live-1', name: '微信群活码', urls, clickCount: 0 })
}
await post('/api/db/config', {
key: 'live_qr_codes',
value: updatedLiveQRCodes,
description: '群活码配置',
})
toast.success('群活码配置已保存!')
await loadConfig()
} catch (e) {
console.error(e)
toast.error('保存失败: ' + (e instanceof Error ? e.message : String(e)))
}
}
const handleSaveWechatGroup = async () => {
try {
await post('/api/db/config', {
key: 'payment_methods',
value: {
...(config.paymentMethods || {}),
wechat: {
...(config.paymentMethods?.wechat || {}),
groupQrCode: wechatGroupUrl,
},
},
description: '支付方式配置',
})
toast.success('微信群链接已保存!用户支付成功后将自动跳转')
await loadConfig()
} catch (e) {
console.error(e)
toast.error('保存失败: ' + (e instanceof Error ? e.message : String(e)))
}
}
const handleTestJump = () => {
if (wechatGroupUrl) window.open(wechatGroupUrl, '_blank')
else toast.error('请先配置微信群链接')
}
return (
<div className="p-8 w-full">
<div className="mb-8">
<h2 className="text-2xl font-bold text-white"></h2>
<p className="text-gray-400 mt-1"></p>
</div>
<div className="mb-6 bg-[#07C160]/10 border border-[#07C160]/30 rounded-xl p-4">
<div className="flex items-start gap-3">
<HelpCircle className="w-5 h-5 text-[#07C160] flex-shrink-0 mt-0.5" />
<div className="text-sm">
<p className="font-medium mb-2 text-[#07C160]"></p>
<div className="text-[#07C160]/80 space-y-2">
<p className="font-medium">使</p>
<ol className="list-decimal list-inside space-y-1 pl-2">
<li>访</li>
<li></li>
<li></li>
<li></li>
</ol>
<p className="font-medium mt-3">使</p>
<ol className="list-decimal list-inside space-y-1 pl-2">
<li> &quot;...&quot; </li>
<li> </li>
</ol>
<p className="text-[#07C160]/60 mt-2">7使</p>
</div>
</div>
</div>
</div>
<div className="grid gap-6 md:grid-cols-2">
<Card className="bg-[#0f2137] border-gray-700/50 shadow-xl md:col-span-2">
<CardHeader>
<CardTitle className="text-[#07C160] flex items-center gap-2">
<QrCode className="w-5 h-5" />
</CardTitle>
<CardDescription className="text-gray-400">
</CardDescription>
</CardHeader>
<CardContent className="space-y-4">
<div className="space-y-2">
<Label className="text-gray-300 flex items-center gap-2">
<Link className="w-4 h-4" />
/
</Label>
<div className="flex gap-2">
<Input
placeholder="https://cli.im/xxxxx 或 https://weixin.qq.com/g/..."
className="bg-[#0a1628] border-gray-700 text-white placeholder:text-gray-500 flex-1"
value={wechatGroupUrl}
onChange={(e) => setWechatGroupUrl(e.target.value)}
/>
<Button
variant="outline"
size="icon"
className="border-gray-700 bg-transparent hover:bg-gray-700/50"
onClick={() => handleCopy(wechatGroupUrl, 'group')}
>
{copied === 'group' ? <Check className="w-4 h-4 text-green-500" /> : <Copy className="w-4 h-4 text-gray-400" />}
</Button>
</div>
<p className="text-xs text-gray-500 flex items-center gap-1">
<ExternalLink className="w-3 h-3" />
(https://weixin.qq.com/g/...)、企业微信链接等
</p>
</div>
<div className="flex gap-3">
<Button onClick={handleSaveWechatGroup} className="flex-1 bg-[#07C160] hover:bg-[#06AD51] text-white">
<Upload className="w-4 h-4 mr-2" />
</Button>
<Button
onClick={handleTestJump}
variant="outline"
className="border-[#07C160] text-[#07C160] hover:bg-[#07C160]/10 bg-transparent"
>
<ExternalLink className="w-4 h-4 mr-2" />
</Button>
</div>
</CardContent>
</Card>
<Card className="bg-[#0f2137] border-gray-700/50 shadow-xl md:col-span-2">
<CardHeader>
<CardTitle className="text-white flex items-center gap-2">
<QrCode className="w-5 h-5 text-[#38bdac]" />
</CardTitle>
<CardDescription className="text-gray-400">
</CardDescription>
</CardHeader>
<CardContent className="space-y-4">
<div className="space-y-2">
<Label className="text-gray-300 flex items-center gap-2">
<Link className="w-4 h-4" />
</Label>
<Textarea
placeholder="https://cli.im/group1\nhttps://cli.im/group2"
className="bg-[#0a1628] border-gray-700 text-white placeholder:text-gray-500 min-h-[120px] font-mono text-sm"
value={liveQRUrls}
onChange={(e) => setLiveQRUrls(e.target.value)}
/>
<p className="text-xs text-gray-500"></p>
</div>
<div className="flex items-center justify-between p-3 bg-[#0a1628] rounded-lg border border-gray-700/50">
<span className="text-sm text-gray-400"></span>
<span className="font-bold text-[#38bdac]">
{liveQRUrls.split('\n').filter(Boolean).length}
</span>
</div>
<Button onClick={handleSaveLiveQR} className="w-full bg-[#38bdac] hover:bg-[#2da396] text-white">
<Upload className="w-4 h-4 mr-2" />
</Button>
</CardContent>
</Card>
</div>
<div className="mt-6 bg-[#0f2137] rounded-xl p-4 border border-gray-700/50">
<h4 className="text-white font-medium mb-3"></h4>
<div className="space-y-3 text-sm">
<div>
<p className="text-[#38bdac]">Q: 为什么推荐使用草料活码</p>
<p className="text-gray-400">
A: 草料活码是永久链接7
</p>
</div>
<div>
<p className="text-[#38bdac]">Q: 支付后没有跳转怎么办</p>
<p className="text-gray-400">
A: 1) 2) 3) 使https开头的链接
</p>
</div>
</div>
</div>
</div>
)
}