feat: 完善短剧管理后台功能,新增编辑、详情展示优化

1.  重构接口类型定义,新增创建/更新短剧和剧集的载荷类型
2.  新增表单数据转换工具,规范化表单提交参数
3.  优化短剧列表展示,新增封面图和更清晰的集数统计
4.  新增短剧和剧集的编辑弹窗功能
5.  修复测试文件导入路径和README标题
6.  优化角色管理页面的表单提示样式
This commit is contained in:
2026-07-02 17:59:31 +08:00
parent a4a1891a7e
commit 96eaa810f3
10 changed files with 542 additions and 193 deletions

View File

@@ -4,6 +4,7 @@ import { Button, Card, Form, Grid, Input, InputNumber, InputTag, Message, Modal,
import { IconPlus } from "@arco-design/web-react/icon";
import { dramaApi } from "../../api";
import { usePageData } from "../../hooks/usePageData";
import { toDramaPayload } from "../formPayload";
import type { Drama, StatusOption } from "../interface";
const { Row, Col } = Grid;
@@ -31,7 +32,7 @@ export function DramaList() {
const createDrama = async (values: Partial<Drama>) => {
setCreating(true);
try {
await dramaApi.createDrama(values);
await dramaApi.createDrama(toDramaPayload(values));
Message.success("短剧创建成功");
setVisible(false);
await reload();
@@ -41,124 +42,128 @@ export function DramaList() {
};
return (
<Card
className="section-card"
title="短剧管理"
extra={
<div className="">
<nav className="flex justify-end border border border-[#e5e5e5] pb-4">
<Button type="primary" icon={<IconPlus />} onClick={() => setVisible(true)}>
</Button>
}
>
<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}`)}>
</nav>
<Card className="section-card" title="短剧管理">
<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: "coverUrl", width: 120, render: (value) => (value ? <img src={value} alt="封面图" style={{ width: 100, height: 100, objectFit: "cover" }} /> : null) },
{ title: "标题", dataIndex: "title" },
{ title: "地区", dataIndex: "region", width: 120 },
{ title: "类型", dataIndex: "type", width: 140 },
{ title: "已发布 / 总集数", dataIndex: "totalEpisodes", width: 180, render: (_, record) => `${record.publishedEpisodes}(已发布)/共${record.totalEpisodes}` },
{
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, totalEpisodes: 1 }}>
<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={8}>
<Form.Item label="排序权重" field="sortOrder">
<InputNumber placeholder="数值越大越靠前" style={{ width: "100%" }} />
</Form.Item>
</Col>
<Col span={8}>
<Form.Item label="剧集总数" field="totalEpisodes" rules={[{ required: true }]}>
<InputNumber min={1} max={500} placeholder="创建剧集占位" style={{ width: "100%" }} />
</Form.Item>
</Col>
<Col span={8}>
<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>
),
},
]}
/>
<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>
</div>
</Form>
</Modal>
</Card>
</div>
);
}