本次提交完成了以下核心变更: 1. 新增完整的频道管理模块,包括频道增删改查API、页面组件与权限配置 2. 为短剧功能添加频道关联字段,支持将短剧绑定到指定频道 3. 全局UI优化:统一使用Panel组件重构页面布局,统一样式规范与交互细节 4. 表格与日志组件优化:新增请求方法颜色标记、耗时高亮、状态码分级展示 5. 登录页、个人中心、布局等页面的视觉升级与交互优化 6. 完善全局TailwindCSS样式与组件样式自定义
180 lines
7.9 KiB
TypeScript
180 lines
7.9 KiB
TypeScript
import { useState } from "react";
|
|
import { Button, Form, Input, InputNumber, Message, Modal, Select, Space, Switch, Table, Tag } from "@arco-design/web-react";
|
|
import { IconDelete, IconEdit, IconPlus } from "@arco-design/web-react/icon";
|
|
import { channelApi } from "../api";
|
|
import type { Channel, ChannelQuery, CreateChannelPayload } from "../api";
|
|
import { Panel } from "../components/Panel";
|
|
import { usePageData } from "../hooks/usePageData";
|
|
|
|
const INITIAL_QUERY: ChannelQuery = { page: 1, pageSize: 20 };
|
|
|
|
const CHANNEL_STATUS_LABEL: Record<string, string> = {
|
|
active: "启用",
|
|
disabled: "停用",
|
|
};
|
|
|
|
type ChannelFormValues = CreateChannelPayload;
|
|
|
|
export default function ChannelPage() {
|
|
const { data, loading, reload, changePage } = usePageData<Channel, ChannelQuery>(channelApi.listChannels, INITIAL_QUERY);
|
|
const [createVisible, setCreateVisible] = useState(false);
|
|
const [editingChannel, setEditingChannel] = useState<Channel | null>(null);
|
|
const [saving, setSaving] = useState(false);
|
|
|
|
const createChannel = async (values: ChannelFormValues) => {
|
|
setSaving(true);
|
|
try {
|
|
await channelApi.createChannel({
|
|
...values,
|
|
sortOrder: Number(values.sortOrder) || 0,
|
|
isDefault: Boolean(values.isDefault),
|
|
status: values.status || "active",
|
|
});
|
|
Message.success("频道创建成功");
|
|
setCreateVisible(false);
|
|
await reload();
|
|
} finally {
|
|
setSaving(false);
|
|
}
|
|
};
|
|
|
|
const updateChannel = async (values: ChannelFormValues) => {
|
|
if (!editingChannel) {
|
|
return;
|
|
}
|
|
setSaving(true);
|
|
try {
|
|
await channelApi.updateChannel(editingChannel.id, {
|
|
...values,
|
|
sortOrder: Number(values.sortOrder) || 0,
|
|
isDefault: Boolean(values.isDefault),
|
|
status: values.status || "active",
|
|
});
|
|
Message.success("频道已更新");
|
|
setEditingChannel(null);
|
|
await reload();
|
|
} finally {
|
|
setSaving(false);
|
|
}
|
|
};
|
|
|
|
const deleteChannel = (channel: Channel) => {
|
|
Modal.confirm({
|
|
title: "删除频道",
|
|
content: `确认删除频道「${channel.name}」?`,
|
|
okButtonProps: { status: "danger" },
|
|
onOk: async () => {
|
|
await channelApi.deleteChannel(channel.id);
|
|
Message.success("频道已删除");
|
|
await reload();
|
|
},
|
|
});
|
|
};
|
|
|
|
const renderForm = (onSubmit: (values: ChannelFormValues) => Promise<void>, initialValues: ChannelFormValues) => (
|
|
<Form layout="vertical" initialValues={initialValues} onSubmit={onSubmit}>
|
|
<Form.Item label="频道名称" field="name" rules={[{ required: true }]}>
|
|
<Input placeholder="请输入频道名称" />
|
|
</Form.Item>
|
|
<Form.Item label="频道编码" field="code" rules={[{ required: true }]}>
|
|
<Input placeholder="请输入频道编码,如 default" disabled={Boolean(editingChannel?.isDefault)} />
|
|
</Form.Item>
|
|
<Form.Item label="描述" field="description">
|
|
<Input.TextArea placeholder="请输入频道描述" autoSize={{ minRows: 2, maxRows: 4 }} />
|
|
</Form.Item>
|
|
<Space size="large" align="start">
|
|
<Form.Item label="排序权重" field="sortOrder">
|
|
<InputNumber min={0} style={{ width: 180 }} />
|
|
</Form.Item>
|
|
<Form.Item label="状态" field="status">
|
|
<Select style={{ width: 180 }} disabled={Boolean(editingChannel?.isDefault)}>
|
|
<Select.Option value="active">启用</Select.Option>
|
|
<Select.Option value="disabled">停用</Select.Option>
|
|
</Select>
|
|
</Form.Item>
|
|
<Form.Item label="默认频道" field="isDefault" triggerPropName="checked">
|
|
<Switch disabled={Boolean(editingChannel?.isDefault)} />
|
|
</Form.Item>
|
|
</Space>
|
|
<div className="modal-actions">
|
|
<Button onClick={() => {
|
|
setCreateVisible(false);
|
|
setEditingChannel(null);
|
|
}}>
|
|
取消
|
|
</Button>
|
|
<Button type="primary" htmlType="submit" loading={saving}>
|
|
保存
|
|
</Button>
|
|
</div>
|
|
</Form>
|
|
);
|
|
|
|
return (
|
|
<div className="page-stack">
|
|
<Panel title="频道管理" extra={<Button type="primary" icon={<IconPlus />} onClick={() => setCreateVisible(true)}>新增频道</Button>} noPadding>
|
|
<Table
|
|
rowKey="id"
|
|
loading={loading}
|
|
data={data.list}
|
|
pagination={{
|
|
current: data.pagination.page,
|
|
pageSize: data.pagination.pageSize,
|
|
total: data.pagination.total,
|
|
sizeCanChange: true,
|
|
showTotal: true,
|
|
onChange: (page, pageSize) => changePage(page, pageSize),
|
|
}}
|
|
columns={[
|
|
{ title: "ID", dataIndex: "id", width: 80 },
|
|
{ title: "名称", dataIndex: "name" },
|
|
{ title: "编码", dataIndex: "code", width: 180 },
|
|
{ title: "排序", dataIndex: "sortOrder", width: 100 },
|
|
{
|
|
title: "默认",
|
|
dataIndex: "isDefault",
|
|
width: 100,
|
|
render: (value) => (value ? <Tag color="arcoblue">是</Tag> : <Tag>否</Tag>),
|
|
},
|
|
{
|
|
title: "状态",
|
|
dataIndex: "status",
|
|
width: 100,
|
|
render: (status) => <Tag color={status === "active" ? "green" : "gray"}>{CHANNEL_STATUS_LABEL[String(status)] || String(status)}</Tag>,
|
|
},
|
|
{
|
|
title: "操作",
|
|
width: 180,
|
|
render: (_, record) => (
|
|
<Space>
|
|
<Button type="text" icon={<IconEdit />} onClick={() => setEditingChannel(record)}>
|
|
编辑
|
|
</Button>
|
|
<Button type="text" status="danger" icon={<IconDelete />} disabled={record.isDefault} onClick={() => deleteChannel(record)}>
|
|
删除
|
|
</Button>
|
|
</Space>
|
|
),
|
|
},
|
|
]}
|
|
/>
|
|
</Panel>
|
|
|
|
<Modal title="新增频道" visible={createVisible} onCancel={() => setCreateVisible(false)} footer={null} style={{ width: 640 }}>
|
|
{renderForm(createChannel, { sortOrder: 0, status: "active", isDefault: false })}
|
|
</Modal>
|
|
|
|
<Modal title="编辑频道" visible={Boolean(editingChannel)} onCancel={() => setEditingChannel(null)} footer={null} style={{ width: 640 }}>
|
|
{editingChannel && renderForm(updateChannel, {
|
|
name: editingChannel.name,
|
|
code: editingChannel.code,
|
|
description: editingChannel.description,
|
|
sortOrder: editingChannel.sortOrder,
|
|
status: editingChannel.status,
|
|
isDefault: editingChannel.isDefault,
|
|
})}
|
|
</Modal>
|
|
</div>
|
|
);
|
|
}
|