zksu
/
WEB
forked from yoone/WEB
1
0
Fork 0
WEB/src/pages/Product/Sync/SiteProductCell.tsx

462 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 { productcontrollerGetproductlist } from '@/servers/api/product';
import {
siteapicontrollerGetproducts,
siteapicontrollerUpsertproduct,
} from '@/servers/api/siteApi';
import { templatecontrollerRendertemplate } from '@/servers/api/template';
import { SyncOutlined } from '@ant-design/icons';
import { ModalForm, ProFormText } from '@ant-design/pro-components';
import { Button, message, Spin, Tag } from 'antd';
import React, { useEffect, useState } from 'react';
// 定义站点接口
interface Site {
id: number;
name: string;
skuPrefix?: string;
isDisabled?: boolean;
}
// 定义本地产品接口(与后端 Product 实体匹配)
interface SiteProduct {
id: number;
sku: string;
name: string;
nameCn: string;
shortDescription?: string;
description?: string;
price: number;
promotionPrice: number;
type: string;
categoryId?: number;
category?: any;
attributes?: any[];
components?: any[];
siteSkus: string[];
source: number;
createdAt: Date;
updatedAt: Date;
}
// 定义本地产品完整接口
interface LocalProduct {
id: number;
sku: string;
name: string;
nameCn: string;
shortDescription?: string;
description?: string;
price: number;
promotionPrice: number;
type: string;
categoryId?: number;
category?: any;
attributes?: any[];
components?: any[];
siteSkus: string[];
source: number;
images?: string[];
weight?: number;
dimensions?: any;
}
// 定义站点产品数据接口
interface SiteProductData {
sku: string;
regular_price?: number;
price?: number;
sale_price?: number;
stock_quantity?: number;
stockQuantity?: number;
status?: string;
externalProductId?: string;
name?: string;
description?: string;
images?: string[];
}
interface SiteProductCellProps {
// 产品行数据
product: SiteProduct;
// 站点列数据
site: Site;
// 同步成功后的回调
onSyncSuccess?: () => void;
}
const SiteProductCell: React.FC<SiteProductCellProps> = ({
product,
site,
onSyncSuccess,
}) => {
// 存储该站点对应的产品数据
const [siteProduct, setSiteProduct] = useState<SiteProductData | null>(null);
// 存储本地产品完整数据
const [localProduct, setLocalProduct] = useState<LocalProduct | null>(null);
// 加载状态
const [loading, setLoading] = useState(false);
// 是否已加载过数据
const [loaded, setLoaded] = useState(false);
// 同步中状态
const [syncing, setSyncing] = useState(false);
// 组件挂载时加载数据
useEffect(() => {
loadSiteProduct();
}, [product.id, site.id]);
// 加载站点产品数据
const loadSiteProduct = async () => {
// 如果已经加载过,则不再重复加载
if (loaded) {
return;
}
setLoading(true);
try {
// 首先查找该产品在该站点的实际SKU
// 注意:siteSkus 现在是字符串数组,无法直接匹配站点
// 这里使用模板生成的 SKU 作为默认值
let siteProductSku = '';
// 如果需要更精确的站点 SKU 匹配,需要后端提供额外的接口
// 如果没有找到实际的siteSku则根据模板或默认规则生成期望的SKU
const expectedSku =
siteProductSku || `${site.skuPrefix || ''}-${product.sku}`;
// 使用 siteapicontrollerGetproducts 获取该站点的所有产品
const productsRes = await siteapicontrollerGetproducts({
siteId: site.id,
current: 1,
pageSize: 10000,
} as any);
if (productsRes.data?.items) {
// 在该站点的产品数据中查找匹配的产品
let foundProduct = productsRes.data.items.find(
(item: any) => item.sku === expectedSku,
);
// 如果根据实际SKU没找到再尝试用模板生成的SKU查找
if (!foundProduct && siteProductSku) {
foundProduct = productsRes.data.items.find(
(item: any) => item.sku === siteProductSku,
);
}
if (foundProduct) {
setSiteProduct(foundProduct as SiteProductData);
}
}
// 标记为已加载
setLoaded(true);
} catch (error) {
console.error(`加载站点 ${site.name} 的产品数据失败:`, error);
} finally {
setLoading(false);
}
};
// 获取本地产品完整信息
const getLocalProduct = async (): Promise<LocalProduct | null> => {
try {
// 如果已经有本地产品数据,直接返回
if (localProduct) {
return localProduct;
}
// 使用 productcontrollerGetproductlist 获取本地产品完整信息
const res = await productcontrollerGetproductlist({
where: {
id: product.id,
},
} as any);
if (res.success && res.data) {
const productData = res.data as LocalProduct;
setLocalProduct(productData);
return productData;
}
return null;
} catch (error) {
console.error('获取本地产品信息失败:', error);
return null;
}
};
// 渲染站点SKU
const renderSiteSku = async (data: any): Promise<string> => {
try {
// 使用 templatecontrollerRendertemplate API 渲染模板
const res = await templatecontrollerRendertemplate(
{ name: 'siteproduct-sku' } as any,
data,
);
return res?.template || res?.result || '';
} catch (error) {
console.error('渲染SKU模板失败:', error);
return '';
}
};
// 同步产品到站点
const syncProductToSite = async (values: any) => {
try {
setSyncing(true);
const hide = message.loading('正在同步...', 0);
// 获取本地产品完整信息
const productDetail = await getLocalProduct();
if (!productDetail) {
hide();
message.error('获取本地产品信息失败');
return false;
}
// 构造要同步的产品数据
const productData: any = {
sku: values.sku,
name: productDetail.name,
description: productDetail.description || '',
regular_price: productDetail.price,
price: productDetail.price,
stock_quantity: productDetail.stock,
status: 'publish',
};
// 如果有图片,添加图片信息
if (productDetail.images && productDetail.images.length > 0) {
productData.images = productDetail.images;
}
// 如果有重量,添加重量信息
if (productDetail.weight) {
productData.weight = productDetail.weight;
}
// 如果有尺寸,添加尺寸信息
if (productDetail.dimensions) {
productData.dimensions = productDetail.dimensions;
}
// 使用 siteapicontrollerUpsertproduct API 同步产品到站点
const res = await siteapicontrollerUpsertproduct(
{ siteId: site.id } as any,
productData as any,
);
if (!res.success) {
hide();
throw new Error(res.message || '同步失败');
}
// 更新本地状态
if (res.data && typeof res.data === 'object') {
setSiteProduct(res.data as SiteProductData);
}
hide();
message.success('同步成功');
// 触发回调
if (onSyncSuccess) {
onSyncSuccess();
}
return true;
} catch (error: any) {
message.error('同步失败: ' + (error.message || error.toString()));
return false;
} finally {
setSyncing(false);
}
};
// 更新同步产品到站点
const updateSyncProduct = async (values: any) => {
try {
setSyncing(true);
const hide = message.loading('正在更新...', 0);
// 获取本地产品完整信息
const productDetail = await getLocalProduct();
if (!productDetail) {
hide();
message.error('获取本地产品信息失败');
return false;
}
// 构造要更新的产品数据
const productData: any = {
...siteProduct,
sku: values.sku,
name: productDetail.name,
description: productDetail.description || '',
regular_price: productDetail.price,
price: productDetail.price,
stock_quantity: productDetail.stock,
status: 'publish',
};
// 如果有图片,添加图片信息
if (productDetail.images && productDetail.images.length > 0) {
productData.images = productDetail.images;
}
// 如果有重量,添加重量信息
if (productDetail.weight) {
productData.weight = productDetail.weight;
}
// 如果有尺寸,添加尺寸信息
if (productDetail.dimensions) {
productData.dimensions = productDetail.dimensions;
}
// 使用 siteapicontrollerUpsertproduct API 更新产品到站点
const res = await siteapicontrollerUpsertproduct(
{ siteId: site.id } as any,
productData as any,
);
if (!res.success) {
hide();
throw new Error(res.message || '更新失败');
}
// 更新本地状态
if (res.data && typeof res.data === 'object') {
setSiteProduct(res.data as SiteProductData);
}
hide();
message.success('更新成功');
// 触发回调
if (onSyncSuccess) {
onSyncSuccess();
}
return true;
} catch (error: any) {
message.error('更新失败: ' + (error.message || error.toString()));
return false;
} finally {
setSyncing(false);
}
};
// 如果正在加载,显示加载状态
if (loading) {
return (
<div style={{ textAlign: 'center', padding: 10 }}>
<Spin size="small" />
</div>
);
}
// 如果没有找到站点产品,显示同步按钮
if (!siteProduct) {
// 首先查找该产品在该站点的实际SKU
// 注意:siteSkus 现在是字符串数组,无法直接匹配站点
// 这里使用模板生成的 SKU 作为默认值
let siteProductSku = '';
// 如果需要更精确的站点 SKU 匹配,需要后端提供额外的接口
const defaultSku =
siteProductSku || `${site.skuPrefix || ''}-${product.sku}`;
return (
<ModalForm
title="同步产品"
trigger={
<Button type="link" icon={<SyncOutlined />}>
</Button>
}
width={400}
onFinish={async (values) => {
return await syncProductToSite(values);
}}
initialValues={{
sku: defaultSku,
}}
>
<ProFormText
name="sku"
label="商店 SKU"
placeholder="请输入商店 SKU"
rules={[{ required: true, message: '请输入 SKU' }]}
/>
</ModalForm>
);
}
// 显示站点产品信息
return (
<div style={{ fontSize: 12 }}>
<div
style={{
display: 'flex',
justifyContent: 'space-between',
alignItems: 'start',
}}
>
<div style={{ fontWeight: 'bold' }}>{siteProduct.sku}</div>
<ModalForm
title="更新同步"
trigger={
<Button
type="link"
size="small"
icon={<SyncOutlined spin={false} />}
>
</Button>
}
width={400}
onFinish={async (values) => {
return await updateSyncProduct(values);
}}
initialValues={{
sku: siteProduct.sku,
}}
>
<ProFormText
name="sku"
label="商店 SKU"
placeholder="请输入商店 SKU"
rules={[{ required: true, message: '请输入 SKU' }]}
disabled
/>
<div style={{ marginBottom: 16, color: '#666' }}>
</div>
</ModalForm>
</div>
<div>Price: {siteProduct.regular_price ?? siteProduct.price}</div>
{siteProduct.sale_price && (
<div style={{ color: 'red' }}>Sale: {siteProduct.sale_price}</div>
)}
<div>
Stock: {siteProduct.stock_quantity ?? siteProduct.stockQuantity}
</div>
<div style={{ marginTop: 2 }}>
Status:{' '}
{siteProduct.status === 'publish' ? (
<Tag color="green">Published</Tag>
) : (
<Tag>{siteProduct.status}</Tag>
)}
</div>
</div>
);
};
export default SiteProductCell;