refactor: 重构分页逻辑并统一代码风格
1. 新增分页数据处理hook和工具函数,实现分页状态统一管理 2. 替换所有页面的表格分页配置为标准current/page模式 3. 统一项目内字符串引号风格为双引号 4. 调整权限树面板样式和布局代码格式
This commit is contained in:
@@ -1,27 +1,23 @@
|
||||
import { useEffect, useState } from "react";
|
||||
import { useNavigate, useParams } from "react-router-dom";
|
||||
import {
|
||||
Button,
|
||||
Card,
|
||||
Descriptions,
|
||||
Form,
|
||||
Grid,
|
||||
Input,
|
||||
InputNumber,
|
||||
InputTag,
|
||||
Message,
|
||||
Modal,
|
||||
Select,
|
||||
Space,
|
||||
Switch,
|
||||
Table,
|
||||
Tag,
|
||||
Button,
|
||||
Card,
|
||||
Descriptions,
|
||||
Form,
|
||||
Grid,
|
||||
Input,
|
||||
InputNumber,
|
||||
InputTag,
|
||||
Message,
|
||||
Modal,
|
||||
Select,
|
||||
Space,
|
||||
Switch,
|
||||
Table,
|
||||
Tag,
|
||||
} from "@arco-design/web-react";
|
||||
import {
|
||||
IconArrowLeft,
|
||||
IconPlus,
|
||||
IconRefresh,
|
||||
} from "@arco-design/web-react/icon";
|
||||
import { IconArrowLeft, IconPlus, IconRefresh } from "@arco-design/web-react/icon";
|
||||
import type { ApiResponse, Drama, Episode, UploadTask } from "../api/types";
|
||||
import { api } from "../api/client";
|
||||
import { usePageData } from "../hooks/usePageData";
|
||||
@@ -29,450 +25,386 @@ import { usePageData } from "../hooks/usePageData";
|
||||
const { Row, Col } = Grid;
|
||||
|
||||
const STATUS_OPTIONS: { value: string; label: string }[] = [
|
||||
{ value: "draft", label: "草稿" },
|
||||
{ value: "scheduled", label: "定时发布" },
|
||||
{ value: "published", label: "已发布" },
|
||||
{ value: "offline", label: "已下线" },
|
||||
{ value: "draft", label: "草稿" },
|
||||
{ value: "scheduled", label: "定时发布" },
|
||||
{ value: "published", label: "已发布" },
|
||||
{ value: "offline", label: "已下线" },
|
||||
];
|
||||
|
||||
const REVIEW_STATUS_LABEL: Record<string, string> = {
|
||||
not_submitted: "未提交",
|
||||
pending: "待审核",
|
||||
reviewing: "审核中",
|
||||
approved: "已通过",
|
||||
rejected: "已拒绝",
|
||||
not_submitted: "未提交",
|
||||
pending: "待审核",
|
||||
reviewing: "审核中",
|
||||
approved: "已通过",
|
||||
rejected: "已拒绝",
|
||||
};
|
||||
|
||||
const PUBLISH_STATUS_LABEL: Record<string, string> = {
|
||||
draft: "草稿",
|
||||
scheduled: "定时发布",
|
||||
published: "已发布",
|
||||
offline: "已下线",
|
||||
draft: "草稿",
|
||||
scheduled: "定时发布",
|
||||
published: "已发布",
|
||||
offline: "已下线",
|
||||
};
|
||||
|
||||
function DramasPage() {
|
||||
const { id } = useParams();
|
||||
const { id } = useParams();
|
||||
|
||||
if (id) {
|
||||
return <DramaDetailPage dramaId={Number(id)} />;
|
||||
}
|
||||
if (id) {
|
||||
return <DramaDetailPage dramaId={Number(id)} />;
|
||||
}
|
||||
|
||||
return <DramaListPage />;
|
||||
return <DramaListPage />;
|
||||
}
|
||||
|
||||
function DramaListPage() {
|
||||
const navigate = useNavigate();
|
||||
const { data, loading, reload } = usePageData<Drama>("/api/admin/v1/dramas");
|
||||
const [visible, setVisible] = useState(false);
|
||||
const [creating, setCreating] = useState(false);
|
||||
const navigate = useNavigate();
|
||||
const { data, loading, reload, changePage } = usePageData<Drama>("/api/admin/v1/dramas");
|
||||
const [visible, setVisible] = useState(false);
|
||||
const [creating, setCreating] = useState(false);
|
||||
|
||||
const createDrama = async (values: Partial<Drama>) => {
|
||||
setCreating(true);
|
||||
try {
|
||||
await api.post("/api/admin/v1/dramas", values);
|
||||
Message.success("短剧创建成功");
|
||||
setVisible(false);
|
||||
await reload();
|
||||
} finally {
|
||||
setCreating(false);
|
||||
}
|
||||
};
|
||||
const createDrama = async (values: Partial<Drama>) => {
|
||||
setCreating(true);
|
||||
try {
|
||||
await api.post("/api/admin/v1/dramas", values);
|
||||
Message.success("短剧创建成功");
|
||||
setVisible(false);
|
||||
await reload();
|
||||
} finally {
|
||||
setCreating(false);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<Card
|
||||
className="section-card"
|
||||
title="短剧管理"
|
||||
extra={
|
||||
<Button
|
||||
type="primary"
|
||||
icon={<IconPlus />}
|
||||
onClick={() => setVisible(true)}
|
||||
return (
|
||||
<Card
|
||||
className="section-card"
|
||||
title="短剧管理"
|
||||
extra={
|
||||
<Button type="primary" icon={<IconPlus />} onClick={() => setVisible(true)}>
|
||||
新增短剧
|
||||
</Button>
|
||||
}
|
||||
>
|
||||
新增短剧
|
||||
</Button>
|
||||
}
|
||||
>
|
||||
<Table
|
||||
rowKey="id"
|
||||
loading={loading}
|
||||
data={data.list}
|
||||
pagination={{
|
||||
total: data.pagination.total,
|
||||
pageSize: data.pagination.pageSize,
|
||||
}}
|
||||
columns={[
|
||||
{ title: "ID", dataIndex: "id", width: 80 },
|
||||
{ title: "标题", dataIndex: "title" },
|
||||
{ title: "地区", dataIndex: "region", width: 120 },
|
||||
{ title: "类型", dataIndex: "type", width: 140 },
|
||||
{ title: "总集数", dataIndex: "totalEpisodes", width: 100 },
|
||||
{ title: "已发布", dataIndex: "publishedEpisodes", width: 100 },
|
||||
{
|
||||
title: "状态",
|
||||
dataIndex: "status",
|
||||
width: 120,
|
||||
render: (status) => (
|
||||
<Tag color={status === "published" ? "green" : "gray"}>
|
||||
{PUBLISH_STATUS_LABEL[String(status)] || String(status)}
|
||||
</Tag>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: "推荐",
|
||||
dataIndex: "isRecommended",
|
||||
width: 100,
|
||||
render: (value) =>
|
||||
value ? <Tag color="arcoblue">是</Tag> : <Tag>否</Tag>,
|
||||
},
|
||||
{
|
||||
title: "操作",
|
||||
width: 120,
|
||||
render: (_, record) => (
|
||||
<Button
|
||||
type="text"
|
||||
onClick={() => navigate(`/dramas/${record.id}`)}
|
||||
>
|
||||
详情
|
||||
</Button>
|
||||
),
|
||||
},
|
||||
]}
|
||||
/>
|
||||
<Modal
|
||||
title="创建短剧"
|
||||
visible={visible}
|
||||
onCancel={() => setVisible(false)}
|
||||
footer={null}
|
||||
style={{ width: 640 }}
|
||||
>
|
||||
<Form
|
||||
layout="vertical"
|
||||
onSubmit={createDrama}
|
||||
initialValues={{
|
||||
status: "draft",
|
||||
isRecommended: false,
|
||||
sortOrder: 0,
|
||||
}}
|
||||
>
|
||||
<Form.Item label="标题" field="title" rules={[{ required: true }]}>
|
||||
<Input placeholder="请输入短剧标题" />
|
||||
</Form.Item>
|
||||
<Row gutter={16}>
|
||||
<Col span={12}>
|
||||
<Form.Item label="类型" field="type" rules={[{ required: true }]}>
|
||||
<Input placeholder="请输入类型" />
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={12}>
|
||||
<Form.Item label="状态" field="status">
|
||||
<Select placeholder="请选择状态">
|
||||
{STATUS_OPTIONS.map((option) => (
|
||||
<Select.Option key={option.value} value={option.value}>
|
||||
{option.label}
|
||||
</Select.Option>
|
||||
))}
|
||||
</Select>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
</Row>
|
||||
<Form.Item
|
||||
label="封面地址"
|
||||
field="coverUrl"
|
||||
rules={[{ required: true }]}
|
||||
>
|
||||
<Input placeholder="请输入封面图片 URL" />
|
||||
</Form.Item>
|
||||
<Form.Item label="简介" field="description">
|
||||
<Input.TextArea
|
||||
placeholder="请输入短剧简介"
|
||||
autoSize={{ minRows: 2, maxRows: 4 }}
|
||||
<Table
|
||||
rowKey="id"
|
||||
loading={loading}
|
||||
data={data.list}
|
||||
pagination={{
|
||||
current: data.pagination.page,
|
||||
total: data.pagination.total,
|
||||
pageSize: data.pagination.pageSize,
|
||||
onChange: (page, pageSize) => changePage(page, pageSize),
|
||||
}}
|
||||
columns={[
|
||||
{ title: "ID", dataIndex: "id", width: 80 },
|
||||
{ title: "标题", dataIndex: "title" },
|
||||
{ title: "地区", dataIndex: "region", width: 120 },
|
||||
{ title: "类型", dataIndex: "type", width: 140 },
|
||||
{ title: "总集数", dataIndex: "totalEpisodes", width: 100 },
|
||||
{ title: "已发布", dataIndex: "publishedEpisodes", width: 100 },
|
||||
{
|
||||
title: "状态",
|
||||
dataIndex: "status",
|
||||
width: 120,
|
||||
render: (status) => (
|
||||
<Tag color={status === "published" ? "green" : "gray"}>{PUBLISH_STATUS_LABEL[String(status)] || String(status)}</Tag>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: "推荐",
|
||||
dataIndex: "isRecommended",
|
||||
width: 100,
|
||||
render: (value) => (value ? <Tag color="arcoblue">是</Tag> : <Tag>否</Tag>),
|
||||
},
|
||||
{
|
||||
title: "操作",
|
||||
width: 120,
|
||||
render: (_, record) => (
|
||||
<Button type="text" onClick={() => navigate(`/dramas/${record.id}`)}>
|
||||
详情
|
||||
</Button>
|
||||
),
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</Form.Item>
|
||||
<Form.Item label="标签" field="tags">
|
||||
<InputTag placeholder="输入后回车添加标签" allowClear />
|
||||
</Form.Item>
|
||||
<Row gutter={16}>
|
||||
<Col span={8}>
|
||||
<Form.Item label="地区" field="region">
|
||||
<Input placeholder="如 中国" />
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={8}>
|
||||
<Form.Item label="语言" field="language">
|
||||
<Input placeholder="如 zh-CN" />
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={8}>
|
||||
<Form.Item label="年份" field="year">
|
||||
<InputNumber
|
||||
placeholder="如 2026"
|
||||
min={1900}
|
||||
style={{ width: "100%" }}
|
||||
/>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
</Row>
|
||||
<Row gutter={16}>
|
||||
<Col span={12}>
|
||||
<Form.Item label="排序权重" field="sortOrder">
|
||||
<InputNumber
|
||||
placeholder="数值越大越靠前"
|
||||
style={{ width: "100%" }}
|
||||
/>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={12}>
|
||||
<Form.Item
|
||||
label="推荐"
|
||||
field="isRecommended"
|
||||
triggerPropName="checked"
|
||||
>
|
||||
<Switch />
|
||||
</Form.Item>
|
||||
</Col>
|
||||
</Row>
|
||||
<div className="modal-actions">
|
||||
<Button onClick={() => setVisible(false)}>取消</Button>
|
||||
<Button type="primary" htmlType="submit" loading={creating}>
|
||||
创建
|
||||
</Button>
|
||||
</div>
|
||||
</Form>
|
||||
</Modal>
|
||||
</Card>
|
||||
);
|
||||
<Modal title="创建短剧" visible={visible} onCancel={() => setVisible(false)} footer={null} style={{ width: 640 }}>
|
||||
<Form
|
||||
layout="vertical"
|
||||
onSubmit={createDrama}
|
||||
initialValues={{
|
||||
status: "draft",
|
||||
isRecommended: false,
|
||||
sortOrder: 0,
|
||||
}}
|
||||
>
|
||||
<Form.Item label="标题" field="title" rules={[{ required: true }]}>
|
||||
<Input placeholder="请输入短剧标题" />
|
||||
</Form.Item>
|
||||
<Row gutter={16}>
|
||||
<Col span={12}>
|
||||
<Form.Item label="类型" field="type" rules={[{ required: true }]}>
|
||||
<Input placeholder="请输入类型,如复仇、爱情、动作等" />
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={12}>
|
||||
<Form.Item label="状态" field="status">
|
||||
<Select placeholder="请选择状态">
|
||||
{STATUS_OPTIONS.map((option) => (
|
||||
<Select.Option key={option.value} value={option.value}>
|
||||
{option.label}
|
||||
</Select.Option>
|
||||
))}
|
||||
</Select>
|
||||
</Form.Item>
|
||||
</Col>
|
||||
</Row>
|
||||
<Form.Item label="封面地址" field="coverUrl" rules={[{ required: true }]}>
|
||||
<Input placeholder="请输入封面图片 URL" />
|
||||
</Form.Item>
|
||||
<Form.Item label="简介" field="description">
|
||||
<Input.TextArea placeholder="请输入短剧简介" autoSize={{ minRows: 2, maxRows: 4 }} />
|
||||
</Form.Item>
|
||||
<Form.Item label="标签" field="tags">
|
||||
<InputTag placeholder="输入后回车添加标签" allowClear />
|
||||
</Form.Item>
|
||||
<Row gutter={16}>
|
||||
<Col span={8}>
|
||||
<Form.Item label="地区" field="region">
|
||||
<Input placeholder="如 中国" />
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={8}>
|
||||
<Form.Item label="语言" field="language">
|
||||
<Input placeholder="如 zh-CN" />
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={8}>
|
||||
<Form.Item label="年份" field="year">
|
||||
<InputNumber placeholder="如 2026" min={1900} style={{ width: "100%" }} />
|
||||
</Form.Item>
|
||||
</Col>
|
||||
</Row>
|
||||
<Row gutter={16}>
|
||||
<Col span={12}>
|
||||
<Form.Item label="排序权重" field="sortOrder">
|
||||
<InputNumber placeholder="数值越大越靠前" style={{ width: "100%" }} />
|
||||
</Form.Item>
|
||||
</Col>
|
||||
<Col span={12}>
|
||||
<Form.Item label="推荐" field="isRecommended" triggerPropName="checked">
|
||||
<Switch />
|
||||
</Form.Item>
|
||||
</Col>
|
||||
</Row>
|
||||
<div className="modal-actions">
|
||||
<Button onClick={() => setVisible(false)}>取消</Button>
|
||||
<Button type="primary" htmlType="submit" loading={creating}>
|
||||
创建
|
||||
</Button>
|
||||
</div>
|
||||
</Form>
|
||||
</Modal>
|
||||
</Card>
|
||||
);
|
||||
}
|
||||
|
||||
function DramaDetailPage({ dramaId }: { dramaId: number }) {
|
||||
const navigate = useNavigate();
|
||||
const [drama, setDrama] = useState<Drama | null>(null);
|
||||
const [episodes, setEpisodes] = useState<Episode[]>([]);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [episodeCount, setEpisodeCount] = useState(1);
|
||||
const [uploadingEpisodeId, setUploadingEpisodeId] = useState<number | null>(
|
||||
null,
|
||||
);
|
||||
const navigate = useNavigate();
|
||||
const [drama, setDrama] = useState<Drama | null>(null);
|
||||
const [episodes, setEpisodes] = useState<Episode[]>([]);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [episodeCount, setEpisodeCount] = useState(1);
|
||||
const [uploadingEpisodeId, setUploadingEpisodeId] = useState<number | null>(null);
|
||||
|
||||
const loadDetail = async () => {
|
||||
setLoading(true);
|
||||
try {
|
||||
const [dramaResponse, episodeResponse] = await Promise.all([
|
||||
api.get<ApiResponse<Drama>>(`/api/admin/v1/dramas/${dramaId}`),
|
||||
api.get<ApiResponse<{ list: Episode[] }>>(
|
||||
`/api/admin/v1/dramas/${dramaId}/episodes?pageSize=200`,
|
||||
),
|
||||
]);
|
||||
setDrama(dramaResponse.data.data);
|
||||
setEpisodeCount(dramaResponse.data.data.totalEpisodes || 1);
|
||||
setEpisodes(episodeResponse.data.data.list);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
void loadDetail();
|
||||
}, [dramaId]);
|
||||
|
||||
const configureEpisodes = async () => {
|
||||
await api.post(`/api/admin/v1/dramas/${dramaId}/episodes/batch`, {
|
||||
episodeCount,
|
||||
});
|
||||
Message.success("剧集数量已更新");
|
||||
await loadDetail();
|
||||
};
|
||||
|
||||
const updateDramaStatus = async (status: string) => {
|
||||
await api.patch(`/api/admin/v1/dramas/${dramaId}`, { status });
|
||||
Message.success("短剧状态已更新");
|
||||
await loadDetail();
|
||||
};
|
||||
|
||||
const uploadEpisodeVideo = async (episode: Episode, file: File) => {
|
||||
setUploadingEpisodeId(episode.id);
|
||||
try {
|
||||
const task = await api.post<ApiResponse<UploadTask>>(
|
||||
"/api/admin/v1/media/upload-tasks",
|
||||
{
|
||||
episodeId: episode.id,
|
||||
fileName: file.name,
|
||||
contentType: file.type || "video/mp4",
|
||||
fileSize: file.size,
|
||||
},
|
||||
);
|
||||
await api.patch(
|
||||
`/api/admin/v1/media/upload-tasks/${task.data.data.jobId}/complete`,
|
||||
{},
|
||||
);
|
||||
await api.post("/api/admin/v1/media/review-status/sync", {});
|
||||
Message.success("视频上传并审核通过");
|
||||
await loadDetail();
|
||||
} finally {
|
||||
setUploadingEpisodeId(null);
|
||||
}
|
||||
};
|
||||
|
||||
const publishEpisode = async (episode: Episode) => {
|
||||
await api.patch(`/api/admin/v1/episodes/${episode.id}`, {
|
||||
publishStatus: "published",
|
||||
status: "published",
|
||||
});
|
||||
Message.success("剧集已发布");
|
||||
await loadDetail();
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="page-stack">
|
||||
<Card
|
||||
className="section-card"
|
||||
loading={loading && !drama}
|
||||
title={
|
||||
<Space>
|
||||
<Button
|
||||
icon={<IconArrowLeft />}
|
||||
onClick={() => navigate("/dramas")}
|
||||
/>
|
||||
短剧详情
|
||||
</Space>
|
||||
const loadDetail = async () => {
|
||||
setLoading(true);
|
||||
try {
|
||||
const [dramaResponse, episodeResponse] = await Promise.all([
|
||||
api.get<ApiResponse<Drama>>(`/api/admin/v1/dramas/${dramaId}`),
|
||||
api.get<ApiResponse<{ list: Episode[] }>>(`/api/admin/v1/dramas/${dramaId}/episodes?pageSize=200`),
|
||||
]);
|
||||
setDrama(dramaResponse.data.data);
|
||||
setEpisodeCount(dramaResponse.data.data.totalEpisodes || 1);
|
||||
setEpisodes(episodeResponse.data.data.list);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
extra={
|
||||
<Space>
|
||||
<Button icon={<IconRefresh />} onClick={loadDetail}>
|
||||
刷新
|
||||
</Button>
|
||||
<Button onClick={() => updateDramaStatus("draft")}>设为草稿</Button>
|
||||
<Button
|
||||
type="primary"
|
||||
onClick={() => updateDramaStatus("published")}
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
void loadDetail();
|
||||
}, [dramaId]);
|
||||
|
||||
const configureEpisodes = async () => {
|
||||
await api.post(`/api/admin/v1/dramas/${dramaId}/episodes/batch`, {
|
||||
episodeCount,
|
||||
});
|
||||
Message.success("剧集数量已更新");
|
||||
await loadDetail();
|
||||
};
|
||||
|
||||
const updateDramaStatus = async (status: string) => {
|
||||
await api.patch(`/api/admin/v1/dramas/${dramaId}`, { status });
|
||||
Message.success("短剧状态已更新");
|
||||
await loadDetail();
|
||||
};
|
||||
|
||||
const uploadEpisodeVideo = async (episode: Episode, file: File) => {
|
||||
setUploadingEpisodeId(episode.id);
|
||||
try {
|
||||
const task = await api.post<ApiResponse<UploadTask>>("/api/admin/v1/media/upload-tasks", {
|
||||
episodeId: episode.id,
|
||||
fileName: file.name,
|
||||
contentType: file.type || "video/mp4",
|
||||
fileSize: file.size,
|
||||
});
|
||||
await api.patch(`/api/admin/v1/media/upload-tasks/${task.data.data.jobId}/complete`, {});
|
||||
await api.post("/api/admin/v1/media/review-status/sync", {});
|
||||
Message.success("视频上传并审核通过");
|
||||
await loadDetail();
|
||||
} finally {
|
||||
setUploadingEpisodeId(null);
|
||||
}
|
||||
};
|
||||
|
||||
const publishEpisode = async (episode: Episode) => {
|
||||
await api.patch(`/api/admin/v1/episodes/${episode.id}`, {
|
||||
publishStatus: "published",
|
||||
status: "published",
|
||||
});
|
||||
Message.success("剧集已发布");
|
||||
await loadDetail();
|
||||
};
|
||||
|
||||
return (
|
||||
<div className="page-stack">
|
||||
<Card
|
||||
className="section-card"
|
||||
loading={loading && !drama}
|
||||
title={
|
||||
<Space>
|
||||
<Button icon={<IconArrowLeft />} onClick={() => navigate("/dramas")} />
|
||||
短剧详情
|
||||
</Space>
|
||||
}
|
||||
extra={
|
||||
<Space>
|
||||
<Button icon={<IconRefresh />} onClick={loadDetail}>
|
||||
刷新
|
||||
</Button>
|
||||
<Button onClick={() => updateDramaStatus("draft")}>设为草稿</Button>
|
||||
<Button type="primary" onClick={() => updateDramaStatus("published")}>
|
||||
上架短剧
|
||||
</Button>
|
||||
</Space>
|
||||
}
|
||||
>
|
||||
上架短剧
|
||||
</Button>
|
||||
</Space>
|
||||
}
|
||||
>
|
||||
{drama && (
|
||||
<Descriptions
|
||||
column={3}
|
||||
data={[
|
||||
{ label: "短剧ID", value: drama.id },
|
||||
{ label: "名称", value: drama.title },
|
||||
{ label: "地区", value: drama.region || "-" },
|
||||
{ label: "类型", value: drama.type },
|
||||
{ label: "总集数", value: drama.totalEpisodes || 0 },
|
||||
{ label: "已发布集数", value: drama.publishedEpisodes || 0 },
|
||||
{
|
||||
label: "状态",
|
||||
value: PUBLISH_STATUS_LABEL[drama.status] || drama.status,
|
||||
},
|
||||
{ label: "封面", value: drama.coverUrl },
|
||||
{ label: "简介", value: drama.description || "-" },
|
||||
]}
|
||||
/>
|
||||
)}
|
||||
</Card>
|
||||
{drama && (
|
||||
<Descriptions
|
||||
column={3}
|
||||
data={[
|
||||
{ label: "短剧ID", value: drama.id },
|
||||
{ label: "名称", value: drama.title },
|
||||
{ label: "地区", value: drama.region || "-" },
|
||||
{ label: "类型", value: drama.type },
|
||||
{ label: "总集数", value: drama.totalEpisodes || 0 },
|
||||
{ label: "已发布集数", value: drama.publishedEpisodes || 0 },
|
||||
{
|
||||
label: "状态",
|
||||
value: PUBLISH_STATUS_LABEL[drama.status] || drama.status,
|
||||
},
|
||||
{ label: "封面", value: drama.coverUrl },
|
||||
{ label: "简介", value: drama.description || "-" },
|
||||
]}
|
||||
/>
|
||||
)}
|
||||
</Card>
|
||||
|
||||
<Card
|
||||
className="section-card"
|
||||
title="剧集配置"
|
||||
extra={
|
||||
<Space>
|
||||
<InputNumber
|
||||
min={1}
|
||||
max={500}
|
||||
value={episodeCount}
|
||||
onChange={(value) => setEpisodeCount(Number(value) || 1)}
|
||||
/>
|
||||
<Button type="primary" onClick={configureEpisodes}>
|
||||
保存集数
|
||||
</Button>
|
||||
</Space>
|
||||
}
|
||||
>
|
||||
<Table
|
||||
rowKey="id"
|
||||
loading={loading}
|
||||
data={episodes}
|
||||
pagination={false}
|
||||
columns={[
|
||||
{ title: "集数", dataIndex: "episodeNo", width: 80 },
|
||||
{ title: "标题", dataIndex: "title" },
|
||||
{
|
||||
title: "BytePlus VID",
|
||||
dataIndex: "byteplusVid",
|
||||
width: 180,
|
||||
render: (value) => value || "-",
|
||||
},
|
||||
{
|
||||
title: "尺寸",
|
||||
width: 120,
|
||||
render: (_, record) =>
|
||||
record.width && record.height
|
||||
? `${record.width}x${record.height}`
|
||||
: "-",
|
||||
},
|
||||
{
|
||||
title: "时长",
|
||||
dataIndex: "durationSeconds",
|
||||
width: 100,
|
||||
render: (value) => (value ? `${value}s` : "-"),
|
||||
},
|
||||
{
|
||||
title: "审核状态",
|
||||
dataIndex: "reviewStatus",
|
||||
width: 120,
|
||||
render: (status) => (
|
||||
<Tag color={status === "approved" ? "green" : "orange"}>
|
||||
{REVIEW_STATUS_LABEL[String(status)] || String(status)}
|
||||
</Tag>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: "发布状态",
|
||||
dataIndex: "publishStatus",
|
||||
width: 120,
|
||||
render: (status) => (
|
||||
<Tag color={status === "published" ? "green" : "gray"}>
|
||||
{PUBLISH_STATUS_LABEL[String(status)] || String(status)}
|
||||
</Tag>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: "操作",
|
||||
width: 280,
|
||||
render: (_, record) => (
|
||||
<Space>
|
||||
<Button loading={uploadingEpisodeId === record.id}>
|
||||
<label>
|
||||
上传视频
|
||||
<input
|
||||
type="file"
|
||||
accept="video/*"
|
||||
style={{ display: "none" }}
|
||||
onChange={(event) => {
|
||||
const file = event.target.files?.[0];
|
||||
if (file) {
|
||||
void uploadEpisodeVideo(record, file);
|
||||
}
|
||||
event.target.value = "";
|
||||
}}
|
||||
/>
|
||||
</label>
|
||||
</Button>
|
||||
<Button
|
||||
type="text"
|
||||
disabled={record.reviewStatus !== "approved"}
|
||||
onClick={() => publishEpisode(record)}
|
||||
>
|
||||
发布
|
||||
</Button>
|
||||
</Space>
|
||||
),
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</Card>
|
||||
</div>
|
||||
);
|
||||
<Card
|
||||
className="section-card"
|
||||
title="剧集配置"
|
||||
extra={
|
||||
<Space>
|
||||
<InputNumber min={1} max={500} value={episodeCount} onChange={(value) => setEpisodeCount(Number(value) || 1)} />
|
||||
<Button type="primary" onClick={configureEpisodes}>
|
||||
保存集数
|
||||
</Button>
|
||||
</Space>
|
||||
}
|
||||
>
|
||||
<Table
|
||||
rowKey="id"
|
||||
loading={loading}
|
||||
data={episodes}
|
||||
pagination={false}
|
||||
columns={[
|
||||
{ title: "集数", dataIndex: "episodeNo", width: 80 },
|
||||
{ title: "标题", dataIndex: "title" },
|
||||
{
|
||||
title: "BytePlus VID",
|
||||
dataIndex: "byteplusVid",
|
||||
width: 180,
|
||||
render: (value) => value || "-",
|
||||
},
|
||||
{
|
||||
title: "尺寸",
|
||||
width: 120,
|
||||
render: (_, record) => (record.width && record.height ? `${record.width}x${record.height}` : "-"),
|
||||
},
|
||||
{
|
||||
title: "时长",
|
||||
dataIndex: "durationSeconds",
|
||||
width: 100,
|
||||
render: (value) => (value ? `${value}s` : "-"),
|
||||
},
|
||||
{
|
||||
title: "审核状态",
|
||||
dataIndex: "reviewStatus",
|
||||
width: 120,
|
||||
render: (status) => (
|
||||
<Tag color={status === "approved" ? "green" : "orange"}>{REVIEW_STATUS_LABEL[String(status)] || String(status)}</Tag>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: "发布状态",
|
||||
dataIndex: "publishStatus",
|
||||
width: 120,
|
||||
render: (status) => (
|
||||
<Tag color={status === "published" ? "green" : "gray"}>{PUBLISH_STATUS_LABEL[String(status)] || String(status)}</Tag>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: "操作",
|
||||
width: 280,
|
||||
render: (_, record) => (
|
||||
<Space>
|
||||
<Button loading={uploadingEpisodeId === record.id}>
|
||||
<label>
|
||||
上传视频
|
||||
<input
|
||||
type="file"
|
||||
accept="video/*"
|
||||
style={{ display: "none" }}
|
||||
onChange={(event) => {
|
||||
const file = event.target.files?.[0];
|
||||
if (file) {
|
||||
void uploadEpisodeVideo(record, file);
|
||||
}
|
||||
event.target.value = "";
|
||||
}}
|
||||
/>
|
||||
</label>
|
||||
</Button>
|
||||
<Button type="text" disabled={record.reviewStatus !== "approved"} onClick={() => publishEpisode(record)}>
|
||||
发布
|
||||
</Button>
|
||||
</Space>
|
||||
),
|
||||
},
|
||||
]}
|
||||
/>
|
||||
</Card>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
export default DramasPage;
|
||||
|
||||
Reference in New Issue
Block a user