import { useState, useEffect, useCallback } from 'react'; import { adminAPI } from '@/api'; import './Apps.css'; import { formatBjTime, formatBjDate } from '@/utils/datetime'; interface AIApp { id: string; name: string; description: string | null; type: string; category: string; tags: string | null; icon: string | null; status: string; version: number; is_template: boolean; is_public: boolean; config: any; created_at: string | null; updated_at: string | null; published_at: string | null; analytics_7d?: any; versions?: any[]; } interface AppStats { total: number; published: number; draft: number; paused: number; archived: number; templates: number; by_type: Record; by_category: Record; recent_7d: number; total_versions: number; } interface Template { id: string; name: string; description: string | null; type: string; category: string; icon: string | null; config: any; created_at: string | null; } const CATEGORY_NAMES: Record = { customer_service: '客服助手', data_analysis: '数据分析', coding: '编程辅助', writing: '内容创作', education: '教育培训', marketing: '营销推广', hr: '人力资源', finance: '财务法务', custom: '自定义', }; const TYPE_NAMES: Record = { chatbot: '聊天机器人', assistant: 'AI助手', workflow: '工作流', api_service: 'API服务', }; const STATUS_COLORS: Record = { draft: '#f59e0b', published: '#10b981', paused: '#6b7280', archived: '#9ca3af', }; const STATUS_NAMES: Record = { draft: '草稿', published: '已发布', paused: '已暂停', archived: '已归档', }; export default function AdminApps() { const [activeTab, setActiveTab] = useState<'apps' | 'templates' | 'analytics'>('apps'); const [stats, setStats] = useState(null); const [apps, setApps] = useState([]); const [templates, setTemplates] = useState([]); const [loading, setLoading] = useState(false); const [search, setSearch] = useState(''); const [filterType, setFilterType] = useState(''); const [filterStatus, setFilterStatus] = useState(''); const [filterCategory, setFilterCategory] = useState(''); const [page, setPage] = useState(1); const [total, setTotal] = useState(0); const [totalPages, setTotalPages] = useState(1); const [showCreateModal, setShowCreateModal] = useState(false); const [editApp, setEditApp] = useState(null); const [detailApp, setDetailApp] = useState(null); const [detailAnalytics, setDetailAnalytics] = useState(null); const [showTemplateModal, setShowTemplateModal] = useState(false); const fetchStats = useCallback(async () => { try { const res = await adminAPI.getAIAppStats(); if (res.code === 0) setStats(res.data); } catch (e) { console.error(e); } }, []); const fetchApps = useCallback(async () => { setLoading(true); try { const params = new URLSearchParams(); params.set('page', String(page)); params.set('page_size', '20'); if (search) params.set('search', search); if (filterType) params.set('app_type', filterType); if (filterStatus) params.set('status', filterStatus); if (filterCategory) params.set('category', filterCategory); const res = await adminAPI.getAIApps(params.toString()); if (res.code === 0) { setApps(res.data.items || []); setTotal(res.data.total || 0); setTotalPages(res.data.total_pages || 1); } } catch (e) { console.error(e); } finally { setLoading(false); } }, [page, search, filterType, filterStatus, filterCategory]); const fetchTemplates = useCallback(async () => { try { const res = await adminAPI.getAIAppTemplates(); if (res.code === 0) setTemplates(res.data.items || []); } catch (e) { console.error(e); } }, []); const fetchDetail = useCallback(async (id: string) => { try { const res = await adminAPI.getAIApp(id); if (res.code === 0) { setDetailApp(res.data); // Fetch analytics try { const aRes = await adminAPI.getAIAppAnalytics(id); if (aRes.code === 0) setDetailAnalytics(aRes.data); } catch (e) { /* analytics may be empty */ } } } catch (e) { console.error(e); } }, []); useEffect(() => { fetchStats(); }, [fetchStats]); useEffect(() => { if (activeTab === 'apps') fetchApps(); if (activeTab === 'templates') fetchTemplates(); }, [activeTab, fetchApps, fetchTemplates]); const handleCreate = async (data: any) => { try { const res = await adminAPI.createAIApp(data); if (res.code === 0) { setShowCreateModal(false); fetchApps(); fetchStats(); } } catch (e) { console.error(e); } }; const handleUpdate = async (data: any) => { if (!editApp) return; try { const res = await adminAPI.updateAIApp(editApp.id, data); if (res.code === 0) { setEditApp(null); fetchApps(); fetchStats(); } } catch (e) { console.error(e); } }; const handleDelete = async (id: string) => { if (!confirm('确定要删除此应用吗?此操作不可逆。')) return; try { const res = await adminAPI.deleteAIApp(id); if (res.code === 0) { fetchApps(); fetchStats(); } } catch (e) { console.error(e); } }; const handleLifecycle = async (id: string, action: 'publish' | 'pause' | 'archive' | 'duplicate') => { try { const res = await adminAPI.aiAppLifecycle(id, action); if (res.code === 0) { fetchApps(); fetchStats(); if (detailApp?.id === id) fetchDetail(id); } } catch (e) { console.error(e); } }; const handleCreateTemplate = async (data: any) => { try { const res = await adminAPI.createAIAppTemplate(data); if (res.code === 0) { setShowTemplateModal(false); fetchTemplates(); fetchStats(); } } catch (e) { console.error(e); } }; const handleFromTemplate = async (templateId: string, name: string) => { try { const res = await adminAPI.createAIAppFromTemplate(templateId, { name }); if (res.code === 0) { setActiveTab('apps'); fetchApps(); fetchStats(); } } catch (e) { console.error(e); } }; const formatDate = (dt: string | null) => { return formatBjTime(dt); }; return (

AI应用管理

{activeTab === 'apps' && ( )} {activeTab === 'templates' && ( )}
{/* Stats Cards */} {stats && (
{stats.total}
应用总数
{stats.published}
已发布
{stats.draft}
草稿
{stats.templates}
应用模板
{stats.recent_7d}
近7天新增
)} {/* Tabs */}
{([ { key: 'apps' as const, label: '应用列表', count: stats?.total }, { key: 'templates' as const, label: '应用模板', count: stats?.templates }, { key: 'analytics' as const, label: '分析概览', count: undefined as number | undefined }, ]).map(tab => ( ))}
{/* Apps Tab */} {activeTab === 'apps' && (
{/* Filters */}
{ setSearch(e.target.value); setPage(1); }} className="filter-input" />
{/* Table */} {loading ? ( ) : apps.length === 0 ? ( ) : apps.map(app => ( fetchDetail(app.id)}> ))}
应用名称 类型 分类 状态 版本 创建时间 操作
加载中...
暂无应用
{app.name}
{app.description &&
{app.description}
}
{TYPE_NAMES[app.type] || app.type} {CATEGORY_NAMES[app.category] || app.category} {STATUS_NAMES[app.status] || app.status} v{app.version} {formatDate(app.created_at)} e.stopPropagation()}>
{app.status === 'draft' && } {app.status === 'published' && } {app.status === 'paused' && }
{/* Pagination */} {totalPages > 1 && (
第 {page} / {totalPages} 页 (共 {total} 条)
)}
)} {/* Templates Tab */} {activeTab === 'templates' && (
{templates.length === 0 ? (
暂无模板,点击右上角"创建模板"添加
) : templates.map(tpl => (
{tpl.icon || '📦'}
{tpl.name}
{tpl.description || '暂无描述'}
{TYPE_NAMES[tpl.type] || tpl.type} {CATEGORY_NAMES[tpl.category] || tpl.category}
))}
)} {/* Analytics Tab */} {activeTab === 'analytics' && stats && (

应用类型分布

{Object.entries(stats.by_type).map(([type, count]) => (
{count}
{TYPE_NAMES[type] || type}
))} {Object.keys(stats.by_type).length === 0 && (
暂无数据
)}

分类分布

{Object.entries(stats.by_category).map(([cat, count]) => (
{count}
{CATEGORY_NAMES[cat] || cat}
))} {Object.keys(stats.by_category).length === 0 && (
暂无数据
)}

版本统计

{stats.total_versions}
总版本数
)} {/* Create/Edit Modal */} {(showCreateModal || editApp) && ( { setShowCreateModal(false); setEditApp(null); }} onSubmit={editApp ? handleUpdate : handleCreate} /> )} {/* Template Modal */} {showTemplateModal && ( setShowTemplateModal(false)} onSubmit={handleCreateTemplate} /> )} {/* Detail Drawer */} {detailApp && ( { setDetailApp(null); setDetailAnalytics(null); }} onAction={handleLifecycle} /> )}
); } // ============================================================ // App Form Modal // ============================================================ function AppFormModal({ app, onClose, onSubmit }: { app: AIApp | null; onClose: () => void; onSubmit: (data: any) => void; }) { const [name, setName] = useState(app?.name || ''); const [description, setDescription] = useState(app?.description || ''); const [type, setType] = useState(app?.type || 'chatbot'); const [category, setCategory] = useState(app?.category || 'custom'); const [tags, setTags] = useState(app?.tags || ''); const [systemPrompt, setSystemPrompt] = useState(app?.config?.system_prompt || ''); const [temperature, setTemperature] = useState(app?.config?.temperature ?? 0.7); const [maxTokens, setMaxTokens] = useState(app?.config?.max_tokens ?? 2000); const [welcomeMessage, setWelcomeMessage] = useState(app?.config?.welcome_message || ''); const handleSubmit = () => { if (!name.trim()) { alert('请输入应用名称'); return; } onSubmit({ name, description, type, category, tags, config: { ...(app?.config || {}), system_prompt: systemPrompt, temperature: Number(temperature), max_tokens: Number(maxTokens), welcome_message: welcomeMessage, } }); }; return (
e.stopPropagation()}>

{app ? '编辑应用' : '创建应用'}

setName(e.target.value)} placeholder="输入应用名称" />