refactor: 重构项目结构并优化代码

- 删除无用的文件和错误日志
- 创建统一的 imports 模块集中管理依赖
- 重构组件使用新的 imports 方式
- 修复文档路径大小写问题
- 优化类型定义和接口导出
- 更新依赖版本
- 改进错误处理和API配置
- 统一组件导出方式
This commit is contained in:
2026-03-27 16:56:06 +08:00
parent 2748456d8a
commit 22308fe042
337 changed files with 37060 additions and 57483 deletions

View File

@@ -1,6 +1,9 @@
import React, { useState, useEffect, useCallback, useMemo } from 'react';
import { useNavigate } from 'react-router-dom';
import {
useState,
useEffect,
useCallback,
useMemo,
useNavigate,
Card,
Table,
Button,
@@ -26,8 +29,6 @@ import {
Spin,
Tabs,
Statistic,
} from 'antd';
import {
PlusOutlined,
EditOutlined,
DeleteOutlined,
@@ -50,66 +51,27 @@ import {
GlobalOutlined,
ShopOutlined,
AppstoreOutlined,
} from '@ant-design/icons';
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';
import type { ColumnsType, TablePaginationConfig } from 'antd/es/table';
import type { FilterValue, SorterResult, TableCurrentDataSource } from 'antd/es/table/interface';
import moment from 'moment';
const { Title, Text } = Typography;
const { Option } = Select;
const { RangePicker } = DatePicker;
const { Search } = Input;
interface Product {
id: string;
sku: string;
name: string;
image: string;
category: string;
price: number;
costPrice: number;
profit: number;
roi: number;
stock: number;
status: 'DRAFT' | 'PRICED' | 'LISTED' | 'SYNCING' | 'LIVE' | 'SYNC_FAILED' | 'OFFLINE';
platformStatus: Record<string, string>;
shopId: string;
shopName: string;
platform: string;
createdAt: string;
updatedAt: string;
}
interface Shop {
id: string;
name: string;
platform: string;
status: 'ACTIVE' | 'INACTIVE' | 'SUSPENDED';
region: string;
currency: string;
}
interface FilterState {
keyword: string;
status: string[];
platform: string[];
shop: string[];
category: string[];
priceRange: [number, number] | null;
stockRange: [number, number] | null;
roiRange: [number, number] | null;
dateRange: any;
minProfit: number | null;
minStock: number | null;
searchInDescription: boolean;
}
interface SortState {
field: string;
order: 'ascend' | 'descend' | null;
}
const CATEGORIES = ['工业自动化', '电子元器件', '工具设备', '仪器仪表', '安防设备'];
const STATUS_CONFIG: Record<string, { color: string; text: string; icon: React.ReactNode }> = {
DRAFT: { color: 'default', text: '草稿', icon: <EditOutlined /> },
@@ -121,86 +83,6 @@ const STATUS_CONFIG: Record<string, { color: string; text: string; icon: React.R
OFFLINE: { color: 'default', text: '已下架', icon: <ExclamationCircleOutlined /> },
};
const PLATFORMS = [
// TikTok系列
'TikTok', 'TikTokFull',
// Shopee系列
'Shopee', 'ShopeeFull', 'ShopeeLight',
// Lazada系列
'Lazada', 'LazadaFull',
// Temu
'TemuFull',
// SHEIN系列
'Shein', 'SheinHalf',
// 其他平台
'Ozon', 'Yandex', 'AliExpress', 'AliExpressHalf', 'AliExpressPop',
'Coupang', 'Walmart', 'Wildberries', 'Allegro', 'MercadoLibre',
'Jumia', 'Joom', 'Amazon', 'Wish', 'Emag', 'Miravia',
'Daraz', 'Joybuy', 'Alibaba', 'Qoo10', 'Shopify', 'Shoplazza',
'ShopyyV1', 'ShopyyV2', 'Shopline', 'GreatBoss', 'Other',
// 原有平台
'eBay',
];
const CATEGORIES = ['工业自动化', '电子元器件', '工具设备', '仪器仪表', '安防设备'];
// 店铺数据
const SHOPS: Shop[] = [
// TikTok平台 - 多个店铺
{ id: 'shop-tiktok-1', name: 'TikTok旗舰店A', platform: 'TikTok', status: 'ACTIVE', region: '中国', currency: 'CNY' },
{ id: 'shop-tiktok-2', name: 'TikTok旗舰店B', platform: 'TikTok', status: 'ACTIVE', region: '美国', currency: 'USD' },
{ id: 'shop-tiktok-3', name: 'TikTok精品店C', platform: 'TikTok', status: 'ACTIVE', region: '欧洲', currency: 'EUR' },
{ id: 'shop-tiktok-4', name: 'TikTok精品店D', platform: 'TikTok', status: 'INACTIVE', region: '东南亚', currency: 'SGD' },
// Shopee平台 - 多个店铺
{ id: 'shop-shopee-1', name: 'Shopee官方店A', platform: 'Shopee', status: 'ACTIVE', region: '东南亚', currency: 'SGD' },
{ id: 'shop-shopee-2', name: 'Shopee官方店B', platform: 'Shopee', status: 'ACTIVE', region: '台湾', currency: 'TWD' },
{ id: 'shop-shopee-3', name: 'Shopee旗舰店C', platform: 'Shopee', status: 'ACTIVE', region: '马来西亚', currency: 'MYR' },
{ id: 'shop-shopee-4', name: 'Shopee旗舰店D', platform: 'Shopee', status: 'SUSPENDED', region: '菲律宾', currency: 'PHP' },
// Lazada平台 - 多个店铺
{ id: 'shop-lazada-1', name: 'Lazada官方店A', platform: 'Lazada', status: 'ACTIVE', region: '东南亚', currency: 'SGD' },
{ id: 'shop-lazada-2', name: 'Lazada官方店B', platform: 'Lazada', status: 'ACTIVE', region: '泰国', currency: 'THB' },
{ id: 'shop-lazada-3', name: 'Lazada旗舰店C', platform: 'Lazada', status: 'ACTIVE', region: '越南', currency: 'VND' },
// Amazon平台 - 多个店铺
{ id: 'shop-amazon-1', name: 'Amazon美国店A', platform: 'Amazon', status: 'ACTIVE', region: '美国', currency: 'USD' },
{ id: 'shop-amazon-2', name: 'Amazon美国店B', platform: 'Amazon', status: 'ACTIVE', region: '美国', currency: 'USD' },
{ id: 'shop-amazon-3', name: 'Amazon欧洲店C', platform: 'Amazon', status: 'ACTIVE', region: '欧洲', currency: 'EUR' },
{ id: 'shop-amazon-4', name: 'Amazon欧洲店D', platform: 'Amazon', status: 'ACTIVE', region: '英国', currency: 'GBP' },
{ id: 'shop-amazon-5', name: 'Amazon日本店E', platform: 'Amazon', status: 'ACTIVE', region: '日本', currency: 'JPY' },
// eBay平台 - 多个店铺
{ id: 'shop-ebay-1', name: 'eBay全球店A', platform: 'eBay', status: 'ACTIVE', region: '全球', currency: 'USD' },
{ id: 'shop-ebay-2', name: 'eBay全球店B', platform: 'eBay', status: 'ACTIVE', region: '欧洲', currency: 'EUR' },
{ id: 'shop-ebay-3', name: 'eBay美国店C', platform: 'eBay', status: 'ACTIVE', region: '美国', currency: 'USD' },
// Shopify平台 - 多个店铺
{ id: 'shop-shopify-1', name: 'Shopify独立站A', platform: 'Shopify', status: 'ACTIVE', region: '全球', currency: 'USD' },
{ id: 'shop-shopify-2', name: 'Shopify独立站B', platform: 'Shopify', status: 'ACTIVE', region: '欧洲', currency: 'EUR' },
{ id: 'shop-shopify-3', name: 'Shopify独立站C', platform: 'Shopify', status: 'ACTIVE', region: '亚洲', currency: 'CNY' },
// Temu平台 - 多个店铺
{ id: 'shop-temu-1', name: 'Temu旗舰店A', platform: 'Temu', status: 'ACTIVE', region: '美国', currency: 'USD' },
{ id: 'shop-temu-2', name: 'Temu旗舰店B', platform: 'Temu', status: 'ACTIVE', region: '欧洲', currency: 'EUR' },
{ id: 'shop-temu-3', name: 'Temu旗舰店C', platform: 'Temu', status: 'ACTIVE', region: '澳大利亚', currency: 'AUD' },
// SHEIN平台 - 多个店铺
{ id: 'shop-shein-1', name: 'SHEIN官方店A', platform: 'Shein', status: 'ACTIVE', region: '全球', currency: 'USD' },
{ id: 'shop-shein-2', name: 'SHEIN官方店B', platform: 'Shein', status: 'ACTIVE', region: '欧洲', currency: 'EUR' },
{ id: 'shop-shein-3', name: 'SHEIN官方店C', platform: 'Shein', status: 'ACTIVE', region: '中东', currency: 'AED' },
// 其他平台店铺
{ id: 'shop-ozon-1', name: 'Ozon旗舰店A', platform: 'Ozon', status: 'ACTIVE', region: '俄罗斯', currency: 'RUB' },
{ id: 'shop-ali-1', name: '速卖通旗舰店A', platform: 'AliExpress', status: 'ACTIVE', region: '全球', currency: 'USD' },
{ id: 'shop-walmart-1', name: 'Walmart旗舰店A', platform: 'Walmart', status: 'ACTIVE', region: '美国', currency: 'USD' },
];
const PLATFORM_CONFIG: Record<string, { icon: React.ReactNode; color: string }> = {
// TikTok系列
TikTok: { icon: <GlobalOutlined />, color: '#000000' },
@@ -255,143 +137,7 @@ const PLATFORM_CONFIG: Record<string, { icon: React.ReactNode; color: string }>
eBay: { icon: <GlobalOutlined />, color: '#e53238' },
};
const MOCK_PRODUCTS: Product[] = [
{
id: '1',
sku: 'TP-TEMP-001',
name: '工业温度传感器 Pro',
image: 'https://via.placeholder.com/80x80?text=Product',
category: '工业自动化',
price: 89.99,
costPrice: 45.00,
profit: 44.99,
roi: 99.98,
stock: 256,
status: 'LIVE',
platformStatus: { Amazon: 'LIVE', eBay: 'LIVE', Shopee: 'PENDING' },
shopId: 'shop-amazon-1',
shopName: 'Amazon美国店',
platform: 'Amazon',
createdAt: '2025-12-15',
updatedAt: '2026-03-18',
},
{
id: '2',
sku: 'TP-PRES-002',
name: '压力传感器 Digital',
image: 'https://via.placeholder.com/80x80?text=Product',
category: '工业自动化',
price: 129.99,
costPrice: 65.00,
profit: 64.99,
roi: 99.98,
stock: 128,
status: 'DRAFT',
platformStatus: {},
shopId: 'shop-tiktok-1',
shopName: 'TikTok旗舰店',
platform: 'TikTok',
createdAt: '2026-03-10',
updatedAt: '2026-03-10',
},
{
id: '3',
sku: 'TP-FLOW-003',
name: '流量计 Ultra',
image: 'https://via.placeholder.com/80x80?text=Product',
category: '仪器仪表',
price: 299.99,
costPrice: 150.00,
profit: 149.99,
roi: 99.99,
stock: 64,
status: 'PRICED',
platformStatus: {},
shopId: 'shop-shopee-1',
shopName: 'Shopee官方店',
platform: 'Shopee',
createdAt: '2026-03-15',
updatedAt: '2026-03-16',
},
{
id: '4',
sku: 'TP-MOTR-004',
name: '步进电机 57型',
image: 'https://via.placeholder.com/80x80?text=Product',
category: '工业自动化',
price: 59.99,
costPrice: 30.00,
profit: 29.99,
roi: 99.97,
stock: 512,
status: 'LISTED',
platformStatus: { Amazon: 'LISTED' },
shopId: 'shop-amazon-2',
shopName: 'Amazon欧洲店',
platform: 'Amazon',
createdAt: '2026-03-01',
updatedAt: '2026-03-17',
},
{
id: '5',
sku: 'TP-CTRL-005',
name: 'PLC控制器 Mini',
image: 'https://via.placeholder.com/80x80?text=Product',
category: '工业自动化',
price: 199.99,
costPrice: 100.00,
profit: 99.99,
roi: 99.99,
stock: 32,
status: 'SYNCING',
platformStatus: { Amazon: 'SYNCING', eBay: 'SYNCING' },
shopId: 'shop-ebay-1',
shopName: 'eBay全球店',
platform: 'eBay',
createdAt: '2026-03-18',
updatedAt: '2026-03-18',
},
{
id: '6',
sku: 'TP-SENS-006',
name: '光电传感器',
image: 'https://via.placeholder.com/80x80?text=Product',
category: '工业自动化',
price: 39.99,
costPrice: 20.00,
profit: 19.99,
roi: 99.95,
stock: 0,
status: 'SYNC_FAILED',
platformStatus: { Amazon: 'FAILED' },
shopId: 'shop-temu-1',
shopName: 'Temu旗舰店',
platform: 'Temu',
createdAt: '2026-03-05',
updatedAt: '2026-03-18',
},
{
id: '7',
sku: 'TP-VALV-007',
name: '电磁阀 24V',
image: 'https://via.placeholder.com/80x80?text=Product',
category: '工具设备',
price: 79.99,
costPrice: 40.00,
profit: 39.99,
roi: 99.98,
stock: 200,
status: 'OFFLINE',
platformStatus: { Amazon: 'OFFLINE', eBay: 'OFFLINE' },
shopId: 'shop-shein-1',
shopName: 'SHEIN官方店',
platform: 'Shein',
createdAt: '2025-11-20',
updatedAt: '2026-02-28',
},
];
export const ProductList: React.FC = () => {
const ProductList: React.FC = () => {
const navigate = useNavigate();
const [products, setProducts] = useState<Product[]>([]);
const [loading, setLoading] = useState(false);
@@ -403,23 +149,28 @@ export const ProductList: React.FC = () => {
const [currentProduct, setCurrentProduct] = useState<Product | null>(null);
const [syncLoading, setSyncLoading] = useState<Record<string, boolean>>({});
const [activePlatformTab, setActivePlatformTab] = useState<string>('all');
const [shops, setShops] = useState<Shop[]>([]);
const [platformStats, setPlatformStats] = useState<Record<string, { total: number; live: number; pending: number; failed: number }>>({
all: { total: 0, live: 0, pending: 0, failed: 0 },
unpublished: { total: 0, live: 0, pending: 0, failed: 0 },
});
const [filters, setFilters] = useState<FilterState>({
const [filters, setFilters] = useState<ProductFilter>({
keyword: '',
status: [],
platform: [],
shop: [],
category: [],
priceRange: null,
stockRange: null,
roiRange: null,
dateRange: null,
minProfit: null,
minStock: null,
priceRange: undefined,
stockRange: undefined,
roiRange: undefined,
dateRange: undefined,
minProfit: undefined,
minStock: undefined,
searchInDescription: false,
});
const [sort, setSort] = useState<SortState>({
const [sort, setSort] = useState<ProductSort>({
field: 'updatedAt',
order: 'descend',
});
@@ -432,17 +183,51 @@ export const ProductList: React.FC = () => {
]);
useEffect(() => {
fetchProducts();
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);
await new Promise(resolve => setTimeout(resolve, 500));
setProducts(MOCK_PRODUCTS);
setLoading(false);
}, []);
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 FilterState, value: any) => {
const handleFilterChange = (key: keyof ProductFilter, value: any) => {
setFilters(prev => ({ ...prev, [key]: value }));
};
@@ -477,12 +262,12 @@ export const ProductList: React.FC = () => {
platform: [],
shop: [],
category: [],
priceRange: null,
stockRange: null,
roiRange: null,
dateRange: null,
minProfit: null,
minStock: null,
priceRange: undefined,
stockRange: undefined,
roiRange: undefined,
dateRange: undefined,
minProfit: undefined,
minStock: undefined,
searchInDescription: false,
});
message.success('筛选条件已重置');
@@ -512,32 +297,36 @@ export const ProductList: React.FC = () => {
navigate(`/dashboard/product/detail/${record.id}`);
};
const handleDeleteProduct = (record: Product) => {
const handleDeleteProduct = async (record: Product) => {
Modal.confirm({
title: '确认删除',
content: `确定要删除商品 "${record.name}" 吗?此操作不可恢复。`,
okText: '删除',
okType: 'danger',
onOk: () => {
setProducts(products.filter(p => p.id !== record.id));
message.success('商品删除成功');
onOk: async () => {
try {
const success = await productDataSource.deleteProduct(record.id);
if (success) {
message.success('商品删除成功');
fetchProducts();
} else {
message.error('删除失败');
}
} catch (error) {
message.error('删除失败');
}
},
});
};
const handleDuplicateProduct = (record: Product) => {
const newProduct: Product = {
...record,
id: `${Date.now()}`,
sku: `${record.sku}-COPY`,
name: `${record.name} (复制)`,
status: 'DRAFT',
platformStatus: {},
createdAt: moment().format('YYYY-MM-DD'),
updatedAt: moment().format('YYYY-MM-DD'),
};
setProducts([newProduct, ...products]);
message.success('商品复制成功');
const handleDuplicateProduct = async (record: Product) => {
try {
await productDataSource.duplicateProduct(record.id);
message.success('商品复制成功');
fetchProducts();
} catch (error) {
message.error('复制失败');
}
};
const handlePricing = (record: Product) => {
@@ -545,13 +334,18 @@ export const ProductList: React.FC = () => {
setPricingModalVisible(true);
};
const handlePublish = (record: Product) => {
const handlePublish = async (record: Product) => {
Modal.confirm({
title: '确认上架',
content: `确定要上架商品 "${record.name}" 吗?`,
onOk: () => {
updateProductStatus(record.id, 'LISTED');
message.success('商品上架成功');
onOk: async () => {
try {
await productDataSource.updateProductStatus(record.id, 'LISTED');
message.success('商品上架成功');
fetchProducts();
} catch (error) {
message.error('上架失败');
}
},
});
};
@@ -559,30 +353,31 @@ export const ProductList: React.FC = () => {
const handleSync = async (record: Product) => {
setSyncLoading(prev => ({ ...prev, [record.id]: true }));
updateProductStatus(record.id, 'SYNCING');
message.loading({ content: '正在同步到平台...', key: record.id });
await new Promise(resolve => setTimeout(resolve, 2000));
const success = Math.random() > 0.3;
if (success) {
updateProductStatus(record.id, 'LIVE');
message.success({ content: '同步成功', key: record.id });
} else {
updateProductStatus(record.id, 'SYNC_FAILED');
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 }));
}
setSyncLoading(prev => ({ ...prev, [record.id]: false }));
};
const handleOffline = (record: Product) => {
const handleOffline = async (record: Product) => {
Modal.confirm({
title: '确认下架',
content: `确定要下架商品 "${record.name}" 吗?`,
onOk: () => {
updateProductStatus(record.id, 'OFFLINE');
message.success('商品已下架');
onOk: async () => {
try {
await productDataSource.updateProductStatus(record.id, 'OFFLINE');
message.success('商品已下架');
fetchProducts();
} catch (error) {
message.error('下架失败');
}
},
});
};
@@ -591,12 +386,6 @@ export const ProductList: React.FC = () => {
handleSync(record);
};
const updateProductStatus = (productId: string, status: Product['status']) => {
setProducts(products.map(p =>
p.id === productId ? { ...p, status, updatedAt: moment().format('YYYY-MM-DD') } : p
));
};
const handleBatchDelete = () => {
if (selectedRows.length === 0) {
message.warning('请先选择要删除的商品');
@@ -607,11 +396,16 @@ export const ProductList: React.FC = () => {
content: `确定要删除选中的 ${selectedRows.length} 个商品吗?`,
okText: '删除',
okType: 'danger',
onOk: () => {
const ids = selectedRows.map(r => r.id);
setProducts(products.filter(p => !ids.includes(p.id)));
setSelectedRows([]);
message.success(`成功删除 ${ids.length} 个商品`);
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('批量删除失败');
}
},
});
};
@@ -624,7 +418,7 @@ export const ProductList: React.FC = () => {
message.info(`批量定价功能:已选择 ${selectedRows.length} 个商品`);
};
const handleBatchPublish = () => {
const handleBatchPublish = async () => {
if (selectedRows.length === 0) {
message.warning('请先选择要上架的商品');
return;
@@ -637,15 +431,21 @@ export const ProductList: React.FC = () => {
Modal.confirm({
title: '确认批量上架',
content: `确定要上架选中的 ${pricedProducts.length} 个商品吗?`,
onOk: () => {
pricedProducts.forEach(p => updateProductStatus(p.id, 'LISTED'));
setSelectedRows([]);
message.success(`成功上架 ${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 = () => {
const handleBatchSync = async () => {
if (selectedRows.length === 0) {
message.warning('请先选择要同步的商品');
return;
@@ -656,10 +456,15 @@ export const ProductList: React.FC = () => {
return;
}
message.loading('正在批量同步...');
setTimeout(() => {
listableProducts.forEach(p => handleSync(p));
try {
const promises = listableProducts.map(p => productDataSource.syncProduct(p.id));
await Promise.all(promises);
message.success('批量同步完成');
setSelectedRows([]);
}, 500);
fetchProducts();
} catch (error) {
message.error('批量同步失败');
}
};
const handleBatchUpdate = () => {
@@ -680,109 +485,7 @@ export const ProductList: React.FC = () => {
// 这里可以实现导出商品数据为Excel或CSV文件
};
const filteredProducts = products.filter(product => {
if (filters.keyword) {
const keywordLower = filters.keyword.toLowerCase();
if (!product.name.toLowerCase().includes(keywordLower) &&
!product.sku.toLowerCase().includes(keywordLower)) {
return false;
}
}
if (filters.status.length > 0 && !filters.status.includes(product.status)) {
return false;
}
if (filters.category.length > 0 && !filters.category.includes(product.category)) {
return false;
}
if (filters.shop.length > 0 && !filters.shop.includes(product.shopId)) {
return false;
}
if (filters.priceRange) {
const [min, max] = filters.priceRange;
if (product.price < min || product.price > max) {
return false;
}
}
if (filters.stockRange) {
const [min, max] = filters.stockRange;
if (product.stock < min || product.stock > max) {
return false;
}
}
if (filters.roiRange) {
const [min, max] = filters.roiRange;
if (product.roi < min || product.roi > max) {
return false;
}
}
if (filters.minProfit !== null && product.profit < filters.minProfit) {
return false;
}
if (filters.minStock !== null && product.stock < filters.minStock) {
return false;
}
if (activePlatformTab !== 'all') {
if (activePlatformTab === 'unpublished') {
if (Object.keys(product.platformStatus).length > 0) {
return false;
}
} else {
if (!product.platformStatus[activePlatformTab]) {
return false;
}
}
}
return true;
});
const platformStats = useMemo(() => {
const stats: Record<string, { total: number; live: number; pending: number; failed: number }> = {
all: { total: products.length, live: 0, pending: 0, failed: 0 },
unpublished: { total: 0, live: 0, pending: 0, failed: 0 },
};
PLATFORMS.forEach(platform => {
stats[platform] = { total: 0, live: 0, pending: 0, failed: 0 };
});
products.forEach(product => {
const platforms = Object.keys(product.platformStatus);
if (platforms.length === 0) {
stats.unpublished.total++;
}
platforms.forEach(platform => {
if (!stats[platform]) {
stats[platform] = { total: 0, live: 0, pending: 0, failed: 0 };
}
stats[platform].total++;
const status = product.platformStatus[platform];
if (status === 'LIVE') {
stats[platform].live++;
stats.all.live++;
} else if (status === 'PENDING' || status === 'SYNCING' || status === 'LISTED') {
stats[platform].pending++;
stats.all.pending++;
} else if (status === 'FAILED' || status === 'SYNC_FAILED') {
stats[platform].failed++;
stats.all.failed++;
}
});
});
return stats;
}, [products]);
const sortedProducts = [...filteredProducts].sort((a, b) => {
const field = sort.field as keyof Product;
const aValue = a[field];
const bValue = b[field];
if (sort.order === 'ascend') {
return aValue > bValue ? 1 : -1;
} else {
return aValue < bValue ? 1 : -1;
}
});
const sortedProducts = products;
const allColumns: ColumnsType<Product> = [
{
@@ -1171,7 +874,7 @@ export const ProductList: React.FC = () => {
title="筛选条件"
placement="right"
onClose={() => setFilterVisible(false)}
visible={filterVisible}
open={filterVisible}
width={400}
>
<Form layout="vertical">
@@ -1209,7 +912,7 @@ export const ProductList: React.FC = () => {
onChange={(value) => handleFilterChange('shop', value)}
style={{ width: '100%' }}
>
{SHOPS.map(shop => (
{shops.map(shop => (
<Option key={shop.id} value={shop.id}>
{shop.name} ({shop.platform})
</Option>
@@ -1362,7 +1065,7 @@ export const ProductList: React.FC = () => {
title="排序设置"
placement="right"
onClose={() => setSortDrawerVisible(false)}
visible={sortDrawerVisible}
open={sortDrawerVisible}
width={300}
>
<Space direction="vertical" style={{ width: '100%' }}>
@@ -1403,7 +1106,7 @@ export const ProductList: React.FC = () => {
title="列设置"
placement="right"
onClose={() => setColumnDrawerVisible(false)}
visible={columnDrawerVisible}
open={columnDrawerVisible}
width={400}
>
<div style={{ marginBottom: 20 }}>
@@ -1509,7 +1212,7 @@ export const ProductList: React.FC = () => {
<Modal
title="商品定价"
visible={pricingModalVisible}
open={pricingModalVisible}
onCancel={() => setPricingModalVisible(false)}
footer={null}
width={600}