diff --git a/dashboard/src/routes/resource/jargon.tsx b/dashboard/src/routes/resource/jargon.tsx deleted file mode 100644 index 6e4e1280..00000000 --- a/dashboard/src/routes/resource/jargon.tsx +++ /dev/null @@ -1,1064 +0,0 @@ -import { Hash, Search, Edit, Trash2, Eye, Plus, ChevronLeft, ChevronRight, ChevronsLeft, ChevronsRight, Check, X, HelpCircle, Globe, MessageCircle } from 'lucide-react' -import { useState, useEffect } from 'react' -import { cn } from '@/lib/utils' -import { MarkdownRenderer } from '@/components/markdown-renderer' -import { Button } from '@/components/ui/button' -import { Input } from '@/components/ui/input' -import { Label } from '@/components/ui/label' -import { ScrollArea } from '@/components/ui/scroll-area' -import { useToast } from '@/hooks/use-toast' -import { Textarea } from '@/components/ui/textarea' -import { Badge } from '@/components/ui/badge' -import { - Table, - TableBody, - TableCell, - TableHead, - TableHeader, - TableRow, -} from '@/components/ui/table' -import { - Dialog, - DialogContent, - DialogDescription, - DialogFooter, - DialogHeader, - DialogTitle, -} from '@/components/ui/dialog' -import { - AlertDialog, - AlertDialogAction, - AlertDialogCancel, - AlertDialogContent, - AlertDialogDescription, - AlertDialogFooter, - AlertDialogHeader, - AlertDialogTitle, -} from '@/components/ui/alert-dialog' -import { - Select, - SelectContent, - SelectItem, - SelectTrigger, - SelectValue, -} from '@/components/ui/select' -import { Checkbox } from '@/components/ui/checkbox' -import { Switch } from '@/components/ui/switch' -import type { Jargon, JargonCreateRequest, JargonUpdateRequest, JargonChatInfo, JargonStats } from '@/types/jargon' -import { getJargonList, getJargonDetail, createJargon, updateJargon, deleteJargon, batchDeleteJargons, getJargonStats, getJargonChatList, batchSetJargonStatus } from '@/lib/jargon-api' - -export function JargonManagementPage() { - const [jargons, setJargons] = useState([]) - const [loading, setLoading] = useState(true) - const [total, setTotal] = useState(0) - const [page, setPage] = useState(1) - const [pageSize, setPageSize] = useState(20) - const [search, setSearch] = useState('') - const [filterChatId, setFilterChatId] = useState('all') - const [filterIsJargon, setFilterIsJargon] = useState('all') - const [selectedJargon, setSelectedJargon] = useState(null) - const [isDetailDialogOpen, setIsDetailDialogOpen] = useState(false) - const [isEditDialogOpen, setIsEditDialogOpen] = useState(false) - const [isCreateDialogOpen, setIsCreateDialogOpen] = useState(false) - const [deleteConfirmJargon, setDeleteConfirmJargon] = useState(null) - const [selectedIds, setSelectedIds] = useState>(new Set()) - const [isBatchDeleteDialogOpen, setIsBatchDeleteDialogOpen] = useState(false) - const [jumpToPage, setJumpToPage] = useState('') - const [stats, setStats] = useState({ - total: 0, - confirmed_jargon: 0, - confirmed_not_jargon: 0, - pending: 0, - global_count: 0, - complete_count: 0, - chat_count: 0, - top_chats: {}, - }) - const [chatList, setChatList] = useState([]) - const { toast } = useToast() - - // 加载黑话列表 - const loadJargons = async () => { - try { - setLoading(true) - const response = await getJargonList({ - page, - page_size: pageSize, - search: search || undefined, - chat_id: filterChatId === 'all' ? undefined : filterChatId, - is_jargon: filterIsJargon === 'all' ? undefined : filterIsJargon === 'true' ? true : filterIsJargon === 'false' ? false : undefined, - }) - setJargons(response.data) - setTotal(response.total) - } catch (error) { - toast({ - title: '加载失败', - description: error instanceof Error ? error.message : '无法加载黑话列表', - variant: 'destructive', - }) - } finally { - setLoading(false) - } - } - - // 加载统计数据 - const loadStats = async () => { - try { - const response = await getJargonStats() - if (response?.data) { - setStats(response.data) - } - } catch (error) { - console.error('加载统计数据失败:', error) - } - } - - // 加载聊天列表 - const loadChatList = async () => { - try { - const response = await getJargonChatList() - if (response?.data) { - setChatList(response.data) - } - } catch (error) { - console.error('加载聊天列表失败:', error) - } - } - - // 初始加载 - useEffect(() => { - loadJargons() - loadStats() - loadChatList() - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [page, pageSize, search, filterChatId, filterIsJargon]) - - // 查看详情 - const handleViewDetail = async (jargon: Jargon) => { - try { - const response = await getJargonDetail(jargon.id) - setSelectedJargon(response.data) - setIsDetailDialogOpen(true) - } catch (error) { - toast({ - title: '加载详情失败', - description: error instanceof Error ? error.message : '无法加载黑话详情', - variant: 'destructive', - }) - } - } - - // 编辑黑话 - const handleEdit = (jargon: Jargon) => { - setSelectedJargon(jargon) - setIsEditDialogOpen(true) - } - - // 删除黑话 - const handleDelete = async (jargon: Jargon) => { - try { - await deleteJargon(jargon.id) - toast({ - title: '删除成功', - description: `已删除黑话: ${jargon.content}`, - }) - setDeleteConfirmJargon(null) - loadJargons() - loadStats() - } catch (error) { - toast({ - title: '删除失败', - description: error instanceof Error ? error.message : '无法删除黑话', - variant: 'destructive', - }) - } - } - - // 切换单个选择 - const toggleSelect = (id: number) => { - const newSelected = new Set(selectedIds) - if (newSelected.has(id)) { - newSelected.delete(id) - } else { - newSelected.add(id) - } - setSelectedIds(newSelected) - } - - // 全选/取消全选 - const toggleSelectAll = () => { - if (selectedIds.size === jargons.length && jargons.length > 0) { - setSelectedIds(new Set()) - } else { - setSelectedIds(new Set(jargons.map(j => j.id))) - } - } - - // 批量删除 - const handleBatchDelete = async () => { - try { - await batchDeleteJargons(Array.from(selectedIds)) - toast({ - title: '批量删除成功', - description: `已删除 ${selectedIds.size} 个黑话`, - }) - setSelectedIds(new Set()) - setIsBatchDeleteDialogOpen(false) - loadJargons() - loadStats() - } catch (error) { - toast({ - title: '批量删除失败', - description: error instanceof Error ? error.message : '无法批量删除黑话', - variant: 'destructive', - }) - } - } - - // 批量设置为黑话 - const handleBatchSetJargon = async (isJargon: boolean) => { - try { - await batchSetJargonStatus(Array.from(selectedIds), isJargon) - toast({ - title: '操作成功', - description: `已将 ${selectedIds.size} 个词条设为${isJargon ? '黑话' : '非黑话'}`, - }) - setSelectedIds(new Set()) - loadJargons() - loadStats() - } catch (error) { - toast({ - title: '操作失败', - description: error instanceof Error ? error.message : '批量设置失败', - variant: 'destructive', - }) - } - } - - // 页面跳转 - const handleJumpToPage = () => { - const targetPage = parseInt(jumpToPage) - const totalPages = Math.ceil(total / pageSize) - if (targetPage >= 1 && targetPage <= totalPages) { - setPage(targetPage) - setJumpToPage('') - } else { - toast({ - title: '无效的页码', - description: `请输入1-${totalPages}之间的页码`, - variant: 'destructive', - }) - } - } - - // 渲染黑话状态徽章 - const renderJargonStatus = (isJargon: boolean | null) => { - if (isJargon === true) { - return 是黑话 - } else if (isJargon === false) { - return 非黑话 - } else { - return 未判定 - } - } - - return ( -
- {/* 页面标题 */} -
-
-
-

- - 黑话管理 -

-

- 管理麦麦学习到的黑话和俚语 -

-
- -
-
- - -
- - {/* 统计卡片 */} -
-
-
总数量
-
{stats.total}
-
-
-
已确认黑话
-
{stats.confirmed_jargon}
-
-
-
确认非黑话
-
{stats.confirmed_not_jargon}
-
-
-
待判定
-
{stats.pending}
-
-
-
全局黑话
-
{stats.global_count}
-
-
-
推断完成
-
{stats.complete_count}
-
-
-
关联聊天数
-
{stats.chat_count}
-
-
- - {/* 搜索和筛选 */} -
-
-
- -
- - setSearch(e.target.value)} - className="pl-9" - /> -
-
-
- - -
-
- - -
-
- - -
-
- - {/* 批量操作工具栏 */} - {selectedIds.size > 0 && ( -
- 已选择 {selectedIds.size} 个 - - - - -
- )} -
- - {/* 黑话列表 */} -
- {/* 桌面端表格视图 */} -
- - - - - 0} - onCheckedChange={toggleSelectAll} - /> - - 内容 - 含义 - 聊天 - 状态 - 次数 - 操作 - - - - {loading ? ( - - - 加载中... - - - ) : jargons.length === 0 ? ( - - - 暂无数据 - - - ) : ( - jargons.map((jargon) => ( - - - toggleSelect(jargon.id)} - /> - - -
- {jargon.is_global && } - {jargon.content} -
-
- - {jargon.meaning || -} - - - {jargon.chat_name || jargon.chat_id} - - {renderJargonStatus(jargon.is_jargon)} - {jargon.count} - -
- - - -
-
-
- )) - )} -
-
-
- - {/* 移动端卡片视图 */} -
- {loading ? ( -
加载中...
- ) : jargons.length === 0 ? ( -
暂无数据
- ) : ( - jargons.map((jargon) => ( -
-
- toggleSelect(jargon.id)} - className="mt-1" - /> -
-
- {jargon.is_global && } -

{jargon.content}

-
- {jargon.meaning && ( -

{jargon.meaning}

- )} -
- {renderJargonStatus(jargon.is_jargon)} - 次数: {jargon.count} -
-
- 聊天: {jargon.chat_name || jargon.chat_id} -
-
-
-
- - - -
-
- )) - )} -
- - {/* 分页 */} - {total > 0 && ( -
-
- 共 {total} 条记录,第 {page} / {Math.ceil(total / pageSize)} 页 -
-
- - -
- setJumpToPage(e.target.value)} - onKeyDown={(e) => e.key === 'Enter' && handleJumpToPage()} - placeholder={page.toString()} - className="w-16 h-8 text-center" - min={1} - max={Math.ceil(total / pageSize)} - /> - -
- - -
-
- )} -
-
-
- - {/* 详情对话框 */} - - - {/* 创建对话框 */} - { - loadJargons() - loadStats() - setIsCreateDialogOpen(false) - }} - /> - - {/* 编辑对话框 */} - { - loadJargons() - loadStats() - setIsEditDialogOpen(false) - }} - /> - - {/* 删除确认对话框 */} - setDeleteConfirmJargon(null)}> - - - 确认删除 - - 确定要删除黑话 "{deleteConfirmJargon?.content}" 吗?此操作不可撤销。 - - - - 取消 - deleteConfirmJargon && handleDelete(deleteConfirmJargon)} - className="bg-destructive text-destructive-foreground hover:bg-destructive/90" - > - 删除 - - - - - - {/* 批量删除确认对话框 */} - - - - 确认批量删除 - - 您即将删除 {selectedIds.size} 个黑话,此操作无法撤销。确定要继续吗? - - - - 取消 - - 确认删除 - - - - -
- ) -} - -// 黑话详情对话框 -function JargonDetailDialog({ - jargon, - open, - onOpenChange, -}: { - jargon: Jargon | null - open: boolean - onOpenChange: (open: boolean) => void -}) { - if (!jargon) return null - - return ( - - - - 黑话详情 - 查看黑话的完整信息 - - - -
-
- - -
- -
- -
{jargon.content}
-
- - {jargon.raw_content && ( -
- -
- {(() => { - try { - const rawArray = JSON.parse(jargon.raw_content) - if (Array.isArray(rawArray)) { - return rawArray.map((item, index) => ( -
- {index > 0 &&
} -
{item}
-
- )) - } - return
{jargon.raw_content}
- } catch { - return
{jargon.raw_content}
- } - })()} -
-
- )} - -
- -
- {jargon.meaning ? ( - - ) : ( - '-' - )} -
-
- -
- -
- -
- {jargon.is_jargon === true && 是黑话} - {jargon.is_jargon === false && 非黑话} - {jargon.is_jargon === null && 未判定} - {jargon.is_global && 全局} - {jargon.is_complete && 推断完成} -
-
-
- - {jargon.inference_with_context && ( -
- -
{jargon.inference_with_context}
-
- )} - - {jargon.inference_content_only && ( -
- -
{jargon.inference_content_only}
-
- )} -
-
- - - - -
-
- ) -} - -// 信息项组件 -function InfoItem({ - icon: Icon, - label, - value, - mono = false, -}: { - icon?: typeof Hash - label: string - value: string | null | undefined - mono?: boolean -}) { - return ( -
- -
- {value || '-'} -
-
- ) -} - -// 黑话创建对话框 -function JargonCreateDialog({ - open, - onOpenChange, - chatList, - onSuccess, -}: { - open: boolean - onOpenChange: (open: boolean) => void - chatList: JargonChatInfo[] - onSuccess: () => void -}) { - const [formData, setFormData] = useState({ - content: '', - meaning: '', - chat_id: '', - is_global: false, - }) - const [saving, setSaving] = useState(false) - const { toast } = useToast() - - const handleCreate = async () => { - if (!formData.content || !formData.chat_id) { - toast({ - title: '验证失败', - description: '请填写必填字段:内容和聊天', - variant: 'destructive', - }) - return - } - - try { - setSaving(true) - await createJargon(formData) - toast({ - title: '创建成功', - description: '黑话已创建', - }) - setFormData({ content: '', meaning: '', chat_id: '', is_global: false }) - onSuccess() - } catch (error) { - toast({ - title: '创建失败', - description: error instanceof Error ? error.message : '无法创建黑话', - variant: 'destructive', - }) - } finally { - setSaving(false) - } - } - - return ( - - - - 新增黑话 - 创建新的黑话记录 - - -
-
- - setFormData({ ...formData, content: e.target.value })} - placeholder="输入黑话内容" - /> -
- -
- -