import { useState, useEffect } from 'react' import { listCategories, getMe, logout, adminCreateCategory, adminUpdateCategory, adminDeleteCategory, adminCreateVariant, adminUpdateVariant, adminDeleteVariant } from '../lib/api' import { RuntimeCategory, RuntimeVariant } from '../types' export default function Admin() { const [categories, setCategories] = useState([]) const [user, setUser] = useState<{ id: number; username: string; role: string } | null>(null) const [loading, setLoading] = useState(true) const [error, setError] = useState(null) // Category form const [editingCat, setEditingCat] = useState(null) const [catName, setCatName] = useState('') const [catLabel, setCatLabel] = useState('') const [catIcon, setCatIcon] = useState('') const [catOrder, setCatOrder] = useState(0) // Variant form const [selectedCatId, setSelectedCatId] = useState(0) const [editingVariant, setEditingVariant] = useState(null) const [vName, setVName] = useState('') const [vLabel, setVLabel] = useState('') const [vExt, setVExt] = useState('') const [vMime, setVMime] = useState('') const [vCmd, setVCmd] = useState('') const [vSrcCmd, setVSrcCmd] = useState('') const [vDefault, setVDefault] = useState(false) const [vOrder, setVOrder] = useState(0) useEffect(() => { Promise.all([ getMe().catch(() => null), listCategories().catch(() => [] as RuntimeCategory[]), ]).then(([u, cats]) => { setUser(u) setCategories(cats) setLoading(false) if (!u || u.role !== 'admin') setError('无权访问管理后台') }) }, []) const refresh = () => listCategories().then(setCategories) const resetCatForm = () => { setEditingCat(null) setCatName('') setCatLabel('') setCatIcon('') setCatOrder(0) } const editCat = (cat: RuntimeCategory) => { setEditingCat(cat) setCatName(cat.Name) setCatLabel(cat.Label) setCatIcon(cat.Icon) setCatOrder(cat.SortOrder) } const saveCategory = async () => { try { if (editingCat) { await adminUpdateCategory(editingCat.ID, { Name: catName, Label: catLabel, Icon: catIcon, SortOrder: catOrder }) } else { await adminCreateCategory({ Name: catName, Label: catLabel, Icon: catIcon, SortOrder: catOrder }) } resetCatForm() await refresh() } catch (e) { alert('操作失败') } } const deleteCategory = async (id: number) => { if (!confirm('确定删除此分类?相关变体也会被删除。')) return try { await adminDeleteCategory(id) await refresh() } catch { alert('删除失败') } } const resetVForm = () => { setEditingVariant(null) setVName('') setVLabel('') setVExt('') setVMime('') setVCmd('') setVSrcCmd('') setVDefault(false) setVOrder(0) } const editVariant = (v: RuntimeVariant) => { setEditingVariant(v) setSelectedCatId(v.CategoryID) setVName(v.Name) setVLabel(v.Label) setVExt(v.Extension) setVMime(v.MIMEType) setVCmd(v.CommandTemplate) setVSrcCmd(v.SourceTemplate) setVDefault(v.IsDefault) setVOrder(v.SortOrder) } const saveVariant = async () => { try { const payload = { Name: vName, Label: vLabel, Extension: vExt, MIMEType: vMime, CommandTemplate: vCmd, SourceTemplate: vSrcCmd, IsDefault: vDefault, SortOrder: vOrder, } if (editingVariant) { await adminUpdateVariant(editingVariant.ID, payload) } else { await adminCreateVariant(selectedCatId, payload) } resetVForm() await refresh() } catch { alert('操作失败') } } const deleteVariant = async (id: number) => { if (!confirm('确定删除此变体?')) return try { await adminDeleteVariant(id) await refresh() } catch { alert('删除失败') } } if (loading) return
加载中...
if (error || !user || user.role !== 'admin') { return (
🔒

无权访问

{error || '请使用管理员账号登录'}

登录
) } const selectedCat = categories.find(c => c.ID === selectedCatId) return (

管理后台

{user.username}
{/* Category Form */}

{editingCat ? '编辑分类' : '新增分类'}

setCatName(e.target.value)} placeholder="名称 (shell)" className="px-3 py-2 bg-gray-800 border border-gray-700 rounded text-xs" /> setCatLabel(e.target.value)} placeholder="标签 (Shell)" className="px-3 py-2 bg-gray-800 border border-gray-700 rounded text-xs" /> setCatIcon(e.target.value)} placeholder="图标 (🐚)" className="px-3 py-2 bg-gray-800 border border-gray-700 rounded text-xs" /> setCatOrder(Number(e.target.value))} placeholder="排序" type="number" className="px-3 py-2 bg-gray-800 border border-gray-700 rounded text-xs" />
{editingCat && }
{/* Categories List */} {categories.map(cat => (
{cat.Icon} {cat.Label} ({cat.Name}) 排序: {cat.SortOrder}
{/* Variants */}
{cat.Variants?.map(v => (
{v.Name} {v.Label} .{v.Extension} {v.IsDefault && 默认}
))}
))} {/* Variant Form */} {(selectedCatId > 0 || editingVariant) && (

{editingVariant ? '编辑变体' : `为 ${selectedCat?.Label || selectedCatId} 添加变体`}

setVName(e.target.value)} placeholder="名称 (bash)" className="px-3 py-2 bg-gray-800 border border-gray-700 rounded text-xs" /> setVLabel(e.target.value)} placeholder="标签 (Bash)" className="px-3 py-2 bg-gray-800 border border-gray-700 rounded text-xs" /> setVExt(e.target.value)} placeholder="扩展名 (.sh)" className="px-3 py-2 bg-gray-800 border border-gray-700 rounded text-xs" /> setVMime(e.target.value)} placeholder="MIME" className="px-3 py-2 bg-gray-800 border border-gray-700 rounded text-xs" />
setVCmd(e.target.value)} placeholder="命令模板 (curl {url} | bash)" className="px-3 py-2 bg-gray-800 border border-gray-700 rounded text-xs" /> setVSrcCmd(e.target.value)} placeholder="source模板 (可选)" className="px-3 py-2 bg-gray-800 border border-gray-700 rounded text-xs" />
setVOrder(Number(e.target.value))} placeholder="排序" className="w-20 px-3 py-2 bg-gray-800 border border-gray-700 rounded text-xs" />
)}
) }