Compare commits
3 Commits
1f8ac01299
...
04b9fed6f7
| Author | SHA1 | Date |
|---|---|---|
|
|
04b9fed6f7 | |
|
|
49e3049681 | |
|
|
af1cebe29d |
10
.umirc.ts
10
.umirc.ts
|
|
@ -149,11 +149,6 @@ export default defineConfig({
|
|||
path: '/product/list',
|
||||
component: './Product/List',
|
||||
},
|
||||
{
|
||||
name: '产品属性排列',
|
||||
path: '/product/permutation',
|
||||
component: './Product/Permutation',
|
||||
},
|
||||
{
|
||||
name: '产品分类',
|
||||
path: '/product/category',
|
||||
|
|
@ -164,6 +159,11 @@ export default defineConfig({
|
|||
path: '/product/attribute',
|
||||
component: './Product/Attribute',
|
||||
},
|
||||
{
|
||||
name: '产品属性排列',
|
||||
path: '/product/permutation',
|
||||
component: './Product/Permutation',
|
||||
},
|
||||
{
|
||||
name: '产品品牌空间',
|
||||
path: '/product/brandspace',
|
||||
|
|
|
|||
|
|
@ -5,8 +5,10 @@ import {
|
|||
DrawerForm,
|
||||
ProForm,
|
||||
ProFormSelect,
|
||||
ProFormDateRangePicker,
|
||||
} from '@ant-design/pro-components';
|
||||
import { Button } from 'antd';
|
||||
import dayjs from 'dayjs';
|
||||
import React from 'react';
|
||||
|
||||
// 定义SyncForm组件的props类型
|
||||
|
|
@ -14,6 +16,7 @@ interface SyncFormProps {
|
|||
tableRef: React.MutableRefObject<ActionType | undefined>;
|
||||
onFinish: (values: any) => Promise<void>;
|
||||
siteId?: string;
|
||||
dateRange?: [dayjs.Dayjs, dayjs.Dayjs];
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
@ -21,7 +24,7 @@ interface SyncFormProps {
|
|||
* @param {SyncFormProps} props 组件属性
|
||||
* @returns {React.ReactElement} 抽屉表单
|
||||
*/
|
||||
const SyncForm: React.FC<SyncFormProps> = ({ tableRef, onFinish, siteId }) => {
|
||||
const SyncForm: React.FC<SyncFormProps> = ({ tableRef, onFinish, siteId, dateRange }) => {
|
||||
// 使用 antd 的 App 组件提供的 message API
|
||||
const [loading, setLoading] = React.useState(false);
|
||||
|
||||
|
|
@ -49,7 +52,11 @@ const SyncForm: React.FC<SyncFormProps> = ({ tableRef, onFinish, siteId }) => {
|
|||
// 返回一个抽屉表单
|
||||
return (
|
||||
<DrawerForm<API.ordercontrollerSyncorderParams>
|
||||
title="同步订单"
|
||||
initialValues={{
|
||||
dateRange: [dayjs().subtract(1, 'week'), dayjs()],
|
||||
}}
|
||||
|
||||
title="同步订单"
|
||||
// 表单的触发器,一个带图标的按钮
|
||||
trigger={
|
||||
<Button key="syncSite" type="primary">
|
||||
|
|
@ -67,6 +74,7 @@ const SyncForm: React.FC<SyncFormProps> = ({ tableRef, onFinish, siteId }) => {
|
|||
onFinish={onFinish}
|
||||
>
|
||||
<ProForm.Group>
|
||||
|
||||
{/* 站点选择框 */}
|
||||
<ProFormSelect
|
||||
name="siteId"
|
||||
|
|
@ -83,6 +91,22 @@ const SyncForm: React.FC<SyncFormProps> = ({ tableRef, onFinish, siteId }) => {
|
|||
}));
|
||||
}}
|
||||
/>
|
||||
|
||||
<ProFormDateRangePicker
|
||||
name="dateRange"
|
||||
label="同步日期范围"
|
||||
placeholder={['开始日期', '结束日期']}
|
||||
|
||||
transform={(value) => {
|
||||
return {
|
||||
dateRange: value,
|
||||
};
|
||||
}}
|
||||
fieldProps={{
|
||||
showTime: false,
|
||||
style: { width: '100%' },
|
||||
}}
|
||||
/>
|
||||
</ProForm.Group>
|
||||
</DrawerForm>
|
||||
);
|
||||
|
|
|
|||
|
|
@ -52,12 +52,6 @@ const DictItemImportButton: React.FC<DictItemImportButtonProps> = ({
|
|||
});
|
||||
}
|
||||
|
||||
// 检查返回结果是否包含 success 字段
|
||||
if (result && result.success !== undefined && !result.success) {
|
||||
throw new Error(result.message || '导入失败');
|
||||
}
|
||||
onSuccess?.(result);
|
||||
|
||||
// 显示导入结果详情
|
||||
showImportResult(result);
|
||||
|
||||
|
|
|
|||
|
|
@ -502,7 +502,7 @@ const ListPage: React.FC = () => {
|
|||
success,
|
||||
message: errMsg,
|
||||
data,
|
||||
} = await ordercontrollerSyncorders(values);
|
||||
} = await ordercontrollerSyncorders(values,{after:values.dateRange?.[0]+'T00:00:00Z',before:values.dateRange?.[1]+'T23:59:59Z'});
|
||||
if (!success) {
|
||||
throw new Error(errMsg);
|
||||
}
|
||||
|
|
@ -997,6 +997,7 @@ const Detail: React.FC<{
|
|||
);
|
||||
}}
|
||||
/>
|
||||
|
||||
{/* 显示 related order */}
|
||||
<ProDescriptions.Item
|
||||
label="关联"
|
||||
|
|
@ -1506,16 +1507,16 @@ const Shipping: React.FC<{
|
|||
<ProFormList
|
||||
label="发货产品"
|
||||
name="sales"
|
||||
// rules={[
|
||||
// {
|
||||
// required: true,
|
||||
// message: '至少需要一个商品',
|
||||
// validator: (_, value) =>
|
||||
// value && value.length > 0
|
||||
// ? Promise.resolve()
|
||||
// : Promise.reject('至少需要一个商品'),
|
||||
// },
|
||||
// ]}
|
||||
// rules={[
|
||||
// {
|
||||
// required: true,
|
||||
// message: '至少需要一个商品',
|
||||
// validator: (_, value) =>
|
||||
// value && value.length > 0
|
||||
//</Col> ? Promise.resolve()
|
||||
// : Promise.reject('至少需要一个商品'),
|
||||
// },
|
||||
// ]}
|
||||
>
|
||||
<ProForm.Group>
|
||||
<ProFormSelect
|
||||
|
|
|
|||
|
|
@ -67,6 +67,7 @@ const BrandSpace: React.FC = () => {
|
|||
|
||||
const [products, setProducts] = useState<Product[]>([]);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const [attributeValuesLoading, setAttributeValuesLoading] = useState(false);
|
||||
|
||||
// Fetch brands list
|
||||
const fetchBrands = async () => {
|
||||
|
|
@ -123,6 +124,7 @@ const BrandSpace: React.FC = () => {
|
|||
|
||||
// Fetch attribute values based on selected attribute
|
||||
const fetchAttributeValues = async (attributeName: string) => {
|
||||
setAttributeValuesLoading(true);
|
||||
try {
|
||||
const response = await request('/dict/items', {
|
||||
params: { dictId: attributeName },
|
||||
|
|
@ -132,6 +134,10 @@ const BrandSpace: React.FC = () => {
|
|||
} catch (error) {
|
||||
console.error('Failed to fetch attribute values:', error);
|
||||
message.error('获取属性值列表失败');
|
||||
// Clear attribute values on error
|
||||
setAttributeValues([]);
|
||||
} finally {
|
||||
setAttributeValuesLoading(false);
|
||||
}
|
||||
};
|
||||
|
||||
|
|
@ -160,13 +166,13 @@ const BrandSpace: React.FC = () => {
|
|||
params,
|
||||
});
|
||||
|
||||
const productList = Array.isArray(response)
|
||||
? response
|
||||
: response?.data || [];
|
||||
const productList = response?.data?.items || [];
|
||||
setProducts(productList);
|
||||
} catch (error) {
|
||||
console.error('Failed to fetch products:', error);
|
||||
message.error('获取产品列表失败');
|
||||
// Clear products on error
|
||||
setProducts([]);
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
|
|
@ -181,7 +187,7 @@ const BrandSpace: React.FC = () => {
|
|||
// Fetch attribute values when attribute changes
|
||||
useEffect(() => {
|
||||
if (selectedAttribute) {
|
||||
fetchAttributeValues(selectedAttribute);
|
||||
fetchAttributeValues(selectedAttribute);
|
||||
setSelectedAttributeValue(null); // Reset selected value when attribute changes
|
||||
}
|
||||
}, [selectedAttribute]);
|
||||
|
|
@ -270,6 +276,7 @@ const BrandSpace: React.FC = () => {
|
|||
value={selectedAttributeValue}
|
||||
onChange={handleAttributeValueChange}
|
||||
allowClear
|
||||
loading={attributeValuesLoading}
|
||||
>
|
||||
{attributeValues.map((value) => (
|
||||
<Option key={value.id} value={value.id}>
|
||||
|
|
@ -340,7 +347,7 @@ const BrandSpace: React.FC = () => {
|
|||
</Sider>
|
||||
|
||||
{/* Main Content - Product List */}
|
||||
<Content style={{ padding: '16px' }}>
|
||||
<Content style={{ padding: '16px', overflowX: 'auto' }}>
|
||||
<div style={{ marginBottom: 16 }}>
|
||||
<Title level={4} style={{ margin: 0 }}>
|
||||
产品列表
|
||||
|
|
@ -354,82 +361,109 @@ const BrandSpace: React.FC = () => {
|
|||
<div style={{ textAlign: 'center', padding: '64px' }}>
|
||||
<Text>加载中...</Text>
|
||||
</div>
|
||||
) : products.length > 0 ? (
|
||||
<Row gutter={[16, 16]}>
|
||||
{products.map((product) => (
|
||||
<Col xs={24} sm={12} md={8} lg={6} key={product.id}>
|
||||
<Card
|
||||
hoverable
|
||||
style={{
|
||||
height: '100%',
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
}}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
height: 200,
|
||||
overflow: 'hidden',
|
||||
marginBottom: 12,
|
||||
}}
|
||||
>
|
||||
<Image
|
||||
src={
|
||||
product.image ||
|
||||
'https://via.placeholder.com/200x200?text=No+Image'
|
||||
) : attributeValues.length > 0 ? (
|
||||
<div style={{ minWidth: 'max-content' }}>
|
||||
<Row gutter={[16, 16]}>
|
||||
{attributeValues.map((attributeValue) => {
|
||||
// Filter products for this attribute value
|
||||
if(!Array.isArray(products)){
|
||||
console.log('products',products)
|
||||
return null
|
||||
}
|
||||
const attributeValueProducts = selectedAttribute
|
||||
? (products || []).filter((product) =>
|
||||
product.attributes &&
|
||||
product.attributes[selectedAttribute] === attributeValue.id
|
||||
)
|
||||
: [];
|
||||
|
||||
return (
|
||||
<Col key={attributeValue.id} style={{ minWidth: 300 }}>
|
||||
<Card
|
||||
title={
|
||||
<Space>
|
||||
{attributeValue.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: '100%',
|
||||
height: '100%',
|
||||
objectFit: 'cover',
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<div
|
||||
style={{
|
||||
flex: 1,
|
||||
display: 'flex',
|
||||
flexDirection: 'column',
|
||||
}}
|
||||
>
|
||||
<Text
|
||||
type="secondary"
|
||||
style={{ fontSize: 12, marginBottom: 4 }}
|
||||
style={{ width: 300 }}
|
||||
>
|
||||
{product.sku}
|
||||
</Text>
|
||||
<Title
|
||||
level={5}
|
||||
style={{
|
||||
margin: '4px 0',
|
||||
fontSize: 16,
|
||||
height: 48,
|
||||
overflow: 'hidden',
|
||||
}}
|
||||
>
|
||||
{product.name}
|
||||
</Title>
|
||||
<div
|
||||
style={{
|
||||
marginTop: 'auto',
|
||||
display: 'flex',
|
||||
justifyContent: 'space-between',
|
||||
alignItems: 'center',
|
||||
}}
|
||||
>
|
||||
<Text
|
||||
strong
|
||||
style={{ fontSize: 18, color: '#ff4d4f' }}
|
||||
>
|
||||
¥{product.price || '--'}
|
||||
</Text>
|
||||
</div>
|
||||
</div>
|
||||
</Card>
|
||||
</Col>
|
||||
))}
|
||||
</Row>
|
||||
{attributeValueProducts.length > 0 ? (
|
||||
<div style={{ maxHeight: 600, overflowY: 'auto' }}>
|
||||
{attributeValueProducts.map((product) => (
|
||||
<Card
|
||||
key={product.id}
|
||||
size="small"
|
||||
hoverable
|
||||
style={{ marginBottom: 8 }}
|
||||
>
|
||||
<div
|
||||
style={{
|
||||
height: 100,
|
||||
overflow: 'hidden',
|
||||
marginBottom: 8,
|
||||
}}
|
||||
>
|
||||
<Image
|
||||
src={
|
||||
product.image ||
|
||||
'https://via.placeholder.com/100x100?text=No+Image'
|
||||
}
|
||||
alt={product.name}
|
||||
style={{
|
||||
width: '100%',
|
||||
height: '100%',
|
||||
objectFit: 'cover',
|
||||
}}
|
||||
/>
|
||||
</div>
|
||||
<div>
|
||||
<Text
|
||||
type="secondary"
|
||||
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
|
||||
style={{
|
||||
|
|
@ -439,7 +473,7 @@ const BrandSpace: React.FC = () => {
|
|||
borderRadius: 8,
|
||||
}}
|
||||
>
|
||||
<Text type="secondary">暂无符合条件的产品</Text>
|
||||
<Text type="secondary">暂无属性值或产品</Text>
|
||||
</div>
|
||||
)}
|
||||
</Content>
|
||||
|
|
|
|||
|
|
@ -418,7 +418,6 @@ const CsvTool: React.FC = () => {
|
|||
if (
|
||||
quantity
|
||||
) {
|
||||
console.log(quantity, attributeMappings.quantities[quantity])
|
||||
// 使用quantity的shortName,如果没有则使用quantity但匹配 4 个零
|
||||
const quantityShortName = attributeMappings.quantities[quantity] || Number(quantity).toString().padStart(4, '0');
|
||||
skuComponents.push(quantityShortName);
|
||||
|
|
@ -570,7 +569,7 @@ const CsvTool: React.FC = () => {
|
|||
|
||||
// Determine which data to use for processing and download
|
||||
let finalData = dataWithSku;
|
||||
|
||||
console.log('generateBundleSkuForSingle',generateBundleSkuForSingle)
|
||||
// If generateBundleSkuForSingle is enabled, generate bundle products for single products
|
||||
if (generateBundleSkuForSingle) {
|
||||
// Filter out single records
|
||||
|
|
@ -773,7 +772,7 @@ const CsvTool: React.FC = () => {
|
|||
valuePropName="checked"
|
||||
initialValue={true}
|
||||
>
|
||||
<Checkbox onChange={setGenerateBundleSkuForSingle}>
|
||||
<Checkbox onChange={(e)=> setGenerateBundleSkuForSingle(e.target.checked)}>
|
||||
启用为single类型生成bundle SKU
|
||||
</Checkbox>
|
||||
</ProForm.Item>
|
||||
|
|
|
|||
|
|
@ -234,6 +234,12 @@ const List: React.FC = () => {
|
|||
</>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: "图片",
|
||||
dataIndex: 'image',
|
||||
width: 100,
|
||||
valueType:'image'
|
||||
},
|
||||
{
|
||||
title: '名称',
|
||||
dataIndex: 'name',
|
||||
|
|
@ -249,6 +255,7 @@ const List: React.FC = () => {
|
|||
},
|
||||
},
|
||||
|
||||
|
||||
{
|
||||
title: '价格',
|
||||
dataIndex: 'price',
|
||||
|
|
|
|||
|
|
@ -2,7 +2,7 @@ import React, { useEffect, useMemo, useRef, useState } from 'react';
|
|||
|
||||
import {
|
||||
statisticscontrollerGetinativeusersbymonth,
|
||||
statisticscontrollerGetordersorce,
|
||||
statisticscontrollerGetordersource,
|
||||
} from '@/servers/api/statistics';
|
||||
import {
|
||||
ActionType,
|
||||
|
|
@ -25,7 +25,7 @@ const ListPage: React.FC = () => {
|
|||
country: ['CA'],
|
||||
};
|
||||
function handleSubmit(values: typeof initialValues) {
|
||||
statisticscontrollerGetordersorce({params: values}).then(({ data, success }) => {
|
||||
statisticscontrollerGetordersource({params: values}).then(({ data, success }) => {
|
||||
if (success) setData(data);
|
||||
});
|
||||
}
|
||||
|
|
|
|||
Loading…
Reference in New Issue