- 新增 frontend/lib/platforms.ts 共享平台配置模块 - 支持6个平台: 抖音、小红书、B站、快手、微博、微信视频号 - 品牌方终端: 项目看板、项目详情、终审台列表添加平台显示 - 代理商终端: 工作台概览、审核台、Brief配置、达人管理、 数据报表、消息中心、申诉处理添加平台显示 - 达人端: 任务列表添加平台显示 - 统一使用彩色头部条样式展示平台信息 Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
811 lines
34 KiB
TypeScript
811 lines
34 KiB
TypeScript
'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 {
|
||
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 [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 = () => {
|
||
alert('设置已保存')
|
||
}
|
||
|
||
const handleLogout = () => {
|
||
// 模拟退出登录
|
||
router.push('/login')
|
||
}
|
||
|
||
const handleChangePassword = () => {
|
||
if (passwordForm.new !== passwordForm.confirm) {
|
||
alert('两次输入的密码不一致')
|
||
return
|
||
}
|
||
alert('密码修改成功')
|
||
setShowPasswordModal(false)
|
||
setPasswordForm({ current: '', new: '', confirm: '' })
|
||
}
|
||
|
||
const handleEnable2FA = () => {
|
||
setTwoFAEnabled(true)
|
||
setShow2FAModal(false)
|
||
alert('双因素认证已启用')
|
||
}
|
||
|
||
const handleChangeEmail = () => {
|
||
alert(`邮箱已更新为 ${newEmail}`)
|
||
setShowEmailModal(false)
|
||
setNewEmail('')
|
||
setEmailCode('')
|
||
}
|
||
|
||
const handleChangePhone = () => {
|
||
alert(`手机号已更新为 ${newPhone}`)
|
||
setShowPhoneModal(false)
|
||
setNewPhone('')
|
||
setPhoneCode('')
|
||
}
|
||
|
||
const handleExport = async () => {
|
||
setIsExporting(true)
|
||
// 模拟导出过程
|
||
await new Promise(resolve => setTimeout(resolve, 2000))
|
||
setIsExporting(false)
|
||
setShowExportModal(false)
|
||
alert('导出任务已创建,完成后将通知您下载')
|
||
}
|
||
|
||
const handleRemoveDevice = (deviceId: string) => {
|
||
alert('已移除该设备的登录状态')
|
||
}
|
||
|
||
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>
|
||
)
|
||
}
|