Files
makemd/dashboard/src/pages/Product/CrossPlatformManage.tsx
wurenzhi 22308fe042 refactor: 重构项目结构并优化代码
- 删除无用的文件和错误日志
- 创建统一的 imports 模块集中管理依赖
- 重构组件使用新的 imports 方式
- 修复文档路径大小写问题
- 优化类型定义和接口导出
- 更新依赖版本
- 改进错误处理和API配置
- 统一组件导出方式
2026-03-27 16:56:06 +08:00

856 lines
30 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 {
useState,
useEffect,
Card,
Table,
Button,
Space,
Tag,
Input,
Select,
Row,
Col,
Modal,
message,
Tooltip,
Badge,
Alert,
Descriptions,
Divider,
Dropdown,
Menu,
Image,
Typography,
Tabs,
Form,
InputNumber,
Switch,
SyncOutlined,
EditOutlined,
EyeOutlined,
GlobalOutlined,
AmazonOutlined,
ShoppingOutlined,
ShopOutlined,
CheckCircleOutlined,
CloseCircleOutlined,
ExclamationCircleOutlined,
LinkOutlined,
ArrowUpOutlined,
ArrowDownOutlined,
Text,
Title,
Option,
Search,
FC,
} from '@/imports';
import type { ColumnsType } from 'antd/es/table';
// ==================== 多商户店铺配置 ====================
// 当前用户拥有的店铺(根据登录用户的权限动态加载)
interface UserShop {
id: string;
platform: string;
shopId: string;
shopName: string;
region: string;
status: 'ACTIVE' | 'INACTIVE';
apiSupported: boolean; // 是否支持API管理
}
// 模拟当前用户拥有的店铺
const CURRENT_USER_SHOPS: UserShop[] = [
{ id: '1', platform: 'AMAZON', shopId: 'AMZ-US-001', shopName: 'Amazon US Store', region: 'US', status: 'ACTIVE', apiSupported: true },
{ id: '2', platform: 'AMAZON', shopId: 'AMZ-EU-001', shopName: 'Amazon EU Store', region: 'EU', status: 'ACTIVE', apiSupported: true },
{ id: '3', platform: 'EBAY', shopId: 'EB-US-001', shopName: 'eBay US Store', region: 'US', status: 'ACTIVE', apiSupported: true },
{ id: '4', platform: 'SHOPIFY', shopId: 'SF-001', shopName: 'My Shopify Store', region: 'US', status: 'ACTIVE', apiSupported: true },
{ id: '5', platform: 'SHOPEE', shopId: 'SP-SG-001', shopName: 'Shopee Singapore', region: 'SG', status: 'ACTIVE', apiSupported: false }, // 不支持API
{ id: '6', platform: 'TIKTOK', shopId: 'TK-US-001', shopName: 'TikTok US Shop', region: 'US', status: 'ACTIVE', apiSupported: false }, // 不支持API
];
// 平台配置
const PLATFORM_CONFIG: Record<string, { name: string; color: string; icon: React.ReactNode }> = {
// TikTok系列
TIKTOK: { name: 'TikTok', color: '#000000', icon: <ShopOutlined /> },
TIKTOK_FULL: { name: 'TikTok全托管', color: '#000000', icon: <ShopOutlined /> },
// Shopee系列
SHOPEE: { name: 'Shopee', color: '#EE4D2D', icon: <ShopOutlined /> },
SHOPEE_FULL: { name: 'Shopee全托管', color: '#EE4D2D', icon: <ShopOutlined /> },
SHOPEE_LIGHT: { name: 'Shopee轻出海', color: '#EE4D2D', icon: <ShopOutlined /> },
// Lazada系列
LAZADA: { name: 'Lazada', color: '#0F156D', icon: <ShopOutlined /> },
LAZADA_FULL: { name: 'Lazada全托管', color: '#0F156D', icon: <ShopOutlined /> },
// Temu
TEMU_FULL: { name: 'Temu全托管', color: '#96BF48', icon: <ShopOutlined /> },
// SHEIN系列
SHEIN: { name: 'SHEIN', color: '#FF6B6B', icon: <ShopOutlined /> },
SHEIN_HALF: { name: 'SHEIN半托管', color: '#FF6B6B', icon: <ShopOutlined /> },
// 其他平台
OZON: { name: 'Ozon', color: '#FFD700', icon: <ShopOutlined /> },
YANDEX: { name: 'Yandex', color: '#FF0000', icon: <ShopOutlined /> },
ALIEXPRESS: { name: 'AliExpress', color: '#FF6A00', icon: <ShopOutlined /> },
ALIEXPRESS_HALF: { name: '速卖通半托管', color: '#FF6A00', icon: <ShopOutlined /> },
ALIEXPRESS_POP: { name: '速卖通本土POP', color: '#FF6A00', icon: <ShopOutlined /> },
COUPANG: { name: 'Coupang', color: '#FF3B30', icon: <ShopOutlined /> },
WALMART: { name: 'Walmart', color: '#007DC6', icon: <ShopOutlined /> },
WILDBERRIES: { name: 'Wildberries', color: '#9370DB', icon: <ShopOutlined /> },
ALLEGRO: { name: 'Allegro', color: '#00A870', icon: <ShopOutlined /> },
MERCADO_LIBRE: { name: 'Mercado Libre', color: '#6C3483', icon: <ShopOutlined /> },
JUMIA: { name: 'Jumia', color: '#FF6B00', icon: <ShopOutlined /> },
JOOM: { name: 'Joom', color: '#8A2BE2', icon: <ShopOutlined /> },
AMAZON: { name: 'Amazon', color: '#FF9900', icon: <AmazonOutlined /> },
WISH: { name: 'Wish', color: '#4A90E2', icon: <ShopOutlined /> },
EMAG: { name: 'eMAG', color: '#00B140', icon: <ShopOutlined /> },
MIRAVIA: { name: 'Miravia', color: '#FF69B4', icon: <ShopOutlined /> },
DARAZ: { name: 'Daraz', color: '#FF5722', icon: <ShopOutlined /> },
JOYBUY: { name: 'Joybuy', color: '#E74C3C', icon: <ShopOutlined /> },
ALIBABA: { name: 'Alibaba', color: '#FF6A00', icon: <ShopOutlined /> },
QOO10: { name: 'Qoo10', color: '#FF4500', icon: <ShopOutlined /> },
SHOPIFY: { name: 'Shopify', color: '#96BF48', icon: <ShoppingOutlined /> },
SHOPLAZZA: { name: 'Shoplazza', color: '#3498DB', icon: <ShopOutlined /> },
SHOPYY_V1: { name: 'SHOPYY v1.0', color: '#9370DB', icon: <ShopOutlined /> },
SHOPYY_V2: { name: 'SHOPYY v2.0', color: '#9370DB', icon: <ShopOutlined /> },
SHOPLINE: { name: 'SHOPLINE', color: '#27AE60', icon: <ShopOutlined /> },
GREATBOSS: { name: 'GreatBoss', color: '#3498DB', icon: <ShopOutlined /> },
OTHER: { name: '其他', color: '#999999', icon: <ShopOutlined /> },
// 原有平台
EBAY: { name: 'eBay', color: '#E53238', icon: <ShopOutlined /> },
};
// ==================== 跨平台商品接口 ====================
interface CrossPlatformProduct {
id: string;
localProductId: string;
sku: string;
name: string;
image: string;
category: string;
description: string;
platforms: {
[shopId: string]: {
status: 'LIVE' | 'OFFLINE' | 'PENDING' | 'ERROR' | 'SYNCING' | 'NOT_LISTED';
platformProductId: string;
platformSku: string;
price: number;
stock: number;
sales: number;
listingUrl: string;
lastSync: string;
shopInfo: UserShop;
};
};
basePrice: number;
baseStock: number;
totalSales: number;
createdAt: string;
updatedAt: string;
}
// ==================== 模拟数据 ====================
const MOCK_CROSS_PLATFORM_PRODUCTS: CrossPlatformProduct[] = [
{
id: 'CP001',
localProductId: 'P001',
sku: 'TP-TEMP-001',
name: '工业温度传感器 Pro',
image: 'https://via.placeholder.com/200x200?text=Product',
category: '工业自动化',
description: '高精度工业温度传感器,适用于各种工业环境,测量范围-40°C至125°C',
basePrice: 89.99,
baseStock: 256,
totalSales: 1250,
createdAt: '2025-12-15 10:00:00',
updatedAt: '2026-03-18 10:30:00',
platforms: {
'AMZ-US-001': {
status: 'LIVE',
platformProductId: 'AMZ-123456',
platformSku: 'TP-TEMP-001-AMZ',
price: 99.99,
stock: 100,
sales: 850,
listingUrl: 'https://amazon.com/dp/123456',
lastSync: '2026-03-18 10:00:00',
shopInfo: CURRENT_USER_SHOPS[0],
},
'AMZ-EU-001': {
status: 'LIVE',
platformProductId: 'AMZ-EU-789',
platformSku: 'TP-TEMP-001-AMZ-EU',
price: 89.99,
stock: 80,
sales: 200,
listingUrl: 'https://amazon.de/dp/789',
lastSync: '2026-03-18 09:00:00',
shopInfo: CURRENT_USER_SHOPS[1],
},
'EB-US-001': {
status: 'LIVE',
platformProductId: 'EB-789012',
platformSku: 'TP-TEMP-001-EB',
price: 95.99,
stock: 76,
sales: 200,
listingUrl: 'https://ebay.com/itm/789012',
lastSync: '2026-03-18 09:30:00',
shopInfo: CURRENT_USER_SHOPS[2],
},
'SP-SG-001': {
status: 'LIVE',
platformProductId: 'SP-345678',
platformSku: 'TP-TEMP-001-SP',
price: 89.99,
stock: 76,
sales: 80,
listingUrl: 'https://shopee.sg/product/345678',
lastSync: '2026-03-18 09:00:00',
shopInfo: CURRENT_USER_SHOPS[4],
},
},
},
{
id: 'CP002',
localProductId: 'P002',
sku: 'TP-PRES-002',
name: '压力传感器 Digital',
image: 'https://via.placeholder.com/200x200?text=Product',
category: '工业自动化',
description: '数字式压力传感器精度0.1%,支持多种输出信号',
basePrice: 129.99,
baseStock: 128,
totalSales: 680,
createdAt: '2026-03-10 14:30:00',
updatedAt: '2026-03-18 09:15:00',
platforms: {
'AMZ-US-001': {
status: 'LIVE',
platformProductId: 'AMZ-234567',
platformSku: 'TP-PRES-002-AMZ',
price: 139.99,
stock: 50,
sales: 500,
listingUrl: 'https://amazon.com/dp/234567',
lastSync: '2026-03-18 10:00:00',
shopInfo: CURRENT_USER_SHOPS[0],
},
'SF-001': {
status: 'LIVE',
platformProductId: 'SF-890123',
platformSku: 'TP-PRES-002-SF',
price: 129.99,
stock: 78,
sales: 180,
listingUrl: 'https://mystore.myshopify.com/products/890123',
lastSync: '2026-03-18 08:30:00',
shopInfo: CURRENT_USER_SHOPS[3],
},
},
},
{
id: 'CP003',
localProductId: 'P003',
sku: 'TP-FLOW-003',
name: '流量计 Ultra',
image: 'https://via.placeholder.com/200x200?text=Product',
category: '仪器仪表',
description: '超声波流量计,非接触式测量,精度高',
basePrice: 299.99,
baseStock: 64,
totalSales: 320,
createdAt: '2026-03-01 09:00:00',
updatedAt: '2026-03-17 16:45:00',
platforms: {
'AMZ-US-001': {
status: 'OFFLINE',
platformProductId: 'AMZ-345678',
platformSku: 'TP-FLOW-003-AMZ',
price: 319.99,
stock: 0,
sales: 300,
listingUrl: 'https://amazon.com/dp/345678',
lastSync: '2026-03-17 16:00:00',
shopInfo: CURRENT_USER_SHOPS[0],
},
'EB-US-001': {
status: 'ERROR',
platformProductId: 'EB-901234',
platformSku: 'TP-FLOW-003-EB',
price: 299.99,
stock: 64,
sales: 20,
listingUrl: 'https://ebay.com/itm/901234',
lastSync: '2026-03-17 15:30:00',
shopInfo: CURRENT_USER_SHOPS[2],
},
},
},
];
const STATUS_CONFIG: Record<string, { color: string; text: string; icon: React.ReactNode }> = {
LIVE: { color: 'success', text: '在线', icon: <CheckCircleOutlined /> },
OFFLINE: { color: 'default', text: '已下架', icon: <CloseCircleOutlined /> },
PENDING: { color: 'warning', text: '审核中', icon: <ExclamationCircleOutlined /> },
ERROR: { color: 'error', text: '异常', icon: <ExclamationCircleOutlined /> },
SYNCING: { color: 'processing', text: '同步中', icon: <SyncOutlined spin /> },
NOT_LISTED: { color: 'default', text: '未上架', icon: <CloseCircleOutlined /> },
};
const CrossPlatformManage: FC = () => {
const [loading, setLoading] = useState(false);
const [products, setProducts] = useState<CrossPlatformProduct[]>([]);
const [filteredProducts, setFilteredProducts] = useState<CrossPlatformProduct[]>([]);
const [selectedShop, setSelectedShop] = useState<string>('ALL');
const [searchText, setSearchText] = useState('');
const [selectedRows, setSelectedRows] = useState<CrossPlatformProduct[]>([]);
const [syncLoading, setSyncLoading] = useState<Record<string, boolean>>({});
// 详情弹窗
const [detailModalVisible, setDetailModalVisible] = useState(false);
const [selectedProduct, setSelectedProduct] = useState<CrossPlatformProduct | null>(null);
// 同步弹窗
const [syncModalVisible, setSyncModalVisible] = useState(false);
const [syncForm] = Form.useForm();
const [syncingProduct, setSyncingProduct] = useState<CrossPlatformProduct | null>(null);
const [selectedSyncShops, setSelectedSyncShops] = useState<string[]>([]);
useEffect(() => {
fetchProducts();
}, []);
useEffect(() => {
filterProducts();
}, [products, selectedShop, searchText]);
const fetchProducts = async () => {
setLoading(true);
try {
await new Promise(resolve => setTimeout(resolve, 500));
setProducts(MOCK_CROSS_PLATFORM_PRODUCTS);
} catch (error) {
message.error('加载跨平台商品失败');
} finally {
setLoading(false);
}
};
const filterProducts = () => {
let result = [...products];
// 按店铺筛选
if (selectedShop !== 'ALL') {
result = result.filter(p =>
p.platforms[selectedShop] &&
p.platforms[selectedShop].status !== 'NOT_LISTED'
);
}
// 按关键词搜索
if (searchText) {
result = result.filter(p =>
p.name.toLowerCase().includes(searchText.toLowerCase()) ||
p.sku.toLowerCase().includes(searchText.toLowerCase())
);
}
setFilteredProducts(result);
};
// 获取用户拥有的店铺列表(按平台分组)
const getUserShopsByPlatform = () => {
const grouped: Record<string, UserShop[]> = {};
CURRENT_USER_SHOPS.forEach(shop => {
if (!grouped[shop.platform]) {
grouped[shop.platform] = [];
}
grouped[shop.platform].push(shop);
});
return grouped;
};
// 查看商品详情
const handleViewDetail = (product: CrossPlatformProduct) => {
setSelectedProduct(product);
setDetailModalVisible(true);
};
// 打开同步弹窗
const handleOpenSyncModal = (product: CrossPlatformProduct) => {
setSyncingProduct(product);
// 默认选中已上架的店铺
const listedShops = Object.entries(product.platforms)
.filter(([_, info]) => info.status !== 'NOT_LISTED')
.filter(([shopId, info]) => info.shopInfo.apiSupported) // 只选中支持API的店铺
.map(([shopId]) => shopId);
setSelectedSyncShops(listedShops);
syncForm.setFieldsValue({
shops: listedShops,
syncStock: true,
syncPrice: false,
});
setSyncModalVisible(true);
};
// 执行同步
const handleSync = async () => {
if (selectedSyncShops.length === 0) {
message.warning('请至少选择一个目标店铺');
return;
}
const values = syncForm.getFieldsValue();
message.loading(`正在同步到 ${selectedSyncShops.length} 个店铺...`, 2);
await new Promise(resolve => setTimeout(resolve, 2000));
message.success(`同步完成!库存: ${values.syncStock ? '是' : '否'}, 价格: ${values.syncPrice ? '是' : '否'}`);
setSyncModalVisible(false);
fetchProducts();
};
// 批量同步库存
const handleBatchSyncStock = async () => {
if (selectedRows.length === 0) {
message.warning('请先选择商品');
return;
}
Modal.confirm({
title: '批量同步库存',
content: `将对 ${selectedRows.length} 个商品同步库存到所有支持API的店铺`,
onOk: async () => {
message.loading('正在批量同步库存...', 2);
await new Promise(resolve => setTimeout(resolve, 2000));
message.success('批量同步完成');
fetchProducts();
},
});
};
// 获取店铺统计
const getShopStats = () => {
const stats: Record<string, number> = { ALL: products.length };
CURRENT_USER_SHOPS.forEach(shop => {
stats[shop.shopId] = products.filter(p =>
p.platforms[shop.shopId] && p.platforms[shop.shopId].status !== 'NOT_LISTED'
).length;
});
return stats;
};
const shopStats = getShopStats();
const userShopsByPlatform = getUserShopsByPlatform();
// 表格列定义
const columns: ColumnsType<CrossPlatformProduct> = [
{
title: '商品信息',
key: 'productInfo',
width: 300,
render: (_, record) => (
<Space>
<Image src={record.image} width={60} height={60} style={{ objectFit: 'cover' }} />
<div>
<div style={{ fontWeight: 500 }}>{record.name}</div>
<div style={{ fontSize: 12, color: '#999' }}>SKU: {record.sku}</div>
<div style={{ fontSize: 12, color: '#666' }}>{record.category}</div>
</div>
</Space>
),
},
{
title: '本地信息',
key: 'localInfo',
width: 150,
render: (_, record) => (
<div>
<div>价格: ${record.basePrice.toFixed(2)}</div>
<div>: {record.baseStock}</div>
<div>: {record.totalSales}</div>
</div>
),
},
// 为每个用户店铺生成一列
...CURRENT_USER_SHOPS.map(shop => ({
title: (
<Tooltip title={`${shop.shopName} (${shop.region})`}>
<div style={{ textAlign: 'center' }}>
<div style={{ color: PLATFORM_CONFIG[shop.platform]?.color }}>
{PLATFORM_CONFIG[shop.platform]?.icon}
</div>
<div style={{ fontSize: 10 }}>{shop.region}</div>
</div>
</Tooltip>
),
key: `shop-${shop.shopId}`,
width: 100,
align: 'center' as const,
render: (_: any, record: CrossPlatformProduct) => {
const platformInfo = record.platforms[shop.shopId];
if (!platformInfo || platformInfo.status === 'NOT_LISTED') {
return <Tag style={{ opacity: 0.5 }}></Tag>;
}
const statusConfig = STATUS_CONFIG[platformInfo.status];
const canManage = shop.apiSupported;
return (
<Dropdown
overlay={
<Menu>
<Menu.Item onClick={() => handleViewDetail(record)}>
<EyeOutlined />
</Menu.Item>
{canManage && platformInfo.status === 'LIVE' && (
<Menu.Item onClick={() => handleOpenSyncModal(record)}>
<SyncOutlined /> /
</Menu.Item>
)}
{canManage && platformInfo.status === 'LIVE' && (
<Menu.Item onClick={() => message.info('打开价格调整')}>
<EditOutlined />
</Menu.Item>
)}
{canManage && platformInfo.status === 'LIVE' && (
<Menu.Item danger onClick={() => message.info('执行下架')}>
<CloseCircleOutlined />
</Menu.Item>
)}
{canManage && platformInfo.status === 'OFFLINE' && (
<Menu.Item onClick={() => message.info('执行上架')}>
<CheckCircleOutlined />
</Menu.Item>
)}
{!canManage && (
<Menu.Item disabled>
<ExclamationCircleOutlined /> API管理
</Menu.Item>
)}
{platformInfo.listingUrl && (
<Menu.Item>
<a href={platformInfo.listingUrl} target="_blank" rel="noopener noreferrer">
<LinkOutlined />
</a>
</Menu.Item>
)}
</Menu>
}
>
<Tag
color={statusConfig.color}
style={{
cursor: 'pointer',
opacity: canManage ? 1 : 0.5,
}}
>
{statusConfig.text}
{!canManage && <span style={{ fontSize: 10 }}>*</span>}
</Tag>
</Dropdown>
);
},
})),
{
title: '操作',
key: 'action',
width: 180,
fixed: 'right',
render: (_, record) => (
<Space size="small">
<Button type="link" size="small" icon={<EyeOutlined />} onClick={() => handleViewDetail(record)}>
</Button>
<Button type="link" size="small" icon={<SyncOutlined />} onClick={() => handleOpenSyncModal(record)}>
</Button>
</Space>
),
},
];
return (
<div className="cross-platform-manage">
<Alert
message="跨平台商品管理说明"
description={
<div>
<p> 线</p>
<p> * API管理</p>
<p> API的店铺</p>
<p> {CURRENT_USER_SHOPS.length} </p>
</div>
}
type="info"
showIcon
style={{ marginBottom: 16 }}
/>
<Card>
<Row gutter={[16, 16]} style={{ marginBottom: 16 }}>
<Col span={24}>
<Space style={{ display: 'flex', justifyContent: 'space-between', width: '100%' }}>
<Space>
<Title level={5} style={{ margin: 0 }}></Title>
<Badge count={filteredProducts.length} showZero style={{ backgroundColor: '#1890ff' }} />
</Space>
<Space>
<Search
placeholder="搜索商品名称或SKU"
allowClear
onSearch={setSearchText}
style={{ width: 250 }}
/>
<Select
value={selectedShop}
onChange={setSelectedShop}
style={{ width: 200 }}
placeholder="选择店铺"
>
<Option value="ALL"> ({shopStats.ALL})</Option>
{Object.entries(userShopsByPlatform).map(([platform, shops]) => (
<OptGroup key={platform} label={PLATFORM_CONFIG[platform]?.name || platform}>
{shops.map(shop => (
<Option key={shop.shopId} value={shop.shopId}>
<Space>
{shop.shopName}
{!shop.apiSupported && <Tag color="warning">No API</Tag>}
<span style={{ color: '#999' }}>({shopStats[shop.shopId] || 0})</span>
</Space>
</Option>
))}
</OptGroup>
))}
</Select>
</Space>
</Space>
</Col>
</Row>
{selectedRows.length > 0 && (
<Alert
message={`已选择 ${selectedRows.length} 个商品`}
type="info"
showIcon
style={{ marginBottom: 16 }}
action={
<Space>
<Button size="small" onClick={handleBatchSyncStock}>
<SyncOutlined />
</Button>
</Space>
}
/>
)}
<Table
columns={columns}
dataSource={filteredProducts}
rowKey="id"
loading={loading}
scroll={{ x: 1500 }}
pagination={{ showSizeChanger: true, showTotal: (total) => `${total} 个商品` }}
rowSelection={{
selectedRowKeys: selectedRows.map(r => r.id),
onChange: (_, rows) => setSelectedRows(rows),
}}
/>
</Card>
{/* 商品详情弹窗 */}
<Modal
title="商品详情"
open={detailModalVisible}
onCancel={() => setDetailModalVisible(false)}
width={900}
footer={[
<Button key="close" onClick={() => setDetailModalVisible(false)}>
</Button>,
]}
>
{selectedProduct && (
<Tabs
defaultActiveKey="info"
items={[
{ key: 'info', label: '基本信息', children: (
<Row gutter={16}>
<Col span={8}>
<Image src={selectedProduct.image} style={{ width: '100%' }} />
</Col>
<Col span={16}>
<Descriptions column={2}>
<Descriptions.Item label="商品名称">{selectedProduct.name}</Descriptions.Item>
<Descriptions.Item label="SKU">{selectedProduct.sku}</Descriptions.Item>
<Descriptions.Item label="分类">{selectedProduct.category}</Descriptions.Item>
<Descriptions.Item label="本地价格">${selectedProduct.basePrice.toFixed(2)}</Descriptions.Item>
<Descriptions.Item label="本地库存">{selectedProduct.baseStock}</Descriptions.Item>
<Descriptions.Item label="总销量">{selectedProduct.totalSales}</Descriptions.Item>
<Descriptions.Item label="创建时间">{selectedProduct.createdAt}</Descriptions.Item>
<Descriptions.Item label="更新时间">{selectedProduct.updatedAt}</Descriptions.Item>
</Descriptions>
<Divider />
<div>
<Text strong></Text>
<p>{selectedProduct.description}</p>
</div>
</Col>
</Row>
)},
{ key: 'shops', label: '店铺分布', children: (
<Table
dataSource={Object.entries(selectedProduct.platforms)}
rowKey="[0]"
pagination={false}
columns={[
{
title: '店铺',
render: ([shopId, info]: [string, any]) => (
<Space>
{PLATFORM_CONFIG[info.shopInfo.platform]?.icon}
<span>{info.shopInfo.shopName}</span>
<Tag>{info.shopInfo.region}</Tag>
{!info.shopInfo.apiSupported && <Tag color="warning">No API</Tag>}
</Space>
),
},
{
title: '状态',
render: ([_, info]: [string, any]) => (
<Tag color={STATUS_CONFIG[info.status].color}>
{STATUS_CONFIG[info.status].text}
</Tag>
),
},
{
title: '平台SKU',
render: ([_, info]: [string, any]) => info.platformSku,
},
{
title: '售价',
render: ([_, info]: [string, any]) => `$${info.price.toFixed(2)}`,
},
{
title: '库存',
render: ([_, info]: [string, any]) => info.stock,
},
{
title: '销量',
render: ([_, info]: [string, any]) => info.sales,
},
{
title: '最后同步',
render: ([_, info]: [string, any]) => info.lastSync,
},
{
title: '操作',
render: ([shopId, info]: [string, any]) => (
<Space>
{info.shopInfo.apiSupported && (
<Button
type="link"
size="small"
onClick={() => handleOpenSyncModal(selectedProduct)}
>
</Button>
)}
{info.listingUrl && (
<a href={info.listingUrl} target="_blank" rel="noopener noreferrer">
<LinkOutlined />
</a>
)}
</Space>
),
},
]}
/>
)},
]}
/>
)}
</Modal>
{/* 同步弹窗 */}
<Modal
title="同步商品"
open={syncModalVisible}
onCancel={() => setSyncModalVisible(false)}
onOk={handleSync}
width={600}
>
{syncingProduct && (
<Form form={syncForm} layout="vertical">
<Alert
message={`正在同步: ${syncingProduct.name}`}
description={`SKU: ${syncingProduct.sku}`}
type="info"
showIcon
style={{ marginBottom: 16 }}
/>
<Form.Item
name="shops"
label="选择目标店铺"
rules={[{ required: true, message: '请至少选择一个店铺' }]}
>
<Select
mode="multiple"
placeholder="选择要同步的店铺"
onChange={setSelectedSyncShops}
>
{Object.entries(syncingProduct.platforms)
.filter(([_, info]) => info.status !== 'NOT_LISTED')
.filter(([_, info]) => info.shopInfo.apiSupported)
.map(([shopId, info]) => (
<Option key={shopId} value={shopId}>
<Space>
{PLATFORM_CONFIG[info.shopInfo.platform]?.icon}
{info.shopInfo.shopName}
<Tag>{info.shopInfo.region}</Tag>
</Space>
</Option>
))}
</Select>
</Form.Item>
<Form.Item
name="syncStock"
label="同步库存"
valuePropName="checked"
>
<Switch checkedChildren="是" unCheckedChildren="否" />
</Form.Item>
<Form.Item
name="syncPrice"
label="同步价格"
valuePropName="checked"
>
<Switch checkedChildren="是" unCheckedChildren="否" />
</Form.Item>
<Alert
message="同步说明"
description="只支持同步到拥有API权限的店铺。不支持API的店铺需要手动操作。"
type="warning"
showIcon
/>
</Form>
)}
</Modal>
</div>
);
};
// 添加 OptGroup 导入
const { OptGroup } = Select;
export default CrossPlatformManage;