import { useState, useEffect } from 'react'; import { getChannelListApi, saveChannelApi, updateChannelApi, deleteChannelApi, getAllCategoryListApi } from '../../../api/radio'; import { FileUploader } from '../../../components/FileUploader'; import { DeleteConfirm } from '../../../components/DeleteConfirm'; import { Button } from '../../../components/ui/button'; import { Input } from '../../../components/ui/input'; import { Table, TableBody, TableCell, TableHead, TableHeader, TableRow } from '../../../components/ui/table'; import { Dialog, DialogContent, DialogTitle } from '../../../components/ui/dialog'; import { Card, CardContent, CardHeader, CardTitle } from '../../../components/ui/card'; import { Label } from '../../../components/ui/label'; import { Plus, Edit, Trash2, Search, Disc3, Crown, Zap, LayoutGrid, CalendarDays } from 'lucide-react'; import { toast } from 'sonner'; import { motion, AnimatePresence } from 'framer-motion'; export default function Channel() { const [data, setData] = useState([]); const [total, setTotal] = useState(0); const [loading, setLoading] = useState(false); const [categories, setCategories] = useState([]); const [selectedCategoryId, setSelectedCategoryId] = useState(""); const [page, setPage] = useState(1); const [pageSize] = useState(10); const [searchName, setSearchName] = useState(''); const [debouncedSearch, setDebouncedSearch] = useState(''); const [open, setOpen] = useState(false); const [isEdit, setIsEdit] = useState(false); const [formData, setFormData] = useState({ id: undefined, categoryId: '', name: '', description: '', isFree: 0, isVipOnly: 0, monthlyPrice: 0, quarterlyPrice: 0, annualPrice: 0, coverId: '', coverUrl: '', tags: '', sort: 0, status: 1 }); const [deleteOpen, setDeleteOpen] = useState(false); const [deleteId, setDeleteId] = useState(null); const fetchCategories = async () => { try { const res: any = await getAllCategoryListApi(); setCategories(res.list || res || []); } catch (e) { console.error(e); } } const fetchData = async () => { setLoading(true); try { const res: any = await getChannelListApi({ current: page, pageSize: pageSize, name: debouncedSearch || "", categoryId: selectedCategoryId || "" }); setData(res.list || res || []); setTotal(res.total || 0); } catch (e) { console.error(e); } finally { setLoading(false); } }; useEffect(() => { fetchCategories(); }, []); useEffect(() => { fetchData(); }, [page, pageSize, selectedCategoryId, debouncedSearch]); useEffect(() => { const timer = setTimeout(() => { setDebouncedSearch(searchName); setPage(1); }, 500); return () => clearTimeout(timer); }, [searchName]); const handleOpenAdd = () => { setIsEdit(false); setFormData({ id: undefined, categoryId: selectedCategoryId || '', name: '', description: '', isFree: 0, isVipOnly: 0, monthlyPrice: 0, quarterlyPrice: 0, annualPrice: 0, coverId: '', coverUrl: '', tags: '', sort: 0, status: 1 }); setOpen(true); }; const handleOpenEdit = (record: any) => { setIsEdit(true); setFormData({ id: String(record.ID || record.id || ""), categoryId: record.categoryId, name: record.name, description: record.description, isFree: record.isFree || 0, isVipOnly: record.isVipOnly || 0, monthlyPrice: record.monthlyPrice || 0, quarterlyPrice: record.quarterlyPrice || 0, annualPrice: record.annualPrice || 0, coverId: record.coverId, coverUrl: record.cover?.url || record.coverUrl || "", tags: record.tags, sort: record.sort, status: record.status }); setOpen(true); }; const handleDeleteClick = (id: any) => { setDeleteId(id); setDeleteOpen(true); }; const confirmDelete = async () => { if (!deleteId) return; try { await deleteChannelApi({ id: String(deleteId) }); toast.success('删除成功'); fetchData(); } catch (e) { console.error(e); } finally { setDeleteOpen(false); setDeleteId(null); } }; const handleSubmit = async () => { if (!formData.name) return toast.error('请填写频道名称'); if (!formData.categoryId) return toast.error('请选择所属分类'); const submitData = { ...formData }; if (submitData.sort === "" || submitData.sort === undefined || submitData.sort === null) submitData.sort = 0; if (submitData.monthlyPrice === "" || submitData.monthlyPrice === undefined || submitData.monthlyPrice === null) submitData.monthlyPrice = 0; if (submitData.quarterlyPrice === "" || submitData.quarterlyPrice === undefined || submitData.quarterlyPrice === null) submitData.quarterlyPrice = 0; if (submitData.annualPrice === "" || submitData.annualPrice === undefined || submitData.annualPrice === null) submitData.annualPrice = 0; try { if (isEdit) { await updateChannelApi(submitData); toast.success('更新成功'); } else { await saveChannelApi(submitData); toast.success('创建成功'); } setOpen(false); fetchData(); } catch (e) { console.error(e); } }; return (

频道管理

构建多元化的电台生态,管理每一个声音频道。

频道分类
{categories.map((category: any) => { const catId = String(category.ID || category.id || ""); const isSelected = selectedCategoryId === catId; return ( ); })}
{selectedCategoryId ? categories.find(c => String(c.ID || c.id) === selectedCategoryId)?.name : '电台频道全集'}
setSearchName(e.target.value)} className="pl-12 h-12 rounded-2xl border-none bg-[#FAF5E6]/80 focus:bg-white shadow-inner transition-all font-bold text-[#4A3A2C]" />
视觉封面 频道信息 价格体系 (元) 准入权限 服务状态 管理指令 {loading ? ( 同步数据中... ) : data.length === 0 ? ( 未发现相关频道 ) : ( data.map((item: any) => (

{item.name}

#{categories.find(c => String(c.ID || c.id) === String(item.categoryId))?.name || '未分类'}

包月

¥{(item.monthlyPrice / 100).toFixed(2)}

包季

¥{(item.quarterlyPrice / 100).toFixed(2)}

包年

¥{(item.annualPrice / 100).toFixed(2)}

{item.isVipOnly === 1 ? (
VIP 专属
) : item.isFree === 1 ? (
全员免费
) : ( 标准访问 )}
{item.status === 1 ? '启用' : '下架'}
)) )}
{total > pageSize && (
{page} / {Math.ceil(total / pageSize)} 页
)}
{isEdit ? '编辑频道方案' : '策划全新广播频道'}

Channel Ecosystem Design

setFormData({ ...formData, name: e.target.value })} className="h-16 rounded-[1.5rem] border-none bg-white shadow-sm font-bold text-[#4A3A2C] focus:ring-4 ring-[#D28F4F]/10 transition-all pl-8 text-lg" />
{ const val = e.target.value; setFormData({ ...formData, sort: val === "" ? "" : parseInt(val) }); }} className="h-16 rounded-[1.5rem] border-none bg-white shadow-sm font-bold text-center text-[#4A3A2C] focus:ring-4 ring-[#D28F4F]/10 transition-all text-lg" />
setFormData({ ...formData, coverId: id })} />
包月 { const val = e.target.value; setFormData({ ...formData, monthlyPrice: val === "" ? "" : Math.round(parseFloat(val) * 100) }); }} className="h-10 border-none shadow-none font-black text-lg text-right flex-1 bg-transparent text-[#4A3A2C]" />
包季 { const val = e.target.value; setFormData({ ...formData, quarterlyPrice: val === "" ? "" : Math.round(parseFloat(val) * 100) }); }} className="h-10 border-none shadow-none font-black text-lg text-right flex-1 bg-transparent text-[#4A3A2C]" />
包年 { const val = e.target.value; setFormData({ ...formData, annualPrice: val === "" ? "" : Math.round(parseFloat(val) * 100) }); }} className="h-10 border-none shadow-none font-black text-lg text-right flex-1 bg-transparent text-[#D28F4F]" />
setFormData({ ...formData, tags: e.target.value })} className="h-16 rounded-[1.5rem] border-none bg-white shadow-sm font-bold text-[#4A3A2C] focus:ring-4 ring-[#D28F4F]/10 transition-all pl-8 text-lg" />