Files
video-gen/video-gen-admin/src/pages/Adminplatform.tsx
T
2026-06-26 10:23:49 +08:00

463 lines
14 KiB
TypeScript

import React, { useEffect, useState } from 'react';
import {
Button, Card, Space, Table, Tag, Typography, message, Modal, Form, Input, InputNumber, Upload,
} from 'antd';
import {
HistoryOutlined, ReloadOutlined, PlusOutlined, UploadOutlined, EyeOutlined, EditOutlined, DeleteOutlined,
} from '@ant-design/icons';
import { getOpenTypeList, getOpenType, createOpenType, updateOpenType, deleteOpenType, uploadImage } from '../api';
import { formatDate } from '../utils/formatDate';
interface OpenType {
id: string;
open_type: number;
type_name: string;
description: string;
thumb?: string;
createdAt: string;
updatedAt: string;
}
const normalizeUploadError = (e: unknown): Error => {
if (e instanceof Error) {
return e;
}
if (typeof e === 'string') {
return new Error(e);
}
return new Error('上传失败');
};
const assertUploadFile = (file: unknown): File => {
if (file instanceof File) {
return file;
}
throw new Error('请选择有效图片文件');
};
const AdminPlatform: React.FC = () => {
const [openTypes, setOpenTypes] = useState<OpenType[]>([]);
const [total, setTotal] = useState(0);
const [loading, setLoading] = useState(false);
const [page, setPage] = useState(1);
const [pageSize, setPageSize] = useState(20);
const [createModalVisible, setCreateModalVisible] = useState(false);
const [detailModalVisible, setDetailModalVisible] = useState(false);
const [updateModalVisible, setUpdateModalVisible] = useState(false);
const [currentOpenType, setCurrentOpenType] = useState<OpenType | null>(null);
const [createForm] = Form.useForm();
const [updateForm] = Form.useForm();
const load = async (p?: number, ps?: number) => {
setLoading(true);
try {
const res = await getOpenTypeList({
page: p || page,
page_size: ps || pageSize,
});
setOpenTypes(res.items || []);
setTotal(res.total || 0);
} catch {
message.error('加载开户方式列表失败');
} finally {
setLoading(false);
}
};
useEffect(() => {
load();
}, []);
const handleCreate = async () => {
try {
const values = await createForm.validateFields();
console.log(values);
await createOpenType({
open_type: values.open_type,
type_name: values.type_name,
description: values.description,
thumb: values.thumb,
});
message.success('创建成功');
setCreateModalVisible(false);
createForm.resetFields();
load();
} catch (e: any) {
message.error(e?.message || '创建失败');
}
};
const handleDetail = async (id: string) => {
try {
const openType = await getOpenType(id);
setCurrentOpenType(openType);
setDetailModalVisible(true);
} catch (e: any) {
message.error(e?.message || '获取详情失败');
}
};
const handleUpdate = async (id: string) => {
try {
const openType = await getOpenType(id);
setCurrentOpenType(openType);
updateForm.setFieldsValue({
open_type: openType.open_type,
type_name: openType.type_name,
description: openType.description,
thumb: openType.thumb,
});
setUpdateModalVisible(true);
} catch (e: any) {
message.error(e?.message || '获取详情失败');
}
};
const handleSaveUpdate = async () => {
if (!currentOpenType) return;
try {
const values = await updateForm.validateFields();
await updateOpenType(currentOpenType.id, {
open_type: values.open_type,
type_name: values.type_name,
description: values.description,
thumb: values.thumb,
});
message.success('更新成功');
setUpdateModalVisible(false);
updateForm.resetFields();
setCurrentOpenType(null);
load();
} catch (e: any) {
message.error(e?.message || '更新失败');
}
};
const handleDelete = (id: string) => {
Modal.confirm({
title: '确认删除',
content: '确定要删除这个开户方式吗?',
okText: '删除',
okType: 'danger',
cancelText: '取消',
onOk: async () => {
try {
await deleteOpenType(id);
message.success('删除成功');
load();
} catch (e: any) {
message.error(e?.message || '删除失败');
}
},
});
};
const columns = [
{
title: 'ID',
dataIndex: 'id',
width: 100,
render: (v: string) => <Typography.Text>{v}</Typography.Text>,
},
{
title: '开户类型',
dataIndex: 'open_type',
width: 100,
render: (v: number) => <Typography.Text>{v}</Typography.Text>,
},
{
title: '类型名称',
dataIndex: 'type_name',
width: 150,
render: (v: string) => <Typography.Text strong>{v}</Typography.Text>,
},
{
title: '描述',
dataIndex: 'description',
width: 250,
ellipsis: true,
render: (v: string) => (
<Typography.Text type="secondary" style={{ fontSize: 12 }}>
{v}
</Typography.Text>
),
},
{
title: '缩略图',
dataIndex: 'thumb',
width: 120,
render: (v: string) => (
v ? <img src={v} alt="thumb" style={{ width: 80, height: 60, objectFit: 'cover' }} /> : '-'
),
},
{
title: '创建时间',
dataIndex: 'createdAt',
width: 160,
render: (v: string) => (
<Typography.Text type="secondary" style={{ fontSize: 12 }}>
{formatDate(v)}
</Typography.Text>
),
},
{
title: '更新时间',
dataIndex: 'updatedAt',
width: 160,
render: (v: string) => (
<Typography.Text type="secondary" style={{ fontSize: 12 }}>
{formatDate(v)}
</Typography.Text>
),
},
{
title: '操作',
width: 200,
render: (_: any, record: OpenType) => (
<Space>
<Button
icon={<EyeOutlined />}
size="small"
onClick={() => handleDetail(record.id)}
>
详情
</Button>
<Button
icon={<EditOutlined />}
size="small"
onClick={() => handleUpdate(record.id)}
>
更新
</Button>
<Button
icon={<DeleteOutlined />}
size="small"
danger
onClick={() => handleDelete(record.id)}
>
删除
</Button>
</Space>
),
},
];
return (
<div>
<Card variant="outlined" style={{ borderRadius: 12, border: '1px solid #f0f0f5' }}>
<div style={{ display: 'flex', justifyContent: 'space-between', marginBottom: 16 }}>
<Space>
<HistoryOutlined style={{ fontSize: 18, color: '#6366f1' }} />
<Typography.Text strong style={{ fontSize: 16 }}>平台开户方式管理</Typography.Text>
</Space>
<Space>
<Button type="primary" icon={<PlusOutlined />} onClick={() => setCreateModalVisible(true)}>
新增
</Button>
<Button icon={<ReloadOutlined />} onClick={() => load()}>
刷新
</Button>
</Space>
</div>
<Table
columns={columns}
dataSource={openTypes}
rowKey="id"
loading={loading}
pagination={{
current: page,
pageSize,
total,
showTotal: (t) => `共 ${t} 条记录`,
onChange: (p, ps) => {
setPage(p);
setPageSize(ps);
load(p, ps);
},
}}
scroll={{ x: 1000 }}
/>
</Card>
<Modal
title="新增开户方式"
open={createModalVisible}
onOk={handleCreate}
onCancel={() => {
setCreateModalVisible(false);
createForm.resetFields();
}}
okText="创建"
cancelText="取消"
width={600}
>
<Form form={createForm} layout="vertical">
<div style={{ display: 'flex', gap: 16 }}>
<Form.Item
name="open_type"
label="开户方式Id"
style={{ flex: 1 }}
rules={[{ required: true, message: '请输入开户方式Id' }]}
>
<InputNumber style={{ width: '100%' }} min={1} placeholder="请输入开户方式Id" />
</Form.Item>
<Form.Item
name="type_name"
label="类型名称"
style={{ flex: 1 }}
rules={[
{ required: true, message: '请输入类型名称' },
{ max: 100, message: '类型名称不能超过100个字符' },
]}
>
<Input style={{ width: '100%' }} placeholder="请输入类型名称" />
</Form.Item>
</div>
<Form.Item
name="description"
label="描述"
rules={[{ required: true, message: '请输入描述' }]}
>
<Input.TextArea placeholder="请输入描述" rows={4} />
</Form.Item>
<Form.Item
name="thumb"
label="缩略图"
>
<Upload
listType="picture-card"
customRequest={async ({ file, onSuccess, onError }) => {
try {
const uploadFile = assertUploadFile(file);
const res = await uploadImage(uploadFile);
console.log(res);
createForm.setFieldsValue({ thumb: res.url });
onSuccess?.({ url: res.url });
} catch (e) {
onError?.(normalizeUploadError(e));
}
}}
>
<div>
<UploadOutlined style={{ fontSize: 24, color: '#999' }} />
<div style={{ marginTop: 8 }}>上传图片</div>
</div>
</Upload>
</Form.Item>
</Form>
</Modal>
<Modal
title="开户方式详情"
open={detailModalVisible}
onCancel={() => {
setDetailModalVisible(false);
setCurrentOpenType(null);
}}
okText="关闭"
cancelText="取消"
width={600}
>
{currentOpenType && (
<div style={{ lineHeight: '2' }}>
<p><strong>ID:</strong> {currentOpenType.id}</p>
<p><strong>开户类型:</strong> {currentOpenType.open_type}</p>
<p><strong>类型名称:</strong> {currentOpenType.type_name}</p>
<p><strong>描述:</strong> {currentOpenType.description}</p>
<p>
<strong>缩略图:</strong>{' '}
{currentOpenType.thumb ? (
<img
src={currentOpenType.thumb}
alt="thumb"
style={{ width: 120, height: 80, objectFit: 'cover' }}
/>
) : '-'}
</p>
<p><strong>创建时间:</strong> {formatDate(currentOpenType.createdAt)}</p>
<p><strong>更新时间:</strong> {formatDate(currentOpenType.updatedAt)}</p>
</div>
)}
</Modal>
<Modal
title="更新开户方式"
open={updateModalVisible}
onOk={handleSaveUpdate}
onCancel={() => {
setUpdateModalVisible(false);
updateForm.resetFields();
setCurrentOpenType(null);
}}
okText="更新"
cancelText="取消"
width={600}
>
<Form form={updateForm} layout="vertical">
<Form.Item
name="open_type"
label="开户类型"
rules={[{ required: true, message: '请输入开户类型' }]}
>
<InputNumber min={1} placeholder="请输入开户类型编号" />
</Form.Item>
<Form.Item
name="type_name"
label="类型名称"
rules={[
{ required: true, message: '请输入类型名称' },
{ max: 100, message: '类型名称不能超过100个字符' },
]}
>
<Input placeholder="请输入类型名称" />
</Form.Item>
<Form.Item
name="description"
label="描述"
rules={[{ required: true, message: '请输入描述' }]}
>
<Input.TextArea placeholder="请输入描述" rows={4} />
</Form.Item>
<Form.Item
name="thumb"
label="缩略图"
>
<Upload
listType="picture-card"
defaultFileList={
currentOpenType?.thumb
? [{ uid: '1', name: 'thumb', status: 'done', url: currentOpenType.thumb }]
: []
}
customRequest={async ({ file, onSuccess, onError }) => {
try {
const uploadFile = assertUploadFile(file);
const res = await uploadImage(uploadFile);
updateForm.setFieldsValue({ thumb: res.url });
onSuccess?.({ url: res.url });
} catch (e) {
onError?.(normalizeUploadError(e));
}
}}
>
{!updateForm.getFieldValue('thumb') && (
<div>
<UploadOutlined style={{ fontSize: 24, color: '#999' }} />
<div style={{ marginTop: 8 }}>上传图片</div>
</div>
)}
</Upload>
</Form.Item>
</Form>
</Modal>
</div>
);
};
export default AdminPlatform;