feat: 添加产品工具, 重构产品 #31

Closed
zksu wants to merge 37 commits from (deleted):main into main
7 changed files with 116 additions and 261 deletions
Showing only changes of commit dc616f5e8d - Show all commits

View File

@ -73,7 +73,7 @@ export default defineConfig({
component: './Site/List',
},
{
name: '店铺页面',
name: '店铺管理',
path: '/site/shop',
component: './Site/Shop/Layout',
routes: [

View File

@ -1721,8 +1721,11 @@ const Shipping: React.FC<{
]}
/>
<ProFormDependency name={[['details', 'packaging_type']]}>
{({ details: { packaging_type } }) => {
if (packaging_type === 'package') {
{({ details }) => {
// 根据包装类型决定渲染内容
const selectedPackagingType = details?.packaging_type;
// 判断是否为纸箱
if (selectedPackagingType === 'package') {
return (
<ProFormList
min={1}
@ -1863,7 +1866,8 @@ const Shipping: React.FC<{
</ProFormList>
);
}
if (packaging_type === 'courier-pak') {
// 判断是否为文件袋
if (selectedPackagingType === 'courier-pak') {
return (
<ProFormList
label="文件袋"

View File

@ -110,6 +110,7 @@ const MediaPage: React.FC = () => {
title: '操作',
valueType: 'option',
width: 160,
fixed: 'right',
render: (_, record) => (
<Space>
<Button
@ -119,14 +120,18 @@ const MediaPage: React.FC = () => {
onClick={() => {
setEditing(record);
}}
/>
>
</Button>
<Popconfirm
title="确定删除吗?"
onConfirm={() => handleDelete(record.id)}
okText="确定"
cancelText="取消"
>
<Button type="link" danger title="删除" icon={<DeleteOutlined />} />
<Button type="link" danger title="删除" icon={<DeleteOutlined />}>
</Button>
</Popconfirm>
</Space>
),
@ -187,7 +192,7 @@ const MediaPage: React.FC = () => {
toolBarRender={() => [
<ModalForm
title="上传媒体"
trigger={<Button type="primary" title="上传媒体" icon={<PlusOutlined />} />}
trigger={<Button type="primary" title="上传媒体" icon={<PlusOutlined />}></Button>}
width={500}
onFinish={async (values) => {
if (!siteId) return false;
@ -252,7 +257,9 @@ const MediaPage: React.FC = () => {
message.error(res.message || '导出失败');
}
}}
/>,
>
</Button>,
<Popconfirm
title="确定批量删除选中项吗?"
okText="确定"
@ -280,9 +287,45 @@ const MediaPage: React.FC = () => {
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>
]}
/>

View File

@ -78,14 +78,10 @@ const OrdersPage: React.FC = () => {
const columns: ProColumns<API.Order>[] = [
{
title: 'ID',
title: '订单号',
dataIndex: 'id',
hideInSearch: true,
},
{
title: '订单号',
dataIndex: 'number',
},
{
title: '状态',
dataIndex: 'status',
@ -208,6 +204,7 @@ const OrdersPage: React.FC = () => {
<PageContainer ghost header={{ title: null, breadcrumb: undefined }}>
<Tabs items={tabs} activeKey={activeKey} onChange={setActiveKey} />
<ProTable
columns={columns}
params={{ status: activeKey }}
headerTitle="查询表格"
scroll={{ x: 'max-content' }}
@ -405,7 +402,7 @@ const OrdersPage: React.FC = () => {
success: false
};
}}
columns={columns}
/>
</PageContainer>
);

View File

@ -1,167 +0,0 @@
import { sitecontrollerAll } from '@/servers/api/site';
import { PageContainer, ProColumns, ProTable } from '@ant-design/pro-components';
import { request } from '@umijs/max';
import { App, Image, Select } from 'antd';
import React, { useEffect, useState } from 'react';
const MediaPage: React.FC = () => {
const { message } = App.useApp();
const [sites, setSites] = useState<any[]>([]);
const [selectedSiteId, setSelectedSiteId] = useState<number | undefined>();
const [mediaList, setMediaList] = useState<any[]>([]);
const [loading, setLoading] = useState(false);
const [pagination, setPagination] = useState({
current: 1,
pageSize: 20,
total: 0,
});
// Fetch sites
useEffect(() => {
const fetchSites = async () => {
try {
const res = await sitecontrollerAll();
if (res.success && res.data) {
setSites(res.data);
if (res.data.length > 0) {
setSelectedSiteId(res.data[0].id);
}
}
} catch (error) {
message.error('获取站点列表失败');
}
};
fetchSites();
}, []);
// Fetch media
const fetchMedia = async (siteId: number, page: number, pageSize: number) => {
setLoading(true);
try {
const res = await request('/media/list', {
params: { siteId, page, pageSize },
});
if (res.success) {
setMediaList(res.data.items || []);
setPagination({
current: page,
pageSize: pageSize,
total: res.data.total || 0,
});
} else {
message.error(res.message || '获取媒体库失败');
}
} catch (error) {
message.error('获取媒体库失败');
} finally {
setLoading(false);
}
};
useEffect(() => {
if (selectedSiteId) {
fetchMedia(selectedSiteId, 1, pagination.pageSize);
}
}, [selectedSiteId]);
const handlePageChange = (page: number, pageSize: number) => {
if (selectedSiteId) {
fetchMedia(selectedSiteId, page, pageSize);
}
};
const formatSize = (bytes: number) => {
if (!bytes) return '-';
if (bytes === 0) return '0 B';
const k = 1024;
const sizes = ['B', 'KB', 'MB', 'GB'];
const i = Math.floor(Math.log(bytes) / Math.log(k));
return parseFloat((bytes / Math.pow(k, i)).toFixed(2)) + ' ' + sizes[i];
};
const columns: ProColumns<any>[] = [
{
title: '展示',
dataIndex: 'source_url',
hideInSearch: true,
render: (_, record) => (
<Image
src={record.media_details?.sizes?.thumbnail?.source_url || record.source_url}
style={{ width: 60, height: 60, objectFit: 'contain', background: '#f0f0f0' }}
fallback="https://via.placeholder.com/60?text=No+Img"
/>
),
},
{
title: '名称',
dataIndex: ['title', 'rendered'],
copyable: true,
ellipsis: true,
width: 200,
},
{
title: '宽高',
dataIndex: 'media_details',
hideInSearch: true,
render: (_, record) => {
const width = record.media_details?.width;
const height = record.media_details?.height;
return width && height ? `${width} x ${height}` : '-';
},
},
{
title: '地址',
dataIndex: 'source_url',
copyable: true,
ellipsis: true,
hideInSearch: true,
},
{
title: '文件大小',
dataIndex: 'media_details',
hideInSearch: true,
render: (_, record) => formatSize(record.media_details?.filesize),
},
{
title: '素材类型',
dataIndex: 'mime_type',
width: 120,
},
];
return (
<PageContainer
header={{
title: '媒体库',
extra: [
<Select
key="site-select"
style={{ width: 200 }}
placeholder="选择站点"
value={selectedSiteId}
onChange={setSelectedSiteId}
options={sites.map(site => ({ label: site.name, value: site.id }))}
/>
]
}}
>
<ProTable
rowKey="id"
columns={columns}
dataSource={mediaList}
loading={loading}
search={false}
pagination={{
current: pagination.current,
pageSize: pagination.pageSize,
total: pagination.total,
onChange: handlePageChange,
showSizeChanger: true,
}}
options={false}
/>
</PageContainer>
);
};
export default MediaPage;

View File

@ -15,10 +15,6 @@ export async function siteapicontrollerGetcustomers(
method: 'GET',
params: {
...queryParams,
where: undefined,
...queryParams['where'],
order: undefined,
...queryParams['order'],
},
...(options || {}),
},
@ -74,10 +70,6 @@ export async function siteapicontrollerExportcustomers(
method: 'GET',
params: {
...queryParams,
where: undefined,
...queryParams['where'],
order: undefined,
...queryParams['order'],
},
...(options || {}),
});
@ -113,10 +105,6 @@ export async function siteapicontrollerGetmedia(
method: 'GET',
params: {
...queryParams,
where: undefined,
...queryParams['where'],
order: undefined,
...queryParams['order'],
},
...(options || {}),
});
@ -141,6 +129,28 @@ export async function siteapicontrollerBatchmedia(
});
}
/** 此处后端没有提供注释 POST /site-api/${param0}/media/convert-webp */
export async function siteapicontrollerConvertmediatowebp(
// 叠加生成的Param类型 (非body参数swagger默认没有生成对象)
params: API.siteapicontrollerConvertmediatowebpParams,
body: Record<string, any>,
options?: { [key: string]: any },
) {
const { siteId: param0, ...queryParams } = params;
return request<Record<string, any>>(
`/site-api/${param0}/media/convert-webp`,
{
method: 'POST',
headers: {
'Content-Type': 'text/plain',
},
params: { ...queryParams },
data: body,
...(options || {}),
},
);
}
/** 此处后端没有提供注释 GET /site-api/${param0}/media/export */
export async function siteapicontrollerExportmedia(
// 叠加生成的Param类型 (非body参数swagger默认没有生成对象)
@ -152,10 +162,6 @@ export async function siteapicontrollerExportmedia(
method: 'GET',
params: {
...queryParams,
where: undefined,
...queryParams['where'],
order: undefined,
...queryParams['order'],
},
...(options || {}),
});
@ -172,10 +178,6 @@ export async function siteapicontrollerGetorders(
method: 'GET',
params: {
...queryParams,
where: undefined,
...queryParams['where'],
order: undefined,
...queryParams['order'],
},
...(options || {}),
});
@ -230,10 +232,6 @@ export async function siteapicontrollerExportorders(
method: 'GET',
params: {
...queryParams,
where: undefined,
...queryParams['where'],
order: undefined,
...queryParams['order'],
},
...(options || {}),
});
@ -271,10 +269,6 @@ export async function siteapicontrollerGetproducts(
method: 'GET',
params: {
...queryParams,
where: undefined,
...queryParams['where'],
order: undefined,
...queryParams['order'],
},
...(options || {}),
},
@ -330,10 +324,6 @@ export async function siteapicontrollerExportproducts(
method: 'GET',
params: {
...queryParams,
where: undefined,
...queryParams['where'],
order: undefined,
...queryParams['order'],
},
...(options || {}),
});
@ -350,10 +340,6 @@ export async function siteapicontrollerExportproductsspecial(
method: 'GET',
params: {
...queryParams,
where: undefined,
...queryParams['where'],
order: undefined,
...queryParams['order'],
},
...(options || {}),
});
@ -413,10 +399,6 @@ export async function siteapicontrollerGetsubscriptions(
method: 'GET',
params: {
...queryParams,
where: undefined,
...queryParams['where'],
order: undefined,
...queryParams['order'],
},
...(options || {}),
},
@ -434,10 +416,6 @@ export async function siteapicontrollerExportsubscriptions(
method: 'GET',
params: {
...queryParams,
where: undefined,
...queryParams['where'],
order: undefined,
...queryParams['order'],
},
...(options || {}),
});
@ -512,10 +490,6 @@ export async function siteapicontrollerGetcustomerorders(
method: 'GET',
params: {
...queryParams,
where: undefined,
...queryParams['where'],
order: undefined,
...queryParams['order'],
},
...(options || {}),
},

View File

@ -1421,6 +1421,10 @@ declare namespace API {
siteId: number;
};
type siteapicontrollerConvertmediatowebpParams = {
siteId: number;
};
type siteapicontrollerCreatecustomerParams = {
siteId: number;
};
@ -1472,9 +1476,9 @@ declare namespace API {
/** 客户ID,用于筛选订单 */
customer_id?: number;
/** 过滤条件对象 */
where?: Record<string, any>;
where?: any;
/** 排序对象,例如 { "sku": "desc" } */
order?: Record<string, any>;
order?: any;
/** 排序字段(兼容旧入参) */
orderby?: string;
/** 排序方式(兼容旧入参) */
@ -1498,9 +1502,9 @@ declare namespace API {
/** 客户ID,用于筛选订单 */
customer_id?: number;
/** 过滤条件对象 */
where?: Record<string, any>;
where?: any;
/** 排序对象,例如 { "sku": "desc" } */
order?: Record<string, any>;
order?: any;
/** 排序字段(兼容旧入参) */
orderby?: string;
/** 排序方式(兼容旧入参) */
@ -1524,9 +1528,9 @@ declare namespace API {
/** 客户ID,用于筛选订单 */
customer_id?: number;
/** 过滤条件对象 */
where?: Record<string, any>;
where?: any;
/** 排序对象,例如 { "sku": "desc" } */
order?: Record<string, any>;
order?: any;
/** 排序字段(兼容旧入参) */
orderby?: string;
/** 排序方式(兼容旧入参) */
@ -1550,9 +1554,9 @@ declare namespace API {
/** 客户ID,用于筛选订单 */
customer_id?: number;
/** 过滤条件对象 */
where?: Record<string, any>;
where?: any;
/** 排序对象,例如 { "sku": "desc" } */
order?: Record<string, any>;
order?: any;
/** 排序字段(兼容旧入参) */
orderby?: string;
/** 排序方式(兼容旧入参) */
@ -1576,9 +1580,9 @@ declare namespace API {
/** 客户ID,用于筛选订单 */
customer_id?: number;
/** 过滤条件对象 */
where?: Record<string, any>;
where?: any;
/** 排序对象,例如 { "sku": "desc" } */
order?: Record<string, any>;
order?: any;
/** 排序字段(兼容旧入参) */
orderby?: string;
/** 排序方式(兼容旧入参) */
@ -1602,9 +1606,9 @@ declare namespace API {
/** 客户ID,用于筛选订单 */
customer_id?: number;
/** 过滤条件对象 */
where?: Record<string, any>;
where?: any;
/** 排序对象,例如 { "sku": "desc" } */
order?: Record<string, any>;
order?: any;
/** 排序字段(兼容旧入参) */
orderby?: string;
/** 排序方式(兼容旧入参) */
@ -1628,9 +1632,9 @@ declare namespace API {
/** 客户ID,用于筛选订单 */
customer_id?: number;
/** 过滤条件对象 */
where?: Record<string, any>;
where?: any;
/** 排序对象,例如 { "sku": "desc" } */
order?: Record<string, any>;
order?: any;
/** 排序字段(兼容旧入参) */
orderby?: string;
/** 排序方式(兼容旧入参) */
@ -1660,9 +1664,9 @@ declare namespace API {
/** 客户ID,用于筛选订单 */
customer_id?: number;
/** 过滤条件对象 */
where?: Record<string, any>;
where?: any;
/** 排序对象,例如 { "sku": "desc" } */
order?: Record<string, any>;
order?: any;
/** 排序字段(兼容旧入参) */
orderby?: string;
/** 排序方式(兼容旧入参) */
@ -1686,9 +1690,9 @@ declare namespace API {
/** 客户ID,用于筛选订单 */
customer_id?: number;
/** 过滤条件对象 */
where?: Record<string, any>;
where?: any;
/** 排序对象,例如 { "sku": "desc" } */
order?: Record<string, any>;
order?: any;
/** 排序字段(兼容旧入参) */
orderby?: string;
/** 排序方式(兼容旧入参) */
@ -1722,9 +1726,9 @@ declare namespace API {
/** 客户ID,用于筛选订单 */
customer_id?: number;
/** 过滤条件对象 */
where?: Record<string, any>;
where?: any;
/** 排序对象,例如 { "sku": "desc" } */
order?: Record<string, any>;
order?: any;
/** 排序字段(兼容旧入参) */
orderby?: string;
/** 排序方式(兼容旧入参) */
@ -1753,9 +1757,9 @@ declare namespace API {
/** 客户ID,用于筛选订单 */
customer_id?: number;
/** 过滤条件对象 */
where?: Record<string, any>;
where?: any;
/** 排序对象,例如 { "sku": "desc" } */
order?: Record<string, any>;
order?: any;
/** 排序字段(兼容旧入参) */
orderby?: string;
/** 排序方式(兼容旧入参) */
@ -1779,9 +1783,9 @@ declare namespace API {
/** 客户ID,用于筛选订单 */
customer_id?: number;
/** 过滤条件对象 */
where?: Record<string, any>;
where?: any;
/** 排序对象,例如 { "sku": "desc" } */
order?: Record<string, any>;
order?: any;
/** 排序字段(兼容旧入参) */
orderby?: string;
/** 排序方式(兼容旧入参) */
@ -2400,9 +2404,9 @@ declare namespace API {
/** 客户ID,用于筛选订单 */
customer_id?: number;
/** 过滤条件对象 */
where?: Record<string, any>;
where?: any;
/** 排序对象,例如 { "sku": "desc" } */
order?: Record<string, any>;
order?: any;
/** 排序字段(兼容旧入参) */
orderby?: string;
/** 排序方式(兼容旧入参) */