refactor(Drama): 抽离表单字段组件统一复用
将新增/编辑短剧的表单字段抽离为DramaFormFields组件,统一维护表单UI逻辑,修复编辑弹窗宽度,优化列表页展示样式与交互
This commit is contained in:
@@ -1,14 +1,13 @@
|
|||||||
import { useEffect, useState } from "react";
|
import { useEffect, useState } from "react";
|
||||||
import { useNavigate } from "react-router-dom";
|
import { useNavigate } from "react-router-dom";
|
||||||
import { Button, Descriptions, Form, Grid, Input, InputNumber, InputTag, Message, Modal, Select, Space, Switch } from "@arco-design/web-react";
|
import { Button, Descriptions, Form, InputNumber, Message, Modal, Space } from "@arco-design/web-react";
|
||||||
import { IconArrowLeft, IconRefresh } from "@arco-design/web-react/icon";
|
import { IconArrowLeft, IconRefresh } from "@arco-design/web-react/icon";
|
||||||
import { channelApi, dramaApi, mediaApi } from "../../api";
|
import { channelApi, dramaApi, mediaApi } from "../../api";
|
||||||
import { Panel } from "../../components/Panel";
|
import { Panel } from "../../components/Panel";
|
||||||
import { toDramaPayload, toEpisodePayload } from "../formPayload";
|
import { toDramaPayload, toEpisodePayload } from "../formPayload";
|
||||||
import type { Channel, Drama, DramaDetailPageProps, Episode } from "../interface";
|
import type { Channel, Drama, DramaDetailPageProps, Episode } from "../interface";
|
||||||
import { EpisodeTable } from "./EpisodeTable";
|
import { EpisodeTable } from "./EpisodeTable";
|
||||||
|
import { DramaFormFields } from "./DramaFormFields";
|
||||||
const { Row, Col } = Grid;
|
|
||||||
|
|
||||||
const STATUS_OPTIONS = [
|
const STATUS_OPTIONS = [
|
||||||
{ value: "draft", label: "草稿" },
|
{ value: "draft", label: "草稿" },
|
||||||
@@ -182,7 +181,7 @@ export function DramaDetail({ dramaId }: DramaDetailPageProps) {
|
|||||||
</div>
|
</div>
|
||||||
|
|
||||||
{drama && (
|
{drama && (
|
||||||
<Modal title="编辑短剧" visible={editVisible} onCancel={() => setEditVisible(false)} footer={null} style={{ width: "45rem" }}>
|
<Modal title="编辑短剧" visible={editVisible} onCancel={() => setEditVisible(false)} footer={null} style={{ width: "50rem" }}>
|
||||||
<Form
|
<Form
|
||||||
key={drama.id}
|
key={drama.id}
|
||||||
layout="vertical"
|
layout="vertical"
|
||||||
@@ -202,76 +201,7 @@ export function DramaDetail({ dramaId }: DramaDetailPageProps) {
|
|||||||
channelId: drama.channelId,
|
channelId: drama.channelId,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Form.Item label="标题" field="title" rules={[{ required: true }]}>
|
<DramaFormFields channels={channels} coverUrl={drama.coverUrl} statusOptions={STATUS_OPTIONS} />
|
||||||
<Input placeholder="请输入短剧标题" />
|
|
||||||
</Form.Item>
|
|
||||||
<Row gutter={16}>
|
|
||||||
<Col span={8}>
|
|
||||||
<Form.Item label="频道" field="channelId">
|
|
||||||
<Select placeholder="默认频道" allowClear>
|
|
||||||
{channels.map((channel) => (
|
|
||||||
<Select.Option key={channel.id} value={channel.id}>
|
|
||||||
{channel.name}
|
|
||||||
</Select.Option>
|
|
||||||
))}
|
|
||||||
</Select>
|
|
||||||
</Form.Item>
|
|
||||||
</Col>
|
|
||||||
<Col span={8}>
|
|
||||||
<Form.Item label="状态" field="status">
|
|
||||||
<Select>
|
|
||||||
{STATUS_OPTIONS.map((option) => (
|
|
||||||
<Select.Option key={option.value} value={option.value}>
|
|
||||||
{option.label}
|
|
||||||
</Select.Option>
|
|
||||||
))}
|
|
||||||
</Select>
|
|
||||||
</Form.Item>
|
|
||||||
</Col>
|
|
||||||
<Col span={8}>
|
|
||||||
<Form.Item label="类型" field="type">
|
|
||||||
<Input placeholder="请输入类型" />
|
|
||||||
</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 min={1900} style={{ width: "100%" }} />
|
|
||||||
</Form.Item>
|
|
||||||
</Col>
|
|
||||||
</Row>
|
|
||||||
<Row gutter={16}>
|
|
||||||
<Col span={12}>
|
|
||||||
<Form.Item label="排序权重" field="sortOrder">
|
|
||||||
<InputNumber 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">
|
<div className="modal-actions">
|
||||||
<Button onClick={() => setEditVisible(false)}>取消</Button>
|
<Button onClick={() => setEditVisible(false)}>取消</Button>
|
||||||
<Button type="primary" htmlType="submit" loading={updatingDrama}>
|
<Button type="primary" htmlType="submit" loading={updatingDrama}>
|
||||||
|
|||||||
114
src/Drama/components/DramaFormFields.tsx
Normal file
114
src/Drama/components/DramaFormFields.tsx
Normal file
@@ -0,0 +1,114 @@
|
|||||||
|
import { Form, Grid, Input, InputNumber, InputTag, Select, Switch } from "@arco-design/web-react";
|
||||||
|
import { IconPlus } from "@arco-design/web-react/icon";
|
||||||
|
import type { Channel, StatusOption } from "../interface";
|
||||||
|
|
||||||
|
const { Row, Col } = Grid;
|
||||||
|
|
||||||
|
type DramaFormFieldsProps = {
|
||||||
|
channels: Channel[];
|
||||||
|
coverUrl?: string;
|
||||||
|
statusOptions: StatusOption[];
|
||||||
|
showTotalEpisodes?: boolean;
|
||||||
|
};
|
||||||
|
|
||||||
|
export function DramaFormFields({ channels, coverUrl, statusOptions, showTotalEpisodes = false }: DramaFormFieldsProps) {
|
||||||
|
return (
|
||||||
|
<div className="grid grid-cols-1 gap-8 md:grid-cols-[12rem_minmax(0,1fr)]">
|
||||||
|
<div className="flex flex-col gap-4">
|
||||||
|
<div className="relative flex aspect-[3/4] min-h-[16rem] items-center justify-center overflow-hidden rounded-lg border border-dashed border-line bg-canvas/40 text-muted">
|
||||||
|
{coverUrl ? (
|
||||||
|
<>
|
||||||
|
<img src={coverUrl} alt="短剧封面" className="h-full w-full object-cover" />
|
||||||
|
<span className="absolute bottom-2 left-2 rounded bg-black/55 px-2 py-1 text-xs text-white">当前封面</span>
|
||||||
|
</>
|
||||||
|
) : (
|
||||||
|
<div className="flex flex-col items-center gap-2">
|
||||||
|
<IconPlus className="text-3xl text-brand" />
|
||||||
|
<span className="text-sm">上传竖封面</span>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</div>
|
||||||
|
<Form.Item label="封面地址" field="coverUrl" rules={[{ required: true }]}>
|
||||||
|
<Input.TextArea autoSize={{ minRows: 5, maxRows: 5 }} placeholder="请输入封面图片 URL" />
|
||||||
|
</Form.Item>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div className="min-w-0">
|
||||||
|
<Form.Item label="标题" field="title" rules={[{ required: true }]}>
|
||||||
|
<Input placeholder="请输入短剧标题" />
|
||||||
|
</Form.Item>
|
||||||
|
<Row gutter={16}>
|
||||||
|
<Col span={8}>
|
||||||
|
<Form.Item label="频道" field="channelId">
|
||||||
|
<Select placeholder="默认频道" allowClear>
|
||||||
|
{channels.map((channel) => (
|
||||||
|
<Select.Option key={channel.id} value={channel.id}>
|
||||||
|
{channel.name}
|
||||||
|
</Select.Option>
|
||||||
|
))}
|
||||||
|
</Select>
|
||||||
|
</Form.Item>
|
||||||
|
</Col>
|
||||||
|
<Col span={8}>
|
||||||
|
<Form.Item label="状态" field="status">
|
||||||
|
<Select placeholder="请选择状态">
|
||||||
|
{statusOptions.map((option) => (
|
||||||
|
<Select.Option key={option.value} value={option.value}>
|
||||||
|
{option.label}
|
||||||
|
</Select.Option>
|
||||||
|
))}
|
||||||
|
</Select>
|
||||||
|
</Form.Item>
|
||||||
|
</Col>
|
||||||
|
<Col span={8}>
|
||||||
|
<Form.Item label="类型" field="type">
|
||||||
|
<Input placeholder="请输入类型" />
|
||||||
|
</Form.Item>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
<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={showTotalEpisodes ? 8 : 12}>
|
||||||
|
<Form.Item label="排序权重" field="sortOrder">
|
||||||
|
<InputNumber placeholder="数值越大越靠前" style={{ width: "100%" }} />
|
||||||
|
</Form.Item>
|
||||||
|
</Col>
|
||||||
|
{showTotalEpisodes && (
|
||||||
|
<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={showTotalEpisodes ? 8 : 12}>
|
||||||
|
<Form.Item label="推荐" field="isRecommended" triggerPropName="checked">
|
||||||
|
<Switch />
|
||||||
|
</Form.Item>
|
||||||
|
</Col>
|
||||||
|
</Row>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
@@ -1,13 +1,14 @@
|
|||||||
import { useEffect, useMemo, useState } from "react";
|
import { useEffect, useMemo, useState } from "react";
|
||||||
import { useNavigate } from "react-router-dom";
|
import { useNavigate } from "react-router-dom";
|
||||||
import { Button, Form, Grid, Input, InputNumber, InputTag, Message, Modal, Select, Space, Switch, Table, Tag } from "@arco-design/web-react";
|
import { Button, Form, Grid, Input, Message, Modal, Select, Space, Table } from "@arco-design/web-react";
|
||||||
import { IconDelete, IconPlus, IconSearch, IconRefresh } from "@arco-design/web-react/icon";
|
import { IconDelete, IconEdit, IconPlus, IconSearch, IconRefresh, IconStarFill } from "@arco-design/web-react/icon";
|
||||||
import { channelApi, dramaApi } from "../../api";
|
import { channelApi, dramaApi } from "../../api";
|
||||||
import type { DramaQuery } from "../../api/interface";
|
import type { DramaQuery } from "../../api/interface";
|
||||||
import { usePageData } from "../../hooks/usePageData";
|
import { usePageData } from "../../hooks/usePageData";
|
||||||
import { Panel } from "../../components/Panel";
|
import { Panel } from "../../components/Panel";
|
||||||
import { toDramaPayload } from "../formPayload";
|
import { toDramaPayload } from "../formPayload";
|
||||||
import type { Channel, Drama, StatusOption } from "../interface";
|
import type { Channel, Drama, StatusOption } from "../interface";
|
||||||
|
import { DramaFormFields } from "./DramaFormFields";
|
||||||
|
|
||||||
const { Row, Col } = Grid;
|
const { Row, Col } = Grid;
|
||||||
|
|
||||||
@@ -27,6 +28,57 @@ const PUBLISH_STATUS_LABEL: Record<string, string> = {
|
|||||||
|
|
||||||
const INITIAL_QUERY: DramaQuery = { page: 1, pageSize: 20 };
|
const INITIAL_QUERY: DramaQuery = { page: 1, pageSize: 20 };
|
||||||
|
|
||||||
|
function EpisodeProgress({ published = 0, total = 0 }: { published?: number; total?: number }) {
|
||||||
|
const percent = total > 0 ? Math.min(100, Math.max(0, (published / total) * 100)) : 0;
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div className="w-[8.75rem]">
|
||||||
|
<div className="mb-3 leading-none text-ink">
|
||||||
|
{published} / {total}
|
||||||
|
</div>
|
||||||
|
<div className="h-2 w-full overflow-hidden rounded-full bg-[#eef1f5]">
|
||||||
|
<div className="h-full rounded-full bg-[#12aaa2]" style={{ width: `${percent}%` }} />
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function StatusPill({ status }: { status: string }) {
|
||||||
|
const colorClass =
|
||||||
|
{
|
||||||
|
published: "bg-[#e6f8ee] text-[#249455]",
|
||||||
|
draft: "bg-[#fff0e6] text-[#d8781f]",
|
||||||
|
scheduled: "bg-[#e8f3ff] text-[#2673dd]",
|
||||||
|
offline: "bg-[#f0f2f5] text-[#697586]",
|
||||||
|
}[status] ?? "bg-[#f0f2f5] text-[#697586]";
|
||||||
|
|
||||||
|
return <span className={`inline-flex min-w-[3.9rem] items-center justify-center rounded-md px-3 py-2 font-semibold ${colorClass}`}>{PUBLISH_STATUS_LABEL[status] || status}</span>;
|
||||||
|
}
|
||||||
|
|
||||||
|
function RecommendationPill({ recommended }: { recommended: boolean }) {
|
||||||
|
return (
|
||||||
|
<span className={`inline-flex min-w-[3.9rem] items-center justify-center gap-2 rounded-md px-3 py-2 font-semibold ${recommended ? "bg-[#fff5df] text-[#d58a18]" : "bg-[#f3f5f8] text-[#343a46]"}`}>
|
||||||
|
<IconStarFill className={recommended ? "text-[#ffad14]" : "text-[#a4adba]"} />
|
||||||
|
{recommended ? "是" : "否"}
|
||||||
|
</span>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
function ActionTextButton({ tone, icon, children, onClick }: { tone: "primary" | "danger"; icon: React.ReactNode; children: React.ReactNode; onClick: () => void }) {
|
||||||
|
const colorClass = tone === "primary" ? "text-[#1f6feb] hover:text-[#1457d9]" : "text-[#ff3b3b] hover:text-[#d92d2d]";
|
||||||
|
|
||||||
|
return (
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
className={`inline-flex items-center gap-2 whitespace-nowrap border-0 bg-transparent p-0 font-semibold leading-none transition-colors ${colorClass}`}
|
||||||
|
onClick={onClick}
|
||||||
|
>
|
||||||
|
<span className="text-[1.25rem] leading-none">{icon}</span>
|
||||||
|
{children}
|
||||||
|
</button>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
export function DramaList() {
|
export function DramaList() {
|
||||||
const navigate = useNavigate();
|
const navigate = useNavigate();
|
||||||
const { data, loading, reload, changePage, setFilter } = usePageData<Drama, DramaQuery>(dramaApi.listDramas, INITIAL_QUERY);
|
const { data, loading, reload, changePage, setFilter } = usePageData<Drama, DramaQuery>(dramaApi.listDramas, INITIAL_QUERY);
|
||||||
@@ -39,7 +91,10 @@ export function DramaList() {
|
|||||||
const defaultChannel = useMemo(() => channels.find((channel) => channel.isDefault) ?? channels[0], [channels]);
|
const defaultChannel = useMemo(() => channels.find((channel) => channel.isDefault) ?? channels[0], [channels]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
channelApi.listChannels({ page: 1, pageSize: 100 }).then((page) => setChannels(page.list)).catch(() => setChannels([]));
|
channelApi
|
||||||
|
.listChannels({ page: 1, pageSize: 100 })
|
||||||
|
.then((page) => setChannels(page.list))
|
||||||
|
.catch(() => setChannels([]));
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
const handleSearch = () => {
|
const handleSearch = () => {
|
||||||
@@ -80,7 +135,14 @@ export function DramaList() {
|
|||||||
|
|
||||||
return (
|
return (
|
||||||
<div className="page-stack">
|
<div className="page-stack">
|
||||||
<Panel title="短剧管理" extra={<Button type="primary" icon={<IconPlus />} onClick={() => setVisible(true)}>新增短剧</Button>}>
|
<Panel
|
||||||
|
title="短剧管理"
|
||||||
|
extra={
|
||||||
|
<Button type="primary" icon={<IconPlus />} onClick={() => setVisible(true)}>
|
||||||
|
新增短剧
|
||||||
|
</Button>
|
||||||
|
}
|
||||||
|
>
|
||||||
<Row gutter={16} align="center">
|
<Row gutter={16} align="center">
|
||||||
<Col span={6}>
|
<Col span={6}>
|
||||||
<div className="form-label">短剧标题</div>
|
<div className="form-label">短剧标题</div>
|
||||||
@@ -133,30 +195,35 @@ export function DramaList() {
|
|||||||
{ title: "标题", dataIndex: "title" },
|
{ title: "标题", dataIndex: "title" },
|
||||||
{ title: "地区", dataIndex: "region", width: 120 },
|
{ title: "地区", dataIndex: "region", width: 120 },
|
||||||
{ title: "类型", dataIndex: "type", width: 140 },
|
{ title: "类型", dataIndex: "type", width: 140 },
|
||||||
{ title: "已发布 / 总集数", dataIndex: "totalEpisodes", width: 180, render: (_, record) => `${record.publishedEpisodes || 0}(已发布)/共${record.totalEpisodes || 0}集` },
|
{
|
||||||
|
title: "已发布 / 总集数",
|
||||||
|
dataIndex: "totalEpisodes",
|
||||||
|
width: 220,
|
||||||
|
render: (_, record) => <EpisodeProgress published={record.publishedEpisodes || 0} total={record.totalEpisodes || 0} />,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
title: "状态",
|
title: "状态",
|
||||||
dataIndex: "status",
|
dataIndex: "status",
|
||||||
width: 120,
|
width: 150,
|
||||||
render: (status) => <Tag color={status === "published" ? "green" : "gray"}>{PUBLISH_STATUS_LABEL[String(status)] || String(status)}</Tag>,
|
render: (status) => <StatusPill status={String(status)} />,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "推荐",
|
title: "推荐",
|
||||||
dataIndex: "isRecommended",
|
dataIndex: "isRecommended",
|
||||||
width: 100,
|
width: 130,
|
||||||
render: (value) => (value ? <Tag color="arcoblue">是</Tag> : <Tag>否</Tag>),
|
render: (value) => <RecommendationPill recommended={Boolean(value)} />,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: "操作",
|
title: "操作",
|
||||||
width: 180,
|
width: 220,
|
||||||
render: (_, record) => (
|
render: (_, record) => (
|
||||||
<Space>
|
<Space size={24}>
|
||||||
<Button type="text" onClick={() => navigate(`/dramas/${record.id}`)}>
|
<ActionTextButton tone="primary" icon={<IconEdit />} onClick={() => navigate(`/dramas/${record.id}`)}>
|
||||||
详情
|
详情
|
||||||
</Button>
|
</ActionTextButton>
|
||||||
<Button type="text" status="danger" icon={<IconDelete />} onClick={() => deleteDrama(record)}>
|
<ActionTextButton tone="danger" icon={<IconDelete />} onClick={() => deleteDrama(record)}>
|
||||||
删除
|
删除
|
||||||
</Button>
|
</ActionTextButton>
|
||||||
</Space>
|
</Space>
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
@@ -164,83 +231,9 @@ export function DramaList() {
|
|||||||
/>
|
/>
|
||||||
</Panel>
|
</Panel>
|
||||||
|
|
||||||
<Modal title="创建短剧" visible={visible} onCancel={() => setVisible(false)} footer={null} style={{ width: "40rem" }}>
|
<Modal title="创建短剧" visible={visible} onCancel={() => setVisible(false)} footer={null} style={{ width: "56rem" }}>
|
||||||
<Form layout="vertical" onSubmit={createDrama} initialValues={{ status: "draft", isRecommended: false, sortOrder: 0, totalEpisodes: 1, channelId: defaultChannel?.id }}>
|
<Form layout="vertical" onSubmit={createDrama} initialValues={{ status: "draft", isRecommended: false, sortOrder: 0, totalEpisodes: 1, channelId: defaultChannel?.id }}>
|
||||||
<Form.Item label="标题" field="title" rules={[{ required: true }]}>
|
<DramaFormFields channels={channels} statusOptions={STATUS_OPTIONS} showTotalEpisodes />
|
||||||
<Input placeholder="请输入短剧标题" />
|
|
||||||
</Form.Item>
|
|
||||||
<Row gutter={16}>
|
|
||||||
<Col span={8}>
|
|
||||||
<Form.Item label="频道" field="channelId">
|
|
||||||
<Select placeholder="默认频道" allowClear>
|
|
||||||
{channels.map((channel) => (
|
|
||||||
<Select.Option key={channel.id} value={channel.id}>
|
|
||||||
{channel.name}
|
|
||||||
</Select.Option>
|
|
||||||
))}
|
|
||||||
</Select>
|
|
||||||
</Form.Item>
|
|
||||||
</Col>
|
|
||||||
<Col span={8}>
|
|
||||||
<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>
|
|
||||||
<Col span={8}>
|
|
||||||
<Form.Item label="类型" field="type">
|
|
||||||
<Input placeholder="请输入类型,如复仇、爱情、动作等" />
|
|
||||||
</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">
|
<div className="modal-actions">
|
||||||
<Button onClick={() => setVisible(false)}>取消</Button>
|
<Button onClick={() => setVisible(false)}>取消</Button>
|
||||||
<Button type="primary" htmlType="submit" loading={creating}>
|
<Button type="primary" htmlType="submit" loading={creating}>
|
||||||
|
|||||||
Reference in New Issue
Block a user