forked from yoone/WEB
Compare commits
4 Commits
8d3c3ff71c
...
fcbf0a4833
| Author | SHA1 | Date |
|---|---|---|
|
|
fcbf0a4833 | |
|
|
236d0a85bf | |
|
|
ec57d7c476 | |
|
|
bd6a2a1509 |
|
|
@ -0,0 +1,46 @@
|
||||||
|
## 实现计划
|
||||||
|
|
||||||
|
### 1. 地图数据准备
|
||||||
|
- 获取加拿大和澳大利亚的省级地图数据(JSON格式)
|
||||||
|
- 将地图数据文件添加到项目的`public`目录下,命名为`canada.json`和`australia.json`
|
||||||
|
|
||||||
|
### 2. 组件实现
|
||||||
|
- 为加拿大创建地图组件:`/Users/zksu/Developer/work/workcode/WEB/src/pages/Area/Canada/index.tsx`
|
||||||
|
- 为澳大利亚创建地图组件:`/Users/zksu/Developer/work/workcode/WEB/src/pages/Area/Australia/index.tsx`
|
||||||
|
- 组件将基于现有的`Map/index.tsx`实现,修改地图数据加载和配置
|
||||||
|
|
||||||
|
### 3. 数据结构设计
|
||||||
|
- 定义省级数据接口,包含:
|
||||||
|
- 英文名称(用于匹配地图数据)
|
||||||
|
- 中文名称
|
||||||
|
- 简称
|
||||||
|
- 人口数据
|
||||||
|
|
||||||
|
### 4. 地图渲染配置
|
||||||
|
- 使用ECharts的Map组件渲染省级地图
|
||||||
|
- 配置tooltip显示中文名称、简称和人口数据
|
||||||
|
- 添加视觉映射组件,根据人口数据显示不同颜色
|
||||||
|
|
||||||
|
### 5. 数据获取与处理
|
||||||
|
- 定义本地数据结构,包含各个省的中文名称、简称和人口
|
||||||
|
- 将数据转换为ECharts需要的格式
|
||||||
|
|
||||||
|
### 6. 组件优化
|
||||||
|
- 添加加载状态
|
||||||
|
- 处理地图数据加载失败的情况
|
||||||
|
- 优化地图交互体验,如缩放、拖拽等
|
||||||
|
|
||||||
|
### 7. 实现步骤
|
||||||
|
1. 准备地图数据文件
|
||||||
|
2. 创建加拿大地图组件
|
||||||
|
3. 创建澳大利亚地图组件
|
||||||
|
4. 配置地图数据和样式
|
||||||
|
5. 添加省级数据
|
||||||
|
6. 测试地图渲染和交互
|
||||||
|
|
||||||
|
### 技术要点
|
||||||
|
- 使用ECharts的MapChart组件
|
||||||
|
- 动态加载地图JSON数据
|
||||||
|
- 数据映射和转换
|
||||||
|
- Tooltip自定义格式化
|
||||||
|
- 视觉映射配置
|
||||||
12
.umirc.ts
12
.umirc.ts
|
|
@ -60,6 +60,16 @@ export default defineConfig({
|
||||||
path: '/area/map',
|
path: '/area/map',
|
||||||
component: './Area/Map',
|
component: './Area/Map',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: '加拿大地图',
|
||||||
|
path: '/area/canada',
|
||||||
|
component: './Area/Canada',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '澳大利亚地图',
|
||||||
|
path: '/area/australia',
|
||||||
|
component: './Area/Australia',
|
||||||
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -170,7 +180,7 @@ export default defineConfig({
|
||||||
component: './Product/Permutation',
|
component: './Product/Permutation',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: '产品品牌空间',
|
name: '产品聚合空间',
|
||||||
path: '/product/groupBy',
|
path: '/product/groupBy',
|
||||||
component: './Product/GroupBy',
|
component: './Product/GroupBy',
|
||||||
},
|
},
|
||||||
|
|
|
||||||
File diff suppressed because one or more lines are too long
File diff suppressed because it is too large
Load Diff
|
|
@ -0,0 +1,149 @@
|
||||||
|
import { Spin, message } from 'antd';
|
||||||
|
import ReactECharts from 'echarts-for-react';
|
||||||
|
import { MapChart } from 'echarts/charts';
|
||||||
|
import { TooltipComponent, VisualMapComponent } from 'echarts/components';
|
||||||
|
import * as echarts from 'echarts/core';
|
||||||
|
import { CanvasRenderer } from 'echarts/renderers';
|
||||||
|
import React, { useEffect, useState } from 'react';
|
||||||
|
|
||||||
|
// 注册 ECharts 组件
|
||||||
|
echarts.use([TooltipComponent, VisualMapComponent, MapChart, CanvasRenderer]);
|
||||||
|
|
||||||
|
interface ProvinceData {
|
||||||
|
name: string; // 英文名称(用于匹配地图)
|
||||||
|
chineseName: string; // 中文名称
|
||||||
|
shortName: string; // 简称
|
||||||
|
population: number; // 人口数据
|
||||||
|
}
|
||||||
|
|
||||||
|
const AustraliaMap: React.FC = () => {
|
||||||
|
const [option, setOption] = useState({});
|
||||||
|
const [loading, setLoading] = useState(true);
|
||||||
|
|
||||||
|
// 澳大利亚省级数据
|
||||||
|
const provinceData: ProvinceData[] = [
|
||||||
|
{ name: 'New South Wales', chineseName: '新南威尔士州', shortName: 'NSW', population: 8166369 },
|
||||||
|
{ name: 'Victoria', chineseName: '维多利亚州', shortName: 'VIC', population: 6704352 },
|
||||||
|
{ name: 'Queensland', chineseName: '昆士兰州', shortName: 'QLD', population: 5265049 },
|
||||||
|
{ name: 'Western Australia', chineseName: '西澳大利亚州', shortName: 'WA', population: 2885548 },
|
||||||
|
{ name: 'South Australia', chineseName: '南澳大利亚州', shortName: 'SA', population: 1806604 },
|
||||||
|
{ name: 'Tasmania', chineseName: '塔斯马尼亚州', shortName: 'TAS', population: 569825 },
|
||||||
|
{ name: 'Australian Capital Territory', chineseName: '澳大利亚首都领地', shortName: 'ACT', population: 453349 },
|
||||||
|
{ name: 'Northern Territory', chineseName: '北领地', shortName: 'NT', population: 249072 },
|
||||||
|
];
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const fetchAndSetMapData = async () => {
|
||||||
|
try {
|
||||||
|
// 加载澳大利亚地图数据
|
||||||
|
const australiaMapResponse = await fetch('/australia.json');
|
||||||
|
const australiaMap = await australiaMapResponse.json();
|
||||||
|
echarts.registerMap('australia', australiaMap);
|
||||||
|
|
||||||
|
// 将省级数据转换为 ECharts 需要的格式
|
||||||
|
const mapData = provinceData.map((province) => ({
|
||||||
|
name: province.name,
|
||||||
|
value: province.population,
|
||||||
|
chineseName: province.chineseName,
|
||||||
|
shortName: province.shortName,
|
||||||
|
population: province.population,
|
||||||
|
}));
|
||||||
|
|
||||||
|
// 配置 ECharts 地图选项
|
||||||
|
const mapOption = {
|
||||||
|
tooltip: {
|
||||||
|
trigger: 'item',
|
||||||
|
formatter: (params: any) => {
|
||||||
|
if (params.data) {
|
||||||
|
return `
|
||||||
|
<div>
|
||||||
|
<div><strong>${params.data.chineseName}</strong> (${params.data.shortName})</div>
|
||||||
|
<div>人口: ${params.data.population.toLocaleString()}</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
return `${params.name}`;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
visualMap: {
|
||||||
|
left: 'left',
|
||||||
|
min: Math.min(...provinceData.map(p => p.population)),
|
||||||
|
max: Math.max(...provinceData.map(p => p.population)),
|
||||||
|
inRange: {
|
||||||
|
color: ['#f0f0f0', '#52c41a', '#389e0d'],
|
||||||
|
},
|
||||||
|
calculable: true,
|
||||||
|
orient: 'vertical',
|
||||||
|
left: 'right',
|
||||||
|
top: 'center',
|
||||||
|
text: ['人口多', '人口少'],
|
||||||
|
},
|
||||||
|
series: [
|
||||||
|
{
|
||||||
|
name: 'Australia States',
|
||||||
|
type: 'map',
|
||||||
|
map: 'australia',
|
||||||
|
roam: true,
|
||||||
|
nameProperty: 'STATE_NAME', // 指定地图数据中用于匹配的字段
|
||||||
|
label: {
|
||||||
|
show: true,
|
||||||
|
formatter: (params: any) => {
|
||||||
|
if (params.data) {
|
||||||
|
return `${params.data.shortName}\n${params.data.chineseName}\n${params.data.population.toLocaleString()}`;
|
||||||
|
}
|
||||||
|
return params.name;
|
||||||
|
},
|
||||||
|
fontSize: 12,
|
||||||
|
color: '#333',
|
||||||
|
fontWeight: 'bold',
|
||||||
|
},
|
||||||
|
emphasis: {
|
||||||
|
label: {
|
||||||
|
show: true,
|
||||||
|
},
|
||||||
|
itemStyle: {
|
||||||
|
areaColor: '#ffc107',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
data: mapData,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
setOption(mapOption);
|
||||||
|
} catch (error: any) {
|
||||||
|
message.error(`加载地图数据失败: ${error.message}`);
|
||||||
|
} finally {
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
fetchAndSetMapData();
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
if (loading) {
|
||||||
|
return (
|
||||||
|
<Spin
|
||||||
|
size="large"
|
||||||
|
style={{
|
||||||
|
display: 'flex',
|
||||||
|
justifyContent: 'center',
|
||||||
|
alignItems: 'center',
|
||||||
|
height: '80vh',
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ReactECharts
|
||||||
|
echarts={echarts}
|
||||||
|
option={option}
|
||||||
|
style={{ height: '80vh', width: '100%' }}
|
||||||
|
notMerge={true}
|
||||||
|
lazyUpdate={true}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default AustraliaMap;
|
||||||
|
|
@ -0,0 +1,153 @@
|
||||||
|
import { Spin, message } from 'antd';
|
||||||
|
import ReactECharts from 'echarts-for-react';
|
||||||
|
import { MapChart } from 'echarts/charts';
|
||||||
|
import { TooltipComponent, VisualMapComponent } from 'echarts/components';
|
||||||
|
import * as echarts from 'echarts/core';
|
||||||
|
import { CanvasRenderer } from 'echarts/renderers';
|
||||||
|
import React, { useEffect, useState } from 'react';
|
||||||
|
|
||||||
|
// 注册 ECharts 组件
|
||||||
|
echarts.use([TooltipComponent, VisualMapComponent, MapChart, CanvasRenderer]);
|
||||||
|
|
||||||
|
interface ProvinceData {
|
||||||
|
name: string; // 英文名称(用于匹配地图)
|
||||||
|
chineseName: string; // 中文名称
|
||||||
|
shortName: string; // 简称
|
||||||
|
population: number; // 人口数据
|
||||||
|
}
|
||||||
|
|
||||||
|
const CanadaMap: React.FC = () => {
|
||||||
|
const [option, setOption] = useState({});
|
||||||
|
const [loading, setLoading] = useState(true);
|
||||||
|
|
||||||
|
// 加拿大省级数据
|
||||||
|
const provinceData: ProvinceData[] = [
|
||||||
|
{ name: 'British Columbia', chineseName: '不列颠哥伦比亚省', shortName: 'BC', population: 5147712 },
|
||||||
|
{ name: 'Alberta', chineseName: '阿尔伯塔省', shortName: 'AB', population: 4413146 },
|
||||||
|
{ name: 'Saskatchewan', chineseName: '萨斯喀彻温省', shortName: 'SK', population: 1181666 },
|
||||||
|
{ name: 'Manitoba', chineseName: '曼尼托巴省', shortName: 'MB', population: 1383765 },
|
||||||
|
{ name: 'Ontario', chineseName: '安大略省', shortName: 'ON', population: 14711827 },
|
||||||
|
{ name: 'Quebec', chineseName: '魁北克省', shortName: 'QC', population: 8501833 },
|
||||||
|
{ name: 'New Brunswick', chineseName: '新不伦瑞克省', shortName: 'NB', population: 789225 },
|
||||||
|
{ name: 'Nova Scotia', chineseName: '新斯科舍省', shortName: 'NS', population: 971395 },
|
||||||
|
{ name: 'Prince Edward Island', chineseName: '爱德华王子岛省', shortName: 'PE', population: 160302 },
|
||||||
|
{ name: 'Newfoundland and Labrador', chineseName: '纽芬兰与拉布拉多省', shortName: 'NL', population: 521365 },
|
||||||
|
{ name: 'Yukon', chineseName: '育空地区', shortName: 'YT', population: 43985 },
|
||||||
|
{ name: 'Northwest Territories', chineseName: '西北地区', shortName: 'NT', population: 45515 },
|
||||||
|
{ name: 'Nunavut', chineseName: '努纳武特地区', shortName: 'NU', population: 39430 },
|
||||||
|
];
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
const fetchAndSetMapData = async () => {
|
||||||
|
try {
|
||||||
|
// 加载加拿大地图数据
|
||||||
|
const canadaMapResponse = await fetch('/canada.json');
|
||||||
|
const canadaMap = await canadaMapResponse.json();
|
||||||
|
echarts.registerMap('canada', canadaMap);
|
||||||
|
|
||||||
|
// 将省级数据转换为 ECharts 需要的格式
|
||||||
|
const mapData = provinceData.map((province) => ({
|
||||||
|
name: province.name,
|
||||||
|
value: province.population,
|
||||||
|
chineseName: province.chineseName,
|
||||||
|
shortName: province.shortName,
|
||||||
|
population: province.population,
|
||||||
|
}));
|
||||||
|
|
||||||
|
// 配置 ECharts 地图选项
|
||||||
|
const mapOption = {
|
||||||
|
tooltip: {
|
||||||
|
trigger: 'item',
|
||||||
|
formatter: (params: any) => {
|
||||||
|
if (params.data) {
|
||||||
|
return `
|
||||||
|
<div>
|
||||||
|
<div><strong>${params.data.chineseName}</strong> (${params.data.shortName})</div>
|
||||||
|
<div>人口: ${params.data.population.toLocaleString()}</div>
|
||||||
|
</div>
|
||||||
|
`;
|
||||||
|
}
|
||||||
|
return `${params.name}`;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
visualMap: {
|
||||||
|
left: 'left',
|
||||||
|
min: Math.min(...provinceData.map(p => p.population)),
|
||||||
|
max: Math.max(...provinceData.map(p => p.population)),
|
||||||
|
inRange: {
|
||||||
|
color: ['#f0f0f0', '#1890ff', '#096dd9'],
|
||||||
|
},
|
||||||
|
calculable: true,
|
||||||
|
orient: 'vertical',
|
||||||
|
left: 'right',
|
||||||
|
top: 'center',
|
||||||
|
text: ['人口多', '人口少'],
|
||||||
|
},
|
||||||
|
series: [
|
||||||
|
{
|
||||||
|
name: 'Canada Provinces',
|
||||||
|
type: 'map',
|
||||||
|
map: 'canada',
|
||||||
|
roam: true,
|
||||||
|
label: {
|
||||||
|
show: true,
|
||||||
|
formatter: (params: any) => {
|
||||||
|
if (params.data) {
|
||||||
|
return `${params.data.shortName}\n${params.data.chineseName}\n${params.data.population.toLocaleString()}`;
|
||||||
|
}
|
||||||
|
return params.name;
|
||||||
|
},
|
||||||
|
fontSize: 12,
|
||||||
|
color: '#333',
|
||||||
|
fontWeight: 'bold',
|
||||||
|
},
|
||||||
|
emphasis: {
|
||||||
|
label: {
|
||||||
|
show: true,
|
||||||
|
},
|
||||||
|
itemStyle: {
|
||||||
|
areaColor: '#ffc107',
|
||||||
|
},
|
||||||
|
},
|
||||||
|
data: mapData,
|
||||||
|
},
|
||||||
|
],
|
||||||
|
};
|
||||||
|
|
||||||
|
setOption(mapOption);
|
||||||
|
} catch (error: any) {
|
||||||
|
message.error(`加载地图数据失败: ${error.message}`);
|
||||||
|
} finally {
|
||||||
|
setLoading(false);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
fetchAndSetMapData();
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
if (loading) {
|
||||||
|
return (
|
||||||
|
<Spin
|
||||||
|
size="large"
|
||||||
|
style={{
|
||||||
|
display: 'flex',
|
||||||
|
justifyContent: 'center',
|
||||||
|
alignItems: 'center',
|
||||||
|
height: '80vh',
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ReactECharts
|
||||||
|
echarts={echarts}
|
||||||
|
option={option}
|
||||||
|
style={{ height: '80vh', width: '100%' }}
|
||||||
|
notMerge={true}
|
||||||
|
lazyUpdate={true}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default CanadaMap;
|
||||||
|
|
@ -3,6 +3,7 @@ import styles from '../../../style/order-list.css';
|
||||||
import InternationalPhoneInput from '@/components/InternationalPhoneInput';
|
import InternationalPhoneInput from '@/components/InternationalPhoneInput';
|
||||||
import SyncForm from '@/components/SyncForm';
|
import SyncForm from '@/components/SyncForm';
|
||||||
import { showSyncResult, SyncResultData } from '@/components/SyncResultMessage';
|
import { showSyncResult, SyncResultData } from '@/components/SyncResultMessage';
|
||||||
|
import { UploadOutlined } from '@ant-design/icons';
|
||||||
import { ORDER_STATUS_ENUM } from '@/constants';
|
import { ORDER_STATUS_ENUM } from '@/constants';
|
||||||
import { HistoryOrder } from '@/pages/Statistics/Order';
|
import { HistoryOrder } from '@/pages/Statistics/Order';
|
||||||
import {
|
import {
|
||||||
|
|
@ -24,6 +25,7 @@ import {
|
||||||
ordercontrollerSyncorderbyid,
|
ordercontrollerSyncorderbyid,
|
||||||
ordercontrollerSyncorders,
|
ordercontrollerSyncorders,
|
||||||
ordercontrollerUpdateorderitems,
|
ordercontrollerUpdateorderitems,
|
||||||
|
ordercontrollerImportwintopay,
|
||||||
} from '@/servers/api/order';
|
} from '@/servers/api/order';
|
||||||
import { productcontrollerSearchproducts } from '@/servers/api/product';
|
import { productcontrollerSearchproducts } from '@/servers/api/product';
|
||||||
import { sitecontrollerAll } from '@/servers/api/site';
|
import { sitecontrollerAll } from '@/servers/api/site';
|
||||||
|
|
@ -74,12 +76,16 @@ import {
|
||||||
Tabs,
|
Tabs,
|
||||||
TabsProps,
|
TabsProps,
|
||||||
Tag,
|
Tag,
|
||||||
|
Upload,
|
||||||
} from 'antd';
|
} from 'antd';
|
||||||
import React, { useMemo, useRef, useState } from 'react';
|
import React, { useMemo, useRef, useState } from 'react';
|
||||||
import RelatedOrders from '../../Subscription/Orders/RelatedOrders';
|
import RelatedOrders from '../../Subscription/Orders/RelatedOrders';
|
||||||
import dayjs from 'dayjs';
|
import dayjs from 'dayjs';
|
||||||
|
import * as XLSX from 'xlsx';
|
||||||
const ListPage: React.FC = () => {
|
const ListPage: React.FC = () => {
|
||||||
|
const [file, setFile] = useState<File | null>(null);
|
||||||
|
const [csvData, setCsvData] = useState<any[]>([]);
|
||||||
|
const [processedData, setProcessedData] = useState<any[]>([]);
|
||||||
const actionRef = useRef<ActionType>();
|
const actionRef = useRef<ActionType>();
|
||||||
const [activeKey, setActiveKey] = useState<string>('all');
|
const [activeKey, setActiveKey] = useState<string>('all');
|
||||||
const [count, setCount] = useState<any[]>([]);
|
const [count, setCount] = useState<any[]>([]);
|
||||||
|
|
@ -469,6 +475,64 @@ const ListPage: React.FC = () => {
|
||||||
];
|
];
|
||||||
const [selectedRowKeys, setSelectedRowKeys] = useState([]);
|
const [selectedRowKeys, setSelectedRowKeys] = useState([]);
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description 处理文件上传
|
||||||
|
*/
|
||||||
|
const handleFileUpload = (uploadedFile: File) => {
|
||||||
|
// 检查文件类型
|
||||||
|
if (!uploadedFile.name.match(/\.(xlsx)$/)) {
|
||||||
|
message.error('请上传 xlsx 格式的文件!');
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
setFile(uploadedFile);
|
||||||
|
|
||||||
|
const reader = new FileReader();
|
||||||
|
// 对于Excel文件,继续使用readAsArrayBuffer
|
||||||
|
reader.onload = (e) => {
|
||||||
|
try {
|
||||||
|
const data = e.target?.result;
|
||||||
|
// 如果是ArrayBuffer,使用type: 'array'来处理
|
||||||
|
const workbook = XLSX.read(data, { type: 'array' });
|
||||||
|
const sheetName = workbook.SheetNames[0];
|
||||||
|
const worksheet = workbook.Sheets[sheetName];
|
||||||
|
const jsonData = XLSX.utils.sheet_to_json(worksheet, { header: 1 });
|
||||||
|
|
||||||
|
if (jsonData.length < 2) {
|
||||||
|
message.error('文件为空或缺少表头!');
|
||||||
|
setCsvData([]);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
// 将数组转换为对象数组
|
||||||
|
const headers = jsonData[0] as string[];
|
||||||
|
const rows = jsonData.slice(1).map((rowArray: any) => {
|
||||||
|
const rowData: { [key: string]: any } = {};
|
||||||
|
headers.forEach((header, index) => {
|
||||||
|
rowData[header] = rowArray[index];
|
||||||
|
});
|
||||||
|
return rowData;
|
||||||
|
});
|
||||||
|
|
||||||
|
message.success(`成功解析 ${rows.length} 条数据.`);
|
||||||
|
setCsvData(rows);
|
||||||
|
setProcessedData([]); // 清空旧的处理结果
|
||||||
|
} catch (error) {
|
||||||
|
message.error('Excel文件解析失败,请检查文件格式!');
|
||||||
|
console.error('Excel Parse Error:', error);
|
||||||
|
setCsvData([]);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
reader.readAsArrayBuffer(uploadedFile);
|
||||||
|
|
||||||
|
|
||||||
|
reader.onerror = (error) => {
|
||||||
|
message.error('文件读取失败!');
|
||||||
|
console.error('File Read Error:', error);
|
||||||
|
};
|
||||||
|
|
||||||
|
return false; // 阻止antd Upload组件的默认上传行为
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<PageContainer ghost>
|
<PageContainer ghost>
|
||||||
<Tabs items={tabs} activeKey={activeKey} onChange={setActiveKey} />
|
<Tabs items={tabs} activeKey={activeKey} onChange={setActiveKey} />
|
||||||
|
|
@ -496,6 +560,55 @@ const ListPage: React.FC = () => {
|
||||||
}}
|
}}
|
||||||
toolBarRender={() => [
|
toolBarRender={() => [
|
||||||
// <CreateOrder tableRef={actionRef} />,
|
// <CreateOrder tableRef={actionRef} />,
|
||||||
|
<Upload
|
||||||
|
// beforeUpload={handleFileUpload}
|
||||||
|
name="file"
|
||||||
|
accept=".xlsx"
|
||||||
|
showUploadList={false}
|
||||||
|
maxCount={1}
|
||||||
|
customRequest={async (options) => {
|
||||||
|
const { file, onSuccess, onError } = options;
|
||||||
|
console.log(file);
|
||||||
|
const formData = new FormData();
|
||||||
|
formData.append('file', file);
|
||||||
|
try {
|
||||||
|
const res = await request('/order/import', {
|
||||||
|
method: 'POST',
|
||||||
|
data: formData,
|
||||||
|
requestType: 'form',
|
||||||
|
});
|
||||||
|
|
||||||
|
if (res?.success && res.data) {
|
||||||
|
// 使用xlsx将JSON数据转换为Excel
|
||||||
|
const XLSX = require('xlsx');
|
||||||
|
const worksheet = XLSX.utils.json_to_sheet(res.data);
|
||||||
|
const workbook = XLSX.utils.book_new();
|
||||||
|
XLSX.utils.book_append_sheet(workbook, worksheet, 'Orders');
|
||||||
|
const excelBuffer = XLSX.write(workbook, { bookType: 'xlsx', type: 'array' });
|
||||||
|
// 否则按原逻辑处理二进制数据
|
||||||
|
const blob = new Blob([excelBuffer], {
|
||||||
|
type: 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet',
|
||||||
|
});
|
||||||
|
const url = URL.createObjectURL(blob);
|
||||||
|
const a = document.createElement('a');
|
||||||
|
a.href = url;
|
||||||
|
a.download = 'orders.xlsx';
|
||||||
|
a.click();
|
||||||
|
URL.revokeObjectURL(url);
|
||||||
|
|
||||||
|
} else {
|
||||||
|
message.error(res.message || '导出失败');
|
||||||
|
}
|
||||||
|
actionRef.current?.reload();
|
||||||
|
setSelectedRowKeys([]);
|
||||||
|
} catch (error: any) {
|
||||||
|
message.error('导入失败: ' + (error.message || '未知错误'));
|
||||||
|
onError?.(error);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Button icon={<UploadOutlined />}>选择文件</Button>
|
||||||
|
</Upload>,
|
||||||
<SyncForm
|
<SyncForm
|
||||||
onFinish={async (values: any) => {
|
onFinish={async (values: any) => {
|
||||||
try {
|
try {
|
||||||
|
|
|
||||||
|
|
@ -238,7 +238,7 @@ const ProductGroupBy: React.FC = () => {
|
||||||
const { Title, Text } = Typography;
|
const { Title, Text } = Typography;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<PageContainer title="品牌空间">
|
<PageContainer title="聚合空间">
|
||||||
<div style={{ padding: '16px', background: '#fff' }}>
|
<div style={{ padding: '16px', background: '#fff' }}>
|
||||||
{/* Filter Section */}
|
{/* Filter Section */}
|
||||||
<div style={{ marginBottom: '24px' }}>
|
<div style={{ marginBottom: '24px' }}>
|
||||||
|
|
|
||||||
|
|
@ -35,7 +35,6 @@ const EditForm: React.FC<{
|
||||||
const [stockStatus, setStockStatus] = useState<
|
const [stockStatus, setStockStatus] = useState<
|
||||||
'in-stock' | 'out-of-stock' | null
|
'in-stock' | 'out-of-stock' | null
|
||||||
>(null);
|
>(null);
|
||||||
const [sites, setSites] = useState<any[]>([]);
|
|
||||||
|
|
||||||
const [categories, setCategories] = useState<any[]>([]);
|
const [categories, setCategories] = useState<any[]>([]);
|
||||||
const [activeAttributes, setActiveAttributes] = useState<any[]>([]);
|
const [activeAttributes, setActiveAttributes] = useState<any[]>([]);
|
||||||
|
|
@ -44,10 +43,6 @@ const EditForm: React.FC<{
|
||||||
productcontrollerGetcategoriesall().then((res: any) => {
|
productcontrollerGetcategoriesall().then((res: any) => {
|
||||||
setCategories(res?.data || []);
|
setCategories(res?.data || []);
|
||||||
});
|
});
|
||||||
// 获取站点列表用于站点SKU选择
|
|
||||||
sitecontrollerAll().then((res: any) => {
|
|
||||||
setSites(res?.data || []);
|
|
||||||
});
|
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
|
|
@ -118,9 +113,6 @@ const EditForm: React.FC<{
|
||||||
components: components,
|
components: components,
|
||||||
type: type,
|
type: type,
|
||||||
categoryId: (record as any).categoryId || (record as any).category?.id,
|
categoryId: (record as any).categoryId || (record as any).category?.id,
|
||||||
// 初始化站点SKU为字符串数组
|
|
||||||
// 修改后代码:
|
|
||||||
siteSkus: (record.siteSkus || []).map((code) => ({ code })),
|
|
||||||
};
|
};
|
||||||
}, [record, components, type]);
|
}, [record, components, type]);
|
||||||
return (
|
return (
|
||||||
|
|
@ -187,7 +179,7 @@ const EditForm: React.FC<{
|
||||||
attributes,
|
attributes,
|
||||||
type: values.type, // 直接使用 type
|
type: values.type, // 直接使用 type
|
||||||
categoryId: values.categoryId,
|
categoryId: values.categoryId,
|
||||||
siteSkus: values.siteSkus.map((v: { code: string }) => v.code) || [], // 直接传递字符串数组
|
siteSkus: values.siteSkus.map((v: { sku: string }) => v.sku) || [], // 直接传递字符串数组
|
||||||
// 连带更新 components
|
// 连带更新 components
|
||||||
components:
|
components:
|
||||||
values.type === 'bundle'
|
values.type === 'bundle'
|
||||||
|
|
@ -251,7 +243,7 @@ const EditForm: React.FC<{
|
||||||
)}
|
)}
|
||||||
>
|
>
|
||||||
<ProFormText
|
<ProFormText
|
||||||
name="code"
|
name="sku"
|
||||||
width="md"
|
width="md"
|
||||||
placeholder="请输入站点SKU"
|
placeholder="请输入站点SKU"
|
||||||
rules={[{ required: true, message: '请输入站点SKU' }]}
|
rules={[{ required: true, message: '请输入站点SKU' }]}
|
||||||
|
|
|
||||||
|
|
@ -228,7 +228,7 @@ const List: React.FC = () => {
|
||||||
<>
|
<>
|
||||||
{record.siteSkus?.map((siteSku, index) => (
|
{record.siteSkus?.map((siteSku, index) => (
|
||||||
<Tag key={index} color="cyan">
|
<Tag key={index} color="cyan">
|
||||||
{siteSku}
|
{siteSku.sku}
|
||||||
</Tag>
|
</Tag>
|
||||||
))}
|
))}
|
||||||
</>
|
</>
|
||||||
|
|
|
||||||
|
|
@ -42,7 +42,7 @@ const ListPage: React.FC = () => {
|
||||||
hideInTable: true,
|
hideInTable: true,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '产品名称',
|
title: '产品sku',
|
||||||
dataIndex: 'sku',
|
dataIndex: 'sku',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -149,6 +149,45 @@ export async function ordercontrollerGetordersales(
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 此处后端没有提供注释 POST /order/import */
|
||||||
|
export async function ordercontrollerImportwintopay(
|
||||||
|
body: {},
|
||||||
|
files?: File[],
|
||||||
|
options?: { [key: string]: any },
|
||||||
|
) {
|
||||||
|
const formData = new FormData();
|
||||||
|
|
||||||
|
if (files) {
|
||||||
|
files.forEach((f) => formData.append('files', f || ''));
|
||||||
|
}
|
||||||
|
|
||||||
|
Object.keys(body).forEach((ele) => {
|
||||||
|
const item = (body as any)[ele];
|
||||||
|
|
||||||
|
if (item !== undefined && item !== null) {
|
||||||
|
if (typeof item === 'object' && !(item instanceof File)) {
|
||||||
|
if (item instanceof Array) {
|
||||||
|
item.forEach((f) => formData.append(ele, f || ''));
|
||||||
|
} else {
|
||||||
|
formData.append(
|
||||||
|
ele,
|
||||||
|
new Blob([JSON.stringify(item)], { type: 'application/json' }),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
formData.append(ele, item);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
return request<any>('/order/import', {
|
||||||
|
method: 'POST',
|
||||||
|
data: formData,
|
||||||
|
requestType: 'form',
|
||||||
|
...(options || {}),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/** 此处后端没有提供注释 POST /order/order/cancel/${param0} */
|
/** 此处后端没有提供注释 POST /order/order/cancel/${param0} */
|
||||||
export async function ordercontrollerCancelorder(
|
export async function ordercontrollerCancelorder(
|
||||||
// 叠加生成的Param类型 (非body参数swagger默认没有生成对象)
|
// 叠加生成的Param类型 (非body参数swagger默认没有生成对象)
|
||||||
|
|
|
||||||
|
|
@ -460,6 +460,8 @@ declare namespace API {
|
||||||
tracking_id?: string;
|
tracking_id?: string;
|
||||||
/** 物流单号 */
|
/** 物流单号 */
|
||||||
tracking_number?: string;
|
tracking_number?: string;
|
||||||
|
/** 物流产品代码 */
|
||||||
|
tracking_product_code?: string;
|
||||||
/** 物流公司 */
|
/** 物流公司 */
|
||||||
shipping_provider?: string;
|
shipping_provider?: string;
|
||||||
/** 发货方式 */
|
/** 发货方式 */
|
||||||
|
|
@ -621,7 +623,7 @@ declare namespace API {
|
||||||
shipping_total?: number;
|
shipping_total?: number;
|
||||||
shipping_tax?: number;
|
shipping_tax?: number;
|
||||||
cart_tax?: number;
|
cart_tax?: number;
|
||||||
total?: number;
|
total?: any;
|
||||||
total_tax?: number;
|
total_tax?: number;
|
||||||
customer_id?: number;
|
customer_id?: number;
|
||||||
customer_email?: string;
|
customer_email?: string;
|
||||||
|
|
@ -820,7 +822,7 @@ declare namespace API {
|
||||||
shipping_total?: number;
|
shipping_total?: number;
|
||||||
shipping_tax?: number;
|
shipping_tax?: number;
|
||||||
cart_tax?: number;
|
cart_tax?: number;
|
||||||
total?: number;
|
total?: any;
|
||||||
total_tax?: number;
|
total_tax?: number;
|
||||||
customer_id?: number;
|
customer_id?: number;
|
||||||
customer_email?: string;
|
customer_email?: string;
|
||||||
|
|
@ -1634,7 +1636,7 @@ declare namespace API {
|
||||||
shipmentPlatform?: string;
|
shipmentPlatform?: string;
|
||||||
stockPointId?: number;
|
stockPointId?: number;
|
||||||
sender?: string;
|
sender?: string;
|
||||||
startPhone?: string;
|
startPhone?: Record<string, any>;
|
||||||
startPostalCode?: string;
|
startPostalCode?: string;
|
||||||
pickupAddress?: string;
|
pickupAddress?: string;
|
||||||
shipperCountryCode?: string;
|
shipperCountryCode?: string;
|
||||||
|
|
@ -1663,6 +1665,7 @@ declare namespace API {
|
||||||
phone_number?: string;
|
phone_number?: string;
|
||||||
phone_number_extension?: string;
|
phone_number_extension?: string;
|
||||||
phone_number_country?: string;
|
phone_number_country?: string;
|
||||||
|
email?: string;
|
||||||
/** 创建时间 */
|
/** 创建时间 */
|
||||||
createdAt: string;
|
createdAt: string;
|
||||||
/** 更新时间 */
|
/** 更新时间 */
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue