import React, { useCallback, useEffect, useState } from 'react'; import Paper from '@mui/material/Paper'; import Typography from '@mui/material/Typography'; import Button from '@mui/material/Button'; import Box from '@mui/material/Box'; import Table from '@mui/material/Table'; import TableBody from '@mui/material/TableBody'; import TableCell from '@mui/material/TableCell'; import TableContainer from '@mui/material/TableContainer'; import TableHead from '@mui/material/TableHead'; import TableRow from '@mui/material/TableRow'; import Chip from '@mui/material/Chip'; import IconButton from '@mui/material/IconButton'; import AddIcon from '@mui/icons-material/Add'; import EditIcon from '@mui/icons-material/Edit'; import DeleteIcon from '@mui/icons-material/Delete'; import Dialog from '@mui/material/Dialog'; import DialogTitle from '@mui/material/DialogTitle'; import DialogContent from '@mui/material/DialogContent'; import DialogActions from '@mui/material/DialogActions'; import TextField from '@mui/material/TextField'; import FormControlLabel from '@mui/material/FormControlLabel'; import Switch from '@mui/material/Switch'; import { listAdminLinks, createAdminLink, updateAdminLink, deleteAdminLink } from '../../api/adminLinks.js'; import { showSnack } from '../snack.jsx'; import ConfirmDialog from '../ConfirmDialog.jsx'; const EMPTY = { title: '', url: '', embed_url: '', use_proxy: false, description: '', icon: '', category: '默认', version: '', sort_order: '0', }; /** 面板链接管理:CRUD + 代理嵌入开关(迁移自 v1 面板链接卡片) */ export default function Links() { const [links, setLinks] = useState(null); const [dialog, setDialog] = useState(false); const [editingId, setEditingId] = useState(null); const [form, setForm] = useState(EMPTY); const [confirm, setConfirm] = useState(null); const load = useCallback(() => { listAdminLinks() .then((ls) => setLinks(ls || [])) .catch((e) => showSnack(e.message, 'error')); }, []); useEffect(() => { load(); }, [load]); const openAdd = () => { setEditingId(null); setForm(EMPTY); setDialog(true); }; const openEdit = (l) => { setEditingId(l.id); setForm({ title: l.title, url: l.url, embed_url: l.embed_url || '', use_proxy: !!l.use_proxy, description: l.description || '', icon: l.icon || '', category: l.category || '默认', version: l.version || '', sort_order: String(l.sort_order || 0), }); setDialog(true); }; const save = async () => { if (!form.title.trim() || !form.url.trim()) { showSnack('标题和链接不能为空', 'error'); return; } const data = { title: form.title.trim(), url: form.url.trim(), embed_url: form.embed_url.trim(), use_proxy: form.use_proxy ? 1 : 0, description: form.description.trim(), icon: form.icon.trim(), category: form.category.trim() || '默认', version: form.version.trim(), sort_order: parseInt(form.sort_order, 10) || 0, }; try { if (editingId) await updateAdminLink(editingId, data); else await createAdminLink(data); showSnack('保存成功'); setDialog(false); load(); } catch (e) { showSnack(e.message, 'error'); } }; const doDelete = async () => { if (!confirm) return; try { await deleteAdminLink(confirm.id); showSnack('已删除'); setConfirm(null); load(); } catch (e) { showSnack(e.message, 'error'); } }; return ( 面板链接管理 排序 标题 版本 URL 嵌入URL 分类 操作 {links === null ? ( 加载中... ) : links.length === 0 ? ( 暂无面板 ) : links.map((l) => ( {l.sort_order} {l.title} {l.version || '-'} {l.url} {l.embed_url || '-'} openEdit(l)}> setConfirm({ id: l.id, label: l.title })}> ))}
setDialog(false)} fullWidth maxWidth="sm"> {editingId ? '编辑面板' : '添加面板'} setForm((p) => ({ ...p, title: e.target.value }))} margin="normal" /> setForm((p) => ({ ...p, url: e.target.value }))} margin="normal" placeholder="https://..." /> setForm((p) => ({ ...p, embed_url: e.target.value }))} margin="normal" placeholder="留空则新标签页打开" /> setForm((p) => ({ ...p, use_proxy: e.target.checked }))} />} label="通过代理嵌入(绕过 X-Frame-Options 限制)" /> setForm((p) => ({ ...p, description: e.target.value }))} margin="normal" /> setForm((p) => ({ ...p, icon: e.target.value }))} margin="normal" placeholder="settings, dashboard, ..." /> setForm((p) => ({ ...p, category: e.target.value }))} margin="normal" placeholder="默认" /> setForm((p) => ({ ...p, version: e.target.value }))} margin="normal" placeholder="v2.1.0" /> setForm((p) => ({ ...p, sort_order: e.target.value }))} margin="normal" sx={{ maxWidth: 160 }} /> setConfirm(null)} onConfirm={doDelete} confirmText="确认删除" />
); }