forked from yoone/WEB
1
0
Fork 0
WEB/src/pages/Site/Shop/Media/index.tsx

417 lines
12 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { siteapicontrollerCreatemedia } from '@/servers/api/siteApi';
import { DeleteOutlined, EditOutlined, PlusOutlined } from '@ant-design/icons';
import {
ModalForm,
PageContainer,
ProColumns,
ProFormText,
ProFormUploadButton,
ProTable,
} from '@ant-design/pro-components';
import { request, useParams } from '@umijs/max';
import { App, Button, Image, Popconfirm, Space } from 'antd';
import React, { useState } from 'react';
const MediaPage: React.FC = () => {
const { message } = App.useApp();
const { siteId } = useParams<{ siteId: string }>();
const [editing, setEditing] = useState<any>(null);
const [selectedRowKeys, setSelectedRowKeys] = useState<React.Key[]>([]);
const actionRef = React.useRef<any>(null);
React.useEffect(() => {
actionRef.current?.reload();
}, [siteId]);
const handleDelete = async (id: number) => {
if (!siteId) return;
try {
const res = await request(`/site-api/${siteId}/media/${id}`, {
method: 'DELETE',
});
if (res.success) {
message.success('删除成功');
actionRef.current?.reload();
} else {
message.error(res.message || '删除失败');
}
} catch (error: any) {
message.error(error.message || '删除失败');
}
};
const handleUpdate = async (id: number, data: any) => {
if (!siteId) return false;
try {
const res = await request(`/site-api/${siteId}/media/${id}`, {
method: 'PUT',
data,
});
if (res.success) {
message.success('更新成功');
actionRef.current?.reload();
return true;
} else {
message.error(res.message || '更新失败');
return false;
}
} catch (error: any) {
message.error(error.message || '更新失败');
return false;
}
};
const columns: ProColumns<any>[] = [
{
title: 'ID',
dataIndex: 'id',
hideInSearch: true,
width: 120,
copyable: true,
render: (_, record) => {
return record?.id ?? '-';
},
},
{
title: '展示',
dataIndex: 'source_url',
hideInSearch: true,
render: (_, record) => (
<Image
src={record.source_url}
style={{
width: 60,
height: 60,
objectFit: 'contain',
background: '#f0f0f0',
}}
fallback="https://via.placeholder.com/60?text=No+Img"
/>
),
},
{
title: '名称',
dataIndex: 'title',
copyable: true,
ellipsis: true,
width: 200,
},
{
title: '地址',
dataIndex: 'source_url',
copyable: true,
ellipsis: true,
hideInSearch: true,
},
{
title: '媒体类型',
dataIndex: 'media_type',
width: 120,
},
{
title: 'MIME类型',
dataIndex: 'mime_type',
width: 120,
},
{
// 文件大小列
title: '文件大小',
dataIndex: 'file_size',
hideInSearch: true,
width: 120,
render: (_: any, record: any) => {
// 获取文件大小
const fileSize = record.file_size;
// 如果文件大小不存在,则直接返回-
if (!fileSize) {
return '-';
}
// 如果文件大小小于1024,则单位为B
if (fileSize < 1024) {
return `${fileSize} B`;
// 如果文件大小小于1024*1024,则单位为KB
} else if (fileSize < 1024 * 1024) {
return `${(fileSize / 1024).toFixed(2)} KB`;
// 否则单位为MB
} else {
return `${(fileSize / (1024 * 1024)).toFixed(2)} MB`;
}
},
},
{
title: '创建时间',
dataIndex: 'date_created',
valueType: 'dateTime',
hideInSearch: true,
},
{
title: '操作',
valueType: 'option',
width: 160,
fixed: 'right',
render: (_, record) => (
<Space>
<Button
type="link"
title="编辑"
icon={<EditOutlined />}
onClick={() => {
setEditing(record);
}}
>
</Button>
<Popconfirm
title="确定删除吗?"
onConfirm={() => handleDelete(record.id)}
okText="确定"
cancelText="取消"
>
<Button type="link" danger title="删除" icon={<DeleteOutlined />}>
</Button>
</Popconfirm>
</Space>
),
},
];
return (
<PageContainer
ghost
header={{
title: null,
breadcrumb: undefined,
}}
>
<ProTable
rowKey="id"
actionRef={actionRef}
columns={columns}
rowSelection={{ selectedRowKeys, onChange: setSelectedRowKeys }}
scroll={{ x: 'max-content' }}
request={async (params, sort) => {
if (!siteId) return { data: [], total: 0 };
const { current, pageSize } = params || {};
let orderObj: Record<string, 'asc' | 'desc'> | undefined = undefined;
if (sort && typeof sort === 'object') {
const [field, dir] = Object.entries(sort)[0] || [];
if (field && dir) {
orderObj = { [field]: dir === 'descend' ? 'desc' : 'asc' };
}
}
const response = await request(`/site-api/${siteId}/media`, {
params: {
page: current,
page_size: pageSize,
...(orderObj ? { order: orderObj } : {}),
},
});
if (!response.success) {
message.error(response.message || '获取媒体列表失败');
return {
data: [],
total: 0,
success: false,
};
}
// 从API响应中正确获取数据API响应结构为 { success, message, data, code }
const data = response.data;
return {
total: data?.total || 0,
data: data?.items || [],
success: true,
};
}}
search={false}
options={{ reload: true }}
toolBarRender={() => [
<ModalForm
title="上传媒体"
trigger={
<Button type="primary" title="上传媒体" icon={<PlusOutlined />}>
</Button>
}
width={500}
onFinish={async (values) => {
if (!siteId) return false;
try {
const formData = new FormData();
formData.append('siteId', siteId);
if (values.file && values.file.length > 0) {
values.file.forEach((f: any) => {
formData.append('file', f.originFileObj);
});
} else {
message.warning('请选择文件');
return false;
}
const res = await siteapicontrollerCreatemedia({
body: formData,
});
if (res.success) {
message.success('上传成功');
actionRef.current?.reload();
return true;
} else {
message.error(res.message || '上传失败');
return false;
}
} catch (error: any) {
message.error(error.message || '上传失败');
return false;
}
}}
>
<ProFormUploadButton
name="file"
label="文件"
fieldProps={{
name: 'file',
listType: 'picture-card',
}}
rules={[{ required: true, message: '请选择文件' }]}
/>
</ModalForm>,
<Button
title="批量导出"
onClick={async () => {
if (!siteId) return;
const idsParam = selectedRowKeys.length
? (selectedRowKeys as any[]).join(',')
: undefined;
const res = await request(`/site-api/${siteId}/media/export`, {
params: { ids: idsParam },
});
if (res?.success && res?.data?.csv) {
const blob = new Blob([res.data.csv], {
type: 'text/csv;charset=utf-8;',
});
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = 'media.csv';
a.click();
URL.revokeObjectURL(url);
} else {
message.error(res.message || '导出失败');
}
}}
>
</Button>,
<Popconfirm
title="确定批量删除选中项吗?"
okText="确定"
cancelText="取消"
disabled={!selectedRowKeys.length}
onConfirm={async () => {
// 条件判断 如果站点編號不存在則直接返回
if (!siteId) return;
// 发起批量删除请求
const response = await request(
`/site-api/${siteId}/media/batch`,
{ method: 'POST', data: { delete: selectedRowKeys } },
);
// 条件判断 根据接口返回结果进行提示
if (response.success) {
message.success('批量删除成功');
} else {
message.warning(response.message || '部分删除失败');
}
// 清空已选择的行鍵值
setSelectedRowKeys([]);
// 刷新列表数据
actionRef.current?.reload();
}}
>
<Button
title="批量删除"
danger
icon={<DeleteOutlined />}
disabled={!selectedRowKeys.length}
>
</Button>
</Popconfirm>,
<Button
title="批量转换为WebP"
disabled={!selectedRowKeys.length}
onClick={async () => {
// 条件判断 如果站点編號不存在則直接返回
if (!siteId) return;
try {
// 发起后端批量转换请求
const response = await request(
`/site-api/${siteId}/media/convert-webp`,
{
method: 'POST',
data: { ids: selectedRowKeys },
},
);
// 条件判断 根据接口返回结果进行提示
if (response.success) {
const convertedCount = response?.data?.converted?.length || 0;
const failedCount = response?.data?.failed?.length || 0;
if (failedCount > 0) {
message.warning(
`部分转换失败 已转换 ${convertedCount} 失败 ${failedCount}`,
);
} else {
message.success(`转换成功 已转换 ${convertedCount}`);
}
// 刷新列表数据
actionRef.current?.reload();
} else {
message.error(response.message || '转换失败');
}
} catch (error: any) {
message.error(error.message || '转换失败');
}
}}
>
WebP
</Button>,
]}
/>
<ModalForm
title="编辑媒体信息"
open={!!editing}
onOpenChange={(visible) => {
if (!visible) setEditing(null);
}}
initialValues={{
title: editing?.title,
}}
modalProps={{
destroyOnClose: true,
}}
onFinish={async (values) => {
if (!editing) return false;
const success = await handleUpdate(editing.id, values);
if (success) {
setEditing(null);
}
return success;
}}
>
<ProFormText
name="title"
label="标题"
placeholder="请输入标题"
rules={[{ required: true, message: '请输入标题' }]}
/>
</ModalForm>
</PageContainer>
);
};
export default MediaPage;