219 lines
5.8 KiB
TypeScript
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; |