import { useState, useMemo } from 'react'; import { Plus, Search, Eye, CheckCircle, XCircle, Play, RotateCcw, Copy, ChevronRight, Shield, FileCode, History, CircleDot, AlertTriangle, Database, Key, Lock, Undo2, Info, } from 'lucide-react'; import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card'; import { tickets, statusMap, statusColorMap, dbTypeColorMap } from '@/lib/mock'; import { useAuth, useActionPermission } from '@/hooks/useAuth'; import PageHeader from '@/components/PageHeader'; import EmptyState from '@/components/EmptyState'; import type { Ticket } from '@/types'; export default function TicketsPage() { const { isDeveloper } = useAuth(); const canCreate = useActionPermission('ticket.create'); const canApprove = useActionPermission('ticket.approve'); const canExecute = useActionPermission('ticket.execute'); const [search, setSearch] = useState(''); const [statusFilter, setStatusFilter] = useState(''); const [dbTypeFilter, setDbTypeFilter] = useState(''); const [priorityFilter, setPriorityFilter] = useState(''); const [selectedTicket, setSelectedTicket] = useState(null); const [showNewTicket, setShowNewTicket] = useState(false); const filtered = useMemo(() => { return tickets.filter(t => { if (statusFilter && t.status !== statusFilter) return false; if (dbTypeFilter && t.dbType !== dbTypeFilter) return false; if (priorityFilter && t.priority !== priorityFilter) return false; if (search && !t.title.includes(search) && !t.id.includes(search) && !t.applicant.includes(search)) return false; // developer 只能看到自己的工单 if (isDeveloper && t.applicant !== '王五' && t.applicant !== 'developer') return false; return true; }); }, [search, statusFilter, dbTypeFilter, priorityFilter, isDeveloper]); const statusCounts = useMemo(() => { const counts: Record = {}; tickets.forEach(t => { counts[t.status] = (counts[t.status] || 0) + 1; }); return counts; }, []); if (showNewTicket) { return setShowNewTicket(false)} />; } if (selectedTicket) { return setSelectedTicket(null)} canApprove={canApprove} canExecute={canExecute} />; } return (
setShowNewTicket(true), permission: 'ticket.create' } : undefined} /> {/* 状态快捷筛选 */}
{[ { key: 'pending', label: '待审批', count: statusCounts.pending || 0, color: 'text-yellow-400 bg-yellow-400/10 border-yellow-400/20' }, { key: 'reviewing', label: '审批中', count: statusCounts.reviewing || 0, color: 'text-blue-400 bg-blue-400/10 border-blue-400/20' }, { key: 'approved', label: '已通过', count: statusCounts.approved || 0, color: 'text-green-400 bg-green-400/10 border-green-400/20' }, { key: 'executing', label: '执行中', count: statusCounts.executing || 0, color: 'text-purple-400 bg-purple-400/10 border-purple-400/20' }, { key: 'success', label: '成功', count: statusCounts.success || 0, color: 'text-green-400 bg-green-400/10 border-green-400/20' }, { key: 'failed', label: '失败', count: statusCounts.failed || 0, color: 'text-red-400 bg-red-400/10 border-red-400/20' }, ].map(s => ( ))}
{/* 筛选栏 */}
setSearch(e.target.value)} />
{/* 工单表格 */} {filtered.length === 0 ? ( ) : ( <>
{filtered.map(ticket => ( ))}
工单号 标题 数据库 实例 申请人 优先级 状态 创建时间 操作
{ticket.id} {ticket.title} {ticket.dbType.toUpperCase()} {ticket.instance} {ticket.applicant} {ticket.priority === 'high' ? '高' : ticket.priority === 'medium' ? '中' : '低'} {statusMap[ticket.status]} {ticket.createTime}
{canApprove && ticket.status === 'pending' && } {canApprove && ticket.status === 'pending' && }
共 {filtered.length} 条记录
{['上一页', '1', '2', '3', '下一页'].map((p, i) => ( ))}
)}
); } function NewTicketForm({ onCancel }: { onCancel: () => void }) { const [sqlTab, setSqlTab] = useState<'change' | 'rollback'>('change'); const [sql, setSql] = useState(''); return (

新建数据变更工单