WEB/src/pages/Product/Sync/index.tsx

219 lines
5.8 KiB
TypeScript

import React, { useEffect, useState } from 'react';
import { ProTable, ProColumns } from '@ant-design/pro-components';
import { Card, Spin, Empty, message } from 'antd';
import { request } from '@umijs/max';
// 定义站点接口
interface Site {
id: string;
name: string;
prefix?: string;
}
// 定义WordPress商品接口
interface WpProduct {
sku: string;
name: string;
price: string;
stockQuantity: number;
status: string;
attributes?: Record<string, any>;
}
// 定义基础商品信息接口
interface ProductBase {
sku: string;
name: string;
attributes: Record<string, any>;
wpProducts: Record<string, WpProduct>;
}
// 定义API响应接口
interface ApiResponse<T> {
data: T[];
success: boolean;
message?: string;
}
// 模拟API请求函数
const getSites = async (): Promise<ApiResponse<Site>> => {
const res = await request('/site/list', {
method: 'GET',
params: {
current: 1,
pageSize: 1000
}
});
return {
data: res.data?.items || [],
success: res.success,
message: res.message
};
};
const getWPProducts = async (): Promise<ApiResponse<WpProduct>> => {
return request('/product/wp-products', {
method: 'GET',
});
};
const ProductSyncPage: React.FC = () => {
const [loading, setLoading] = useState(true);
const [sites, setSites] = useState<Site[]>([]);
const [products, setProducts] = useState<ProductBase[]>([]);
const [wpProducts, setWpProducts] = useState<WpProduct[]>([]);
// 从 SKU 中去除站点前缀
const removeSitePrefix = (sku: string, sitePrefixes: string[]): string => {
for (const prefix of sitePrefixes) {
if (prefix && sku.startsWith(prefix)) {
return sku.substring(prefix.length);
}
}
return sku;
};
// 初始化数据
useEffect(() => {
const fetchData = async () => {
try {
setLoading(true);
// 获取所有站点
const sitesResponse = await getSites();
const siteList: Site[] = sitesResponse.data || [];
setSites(siteList);
// 获取所有 WordPress 商品
const wpProductsResponse = await getWPProducts();
const wpProductList: WpProduct[] = wpProductsResponse.data || [];
setWpProducts(wpProductList);
// 提取所有站点前缀
const sitePrefixes = siteList.map(site => site.prefix || '');
// 按基础 SKU 分组商品
const productMap = new Map<string, ProductBase>();
wpProductList.forEach((wpProduct: WpProduct) => {
// 去除前缀获取基础 SKU
const baseSku = removeSitePrefix(wpProduct.sku, sitePrefixes);
if (!productMap.has(baseSku)) {
productMap.set(baseSku, {
sku: baseSku,
name: wpProduct.name,
attributes: wpProduct.attributes || {},
wpProducts: {},
});
}
// 查找对应的站点
const site = siteList.find((s: Site) => s.prefix && wpProduct.sku.startsWith(s.prefix));
if (site) {
const product = productMap.get(baseSku);
if (product) {
product.wpProducts[site.id] = wpProduct;
}
}
});
// 转换为数组
setProducts(Array.from(productMap.values()));
} catch (error) {
message.error('获取数据失败,请重试');
console.error('Error fetching data:', error);
} finally {
setLoading(false);
}
};
fetchData();
}, []);
// 生成表格列配置
const generateColumns = (): ProColumns<ProductBase>[] => {
const columns: ProColumns<ProductBase>[] = [
{
title: '商品 SKU',
dataIndex: 'sku',
key: 'sku',
width: 150,
fixed: 'left',
},
{
title: '商品信息',
dataIndex: 'name',
key: 'name',
width: 200,
fixed: 'left',
render: (dom: React.ReactNode, record: ProductBase) => (
<div>
<div>{dom}</div>
<div style={{ marginTop: 4, fontSize: 12, color: '#666' }}>
{Object.entries(record.attributes || {})
.map(([key, value]) => `${key}: ${value}`)
.join(', ')}
</div>
</div>
),
},
];
// 为每个站点生成列
sites.forEach((site: Site) => {
const siteColumn: ProColumns<ProductBase> = {
title: site.name,
key: `site_${site.id}`,
width: 250,
render: (_, record: ProductBase) => {
const wpProduct = record.wpProducts[site.id];
if (!wpProduct) {
return <Empty description="未同步" image={Empty.PRESENTED_IMAGE_SIMPLE} />;
}
return (
<div>
<div>SKU: {wpProduct.sku}</div>
<div>: {wpProduct.price}</div>
<div>: {wpProduct.stockQuantity}</div>
<div>: {wpProduct.status === 'publish' ? '已发布' : '草稿'}</div>
</div>
);
},
};
columns.push(siteColumn);
});
return columns;
};
return (
<Card title="商品同步状态" className="product-sync-card">
{loading ? (
<Spin size="large" style={{ display: 'flex', justifyContent: 'center', padding: 40 }} />
) : (
<ProTable<ProductBase>
columns={generateColumns()}
dataSource={products}
rowKey="sku"
pagination={{
pageSize: 10,
showSizeChanger: true,
showTotal: (total) => `${total} 个商品`,
}}
scroll={{ x: 'max-content' }}
search={{
labelWidth: 'auto',
}}
options={{
density: true,
}}
locale={{
emptyText: '暂无商品数据',
}}
/>
)}
</Card>
);
};
export default ProductSyncPage;