import { useState, useEffect, useCallback, useMemo, useNavigate, FC, Card, Table, Button, Space, Tag, Input, InputNumber, Select, Row, Col, Modal, message, Tooltip, Badge, Dropdown, Menu, Image, Typography, DatePicker, Form, Drawer, Alert, Spin, Tabs, Statistic, PlusOutlined, EditOutlined, DeleteOutlined, SyncOutlined, UploadOutlined, FilterOutlined, SortAscendingOutlined, SortDescendingOutlined, MoreOutlined, EyeOutlined, CopyOutlined, ArrowUpOutlined, ArrowDownOutlined, DollarOutlined, ShoppingOutlined, CheckCircleOutlined, CloseCircleOutlined, ExclamationCircleOutlined, AmazonOutlined, GlobalOutlined, ShopOutlined, AppstoreOutlined, Title, Text, Option, RangePicker, Search, ColumnsType, TablePaginationConfig, FilterValue, SorterResult, TableCurrentDataSource, moment, productDataSource, Product, Shop, ProductFilter, ProductSort, PLATFORMS, } from '@/imports'; import { LoadingState, EmptyState } from '../../components/ui'; const CATEGORIES = ['工业自动化', '电子元器件', '工具设备', '仪器仪表', '安防设备']; const STATUS_CONFIG: Record = { DRAFT: { color: 'default', text: '草稿', icon: }, PRICED: { color: 'processing', text: '已定价', icon: }, LISTED: { color: 'warning', text: '已上架', icon: }, SYNCING: { color: 'processing', text: '同步中', icon: }, LIVE: { color: 'success', text: '已在线', icon: }, SYNC_FAILED: { color: 'error', text: '同步失败', icon: }, OFFLINE: { color: 'default', text: '已下架', icon: }, }; const PLATFORM_CONFIG: Record = { // TikTok系列 TikTok: { icon: , color: '#000000' }, TikTokFull: { icon: , color: '#000000' }, // Shopee系列 Shopee: { icon: , color: '#ee4d2d' }, ShopeeFull: { icon: , color: '#ee4d2d' }, ShopeeLight: { icon: , color: '#ee4d2d' }, // Lazada系列 Lazada: { icon: , color: '#0F156D' }, LazadaFull: { icon: , color: '#0F156D' }, // Temu TemuFull: { icon: , color: '#96bf48' }, // SHEIN系列 Shein: { icon: , color: '#FF6B6B' }, SheinHalf: { icon: , color: '#FF6B6B' }, // 其他平台 Ozon: { icon: , color: '#FFD700' }, Yandex: { icon: , color: '#FF0000' }, AliExpress: { icon: , color: '#FF6A00' }, AliExpressHalf: { icon: , color: '#FF6A00' }, AliExpressPop: { icon: , color: '#FF6A00' }, Coupang: { icon: , color: '#FF3B30' }, Walmart: { icon: , color: '#007DC6' }, Wildberries: { icon: , color: '#9370DB' }, Allegro: { icon: , color: '#00A870' }, MercadoLibre: { icon: , color: '#6C3483' }, Jumia: { icon: , color: '#FF6B00' }, Joom: { icon: , color: '#8A2BE2' }, Amazon: { icon: , color: '#ff9900' }, Wish: { icon: , color: '#4A90E2' }, Emag: { icon: , color: '#00B140' }, Miravia: { icon: , color: '#FF69B4' }, Daraz: { icon: , color: '#FF5722' }, Joybuy: { icon: , color: '#E74C3C' }, Alibaba: { icon: , color: '#FF6A00' }, Qoo10: { icon: , color: '#FF4500' }, Shopify: { icon: , color: '#96bf48' }, Shoplazza: { icon: , color: '#3498DB' }, ShopyyV1: { icon: , color: '#9370DB' }, ShopyyV2: { icon: , color: '#9370DB' }, Shopline: { icon: , color: '#27AE60' }, GreatBoss: { icon: , color: '#3498DB' }, Other: { icon: , color: '#999999' }, // 原有平台 eBay: { icon: , color: '#e53238' }, }; const ProductList: FC = () => { const navigate = useNavigate(); const [products, setProducts] = useState([]); const [loading, setLoading] = useState(false); const [selectedRows, setSelectedRows] = useState([]); const [filterVisible, setFilterVisible] = useState(false); const [sortDrawerVisible, setSortDrawerVisible] = useState(false); const [pricingModalVisible, setPricingModalVisible] = useState(false); const [columnDrawerVisible, setColumnDrawerVisible] = useState(false); const [currentProduct, setCurrentProduct] = useState(null); const [syncLoading, setSyncLoading] = useState>({}); const [activePlatformTab, setActivePlatformTab] = useState('all'); const [shops, setShops] = useState([]); const [platformStats, setPlatformStats] = useState>({ all: { total: 0, live: 0, pending: 0, failed: 0 }, unpublished: { total: 0, live: 0, pending: 0, failed: 0 }, }); const [filters, setFilters] = useState({ keyword: '', status: [], platform: [], shop: [], category: [], priceRange: undefined, stockRange: undefined, roiRange: undefined, dateRange: undefined, minProfit: undefined, minStock: undefined, searchInDescription: false, }); const [sort, setSort] = useState({ field: 'updatedAt', order: 'descend', }); const [visibleColumns, setVisibleColumns] = useState([ 'name', 'price', 'costPrice', 'profit', 'roi', 'stock', 'status', 'platformStatus', 'updatedAt', 'action' ]); const [columnOrder, setColumnOrder] = useState([ 'name', 'price', 'costPrice', 'profit', 'roi', 'stock', 'status', 'platformStatus', 'updatedAt', 'action' ]); useEffect(() => { fetchInitialData(); }, []); useEffect(() => { fetchProducts(); }, [filters, sort, activePlatformTab]); const fetchInitialData = async () => { try { const [shopsData, stats] = await Promise.all([ productDataSource.fetchShops(), productDataSource.getPlatformStats() ]); setShops(shopsData); setPlatformStats(stats); } catch (error) { console.error('Failed to fetch initial data:', error); } }; const fetchProducts = useCallback(async () => { setLoading(true); try { const productFilter: ProductFilter = { ...filters, platform: activePlatformTab !== 'all' && activePlatformTab !== 'unpublished' ? [activePlatformTab] : filters.platform }; const productsData = await productDataSource.fetchProducts(productFilter, sort); setProducts(productsData); // 更新平台统计数据 const stats = await productDataSource.getPlatformStats(); setPlatformStats(stats); } catch (error) { console.error('Failed to fetch products:', error); message.error('Failed to load products'); } finally { setLoading(false); } }, [filters, sort, activePlatformTab]); const handleFilterChange = (key: keyof ProductFilter, value: any) => { setFilters(prev => ({ ...prev, [key]: value })); }; const handleSortChange = (field: string, order: 'ascend' | 'descend') => { setSort({ field, order }); setSortDrawerVisible(false); message.success(`已按 ${field} ${order === 'ascend' ? '升序' : '降序'} 排序`); }; const handleTableChange = ( pagination: TablePaginationConfig, filters: Record, sorter: SorterResult | SorterResult[], extra: TableCurrentDataSource ) => { if (!Array.isArray(sorter) && sorter.field) { setSort({ field: sorter.field as string, order: sorter.order || null, }); } }; const handleSearch = (value: string) => { handleFilterChange('keyword', value); }; const handleResetFilters = () => { setFilters({ keyword: '', status: [], platform: [], shop: [], category: [], priceRange: undefined, stockRange: undefined, roiRange: undefined, dateRange: undefined, minProfit: undefined, minStock: undefined, searchInDescription: false, }); message.success('筛选条件已重置'); }; const handleColumnVisibilityChange = (columnKey: string, visible: boolean) => { if (visible) { setVisibleColumns([...visibleColumns, columnKey]); } else { setVisibleColumns(visibleColumns.filter(key => key !== columnKey)); } }; const handleColumnOrderChange = (newOrder: string[]) => { setColumnOrder(newOrder); }; const handleAddProduct = () => { navigate('/dashboard/product/publish'); }; const handleEditProduct = (record: Product) => { navigate(`/dashboard/product/detail/${record.id}`); }; const handleViewProduct = (record: Product) => { navigate(`/dashboard/product/detail/${record.id}`); }; const handleDeleteProduct = async (record: Product) => { Modal.confirm({ title: '确认删除', content: `确定要删除商品 "${record.name}" 吗?此操作不可恢复。`, okText: '删除', okType: 'danger', onOk: async () => { try { const success = await productDataSource.deleteProduct(record.id); if (success) { message.success('商品删除成功'); fetchProducts(); } else { message.error('删除失败'); } } catch (error) { message.error('删除失败'); } }, }); }; const handleDuplicateProduct = async (record: Product) => { try { await productDataSource.duplicateProduct(record.id); message.success('商品复制成功'); fetchProducts(); } catch (error) { message.error('复制失败'); } }; const handlePricing = (record: Product) => { setCurrentProduct(record); setPricingModalVisible(true); }; const handlePublish = async (record: Product) => { Modal.confirm({ title: '确认上架', content: `确定要上架商品 "${record.name}" 吗?`, onOk: async () => { try { await productDataSource.updateProductStatus(record.id, 'LISTED'); message.success('商品上架成功'); fetchProducts(); } catch (error) { message.error('上架失败'); } }, }); }; const handleSync = async (record: Product) => { setSyncLoading(prev => ({ ...prev, [record.id]: true })); message.loading({ content: '正在同步到平台...', key: record.id }); try { await productDataSource.syncProduct(record.id); message.success({ content: '同步完成', key: record.id }); fetchProducts(); } catch (error) { message.error({ content: '同步失败,请重试', key: record.id }); } finally { setSyncLoading(prev => ({ ...prev, [record.id]: false })); } }; const handleOffline = async (record: Product) => { Modal.confirm({ title: '确认下架', content: `确定要下架商品 "${record.name}" 吗?`, onOk: async () => { try { await productDataSource.updateProductStatus(record.id, 'OFFLINE'); message.success('商品已下架'); fetchProducts(); } catch (error) { message.error('下架失败'); } }, }); }; const handleRetrySync = (record: Product) => { handleSync(record); }; const handleBatchDelete = () => { if (selectedRows.length === 0) { message.warning('请先选择要删除的商品'); return; } Modal.confirm({ title: '确认批量删除', content: `确定要删除选中的 ${selectedRows.length} 个商品吗?`, okText: '删除', okType: 'danger', onOk: async () => { try { const promises = selectedRows.map(r => productDataSource.deleteProduct(r.id)); await Promise.all(promises); message.success(`成功删除 ${selectedRows.length} 个商品`); setSelectedRows([]); fetchProducts(); } catch (error) { message.error('批量删除失败'); } }, }); }; const handleBatchPricing = () => { if (selectedRows.length === 0) { message.warning('请先选择要定价的商品'); return; } message.info(`批量定价功能:已选择 ${selectedRows.length} 个商品`); }; const handleBatchPublish = async () => { if (selectedRows.length === 0) { message.warning('请先选择要上架的商品'); return; } const pricedProducts = selectedRows.filter(p => p.status === 'PRICED'); if (pricedProducts.length === 0) { message.warning('选中的商品中没有已定价的商品'); return; } Modal.confirm({ title: '确认批量上架', content: `确定要上架选中的 ${pricedProducts.length} 个商品吗?`, onOk: async () => { try { const promises = pricedProducts.map(p => productDataSource.updateProductStatus(p.id, 'LISTED')); await Promise.all(promises); message.success(`成功上架 ${pricedProducts.length} 个商品`); setSelectedRows([]); fetchProducts(); } catch (error) { message.error('批量上架失败'); } }, }); }; const handleBatchSync = async () => { if (selectedRows.length === 0) { message.warning('请先选择要同步的商品'); return; } const listableProducts = selectedRows.filter(p => p.status === 'LISTED' || p.status === 'LIVE'); if (listableProducts.length === 0) { message.warning('选中的商品中没有可同步的商品'); return; } message.loading('正在批量同步...'); try { const promises = listableProducts.map(p => productDataSource.syncProduct(p.id)); await Promise.all(promises); message.success('批量同步完成'); setSelectedRows([]); fetchProducts(); } catch (error) { message.error('批量同步失败'); } }; const handleBatchUpdate = () => { if (selectedRows.length === 0) { message.warning('请先选择要更新的商品'); return; } message.info(`批量更新功能:已选择 ${selectedRows.length} 个商品`); // 这里可以打开批量更新模态框,实现批量更新商品信息 }; const handleBatchExport = () => { if (selectedRows.length === 0) { message.warning('请先选择要导出的商品'); return; } message.success(`成功导出 ${selectedRows.length} 个商品数据`); // 这里可以实现导出商品数据为Excel或CSV文件 }; const sortedProducts = products; const allColumns: ColumnsType = [ { title: '商品信息', dataIndex: 'name', key: 'name', render: (text, record) => (
{text}
SKU: {record.sku}
{record.category}
), sorter: true, }, { title: '售价', dataIndex: 'price', key: 'price', render: (value) => ${value.toFixed(2)}, sorter: true, }, { title: '成本', dataIndex: 'costPrice', key: 'costPrice', render: (value) => ${value.toFixed(2)}, sorter: true, }, { title: '利润', dataIndex: 'profit', key: 'profit', render: (value) => ${value.toFixed(2)}, sorter: true, }, { title: 'ROI', dataIndex: 'roi', key: 'roi', render: (value) => {value.toFixed(2)}%, sorter: true, }, { title: '库存', dataIndex: 'stock', key: 'stock', render: (value) => ( 50 ? '#52c41a' : value > 0 ? '#faad14' : '#ff4d4f' }} /> ), sorter: true, }, { title: '状态', dataIndex: 'status', key: 'status', render: (status) => { const config = STATUS_CONFIG[status]; return ( {config.text} ); }, filters: Object.entries(STATUS_CONFIG).map(([key, config]) => ({ text: config.text, value: key, })), }, { title: '平台状态', key: 'platformStatus', render: (_, record) => ( {Object.entries(record.platformStatus).map(([platform, status]) => ( {platform.charAt(0)} ))} ), }, { title: '更新时间', dataIndex: 'updatedAt', key: 'updatedAt', sorter: true, }, { title: '操作', key: 'action', fixed: 'right', width: 200, render: (_, record) => { const menu = ( } onClick={() => handleViewProduct(record)}> 查看详情 } onClick={() => handleEditProduct(record)}> 编辑 } onClick={() => handleDuplicateProduct(record)}> 复制 } onClick={() => handlePricing(record)}> 定价 {record.status === 'PRICED' && ( } onClick={() => handlePublish(record)}> 上架 )} {(record.status === 'LISTED' || record.status === 'LIVE') && ( } onClick={() => handleSync(record)} disabled={syncLoading[record.id]} > 同步到平台 )} {record.status === 'SYNC_FAILED' && ( } onClick={() => handleRetrySync(record)}> 重试同步 )} {(record.status === 'LISTED' || record.status === 'LIVE') && ( } onClick={() => handleOffline(record)}> 下架 )} } danger onClick={() => handleDeleteProduct(record)}> 删除 ); return ( ); }, }, ]; const columns = columnOrder .filter(key => visibleColumns.includes(key)) .map(key => allColumns.find(col => col.key === key)) .filter(Boolean) as ColumnsType; const rowSelection = { selectedRowKeys: selectedRows.map(r => r.id), onChange: (selectedRowKeys: React.Key[], selectedRows: Product[]) => { setSelectedRows(selectedRows); }, }; return (

1. 新建商品:先保存到本地系统(草稿状态)

2. 定价上架:完成定价后上架到本地(已上架状态)

3. 平台同步:手动或自动同步到各销售平台(同步中 → 已在线)

所有商品数据以本地系统为准,平台数据通过同步机制保持一致

} type="info" showIcon style={{ marginBottom: 16 }} /> } > 全部 ({platformStats.all?.total || 0}) } key="all" /> {PLATFORMS.map(platform => { const config = PLATFORM_CONFIG[platform]; const stat = platformStats[platform] || { total: 0, live: 0, pending: 0, failed: 0 }; return ( {config.icon} {platform} ({stat.total}) {stat.failed > 0 && ( )} } key={platform} /> ); })} 未发布 ({platformStats.unpublished?.total || 0}) } key="unpublished" /> {activePlatformTab !== 'all' && activePlatformTab !== 'unpublished' && ( } /> } /> } /> } /> )} {activePlatformTab === 'all' ? '全部商品' : activePlatformTab === 'unpublished' ? '未发布商品' : `${activePlatformTab} 商品`} {selectedRows.length > 0 && ( } style={{ marginBottom: 16 }} /> )} {loading ? ( ) : sortedProducts.length === 0 ? ( ) : ( `共 ${total} 条`, }} /> )} setFilterVisible(false)} open={filterVisible} width={400} >
{ handleFilterChange('priceRange', filters.priceRange ? [value || 0, filters.priceRange[1]] : [value || 0, 999999]); }} style={{ width: '100%' }} /> - { handleFilterChange('priceRange', filters.priceRange ? [filters.priceRange[0], value || 999999] : [0, value || 999999]); }} style={{ width: '100%' }} /> { handleFilterChange('stockRange', filters.stockRange ? [value || 0, filters.stockRange[1]] : [value || 0, 999999]); }} style={{ width: '100%' }} /> - { handleFilterChange('stockRange', filters.stockRange ? [filters.stockRange[0], value || 999999] : [0, value || 999999]); }} style={{ width: '100%' }} /> { handleFilterChange('roiRange', filters.roiRange ? [value || 0, filters.roiRange[1]] : [value || 0, 999999]); }} style={{ width: '100%' }} /> - { handleFilterChange('roiRange', filters.roiRange ? [filters.roiRange[0], value || 999999] : [0, value || 999999]); }} style={{ width: '100%' }} /> handleFilterChange('minProfit', value)} style={{ width: '100%' }} /> handleFilterChange('minStock', value)} style={{ width: '100%' }} /> handleFilterChange('dateRange', dates)} style={{ width: '100%' }} />
handleFilterChange('searchInDescription', e.target.checked)} style={{ marginRight: 8 }} />
setSortDrawerVisible(false)} open={sortDrawerVisible} width={300} > {[ { key: 'name', label: '商品名称' }, { key: 'price', label: '售价' }, { key: 'profit', label: '利润' }, { key: 'roi', label: 'ROI' }, { key: 'stock', label: '库存' }, { key: 'updatedAt', label: '更新时间' }, ].map(item => (
{item.label}
))}
setColumnDrawerVisible(false)} open={columnDrawerVisible} width={400} >

