feat(Product/List): 添加图片列到产品列表
feat(Product/BrandSpace): 重构品牌空间页面布局和逻辑 refactor(.umirc): 调整产品路由顺序 fix(Product/CsvTool): 修复复选框事件处理并移除调试日志
This commit is contained in:
parent
49e3049681
commit
04b9fed6f7
10
.umirc.ts
10
.umirc.ts
|
|
@ -149,11 +149,6 @@ export default defineConfig({
|
||||||
path: '/product/list',
|
path: '/product/list',
|
||||||
component: './Product/List',
|
component: './Product/List',
|
||||||
},
|
},
|
||||||
{
|
|
||||||
name: '产品属性排列',
|
|
||||||
path: '/product/permutation',
|
|
||||||
component: './Product/Permutation',
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
name: '产品分类',
|
name: '产品分类',
|
||||||
path: '/product/category',
|
path: '/product/category',
|
||||||
|
|
@ -164,6 +159,11 @@ export default defineConfig({
|
||||||
path: '/product/attribute',
|
path: '/product/attribute',
|
||||||
component: './Product/Attribute',
|
component: './Product/Attribute',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: '产品属性排列',
|
||||||
|
path: '/product/permutation',
|
||||||
|
component: './Product/Permutation',
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: '产品品牌空间',
|
name: '产品品牌空间',
|
||||||
path: '/product/brandspace',
|
path: '/product/brandspace',
|
||||||
|
|
|
||||||
|
|
@ -67,6 +67,7 @@ const BrandSpace: React.FC = () => {
|
||||||
|
|
||||||
const [products, setProducts] = useState<Product[]>([]);
|
const [products, setProducts] = useState<Product[]>([]);
|
||||||
const [loading, setLoading] = useState(false);
|
const [loading, setLoading] = useState(false);
|
||||||
|
const [attributeValuesLoading, setAttributeValuesLoading] = useState(false);
|
||||||
|
|
||||||
// Fetch brands list
|
// Fetch brands list
|
||||||
const fetchBrands = async () => {
|
const fetchBrands = async () => {
|
||||||
|
|
@ -123,6 +124,7 @@ const BrandSpace: React.FC = () => {
|
||||||
|
|
||||||
// Fetch attribute values based on selected attribute
|
// Fetch attribute values based on selected attribute
|
||||||
const fetchAttributeValues = async (attributeName: string) => {
|
const fetchAttributeValues = async (attributeName: string) => {
|
||||||
|
setAttributeValuesLoading(true);
|
||||||
try {
|
try {
|
||||||
const response = await request('/dict/items', {
|
const response = await request('/dict/items', {
|
||||||
params: { dictId: attributeName },
|
params: { dictId: attributeName },
|
||||||
|
|
@ -132,6 +134,10 @@ const BrandSpace: React.FC = () => {
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to fetch attribute values:', error);
|
console.error('Failed to fetch attribute values:', error);
|
||||||
message.error('获取属性值列表失败');
|
message.error('获取属性值列表失败');
|
||||||
|
// Clear attribute values on error
|
||||||
|
setAttributeValues([]);
|
||||||
|
} finally {
|
||||||
|
setAttributeValuesLoading(false);
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -160,13 +166,13 @@ const BrandSpace: React.FC = () => {
|
||||||
params,
|
params,
|
||||||
});
|
});
|
||||||
|
|
||||||
const productList = Array.isArray(response)
|
const productList = response?.data?.items || [];
|
||||||
? response
|
|
||||||
: response?.data || [];
|
|
||||||
setProducts(productList);
|
setProducts(productList);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('Failed to fetch products:', error);
|
console.error('Failed to fetch products:', error);
|
||||||
message.error('获取产品列表失败');
|
message.error('获取产品列表失败');
|
||||||
|
// Clear products on error
|
||||||
|
setProducts([]);
|
||||||
} finally {
|
} finally {
|
||||||
setLoading(false);
|
setLoading(false);
|
||||||
}
|
}
|
||||||
|
|
@ -181,7 +187,7 @@ const BrandSpace: React.FC = () => {
|
||||||
// Fetch attribute values when attribute changes
|
// Fetch attribute values when attribute changes
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (selectedAttribute) {
|
if (selectedAttribute) {
|
||||||
fetchAttributeValues(selectedAttribute);
|
fetchAttributeValues(selectedAttribute);
|
||||||
setSelectedAttributeValue(null); // Reset selected value when attribute changes
|
setSelectedAttributeValue(null); // Reset selected value when attribute changes
|
||||||
}
|
}
|
||||||
}, [selectedAttribute]);
|
}, [selectedAttribute]);
|
||||||
|
|
@ -270,6 +276,7 @@ const BrandSpace: React.FC = () => {
|
||||||
value={selectedAttributeValue}
|
value={selectedAttributeValue}
|
||||||
onChange={handleAttributeValueChange}
|
onChange={handleAttributeValueChange}
|
||||||
allowClear
|
allowClear
|
||||||
|
loading={attributeValuesLoading}
|
||||||
>
|
>
|
||||||
{attributeValues.map((value) => (
|
{attributeValues.map((value) => (
|
||||||
<Option key={value.id} value={value.id}>
|
<Option key={value.id} value={value.id}>
|
||||||
|
|
@ -340,7 +347,7 @@ const BrandSpace: React.FC = () => {
|
||||||
</Sider>
|
</Sider>
|
||||||
|
|
||||||
{/* Main Content - Product List */}
|
{/* Main Content - Product List */}
|
||||||
<Content style={{ padding: '16px' }}>
|
<Content style={{ padding: '16px', overflowX: 'auto' }}>
|
||||||
<div style={{ marginBottom: 16 }}>
|
<div style={{ marginBottom: 16 }}>
|
||||||
<Title level={4} style={{ margin: 0 }}>
|
<Title level={4} style={{ margin: 0 }}>
|
||||||
产品列表
|
产品列表
|
||||||
|
|
@ -354,82 +361,109 @@ const BrandSpace: React.FC = () => {
|
||||||
<div style={{ textAlign: 'center', padding: '64px' }}>
|
<div style={{ textAlign: 'center', padding: '64px' }}>
|
||||||
<Text>加载中...</Text>
|
<Text>加载中...</Text>
|
||||||
</div>
|
</div>
|
||||||
) : products.length > 0 ? (
|
) : attributeValues.length > 0 ? (
|
||||||
<Row gutter={[16, 16]}>
|
<div style={{ minWidth: 'max-content' }}>
|
||||||
{products.map((product) => (
|
<Row gutter={[16, 16]}>
|
||||||
<Col xs={24} sm={12} md={8} lg={6} key={product.id}>
|
{attributeValues.map((attributeValue) => {
|
||||||
<Card
|
// Filter products for this attribute value
|
||||||
hoverable
|
if(!Array.isArray(products)){
|
||||||
style={{
|
console.log('products',products)
|
||||||
height: '100%',
|
return null
|
||||||
display: 'flex',
|
}
|
||||||
flexDirection: 'column',
|
const attributeValueProducts = selectedAttribute
|
||||||
}}
|
? (products || []).filter((product) =>
|
||||||
>
|
product.attributes &&
|
||||||
<div
|
product.attributes[selectedAttribute] === attributeValue.id
|
||||||
style={{
|
)
|
||||||
height: 200,
|
: [];
|
||||||
overflow: 'hidden',
|
|
||||||
marginBottom: 12,
|
return (
|
||||||
}}
|
<Col key={attributeValue.id} style={{ minWidth: 300 }}>
|
||||||
>
|
<Card
|
||||||
<Image
|
title={
|
||||||
src={
|
<Space>
|
||||||
product.image ||
|
{attributeValue.image && (
|
||||||
'https://via.placeholder.com/200x200?text=No+Image'
|
<Image
|
||||||
|
src={attributeValue.image}
|
||||||
|
style={{
|
||||||
|
width: 24,
|
||||||
|
height: 24,
|
||||||
|
objectFit: 'cover',
|
||||||
|
borderRadius: 4,
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
|
<span>
|
||||||
|
{attributeValue.titleCN || attributeValue.title || attributeValue.name}
|
||||||
|
</span>
|
||||||
|
</Space>
|
||||||
}
|
}
|
||||||
alt={product.name}
|
style={{ width: 300 }}
|
||||||
style={{
|
|
||||||
width: '100%',
|
|
||||||
height: '100%',
|
|
||||||
objectFit: 'cover',
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</div>
|
|
||||||
<div
|
|
||||||
style={{
|
|
||||||
flex: 1,
|
|
||||||
display: 'flex',
|
|
||||||
flexDirection: 'column',
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Text
|
|
||||||
type="secondary"
|
|
||||||
style={{ fontSize: 12, marginBottom: 4 }}
|
|
||||||
>
|
>
|
||||||
{product.sku}
|
{attributeValueProducts.length > 0 ? (
|
||||||
</Text>
|
<div style={{ maxHeight: 600, overflowY: 'auto' }}>
|
||||||
<Title
|
{attributeValueProducts.map((product) => (
|
||||||
level={5}
|
<Card
|
||||||
style={{
|
key={product.id}
|
||||||
margin: '4px 0',
|
size="small"
|
||||||
fontSize: 16,
|
hoverable
|
||||||
height: 48,
|
style={{ marginBottom: 8 }}
|
||||||
overflow: 'hidden',
|
>
|
||||||
}}
|
<div
|
||||||
>
|
style={{
|
||||||
{product.name}
|
height: 100,
|
||||||
</Title>
|
overflow: 'hidden',
|
||||||
<div
|
marginBottom: 8,
|
||||||
style={{
|
}}
|
||||||
marginTop: 'auto',
|
>
|
||||||
display: 'flex',
|
<Image
|
||||||
justifyContent: 'space-between',
|
src={
|
||||||
alignItems: 'center',
|
product.image ||
|
||||||
}}
|
'https://via.placeholder.com/100x100?text=No+Image'
|
||||||
>
|
}
|
||||||
<Text
|
alt={product.name}
|
||||||
strong
|
style={{
|
||||||
style={{ fontSize: 18, color: '#ff4d4f' }}
|
width: '100%',
|
||||||
>
|
height: '100%',
|
||||||
¥{product.price || '--'}
|
objectFit: 'cover',
|
||||||
</Text>
|
}}
|
||||||
</div>
|
/>
|
||||||
</div>
|
</div>
|
||||||
</Card>
|
<div>
|
||||||
</Col>
|
<Text
|
||||||
))}
|
type="secondary"
|
||||||
</Row>
|
style={{ fontSize: 12, marginBottom: 4 }}
|
||||||
|
>
|
||||||
|
{product.sku}
|
||||||
|
</Text>
|
||||||
|
<div style={{ marginTop: 4 }}>
|
||||||
|
<Text ellipsis style={{ width: '100%', display: 'block' }}>
|
||||||
|
{product.name}
|
||||||
|
</Text>
|
||||||
|
</div>
|
||||||
|
<div style={{ marginTop: 8 }}>
|
||||||
|
<Text
|
||||||
|
strong
|
||||||
|
style={{ fontSize: 14, color: '#ff4d4f' }}
|
||||||
|
>
|
||||||
|
¥{product.price || '--'}
|
||||||
|
</Text>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</Card>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
) : (
|
||||||
|
<div style={{ textAlign: 'center', padding: '32px' }}>
|
||||||
|
<Text type="secondary">暂无产品</Text>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
</Card>
|
||||||
|
</Col>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</Row>
|
||||||
|
</div>
|
||||||
) : (
|
) : (
|
||||||
<div
|
<div
|
||||||
style={{
|
style={{
|
||||||
|
|
@ -439,7 +473,7 @@ const BrandSpace: React.FC = () => {
|
||||||
borderRadius: 8,
|
borderRadius: 8,
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Text type="secondary">暂无符合条件的产品</Text>
|
<Text type="secondary">暂无属性值或产品</Text>
|
||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</Content>
|
</Content>
|
||||||
|
|
|
||||||
|
|
@ -418,7 +418,6 @@ const CsvTool: React.FC = () => {
|
||||||
if (
|
if (
|
||||||
quantity
|
quantity
|
||||||
) {
|
) {
|
||||||
console.log(quantity, attributeMappings.quantities[quantity])
|
|
||||||
// 使用quantity的shortName,如果没有则使用quantity但匹配 4 个零
|
// 使用quantity的shortName,如果没有则使用quantity但匹配 4 个零
|
||||||
const quantityShortName = attributeMappings.quantities[quantity] || Number(quantity).toString().padStart(4, '0');
|
const quantityShortName = attributeMappings.quantities[quantity] || Number(quantity).toString().padStart(4, '0');
|
||||||
skuComponents.push(quantityShortName);
|
skuComponents.push(quantityShortName);
|
||||||
|
|
@ -570,7 +569,7 @@ const CsvTool: React.FC = () => {
|
||||||
|
|
||||||
// Determine which data to use for processing and download
|
// Determine which data to use for processing and download
|
||||||
let finalData = dataWithSku;
|
let finalData = dataWithSku;
|
||||||
|
console.log('generateBundleSkuForSingle',generateBundleSkuForSingle)
|
||||||
// If generateBundleSkuForSingle is enabled, generate bundle products for single products
|
// If generateBundleSkuForSingle is enabled, generate bundle products for single products
|
||||||
if (generateBundleSkuForSingle) {
|
if (generateBundleSkuForSingle) {
|
||||||
// Filter out single records
|
// Filter out single records
|
||||||
|
|
@ -773,7 +772,7 @@ const CsvTool: React.FC = () => {
|
||||||
valuePropName="checked"
|
valuePropName="checked"
|
||||||
initialValue={true}
|
initialValue={true}
|
||||||
>
|
>
|
||||||
<Checkbox onChange={setGenerateBundleSkuForSingle}>
|
<Checkbox onChange={(e)=> setGenerateBundleSkuForSingle(e.target.checked)}>
|
||||||
启用为single类型生成bundle SKU
|
启用为single类型生成bundle SKU
|
||||||
</Checkbox>
|
</Checkbox>
|
||||||
</ProForm.Item>
|
</ProForm.Item>
|
||||||
|
|
|
||||||
|
|
@ -234,6 +234,12 @@ const List: React.FC = () => {
|
||||||
</>
|
</>
|
||||||
),
|
),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
title: "图片",
|
||||||
|
dataIndex: 'image',
|
||||||
|
width: 100,
|
||||||
|
valueType:'image'
|
||||||
|
},
|
||||||
{
|
{
|
||||||
title: '名称',
|
title: '名称',
|
||||||
dataIndex: 'name',
|
dataIndex: 'name',
|
||||||
|
|
@ -249,6 +255,7 @@ const List: React.FC = () => {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|
||||||
{
|
{
|
||||||
title: '价格',
|
title: '价格',
|
||||||
dataIndex: 'price',
|
dataIndex: 'price',
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue