// @ts-nocheck /** * Phase 4 剩余 - 成就系统管理页面 * 成就管理、用户进度、排行榜、奖励领取 */ import React, { useState, useEffect, useCallback } from 'react'; import { adminAPI } from '@/api'; import { formatBjTime } from '@/utils/datetime'; // ==================== 类型定义 ==================== interface Achievement { id: string; name: string; description: string; icon: string; category: string; criteria_type: string; criteria_key: string | null; criteria_value: number; reward_type: string; reward_value: number; badge_icon: string | null; is_active: boolean; display_order: number; user_progress?: number; is_completed?: boolean; reward_claimed?: boolean; completed_at?: string | null; } interface AchievementStats { total_achievements: number; active_achievements: number; completed_achievements: number; rewards_claimed: number; total_points: number; by_category: Record; } const CATEGORY_LABELS: Record = { usage: '使用量', social: '社交', content: '内容创作', payment: '付费', special: '特殊', }; const CATEGORY_COLORS: Record = { usage: '#6C63FF', social: '#00D4AA', content: '#FFB800', payment: '#FF6B6B', special: '#C084FC', }; const REWARD_LABELS: Record = { points: '积分', badge: '徽章', discount: '折扣', feature: '功能解锁', credits: '额度', }; const CRITERIA_LABELS: Record = { message_count: '消息数量', conversation_count: '对话数量', token_usage: 'Token用量', referral_count: '推荐人数', kb_doc_count: '知识库文档数', agent_count: 'Agent数量', skill_count: '技能数量', payment_amount: '付费金额', days_active: '活跃天数', workflow_count: '工作流数量', }; // ==================== 主组件 ==================== const Achievement: React.FC = () => { const [activeTab, setActiveTab] = useState('overview'); const [loading, setLoading] = useState(false); const [error, setError] = useState(''); const [stats, setStats] = useState(null); const [achievements, setAchievements] = useState([]); const [userProgress, setUserProgress] = useState([]); const [leaderboard, setLeaderboard] = useState([]); const [categoryFilter, setCategoryFilter] = useState(''); // Modal states const [showCreateModal, setShowCreateModal] = useState(false); const [editingAch, setEditingAch] = useState(null); const [formData, setFormData] = useState({ name: '', description: '', category: 'usage', criteria_type: 'message_count', criteria_value: 100, reward_type: 'points', reward_value: 100, icon: '', }); const loadStats = useCallback(async () => { try { const allRes = await adminAPI.getAchievements(''); const items = allRes.data?.items || []; const active = items.filter((a: Achievement) => a.is_active); const completed = items.filter((a: Achievement) => a.is_completed); const claimed = items.filter((a: Achievement) => a.reward_claimed); const totalPoints = items .filter((a: Achievement) => a.is_completed) .reduce((sum: number, a: Achievement) => sum + (a.reward_value || 0), 0); const byCategory: Record = {}; items.forEach((a: Achievement) => { byCategory[a.category] = (byCategory[a.category] || 0) + 1; }); setStats({ total_achievements: items.length, active_achievements: active.length, completed_achievements: completed.length, rewards_claimed: claimed.length, total_points: totalPoints, by_category: byCategory, }); } catch (e: any) { console.error('Failed to load stats:', e); } }, []); const loadAchievements = useCallback(async () => { setLoading(true); setError(''); try { const params = categoryFilter ? `?category=${categoryFilter}` : ''; const res = await adminAPI.getAchievements(params); setAchievements(res.data?.items || []); } catch (e: any) { setError(e.message || '加载失败'); } finally { setLoading(false); } }, [categoryFilter]); const loadUserProgress = useCallback(async () => { setLoading(true); try { const res = await adminAPI.getUserAchievements(); setUserProgress(res.data?.items || []); } catch (e: any) { setError(e.message || '加载失败'); } finally { setLoading(false); } }, []); const loadLeaderboard = useCallback(async () => { setLoading(true); try { const res = await adminAPI.getAchievementLeaderboard('?period=all'); setLeaderboard(res.data?.items || []); } catch (e: any) { setError(e.message || '加载失败'); } finally { setLoading(false); } }, []); useEffect(() => { loadStats(); }, [loadStats]); useEffect(() => { if (activeTab === 'list') loadAchievements(); else if (activeTab === 'progress') loadUserProgress(); else if (activeTab === 'leaderboard') loadLeaderboard(); }, [activeTab, loadAchievements, loadUserProgress, loadLeaderboard]); const handleSave = async () => { if (!formData.name.trim()) { setError('成就名称不能为空'); return; } setLoading(true); try { const params = `?name=${encodeURIComponent(formData.name)}&category=${formData.category}&criteria_type=${formData.criteria_type}&criteria_value=${formData.criteria_value}&description=${encodeURIComponent(formData.description)}&reward_type=${formData.reward_type}&reward_value=${formData.reward_value}&icon=${encodeURIComponent(formData.icon)}`; if (editingAch) { const updateParams = `?name=${encodeURIComponent(formData.name)}&description=${encodeURIComponent(formData.description)}&reward_value=${formData.reward_value}`; await adminAPI.updateAchievement(editingAch.id, updateParams); } else { await adminAPI.createAchievement(params); } setShowCreateModal(false); setEditingAch(null); setFormData({ name: '', description: '', category: 'usage', criteria_type: 'message_count', criteria_value: 100, reward_type: 'points', reward_value: 100, icon: '', }); loadAchievements(); loadStats(); } catch (e: any) { setError(e.message || '保存失败'); } finally { setLoading(false); } }; const handleDelete = async (id: string) => { if (!confirm('确定要删除这个成就吗?')) return; try { await adminAPI.deleteAchievement(id); loadAchievements(); loadStats(); } catch (e: any) { setError(e.message || '删除失败'); } }; const handleToggle = async (ach: Achievement) => { try { const params = `?is_active=${!ach.is_active}`; await adminAPI.updateAchievement(ach.id, params); loadAchievements(); loadStats(); } catch (e: any) { setError(e.message || '操作失败'); } }; const handleClaim = async (id: string) => { try { await adminAPI.claimAchievementReward(id); loadUserProgress(); loadStats(); } catch (e: any) { setError(e.message || '领取失败'); } }; const handleCheckProgress = async () => { try { await adminAPI.checkAchievementProgress(); loadUserProgress(); loadStats(); } catch (e: any) { setError(e.message || '检查失败'); } }; const openEdit = (ach: Achievement) => { setEditingAch(ach); setFormData({ name: ach.name, description: ach.description || '', category: ach.category, criteria_type: ach.criteria_type, criteria_value: ach.criteria_value, reward_type: ach.reward_type, reward_value: ach.reward_value, icon: ach.icon || '', }); setShowCreateModal(true); }; const getProgressPercent = (current: number, target: number) => { if (target <= 0) return 0; return Math.min(100, Math.round((current / target) * 100)); }; // ==================== 渲染 ==================== return (