显示列

{ [ { key: 'name', label: '商品信息' }, { key: 'price', label: '售价' }, { key: 'costPrice', label: '成本' }, { key: 'profit', label: '利润' }, { key: 'roi', label: 'ROI' }, { key: 'stock', label: '库存' }, { key: 'status', label: '状态' }, { key: 'platformStatus', label: '平台状态' }, { key: 'updatedAt', label: '更新时间' }, { key: 'action', label: '操作' }, ].map(item => (
handleColumnVisibilityChange(item.key, e.target.checked)} style={{ marginRight: 12 }} /> {item.label}
)) }

列顺序

拖拽调整列的显示顺序

{columnOrder.map(key => { const columnInfo = [ { key: 'name', label: '商品信息' }, { key: 'price', label: '售价' }, { key: 'costPrice', label: '成本' }, { key: 'profit', label: '利润' }, { key: 'roi', label: 'ROI' }, { key: 'stock', label: '库存' }, { key: 'status', label: '状态' }, { key: 'platformStatus', label: '平台状态' }, { key: 'updatedAt', label: '更新时间' }, { key: 'action', label: '操作' }, ].find(item => item.key === key); return (
{columnInfo?.label}
); })}
setPricingModalVisible(false)} footer={null} width={600} > {currentProduct && (
{currentProduct.name} ${currentProduct.price.toFixed(2)} )}
); }; export default ProductList;