import { useState, useEffect, useCallback } from 'react' import { channelsApi, type Channel, type ChannelUpsertRequest } from '@/api/channels' import { Card, CardContent, CardDescription, CardHeader, CardTitle } from '@/components/ui/card' import { Button } from '@/components/ui/button' import { Input } from '@/components/ui/input' import { Badge } from '@/components/ui/badge' import { Switch } from '@/components/ui/switch' import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow, } from '@/components/ui/table' import { Dialog, DialogContent, DialogDescription, DialogFooter, DialogHeader, DialogTitle, } from '@/components/ui/dialog' import { PaginationNavigator } from '@/components/ui/pagination' import { Label } from '@/components/ui/label' import { Textarea } from '@/components/ui/textarea' import { Loader2, Plus, Pencil, Trash2, RefreshCw, Layers } from 'lucide-react' import { format } from 'date-fns' export default function Channels() { const [channels, setChannels] = useState([]) const [loading, setLoading] = useState(true) const [page, setPage] = useState(1) const pageSize = 10 // 创建/编辑对话框 const [dialogOpen, setDialogOpen] = useState(false) const [editingChannel, setEditingChannel] = useState(null) const [formData, setFormData] = useState({ name: '', slug: '', description: '', sort_order: 0, is_active: true, }) const [dialogLoading, setDialogLoading] = useState(false) // 删除确认对话框 const [deleteOpen, setDeleteOpen] = useState(false) const [deletingChannel, setDeletingChannel] = useState(null) const [deleteLoading, setDeleteLoading] = useState(false) // 加载频道列表 const loadChannels = useCallback(async () => { setLoading(true) try { const response = await channelsApi.getChannels() const data = response.data.data const list = Array.isArray(data) ? data : (data?.list || []) list.sort((a: Channel, b: Channel) => a.sort_order - b.sort_order) setChannels(list) } catch (error) { console.error('Failed to load channels:', error) setChannels([]) } finally { setLoading(false) } }, []) useEffect(() => { loadChannels() }, [loadChannels]) // 打开创建对话框 const handleOpenCreate = () => { setEditingChannel(null) setFormData({ name: '', slug: '', description: '', sort_order: channels.length, is_active: true, }) setDialogOpen(true) } // 打开编辑对话框 const handleOpenEdit = (channel: Channel) => { setEditingChannel(channel) setFormData({ name: channel.name, slug: channel.slug, description: channel.description, sort_order: channel.sort_order, is_active: channel.is_active, }) setDialogOpen(true) } // 生成 slug(根据名称) const generateSlug = (name: string) => { return name .toLowerCase() .replace(/[^\w\u4e00-\u9fa5]+/g, '-') .replace(/^-+|-+$/g, '') } // 名称改变时自动生成 slug const handleNameChange = (name: string) => { setFormData({ ...formData, name, slug: editingChannel ? formData.slug : generateSlug(name), }) } // 保存频道 const handleSave = async () => { if (!formData.name?.trim() || !formData.slug?.trim()) return setDialogLoading(true) try { if (editingChannel) { await channelsApi.updateChannel(editingChannel.id, formData) } else { await channelsApi.createChannel(formData) } setDialogOpen(false) loadChannels() } catch (error: any) { console.error('Failed to save channel:', error) alert(error?.response?.data?.message || '保存失败') } finally { setDialogLoading(false) } } // 打开删除确认 const handleOpenDelete = (channel: Channel) => { setDeletingChannel(channel) setDeleteOpen(true) } // 确认删除 const handleConfirmDelete = async () => { if (!deletingChannel) return setDeleteLoading(true) try { await channelsApi.deleteChannel(deletingChannel.id) setDeleteOpen(false) loadChannels() } catch (error: any) { console.error('Failed to delete channel:', error) alert(error?.response?.data?.message || '删除失败') } finally { setDeleteLoading(false) } } // 格式化日期 const formatDate = (dateStr: string) => { try { return format(new Date(dateStr), 'yyyy-MM-dd HH:mm') } catch { return dateStr } } // 分页后的数据 const paginatedChannels = channels.slice((page - 1) * pageSize, page * pageSize) const totalPages = Math.ceil(channels.length / pageSize) return (

频道管理

管理帖子的分类频道

频道列表 共 {channels.length} 个频道 排序 频道名称 Slug 描述 状态 创建时间 操作 {loading ? (

加载中...

) : paginatedChannels.length === 0 ? ( 暂无数据,点击「添加频道」创建第一个频道 ) : ( paginatedChannels.map((channel) => ( {channel.sort_order} {channel.name} {channel.slug} {channel.description || '-'} {channel.is_active ? '启用' : '禁用'} {formatDate(channel.created_at)}
)) )}
{totalPages > 1 && (
)}
{/* 创建/编辑对话框 */} {editingChannel ? '编辑频道' : '添加频道'} {editingChannel ? '修改频道信息' : '创建新的帖子分类频道'}
handleNameChange(e.target.value)} placeholder="如:校园生活" />
setFormData({ ...formData, slug: e.target.value })} placeholder="如:campus-life" />

用于 URL 和 API 调用,建议使用英文小写、数字和连字符