成就系统

{activeTab === 'list' && ( )} {activeTab === 'progress' && ( )}
{error &&
{error}
} {/* Tabs */}
{/* Overview Tab */} {activeTab === 'overview' && stats && (
总成就数
{stats.total_achievements}
已激活
{stats.active_achievements}
已完成
{stats.completed_achievements}
奖励已领取
{stats.rewards_claimed}
累计积分
{stats.total_points}

分类分布

{Object.entries(stats.by_category).map(([cat, count]) => { const pct = stats.total_achievements > 0 ? (count / stats.total_achievements) * 100 : 0; return (
{CATEGORY_LABELS[cat] || cat}
{count}
); })} {Object.keys(stats.by_category).length === 0 && (
暂无数据
)}
)} {/* List Tab */} {activeTab === 'list' && (
{loading ? (
加载中...
) : achievements.length === 0 ? (
暂无成就数据
) : (
{achievements.map((ach) => { const progressPct = getProgressPercent(ach.user_progress || 0, ach.criteria_value); const color = CATEGORY_COLORS[ach.category] || '#6C63FF'; return (
{ach.icon || CATEGORY_LABELS[ach.category]?.[0] || '★'}
{ach.name}
{ach.description || '暂无描述'}
{CATEGORY_LABELS[ach.category] || ach.category} {CRITERIA_LABELS[ach.criteria_type] || ach.criteria_type}: {ach.criteria_value} {REWARD_LABELS[ach.reward_type] || ach.reward_type}: {ach.reward_value} {!ach.is_active && ( 已停用 )}
{ach.user_progress !== undefined && ( <>
{ach.user_progress || 0} / {ach.criteria_value} {progressPct}%
)}
); })}
)}
)} {/* Progress Tab */} {activeTab === 'progress' && (
{loading ? (
加载中...
) : userProgress.length === 0 ? (
暂无进度数据
) : (
{userProgress.map((ach) => { const progressPct = getProgressPercent(ach.progress || 0, ach.criteria_value); const color = CATEGORY_COLORS[ach.category] || '#6C63FF'; return (
{ach.icon || CATEGORY_LABELS[ach.category]?.[0] || '★'}
{ach.name}
{ach.description || '暂无描述'}
{CATEGORY_LABELS[ach.category] || ach.category} {ach.is_completed && ( 已完成 )} {ach.reward_claimed && ( 已领取 )}
{ach.progress || 0} / {ach.criteria_value} {progressPct}%
{ach.completed_at && (
完成时间: {formatBjTime(ach.completed_at)}
)}
{ach.is_completed && !ach.reward_claimed && ( )} {ach.reward_claimed && ( 奖励已领取 )}
); })}
)}
)} {/* Leaderboard Tab */} {activeTab === 'leaderboard' && (
{loading ? (
加载中...
) : leaderboard.length === 0 ? (
排行榜暂无数据
用户完成成就后将出现在排行榜中
) : ( {leaderboard.map((entry, idx) => ( ))}
排名 用户 完成成就 累计积分 最近完成
{idx + 1} {entry.username || entry.user_id || '匿名用户'} {entry.achievement_count || 0} {entry.total_points || 0} {entry.last_completed ? new Date(entry.last_completed).toLocaleDateString('zh-CN') : '-'}
)}
)} {/* Create/Edit Modal */} {showCreateModal && (
setShowCreateModal(false)}>
e.stopPropagation()}>

{editingAch ? '编辑成就' : '创建成就'}

setFormData({ ...formData, name: e.target.value })} placeholder="例如:对话达人" />