Skip to content

Commit 4b72aa1

Browse files
h7mlclaude
andcommitted
feat: 重构 Admin 页面为多模块管理系统
**新增功能模块** - 文件管理 - 原有的文件 CRUD 功能 - 素材管理 - 素材广场内容管理入口 - 用户管理 - 用户和权限管理(待开发) - 数据统计 - 系统运营数据概览 **标签页导航** - 使用 Tabs 组件实现模块切换 - 每个标签带图标和标题 - 响应式 4 列网格布局 **数据统计仪表板** - 总文件数统计卡片 - 公开文件数统计卡片 - HTML 文件数统计卡片 - Markdown 文件数统计卡片 - 彩色图标和大字号数据展示 **素材管理优化** - 引导用户在文件管理中配置素材 - 快速跳转到文件管理标签页 **用户管理占位** - 预留用户管理功能入口 - 待后续开发完善 **整体优化** - 统一的页面标题和描述 - 更清晰的功能分区 - 更好的视觉层次 - 支持扩展更多管理模块 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
1 parent 95b4663 commit 4b72aa1

1 file changed

Lines changed: 212 additions & 80 deletions

File tree

src/app/admin/page.tsx

Lines changed: 212 additions & 80 deletions
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,10 @@ import {
2828
ExternalLink,
2929
Maximize2,
3030
Minimize2,
31-
X
31+
X,
32+
ShoppingBag,
33+
User,
34+
TrendingUp
3235
} from 'lucide-react'
3336
import dynamic from 'next/dynamic'
3437

@@ -520,91 +523,220 @@ export default function AdminDashboard() {
520523

521524
{/* Main Content */}
522525
<main className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8 py-8">
523-
<div className="flex justify-between items-center mb-6">
524-
<h2 className="text-2xl font-bold text-gray-900">文件管理</h2>
525-
<Button onClick={handleCreateFile}>
526-
<Plus className="w-4 h-4 mr-2" />
527-
新建文件
528-
</Button>
529-
</div>
526+
{/* 管理模块标签页 */}
527+
<Tabs defaultValue="files" className="space-y-6">
528+
<TabsList className="grid grid-cols-4 w-full max-w-2xl">
529+
<TabsTrigger value="files" className="gap-2">
530+
<FileText className="w-4 h-4" />
531+
文件管理
532+
</TabsTrigger>
533+
<TabsTrigger value="materials" className="gap-2">
534+
<ShoppingBag className="w-4 h-4" />
535+
素材管理
536+
</TabsTrigger>
537+
<TabsTrigger value="users" className="gap-2">
538+
<User className="w-4 h-4" />
539+
用户管理
540+
</TabsTrigger>
541+
<TabsTrigger value="stats" className="gap-2">
542+
<TrendingUp className="w-4 h-4" />
543+
数据统计
544+
</TabsTrigger>
545+
</TabsList>
530546

531-
{/* Files List */}
532-
<div className="grid gap-4">
533-
{files.map((file) => (
534-
<Card key={file.id}>
535-
<CardContent className="p-6">
536-
<div className="flex items-center justify-between">
537-
<div className="flex items-center space-x-4">
538-
<div className="w-10 h-10 bg-purple-100 rounded-lg flex items-center justify-center">
539-
{file.type === 'html' ? (
540-
<Code className="w-5 h-5 text-purple-600" />
541-
) : (
542-
<FileText className="w-5 h-5 text-purple-600" />
543-
)}
544-
</div>
545-
<div>
546-
<h3 className="font-semibold text-gray-900">{file.title || file.filename}</h3>
547-
<p className="text-sm text-gray-600">{file.filename}</p>
548-
<div className="flex items-center space-x-2 mt-1">
549-
<Badge variant={file.type === 'html' ? 'default' : 'secondary'}>
550-
{file.type.toUpperCase()}
551-
</Badge>
552-
{file.isPublic && (
553-
<Badge variant="outline">公开</Badge>
554-
)}
555-
<span className="text-xs text-gray-500">
556-
{new Date(file.createdAt).toLocaleString()}
557-
</span>
547+
{/* 文件管理 */}
548+
<TabsContent value="files" className="space-y-6">
549+
<div className="flex justify-between items-center">
550+
<div>
551+
<h2 className="text-2xl font-bold text-gray-900">文件管理</h2>
552+
<p className="text-sm text-gray-600 mt-1">管理系统中的所有文件</p>
553+
</div>
554+
<Button onClick={handleCreateFile} className="gap-2">
555+
<Plus className="w-4 h-4" />
556+
新建文件
557+
</Button>
558+
</div>
559+
560+
{/* Files List */}
561+
<div className="grid gap-4">
562+
{files.map((file) => (
563+
<Card key={file.id}>
564+
<CardContent className="p-6">
565+
<div className="flex items-center justify-between">
566+
<div className="flex items-center space-x-4">
567+
<div className="w-10 h-10 bg-purple-100 rounded-lg flex items-center justify-center">
568+
{file.type === 'html' ? (
569+
<Code className="w-5 h-5 text-purple-600" />
570+
) : (
571+
<FileText className="w-5 h-5 text-purple-600" />
572+
)}
573+
</div>
574+
<div>
575+
<h3 className="font-semibold text-gray-900">{file.title || file.filename}</h3>
576+
<p className="text-sm text-gray-600">{file.filename}</p>
577+
<div className="flex items-center space-x-2 mt-1">
578+
<Badge variant={file.type === 'html' ? 'default' : 'secondary'}>
579+
{file.type.toUpperCase()}
580+
</Badge>
581+
{file.isPublic && (
582+
<Badge variant="outline">公开</Badge>
583+
)}
584+
<span className="text-xs text-gray-500">
585+
{new Date(file.createdAt).toLocaleString()}
586+
</span>
587+
</div>
588+
</div>
589+
</div>
590+
<div className="flex items-center space-x-2">
591+
<Button
592+
variant="outline"
593+
size="sm"
594+
onClick={() => handleCopyShareUrl(file.shareUrl)}
595+
>
596+
<Copy className="w-4 h-4" />
597+
</Button>
598+
<Button
599+
variant="outline"
600+
size="sm"
601+
onClick={() => window.open(file.shareUrl, '_blank')}
602+
>
603+
<ExternalLink className="w-4 h-4" />
604+
</Button>
605+
<Button
606+
variant="outline"
607+
size="sm"
608+
onClick={() => handleEditFile(file)}
609+
>
610+
<Edit className="w-4 h-4" />
611+
</Button>
612+
<Button
613+
variant="outline"
614+
size="sm"
615+
onClick={() => handleDeleteFile(file.id)}
616+
>
617+
<Trash2 className="w-4 h-4" />
618+
</Button>
558619
</div>
559620
</div>
560-
</div>
561-
<div className="flex items-center space-x-2">
562-
<Button
563-
variant="outline"
564-
size="sm"
565-
onClick={() => handleCopyShareUrl(file.shareUrl)}
566-
>
567-
<Copy className="w-4 h-4" />
568-
</Button>
569-
<Button
570-
variant="outline"
571-
size="sm"
572-
onClick={() => window.open(file.shareUrl, '_blank')}
573-
>
574-
<ExternalLink className="w-4 h-4" />
575-
</Button>
576-
<Button
577-
variant="outline"
578-
size="sm"
579-
onClick={() => handleEditFile(file)}
580-
>
581-
<Edit className="w-4 h-4" />
582-
</Button>
583-
<Button
584-
variant="outline"
585-
size="sm"
586-
onClick={() => handleDeleteFile(file.id)}
587-
>
588-
<Trash2 className="w-4 h-4" />
589-
</Button>
590-
</div>
591-
</div>
621+
</CardContent>
622+
</Card>
623+
))}
624+
</div>
625+
626+
{files.length === 0 && (
627+
<div className="text-center py-12">
628+
<FileText className="w-12 h-12 text-gray-400 mx-auto mb-4" />
629+
<h3 className="text-lg font-medium text-gray-900 mb-2">暂无文件</h3>
630+
<p className="text-gray-600 mb-4">创建您的第一个文件开始使用</p>
631+
<Button onClick={handleCreateFile}>
632+
<Plus className="w-4 h-4 mr-2" />
633+
新建文件
634+
</Button>
635+
</div>
636+
)}
637+
</TabsContent>
638+
639+
{/* 素材管理 */}
640+
<TabsContent value="materials" className="space-y-6">
641+
<div>
642+
<h2 className="text-2xl font-bold text-gray-900">素材管理</h2>
643+
<p className="text-sm text-gray-600 mt-1">管理素材广场中展示的素材</p>
644+
</div>
645+
<Card>
646+
<CardContent className="p-8 text-center">
647+
<ShoppingBag className="w-12 h-12 text-gray-400 mx-auto mb-4" />
648+
<h3 className="text-lg font-medium text-gray-900 mb-2">素材管理功能</h3>
649+
<p className="text-gray-600 mb-4">
650+
在文件管理中勾选"在素材广场展示"即可将文件添加到素材广场
651+
</p>
652+
<Button variant="outline" onClick={() => {
653+
const tabsList = document.querySelector('[role="tablist"]') as HTMLElement
654+
const filesTab = tabsList?.querySelector('[value="files"]') as HTMLButtonElement
655+
filesTab?.click()
656+
}}>
657+
前往文件管理
658+
</Button>
592659
</CardContent>
593660
</Card>
594-
))}
595-
</div>
661+
</TabsContent>
596662

597-
{files.length === 0 && (
598-
<div className="text-center py-12">
599-
<FileText className="w-12 h-12 text-gray-400 mx-auto mb-4" />
600-
<h3 className="text-lg font-medium text-gray-900 mb-2">暂无文件</h3>
601-
<p className="text-gray-600 mb-4">创建您的第一个文件开始使用</p>
602-
<Button onClick={handleCreateFile}>
603-
<Plus className="w-4 h-4 mr-2" />
604-
新建文件
605-
</Button>
606-
</div>
607-
)}
663+
{/* 用户管理 */}
664+
<TabsContent value="users" className="space-y-6">
665+
<div>
666+
<h2 className="text-2xl font-bold text-gray-900">用户管理</h2>
667+
<p className="text-sm text-gray-600 mt-1">管理系统用户和权限</p>
668+
</div>
669+
<Card>
670+
<CardContent className="p-8 text-center">
671+
<User className="w-12 h-12 text-gray-400 mx-auto mb-4" />
672+
<h3 className="text-lg font-medium text-gray-900 mb-2">用户管理功能</h3>
673+
<p className="text-gray-600">
674+
此功能正在开发中,敬请期待...
675+
</p>
676+
</CardContent>
677+
</Card>
678+
</TabsContent>
679+
680+
{/* 数据统计 */}
681+
<TabsContent value="stats" className="space-y-6">
682+
<div>
683+
<h2 className="text-2xl font-bold text-gray-900">数据统计</h2>
684+
<p className="text-sm text-gray-600 mt-1">查看系统运营数据</p>
685+
</div>
686+
<div className="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-4">
687+
<Card>
688+
<CardContent className="p-6">
689+
<div className="flex items-center justify-between">
690+
<div>
691+
<p className="text-sm text-gray-600">总文件数</p>
692+
<p className="text-2xl font-bold text-gray-900 mt-1">{files.length}</p>
693+
</div>
694+
<FileText className="w-8 h-8 text-purple-600" />
695+
</div>
696+
</CardContent>
697+
</Card>
698+
<Card>
699+
<CardContent className="p-6">
700+
<div className="flex items-center justify-between">
701+
<div>
702+
<p className="text-sm text-gray-600">公开文件</p>
703+
<p className="text-2xl font-bold text-gray-900 mt-1">
704+
{files.filter(f => f.isPublic).length}
705+
</p>
706+
</div>
707+
<Eye className="w-8 h-8 text-blue-600" />
708+
</div>
709+
</CardContent>
710+
</Card>
711+
<Card>
712+
<CardContent className="p-6">
713+
<div className="flex items-center justify-between">
714+
<div>
715+
<p className="text-sm text-gray-600">HTML 文件</p>
716+
<p className="text-2xl font-bold text-gray-900 mt-1">
717+
{files.filter(f => f.type === 'html').length}
718+
</p>
719+
</div>
720+
<Code className="w-8 h-8 text-green-600" />
721+
</div>
722+
</CardContent>
723+
</Card>
724+
<Card>
725+
<CardContent className="p-6">
726+
<div className="flex items-center justify-between">
727+
<div>
728+
<p className="text-sm text-gray-600">Markdown 文件</p>
729+
<p className="text-2xl font-bold text-gray-900 mt-1">
730+
{files.filter(f => f.type === 'markdown').length}
731+
</p>
732+
</div>
733+
<FileText className="w-8 h-8 text-orange-600" />
734+
</div>
735+
</CardContent>
736+
</Card>
737+
</div>
738+
</TabsContent>
739+
</Tabs>
608740
</main>
609741

610742
{/* Editor Dialog */}

0 commit comments

Comments
 (0)