Your Name 37ac749071 fix: 修复前端代码质量问题
- 创建 Toast 通知组件,替换所有 alert() 调用
- 修复 useReview hook 内存泄漏(setInterval 清理)
- 移除所有 console.error 和 console.log 语句
- 为复制操作失败添加用户友好的 toast 提示

Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
2026-02-09 12:48:22 +08:00

813 lines
34 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.

'use client'
import { useState } from 'react'
import { useRouter } from 'next/navigation'
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/Card'
import { Button } from '@/components/ui/Button'
import { Modal } from '@/components/ui/Modal'
import { useToast } from '@/components/ui/Toast'
import {
Bell,
Shield,
Download,
Key,
Mail,
Smartphone,
Globe,
Moon,
Sun,
Check,
LogOut,
Monitor,
Trash2,
AlertCircle,
Clock,
FileText,
BarChart3,
Users,
CheckCircle,
Eye,
EyeOff,
Phone
} from 'lucide-react'
export default function BrandSettingsPage() {
const router = useRouter()
const toast = useToast()
const [notifications, setNotifications] = useState({
email: true,
push: true,
reviewComplete: true,
newSubmission: true,
riskAlert: true,
})
const [theme, setTheme] = useState<'light' | 'dark' | 'system'>('dark')
// 退出登录弹窗
const [showLogoutModal, setShowLogoutModal] = useState(false)
// 修改密码弹窗
const [showPasswordModal, setShowPasswordModal] = useState(false)
const [passwordForm, setPasswordForm] = useState({
current: '',
new: '',
confirm: ''
})
const [showPasswords, setShowPasswords] = useState({
current: false,
new: false,
confirm: false
})
// 双因素认证弹窗
const [show2FAModal, setShow2FAModal] = useState(false)
const [twoFAEnabled, setTwoFAEnabled] = useState(false)
// 修改邮箱弹窗
const [showEmailModal, setShowEmailModal] = useState(false)
const [newEmail, setNewEmail] = useState('')
const [emailCode, setEmailCode] = useState('')
// 修改手机弹窗
const [showPhoneModal, setShowPhoneModal] = useState(false)
const [newPhone, setNewPhone] = useState('')
const [phoneCode, setPhoneCode] = useState('')
// 数据导出弹窗
const [showExportModal, setShowExportModal] = useState(false)
const [exportType, setExportType] = useState<string>('')
const [exportRange, setExportRange] = useState('month')
const [isExporting, setIsExporting] = useState(false)
// 登录设备管理
const [showDevicesModal, setShowDevicesModal] = useState(false)
// 模拟登录设备数据
const loginDevices = [
{ id: 'd-1', name: 'Chrome - MacOS', location: '上海', lastActive: '当前设备', isCurrent: true },
{ id: 'd-2', name: 'Safari - iPhone', location: '上海', lastActive: '2小时前', isCurrent: false },
{ id: 'd-3', name: 'Chrome - Windows', location: '北京', lastActive: '3天前', isCurrent: false },
]
// 模拟导出历史
const exportHistory = [
{ id: 'e-1', type: '审核记录', range: '2026年1月', status: 'completed', createdAt: '2026-02-01 10:30', size: '2.3MB' },
{ id: 'e-2', type: '统计报告', range: '2025年Q4', status: 'completed', createdAt: '2026-01-15 14:20', size: '1.1MB' },
]
const handleSave = () => {
toast.success('设置已保存')
}
const handleLogout = () => {
// 模拟退出登录
router.push('/login')
}
const handleChangePassword = () => {
if (passwordForm.new !== passwordForm.confirm) {
toast.error('两次输入的密码不一致')
return
}
toast.success('密码修改成功')
setShowPasswordModal(false)
setPasswordForm({ current: '', new: '', confirm: '' })
}
const handleEnable2FA = () => {
setTwoFAEnabled(true)
setShow2FAModal(false)
toast.success('双因素认证已启用')
}
const handleChangeEmail = () => {
toast.success(`邮箱已更新为 ${newEmail}`)
setShowEmailModal(false)
setNewEmail('')
setEmailCode('')
}
const handleChangePhone = () => {
toast.success(`手机号已更新为 ${newPhone}`)
setShowPhoneModal(false)
setNewPhone('')
setPhoneCode('')
}
const handleExport = async () => {
setIsExporting(true)
// 模拟导出过程
await new Promise(resolve => setTimeout(resolve, 2000))
setIsExporting(false)
setShowExportModal(false)
toast.info('导出任务已创建,完成后将通知您下载')
}
const handleRemoveDevice = (deviceId: string) => {
toast.success('已移除该设备的登录状态')
}
return (
<div className="space-y-6 max-w-4xl">
{/* 页面标题 */}
<div className="flex items-center justify-between">
<div>
<h1 className="text-2xl font-bold text-text-primary"></h1>
<p className="text-sm text-text-secondary mt-1"></p>
</div>
<Button
variant="secondary"
className="bg-accent-coral/15 text-accent-coral border-accent-coral hover:bg-accent-coral hover:text-white"
onClick={() => setShowLogoutModal(true)}
>
<LogOut size={16} />
退
</Button>
</div>
{/* 通知设置 */}
<Card>
<CardHeader>
<CardTitle className="flex items-center gap-2">
<Bell size={18} className="text-accent-indigo" />
</CardTitle>
</CardHeader>
<CardContent className="space-y-4">
<div className="flex items-center justify-between py-3 border-b border-border-subtle">
<div>
<p className="font-medium text-text-primary"></p>
<p className="text-sm text-text-secondary"></p>
</div>
<label className="relative inline-flex items-center cursor-pointer">
<input
type="checkbox"
checked={notifications.email}
onChange={(e) => setNotifications({ ...notifications, email: e.target.checked })}
className="sr-only peer"
/>
<div className="w-11 h-6 bg-bg-elevated peer-focus:outline-none peer-focus:ring-2 peer-focus:ring-accent-indigo rounded-full peer peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:left-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all peer-checked:bg-accent-indigo"></div>
</label>
</div>
<div className="flex items-center justify-between py-3 border-b border-border-subtle">
<div>
<p className="font-medium text-text-primary"></p>
<p className="text-sm text-text-secondary"></p>
</div>
<label className="relative inline-flex items-center cursor-pointer">
<input
type="checkbox"
checked={notifications.push}
onChange={(e) => setNotifications({ ...notifications, push: e.target.checked })}
className="sr-only peer"
/>
<div className="w-11 h-6 bg-bg-elevated peer-focus:outline-none peer-focus:ring-2 peer-focus:ring-accent-indigo rounded-full peer peer-checked:after:translate-x-full peer-checked:after:border-white after:content-[''] after:absolute after:top-[2px] after:left-[2px] after:bg-white after:border-gray-300 after:border after:rounded-full after:h-5 after:w-5 after:transition-all peer-checked:bg-accent-indigo"></div>
</label>
</div>
<div className="pt-2">
<p className="text-sm font-medium text-text-primary mb-3"></p>
<div className="space-y-3">
<label className="flex items-center gap-3 cursor-pointer">
<input
type="checkbox"
checked={notifications.reviewComplete}
onChange={(e) => setNotifications({ ...notifications, reviewComplete: e.target.checked })}
className="w-4 h-4 accent-accent-indigo"
/>
<span className="text-text-secondary"></span>
</label>
<label className="flex items-center gap-3 cursor-pointer">
<input
type="checkbox"
checked={notifications.newSubmission}
onChange={(e) => setNotifications({ ...notifications, newSubmission: e.target.checked })}
className="w-4 h-4 accent-accent-indigo"
/>
<span className="text-text-secondary"></span>
</label>
<label className="flex items-center gap-3 cursor-pointer">
<input
type="checkbox"
checked={notifications.riskAlert}
onChange={(e) => setNotifications({ ...notifications, riskAlert: e.target.checked })}
className="w-4 h-4 accent-accent-indigo"
/>
<span className="text-text-secondary"></span>
</label>
</div>
</div>
</CardContent>
</Card>
{/* 外观设置 */}
<Card>
<CardHeader>
<CardTitle className="flex items-center gap-2">
<Globe size={18} className="text-purple-400" />
</CardTitle>
</CardHeader>
<CardContent>
<p className="text-sm text-text-secondary mb-4"></p>
<div className="flex gap-3">
<button
type="button"
onClick={() => setTheme('light')}
className={`flex items-center gap-2 px-4 py-3 rounded-lg border-2 transition-colors ${
theme === 'light' ? 'border-accent-indigo bg-accent-indigo/10' : 'border-border-subtle hover:border-accent-indigo/50'
}`}
>
<Sun size={20} className="text-yellow-400" />
<span className="text-text-primary"></span>
{theme === 'light' && <Check size={16} className="text-accent-indigo ml-2" />}
</button>
<button
type="button"
onClick={() => setTheme('dark')}
className={`flex items-center gap-2 px-4 py-3 rounded-lg border-2 transition-colors ${
theme === 'dark' ? 'border-accent-indigo bg-accent-indigo/10' : 'border-border-subtle hover:border-accent-indigo/50'
}`}
>
<Moon size={20} className="text-accent-indigo" />
<span className="text-text-primary"></span>
{theme === 'dark' && <Check size={16} className="text-accent-indigo ml-2" />}
</button>
<button
type="button"
onClick={() => setTheme('system')}
className={`flex items-center gap-2 px-4 py-3 rounded-lg border-2 transition-colors ${
theme === 'system' ? 'border-accent-indigo bg-accent-indigo/10' : 'border-border-subtle hover:border-accent-indigo/50'
}`}
>
<Globe size={20} className="text-text-secondary" />
<span className="text-text-primary"></span>
{theme === 'system' && <Check size={16} className="text-accent-indigo ml-2" />}
</button>
</div>
</CardContent>
</Card>
{/* 账户安全 */}
<Card>
<CardHeader>
<CardTitle className="flex items-center gap-2">
<Shield size={18} className="text-accent-green" />
</CardTitle>
</CardHeader>
<CardContent className="space-y-4">
<div className="flex items-center justify-between py-3 border-b border-border-subtle">
<div className="flex items-center gap-3">
<Key size={20} className="text-text-tertiary" />
<div>
<p className="font-medium text-text-primary"></p>
<p className="text-sm text-text-secondary"></p>
</div>
</div>
<Button variant="secondary" size="sm" onClick={() => setShowPasswordModal(true)}></Button>
</div>
<div className="flex items-center justify-between py-3 border-b border-border-subtle">
<div className="flex items-center gap-3">
<Smartphone size={20} className="text-text-tertiary" />
<div>
<p className="font-medium text-text-primary"></p>
<p className="text-sm text-text-secondary">
{twoFAEnabled ? '已启用,登录时需要手机验证码' : '启用后需要手机验证码登录'}
</p>
</div>
</div>
{twoFAEnabled ? (
<span className="px-3 py-1 rounded-full bg-accent-green/15 text-accent-green text-sm font-medium"></span>
) : (
<Button variant="secondary" size="sm" onClick={() => setShow2FAModal(true)}></Button>
)}
</div>
<div className="flex items-center justify-between py-3 border-b border-border-subtle">
<div className="flex items-center gap-3">
<Mail size={20} className="text-text-tertiary" />
<div>
<p className="font-medium text-text-primary"></p>
<p className="text-sm text-text-secondary">brand@example.com</p>
</div>
</div>
<Button variant="secondary" size="sm" onClick={() => setShowEmailModal(true)}></Button>
</div>
<div className="flex items-center justify-between py-3 border-b border-border-subtle">
<div className="flex items-center gap-3">
<Phone size={20} className="text-text-tertiary" />
<div>
<p className="font-medium text-text-primary"></p>
<p className="text-sm text-text-secondary">138****8888</p>
</div>
</div>
<Button variant="secondary" size="sm" onClick={() => setShowPhoneModal(true)}></Button>
</div>
<div className="flex items-center justify-between py-3">
<div className="flex items-center gap-3">
<Monitor size={20} className="text-text-tertiary" />
<div>
<p className="font-medium text-text-primary"></p>
<p className="text-sm text-text-secondary"></p>
</div>
</div>
<Button variant="secondary" size="sm" onClick={() => setShowDevicesModal(true)}></Button>
</div>
</CardContent>
</Card>
{/* 数据导出 */}
<Card>
<CardHeader>
<CardTitle className="flex items-center gap-2">
<Download size={18} className="text-orange-400" />
</CardTitle>
</CardHeader>
<CardContent className="space-y-6">
<div>
<p className="text-sm text-text-secondary mb-4"></p>
<div className="grid grid-cols-2 md:grid-cols-4 gap-3">
<button
type="button"
onClick={() => { setExportType('review'); setShowExportModal(true); }}
className="p-4 rounded-xl border border-border-subtle hover:border-accent-indigo/50 transition-colors text-left"
>
<FileText size={24} className="text-accent-indigo mb-2" />
<p className="font-medium text-text-primary"></p>
<p className="text-xs text-text-tertiary mt-1"></p>
</button>
<button
type="button"
onClick={() => { setExportType('stats'); setShowExportModal(true); }}
className="p-4 rounded-xl border border-border-subtle hover:border-accent-indigo/50 transition-colors text-left"
>
<BarChart3 size={24} className="text-accent-green mb-2" />
<p className="font-medium text-text-primary"></p>
<p className="text-xs text-text-tertiary mt-1"></p>
</button>
<button
type="button"
onClick={() => { setExportType('agency'); setShowExportModal(true); }}
className="p-4 rounded-xl border border-border-subtle hover:border-accent-indigo/50 transition-colors text-left"
>
<Users size={24} className="text-purple-400 mb-2" />
<p className="font-medium text-text-primary"></p>
<p className="text-xs text-text-tertiary mt-1"></p>
</button>
<button
type="button"
onClick={() => { setExportType('all'); setShowExportModal(true); }}
className="p-4 rounded-xl border border-border-subtle hover:border-accent-indigo/50 transition-colors text-left"
>
<Download size={24} className="text-orange-400 mb-2" />
<p className="font-medium text-text-primary"></p>
<p className="text-xs text-text-tertiary mt-1"></p>
</button>
</div>
</div>
{/* 导出历史 */}
<div>
<p className="text-sm font-medium text-text-primary mb-3"></p>
{exportHistory.length > 0 ? (
<div className="space-y-2">
{exportHistory.map(item => (
<div key={item.id} className="flex items-center justify-between p-3 rounded-xl bg-bg-elevated">
<div className="flex items-center gap-3">
<div className="w-10 h-10 rounded-lg bg-accent-green/15 flex items-center justify-center">
<CheckCircle size={20} className="text-accent-green" />
</div>
<div>
<p className="font-medium text-text-primary">{item.type} - {item.range}</p>
<p className="text-xs text-text-tertiary">{item.createdAt} · {item.size}</p>
</div>
</div>
<Button variant="secondary" size="sm">
<Download size={14} />
</Button>
</div>
))}
</div>
) : (
<div className="text-center py-8 text-text-tertiary">
<Download size={32} className="mx-auto mb-2 opacity-50" />
<p></p>
</div>
)}
</div>
</CardContent>
</Card>
{/* 保存按钮 */}
<div className="flex justify-end pt-4">
<Button onClick={handleSave}>
</Button>
</div>
{/* 退出登录确认弹窗 */}
<Modal
isOpen={showLogoutModal}
onClose={() => setShowLogoutModal(false)}
title="确认退出登录"
>
<div className="space-y-4">
<div className="p-4 rounded-xl bg-accent-coral/10 border border-accent-coral/20">
<div className="flex items-start gap-3">
<AlertCircle size={20} className="text-accent-coral flex-shrink-0 mt-0.5" />
<div>
<p className="text-text-primary font-medium">退</p>
<p className="text-sm text-text-secondary mt-1">
退访
</p>
</div>
</div>
</div>
<div className="flex gap-3 justify-end">
<Button variant="ghost" onClick={() => setShowLogoutModal(false)}>
</Button>
<Button
variant="secondary"
className="border-accent-coral text-accent-coral hover:bg-accent-coral/10"
onClick={handleLogout}
>
<LogOut size={16} />
退
</Button>
</div>
</div>
</Modal>
{/* 修改密码弹窗 */}
<Modal
isOpen={showPasswordModal}
onClose={() => { setShowPasswordModal(false); setPasswordForm({ current: '', new: '', confirm: '' }); }}
title="修改密码"
>
<div className="space-y-4">
<div>
<label className="block text-sm font-medium text-text-primary mb-2"></label>
<div className="relative">
<input
type={showPasswords.current ? 'text' : 'password'}
value={passwordForm.current}
onChange={(e) => setPasswordForm({ ...passwordForm, current: e.target.value })}
className="w-full px-4 py-2.5 pr-10 border border-border-subtle rounded-xl bg-bg-elevated text-text-primary focus:outline-none focus:ring-2 focus:ring-accent-indigo"
/>
<button
type="button"
onClick={() => setShowPasswords({ ...showPasswords, current: !showPasswords.current })}
className="absolute right-3 top-1/2 -translate-y-1/2 text-text-tertiary"
>
{showPasswords.current ? <EyeOff size={18} /> : <Eye size={18} />}
</button>
</div>
</div>
<div>
<label className="block text-sm font-medium text-text-primary mb-2"></label>
<div className="relative">
<input
type={showPasswords.new ? 'text' : 'password'}
value={passwordForm.new}
onChange={(e) => setPasswordForm({ ...passwordForm, new: e.target.value })}
className="w-full px-4 py-2.5 pr-10 border border-border-subtle rounded-xl bg-bg-elevated text-text-primary focus:outline-none focus:ring-2 focus:ring-accent-indigo"
/>
<button
type="button"
onClick={() => setShowPasswords({ ...showPasswords, new: !showPasswords.new })}
className="absolute right-3 top-1/2 -translate-y-1/2 text-text-tertiary"
>
{showPasswords.new ? <EyeOff size={18} /> : <Eye size={18} />}
</button>
</div>
<p className="text-xs text-text-tertiary mt-1">8</p>
</div>
<div>
<label className="block text-sm font-medium text-text-primary mb-2"></label>
<div className="relative">
<input
type={showPasswords.confirm ? 'text' : 'password'}
value={passwordForm.confirm}
onChange={(e) => setPasswordForm({ ...passwordForm, confirm: e.target.value })}
className="w-full px-4 py-2.5 pr-10 border border-border-subtle rounded-xl bg-bg-elevated text-text-primary focus:outline-none focus:ring-2 focus:ring-accent-indigo"
/>
<button
type="button"
onClick={() => setShowPasswords({ ...showPasswords, confirm: !showPasswords.confirm })}
className="absolute right-3 top-1/2 -translate-y-1/2 text-text-tertiary"
>
{showPasswords.confirm ? <EyeOff size={18} /> : <Eye size={18} />}
</button>
</div>
</div>
<div className="flex gap-3 justify-end pt-2">
<Button variant="ghost" onClick={() => { setShowPasswordModal(false); setPasswordForm({ current: '', new: '', confirm: '' }); }}>
</Button>
<Button onClick={handleChangePassword} disabled={!passwordForm.current || !passwordForm.new || !passwordForm.confirm}>
</Button>
</div>
</div>
</Modal>
{/* 双因素认证弹窗 */}
<Modal
isOpen={show2FAModal}
onClose={() => setShow2FAModal(false)}
title="启用双因素认证"
>
<div className="space-y-4">
<p className="text-text-secondary text-sm">
</p>
<div className="p-4 rounded-xl bg-bg-elevated">
<p className="text-sm text-text-secondary mb-2"></p>
<p className="font-medium text-text-primary">138****8888</p>
</div>
<div className="p-4 rounded-xl bg-accent-indigo/10 border border-accent-indigo/20">
<div className="flex items-start gap-3">
<Shield size={20} className="text-accent-indigo flex-shrink-0 mt-0.5" />
<div>
<p className="text-text-primary font-medium"></p>
<p className="text-sm text-text-secondary mt-1">
</p>
</div>
</div>
</div>
<div className="flex gap-3 justify-end pt-2">
<Button variant="ghost" onClick={() => setShow2FAModal(false)}>
</Button>
<Button onClick={handleEnable2FA}>
<Shield size={16} />
</Button>
</div>
</div>
</Modal>
{/* 修改邮箱弹窗 */}
<Modal
isOpen={showEmailModal}
onClose={() => { setShowEmailModal(false); setNewEmail(''); setEmailCode(''); }}
title="修改绑定邮箱"
>
<div className="space-y-4">
<div className="p-3 rounded-xl bg-bg-elevated">
<p className="text-sm text-text-secondary"></p>
<p className="font-medium text-text-primary">brand@example.com</p>
</div>
<div>
<label className="block text-sm font-medium text-text-primary mb-2"></label>
<input
type="email"
value={newEmail}
onChange={(e) => setNewEmail(e.target.value)}
placeholder="请输入新邮箱"
className="w-full px-4 py-2.5 border border-border-subtle rounded-xl bg-bg-elevated text-text-primary focus:outline-none focus:ring-2 focus:ring-accent-indigo"
/>
</div>
<div>
<label className="block text-sm font-medium text-text-primary mb-2"></label>
<div className="flex gap-2">
<input
type="text"
value={emailCode}
onChange={(e) => setEmailCode(e.target.value)}
placeholder="请输入验证码"
className="flex-1 px-4 py-2.5 border border-border-subtle rounded-xl bg-bg-elevated text-text-primary focus:outline-none focus:ring-2 focus:ring-accent-indigo"
/>
<Button variant="secondary"></Button>
</div>
</div>
<div className="flex gap-3 justify-end pt-2">
<Button variant="ghost" onClick={() => { setShowEmailModal(false); setNewEmail(''); setEmailCode(''); }}>
</Button>
<Button onClick={handleChangeEmail} disabled={!newEmail || !emailCode}>
</Button>
</div>
</div>
</Modal>
{/* 修改手机弹窗 */}
<Modal
isOpen={showPhoneModal}
onClose={() => { setShowPhoneModal(false); setNewPhone(''); setPhoneCode(''); }}
title="修改绑定手机"
>
<div className="space-y-4">
<div className="p-3 rounded-xl bg-bg-elevated">
<p className="text-sm text-text-secondary"></p>
<p className="font-medium text-text-primary">138****8888</p>
</div>
<div>
<label className="block text-sm font-medium text-text-primary mb-2"></label>
<input
type="tel"
value={newPhone}
onChange={(e) => setNewPhone(e.target.value)}
placeholder="请输入新手机号"
className="w-full px-4 py-2.5 border border-border-subtle rounded-xl bg-bg-elevated text-text-primary focus:outline-none focus:ring-2 focus:ring-accent-indigo"
/>
</div>
<div>
<label className="block text-sm font-medium text-text-primary mb-2"></label>
<div className="flex gap-2">
<input
type="text"
value={phoneCode}
onChange={(e) => setPhoneCode(e.target.value)}
placeholder="请输入验证码"
className="flex-1 px-4 py-2.5 border border-border-subtle rounded-xl bg-bg-elevated text-text-primary focus:outline-none focus:ring-2 focus:ring-accent-indigo"
/>
<Button variant="secondary"></Button>
</div>
</div>
<div className="flex gap-3 justify-end pt-2">
<Button variant="ghost" onClick={() => { setShowPhoneModal(false); setNewPhone(''); setPhoneCode(''); }}>
</Button>
<Button onClick={handleChangePhone} disabled={!newPhone || !phoneCode}>
</Button>
</div>
</div>
</Modal>
{/* 登录设备管理弹窗 */}
<Modal
isOpen={showDevicesModal}
onClose={() => setShowDevicesModal(false)}
title="登录设备管理"
size="lg"
>
<div className="space-y-4">
<p className="text-text-secondary text-sm">
</p>
<div className="space-y-3">
{loginDevices.map(device => (
<div key={device.id} className="flex items-center justify-between p-4 rounded-xl bg-bg-elevated">
<div className="flex items-center gap-3">
<div className={`w-10 h-10 rounded-lg flex items-center justify-center ${
device.isCurrent ? 'bg-accent-green/15' : 'bg-bg-page'
}`}>
<Monitor size={20} className={device.isCurrent ? 'text-accent-green' : 'text-text-tertiary'} />
</div>
<div>
<div className="flex items-center gap-2">
<p className="font-medium text-text-primary">{device.name}</p>
{device.isCurrent && (
<span className="px-2 py-0.5 rounded bg-accent-green/15 text-accent-green text-xs"></span>
)}
</div>
<p className="text-sm text-text-tertiary">{device.location} · {device.lastActive}</p>
</div>
</div>
{!device.isCurrent && (
<Button
variant="ghost"
size="sm"
className="text-accent-coral hover:bg-accent-coral/10"
onClick={() => handleRemoveDevice(device.id)}
>
<Trash2 size={14} />
</Button>
)}
</div>
))}
</div>
<div className="flex justify-end pt-2">
<Button variant="ghost" onClick={() => setShowDevicesModal(false)}>
</Button>
</div>
</div>
</Modal>
{/* 数据导出弹窗 */}
<Modal
isOpen={showExportModal}
onClose={() => { setShowExportModal(false); setExportType(''); }}
title="导出数据"
>
<div className="space-y-4">
<div className="p-3 rounded-xl bg-bg-elevated">
<p className="text-sm text-text-secondary"></p>
<p className="font-medium text-text-primary">
{exportType === 'review' && '审核记录'}
{exportType === 'stats' && '统计报告'}
{exportType === 'agency' && '代理商数据'}
{exportType === 'all' && '全部数据'}
</p>
</div>
<div>
<label className="block text-sm font-medium text-text-primary mb-2"></label>
<div className="grid grid-cols-2 gap-2">
{[
{ value: 'week', label: '最近一周' },
{ value: 'month', label: '最近一月' },
{ value: 'quarter', label: '最近三月' },
{ value: 'year', label: '最近一年' },
].map(option => (
<button
key={option.value}
type="button"
onClick={() => setExportRange(option.value)}
className={`p-3 rounded-xl border-2 text-sm font-medium transition-colors ${
exportRange === option.value
? 'border-accent-indigo bg-accent-indigo/10 text-accent-indigo'
: 'border-border-subtle text-text-secondary hover:border-accent-indigo/50'
}`}
>
{option.label}
</button>
))}
</div>
</div>
<div>
<label className="block text-sm font-medium text-text-primary mb-2"></label>
<div className="flex gap-2">
<span className="px-3 py-2 rounded-lg bg-accent-indigo/15 text-accent-indigo text-sm font-medium">Excel (.xlsx)</span>
<span className="px-3 py-2 rounded-lg bg-bg-elevated text-text-tertiary text-sm">CSV</span>
<span className="px-3 py-2 rounded-lg bg-bg-elevated text-text-tertiary text-sm">PDF</span>
</div>
</div>
<div className="flex gap-3 justify-end pt-2">
<Button variant="ghost" onClick={() => { setShowExportModal(false); setExportType(''); }}>
</Button>
<Button onClick={handleExport} disabled={isExporting}>
{isExporting ? (
<>
<Clock size={16} className="animate-spin" />
...
</>
) : (
<>
<Download size={16} />
</>
)}
</Button>
</div>
</div>
</Modal>
</div>
)
}