forked from yoone/WEB
refactor: 统一代码格式并优化多个页面组件
style: 修复文件末尾缺少换行符的问题 style: 调整CSS缩进和对象属性逗号格式 style: 统一React导入顺序和组件命名 fix: 修正统计页面图表数据展示格式 fix: 修复订单页面日期显示问题 feat(statistics): 添加按周统计订单功能 feat(login): 增加设备指纹验证功能 chore: 更新依赖包和配置项
This commit is contained in:
parent
bd3d9a00c5
commit
03af937edf
26
.umirc.ts
26
.umirc.ts
|
|
@ -1,10 +1,10 @@
|
||||||
import { defineConfig } from '@umijs/max';
|
import { defineConfig } from '@umijs/max';
|
||||||
|
import { codeInspectorPlugin } from 'code-inspector-plugin';
|
||||||
|
|
||||||
const isDev = process.env.NODE_ENV === 'development';
|
const isDev = process.env.NODE_ENV === 'development';
|
||||||
const UMI_APP_API_URL = isDev
|
const UMI_APP_API_URL = isDev
|
||||||
? 'http://localhost:7001'
|
? 'http://localhost:7001'
|
||||||
: 'https://api.yoone.ca';
|
: 'https://api.yoone.ca';
|
||||||
import { codeInspectorPlugin } from 'code-inspector-plugin';
|
|
||||||
|
|
||||||
export default defineConfig({
|
export default defineConfig({
|
||||||
hash: true,
|
hash: true,
|
||||||
|
|
@ -23,7 +23,7 @@ export default defineConfig({
|
||||||
config.plugin('code-inspector-plugin').use(
|
config.plugin('code-inspector-plugin').use(
|
||||||
codeInspectorPlugin({
|
codeInspectorPlugin({
|
||||||
bundler: 'webpack',
|
bundler: 'webpack',
|
||||||
})
|
}),
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
routes: [
|
routes: [
|
||||||
|
|
@ -44,17 +44,17 @@ export default defineConfig({
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: '站点管理',
|
name: '站点管理',
|
||||||
path: '/site',
|
path: '/site',
|
||||||
access: 'canSeeSite',
|
access: 'canSeeSite',
|
||||||
routes: [
|
routes: [
|
||||||
{
|
{
|
||||||
name: '站点列表',
|
name: '站点列表',
|
||||||
path: '/site/list',
|
path: '/site/list',
|
||||||
component: './Site/List',
|
component: './Site/List',
|
||||||
},
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: '商品管理',
|
name: '商品管理',
|
||||||
path: '/product',
|
path: '/product',
|
||||||
|
|
|
||||||
|
|
@ -1,15 +1,39 @@
|
||||||
export default (initialState: any) => {
|
export default (initialState: any) => {
|
||||||
const isSuper = initialState?.user?.isSuper ?? false;
|
const isSuper = initialState?.user?.isSuper ?? false;
|
||||||
const isAdmin = initialState?.user?.Admin ?? false;
|
const isAdmin = initialState?.user?.Admin ?? false;
|
||||||
const canSeeOrganiza = (isSuper || isAdmin) || (initialState?.user?.permissions?.includes('organiza') ?? false);
|
const canSeeOrganiza =
|
||||||
const canSeeProduct = (isSuper || isAdmin) || (initialState?.user?.permissions?.includes('product') ?? false);
|
isSuper ||
|
||||||
const canSeeStock = (isSuper || isAdmin) || (initialState?.user?.permissions?.includes('stock') ?? false);
|
isAdmin ||
|
||||||
const canSeeOrder = (isSuper || isAdmin) ||
|
(initialState?.user?.permissions?.includes('organiza') ?? false);
|
||||||
((initialState?.user?.permissions?.includes('order') ?? false) || (initialState?.user?.permissions?.includes('order-10-days') ?? false));
|
const canSeeProduct =
|
||||||
const canSeeCustomer = (isSuper || isAdmin) || (initialState?.user?.permissions?.includes('customer') ?? false);
|
isSuper ||
|
||||||
const canSeeLogistics = (isSuper || isAdmin) || (initialState?.user?.permissions?.includes('logistics') ?? false);
|
isAdmin ||
|
||||||
const canSeeStatistics = (isSuper || isAdmin) || (initialState?.user?.permissions?.includes('statistics') ?? false);
|
(initialState?.user?.permissions?.includes('product') ?? false);
|
||||||
const canSeeSite = (isSuper || isAdmin) || (initialState?.user?.permissions?.includes('site') ?? false);
|
const canSeeStock =
|
||||||
|
isSuper ||
|
||||||
|
isAdmin ||
|
||||||
|
(initialState?.user?.permissions?.includes('stock') ?? false);
|
||||||
|
const canSeeOrder =
|
||||||
|
isSuper ||
|
||||||
|
isAdmin ||
|
||||||
|
(initialState?.user?.permissions?.includes('order') ?? false) ||
|
||||||
|
(initialState?.user?.permissions?.includes('order-10-days') ?? false);
|
||||||
|
const canSeeCustomer =
|
||||||
|
isSuper ||
|
||||||
|
isAdmin ||
|
||||||
|
(initialState?.user?.permissions?.includes('customer') ?? false);
|
||||||
|
const canSeeLogistics =
|
||||||
|
isSuper ||
|
||||||
|
isAdmin ||
|
||||||
|
(initialState?.user?.permissions?.includes('logistics') ?? false);
|
||||||
|
const canSeeStatistics =
|
||||||
|
isSuper ||
|
||||||
|
isAdmin ||
|
||||||
|
(initialState?.user?.permissions?.includes('statistics') ?? false);
|
||||||
|
const canSeeSite =
|
||||||
|
isSuper ||
|
||||||
|
isAdmin ||
|
||||||
|
(initialState?.user?.permissions?.includes('site') ?? false);
|
||||||
|
|
||||||
return {
|
return {
|
||||||
canSeeOrganiza,
|
canSeeOrganiza,
|
||||||
|
|
|
||||||
|
|
@ -116,5 +116,5 @@ export const ORDER_STATUS_ENUM: ProSchemaValueEnumObj = {
|
||||||
refund_cancelled: {
|
refund_cancelled: {
|
||||||
text: '已取消退款',
|
text: '已取消退款',
|
||||||
status: 'refund_cancelled',
|
status: 'refund_cancelled',
|
||||||
}
|
},
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import { useEffect, useState } from 'react';
|
|
||||||
import FingerprintJS from '@fingerprintjs/fingerprintjs';
|
import FingerprintJS from '@fingerprintjs/fingerprintjs';
|
||||||
|
import { useEffect, useState } from 'react';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Hook: 获取设备指纹(visitorId)
|
* Hook: 获取设备指纹(visitorId)
|
||||||
|
|
|
||||||
|
|
@ -39,9 +39,9 @@ const ListPage: React.FC = () => {
|
||||||
{
|
{
|
||||||
title: '客户编号',
|
title: '客户编号',
|
||||||
dataIndex: 'customerId',
|
dataIndex: 'customerId',
|
||||||
render: (_, record) => {
|
render: (_, record) => {
|
||||||
if(!record.customerId) return '-';
|
if (!record.customerId) return '-';
|
||||||
return String(record.customerId).padStart(6,0)
|
return String(record.customerId).padStart(6, 0);
|
||||||
},
|
},
|
||||||
sorter: true,
|
sorter: true,
|
||||||
},
|
},
|
||||||
|
|
@ -95,31 +95,37 @@ const ListPage: React.FC = () => {
|
||||||
title: '等级',
|
title: '等级',
|
||||||
hideInSearch: true,
|
hideInSearch: true,
|
||||||
render: (_, record) => {
|
render: (_, record) => {
|
||||||
if(!record.yoone_orders || !record.yoone_total) return '-'
|
if (!record.yoone_orders || !record.yoone_total) return '-';
|
||||||
if(Number(record.yoone_orders) === 1 && Number(record.yoone_total) > 0 ) return 'B'
|
if (Number(record.yoone_orders) === 1 && Number(record.yoone_total) > 0)
|
||||||
return '-'
|
return 'B';
|
||||||
}
|
return '-';
|
||||||
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '评星',
|
title: '评星',
|
||||||
dataIndex: 'rate',
|
dataIndex: 'rate',
|
||||||
width: 200,
|
width: 200,
|
||||||
render: (_, record) => {
|
render: (_, record) => {
|
||||||
return <Rate onChange={async(val)=>{
|
return (
|
||||||
try{
|
<Rate
|
||||||
const { success, message: msg } = await customercontrollerSetrate({
|
onChange={async (val) => {
|
||||||
id: record.customerId,
|
try {
|
||||||
rate: val
|
const { success, message: msg } =
|
||||||
});
|
await customercontrollerSetrate({
|
||||||
if (success) {
|
id: record.customerId,
|
||||||
message.success(msg);
|
rate: val,
|
||||||
actionRef.current?.reload();
|
});
|
||||||
}
|
if (success) {
|
||||||
}catch(e){
|
message.success(msg);
|
||||||
message.error(e.message);
|
actionRef.current?.reload();
|
||||||
|
}
|
||||||
}
|
} catch (e) {
|
||||||
}} value={record.rate} />
|
message.error(e.message);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
value={record.rate}
|
||||||
|
/>
|
||||||
|
);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,4 @@
|
||||||
|
import { useDeviceFingerprint } from '@/hooks/useDeviceFingerprint';
|
||||||
import { usercontrollerGetuser, usercontrollerLogin } from '@/servers/api/user';
|
import { usercontrollerGetuser, usercontrollerLogin } from '@/servers/api/user';
|
||||||
import { LockOutlined, UserOutlined } from '@ant-design/icons';
|
import { LockOutlined, UserOutlined } from '@ant-design/icons';
|
||||||
import {
|
import {
|
||||||
|
|
@ -7,7 +8,6 @@ import {
|
||||||
} from '@ant-design/pro-components';
|
} from '@ant-design/pro-components';
|
||||||
import { history, useModel } from '@umijs/max';
|
import { history, useModel } from '@umijs/max';
|
||||||
import { App, theme } from 'antd';
|
import { App, theme } from 'antd';
|
||||||
import {useDeviceFingerprint} from '@/hooks/useDeviceFingerprint';
|
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
|
|
||||||
const Page = () => {
|
const Page = () => {
|
||||||
|
|
@ -15,28 +15,32 @@ const Page = () => {
|
||||||
const { token } = theme.useToken();
|
const { token } = theme.useToken();
|
||||||
const { message } = App.useApp();
|
const { message } = App.useApp();
|
||||||
const deviceId = useDeviceFingerprint();
|
const deviceId = useDeviceFingerprint();
|
||||||
const [ isAuth, setIsAuth ] = useState(false)
|
const [isAuth, setIsAuth] = useState(false);
|
||||||
|
|
||||||
console.log(deviceId) ;
|
console.log(deviceId);
|
||||||
|
|
||||||
const onFinish = async (values: { username: string; password: string }) => {
|
const onFinish = async (values: { username: string; password: string }) => {
|
||||||
try {
|
try {
|
||||||
const { data, success, code, message: msg } = await usercontrollerLogin({...values, deviceId});
|
const {
|
||||||
|
data,
|
||||||
|
success,
|
||||||
|
code,
|
||||||
|
message: msg,
|
||||||
|
} = await usercontrollerLogin({ ...values, deviceId });
|
||||||
if (success) {
|
if (success) {
|
||||||
message.success('登录成功');
|
message.success('登录成功');
|
||||||
localStorage.setItem('token', data?.token as string);
|
localStorage.setItem('token', data?.token as string);
|
||||||
const { data: user } = await usercontrollerGetuser();
|
const { data: user } = await usercontrollerGetuser();
|
||||||
setInitialState({ user });
|
setInitialState({ user });
|
||||||
history.push('/');
|
history.push('/');
|
||||||
return
|
return;
|
||||||
}
|
}
|
||||||
if(code === 10001){
|
if (code === 10001) {
|
||||||
message.info("验证码已发送至管理邮箱")
|
message.info('验证码已发送至管理邮箱');
|
||||||
setIsAuth(true);
|
setIsAuth(true);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
message.error(msg);
|
message.error(msg);
|
||||||
|
|
||||||
} catch {
|
} catch {
|
||||||
message.error('登录失败');
|
message.error('登录失败');
|
||||||
}
|
}
|
||||||
|
|
@ -100,16 +104,17 @@ const Page = () => {
|
||||||
},
|
},
|
||||||
]}
|
]}
|
||||||
/>
|
/>
|
||||||
{
|
{isAuth ? (
|
||||||
isAuth?
|
|
||||||
<ProFormText
|
<ProFormText
|
||||||
name="authCode"
|
name="authCode"
|
||||||
label="验证码"
|
label="验证码"
|
||||||
width="lg"
|
width="lg"
|
||||||
placeholder="请输入验证码"
|
placeholder="请输入验证码"
|
||||||
rules={[{ required: true, message: '请输入验证码' }]}
|
rules={[{ required: true, message: '请输入验证码' }]}
|
||||||
/>:<></>
|
/>
|
||||||
}
|
) : (
|
||||||
|
<></>
|
||||||
|
)}
|
||||||
{/* <div
|
{/* <div
|
||||||
style={{
|
style={{
|
||||||
marginBlockEnd: 24,
|
marginBlockEnd: 24,
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,14 @@
|
||||||
import { logisticscontrollerGetlist, logisticscontrollerGetshipmentlabel,
|
import {
|
||||||
logisticscontrollerDeleteshipment,
|
logisticscontrollerDeleteshipment,
|
||||||
logisticscontrollerUpdateshipmentstate
|
logisticscontrollerGetlist,
|
||||||
} from '@/servers/api/logistics';
|
logisticscontrollerGetshipmentlabel,
|
||||||
|
logisticscontrollerUpdateshipmentstate,
|
||||||
|
} from '@/servers/api/logistics';
|
||||||
|
import { sitecontrollerAll } from '@/servers/api/site';
|
||||||
import { stockcontrollerGetallstockpoints } from '@/servers/api/stock';
|
import { stockcontrollerGetallstockpoints } from '@/servers/api/stock';
|
||||||
import { formatUniuniShipmentState } from '@/utils/format';
|
import { formatUniuniShipmentState } from '@/utils/format';
|
||||||
import { printPDF } from '@/utils/util';
|
import { printPDF } from '@/utils/util';
|
||||||
import { CopyOutlined } from '@ant-design/icons';
|
import { CopyOutlined } from '@ant-design/icons';
|
||||||
import { ToastContainer, toast } from 'react-toastify';
|
|
||||||
import {
|
import {
|
||||||
ActionType,
|
ActionType,
|
||||||
PageContainer,
|
PageContainer,
|
||||||
|
|
@ -15,7 +17,7 @@ import {
|
||||||
} from '@ant-design/pro-components';
|
} from '@ant-design/pro-components';
|
||||||
import { App, Button, Divider, Popconfirm } from 'antd';
|
import { App, Button, Divider, Popconfirm } from 'antd';
|
||||||
import { useRef, useState } from 'react';
|
import { useRef, useState } from 'react';
|
||||||
import { sitecontrollerAll } from '@/servers/api/site';
|
import { ToastContainer } from 'react-toastify';
|
||||||
|
|
||||||
const ListPage: React.FC = () => {
|
const ListPage: React.FC = () => {
|
||||||
const actionRef = useRef<ActionType>();
|
const actionRef = useRef<ActionType>();
|
||||||
|
|
@ -69,7 +71,9 @@ const ListPage: React.FC = () => {
|
||||||
<CopyOutlined
|
<CopyOutlined
|
||||||
onClick={async () => {
|
onClick={async () => {
|
||||||
try {
|
try {
|
||||||
await navigator.clipboard.writeText(record.return_tracking_number);
|
await navigator.clipboard.writeText(
|
||||||
|
record.return_tracking_number,
|
||||||
|
);
|
||||||
message.success('复制成功!');
|
message.success('复制成功!');
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
message.error('复制失败!');
|
message.error('复制失败!');
|
||||||
|
|
@ -106,7 +110,9 @@ const ListPage: React.FC = () => {
|
||||||
disabled={isLoading}
|
disabled={isLoading}
|
||||||
onClick={async () => {
|
onClick={async () => {
|
||||||
setIsLoading(true);
|
setIsLoading(true);
|
||||||
const { data } = await logisticscontrollerGetshipmentlabel({shipmentId:record.id});
|
const { data } = await logisticscontrollerGetshipmentlabel({
|
||||||
|
shipmentId: record.id,
|
||||||
|
});
|
||||||
const content = data.content;
|
const content = data.content;
|
||||||
printPDF([content]);
|
printPDF([content]);
|
||||||
setIsLoading(false);
|
setIsLoading(false);
|
||||||
|
|
@ -120,7 +126,9 @@ const ListPage: React.FC = () => {
|
||||||
disabled={isLoading}
|
disabled={isLoading}
|
||||||
onClick={async () => {
|
onClick={async () => {
|
||||||
setIsLoading(true);
|
setIsLoading(true);
|
||||||
const res = await logisticscontrollerUpdateshipmentstate({shipmentId:record.id});
|
const res = await logisticscontrollerUpdateshipmentstate({
|
||||||
|
shipmentId: record.id,
|
||||||
|
});
|
||||||
console.log('res', res);
|
console.log('res', res);
|
||||||
|
|
||||||
setIsLoading(false);
|
setIsLoading(false);
|
||||||
|
|
@ -137,7 +145,7 @@ const ListPage: React.FC = () => {
|
||||||
try {
|
try {
|
||||||
setIsLoading(true);
|
setIsLoading(true);
|
||||||
const { success, message: errMsg } =
|
const { success, message: errMsg } =
|
||||||
await logisticscontrollerDeleteshipment({id:record.id});
|
await logisticscontrollerDeleteshipment({ id: record.id });
|
||||||
if (!success) {
|
if (!success) {
|
||||||
throw new Error(errMsg);
|
throw new Error(errMsg);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
import {
|
import {
|
||||||
logisticscontrollerGetservicelist,
|
logisticscontrollerGetservicelist,
|
||||||
logisticscontrollerSyncservices,
|
|
||||||
logisticscontrollerToggleactive,
|
logisticscontrollerToggleactive,
|
||||||
} from '@/servers/api/logistics';
|
} from '@/servers/api/logistics';
|
||||||
import {
|
import {
|
||||||
|
|
@ -10,7 +9,7 @@ import {
|
||||||
ProFormSwitch,
|
ProFormSwitch,
|
||||||
ProTable,
|
ProTable,
|
||||||
} from '@ant-design/pro-components';
|
} from '@ant-design/pro-components';
|
||||||
import { App, Button } from 'antd';
|
import { App } from 'antd';
|
||||||
import { useRef } from 'react';
|
import { useRef } from 'react';
|
||||||
|
|
||||||
const ListPage: React.FC = () => {
|
const ListPage: React.FC = () => {
|
||||||
|
|
|
||||||
|
|
@ -1,11 +1,15 @@
|
||||||
import React, { useRef } from 'react';
|
|
||||||
import { PageContainer } from '@ant-design/pro-layout';
|
|
||||||
import type { ProColumns, ActionType, ProTableProps } from '@ant-design/pro-components';
|
|
||||||
import { ProTable } from '@ant-design/pro-components';
|
|
||||||
import { App } from 'antd';
|
|
||||||
import dayjs from 'dayjs';
|
|
||||||
import { ordercontrollerGetordersales } from '@/servers/api/order';
|
import { ordercontrollerGetordersales } from '@/servers/api/order';
|
||||||
import { sitecontrollerAll } from '@/servers/api/site';
|
import { sitecontrollerAll } from '@/servers/api/site';
|
||||||
|
import type {
|
||||||
|
ActionType,
|
||||||
|
ProColumns,
|
||||||
|
ProTableProps,
|
||||||
|
} from '@ant-design/pro-components';
|
||||||
|
import { ProTable } from '@ant-design/pro-components';
|
||||||
|
import { PageContainer } from '@ant-design/pro-layout';
|
||||||
|
import { App } from 'antd';
|
||||||
|
import dayjs from 'dayjs';
|
||||||
|
import React, { useRef } from 'react';
|
||||||
|
|
||||||
// 列表行数据结构(订单商品聚合)
|
// 列表行数据结构(订单商品聚合)
|
||||||
interface OrderItemAggRow {
|
interface OrderItemAggRow {
|
||||||
|
|
@ -87,7 +91,10 @@ const OrderItemsPage: React.FC = () => {
|
||||||
request: async () => {
|
request: async () => {
|
||||||
// 拉取站点列表(后台 /site/all)
|
// 拉取站点列表(后台 /site/all)
|
||||||
const { data = [] } = await sitecontrollerAll();
|
const { data = [] } = await sitecontrollerAll();
|
||||||
return (data || []).map((item: any) => ({ label: item.siteName, value: item.id }));
|
return (data || []).map((item: any) => ({
|
||||||
|
label: item.siteName,
|
||||||
|
value: item.id,
|
||||||
|
}));
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -104,7 +111,9 @@ const OrderItemsPage: React.FC = () => {
|
||||||
];
|
];
|
||||||
|
|
||||||
// 表格请求方法:调用 /order/getOrderSales 接口并设置 isSource=true 获取订单项聚合
|
// 表格请求方法:调用 /order/getOrderSales 接口并设置 isSource=true 获取订单项聚合
|
||||||
const request: ProTableProps<OrderItemAggRow>['request'] = async (params:any) => {
|
const request: ProTableProps<OrderItemAggRow>['request'] = async (
|
||||||
|
params: any,
|
||||||
|
) => {
|
||||||
try {
|
try {
|
||||||
const { current = 1, pageSize = 10, siteId, name } = params as any;
|
const { current = 1, pageSize = 10, siteId, name } = params as any;
|
||||||
const [startDate, endDate] = (params as any).dateRange || [];
|
const [startDate, endDate] = (params as any).dateRange || [];
|
||||||
|
|
@ -115,7 +124,9 @@ const OrderItemsPage: React.FC = () => {
|
||||||
siteId,
|
siteId,
|
||||||
name,
|
name,
|
||||||
isSource: true as any,
|
isSource: true as any,
|
||||||
startDate: startDate ? (dayjs(startDate).toISOString() as any) : undefined,
|
startDate: startDate
|
||||||
|
? (dayjs(startDate).toISOString() as any)
|
||||||
|
: undefined,
|
||||||
endDate: endDate ? (dayjs(endDate).toISOString() as any) : undefined,
|
endDate: endDate ? (dayjs(endDate).toISOString() as any) : undefined,
|
||||||
} as any);
|
} as any);
|
||||||
const { success, data, message: errMsg } = resp as any;
|
const { success, data, message: errMsg } = resp as any;
|
||||||
|
|
@ -132,10 +143,12 @@ const OrderItemsPage: React.FC = () => {
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<PageContainer title='订单商品概览'>
|
<PageContainer title="订单商品概览">
|
||||||
<ProTable<OrderItemAggRow>
|
<ProTable<OrderItemAggRow>
|
||||||
actionRef={actionRef}
|
actionRef={actionRef}
|
||||||
rowKey={(r) => `${r.externalProductId}-${r.externalVariationId}-${r.name}`}
|
rowKey={(r) =>
|
||||||
|
`${r.externalProductId}-${r.externalVariationId}-${r.name}`
|
||||||
|
}
|
||||||
columns={columns}
|
columns={columns}
|
||||||
request={request}
|
request={request}
|
||||||
pagination={{ showSizeChanger: true }}
|
pagination={{ showSizeChanger: true }}
|
||||||
|
|
@ -146,4 +159,4 @@ const OrderItemsPage: React.FC = () => {
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default OrderItemsPage;
|
export default OrderItemsPage;
|
||||||
|
|
|
||||||
|
|
@ -1,16 +1,13 @@
|
||||||
import styles from '../../../style/order-list.css';
|
import styles from '../../../style/order-list.css';
|
||||||
|
|
||||||
import InternationalPhoneInput from '@/components/InternationalPhoneInput';
|
import InternationalPhoneInput from '@/components/InternationalPhoneInput';
|
||||||
import { HistoryOrder } from '@/pages/Statistics/Order';
|
|
||||||
import { ORDER_STATUS_ENUM } from '@/constants';
|
import { ORDER_STATUS_ENUM } from '@/constants';
|
||||||
|
import { HistoryOrder } from '@/pages/Statistics/Order';
|
||||||
import {
|
import {
|
||||||
logisticscontrollerCreateshipment,
|
logisticscontrollerCreateshipment,
|
||||||
logisticscontrollerGetshipmentfee,
|
|
||||||
logisticscontrollerDelshipment,
|
logisticscontrollerDelshipment,
|
||||||
logisticscontrollerGetpaymentmethods,
|
logisticscontrollerGetshipmentfee,
|
||||||
logisticscontrollerGetratelist,
|
|
||||||
logisticscontrollerGetshippingaddresslist,
|
logisticscontrollerGetshippingaddresslist,
|
||||||
// logisticscontrollerGetshipmentlabel,
|
|
||||||
} from '@/servers/api/logistics';
|
} from '@/servers/api/logistics';
|
||||||
import {
|
import {
|
||||||
ordercontrollerCancelorder,
|
ordercontrollerCancelorder,
|
||||||
|
|
@ -27,9 +24,9 @@ import {
|
||||||
ordercontrollerUpdateorderitems,
|
ordercontrollerUpdateorderitems,
|
||||||
} from '@/servers/api/order';
|
} from '@/servers/api/order';
|
||||||
import { productcontrollerSearchproducts } from '@/servers/api/product';
|
import { productcontrollerSearchproducts } from '@/servers/api/product';
|
||||||
import { wpproductcontrollerSearchproducts } from '@/servers/api/wpProduct';
|
|
||||||
import { sitecontrollerAll } from '@/servers/api/site';
|
import { sitecontrollerAll } from '@/servers/api/site';
|
||||||
import { stockcontrollerGetallstockpoints } from '@/servers/api/stock';
|
import { stockcontrollerGetallstockpoints } from '@/servers/api/stock';
|
||||||
|
import { wpproductcontrollerSearchproducts } from '@/servers/api/wpProduct';
|
||||||
import { formatShipmentState, formatSource } from '@/utils/format';
|
import { formatShipmentState, formatSource } from '@/utils/format';
|
||||||
import {
|
import {
|
||||||
CodeSandboxOutlined,
|
CodeSandboxOutlined,
|
||||||
|
|
@ -65,7 +62,6 @@ import {
|
||||||
Button,
|
Button,
|
||||||
Card,
|
Card,
|
||||||
Col,
|
Col,
|
||||||
Descriptions,
|
|
||||||
Divider,
|
Divider,
|
||||||
Drawer,
|
Drawer,
|
||||||
Dropdown,
|
Dropdown,
|
||||||
|
|
@ -79,10 +75,8 @@ import {
|
||||||
TabsProps,
|
TabsProps,
|
||||||
Tag,
|
Tag,
|
||||||
} from 'antd';
|
} from 'antd';
|
||||||
import Item from 'antd/es/list/Item';
|
|
||||||
import RelatedOrders from '../../Subscription/Orders/RelatedOrders';
|
|
||||||
import React, { useMemo, useRef, useState } from 'react';
|
import React, { useMemo, useRef, useState } from 'react';
|
||||||
import { printPDF } from '@/utils/util';
|
import RelatedOrders from '../../Subscription/Orders/RelatedOrders';
|
||||||
|
|
||||||
const ListPage: React.FC = () => {
|
const ListPage: React.FC = () => {
|
||||||
const actionRef = useRef<ActionType>();
|
const actionRef = useRef<ActionType>();
|
||||||
|
|
@ -129,14 +123,13 @@ const ListPage: React.FC = () => {
|
||||||
label: '已申请退款',
|
label: '已申请退款',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
||||||
key: 'refund_approved',
|
key: 'refund_approved',
|
||||||
label: "已退款",
|
label: '已退款',
|
||||||
// label: '退款申请已通过',
|
// label: '退款申请已通过',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'refund_cancelled',
|
key: 'refund_cancelled',
|
||||||
label: "已完成"
|
label: '已完成',
|
||||||
// label: '已取消退款',
|
// label: '已取消退款',
|
||||||
},
|
},
|
||||||
// {
|
// {
|
||||||
|
|
@ -167,21 +160,32 @@ const ListPage: React.FC = () => {
|
||||||
dataIndex: 'id',
|
dataIndex: 'id',
|
||||||
hideInSearch: true,
|
hideInSearch: true,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
title: '订单ID',
|
||||||
|
dataIndex: 'externalOrderId',
|
||||||
|
},
|
||||||
{
|
{
|
||||||
title: '日期',
|
title: '日期',
|
||||||
dataIndex: 'date',
|
dataIndex: 'date',
|
||||||
hideInTable: true,
|
hideInTable: true,
|
||||||
valueType: 'dateRange',
|
valueType: 'dateRange',
|
||||||
},
|
},
|
||||||
|
|
||||||
{
|
{
|
||||||
title: '订阅',
|
title: '订阅',
|
||||||
dataIndex: 'isSubscription',
|
dataIndex: 'isSubscription',
|
||||||
hideInSearch: true,
|
hideInSearch: true,
|
||||||
render: (_, record) => {
|
render: (_, record) => {
|
||||||
const related = Array.isArray((record as any)?.related) ? (record as any).related : [];
|
const related = Array.isArray((record as any)?.related)
|
||||||
const isSub = related.some((it) => it?.externalSubscriptionId || it?.billing_period || it?.line_items);
|
? (record as any).related
|
||||||
return <Tag color={isSub ? 'green' : 'default'}>{isSub ? '是' : '否'}</Tag>;
|
: [];
|
||||||
|
const isSub = related.some(
|
||||||
|
(it) =>
|
||||||
|
it?.externalSubscriptionId || it?.billing_period || it?.line_items,
|
||||||
|
);
|
||||||
|
return (
|
||||||
|
<Tag color={isSub ? 'green' : 'default'}>{isSub ? '是' : '否'}</Tag>
|
||||||
|
);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -235,7 +239,7 @@ const ListPage: React.FC = () => {
|
||||||
dataIndex: 'billing_phone',
|
dataIndex: 'billing_phone',
|
||||||
render: (_, record) => record.shipping?.phone || record.billing?.phone,
|
render: (_, record) => record.shipping?.phone || record.billing?.phone,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '换货次数',
|
title: '换货次数',
|
||||||
dataIndex: 'exchange_frequency',
|
dataIndex: 'exchange_frequency',
|
||||||
hideInSearch: true,
|
hideInSearch: true,
|
||||||
|
|
@ -306,7 +310,11 @@ const ListPage: React.FC = () => {
|
||||||
record.orderStatus,
|
record.orderStatus,
|
||||||
) ? (
|
) ? (
|
||||||
<>
|
<>
|
||||||
<Shipping id={record.id as number} tableRef={actionRef} setActiveLine={setActiveLine} />
|
<Shipping
|
||||||
|
id={record.id as number}
|
||||||
|
tableRef={actionRef}
|
||||||
|
setActiveLine={setActiveLine}
|
||||||
|
/>
|
||||||
<Divider type="vertical" />
|
<Divider type="vertical" />
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
|
|
@ -359,11 +367,12 @@ const ListPage: React.FC = () => {
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'history',
|
key: 'history',
|
||||||
label:
|
label: (
|
||||||
<HistoryOrder
|
<HistoryOrder
|
||||||
email={record.customer_email}
|
email={record.customer_email}
|
||||||
tableRef={actionRef}
|
tableRef={actionRef}
|
||||||
/>,
|
/>
|
||||||
|
),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
key: 'note',
|
key: 'note',
|
||||||
|
|
@ -436,7 +445,9 @@ const ListPage: React.FC = () => {
|
||||||
actionRef={actionRef}
|
actionRef={actionRef}
|
||||||
rowKey="id"
|
rowKey="id"
|
||||||
rowClassName={(record) => {
|
rowClassName={(record) => {
|
||||||
return record.id === activeLine ? styles['selected-line-order-protable'] : '';
|
return record.id === activeLine
|
||||||
|
? styles['selected-line-order-protable']
|
||||||
|
: '';
|
||||||
}}
|
}}
|
||||||
toolBarRender={() => [
|
toolBarRender={() => [
|
||||||
<CreateOrder tableRef={actionRef} />,
|
<CreateOrder tableRef={actionRef} />,
|
||||||
|
|
@ -525,7 +536,7 @@ const Detail: React.FC<{
|
||||||
tableRef: React.MutableRefObject<ActionType | undefined>;
|
tableRef: React.MutableRefObject<ActionType | undefined>;
|
||||||
orderId: number;
|
orderId: number;
|
||||||
record: API.Order;
|
record: API.Order;
|
||||||
setActiveLine: Function
|
setActiveLine: Function;
|
||||||
}> = ({ tableRef, orderId, record, setActiveLine }) => {
|
}> = ({ tableRef, orderId, record, setActiveLine }) => {
|
||||||
const [visiable, setVisiable] = useState(false);
|
const [visiable, setVisiable] = useState(false);
|
||||||
const { message } = App.useApp();
|
const { message } = App.useApp();
|
||||||
|
|
@ -557,10 +568,14 @@ const Detail: React.FC<{
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<Button key="detail" type="primary" onClick={() => {
|
<Button
|
||||||
setVisiable(true);
|
key="detail"
|
||||||
setActiveLine(record.id);
|
type="primary"
|
||||||
}}>
|
onClick={() => {
|
||||||
|
setVisiable(true);
|
||||||
|
setActiveLine(record.id);
|
||||||
|
}}
|
||||||
|
>
|
||||||
<FileDoneOutlined />
|
<FileDoneOutlined />
|
||||||
详情
|
详情
|
||||||
</Button>
|
</Button>
|
||||||
|
|
@ -577,29 +592,29 @@ const Detail: React.FC<{
|
||||||
)
|
)
|
||||||
? []
|
? []
|
||||||
: [
|
: [
|
||||||
<Divider type="vertical" />,
|
<Divider type="vertical" />,
|
||||||
<Button
|
<Button
|
||||||
type="primary"
|
type="primary"
|
||||||
onClick={async () => {
|
onClick={async () => {
|
||||||
try {
|
try {
|
||||||
const { success, message: errMsg } =
|
const { success, message: errMsg } =
|
||||||
await ordercontrollerSyncorderbyid({
|
await ordercontrollerSyncorderbyid({
|
||||||
siteId: record.siteId as string,
|
siteId: record.siteId as string,
|
||||||
orderId: record.externalOrderId as string,
|
orderId: record.externalOrderId as string,
|
||||||
});
|
});
|
||||||
if (!success) {
|
if (!success) {
|
||||||
throw new Error(errMsg);
|
throw new Error(errMsg);
|
||||||
|
}
|
||||||
|
message.success('同步成功');
|
||||||
|
tableRef.current?.reload();
|
||||||
|
} catch (error) {
|
||||||
|
message.error(error?.message || '同步失败');
|
||||||
}
|
}
|
||||||
message.success('同步成功');
|
}}
|
||||||
tableRef.current?.reload();
|
>
|
||||||
} catch (error) {
|
同步订单
|
||||||
message.error(error?.message || '同步失败');
|
</Button>,
|
||||||
}
|
]),
|
||||||
}}
|
|
||||||
>
|
|
||||||
同步订单
|
|
||||||
</Button>,
|
|
||||||
]),
|
|
||||||
// ...(['processing', 'pending_reshipment'].includes(record.orderStatus)
|
// ...(['processing', 'pending_reshipment'].includes(record.orderStatus)
|
||||||
// ? [
|
// ? [
|
||||||
// <Divider type="vertical" />,
|
// <Divider type="vertical" />,
|
||||||
|
|
@ -618,136 +633,136 @@ const Detail: React.FC<{
|
||||||
'pending_refund',
|
'pending_refund',
|
||||||
].includes(record.orderStatus)
|
].includes(record.orderStatus)
|
||||||
? [
|
? [
|
||||||
<Divider type="vertical" />,
|
<Divider type="vertical" />,
|
||||||
<Popconfirm
|
<Popconfirm
|
||||||
title="转至售后"
|
title="转至售后"
|
||||||
description="确认转至售后?"
|
description="确认转至售后?"
|
||||||
onConfirm={async () => {
|
onConfirm={async () => {
|
||||||
try {
|
try {
|
||||||
const { success, message: errMsg } =
|
const { success, message: errMsg } =
|
||||||
await ordercontrollerChangestatus(
|
await ordercontrollerChangestatus(
|
||||||
{
|
{
|
||||||
id: record.id,
|
id: record.id,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
status: 'after_sale_pending',
|
status: 'after_sale_pending',
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
if (!success) {
|
if (!success) {
|
||||||
throw new Error(errMsg);
|
throw new Error(errMsg);
|
||||||
|
}
|
||||||
|
tableRef.current?.reload();
|
||||||
|
} catch (error: any) {
|
||||||
|
message.error(error.message);
|
||||||
}
|
}
|
||||||
tableRef.current?.reload();
|
}}
|
||||||
} catch (error: any) {
|
>
|
||||||
message.error(error.message);
|
<Button type="primary" ghost>
|
||||||
}
|
转至售后
|
||||||
}}
|
</Button>
|
||||||
>
|
</Popconfirm>,
|
||||||
<Button type="primary" ghost>
|
]
|
||||||
转至售后
|
|
||||||
</Button>
|
|
||||||
</Popconfirm>,
|
|
||||||
]
|
|
||||||
: []),
|
: []),
|
||||||
...(record.orderStatus === 'after_sale_pending'
|
...(record.orderStatus === 'after_sale_pending'
|
||||||
? [
|
? [
|
||||||
<Divider type="vertical" />,
|
<Divider type="vertical" />,
|
||||||
<Popconfirm
|
<Popconfirm
|
||||||
title="转至取消"
|
title="转至取消"
|
||||||
description="确认转至取消?"
|
description="确认转至取消?"
|
||||||
onConfirm={async () => {
|
onConfirm={async () => {
|
||||||
try {
|
try {
|
||||||
const { success, message: errMsg } =
|
const { success, message: errMsg } =
|
||||||
await ordercontrollerCancelorder({
|
await ordercontrollerCancelorder({
|
||||||
id: record.id,
|
|
||||||
});
|
|
||||||
if (!success) {
|
|
||||||
throw new Error(errMsg);
|
|
||||||
}
|
|
||||||
tableRef.current?.reload();
|
|
||||||
} catch (error: any) {
|
|
||||||
message.error(error.message);
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Button type="primary" ghost>
|
|
||||||
转至取消
|
|
||||||
</Button>
|
|
||||||
</Popconfirm>,
|
|
||||||
<Divider type="vertical" />,
|
|
||||||
<Popconfirm
|
|
||||||
title="转至退款"
|
|
||||||
description="确认转至退款?"
|
|
||||||
onConfirm={async () => {
|
|
||||||
try {
|
|
||||||
const { success, message: errMsg } =
|
|
||||||
await ordercontrollerRefundorder({
|
|
||||||
id: record.id,
|
|
||||||
});
|
|
||||||
if (!success) {
|
|
||||||
throw new Error(errMsg);
|
|
||||||
}
|
|
||||||
tableRef.current?.reload();
|
|
||||||
} catch (error: any) {
|
|
||||||
message.error(error.message);
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Button type="primary" ghost>
|
|
||||||
转至退款
|
|
||||||
</Button>
|
|
||||||
</Popconfirm>,
|
|
||||||
<Divider type="vertical" />,
|
|
||||||
<Popconfirm
|
|
||||||
title="转至完成"
|
|
||||||
description="确认转至完成?"
|
|
||||||
onConfirm={async () => {
|
|
||||||
try {
|
|
||||||
const { success, message: errMsg } =
|
|
||||||
await ordercontrollerCompletedorder({
|
|
||||||
id: record.id,
|
|
||||||
});
|
|
||||||
if (!success) {
|
|
||||||
throw new Error(errMsg);
|
|
||||||
}
|
|
||||||
tableRef.current?.reload();
|
|
||||||
} catch (error: any) {
|
|
||||||
message.error(error.message);
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
<Button type="primary" ghost>
|
|
||||||
转至完成
|
|
||||||
</Button>
|
|
||||||
</Popconfirm>,
|
|
||||||
<Divider type="vertical" />,
|
|
||||||
<Popconfirm
|
|
||||||
title="转至待补发"
|
|
||||||
description="确认转至待补发?"
|
|
||||||
onConfirm={async () => {
|
|
||||||
try {
|
|
||||||
const { success, message: errMsg } =
|
|
||||||
await ordercontrollerChangestatus(
|
|
||||||
{
|
|
||||||
id: record.id,
|
id: record.id,
|
||||||
},
|
});
|
||||||
{
|
if (!success) {
|
||||||
status: 'pending_reshipment',
|
throw new Error(errMsg);
|
||||||
},
|
}
|
||||||
);
|
tableRef.current?.reload();
|
||||||
if (!success) {
|
} catch (error: any) {
|
||||||
throw new Error(errMsg);
|
message.error(error.message);
|
||||||
}
|
}
|
||||||
tableRef.current?.reload();
|
}}
|
||||||
} catch (error: any) {
|
>
|
||||||
message.error(error.message);
|
<Button type="primary" ghost>
|
||||||
}
|
转至取消
|
||||||
}}
|
</Button>
|
||||||
>
|
</Popconfirm>,
|
||||||
<Button type="primary" ghost>
|
<Divider type="vertical" />,
|
||||||
转至待补发
|
<Popconfirm
|
||||||
</Button>
|
title="转至退款"
|
||||||
</Popconfirm>,
|
description="确认转至退款?"
|
||||||
]
|
onConfirm={async () => {
|
||||||
|
try {
|
||||||
|
const { success, message: errMsg } =
|
||||||
|
await ordercontrollerRefundorder({
|
||||||
|
id: record.id,
|
||||||
|
});
|
||||||
|
if (!success) {
|
||||||
|
throw new Error(errMsg);
|
||||||
|
}
|
||||||
|
tableRef.current?.reload();
|
||||||
|
} catch (error: any) {
|
||||||
|
message.error(error.message);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Button type="primary" ghost>
|
||||||
|
转至退款
|
||||||
|
</Button>
|
||||||
|
</Popconfirm>,
|
||||||
|
<Divider type="vertical" />,
|
||||||
|
<Popconfirm
|
||||||
|
title="转至完成"
|
||||||
|
description="确认转至完成?"
|
||||||
|
onConfirm={async () => {
|
||||||
|
try {
|
||||||
|
const { success, message: errMsg } =
|
||||||
|
await ordercontrollerCompletedorder({
|
||||||
|
id: record.id,
|
||||||
|
});
|
||||||
|
if (!success) {
|
||||||
|
throw new Error(errMsg);
|
||||||
|
}
|
||||||
|
tableRef.current?.reload();
|
||||||
|
} catch (error: any) {
|
||||||
|
message.error(error.message);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Button type="primary" ghost>
|
||||||
|
转至完成
|
||||||
|
</Button>
|
||||||
|
</Popconfirm>,
|
||||||
|
<Divider type="vertical" />,
|
||||||
|
<Popconfirm
|
||||||
|
title="转至待补发"
|
||||||
|
description="确认转至待补发?"
|
||||||
|
onConfirm={async () => {
|
||||||
|
try {
|
||||||
|
const { success, message: errMsg } =
|
||||||
|
await ordercontrollerChangestatus(
|
||||||
|
{
|
||||||
|
id: record.id,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
status: 'pending_reshipment',
|
||||||
|
},
|
||||||
|
);
|
||||||
|
if (!success) {
|
||||||
|
throw new Error(errMsg);
|
||||||
|
}
|
||||||
|
tableRef.current?.reload();
|
||||||
|
} catch (error: any) {
|
||||||
|
message.error(error.message);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Button type="primary" ghost>
|
||||||
|
转至待补发
|
||||||
|
</Button>
|
||||||
|
</Popconfirm>,
|
||||||
|
]
|
||||||
: []),
|
: []),
|
||||||
]}
|
]}
|
||||||
>
|
>
|
||||||
|
|
@ -781,7 +796,9 @@ const Detail: React.FC<{
|
||||||
/>
|
/>
|
||||||
<ProDescriptions.Item label="金额" dataIndex="total" />
|
<ProDescriptions.Item label="金额" dataIndex="total" />
|
||||||
<ProDescriptions.Item label="客户邮箱" dataIndex="customer_email" />
|
<ProDescriptions.Item label="客户邮箱" dataIndex="customer_email" />
|
||||||
<ProDescriptions.Item label="联系电话" span={3}
|
<ProDescriptions.Item
|
||||||
|
label="联系电话"
|
||||||
|
span={3}
|
||||||
render={(_, record) => {
|
render={(_, record) => {
|
||||||
return (
|
return (
|
||||||
<div>
|
<div>
|
||||||
|
|
@ -790,7 +807,8 @@ const Detail: React.FC<{
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
}} />
|
}}
|
||||||
|
/>
|
||||||
<ProDescriptions.Item label="交易Id" dataIndex="transaction_id" />
|
<ProDescriptions.Item label="交易Id" dataIndex="transaction_id" />
|
||||||
<ProDescriptions.Item label="IP" dataIndex="customer_id_address" />
|
<ProDescriptions.Item label="IP" dataIndex="customer_id_address" />
|
||||||
<ProDescriptions.Item label="设备" dataIndex="device_type" />
|
<ProDescriptions.Item label="设备" dataIndex="device_type" />
|
||||||
|
|
@ -902,7 +920,7 @@ const Detail: React.FC<{
|
||||||
<ul>
|
<ul>
|
||||||
{record?.items?.map((item: any) => (
|
{record?.items?.map((item: any) => (
|
||||||
<li key={item.id}>
|
<li key={item.id}>
|
||||||
{item.name}:{item.quantity}
|
{item.name}:{item.quantity}
|
||||||
</li>
|
</li>
|
||||||
))}
|
))}
|
||||||
</ul>
|
</ul>
|
||||||
|
|
@ -910,13 +928,13 @@ const Detail: React.FC<{
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
{/* 显示 related order */}
|
{/* 显示 related order */}
|
||||||
<ProDescriptions.Item
|
<ProDescriptions.Item
|
||||||
label="关联"
|
label="关联"
|
||||||
span={3}
|
span={3}
|
||||||
render={(_, record) => {
|
render={(_, record) => {
|
||||||
return <RelatedOrders data={record?.related} />;
|
return <RelatedOrders data={record?.related} />;
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
{/* 订单内容 */}
|
{/* 订单内容 */}
|
||||||
<ProDescriptions.Item
|
<ProDescriptions.Item
|
||||||
label="订单内容"
|
label="订单内容"
|
||||||
|
|
@ -937,12 +955,7 @@ const Detail: React.FC<{
|
||||||
label="换货"
|
label="换货"
|
||||||
span={3}
|
span={3}
|
||||||
render={(_, record) => {
|
render={(_, record) => {
|
||||||
return (
|
return <SalesChange detailRef={ref} id={record.id as number} />;
|
||||||
<SalesChange
|
|
||||||
detailRef={ref}
|
|
||||||
id={record.id as number}
|
|
||||||
/>
|
|
||||||
)
|
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<ProDescriptions.Item
|
<ProDescriptions.Item
|
||||||
|
|
@ -1011,31 +1024,31 @@ const Detail: React.FC<{
|
||||||
}
|
}
|
||||||
actions={
|
actions={
|
||||||
v.state === 'waiting-for-scheduling' ||
|
v.state === 'waiting-for-scheduling' ||
|
||||||
v.state === 'waiting-for-transit'
|
v.state === 'waiting-for-transit'
|
||||||
? [
|
? [
|
||||||
<Popconfirm
|
<Popconfirm
|
||||||
title="取消运单"
|
title="取消运单"
|
||||||
description="确认取消运单?"
|
description="确认取消运单?"
|
||||||
onConfirm={async () => {
|
onConfirm={async () => {
|
||||||
try {
|
try {
|
||||||
const { success, message: errMsg } =
|
const { success, message: errMsg } =
|
||||||
await logisticscontrollerDelshipment({
|
await logisticscontrollerDelshipment({
|
||||||
id: v.id,
|
id: v.id,
|
||||||
});
|
});
|
||||||
if (!success) {
|
if (!success) {
|
||||||
throw new Error(errMsg);
|
throw new Error(errMsg);
|
||||||
|
}
|
||||||
|
tableRef.current?.reload();
|
||||||
|
initRequest();
|
||||||
|
} catch (error: any) {
|
||||||
|
message.error(error.message);
|
||||||
}
|
}
|
||||||
tableRef.current?.reload();
|
}}
|
||||||
initRequest();
|
>
|
||||||
} catch (error: any) {
|
<DeleteFilled />
|
||||||
message.error(error.message);
|
取消运单
|
||||||
}
|
</Popconfirm>,
|
||||||
}}
|
]
|
||||||
>
|
|
||||||
<DeleteFilled />
|
|
||||||
取消运单
|
|
||||||
</Popconfirm>,
|
|
||||||
]
|
|
||||||
: []
|
: []
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
|
|
@ -1194,7 +1207,8 @@ const Shipping: React.FC<{
|
||||||
},
|
},
|
||||||
}}
|
}}
|
||||||
trigger={
|
trigger={
|
||||||
<Button type="primary"
|
<Button
|
||||||
|
type="primary"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
setActiveLine(id);
|
setActiveLine(id);
|
||||||
}}
|
}}
|
||||||
|
|
@ -1289,10 +1303,16 @@ const Shipping: React.FC<{
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
|
||||||
};
|
};
|
||||||
}}
|
}}
|
||||||
onFinish={async ({ customer_note, notes, items, details, externalOrderId, ...data }) => {
|
onFinish={async ({
|
||||||
|
customer_note,
|
||||||
|
notes,
|
||||||
|
items,
|
||||||
|
details,
|
||||||
|
externalOrderId,
|
||||||
|
...data
|
||||||
|
}) => {
|
||||||
details.origin.email_addresses =
|
details.origin.email_addresses =
|
||||||
details.origin.email_addresses.split(',');
|
details.origin.email_addresses.split(',');
|
||||||
details.destination.email_addresses =
|
details.destination.email_addresses =
|
||||||
|
|
@ -1301,14 +1321,17 @@ const Shipping: React.FC<{
|
||||||
details.destination.phone_number.phone;
|
details.destination.phone_number.phone;
|
||||||
details.origin.phone_number.number = details.origin.phone_number.phone;
|
details.origin.phone_number.number = details.origin.phone_number.phone;
|
||||||
try {
|
try {
|
||||||
const { success, message: errMsg, ...resShipment } =
|
const {
|
||||||
await logisticscontrollerCreateshipment(
|
success,
|
||||||
{ orderId: id },
|
message: errMsg,
|
||||||
{
|
...resShipment
|
||||||
details,
|
} = await logisticscontrollerCreateshipment(
|
||||||
...data,
|
{ orderId: id },
|
||||||
},
|
{
|
||||||
);
|
details,
|
||||||
|
...data,
|
||||||
|
},
|
||||||
|
);
|
||||||
if (!success) throw new Error(errMsg);
|
if (!success) throw new Error(errMsg);
|
||||||
message.success('创建成功');
|
message.success('创建成功');
|
||||||
tableRef?.current?.reload();
|
tableRef?.current?.reload();
|
||||||
|
|
@ -1344,11 +1367,7 @@ const Shipping: React.FC<{
|
||||||
}
|
}
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<ProFormText
|
<ProFormText label="订单号" readonly name={'externalOrderId'} />
|
||||||
label="订单号"
|
|
||||||
readonly
|
|
||||||
name={"externalOrderId"}
|
|
||||||
/>
|
|
||||||
<ProFormText label="客户备注" readonly name="customer_note" />
|
<ProFormText label="客户备注" readonly name="customer_note" />
|
||||||
<ProFormList
|
<ProFormList
|
||||||
label="后台备注"
|
label="后台备注"
|
||||||
|
|
@ -1417,16 +1436,16 @@ const Shipping: React.FC<{
|
||||||
<ProFormList
|
<ProFormList
|
||||||
label="发货产品"
|
label="发货产品"
|
||||||
name="sales"
|
name="sales"
|
||||||
// rules={[
|
// rules={[
|
||||||
// {
|
// {
|
||||||
// required: true,
|
// required: true,
|
||||||
// message: '至少需要一个商品',
|
// message: '至少需要一个商品',
|
||||||
// validator: (_, value) =>
|
// validator: (_, value) =>
|
||||||
// value && value.length > 0
|
// value && value.length > 0
|
||||||
// ? Promise.resolve()
|
// ? Promise.resolve()
|
||||||
// : Promise.reject('至少需要一个商品'),
|
// : Promise.reject('至少需要一个商品'),
|
||||||
// },
|
// },
|
||||||
// ]}
|
// ]}
|
||||||
>
|
>
|
||||||
<ProForm.Group>
|
<ProForm.Group>
|
||||||
<ProFormSelect
|
<ProFormSelect
|
||||||
|
|
@ -1848,7 +1867,7 @@ const Shipping: React.FC<{
|
||||||
name="description"
|
name="description"
|
||||||
placeholder="请输入描述"
|
placeholder="请输入描述"
|
||||||
width="lg"
|
width="lg"
|
||||||
// rules={[{ required: true, message: '请输入描述' }]}
|
// rules={[{ required: true, message: '请输入描述' }]}
|
||||||
/>
|
/>
|
||||||
</ProForm.Group>
|
</ProForm.Group>
|
||||||
</ProFormList>
|
</ProFormList>
|
||||||
|
|
@ -1901,7 +1920,14 @@ const Shipping: React.FC<{
|
||||||
loading={ratesLoading}
|
loading={ratesLoading}
|
||||||
onClick={async () => {
|
onClick={async () => {
|
||||||
try {
|
try {
|
||||||
const { customer_note, notes, items, details, externalOrderId, ...data } = formRef.current?.getFieldsValue();
|
const {
|
||||||
|
customer_note,
|
||||||
|
notes,
|
||||||
|
items,
|
||||||
|
details,
|
||||||
|
externalOrderId,
|
||||||
|
...data
|
||||||
|
} = formRef.current?.getFieldsValue();
|
||||||
const originEmail = details.origin.email_addresses;
|
const originEmail = details.origin.email_addresses;
|
||||||
const destinationEmail = details.destination.email_addresses;
|
const destinationEmail = details.destination.email_addresses;
|
||||||
details.origin.email_addresses =
|
details.origin.email_addresses =
|
||||||
|
|
@ -1910,41 +1936,54 @@ const Shipping: React.FC<{
|
||||||
details.destination.email_addresses.split(',');
|
details.destination.email_addresses.split(',');
|
||||||
details.destination.phone_number.number =
|
details.destination.phone_number.number =
|
||||||
details.destination.phone_number.phone;
|
details.destination.phone_number.phone;
|
||||||
details.origin.phone_number.number = details.origin.phone_number.phone;
|
details.origin.phone_number.number =
|
||||||
const res =
|
details.origin.phone_number.phone;
|
||||||
await logisticscontrollerGetshipmentfee(
|
const res = await logisticscontrollerGetshipmentfee({
|
||||||
{
|
stockPointId: data.stockPointId,
|
||||||
stockPointId: data.stockPointId,
|
|
||||||
|
|
||||||
sender: details.origin.contact_name,
|
sender: details.origin.contact_name,
|
||||||
startPhone: details.origin.phone_number,
|
startPhone: details.origin.phone_number,
|
||||||
startPostalCode: details.origin.address.postal_code.replace(/\s/g, ''),
|
startPostalCode: details.origin.address.postal_code.replace(
|
||||||
pickupAddress: details.origin.address.address_line_1,
|
/\s/g,
|
||||||
shipperCountryCode: details.origin.address.country,
|
'',
|
||||||
receiver: details.destination.contact_name,
|
),
|
||||||
city: details.destination.address.city,
|
pickupAddress: details.origin.address.address_line_1,
|
||||||
province: details.destination.address.region,
|
shipperCountryCode: details.origin.address.country,
|
||||||
country: details.destination.address.country,
|
receiver: details.destination.contact_name,
|
||||||
postalCode: details.destination.address.postal_code.replace(/\s/g, ''),
|
city: details.destination.address.city,
|
||||||
deliveryAddress: details.destination.address.address_line_1,
|
province: details.destination.address.region,
|
||||||
receiverPhone: details.destination.phone_number.number,
|
country: details.destination.address.country,
|
||||||
receiverEmail: details.destination.email_addresses,
|
postalCode: details.destination.address.postal_code.replace(
|
||||||
length: details.packaging_properties.packages[0].measurements.cuboid.l,
|
/\s/g,
|
||||||
width: details.packaging_properties.packages[0].measurements.cuboid.w,
|
'',
|
||||||
height: details.packaging_properties.packages[0].measurements.cuboid.h,
|
),
|
||||||
dimensionUom: details.packaging_properties.packages[0].measurements.cuboid.unit,
|
deliveryAddress: details.destination.address.address_line_1,
|
||||||
weight: details.packaging_properties.packages[0].measurements.weight.value,
|
receiverPhone: details.destination.phone_number.number,
|
||||||
weightUom: details.packaging_properties.packages[0].measurements.weight.unit,
|
receiverEmail: details.destination.email_addresses,
|
||||||
},
|
length:
|
||||||
);
|
details.packaging_properties.packages[0].measurements.cuboid.l,
|
||||||
|
width:
|
||||||
|
details.packaging_properties.packages[0].measurements.cuboid.w,
|
||||||
|
height:
|
||||||
|
details.packaging_properties.packages[0].measurements.cuboid.h,
|
||||||
|
dimensionUom:
|
||||||
|
details.packaging_properties.packages[0].measurements.cuboid
|
||||||
|
.unit,
|
||||||
|
weight:
|
||||||
|
details.packaging_properties.packages[0].measurements.weight
|
||||||
|
.value,
|
||||||
|
weightUom:
|
||||||
|
details.packaging_properties.packages[0].measurements.weight
|
||||||
|
.unit,
|
||||||
|
});
|
||||||
if (!res?.success) throw new Error(res?.message);
|
if (!res?.success) throw new Error(res?.message);
|
||||||
const fee = res.data;
|
const fee = res.data;
|
||||||
setShipmentFee(fee);
|
setShipmentFee(fee);
|
||||||
details.origin.email_addresses = originEmail;
|
details.origin.email_addresses = originEmail;
|
||||||
details.destination.email_addresses = destinationEmail;
|
details.destination.email_addresses = destinationEmail;
|
||||||
formRef.current?.setFieldValue("details", {
|
formRef.current?.setFieldValue('details', {
|
||||||
...details,
|
...details,
|
||||||
shipmentFee: fee
|
shipmentFee: fee,
|
||||||
});
|
});
|
||||||
message.success('获取运费成功');
|
message.success('获取运费成功');
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
|
|
@ -1956,9 +1995,9 @@ const Shipping: React.FC<{
|
||||||
</Button>
|
</Button>
|
||||||
<ProFormText
|
<ProFormText
|
||||||
readonly
|
readonly
|
||||||
name={["details", "shipmentFee"]}
|
name={['details', 'shipmentFee']}
|
||||||
fieldProps={{
|
fieldProps={{
|
||||||
value: (shipmentFee / 100.0).toFixed(2)
|
value: (shipmentFee / 100.0).toFixed(2),
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
</ModalForm>
|
</ModalForm>
|
||||||
|
|
@ -1972,7 +2011,6 @@ const SalesChange: React.FC<{
|
||||||
}> = ({ id, detailRef }) => {
|
}> = ({ id, detailRef }) => {
|
||||||
const formRef = useRef<ProFormInstance>();
|
const formRef = useRef<ProFormInstance>();
|
||||||
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ModalForm
|
<ModalForm
|
||||||
formRef={formRef}
|
formRef={formRef}
|
||||||
|
|
@ -1997,60 +2035,57 @@ const SalesChange: React.FC<{
|
||||||
orderId: id,
|
orderId: id,
|
||||||
});
|
});
|
||||||
if (!success || !data) return {};
|
if (!success || !data) return {};
|
||||||
data.sales = data.sales?.reduce((acc: API.OrderSale[], cur: API.OrderSale) => {
|
data.sales = data.sales?.reduce(
|
||||||
let idx = acc.findIndex((v: any) => v.productId === cur.productId);
|
(acc: API.OrderSale[], cur: API.OrderSale) => {
|
||||||
if (idx === -1) {
|
let idx = acc.findIndex((v: any) => v.productId === cur.productId);
|
||||||
acc.push(cur);
|
if (idx === -1) {
|
||||||
} else {
|
acc.push(cur);
|
||||||
acc[idx].quantity += cur.quantity;
|
} else {
|
||||||
}
|
acc[idx].quantity += cur.quantity;
|
||||||
return acc;
|
}
|
||||||
},
|
return acc;
|
||||||
|
},
|
||||||
[],
|
[],
|
||||||
);
|
);
|
||||||
|
|
||||||
|
|
||||||
// setOptions(
|
// setOptions(
|
||||||
// data.sales?.map((item) => ({
|
// data.sales?.map((item) => ({
|
||||||
// label: item.name,
|
// label: item.name,
|
||||||
// value: item.sku,
|
// value: item.sku,
|
||||||
// })) || [],
|
// })) || [],
|
||||||
// );
|
// );
|
||||||
return { ...data};
|
return { ...data };
|
||||||
}}
|
}}
|
||||||
onFinish={async (formData: any) => {
|
onFinish={async (formData: any) => {
|
||||||
const { sales } = formData;
|
const { sales } = formData;
|
||||||
const res = await ordercontrollerUpdateorderitems({ orderId: id }, sales);
|
const res = await ordercontrollerUpdateorderitems(
|
||||||
|
{ orderId: id },
|
||||||
|
sales,
|
||||||
|
);
|
||||||
if (!res.success) {
|
if (!res.success) {
|
||||||
message.error(`更新货物信息失败: ${res.message}`);
|
message.error(`更新货物信息失败: ${res.message}`);
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
message.success('更新成功')
|
message.success('更新成功');
|
||||||
detailRef?.current?.reload();
|
detailRef?.current?.reload();
|
||||||
return true;
|
return true;
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<ProFormList
|
<ProFormList label="换货订单" name="items">
|
||||||
label="换货订单"
|
|
||||||
name="items"
|
|
||||||
>
|
|
||||||
<ProForm.Group>
|
<ProForm.Group>
|
||||||
|
|
||||||
<ProFormSelect
|
<ProFormSelect
|
||||||
params={{ }}
|
params={{}}
|
||||||
request={async ({ keyWords }) => {
|
request={async ({ keyWords }) => {
|
||||||
try {
|
try {
|
||||||
const { data } = await wpproductcontrollerSearchproducts({
|
const { data } = await wpproductcontrollerSearchproducts({
|
||||||
name: keyWords,
|
name: keyWords,
|
||||||
});
|
});
|
||||||
return (
|
return data?.map((item) => {
|
||||||
data?.map((item) => {
|
return {
|
||||||
return {
|
label: `${item.name}`,
|
||||||
label: `${item.name}`,
|
value: item?.sku,
|
||||||
value: item?.sku,
|
};
|
||||||
};
|
});
|
||||||
})
|
|
||||||
);
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
@ -2076,17 +2111,11 @@ const SalesChange: React.FC<{
|
||||||
precision: 0,
|
precision: 0,
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
|
|
||||||
</ProForm.Group>
|
</ProForm.Group>
|
||||||
</ProFormList>
|
</ProFormList>
|
||||||
|
|
||||||
<ProFormList
|
<ProFormList label="换货产品" name="sales">
|
||||||
label="换货产品"
|
|
||||||
name="sales"
|
|
||||||
>
|
|
||||||
<ProForm.Group>
|
<ProForm.Group>
|
||||||
|
|
||||||
|
|
||||||
<ProFormSelect
|
<ProFormSelect
|
||||||
params={{}}
|
params={{}}
|
||||||
request={async ({ keyWords }) => {
|
request={async ({ keyWords }) => {
|
||||||
|
|
@ -2094,14 +2123,12 @@ const SalesChange: React.FC<{
|
||||||
const { data } = await productcontrollerSearchproducts({
|
const { data } = await productcontrollerSearchproducts({
|
||||||
name: keyWords,
|
name: keyWords,
|
||||||
});
|
});
|
||||||
return (
|
return data?.map((item) => {
|
||||||
data?.map((item) => {
|
return {
|
||||||
return {
|
label: `${item.name} - ${item.nameCn}`,
|
||||||
label: `${item.name} - ${item.nameCn}`,
|
value: item?.sku,
|
||||||
value: item?.sku,
|
};
|
||||||
};
|
});
|
||||||
})
|
|
||||||
);
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
return [];
|
return [];
|
||||||
}
|
}
|
||||||
|
|
@ -2131,7 +2158,7 @@ const SalesChange: React.FC<{
|
||||||
</ProFormList>
|
</ProFormList>
|
||||||
</ModalForm>
|
</ModalForm>
|
||||||
);
|
);
|
||||||
}
|
};
|
||||||
|
|
||||||
const CreateOrder: React.FC<{
|
const CreateOrder: React.FC<{
|
||||||
tableRef?: React.MutableRefObject<ActionType | undefined>;
|
tableRef?: React.MutableRefObject<ActionType | undefined>;
|
||||||
|
|
|
||||||
|
|
@ -26,24 +26,31 @@ const NameCn: React.FC<{
|
||||||
id: number;
|
id: number;
|
||||||
value: string;
|
value: string;
|
||||||
tableRef: React.MutableRefObject<ActionType | undefined>;
|
tableRef: React.MutableRefObject<ActionType | undefined>;
|
||||||
}> = ({value,tableRef, id}) => {
|
}> = ({ value, tableRef, id }) => {
|
||||||
const { message } = App.useApp();
|
const { message } = App.useApp();
|
||||||
const [editable, setEditable] = React.useState<boolean>(false);
|
const [editable, setEditable] = React.useState<boolean>(false);
|
||||||
if (!editable) return <div onClick={() => setEditable(true)}>{value||'-'}</div>;
|
if (!editable)
|
||||||
return <ProFormText fieldProps={{autoFocus:true}} initialValue={value} onBlur={async(e) => {
|
return <div onClick={() => setEditable(true)}>{value || '-'}</div>;
|
||||||
if(!e.target.value) return setEditable(false)
|
return (
|
||||||
const { success, message: errMsg } =
|
<ProFormText
|
||||||
await productcontrollerUpdateproductnamecn({
|
fieldProps={{ autoFocus: true }}
|
||||||
id,
|
initialValue={value}
|
||||||
nameCn: e.target.value,
|
onBlur={async (e) => {
|
||||||
})
|
if (!e.target.value) return setEditable(false);
|
||||||
setEditable(false)
|
const { success, message: errMsg } =
|
||||||
if (!success) {
|
await productcontrollerUpdateproductnamecn({
|
||||||
return message.error(errMsg)
|
id,
|
||||||
}
|
nameCn: e.target.value,
|
||||||
tableRef?.current?.reload()
|
});
|
||||||
}} />
|
setEditable(false);
|
||||||
}
|
if (!success) {
|
||||||
|
return message.error(errMsg);
|
||||||
|
}
|
||||||
|
tableRef?.current?.reload();
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
};
|
||||||
const List: React.FC = () => {
|
const List: React.FC = () => {
|
||||||
const actionRef = useRef<ActionType>();
|
const actionRef = useRef<ActionType>();
|
||||||
|
|
||||||
|
|
@ -56,10 +63,10 @@ const List: React.FC = () => {
|
||||||
{
|
{
|
||||||
title: '中文名',
|
title: '中文名',
|
||||||
dataIndex: 'nameCn',
|
dataIndex: 'nameCn',
|
||||||
render: (_, record) => {
|
render: (_, record) => {
|
||||||
return (
|
return (
|
||||||
<NameCn value={record.nameCn} id={record.id} tableRef={actionRef} />
|
<NameCn value={record.nameCn} id={record.id} tableRef={actionRef} />
|
||||||
)
|
);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -148,7 +155,7 @@ const List: React.FC = () => {
|
||||||
success,
|
success,
|
||||||
};
|
};
|
||||||
}}
|
}}
|
||||||
columns={columns}
|
columns={columns}
|
||||||
editable={{
|
editable={{
|
||||||
type: 'single',
|
type: 'single',
|
||||||
onSave: async (key, record, originRow) => {
|
onSave: async (key, record, originRow) => {
|
||||||
|
|
|
||||||
|
|
@ -262,9 +262,9 @@ const UpdateStatus: React.FC<{
|
||||||
{
|
{
|
||||||
id: initialValues.id,
|
id: initialValues.id,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
status,
|
status,
|
||||||
stock_status
|
stock_status,
|
||||||
},
|
},
|
||||||
);
|
);
|
||||||
if (!success) {
|
if (!success) {
|
||||||
|
|
@ -285,7 +285,7 @@ const UpdateStatus: React.FC<{
|
||||||
name="status"
|
name="status"
|
||||||
valueEnum={PRODUCT_STATUS_ENUM}
|
valueEnum={PRODUCT_STATUS_ENUM}
|
||||||
/>
|
/>
|
||||||
<ProFormSelect
|
<ProFormSelect
|
||||||
label="上下架状态"
|
label="上下架状态"
|
||||||
width="lg"
|
width="lg"
|
||||||
name="stock_status"
|
name="stock_status"
|
||||||
|
|
@ -296,7 +296,6 @@ const UpdateStatus: React.FC<{
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
||||||
const UpdateForm: React.FC<{
|
const UpdateForm: React.FC<{
|
||||||
tableRef: React.MutableRefObject<ActionType | undefined>;
|
tableRef: React.MutableRefObject<ActionType | undefined>;
|
||||||
values: API.WpProductDTO;
|
values: API.WpProductDTO;
|
||||||
|
|
|
||||||
|
|
@ -1,8 +1,16 @@
|
||||||
import React, { useEffect, useRef, useState } from 'react';
|
import {
|
||||||
import { ActionType, ProColumns, ProTable, ProFormInstance } from '@ant-design/pro-components';
|
ActionType,
|
||||||
import { DrawerForm, ProFormText, ProFormSelect, ProFormSwitch } from '@ant-design/pro-components';
|
DrawerForm,
|
||||||
import { Button, message, Popconfirm, Space, Tag } from 'antd';
|
ProColumns,
|
||||||
|
ProFormInstance,
|
||||||
|
ProFormSelect,
|
||||||
|
ProFormSwitch,
|
||||||
|
ProFormText,
|
||||||
|
ProTable,
|
||||||
|
} from '@ant-design/pro-components';
|
||||||
import { request } from '@umijs/max';
|
import { request } from '@umijs/max';
|
||||||
|
import { Button, message, Popconfirm, Space, Tag } from 'antd';
|
||||||
|
import React, { useEffect, useRef, useState } from 'react';
|
||||||
|
|
||||||
// 站点数据项类型(前端不包含密钥字段,后端列表不返回密钥)
|
// 站点数据项类型(前端不包含密钥字段,后端列表不返回密钥)
|
||||||
interface SiteItem {
|
interface SiteItem {
|
||||||
|
|
@ -58,10 +66,21 @@ const SiteList: React.FC = () => {
|
||||||
|
|
||||||
// 表格列定义
|
// 表格列定义
|
||||||
const columns: ProColumns<SiteItem>[] = [
|
const columns: ProColumns<SiteItem>[] = [
|
||||||
{ title: 'ID', dataIndex: 'id', width: 80, sorter: true, hideInSearch: true },
|
{
|
||||||
|
title: 'ID',
|
||||||
|
dataIndex: 'id',
|
||||||
|
width: 80,
|
||||||
|
sorter: true,
|
||||||
|
hideInSearch: true,
|
||||||
|
},
|
||||||
{ title: '站点名称', dataIndex: 'siteName', width: 220 },
|
{ title: '站点名称', dataIndex: 'siteName', width: 220 },
|
||||||
{ title: 'API 地址', dataIndex: 'apiUrl', width: 280, hideInSearch: true },
|
{ title: 'API 地址', dataIndex: 'apiUrl', width: 280, hideInSearch: true },
|
||||||
{ title: 'SKU 前缀', dataIndex: 'skuPrefix', width: 160, hideInSearch: true },
|
{
|
||||||
|
title: 'SKU 前缀',
|
||||||
|
dataIndex: 'skuPrefix',
|
||||||
|
width: 160,
|
||||||
|
hideInSearch: true,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
title: '平台',
|
title: '平台',
|
||||||
dataIndex: 'type',
|
dataIndex: 'type',
|
||||||
|
|
@ -101,7 +120,9 @@ const SiteList: React.FC = () => {
|
||||||
</Button>
|
</Button>
|
||||||
<Popconfirm
|
<Popconfirm
|
||||||
title={row.isDisabled ? '启用站点' : '禁用站点'}
|
title={row.isDisabled ? '启用站点' : '禁用站点'}
|
||||||
description={row.isDisabled ? '确认启用该站点?' : '确认禁用该站点?'}
|
description={
|
||||||
|
row.isDisabled ? '确认启用该站点?' : '确认禁用该站点?'
|
||||||
|
}
|
||||||
onConfirm={async () => {
|
onConfirm={async () => {
|
||||||
try {
|
try {
|
||||||
await request(`/site/disable/${row.id}`, {
|
await request(`/site/disable/${row.id}`, {
|
||||||
|
|
@ -159,7 +180,9 @@ const SiteList: React.FC = () => {
|
||||||
...(values.siteName ? { siteName: values.siteName } : {}),
|
...(values.siteName ? { siteName: values.siteName } : {}),
|
||||||
...(values.apiUrl ? { apiUrl: values.apiUrl } : {}),
|
...(values.apiUrl ? { apiUrl: values.apiUrl } : {}),
|
||||||
...(values.type ? { type: values.type } : {}),
|
...(values.type ? { type: values.type } : {}),
|
||||||
...(typeof values.isDisabled === 'boolean' ? { isDisabled: values.isDisabled } : {}),
|
...(typeof values.isDisabled === 'boolean'
|
||||||
|
? { isDisabled: values.isDisabled }
|
||||||
|
: {}),
|
||||||
...(values.skuPrefix ? { skuPrefix: values.skuPrefix } : {}),
|
...(values.skuPrefix ? { skuPrefix: values.skuPrefix } : {}),
|
||||||
};
|
};
|
||||||
// 仅当输入了新密钥时才提交,未输入则保持原本值
|
// 仅当输入了新密钥时才提交,未输入则保持原本值
|
||||||
|
|
@ -169,7 +192,10 @@ const SiteList: React.FC = () => {
|
||||||
if (values.consumerSecret && values.consumerSecret.trim()) {
|
if (values.consumerSecret && values.consumerSecret.trim()) {
|
||||||
payload.consumerSecret = values.consumerSecret.trim();
|
payload.consumerSecret = values.consumerSecret.trim();
|
||||||
}
|
}
|
||||||
await request(`/site/update/${editing.id}`, { method: 'PUT', data: payload });
|
await request(`/site/update/${editing.id}`, {
|
||||||
|
method: 'PUT',
|
||||||
|
data: payload,
|
||||||
|
});
|
||||||
} else {
|
} else {
|
||||||
// 新增站点时要求填写 consumerKey 和 consumerSecret
|
// 新增站点时要求填写 consumerKey 和 consumerSecret
|
||||||
if (!values.consumerKey || !values.consumerSecret) {
|
if (!values.consumerKey || !values.consumerSecret) {
|
||||||
|
|
@ -227,9 +253,18 @@ const SiteList: React.FC = () => {
|
||||||
onFinish={handleSubmit}
|
onFinish={handleSubmit}
|
||||||
>
|
>
|
||||||
{/* 站点名称,必填 */}
|
{/* 站点名称,必填 */}
|
||||||
<ProFormText name="siteName" label="站点名称" placeholder="例如:本地商店" rules={[{ required: true, message: '站点名称为必填项' }]} />
|
<ProFormText
|
||||||
|
name="siteName"
|
||||||
|
label="站点名称"
|
||||||
|
placeholder="例如:本地商店"
|
||||||
|
rules={[{ required: true, message: '站点名称为必填项' }]}
|
||||||
|
/>
|
||||||
{/* API 地址,可选 */}
|
{/* API 地址,可选 */}
|
||||||
<ProFormText name="apiUrl" label="API 地址" placeholder="例如:https://shop.example.com" />
|
<ProFormText
|
||||||
|
name="apiUrl"
|
||||||
|
label="API 地址"
|
||||||
|
placeholder="例如:https://shop.example.com"
|
||||||
|
/>
|
||||||
{/* 平台类型选择 */}
|
{/* 平台类型选择 */}
|
||||||
<ProFormSelect
|
<ProFormSelect
|
||||||
name="type"
|
name="type"
|
||||||
|
|
@ -241,14 +276,30 @@ const SiteList: React.FC = () => {
|
||||||
/>
|
/>
|
||||||
{/* 是否禁用 */}
|
{/* 是否禁用 */}
|
||||||
<ProFormSwitch name="isDisabled" label="禁用" />
|
<ProFormSwitch name="isDisabled" label="禁用" />
|
||||||
<ProFormText name="skuPrefix" label="SKU 前缀" placeholder={editing ? '留空表示不修改' : '可选'} />
|
<ProFormText
|
||||||
|
name="skuPrefix"
|
||||||
|
label="SKU 前缀"
|
||||||
|
placeholder={editing ? '留空表示不修改' : '可选'}
|
||||||
|
/>
|
||||||
{/* WooCommerce REST consumer key;新增必填,编辑不填则保持原值 */}
|
{/* WooCommerce REST consumer key;新增必填,编辑不填则保持原值 */}
|
||||||
<ProFormText name="consumerKey" label="Key" placeholder={editing ? '留空表示不修改' : '必填'} rules={editing ? [] : [{ required: true, message: 'Key 为必填项' }]} />
|
<ProFormText
|
||||||
|
name="consumerKey"
|
||||||
|
label="Key"
|
||||||
|
placeholder={editing ? '留空表示不修改' : '必填'}
|
||||||
|
rules={editing ? [] : [{ required: true, message: 'Key 为必填项' }]}
|
||||||
|
/>
|
||||||
{/* WooCommerce REST consumer secret;新增必填,编辑不填则保持原值 */}
|
{/* WooCommerce REST consumer secret;新增必填,编辑不填则保持原值 */}
|
||||||
<ProFormText name="consumerSecret" label="Secret" placeholder={editing ? '留空表示不修改' : '必填'} rules={editing ? [] : [{ required: true, message: 'Secret 为必填项' }]} />
|
<ProFormText
|
||||||
|
name="consumerSecret"
|
||||||
|
label="Secret"
|
||||||
|
placeholder={editing ? '留空表示不修改' : '必填'}
|
||||||
|
rules={
|
||||||
|
editing ? [] : [{ required: true, message: 'Secret 为必填项' }]
|
||||||
|
}
|
||||||
|
/>
|
||||||
</DrawerForm>
|
</DrawerForm>
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default SiteList;
|
export default SiteList;
|
||||||
|
|
|
||||||
|
|
@ -21,7 +21,9 @@ import { Button, Space, Tag } from 'antd';
|
||||||
import dayjs from 'dayjs';
|
import dayjs from 'dayjs';
|
||||||
import ReactECharts from 'echarts-for-react';
|
import ReactECharts from 'echarts-for-react';
|
||||||
import { useEffect, useMemo, useRef, useState } from 'react';
|
import { useEffect, useMemo, useRef, useState } from 'react';
|
||||||
|
import weekOfYear from 'dayjs/plugin/weekOfYear';
|
||||||
|
|
||||||
|
dayjs.extend(weekOfYear);
|
||||||
const highlightText = (text: string, keyword: string) => {
|
const highlightText = (text: string, keyword: string) => {
|
||||||
if (!keyword) return text;
|
if (!keyword) return text;
|
||||||
const parts = text.split(new RegExp(`(${keyword})`, 'gi'));
|
const parts = text.split(new RegExp(`(${keyword})`, 'gi'));
|
||||||
|
|
@ -58,10 +60,9 @@ const ListPage: React.FC = () => {
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
`<div>${xValue} ${
|
`<div>${xValue} ${['周日', '周一', '周二', '周三', '周四', '周五', '周六'][
|
||||||
['周日', '周一', '周二', '周三', '周四', '周五', '周六'][
|
dayjs(xValue).day()
|
||||||
dayjs(xValue).day()
|
]
|
||||||
]
|
|
||||||
}</div>` + rows.join('<br/>')
|
}</div>` + rows.join('<br/>')
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
|
|
@ -128,7 +129,22 @@ const ListPage: React.FC = () => {
|
||||||
});
|
});
|
||||||
if (success) {
|
if (success) {
|
||||||
const res = data?.sort(() => -1);
|
const res = data?.sort(() => -1);
|
||||||
setXAxis(res?.map((v) => dayjs(v.order_date).format('YYYY-MM-DD')));
|
const formatMap = {
|
||||||
|
month: 'YYYY-MM',
|
||||||
|
week: 'YYYY年第WW周',
|
||||||
|
day: 'YYYY-MM-DD',
|
||||||
|
};
|
||||||
|
const format = formatMap[params.grouping] || 'YYYY-MM-DD';
|
||||||
|
|
||||||
|
if (params.grouping === 'week') {
|
||||||
|
setXAxis(res?.map((v) => {
|
||||||
|
const [year, week] = v.order_date.split('-');
|
||||||
|
return `${year}年第${week}周`;
|
||||||
|
}));
|
||||||
|
} else {
|
||||||
|
setXAxis(res?.map((v) => dayjs(v.order_date).format(format)));
|
||||||
|
}
|
||||||
|
|
||||||
setSeries([
|
setSeries([
|
||||||
{
|
{
|
||||||
name: 'TOGO CPC订单数',
|
name: 'TOGO CPC订单数',
|
||||||
|
|
@ -583,6 +599,16 @@ const ListPage: React.FC = () => {
|
||||||
name="date"
|
name="date"
|
||||||
/>
|
/>
|
||||||
{/* <ProFormText label="关键词" name="keyword" /> */}
|
{/* <ProFormText label="关键词" name="keyword" /> */}
|
||||||
|
<ProFormSelect
|
||||||
|
label="统计周期"
|
||||||
|
name="grouping"
|
||||||
|
initialValue="day"
|
||||||
|
options={[
|
||||||
|
{ label: '月', value: 'month' },
|
||||||
|
{ label: '周', value: 'week' },
|
||||||
|
{ label: '日', value: 'day' },
|
||||||
|
]}
|
||||||
|
/>
|
||||||
<ProFormSelect
|
<ProFormSelect
|
||||||
label="站点"
|
label="站点"
|
||||||
name="siteId"
|
name="siteId"
|
||||||
|
|
|
||||||
|
|
@ -1,257 +1,283 @@
|
||||||
import React, { useEffect, useState, useMemo, useRef } from "react"
|
import React, { useEffect, useMemo, useRef, useState } from 'react';
|
||||||
|
|
||||||
import {
|
import {
|
||||||
ActionType,
|
statisticscontrollerGetinativeusersbymonth,
|
||||||
PageContainer, ProColumns, ProTable,
|
statisticscontrollerGetordersorce,
|
||||||
} from '@ant-design/pro-components';
|
} from '@/servers/api/statistics';
|
||||||
import { statisticscontrollerGetordersorce, statisticscontrollerGetinativeusersbymonth } from "@/servers/api/statistics";
|
import {
|
||||||
import ReactECharts from 'echarts-for-react';
|
ActionType,
|
||||||
import { App, Button, Space, Tag } from 'antd';
|
PageContainer,
|
||||||
import { HistoryOrder } from "../Order";
|
ProColumns,
|
||||||
|
ProTable,
|
||||||
|
} from '@ant-design/pro-components';
|
||||||
|
import { Space, Tag } from 'antd';
|
||||||
import dayjs from 'dayjs';
|
import dayjs from 'dayjs';
|
||||||
|
import ReactECharts from 'echarts-for-react';
|
||||||
|
import { HistoryOrder } from '../Order';
|
||||||
const ListPage: React.FC = () => {
|
const ListPage: React.FC = () => {
|
||||||
|
const [data, setData] = useState({});
|
||||||
|
|
||||||
const [data, setData] = useState({});
|
useEffect(() => {
|
||||||
|
statisticscontrollerGetordersorce().then(({ data, success }) => {
|
||||||
|
if (success) setData(data);
|
||||||
|
});
|
||||||
|
}, []);
|
||||||
|
|
||||||
useEffect(() => {
|
const option = useMemo(() => {
|
||||||
statisticscontrollerGetordersorce().then(({ data, success }) => {
|
if (!data.inactiveRes) return {};
|
||||||
if(success) setData(data)
|
const xAxisData = data?.inactiveRes
|
||||||
});
|
?.map((v) => v.order_month)
|
||||||
}, []);
|
?.sort((_) => -1);
|
||||||
|
const arr = data?.res?.map((v) => v.first_order_month_group);
|
||||||
const option = useMemo(() => {
|
const uniqueArr = arr
|
||||||
if(!data.inactiveRes) return {}
|
.filter((item, index) => arr.indexOf(item) === index)
|
||||||
const xAxisData = data?.inactiveRes?.map(v=> v.order_month)?.sort(_=>-1)
|
.sort((a, b) => a.localeCompare(b));
|
||||||
const arr = data?.res?.map(v=>v.first_order_month_group)
|
const series = [
|
||||||
const uniqueArr = arr.filter((item, index) => arr.indexOf(item) === index).sort((a,b)=> a.localeCompare(b))
|
|
||||||
const series = [
|
|
||||||
{
|
|
||||||
name: '新客户',
|
|
||||||
type: 'bar',
|
|
||||||
data: data?.inactiveRes?.map(v=> v.new_user_count)?.sort(_=>-1),
|
|
||||||
label: {
|
|
||||||
show: true,
|
|
||||||
},
|
|
||||||
emphasis: {
|
|
||||||
focus: 'series'
|
|
||||||
},
|
|
||||||
xAxisIndex: 0,
|
|
||||||
yAxisIndex: 0,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: '老客户',
|
|
||||||
type: 'bar',
|
|
||||||
data: data?.inactiveRes?.map(v=> v.old_user_count)?.sort(_=>-1),
|
|
||||||
label: {
|
|
||||||
show: true,
|
|
||||||
},
|
|
||||||
emphasis: {
|
|
||||||
focus: 'series'
|
|
||||||
},
|
|
||||||
xAxisIndex: 0,
|
|
||||||
yAxisIndex: 0,
|
|
||||||
},
|
|
||||||
...uniqueArr?.map(v => {
|
|
||||||
data?.res?.filter(item => item.order_month === v)
|
|
||||||
return {
|
|
||||||
name: v,
|
|
||||||
type: "bar",
|
|
||||||
stack: "total",
|
|
||||||
label: {
|
|
||||||
"show": true,
|
|
||||||
formatter: function(params) {
|
|
||||||
if(!params.value) return ''
|
|
||||||
return Math.abs(params.value)
|
|
||||||
},
|
|
||||||
color: '#fff'
|
|
||||||
},
|
|
||||||
"data": xAxisData.map(month => {
|
|
||||||
return (data?.res?.find(item => item.order_month === month && item.first_order_month_group === v)?.order_count || 0)
|
|
||||||
}),
|
|
||||||
xAxisIndex: 0,
|
|
||||||
yAxisIndex: 0,
|
|
||||||
}
|
|
||||||
}),
|
|
||||||
{
|
|
||||||
name: '未复购客户',
|
|
||||||
type: 'bar',
|
|
||||||
data: data?.inactiveRes?.map(v=> -v.inactive_user_count)?.sort(_=>-1),
|
|
||||||
stack: "total",
|
|
||||||
label: {
|
|
||||||
show: true,
|
|
||||||
},
|
|
||||||
emphasis: {
|
|
||||||
focus: 'series'
|
|
||||||
},
|
|
||||||
xAxisIndex: 1,
|
|
||||||
yAxisIndex: 1,
|
|
||||||
barWidth: "60%",
|
|
||||||
|
|
||||||
itemStyle: {
|
|
||||||
color: '#f44336'
|
|
||||||
}
|
|
||||||
},
|
|
||||||
]
|
|
||||||
return {
|
|
||||||
grid: [
|
|
||||||
{ top: '10%', height: '70%' },
|
|
||||||
{ bottom: '10%', height: '10%' }
|
|
||||||
],
|
|
||||||
legend: {
|
|
||||||
selectedMode: false
|
|
||||||
},
|
|
||||||
xAxis: [{
|
|
||||||
type: 'category',
|
|
||||||
data: xAxisData,
|
|
||||||
gridIndex: 0,
|
|
||||||
},{
|
|
||||||
type: 'category',
|
|
||||||
data: xAxisData,
|
|
||||||
gridIndex: 1,
|
|
||||||
}],
|
|
||||||
yAxis: [{
|
|
||||||
type: 'value',
|
|
||||||
gridIndex: 0,
|
|
||||||
},{
|
|
||||||
type: 'value',
|
|
||||||
gridIndex: 1,
|
|
||||||
}],
|
|
||||||
series,
|
|
||||||
}
|
|
||||||
}, [data])
|
|
||||||
|
|
||||||
const [tableData, setTableData] = useState<any[]>([])
|
|
||||||
const actionRef = useRef<ActionType>();
|
|
||||||
const columns: ProColumns[] = [
|
|
||||||
{
|
{
|
||||||
title: '用户名',
|
name: '新客户',
|
||||||
dataIndex: 'username',
|
type: 'bar',
|
||||||
hideInSearch: true,
|
data: data?.inactiveRes?.map((v) => v.new_user_count)?.sort((_) => -1),
|
||||||
render: (_, record) => {
|
label: {
|
||||||
if (record.billing.first_name || record.billing.last_name)
|
show: true,
|
||||||
return record.billing.first_name + ' ' + record.billing.last_name;
|
|
||||||
return record.shipping.first_name + ' ' + record.shipping.last_name;
|
|
||||||
},
|
},
|
||||||
},
|
formatter: function (params) {
|
||||||
{
|
if (!params.value) return '';
|
||||||
title: '邮箱',
|
return Math.abs(params.value) + '人';
|
||||||
dataIndex: 'email',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '首单时间',
|
|
||||||
dataIndex: 'first_purchase_date',
|
|
||||||
valueType: 'dateMonth',
|
|
||||||
sorter: true,
|
|
||||||
render: (_, record) =>
|
|
||||||
record.first_purchase_date
|
|
||||||
? dayjs(record.first_purchase_date).format('YYYY-MM-DD HH:mm:ss')
|
|
||||||
: '-',
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '尾单时间',
|
|
||||||
hideInSearch: true,
|
|
||||||
dataIndex: 'last_purchase_date',
|
|
||||||
valueType: 'dateTime',
|
|
||||||
sorter: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '订单数',
|
|
||||||
dataIndex: 'orders',
|
|
||||||
hideInSearch: true,
|
|
||||||
sorter: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '金额',
|
|
||||||
dataIndex: 'total',
|
|
||||||
hideInSearch: true,
|
|
||||||
sorter: true,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'state',
|
|
||||||
dataIndex: 'state',
|
|
||||||
render: (_, record) => record?.billing.state || record?.shipping.state,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: 'city',
|
|
||||||
dataIndex: 'city',
|
|
||||||
hideInSearch: true,
|
|
||||||
render: (_, record) => record?.billing.city || record?.shipping.city,
|
|
||||||
},
|
|
||||||
{
|
|
||||||
title: '标签',
|
|
||||||
dataIndex: 'tags',
|
|
||||||
render: (_, record) => {
|
|
||||||
return (
|
|
||||||
<Space>
|
|
||||||
{(record.tags || []).map((tag) => {
|
|
||||||
return (
|
|
||||||
<Tag
|
|
||||||
key={tag}
|
|
||||||
closable
|
|
||||||
onClose={async () => {
|
|
||||||
const { success, message: msg } =
|
|
||||||
await customercontrollerDeltag({
|
|
||||||
email: record.email,
|
|
||||||
tag,
|
|
||||||
});
|
|
||||||
return false;
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
{tag}
|
|
||||||
</Tag>
|
|
||||||
);
|
|
||||||
})}
|
|
||||||
</Space>
|
|
||||||
);
|
|
||||||
},
|
},
|
||||||
|
emphasis: {
|
||||||
|
focus: 'series',
|
||||||
|
},
|
||||||
|
xAxisIndex: 0,
|
||||||
|
yAxisIndex: 0,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '操作',
|
name: '老客户',
|
||||||
dataIndex: 'option',
|
type: 'bar',
|
||||||
valueType: 'option',
|
data: data?.inactiveRes?.map((v) => v.old_user_count)?.sort((_) => -1),
|
||||||
render: (_, record) => {
|
label: {
|
||||||
return (
|
show: true,
|
||||||
<HistoryOrder
|
},
|
||||||
email={record.email}
|
emphasis: {
|
||||||
tags={record.tags}
|
focus: 'series',
|
||||||
tableRef={actionRef}
|
},
|
||||||
/>
|
xAxisIndex: 0,
|
||||||
);
|
yAxisIndex: 0,
|
||||||
|
},
|
||||||
|
...uniqueArr?.map((v) => {
|
||||||
|
data?.res?.filter((item) => item.order_month === v);
|
||||||
|
return {
|
||||||
|
name: v,
|
||||||
|
type: 'bar',
|
||||||
|
stack: 'total',
|
||||||
|
label: {
|
||||||
|
show: true,
|
||||||
|
formatter: function (params) {
|
||||||
|
if (!params.value) return '';
|
||||||
|
return Math.abs(params.value);
|
||||||
|
},
|
||||||
|
color: '#fff',
|
||||||
|
},
|
||||||
|
data: xAxisData.map((month) => {
|
||||||
|
return (
|
||||||
|
data?.res?.find(
|
||||||
|
(item) =>
|
||||||
|
item.order_month === month &&
|
||||||
|
item.first_order_month_group === v,
|
||||||
|
)?.order_count || 0
|
||||||
|
);
|
||||||
|
}),
|
||||||
|
xAxisIndex: 0,
|
||||||
|
yAxisIndex: 0,
|
||||||
|
};
|
||||||
|
}),
|
||||||
|
{
|
||||||
|
name: '未复购客户',
|
||||||
|
type: 'bar',
|
||||||
|
data: data?.inactiveRes
|
||||||
|
?.map((v) => -v.inactive_user_count)
|
||||||
|
?.sort((_) => -1),
|
||||||
|
stack: 'total',
|
||||||
|
label: {
|
||||||
|
show: true,
|
||||||
|
},
|
||||||
|
emphasis: {
|
||||||
|
focus: 'series',
|
||||||
|
},
|
||||||
|
xAxisIndex: 1,
|
||||||
|
yAxisIndex: 1,
|
||||||
|
barWidth: '60%',
|
||||||
|
|
||||||
|
itemStyle: {
|
||||||
|
color: '#f44336',
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
];
|
];
|
||||||
return(
|
return {
|
||||||
<PageContainer ghost>
|
grid: [
|
||||||
<ReactECharts
|
{ top: '10%', height: '70%' },
|
||||||
option={option}
|
{ bottom: '10%', height: '10%' },
|
||||||
style={{ height: 1050 }}
|
],
|
||||||
onEvents={{
|
legend: {
|
||||||
click: async (params) => {
|
selectedMode: false,
|
||||||
if (params.componentType === 'series') {
|
},
|
||||||
setTableData([])
|
xAxis: [
|
||||||
const {success, data} = await statisticscontrollerGetinativeusersbymonth({
|
{
|
||||||
month: params.name
|
type: 'category',
|
||||||
})
|
data: xAxisData,
|
||||||
if(success) setTableData(data)
|
gridIndex: 0,
|
||||||
}
|
},
|
||||||
},
|
{
|
||||||
}}
|
type: 'category',
|
||||||
/>
|
data: xAxisData,
|
||||||
{
|
gridIndex: 1,
|
||||||
tableData?.length ?
|
},
|
||||||
<ProTable
|
],
|
||||||
search={false}
|
yAxis: [
|
||||||
headerTitle="查询表格"
|
{
|
||||||
actionRef={actionRef}
|
type: 'value',
|
||||||
rowKey="id"
|
gridIndex: 0,
|
||||||
dataSource={tableData}
|
},
|
||||||
columns={columns}
|
{
|
||||||
/>
|
type: 'value',
|
||||||
:<></>
|
gridIndex: 1,
|
||||||
|
},
|
||||||
}
|
],
|
||||||
</PageContainer>
|
series,
|
||||||
)
|
};
|
||||||
}
|
}, [data]);
|
||||||
|
|
||||||
export default ListPage;
|
const [tableData, setTableData] = useState<any[]>([]);
|
||||||
|
const actionRef = useRef<ActionType>();
|
||||||
|
const columns: ProColumns[] = [
|
||||||
|
{
|
||||||
|
title: '用户名',
|
||||||
|
dataIndex: 'username',
|
||||||
|
hideInSearch: true,
|
||||||
|
render: (_, record) => {
|
||||||
|
if (record.billing.first_name || record.billing.last_name)
|
||||||
|
return record.billing.first_name + ' ' + record.billing.last_name;
|
||||||
|
return record.shipping.first_name + ' ' + record.shipping.last_name;
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '邮箱',
|
||||||
|
dataIndex: 'email',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '首单时间',
|
||||||
|
dataIndex: 'first_purchase_date',
|
||||||
|
valueType: 'dateMonth',
|
||||||
|
sorter: true,
|
||||||
|
render: (_, record) =>
|
||||||
|
record.first_purchase_date
|
||||||
|
? dayjs(record.first_purchase_date).format('YYYY-MM-DD HH:mm:ss')
|
||||||
|
: '-',
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '尾单时间',
|
||||||
|
hideInSearch: true,
|
||||||
|
dataIndex: 'last_purchase_date',
|
||||||
|
valueType: 'dateTime',
|
||||||
|
sorter: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '订单数',
|
||||||
|
dataIndex: 'orders',
|
||||||
|
hideInSearch: true,
|
||||||
|
sorter: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '金额',
|
||||||
|
dataIndex: 'total',
|
||||||
|
hideInSearch: true,
|
||||||
|
sorter: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'state',
|
||||||
|
dataIndex: 'state',
|
||||||
|
render: (_, record) => record?.billing.state || record?.shipping.state,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'city',
|
||||||
|
dataIndex: 'city',
|
||||||
|
hideInSearch: true,
|
||||||
|
render: (_, record) => record?.billing.city || record?.shipping.city,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '标签',
|
||||||
|
dataIndex: 'tags',
|
||||||
|
render: (_, record) => {
|
||||||
|
return (
|
||||||
|
<Space>
|
||||||
|
{(record.tags || []).map((tag) => {
|
||||||
|
return (
|
||||||
|
<Tag
|
||||||
|
key={tag}
|
||||||
|
closable
|
||||||
|
onClose={async () => {
|
||||||
|
const { success, message: msg } =
|
||||||
|
await customercontrollerDeltag({
|
||||||
|
email: record.email,
|
||||||
|
tag,
|
||||||
|
});
|
||||||
|
return false;
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{tag}
|
||||||
|
</Tag>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</Space>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '操作',
|
||||||
|
dataIndex: 'option',
|
||||||
|
valueType: 'option',
|
||||||
|
render: (_, record) => {
|
||||||
|
return (
|
||||||
|
<HistoryOrder
|
||||||
|
email={record.email}
|
||||||
|
tags={record.tags}
|
||||||
|
tableRef={actionRef}
|
||||||
|
/>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
return (
|
||||||
|
<PageContainer ghost>
|
||||||
|
<ReactECharts
|
||||||
|
option={option}
|
||||||
|
style={{ height: 1050 }}
|
||||||
|
onEvents={{
|
||||||
|
click: async (params) => {
|
||||||
|
if (params.componentType === 'series') {
|
||||||
|
setTableData([]);
|
||||||
|
const { success, data } =
|
||||||
|
await statisticscontrollerGetinativeusersbymonth({
|
||||||
|
month: params.name,
|
||||||
|
});
|
||||||
|
if (success) setTableData(data);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
{tableData?.length ? (
|
||||||
|
<ProTable
|
||||||
|
search={false}
|
||||||
|
headerTitle="查询表格"
|
||||||
|
actionRef={actionRef}
|
||||||
|
rowKey="id"
|
||||||
|
dataSource={tableData}
|
||||||
|
columns={columns}
|
||||||
|
/>
|
||||||
|
) : (
|
||||||
|
<></>
|
||||||
|
)}
|
||||||
|
</PageContainer>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ListPage;
|
||||||
|
|
|
||||||
|
|
@ -223,14 +223,17 @@ const ListPage: React.FC = () => {
|
||||||
(yooneTotal.yoone12Quantity || 0) +
|
(yooneTotal.yoone12Quantity || 0) +
|
||||||
(yooneTotal.yoone15Quantity || 0) +
|
(yooneTotal.yoone15Quantity || 0) +
|
||||||
(yooneTotal.yoone18Quantity || 0) +
|
(yooneTotal.yoone18Quantity || 0) +
|
||||||
(yooneTotal.zexQuantity || 0)
|
(yooneTotal.zexQuantity || 0)}
|
||||||
}
|
|
||||||
</div>
|
</div>
|
||||||
<div>YOONE 3MG: {yooneTotal.yoone3Quantity || 0}</div>
|
<div>YOONE 3MG: {yooneTotal.yoone3Quantity || 0}</div>
|
||||||
<div>YOONE 6MG: {yooneTotal.yoone6Quantity || 0}</div>
|
<div>YOONE 6MG: {yooneTotal.yoone6Quantity || 0}</div>
|
||||||
<div>YOONE 9MG: {yooneTotal.yoone9Quantity || 0}</div>
|
<div>YOONE 9MG: {yooneTotal.yoone9Quantity || 0}</div>
|
||||||
<div>YOONE 12MG新: {yooneTotal.yoone12QuantityNew || 0}</div>
|
<div>YOONE 12MG新: {yooneTotal.yoone12QuantityNew || 0}</div>
|
||||||
<div>YOONE 12MG白: {(yooneTotal.yoone12Quantity || 0) - (yooneTotal.yoone12QuantityNew || 0)}</div>
|
<div>
|
||||||
|
YOONE 12MG白:{' '}
|
||||||
|
{(yooneTotal.yoone12Quantity || 0) -
|
||||||
|
(yooneTotal.yoone12QuantityNew || 0)}
|
||||||
|
</div>
|
||||||
<div>YOONE 15MG: {yooneTotal.yoone15Quantity || 0}</div>
|
<div>YOONE 15MG: {yooneTotal.yoone15Quantity || 0}</div>
|
||||||
<div>YOONE 18MG: {yooneTotal.yoone18Quantity || 0}</div>
|
<div>YOONE 18MG: {yooneTotal.yoone18Quantity || 0}</div>
|
||||||
<div>ZEX: {yooneTotal.zexQuantity || 0}</div>
|
<div>ZEX: {yooneTotal.zexQuantity || 0}</div>
|
||||||
|
|
|
||||||
|
|
@ -427,9 +427,7 @@ const UpdateForm: React.FC<{
|
||||||
<ProFormTextArea label="备注" name="note" width={'lg'} />
|
<ProFormTextArea label="备注" name="note" width={'lg'} />
|
||||||
<ProFormDependency name={['items']}>
|
<ProFormDependency name={['items']}>
|
||||||
{({ items }) => {
|
{({ items }) => {
|
||||||
return (
|
return '数量:' + items?.reduce((acc, cur) => acc + cur.quantity, 0);
|
||||||
'数量:' + items?.reduce((acc, cur) => acc + cur.quantity, 0)
|
|
||||||
);
|
|
||||||
}}
|
}}
|
||||||
</ProFormDependency>
|
</ProFormDependency>
|
||||||
<ProFormList<API.PurchaseOrderItem>
|
<ProFormList<API.PurchaseOrderItem>
|
||||||
|
|
@ -459,7 +457,7 @@ const UpdateForm: React.FC<{
|
||||||
return (
|
return (
|
||||||
data?.map((item) => {
|
data?.map((item) => {
|
||||||
return {
|
return {
|
||||||
label: `${item.name} - ${item.nameCn}`,
|
label: `${item.name} - ${item.nameCn}`,
|
||||||
value: item.sku,
|
value: item.sku,
|
||||||
};
|
};
|
||||||
}) || []
|
}) || []
|
||||||
|
|
@ -624,7 +622,7 @@ const DetailForm: React.FC<{
|
||||||
return (
|
return (
|
||||||
data?.map((item) => {
|
data?.map((item) => {
|
||||||
return {
|
return {
|
||||||
label: `${item.name} - ${item.nameCn}`,
|
label: `${item.name} - ${item.nameCn}`,
|
||||||
value: item.sku,
|
value: item.sku,
|
||||||
};
|
};
|
||||||
}) || []
|
}) || []
|
||||||
|
|
|
||||||
|
|
@ -31,12 +31,12 @@ const ListPage: React.FC = () => {
|
||||||
dataIndex: 'operationType',
|
dataIndex: 'operationType',
|
||||||
valueType: 'select',
|
valueType: 'select',
|
||||||
valueEnum: {
|
valueEnum: {
|
||||||
'in': {
|
in: {
|
||||||
text: '入库'
|
text: '入库',
|
||||||
|
},
|
||||||
|
out: {
|
||||||
|
text: '出库',
|
||||||
},
|
},
|
||||||
"out": {
|
|
||||||
text: '出库'
|
|
||||||
}
|
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -207,7 +207,7 @@ const CreateForm: React.FC<{
|
||||||
drawerProps={{
|
drawerProps={{
|
||||||
destroyOnHidden: true,
|
destroyOnHidden: true,
|
||||||
}}
|
}}
|
||||||
onFinish={async ({orderNumber,...values}) => {
|
onFinish={async ({ orderNumber, ...values }) => {
|
||||||
try {
|
try {
|
||||||
const { success, message: errMsg } =
|
const { success, message: errMsg } =
|
||||||
await stockcontrollerCreatetransfer(values);
|
await stockcontrollerCreatetransfer(values);
|
||||||
|
|
@ -272,24 +272,38 @@ const CreateForm: React.FC<{
|
||||||
rules={[{ required: true, message: '请选择源目标仓库' }]}
|
rules={[{ required: true, message: '请选择源目标仓库' }]}
|
||||||
/>
|
/>
|
||||||
<ProFormTextArea name="note" label="备注" />
|
<ProFormTextArea name="note" label="备注" />
|
||||||
<ProFormText name={'orderNumber'} addonAfter={<Button onClick={async () => {
|
<ProFormText
|
||||||
const orderNumber = await form.getFieldValue('orderNumber')
|
name={'orderNumber'}
|
||||||
const { data } = await stockcontrollerGetpurchaseorder({orderNumber})
|
addonAfter={
|
||||||
form.setFieldsValue({
|
<Button
|
||||||
items: data?.map(
|
onClick={async () => {
|
||||||
(item: { productName: string; productSku: string }) => ({
|
const orderNumber = await form.getFieldValue('orderNumber');
|
||||||
...item,
|
const { data } = await stockcontrollerGetpurchaseorder({
|
||||||
productSku: {
|
orderNumber,
|
||||||
label: item.productName,
|
});
|
||||||
value: item.productSku,
|
form.setFieldsValue({
|
||||||
},
|
items: data?.map(
|
||||||
}),
|
(item: { productName: string; productSku: string }) => ({
|
||||||
),
|
...item,
|
||||||
})
|
productSku: {
|
||||||
}}>引用</Button>} />
|
label: item.productName,
|
||||||
|
value: item.productSku,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
});
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
引用
|
||||||
|
</Button>
|
||||||
|
}
|
||||||
|
/>
|
||||||
<ProFormDependency name={['items']}>
|
<ProFormDependency name={['items']}>
|
||||||
{({ items }) => {
|
{({ items }) => {
|
||||||
return '数量:' + (items?.reduce?.((acc, cur) => acc + cur.quantity, 0)||0);
|
return (
|
||||||
|
'数量:' +
|
||||||
|
(items?.reduce?.((acc, cur) => acc + cur.quantity, 0) || 0)
|
||||||
|
);
|
||||||
}}
|
}}
|
||||||
</ProFormDependency>
|
</ProFormDependency>
|
||||||
<ProFormList
|
<ProFormList
|
||||||
|
|
@ -653,7 +667,7 @@ const DetailForm: React.FC<{
|
||||||
return (
|
return (
|
||||||
data?.map((item) => {
|
data?.map((item) => {
|
||||||
return {
|
return {
|
||||||
label: `${item.name} - ${item.nameCn}`,
|
label: `${item.name} - ${item.nameCn}`,
|
||||||
value: item.sku,
|
value: item.sku,
|
||||||
};
|
};
|
||||||
}) || []
|
}) || []
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,8 @@
|
||||||
import React, { useRef, useState } from 'react';
|
import { sitecontrollerAll } from '@/servers/api/site';
|
||||||
|
import {
|
||||||
|
subscriptioncontrollerList,
|
||||||
|
subscriptioncontrollerSync,
|
||||||
|
} from '@/servers/api/subscription';
|
||||||
import {
|
import {
|
||||||
ActionType,
|
ActionType,
|
||||||
DrawerForm,
|
DrawerForm,
|
||||||
|
|
@ -7,14 +11,10 @@ import {
|
||||||
ProFormSelect,
|
ProFormSelect,
|
||||||
ProTable,
|
ProTable,
|
||||||
} from '@ant-design/pro-components';
|
} from '@ant-design/pro-components';
|
||||||
import { App, Button, Tag, Drawer, List } from 'antd';
|
import { App, Button, Drawer, List, Tag } from 'antd';
|
||||||
import dayjs from 'dayjs';
|
import dayjs from 'dayjs';
|
||||||
|
import React, { useRef, useState } from 'react';
|
||||||
import { request } from 'umi';
|
import { request } from 'umi';
|
||||||
import {
|
|
||||||
subscriptioncontrollerList,
|
|
||||||
subscriptioncontrollerSync,
|
|
||||||
} from '@/servers/api/subscription';
|
|
||||||
import { sitecontrollerAll } from '@/servers/api/site';
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 订阅状态枚举(用于筛选与展示)
|
* 订阅状态枚举(用于筛选与展示)
|
||||||
|
|
@ -77,7 +77,11 @@ const ListPage: React.FC = () => {
|
||||||
valueEnum: SUBSCRIPTION_STATUS_ENUM,
|
valueEnum: SUBSCRIPTION_STATUS_ENUM,
|
||||||
// 以 Tag 形式展示,更易辨识
|
// 以 Tag 形式展示,更易辨识
|
||||||
render: (_, row) =>
|
render: (_, row) =>
|
||||||
row?.status ? <Tag>{SUBSCRIPTION_STATUS_ENUM[row.status]?.text || row.status}</Tag> : '-',
|
row?.status ? (
|
||||||
|
<Tag>{SUBSCRIPTION_STATUS_ENUM[row.status]?.text || row.status}</Tag>
|
||||||
|
) : (
|
||||||
|
'-'
|
||||||
|
),
|
||||||
width: 120,
|
width: 120,
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -152,9 +156,9 @@ const ListPage: React.FC = () => {
|
||||||
const { success, data, message: errMsg } = resp as any;
|
const { success, data, message: errMsg } = resp as any;
|
||||||
if (!success) throw new Error(errMsg || '获取失败');
|
if (!success) throw new Error(errMsg || '获取失败');
|
||||||
// 仅保留与父订单号完全一致的订单(避免模糊匹配误入)
|
// 仅保留与父订单号完全一致的订单(避免模糊匹配误入)
|
||||||
const candidates: any[] = (Array.isArray(data) ? data : []).filter(
|
const candidates: any[] = (
|
||||||
(c: any) => String(c?.externalOrderId) === parentNumber
|
Array.isArray(data) ? data : []
|
||||||
);
|
).filter((c: any) => String(c?.externalOrderId) === parentNumber);
|
||||||
// 拉取详情,补充状态、金额、时间
|
// 拉取详情,补充状态、金额、时间
|
||||||
const details = [] as any[];
|
const details = [] as any[];
|
||||||
for (const c of candidates) {
|
for (const c of candidates) {
|
||||||
|
|
@ -230,14 +234,22 @@ const ListPage: React.FC = () => {
|
||||||
<List.Item>
|
<List.Item>
|
||||||
<List.Item.Meta
|
<List.Item.Meta
|
||||||
title={`#${item?.externalOrderId || '-'}`}
|
title={`#${item?.externalOrderId || '-'}`}
|
||||||
description={`关系:${item?.relationship || '-'},站点:${item?.siteName || '-'}`}
|
description={`关系:${item?.relationship || '-'},站点:${
|
||||||
|
item?.siteName || '-'
|
||||||
|
}`}
|
||||||
/>
|
/>
|
||||||
<div style={{ display: 'flex', gap: 12, alignItems: 'center' }}>
|
<div style={{ display: 'flex', gap: 12, alignItems: 'center' }}>
|
||||||
<span>{item?.date_created ? dayjs(item.date_created).format('YYYY-MM-DD HH:mm') : '-'}</span>
|
<span>
|
||||||
|
{item?.date_created
|
||||||
|
? dayjs(item.date_created).format('YYYY-MM-DD HH:mm')
|
||||||
|
: '-'}
|
||||||
|
</span>
|
||||||
<Tag>{item?.status || '-'}</Tag>
|
<Tag>{item?.status || '-'}</Tag>
|
||||||
<span>
|
<span>
|
||||||
{item?.currency_symbol || ''}
|
{item?.currency_symbol || ''}
|
||||||
{typeof item?.total === 'number' ? item.total.toFixed(2) : item?.total ?? '-'}
|
{typeof item?.total === 'number'
|
||||||
|
? item.total.toFixed(2)
|
||||||
|
: item?.total ?? '-'}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</List.Item>
|
</List.Item>
|
||||||
|
|
@ -274,7 +286,9 @@ const SyncForm: React.FC<{
|
||||||
*/
|
*/
|
||||||
onFinish={async (values) => {
|
onFinish={async (values) => {
|
||||||
try {
|
try {
|
||||||
const { success, message: errMsg } = await subscriptioncontrollerSync(values);
|
const { success, message: errMsg } = await subscriptioncontrollerSync(
|
||||||
|
values,
|
||||||
|
);
|
||||||
if (!success) {
|
if (!success) {
|
||||||
throw new Error(errMsg);
|
throw new Error(errMsg);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,31 +1,21 @@
|
||||||
import React, { useEffect, useRef, useState } from 'react';
|
|
||||||
import {
|
|
||||||
App,
|
|
||||||
Button,
|
|
||||||
Card,
|
|
||||||
Divider,
|
|
||||||
Drawer,
|
|
||||||
Empty,
|
|
||||||
Popconfirm,
|
|
||||||
Space,
|
|
||||||
Tag,
|
|
||||||
} from 'antd';
|
|
||||||
import { ActionType, ProDescriptions } from '@ant-design/pro-components';
|
|
||||||
import { CopyOutlined, DeleteFilled } from '@ant-design/icons';
|
import { CopyOutlined, DeleteFilled } from '@ant-design/icons';
|
||||||
|
import { ActionType, ProDescriptions } from '@ant-design/pro-components';
|
||||||
|
import { App, Button, Card, Divider, Drawer, Empty, Popconfirm } from 'antd';
|
||||||
|
import React, { useEffect, useRef } from 'react';
|
||||||
|
|
||||||
// 服务器 API 引用(保持与原 index.tsx 一致)
|
// 服务器 API 引用(保持与原 index.tsx 一致)
|
||||||
|
import { logisticscontrollerDelshipment } from '@/servers/api/logistics';
|
||||||
import {
|
import {
|
||||||
ordercontrollerChangestatus,
|
ordercontrollerChangestatus,
|
||||||
ordercontrollerGetorderdetail,
|
ordercontrollerGetorderdetail,
|
||||||
ordercontrollerSyncorderbyid,
|
ordercontrollerSyncorderbyid,
|
||||||
} from '@/servers/api/order';
|
} from '@/servers/api/order';
|
||||||
import { logisticscontrollerDelshipment } from '@/servers/api/logistics';
|
|
||||||
import { sitecontrollerAll } from '@/servers/api/site';
|
import { sitecontrollerAll } from '@/servers/api/site';
|
||||||
|
|
||||||
// 工具与子组件
|
// 工具与子组件
|
||||||
|
import { ORDER_STATUS_ENUM } from '@/constants';
|
||||||
import { formatShipmentState, formatSource } from '@/utils/format';
|
import { formatShipmentState, formatSource } from '@/utils/format';
|
||||||
import RelatedOrders from './RelatedOrders';
|
import RelatedOrders from './RelatedOrders';
|
||||||
import { ORDER_STATUS_ENUM } from '@/constants';
|
|
||||||
|
|
||||||
// 为保持原文件结构简单,此处从 index.tsx 引入的子组件仍由原文件导出或保持原状
|
// 为保持原文件结构简单,此处从 index.tsx 引入的子组件仍由原文件导出或保持原状
|
||||||
// 若后续需要彻底解耦,可将 OrderNote / Shipping / SalesChange 也独立到文件
|
// 若后续需要彻底解耦,可将 OrderNote / Shipping / SalesChange 也独立到文件
|
||||||
|
|
@ -35,13 +25,13 @@ type OrderRecord = API.Order;
|
||||||
|
|
||||||
interface OrderDetailDrawerProps {
|
interface OrderDetailDrawerProps {
|
||||||
tableRef: React.MutableRefObject<ActionType | undefined>; // 列表刷新引用
|
tableRef: React.MutableRefObject<ActionType | undefined>; // 列表刷新引用
|
||||||
orderId: number; // 订单主键 ID
|
orderId: number; // 订单主键 ID
|
||||||
record: OrderRecord; // 订单行记录
|
record: OrderRecord; // 订单行记录
|
||||||
open: boolean; // 是否打开抽屉
|
open: boolean; // 是否打开抽屉
|
||||||
onClose: () => void; // 关闭抽屉回调
|
onClose: () => void; // 关闭抽屉回调
|
||||||
setActiveLine: (id: number) => void; // 高亮当前行
|
setActiveLine: (id: number) => void; // 高亮当前行
|
||||||
OrderNoteComponent: React.ComponentType<any>; // 备注组件(从外部注入)
|
OrderNoteComponent: React.ComponentType<any>; // 备注组件(从外部注入)
|
||||||
SalesChangeComponent: React.ComponentType<any>; // 换货组件(从外部注入)
|
SalesChangeComponent: React.ComponentType<any>; // 换货组件(从外部注入)
|
||||||
}
|
}
|
||||||
|
|
||||||
const OrderDetailDrawer: React.FC<OrderDetailDrawerProps> = ({
|
const OrderDetailDrawer: React.FC<OrderDetailDrawerProps> = ({
|
||||||
|
|
@ -59,14 +49,18 @@ const OrderDetailDrawer: React.FC<OrderDetailDrawerProps> = ({
|
||||||
|
|
||||||
// 加载详情数据(与 index.tsx 中完全保持一致)
|
// 加载详情数据(与 index.tsx 中完全保持一致)
|
||||||
const initRequest = async () => {
|
const initRequest = async () => {
|
||||||
const { data, success }: API.OrderDetailRes = await ordercontrollerGetorderdetail({ orderId });
|
const { data, success }: API.OrderDetailRes =
|
||||||
|
await ordercontrollerGetorderdetail({ orderId });
|
||||||
if (!success || !data) return { data: {} } as any;
|
if (!success || !data) return { data: {} } as any;
|
||||||
data.sales = data.sales?.reduce((acc: API.OrderSale[], cur: API.OrderSale) => {
|
data.sales = data.sales?.reduce(
|
||||||
const idx = acc.findIndex((v: any) => v.productId === cur.productId);
|
(acc: API.OrderSale[], cur: API.OrderSale) => {
|
||||||
if (idx === -1) acc.push(cur);
|
const idx = acc.findIndex((v: any) => v.productId === cur.productId);
|
||||||
else acc[idx].quantity += cur.quantity;
|
if (idx === -1) acc.push(cur);
|
||||||
return acc;
|
else acc[idx].quantity += cur.quantity;
|
||||||
}, []);
|
return acc;
|
||||||
|
},
|
||||||
|
[],
|
||||||
|
);
|
||||||
return { data } as any;
|
return { data } as any;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
@ -220,108 +214,273 @@ const OrderDetailDrawer: React.FC<OrderDetailDrawerProps> = ({
|
||||||
: []),
|
: []),
|
||||||
]}
|
]}
|
||||||
>
|
>
|
||||||
<ProDescriptions labelStyle={{ width: '100px' }} actionRef={ref} request={initRequest}>
|
<ProDescriptions
|
||||||
<ProDescriptions.Item label="站点" dataIndex="siteId" valueType="select" request={async () => {
|
labelStyle={{ width: '100px' }}
|
||||||
const { data = [] } = await sitecontrollerAll();
|
actionRef={ref}
|
||||||
return data.map((item) => ({ label: item.siteName, value: item.id }));
|
request={initRequest}
|
||||||
}} />
|
>
|
||||||
<ProDescriptions.Item label="订单日期" dataIndex="date_created" valueType="dateTime" />
|
<ProDescriptions.Item
|
||||||
<ProDescriptions.Item label="订单状态" dataIndex="orderStatus" valueType="select" valueEnum={ORDER_STATUS_ENUM as any} />
|
label="站点"
|
||||||
|
dataIndex="siteId"
|
||||||
|
valueType="select"
|
||||||
|
request={async () => {
|
||||||
|
const { data = [] } = await sitecontrollerAll();
|
||||||
|
return data.map((item) => ({
|
||||||
|
label: item.siteName,
|
||||||
|
value: item.id,
|
||||||
|
}));
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
<ProDescriptions.Item
|
||||||
|
label="订单日期"
|
||||||
|
dataIndex="date_created"
|
||||||
|
valueType="dateTime"
|
||||||
|
/>
|
||||||
|
<ProDescriptions.Item
|
||||||
|
label="订单状态"
|
||||||
|
dataIndex="orderStatus"
|
||||||
|
valueType="select"
|
||||||
|
valueEnum={ORDER_STATUS_ENUM as any}
|
||||||
|
/>
|
||||||
<ProDescriptions.Item label="金额" dataIndex="total" />
|
<ProDescriptions.Item label="金额" dataIndex="total" />
|
||||||
<ProDescriptions.Item label="客户邮箱" dataIndex="customer_email" />
|
<ProDescriptions.Item label="客户邮箱" dataIndex="customer_email" />
|
||||||
<ProDescriptions.Item label="联系电话" span={3} render={(_, r: any) => (
|
<ProDescriptions.Item
|
||||||
<div><span>{r?.shipping?.phone || r?.billing?.phone || '-'}</span></div>
|
label="联系电话"
|
||||||
)} />
|
span={3}
|
||||||
|
render={(_, r: any) => (
|
||||||
|
<div>
|
||||||
|
<span>{r?.shipping?.phone || r?.billing?.phone || '-'}</span>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
<ProDescriptions.Item label="交易Id" dataIndex="transaction_id" />
|
<ProDescriptions.Item label="交易Id" dataIndex="transaction_id" />
|
||||||
<ProDescriptions.Item label="IP" dataIndex="customer_id_address" />
|
<ProDescriptions.Item label="IP" dataIndex="customer_id_address" />
|
||||||
<ProDescriptions.Item label="设备" dataIndex="device_type" />
|
<ProDescriptions.Item label="设备" dataIndex="device_type" />
|
||||||
<ProDescriptions.Item label="来源" render={(_, r: any) => formatSource(r.source_type, r.utm_source)} />
|
<ProDescriptions.Item
|
||||||
<ProDescriptions.Item label="原订单状态" dataIndex="status" valueType="select" valueEnum={ORDER_STATUS_ENUM as any} />
|
label="来源"
|
||||||
<ProDescriptions.Item label="支付链接" dataIndex="payment_url" span={3} copyable />
|
render={(_, r: any) => formatSource(r.source_type, r.utm_source)}
|
||||||
<ProDescriptions.Item label="客户备注" dataIndex="customer_note" span={3} />
|
/>
|
||||||
<ProDescriptions.Item label="发货信息" span={3} render={(_, r: any) => (
|
<ProDescriptions.Item
|
||||||
<div>
|
label="原订单状态"
|
||||||
<div>company:<span>{r?.shipping?.company || r?.billing?.company || '-'}</span></div>
|
dataIndex="status"
|
||||||
<div>first_name:<span>{r?.shipping?.first_name || r?.billing?.first_name || '-'}</span></div>
|
valueType="select"
|
||||||
<div>last_name:<span>{r?.shipping?.last_name || r?.billing?.last_name || '-'}</span></div>
|
valueEnum={ORDER_STATUS_ENUM as any}
|
||||||
<div>country:<span>{r?.shipping?.country || r?.billing?.country || '-'}</span></div>
|
/>
|
||||||
<div>state:<span>{r?.shipping?.state || r?.billing?.state || '-'}</span></div>
|
<ProDescriptions.Item
|
||||||
<div>city:<span>{r?.shipping?.city || r?.billing?.city || '-'}</span></div>
|
label="支付链接"
|
||||||
<div>postcode:<span>{r?.shipping?.postcode || r?.billing?.postcode || '-'}</span></div>
|
dataIndex="payment_url"
|
||||||
<div>phone:<span>{r?.shipping?.phone || r?.billing?.phone || '-'}</span></div>
|
span={3}
|
||||||
<div>address_1:<span>{r?.shipping?.address_1 || r?.billing?.address_1 || '-'}</span></div>
|
copyable
|
||||||
</div>
|
/>
|
||||||
)} />
|
<ProDescriptions.Item
|
||||||
<ProDescriptions.Item label="原始订单" span={3} render={(_, r: any) => (
|
label="客户备注"
|
||||||
<ul>
|
dataIndex="customer_note"
|
||||||
{(r?.items || []).map((item: any) => (
|
span={3}
|
||||||
<li key={item.id}>{item.name}:{item.quantity}</li>
|
/>
|
||||||
))}
|
<ProDescriptions.Item
|
||||||
</ul>
|
label="发货信息"
|
||||||
)} />
|
span={3}
|
||||||
<ProDescriptions.Item label="关联" span={3} render={(_, r: any) => (
|
render={(_, r: any) => (
|
||||||
<RelatedOrders data={r?.related} />
|
<div>
|
||||||
)} />
|
<div>
|
||||||
<ProDescriptions.Item label="订单内容" span={3} render={(_, r: any) => (
|
company:
|
||||||
<ul>
|
<span>
|
||||||
{(r?.sales || []).map((item: any) => (
|
{r?.shipping?.company || r?.billing?.company || '-'}
|
||||||
<li key={item.id}>{item.name}:{item.quantity}</li>
|
</span>
|
||||||
))}
|
</div>
|
||||||
</ul>
|
<div>
|
||||||
)} />
|
first_name:
|
||||||
<ProDescriptions.Item label="换货" span={3} render={(_, r: any) => (
|
<span>
|
||||||
<SalesChangeComponent detailRef={ref} id={r.id as number} />
|
{r?.shipping?.first_name || r?.billing?.first_name || '-'}
|
||||||
)} />
|
</span>
|
||||||
<ProDescriptions.Item label="备注" span={3} render={(_, r: any) => {
|
</div>
|
||||||
if (!r.notes || r.notes.length === 0) return (<Empty description="暂无备注" />);
|
<div>
|
||||||
return (
|
last_name:
|
||||||
<div style={{ width: '100%' }}>
|
<span>
|
||||||
{r.notes.map((note: any) => (
|
{r?.shipping?.last_name || r?.billing?.last_name || '-'}
|
||||||
<div style={{ marginBottom: 10 }} key={note.id}>
|
</span>
|
||||||
<div style={{ display: 'flex', justifyContent: 'space-between' }}>
|
</div>
|
||||||
<span>{note.username}</span>
|
<div>
|
||||||
<span>{note.createdAt}</span>
|
country:
|
||||||
|
<span>
|
||||||
|
{r?.shipping?.country || r?.billing?.country || '-'}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
state:
|
||||||
|
<span>{r?.shipping?.state || r?.billing?.state || '-'}</span>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
city:<span>{r?.shipping?.city || r?.billing?.city || '-'}</span>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
postcode:
|
||||||
|
<span>
|
||||||
|
{r?.shipping?.postcode || r?.billing?.postcode || '-'}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
phone:
|
||||||
|
<span>{r?.shipping?.phone || r?.billing?.phone || '-'}</span>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
address_1:
|
||||||
|
<span>
|
||||||
|
{r?.shipping?.address_1 || r?.billing?.address_1 || '-'}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
<ProDescriptions.Item
|
||||||
|
label="原始订单"
|
||||||
|
span={3}
|
||||||
|
render={(_, r: any) => (
|
||||||
|
<ul>
|
||||||
|
{(r?.items || []).map((item: any) => (
|
||||||
|
<li key={item.id}>
|
||||||
|
{item.name}:{item.quantity}
|
||||||
|
</li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
<ProDescriptions.Item
|
||||||
|
label="关联"
|
||||||
|
span={3}
|
||||||
|
render={(_, r: any) => <RelatedOrders data={r?.related} />}
|
||||||
|
/>
|
||||||
|
<ProDescriptions.Item
|
||||||
|
label="订单内容"
|
||||||
|
span={3}
|
||||||
|
render={(_, r: any) => (
|
||||||
|
<ul>
|
||||||
|
{(r?.sales || []).map((item: any) => (
|
||||||
|
<li key={item.id}>
|
||||||
|
{item.name}:{item.quantity}
|
||||||
|
</li>
|
||||||
|
))}
|
||||||
|
</ul>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
<ProDescriptions.Item
|
||||||
|
label="换货"
|
||||||
|
span={3}
|
||||||
|
render={(_, r: any) => (
|
||||||
|
<SalesChangeComponent detailRef={ref} id={r.id as number} />
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
<ProDescriptions.Item
|
||||||
|
label="备注"
|
||||||
|
span={3}
|
||||||
|
render={(_, r: any) => {
|
||||||
|
if (!r.notes || r.notes.length === 0)
|
||||||
|
return <Empty description="暂无备注" />;
|
||||||
|
return (
|
||||||
|
<div style={{ width: '100%' }}>
|
||||||
|
{r.notes.map((note: any) => (
|
||||||
|
<div style={{ marginBottom: 10 }} key={note.id}>
|
||||||
|
<div
|
||||||
|
style={{
|
||||||
|
display: 'flex',
|
||||||
|
justifyContent: 'space-between',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<span>{note.username}</span>
|
||||||
|
<span>{note.createdAt}</span>
|
||||||
|
</div>
|
||||||
|
<div>{note.content}</div>
|
||||||
</div>
|
</div>
|
||||||
<div>{note.content}</div>
|
))}
|
||||||
</div>
|
</div>
|
||||||
))}
|
);
|
||||||
</div>
|
}}
|
||||||
);
|
/>
|
||||||
}} />
|
<ProDescriptions.Item
|
||||||
<ProDescriptions.Item label="物流信息" span={3} render={(_, r: any) => {
|
label="物流信息"
|
||||||
if (!r.shipment || r.shipment.length === 0) return (<Empty description="暂无物流信息" />);
|
span={3}
|
||||||
return (
|
render={(_, r: any) => {
|
||||||
<div style={{ display: 'flex', flexDirection: 'column', width: '100%' }}>
|
if (!r.shipment || r.shipment.length === 0)
|
||||||
{r.shipment.map((v: any) => (
|
return <Empty description="暂无物流信息" />;
|
||||||
<Card key={v.id} style={{ marginBottom: '10px' }} extra={formatShipmentState(v.state)} title={<>
|
return (
|
||||||
{v.tracking_provider}
|
<div
|
||||||
{v.primary_tracking_number}
|
style={{
|
||||||
<CopyOutlined onClick={async () => {
|
display: 'flex',
|
||||||
try { await navigator.clipboard.writeText(v.tracking_url); message.success('复制成功!'); }
|
flexDirection: 'column',
|
||||||
catch { message.error('复制失败!'); }
|
width: '100%',
|
||||||
}} />
|
}}
|
||||||
</>}
|
>
|
||||||
actions={ (v.state === 'waiting-for-scheduling' || v.state === 'waiting-for-transit') ? [
|
{r.shipment.map((v: any) => (
|
||||||
<Popconfirm key="action-cancel" title="取消运单" description="确认取消运单?" onConfirm={async () => {
|
<Card
|
||||||
try { const { success, message: errMsg } = await logisticscontrollerDelshipment({ id: v.id }); if (!success) throw new Error(errMsg); tableRef.current?.reload(); ref.current?.reload?.(); }
|
key={v.id}
|
||||||
catch (error: any) { message.error(error.message); }
|
style={{ marginBottom: '10px' }}
|
||||||
}}>
|
extra={formatShipmentState(v.state)}
|
||||||
<DeleteFilled />取消运单
|
title={
|
||||||
</Popconfirm>
|
<>
|
||||||
] : [] }
|
{v.tracking_provider}
|
||||||
>
|
{v.primary_tracking_number}
|
||||||
<div>订单号: {Array.isArray(v?.orderIds) ? v.orderIds.join(',') : '-'}</div>
|
<CopyOutlined
|
||||||
{Array.isArray(v?.items) && v.items.map((item: any) => (
|
onClick={async () => {
|
||||||
<div key={item.id}>{item.name}: {item.quantity}</div>
|
try {
|
||||||
))}
|
await navigator.clipboard.writeText(
|
||||||
</Card>
|
v.tracking_url,
|
||||||
))}
|
);
|
||||||
</div>
|
message.success('复制成功!');
|
||||||
);
|
} catch {
|
||||||
}} />
|
message.error('复制失败!');
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
}
|
||||||
|
actions={
|
||||||
|
v.state === 'waiting-for-scheduling' ||
|
||||||
|
v.state === 'waiting-for-transit'
|
||||||
|
? [
|
||||||
|
<Popconfirm
|
||||||
|
key="action-cancel"
|
||||||
|
title="取消运单"
|
||||||
|
description="确认取消运单?"
|
||||||
|
onConfirm={async () => {
|
||||||
|
try {
|
||||||
|
const { success, message: errMsg } =
|
||||||
|
await logisticscontrollerDelshipment({
|
||||||
|
id: v.id,
|
||||||
|
});
|
||||||
|
if (!success) throw new Error(errMsg);
|
||||||
|
tableRef.current?.reload();
|
||||||
|
ref.current?.reload?.();
|
||||||
|
} catch (error: any) {
|
||||||
|
message.error(error.message);
|
||||||
|
}
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<DeleteFilled />
|
||||||
|
取消运单
|
||||||
|
</Popconfirm>,
|
||||||
|
]
|
||||||
|
: []
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<div>
|
||||||
|
订单号:{' '}
|
||||||
|
{Array.isArray(v?.orderIds) ? v.orderIds.join(',') : '-'}
|
||||||
|
</div>
|
||||||
|
{Array.isArray(v?.items) &&
|
||||||
|
v.items.map((item: any) => (
|
||||||
|
<div key={item.id}>
|
||||||
|
{item.name}: {item.quantity}
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</Card>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
}}
|
||||||
|
/>
|
||||||
</ProDescriptions>
|
</ProDescriptions>
|
||||||
</Drawer>
|
</Drawer>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
export default OrderDetailDrawer;
|
export default OrderDetailDrawer;
|
||||||
|
|
|
||||||
|
|
@ -1,7 +1,7 @@
|
||||||
import React from 'react';
|
|
||||||
import { Empty, Tag } from 'antd';
|
import { Empty, Tag } from 'antd';
|
||||||
import dayjs from 'dayjs';
|
import dayjs from 'dayjs';
|
||||||
import relativeTime from 'dayjs/plugin/relativeTime';
|
import relativeTime from 'dayjs/plugin/relativeTime';
|
||||||
|
import React from 'react';
|
||||||
|
|
||||||
dayjs.extend(relativeTime);
|
dayjs.extend(relativeTime);
|
||||||
|
|
||||||
|
|
@ -12,17 +12,36 @@ dayjs.extend(relativeTime);
|
||||||
*/
|
*/
|
||||||
const RelatedOrders: React.FC<{ data?: any[] }> = ({ data = [] }) => {
|
const RelatedOrders: React.FC<{ data?: any[] }> = ({ data = [] }) => {
|
||||||
const rows = (Array.isArray(data) ? data : []).map((it: any) => {
|
const rows = (Array.isArray(data) ? data : []).map((it: any) => {
|
||||||
const isSubscription = !!it?.externalSubscriptionId || !!it?.billing_period || !!it?.line_items;
|
const isSubscription =
|
||||||
const number = isSubscription ? `#${it?.externalSubscriptionId || it?.id}` : `#${it?.externalOrderId || it?.id}`;
|
!!it?.externalSubscriptionId || !!it?.billing_period || !!it?.line_items;
|
||||||
|
const number = isSubscription
|
||||||
|
? `#${it?.externalSubscriptionId || it?.id}`
|
||||||
|
: `#${it?.externalOrderId || it?.id}`;
|
||||||
const relationship = isSubscription ? 'Subscription' : 'Order';
|
const relationship = isSubscription ? 'Subscription' : 'Order';
|
||||||
const dateRaw = it?.start_date || it?.date_created || it?.createdAt || it?.updatedAt;
|
const dateRaw =
|
||||||
|
it?.start_date || it?.date_created || it?.createdAt || it?.updatedAt;
|
||||||
const dateText = dateRaw ? dayjs(dateRaw).fromNow() : '-';
|
const dateText = dateRaw ? dayjs(dateRaw).fromNow() : '-';
|
||||||
const status = (isSubscription ? it?.status : it?.orderStatus) || '-';
|
const status = (isSubscription ? it?.status : it?.orderStatus) || '-';
|
||||||
const statusLower = String(status).toLowerCase();
|
const statusLower = String(status).toLowerCase();
|
||||||
const color = statusLower === 'active' ? 'green' : statusLower === 'cancelled' ? 'red' : 'default';
|
const color =
|
||||||
|
statusLower === 'active'
|
||||||
|
? 'green'
|
||||||
|
: statusLower === 'cancelled'
|
||||||
|
? 'red'
|
||||||
|
: 'default';
|
||||||
const totalNum = Number(it?.total || 0);
|
const totalNum = Number(it?.total || 0);
|
||||||
const totalText = isSubscription ? `$${totalNum.toFixed(2)} / ${it?.billing_period || 'period'}` : `$${totalNum.toFixed(2)}`;
|
const totalText = isSubscription
|
||||||
return { key: `${isSubscription ? 'sub' : 'order'}-${it?.id}`, number, relationship, dateText, status, color, totalText };
|
? `$${totalNum.toFixed(2)} / ${it?.billing_period || 'period'}`
|
||||||
|
: `$${totalNum.toFixed(2)}`;
|
||||||
|
return {
|
||||||
|
key: `${isSubscription ? 'sub' : 'order'}-${it?.id}`,
|
||||||
|
number,
|
||||||
|
relationship,
|
||||||
|
dateText,
|
||||||
|
status,
|
||||||
|
color,
|
||||||
|
totalText,
|
||||||
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
if (rows.length === 0) return <Empty description="暂无关联" />;
|
if (rows.length === 0) return <Empty description="暂无关联" />;
|
||||||
|
|
@ -30,7 +49,14 @@ const RelatedOrders: React.FC<{ data?: any[] }> = ({ data = [] }) => {
|
||||||
return (
|
return (
|
||||||
<div style={{ width: '100%' }}>
|
<div style={{ width: '100%' }}>
|
||||||
{/* 表头(英文文案,符合国际化默认英文的要求) */}
|
{/* 表头(英文文案,符合国际化默认英文的要求) */}
|
||||||
<div style={{ display: 'grid', gridTemplateColumns: '1.5fr 1fr 1fr 1fr 1fr', padding: '8px 0', fontWeight: 600 }}>
|
<div
|
||||||
|
style={{
|
||||||
|
display: 'grid',
|
||||||
|
gridTemplateColumns: '1.5fr 1fr 1fr 1fr 1fr',
|
||||||
|
padding: '8px 0',
|
||||||
|
fontWeight: 600,
|
||||||
|
}}
|
||||||
|
>
|
||||||
<div>订单编号</div>
|
<div>订单编号</div>
|
||||||
<div>关系</div>
|
<div>关系</div>
|
||||||
<div>日期</div>
|
<div>日期</div>
|
||||||
|
|
@ -39,11 +65,23 @@ const RelatedOrders: React.FC<{ data?: any[] }> = ({ data = [] }) => {
|
||||||
</div>
|
</div>
|
||||||
<div>
|
<div>
|
||||||
{rows.map((r) => (
|
{rows.map((r) => (
|
||||||
<div key={r.key} style={{ display: 'grid', gridTemplateColumns: '1.5fr 1fr 1fr 1fr 1fr', padding: '6px 0', borderTop: '1px solid #f0f0f0' }}>
|
<div
|
||||||
<div><a>{r.number}</a></div>
|
key={r.key}
|
||||||
|
style={{
|
||||||
|
display: 'grid',
|
||||||
|
gridTemplateColumns: '1.5fr 1fr 1fr 1fr 1fr',
|
||||||
|
padding: '6px 0',
|
||||||
|
borderTop: '1px solid #f0f0f0',
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<div>
|
||||||
|
<a>{r.number}</a>
|
||||||
|
</div>
|
||||||
<div>{r.relationship}</div>
|
<div>{r.relationship}</div>
|
||||||
<div style={{ color: '#1677ff' }}>{r.dateText}</div>
|
<div style={{ color: '#1677ff' }}>{r.dateText}</div>
|
||||||
<div><Tag color={r.color}>{r.status}</Tag></div>
|
<div>
|
||||||
|
<Tag color={r.color}>{r.status}</Tag>
|
||||||
|
</div>
|
||||||
<div>{r.totalText}</div>
|
<div>{r.totalText}</div>
|
||||||
</div>
|
</div>
|
||||||
))}
|
))}
|
||||||
|
|
|
||||||
|
|
@ -1,12 +1,16 @@
|
||||||
import React, { useRef, useState } from 'react';
|
|
||||||
import { PageContainer } from '@ant-design/pro-layout';
|
|
||||||
import type { ProColumns, ActionType, ProTableProps } from '@ant-design/pro-components';
|
|
||||||
import { ProTable } from '@ant-design/pro-components';
|
|
||||||
import { App, Tag, Button } from 'antd';
|
|
||||||
import dayjs from 'dayjs';
|
|
||||||
import { ordercontrollerGetorders } from '@/servers/api/order';
|
import { ordercontrollerGetorders } from '@/servers/api/order';
|
||||||
import OrderDetailDrawer from './OrderDetailDrawer';
|
|
||||||
import { sitecontrollerAll } from '@/servers/api/site';
|
import { sitecontrollerAll } from '@/servers/api/site';
|
||||||
|
import type {
|
||||||
|
ActionType,
|
||||||
|
ProColumns,
|
||||||
|
ProTableProps,
|
||||||
|
} from '@ant-design/pro-components';
|
||||||
|
import { ProTable } from '@ant-design/pro-components';
|
||||||
|
import { PageContainer } from '@ant-design/pro-layout';
|
||||||
|
import { App, Button, Tag } from 'antd';
|
||||||
|
import dayjs from 'dayjs';
|
||||||
|
import React, { useRef, useState } from 'react';
|
||||||
|
import OrderDetailDrawer from './OrderDetailDrawer';
|
||||||
|
|
||||||
interface OrderItemRow {
|
interface OrderItemRow {
|
||||||
id: number;
|
id: number;
|
||||||
|
|
@ -43,7 +47,10 @@ const OrdersPage: React.FC = () => {
|
||||||
valueType: 'select',
|
valueType: 'select',
|
||||||
request: async () => {
|
request: async () => {
|
||||||
const { data = [] } = await sitecontrollerAll();
|
const { data = [] } = await sitecontrollerAll();
|
||||||
return (data || []).map((item: any) => ({ label: item.siteName, value: item.id }));
|
return (data || []).map((item: any) => ({
|
||||||
|
label: item.siteName,
|
||||||
|
value: item.id,
|
||||||
|
}));
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -51,7 +58,10 @@ const OrdersPage: React.FC = () => {
|
||||||
dataIndex: 'date_created',
|
dataIndex: 'date_created',
|
||||||
width: 180,
|
width: 180,
|
||||||
hideInSearch: true,
|
hideInSearch: true,
|
||||||
render: (_, row) => (row?.date_created ? dayjs(row.date_created).format('YYYY-MM-DD HH:mm') : '-'),
|
render: (_, row) =>
|
||||||
|
row?.date_created
|
||||||
|
? dayjs(row.date_created).format('YYYY-MM-DD HH:mm')
|
||||||
|
: '-',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
title: '邮箱',
|
title: '邮箱',
|
||||||
|
|
@ -109,7 +119,14 @@ const OrdersPage: React.FC = () => {
|
||||||
|
|
||||||
const request: ProTableProps<OrderItemRow>['request'] = async (params) => {
|
const request: ProTableProps<OrderItemRow>['request'] = async (params) => {
|
||||||
try {
|
try {
|
||||||
const { current = 1, pageSize = 10, siteId, keyword, customer_email, payment_method } = params as any;
|
const {
|
||||||
|
current = 1,
|
||||||
|
pageSize = 10,
|
||||||
|
siteId,
|
||||||
|
keyword,
|
||||||
|
customer_email,
|
||||||
|
payment_method,
|
||||||
|
} = params as any;
|
||||||
const [startDate, endDate] = (params as any).dateRange || [];
|
const [startDate, endDate] = (params as any).dateRange || [];
|
||||||
const resp = await ordercontrollerGetorders({
|
const resp = await ordercontrollerGetorders({
|
||||||
current,
|
current,
|
||||||
|
|
@ -119,7 +136,9 @@ const OrdersPage: React.FC = () => {
|
||||||
customer_email,
|
customer_email,
|
||||||
payment_method,
|
payment_method,
|
||||||
isSubscriptionOnly: true as any,
|
isSubscriptionOnly: true as any,
|
||||||
startDate: startDate ? (dayjs(startDate).toISOString() as any) : undefined,
|
startDate: startDate
|
||||||
|
? (dayjs(startDate).toISOString() as any)
|
||||||
|
: undefined,
|
||||||
endDate: endDate ? (dayjs(endDate).toISOString() as any) : undefined,
|
endDate: endDate ? (dayjs(endDate).toISOString() as any) : undefined,
|
||||||
} as any);
|
} as any);
|
||||||
const { success, data, message: errMsg } = resp as any;
|
const { success, data, message: errMsg } = resp as any;
|
||||||
|
|
@ -136,10 +155,10 @@ const OrdersPage: React.FC = () => {
|
||||||
};
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<PageContainer title='订阅订单'>
|
<PageContainer title="订阅订单">
|
||||||
<ProTable<OrderItemRow>
|
<ProTable<OrderItemRow>
|
||||||
actionRef={actionRef}
|
actionRef={actionRef}
|
||||||
rowKey='id'
|
rowKey="id"
|
||||||
columns={columns}
|
columns={columns}
|
||||||
request={request}
|
request={request}
|
||||||
pagination={{ showSizeChanger: true }}
|
pagination={{ showSizeChanger: true }}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
import {
|
import {
|
||||||
|
logisticscontrollerGetlistbyorderid,
|
||||||
logisticscontrollerGetorderlist,
|
logisticscontrollerGetorderlist,
|
||||||
logisticscontrollerGetlistbyorderid
|
|
||||||
} from '@/servers/api/logistics';
|
} from '@/servers/api/logistics';
|
||||||
import { SearchOutlined } from '@ant-design/icons';
|
import { SearchOutlined } from '@ant-design/icons';
|
||||||
import { PageContainer, ProFormSelect } from '@ant-design/pro-components';
|
import { PageContainer, ProFormSelect } from '@ant-design/pro-components';
|
||||||
|
|
@ -16,8 +16,9 @@ const TrackPage: React.FC = () => {
|
||||||
debounceTime={500}
|
debounceTime={500}
|
||||||
request={async ({ keyWords }) => {
|
request={async ({ keyWords }) => {
|
||||||
if (!keyWords || keyWords.length < 3) return [];
|
if (!keyWords || keyWords.length < 3) return [];
|
||||||
const { data: trackList } =
|
const { data: trackList } = await logisticscontrollerGetorderlist({
|
||||||
await logisticscontrollerGetorderlist({ number: keyWords });
|
number: keyWords,
|
||||||
|
});
|
||||||
return trackList?.map((v) => {
|
return trackList?.map((v) => {
|
||||||
return {
|
return {
|
||||||
label: v.siteName + ' ' + v.externalOrderId,
|
label: v.siteName + ' ' + v.externalOrderId,
|
||||||
|
|
@ -29,8 +30,8 @@ const TrackPage: React.FC = () => {
|
||||||
prefix: '订单号',
|
prefix: '订单号',
|
||||||
async onChange(value: string) {
|
async onChange(value: string) {
|
||||||
setId(value);
|
setId(value);
|
||||||
setData({})
|
setData({});
|
||||||
|
|
||||||
const { data } = await logisticscontrollerGetlistbyorderid({
|
const { data } = await logisticscontrollerGetlistbyorderid({
|
||||||
id,
|
id,
|
||||||
});
|
});
|
||||||
|
|
@ -53,32 +54,34 @@ const TrackPage: React.FC = () => {
|
||||||
),
|
),
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
{
|
{data?.item ? (
|
||||||
data?.item ?
|
<div>
|
||||||
<div>
|
<div>
|
||||||
<div>
|
<h4>原订单内容</h4>
|
||||||
<h4>原订单内容</h4>
|
{data?.item?.map((item) => (
|
||||||
{data?.item?.map((item) => (
|
<div style={{ paddingLeft: 20, color: 'blue' }}>
|
||||||
<div style={{ paddingLeft: 20, color: 'blue' }}>
|
{item.name} * {item.quantity}
|
||||||
{item.name} * {item.quantity}
|
</div>
|
||||||
</div>
|
))}
|
||||||
))}
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div> : <></>
|
) : (
|
||||||
}
|
<></>
|
||||||
{
|
)}
|
||||||
data?.saleItem ?
|
{data?.saleItem ? (
|
||||||
|
<div>
|
||||||
<div>
|
<div>
|
||||||
<div>
|
<h4>订单内容</h4>
|
||||||
<h4>订单内容</h4>
|
{data?.saleItem?.map((item) => (
|
||||||
{data?.saleItem?.map((item) => (
|
<div style={{ paddingLeft: 20, color: 'blue' }}>
|
||||||
<div style={{ paddingLeft: 20, color: 'blue' }}>
|
{item.name} * {item.quantity}
|
||||||
{item.name} * {item.quantity}
|
</div>
|
||||||
</div>
|
))}
|
||||||
))}
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div> : <></>
|
) : (
|
||||||
}
|
<></>
|
||||||
|
)}
|
||||||
</PageContainer>
|
</PageContainer>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -1,3 +1,3 @@
|
||||||
.selected-line-order-protable {
|
.selected-line-order-protable {
|
||||||
background-color: #add8e6;
|
background-color: #add8e6;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -95,8 +95,8 @@ export function formatUniuniShipmentState(state: string) {
|
||||||
'230': 'RETURN TO SENDER WAREHOUSE',
|
'230': 'RETURN TO SENDER WAREHOUSE',
|
||||||
'231': 'FAILED_DELIVERY_RETRY1',
|
'231': 'FAILED_DELIVERY_RETRY1',
|
||||||
'232': 'FAILED_DELIVERY_RETRY2',
|
'232': 'FAILED_DELIVERY_RETRY2',
|
||||||
'255': 'Gateway_To_Gateway_Transit'
|
'255': 'Gateway_To_Gateway_Transit',
|
||||||
}
|
};
|
||||||
if (state in UNIUNI_STATUS_ENUM) {
|
if (state in UNIUNI_STATUS_ENUM) {
|
||||||
return UNIUNI_STATUS_ENUM[state];
|
return UNIUNI_STATUS_ENUM[state];
|
||||||
} else {
|
} else {
|
||||||
|
|
|
||||||
|
|
@ -23,4 +23,4 @@ export async function printPDF(urls: string[]) {
|
||||||
}
|
}
|
||||||
|
|
||||||
next();
|
next();
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue