客户列表
This commit is contained in:
parent
354812c7c4
commit
b5d05bfc65
27
.umirc.ts
27
.umirc.ts
|
|
@ -80,12 +80,12 @@ export default defineConfig({
|
||||||
component: './Stock/Warehouse',
|
component: './Stock/Warehouse',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: '入库管理',
|
name: '采购管理',
|
||||||
path: '/stock/purchaseOrder',
|
path: '/stock/purchaseOrder',
|
||||||
component: './Stock/PurchaseOrder',
|
component: './Stock/PurchaseOrder',
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
name: '调拨管理',
|
name: '发货管理',
|
||||||
path: '/stock/transfer',
|
path: '/stock/transfer',
|
||||||
component: './Stock/Transfer',
|
component: './Stock/Transfer',
|
||||||
},
|
},
|
||||||
|
|
@ -106,6 +106,23 @@ export default defineConfig({
|
||||||
path: '/order/list',
|
path: '/order/list',
|
||||||
component: './Order/List',
|
component: './Order/List',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: '待发货产品',
|
||||||
|
path: '/order/item',
|
||||||
|
component: './Order/PendingItems',
|
||||||
|
},
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '客户管理',
|
||||||
|
path: '/customer',
|
||||||
|
access: 'canSeeAdmin',
|
||||||
|
routes: [
|
||||||
|
{
|
||||||
|
name: '客户列表',
|
||||||
|
path: '/customer/list',
|
||||||
|
component: './Customer/List',
|
||||||
|
},
|
||||||
],
|
],
|
||||||
},
|
},
|
||||||
// {
|
// {
|
||||||
|
|
@ -145,6 +162,12 @@ export default defineConfig({
|
||||||
component: './Statistics/Order',
|
component: './Statistics/Order',
|
||||||
access: 'canSeeSuper',
|
access: 'canSeeSuper',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: '订单来源',
|
||||||
|
path: '/statistics/orderSource',
|
||||||
|
component: './Statistics/OrderSource',
|
||||||
|
access: 'canSeeSuper',
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: '客户统计',
|
name: '客户统计',
|
||||||
path: '/statistics/customer',
|
path: '/statistics/customer',
|
||||||
|
|
|
||||||
|
|
@ -22,8 +22,10 @@
|
||||||
"dayjs": "^1.11.9",
|
"dayjs": "^1.11.9",
|
||||||
"echarts": "^5.6.0",
|
"echarts": "^5.6.0",
|
||||||
"echarts-for-react": "^3.0.2",
|
"echarts-for-react": "^3.0.2",
|
||||||
|
"file-saver": "^2.0.5",
|
||||||
"print-js": "^1.6.0",
|
"print-js": "^1.6.0",
|
||||||
"react-phone-input-2": "^2.15.1"
|
"react-phone-input-2": "^2.15.1",
|
||||||
|
"xlsx": "^0.18.5"
|
||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/react": "^18.0.33",
|
"@types/react": "^18.0.33",
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,247 @@
|
||||||
|
import { HistoryOrder } from '@/pages/Statistics/Order';
|
||||||
|
import {
|
||||||
|
customercontrollerAddtag,
|
||||||
|
customercontrollerDeltag,
|
||||||
|
customercontrollerGetcustomerlist,
|
||||||
|
customercontrollerGettags,
|
||||||
|
} from '@/servers/api/customer';
|
||||||
|
import {
|
||||||
|
ActionType,
|
||||||
|
ModalForm,
|
||||||
|
PageContainer,
|
||||||
|
ProColumns,
|
||||||
|
ProFormSelect,
|
||||||
|
ProTable,
|
||||||
|
} from '@ant-design/pro-components';
|
||||||
|
import { App, Button, Space, Tag } from 'antd';
|
||||||
|
import dayjs from 'dayjs';
|
||||||
|
import { useRef, useState } from 'react';
|
||||||
|
|
||||||
|
const ListPage: React.FC = () => {
|
||||||
|
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: 'customerId',
|
||||||
|
render: (_, record) => {
|
||||||
|
if(!record.customerId) return '-';
|
||||||
|
return String(record.customerId).padStart(6,0)
|
||||||
|
},
|
||||||
|
sorter: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
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')
|
||||||
|
: '-',
|
||||||
|
// search: {
|
||||||
|
// transform: (value: string) => {
|
||||||
|
// return { month: value };
|
||||||
|
// },
|
||||||
|
// },
|
||||||
|
},
|
||||||
|
{
|
||||||
|
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: 'YOONE订单数',
|
||||||
|
dataIndex: 'yoone_orders',
|
||||||
|
hideInSearch: true,
|
||||||
|
sorter: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: 'YOONE金额',
|
||||||
|
dataIndex: 'yoone_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 (
|
||||||
|
<Space>
|
||||||
|
<AddTag
|
||||||
|
email={record.email}
|
||||||
|
tags={record.tags}
|
||||||
|
tableRef={actionRef}
|
||||||
|
/>
|
||||||
|
<HistoryOrder
|
||||||
|
email={record.email}
|
||||||
|
tags={record.tags}
|
||||||
|
tableRef={actionRef}
|
||||||
|
/>
|
||||||
|
</Space>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
return (
|
||||||
|
<PageContainer ghost>
|
||||||
|
<ProTable
|
||||||
|
headerTitle="查询表格"
|
||||||
|
actionRef={actionRef}
|
||||||
|
rowKey="id"
|
||||||
|
request={async (params, sorter) => {
|
||||||
|
const key = Object.keys(sorter)[0];
|
||||||
|
const { data, success } = await customercontrollerGetcustomerlist({
|
||||||
|
...params,
|
||||||
|
...(key ? { sorterKey: key, sorterValue: sorter[key] } : {}),
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
total: data?.total || 0,
|
||||||
|
data: data?.items || [],
|
||||||
|
success,
|
||||||
|
};
|
||||||
|
}}
|
||||||
|
columns={columns}
|
||||||
|
/>
|
||||||
|
</PageContainer>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export const AddTag: React.FC<{
|
||||||
|
email: string;
|
||||||
|
tags?: string[];
|
||||||
|
tableRef: React.MutableRefObject<ActionType | undefined>;
|
||||||
|
}> = ({ email, tags, tableRef }) => {
|
||||||
|
const { message } = App.useApp();
|
||||||
|
const [tagList, setTagList] = useState<string[]>([]);
|
||||||
|
|
||||||
|
return (
|
||||||
|
<ModalForm
|
||||||
|
title={`修改标签 - ${email}`}
|
||||||
|
trigger={<Button>修改标签</Button>}
|
||||||
|
width={800}
|
||||||
|
modalProps={{
|
||||||
|
destroyOnHidden: true,
|
||||||
|
}}
|
||||||
|
submitter={false}
|
||||||
|
>
|
||||||
|
<ProFormSelect
|
||||||
|
mode="tags"
|
||||||
|
allowClear
|
||||||
|
name="tag"
|
||||||
|
label="标签"
|
||||||
|
request={async () => {
|
||||||
|
const { data, success } = await customercontrollerGettags();
|
||||||
|
if (!success) return [];
|
||||||
|
setTagList(tags || []);
|
||||||
|
return data
|
||||||
|
.filter((tag) => {
|
||||||
|
return !(tags || []).includes(tag);
|
||||||
|
})
|
||||||
|
.map((tag) => ({ label: tag, value: tag }));
|
||||||
|
}}
|
||||||
|
fieldProps={{
|
||||||
|
value: tagList, // 当前值
|
||||||
|
onChange: async (newValue) => {
|
||||||
|
const added = newValue.filter((x) => !tagList.includes(x));
|
||||||
|
const removed = tagList.filter((x) => !newValue.includes(x));
|
||||||
|
|
||||||
|
if (added.length) {
|
||||||
|
const { success, message: msg } = await customercontrollerAddtag({
|
||||||
|
email,
|
||||||
|
tag: added[0],
|
||||||
|
});
|
||||||
|
if (!success) {
|
||||||
|
message.error(msg);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (removed.length) {
|
||||||
|
const { success, message: msg } = await customercontrollerDeltag({
|
||||||
|
email,
|
||||||
|
tag: removed[0],
|
||||||
|
});
|
||||||
|
if (!success) {
|
||||||
|
message.error(msg);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
tableRef?.current?.reload();
|
||||||
|
|
||||||
|
setTagList(newValue);
|
||||||
|
},
|
||||||
|
}}
|
||||||
|
></ProFormSelect>
|
||||||
|
</ModalForm>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ListPage;
|
||||||
|
|
@ -154,7 +154,7 @@ const CreateForm: React.FC<{
|
||||||
}
|
}
|
||||||
autoFocusFirstInput
|
autoFocusFirstInput
|
||||||
drawerProps={{
|
drawerProps={{
|
||||||
destroyOnClose: true,
|
destroyOnHidden: true,
|
||||||
}}
|
}}
|
||||||
onFinish={async (values) => {
|
onFinish={async (values) => {
|
||||||
const { contact, ...params } = values;
|
const { contact, ...params } = values;
|
||||||
|
|
@ -284,7 +284,7 @@ const UpdateForm: React.FC<{
|
||||||
}
|
}
|
||||||
autoFocusFirstInput
|
autoFocusFirstInput
|
||||||
drawerProps={{
|
drawerProps={{
|
||||||
destroyOnClose: true,
|
destroyOnHidden: true,
|
||||||
}}
|
}}
|
||||||
onFinish={async (values) => {
|
onFinish={async (values) => {
|
||||||
const { contact, ...params } = values;
|
const { contact, ...params } = values;
|
||||||
|
|
|
||||||
|
|
@ -423,7 +423,7 @@ const SyncForm: React.FC<{
|
||||||
}
|
}
|
||||||
autoFocusFirstInput
|
autoFocusFirstInput
|
||||||
drawerProps={{
|
drawerProps={{
|
||||||
destroyOnClose: true,
|
destroyOnHidden: true,
|
||||||
}}
|
}}
|
||||||
onFinish={async (values) => {
|
onFinish={async (values) => {
|
||||||
try {
|
try {
|
||||||
|
|
@ -501,7 +501,7 @@ const Detail: React.FC<{
|
||||||
<Drawer
|
<Drawer
|
||||||
title="订单详情"
|
title="订单详情"
|
||||||
open={visiable}
|
open={visiable}
|
||||||
destroyOnClose
|
destroyOnHidden
|
||||||
size="large"
|
size="large"
|
||||||
onClose={() => setVisiable(false)}
|
onClose={() => setVisiable(false)}
|
||||||
footer={[
|
footer={[
|
||||||
|
|
@ -1036,7 +1036,7 @@ const Shipping: React.FC<{
|
||||||
size="large"
|
size="large"
|
||||||
width="80vw"
|
width="80vw"
|
||||||
modalProps={{
|
modalProps={{
|
||||||
destroyOnClose: true,
|
destroyOnHidden: true,
|
||||||
styles: {
|
styles: {
|
||||||
body: { maxHeight: '65vh', overflowY: 'auto', overflowX: 'hidden' },
|
body: { maxHeight: '65vh', overflowY: 'auto', overflowX: 'hidden' },
|
||||||
},
|
},
|
||||||
|
|
@ -1265,7 +1265,7 @@ const Shipping: React.FC<{
|
||||||
<ProFormSelect
|
<ProFormSelect
|
||||||
params={{ options }}
|
params={{ options }}
|
||||||
request={async ({ keyWords, options }) => {
|
request={async ({ keyWords, options }) => {
|
||||||
if (!keyWords || keyWords.length < 3) return options;
|
if (!keyWords || keyWords.length < 2) return options;
|
||||||
try {
|
try {
|
||||||
const { data } = await productcontrollerSearchproducts({
|
const { data } = await productcontrollerSearchproducts({
|
||||||
name: keyWords,
|
name: keyWords,
|
||||||
|
|
@ -1273,7 +1273,7 @@ const Shipping: React.FC<{
|
||||||
return (
|
return (
|
||||||
data?.map((item) => {
|
data?.map((item) => {
|
||||||
return {
|
return {
|
||||||
label: item?.name,
|
label: `${item.name} - ${item.nameCn}`,
|
||||||
value: item?.sku,
|
value: item?.sku,
|
||||||
};
|
};
|
||||||
}) || options
|
}) || options
|
||||||
|
|
@ -1802,7 +1802,7 @@ const CreateOrder: React.FC<{
|
||||||
size="large"
|
size="large"
|
||||||
width="80vw"
|
width="80vw"
|
||||||
modalProps={{
|
modalProps={{
|
||||||
destroyOnClose: true,
|
destroyOnHidden: true,
|
||||||
styles: {
|
styles: {
|
||||||
body: { maxHeight: '65vh', overflowY: 'auto', overflowX: 'hidden' },
|
body: { maxHeight: '65vh', overflowY: 'auto', overflowX: 'hidden' },
|
||||||
},
|
},
|
||||||
|
|
@ -1844,7 +1844,7 @@ const CreateOrder: React.FC<{
|
||||||
<ProForm.Group>
|
<ProForm.Group>
|
||||||
<ProFormSelect
|
<ProFormSelect
|
||||||
request={async ({ keyWords, options }) => {
|
request={async ({ keyWords, options }) => {
|
||||||
if (!keyWords || keyWords.length < 3) return options;
|
if (!keyWords || keyWords.length < 2) return options;
|
||||||
try {
|
try {
|
||||||
const { data } = await productcontrollerSearchproducts({
|
const { data } = await productcontrollerSearchproducts({
|
||||||
name: keyWords,
|
name: keyWords,
|
||||||
|
|
@ -1852,7 +1852,7 @@ const CreateOrder: React.FC<{
|
||||||
return (
|
return (
|
||||||
data?.map((item) => {
|
data?.map((item) => {
|
||||||
return {
|
return {
|
||||||
label: item?.name,
|
label: `${item.name} - ${item.nameCn}`,
|
||||||
value: item?.sku,
|
value: item?.sku,
|
||||||
};
|
};
|
||||||
}) || options
|
}) || options
|
||||||
|
|
@ -2011,7 +2011,7 @@ const AddressPicker: React.FC<{
|
||||||
<ModalForm
|
<ModalForm
|
||||||
title="选择地址"
|
title="选择地址"
|
||||||
trigger={<Button type="primary">选择地址</Button>}
|
trigger={<Button type="primary">选择地址</Button>}
|
||||||
modalProps={{ destroyOnClose: true }}
|
modalProps={{ destroyOnHidden: true }}
|
||||||
onFinish={async () => {
|
onFinish={async () => {
|
||||||
if (!selectedRow) {
|
if (!selectedRow) {
|
||||||
message.error('请选择地址');
|
message.error('请选择地址');
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,100 @@
|
||||||
|
import { ordercontrollerPengdingitems } from '@/servers/api/order';
|
||||||
|
import { stockcontrollerGetallstockpoints } from '@/servers/api/stock';
|
||||||
|
import {
|
||||||
|
ActionType,
|
||||||
|
PageContainer,
|
||||||
|
ProColumns,
|
||||||
|
ProTable,
|
||||||
|
} from '@ant-design/pro-components';
|
||||||
|
import { App, Button } from 'antd';
|
||||||
|
import { saveAs } from 'file-saver';
|
||||||
|
import { useEffect, useRef, useState } from 'react';
|
||||||
|
import * as XLSX from 'xlsx';
|
||||||
|
|
||||||
|
const ListPage: React.FC = () => {
|
||||||
|
const { message } = App.useApp();
|
||||||
|
const actionRef = useRef<ActionType>();
|
||||||
|
const [points, setPoints] = useState<API.StockPoint[]>([]);
|
||||||
|
useEffect(() => {
|
||||||
|
stockcontrollerGetallstockpoints().then(({ data }) => {
|
||||||
|
setPoints(data as API.StockPoint[]);
|
||||||
|
});
|
||||||
|
}, []);
|
||||||
|
const columns: ProColumns<API.StockDTO>[] = [
|
||||||
|
{
|
||||||
|
title: '产品名称',
|
||||||
|
dataIndex: 'name',
|
||||||
|
hideInSearch: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '数量',
|
||||||
|
dataIndex: 'quantity',
|
||||||
|
hideInSearch: true,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
title: '订单号',
|
||||||
|
dataIndex: 'numbers',
|
||||||
|
hideInSearch: true,
|
||||||
|
width: 800,
|
||||||
|
render: (_, record) => {
|
||||||
|
return record?.numbers?.join?.('、');
|
||||||
|
},
|
||||||
|
},
|
||||||
|
];
|
||||||
|
|
||||||
|
return (
|
||||||
|
<PageContainer ghost>
|
||||||
|
<ProTable<API.StockDTO>
|
||||||
|
headerTitle="查询表格"
|
||||||
|
actionRef={actionRef}
|
||||||
|
rowKey="id"
|
||||||
|
request={async (params) => {
|
||||||
|
const { data, success } = await ordercontrollerPengdingitems(params);
|
||||||
|
|
||||||
|
return {
|
||||||
|
total: data?.total || 0,
|
||||||
|
data: data?.items || [],
|
||||||
|
success,
|
||||||
|
};
|
||||||
|
}}
|
||||||
|
columns={columns}
|
||||||
|
toolBarRender={() => [
|
||||||
|
<Button
|
||||||
|
type="primary"
|
||||||
|
onClick={async () => {
|
||||||
|
const { data, success } = await ordercontrollerPengdingitems({
|
||||||
|
current: 1,
|
||||||
|
pageSize: 20000,
|
||||||
|
});
|
||||||
|
if (!success) return;
|
||||||
|
// 表头
|
||||||
|
const headers = ['产品名', '数量', '订单号'];
|
||||||
|
|
||||||
|
// 数据行
|
||||||
|
const rows = (data?.items || []).map((item) => {
|
||||||
|
return [item.name, item.quantity, item.numbers?.join('、')];
|
||||||
|
});
|
||||||
|
|
||||||
|
// 导出
|
||||||
|
const sheet = XLSX.utils.aoa_to_sheet([headers, ...rows]);
|
||||||
|
const book = XLSX.utils.book_new();
|
||||||
|
XLSX.utils.book_append_sheet(book, sheet, '待发产品');
|
||||||
|
const buffer = XLSX.write(book, {
|
||||||
|
bookType: 'xlsx',
|
||||||
|
type: 'array',
|
||||||
|
});
|
||||||
|
const blob = new Blob([buffer], {
|
||||||
|
type: 'application/octet-stream',
|
||||||
|
});
|
||||||
|
saveAs(blob, '待发产品.xlsx');
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
导出
|
||||||
|
</Button>,
|
||||||
|
]}
|
||||||
|
/>
|
||||||
|
</PageContainer>
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
export default ListPage;
|
||||||
|
|
@ -84,7 +84,7 @@ const CreateForm: React.FC<{
|
||||||
}
|
}
|
||||||
autoFocusFirstInput
|
autoFocusFirstInput
|
||||||
drawerProps={{
|
drawerProps={{
|
||||||
destroyOnClose: true,
|
destroyOnHidden: true,
|
||||||
}}
|
}}
|
||||||
onFinish={async (values) => {
|
onFinish={async (values) => {
|
||||||
try {
|
try {
|
||||||
|
|
|
||||||
|
|
@ -122,7 +122,7 @@ const CreateForm: React.FC<{
|
||||||
}
|
}
|
||||||
autoFocusFirstInput
|
autoFocusFirstInput
|
||||||
drawerProps={{
|
drawerProps={{
|
||||||
destroyOnClose: true,
|
destroyOnHidden: true,
|
||||||
}}
|
}}
|
||||||
onFinish={async (values) => {
|
onFinish={async (values) => {
|
||||||
try {
|
try {
|
||||||
|
|
@ -174,7 +174,7 @@ const UpdateForm: React.FC<{
|
||||||
}
|
}
|
||||||
autoFocusFirstInput
|
autoFocusFirstInput
|
||||||
drawerProps={{
|
drawerProps={{
|
||||||
destroyOnClose: true,
|
destroyOnHidden: true,
|
||||||
}}
|
}}
|
||||||
onFinish={async (values) => {
|
onFinish={async (values) => {
|
||||||
try {
|
try {
|
||||||
|
|
|
||||||
|
|
@ -120,7 +120,7 @@ const CreateForm: React.FC<{
|
||||||
}
|
}
|
||||||
autoFocusFirstInput
|
autoFocusFirstInput
|
||||||
drawerProps={{
|
drawerProps={{
|
||||||
destroyOnClose: true,
|
destroyOnHidden: true,
|
||||||
}}
|
}}
|
||||||
onFinish={async (values) => {
|
onFinish={async (values) => {
|
||||||
try {
|
try {
|
||||||
|
|
@ -172,7 +172,7 @@ const UpdateForm: React.FC<{
|
||||||
}
|
}
|
||||||
autoFocusFirstInput
|
autoFocusFirstInput
|
||||||
drawerProps={{
|
drawerProps={{
|
||||||
destroyOnClose: true,
|
destroyOnHidden: true,
|
||||||
}}
|
}}
|
||||||
onFinish={async (values) => {
|
onFinish={async (values) => {
|
||||||
try {
|
try {
|
||||||
|
|
|
||||||
|
|
@ -5,6 +5,7 @@ import {
|
||||||
productcontrollerGetflavorsall,
|
productcontrollerGetflavorsall,
|
||||||
productcontrollerGetproductlist,
|
productcontrollerGetproductlist,
|
||||||
productcontrollerGetstrengthall,
|
productcontrollerGetstrengthall,
|
||||||
|
productcontrollerUpdateproductnamecn,
|
||||||
} from '@/servers/api/product';
|
} from '@/servers/api/product';
|
||||||
import { PlusOutlined } from '@ant-design/icons';
|
import { PlusOutlined } from '@ant-design/icons';
|
||||||
import {
|
import {
|
||||||
|
|
@ -21,6 +22,28 @@ import {
|
||||||
import { App, Button, Popconfirm } from 'antd';
|
import { App, Button, Popconfirm } from 'antd';
|
||||||
import React, { useRef } from 'react';
|
import React, { useRef } from 'react';
|
||||||
|
|
||||||
|
const NameCn: React.FC<{
|
||||||
|
id: number;
|
||||||
|
value: string;
|
||||||
|
tableRef: React.MutableRefObject<ActionType | undefined>;
|
||||||
|
}> = ({value,tableRef, id}) => {
|
||||||
|
const { message } = App.useApp();
|
||||||
|
const [editable, setEditable] = React.useState<boolean>(false);
|
||||||
|
if (!editable) return <div onClick={() => setEditable(true)}>{value||'-'}</div>;
|
||||||
|
return <ProFormText fieldProps={{autoFocus:true}} initialValue={value} onBlur={async(e) => {
|
||||||
|
if(!e.target.value) return setEditable(false)
|
||||||
|
const { success, message: errMsg } =
|
||||||
|
await productcontrollerUpdateproductnamecn({
|
||||||
|
id,
|
||||||
|
nameCn: e.target.value,
|
||||||
|
})
|
||||||
|
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>();
|
||||||
|
|
||||||
|
|
@ -30,6 +53,15 @@ const List: React.FC = () => {
|
||||||
title: '名称',
|
title: '名称',
|
||||||
dataIndex: 'name',
|
dataIndex: 'name',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
title: '中文名',
|
||||||
|
dataIndex: 'nameCn',
|
||||||
|
render: (_, record) => {
|
||||||
|
return (
|
||||||
|
<NameCn value={record.nameCn} id={record.id} tableRef={actionRef} />
|
||||||
|
)
|
||||||
|
},
|
||||||
|
},
|
||||||
{
|
{
|
||||||
title: '产品描述',
|
title: '产品描述',
|
||||||
dataIndex: 'description',
|
dataIndex: 'description',
|
||||||
|
|
@ -117,6 +149,12 @@ const List: React.FC = () => {
|
||||||
};
|
};
|
||||||
}}
|
}}
|
||||||
columns={columns}
|
columns={columns}
|
||||||
|
editable={{
|
||||||
|
type: 'single',
|
||||||
|
onSave: async (key, record, originRow) => {
|
||||||
|
console.log('保存数据:', record);
|
||||||
|
},
|
||||||
|
}}
|
||||||
rowSelection={{
|
rowSelection={{
|
||||||
onChange: (_, selectedRows) => setSelectedRows(selectedRows),
|
onChange: (_, selectedRows) => setSelectedRows(selectedRows),
|
||||||
}}
|
}}
|
||||||
|
|
@ -140,7 +178,7 @@ const CreateForm: React.FC<{
|
||||||
}
|
}
|
||||||
autoFocusFirstInput
|
autoFocusFirstInput
|
||||||
drawerProps={{
|
drawerProps={{
|
||||||
destroyOnClose: true,
|
destroyOnHidden: true,
|
||||||
}}
|
}}
|
||||||
onFinish={async (values) => {
|
onFinish={async (values) => {
|
||||||
try {
|
try {
|
||||||
|
|
|
||||||
|
|
@ -118,7 +118,7 @@ const CreateForm: React.FC<{
|
||||||
}
|
}
|
||||||
autoFocusFirstInput
|
autoFocusFirstInput
|
||||||
drawerProps={{
|
drawerProps={{
|
||||||
destroyOnClose: true,
|
destroyOnHidden: true,
|
||||||
}}
|
}}
|
||||||
onFinish={async (values) => {
|
onFinish={async (values) => {
|
||||||
try {
|
try {
|
||||||
|
|
@ -170,7 +170,7 @@ const CreateForm: React.FC<{
|
||||||
// }
|
// }
|
||||||
// autoFocusFirstInput
|
// autoFocusFirstInput
|
||||||
// drawerProps={{
|
// drawerProps={{
|
||||||
// destroyOnClose: true,
|
// destroyOnHidden: true,
|
||||||
// }}
|
// }}
|
||||||
// onFinish={async (values) => {
|
// onFinish={async (values) => {
|
||||||
// try {
|
// try {
|
||||||
|
|
|
||||||
|
|
@ -196,7 +196,7 @@ const SyncForm: React.FC<{
|
||||||
}
|
}
|
||||||
autoFocusFirstInput
|
autoFocusFirstInput
|
||||||
drawerProps={{
|
drawerProps={{
|
||||||
destroyOnClose: true,
|
destroyOnHidden: true,
|
||||||
}}
|
}}
|
||||||
onFinish={async (values) => {
|
onFinish={async (values) => {
|
||||||
try {
|
try {
|
||||||
|
|
@ -249,7 +249,7 @@ const UpdateForm: React.FC<{
|
||||||
}
|
}
|
||||||
autoFocusFirstInput
|
autoFocusFirstInput
|
||||||
drawerProps={{
|
drawerProps={{
|
||||||
destroyOnClose: true,
|
destroyOnHidden: true,
|
||||||
}}
|
}}
|
||||||
onFinish={async (values) => {
|
onFinish={async (values) => {
|
||||||
const { siteId, ...params } = values;
|
const { siteId, ...params } = values;
|
||||||
|
|
@ -339,7 +339,7 @@ const UpdateVaritation: React.FC<{
|
||||||
}
|
}
|
||||||
autoFocusFirstInput
|
autoFocusFirstInput
|
||||||
drawerProps={{
|
drawerProps={{
|
||||||
destroyOnClose: true,
|
destroyOnHidden: true,
|
||||||
}}
|
}}
|
||||||
onFinish={async (values) => {
|
onFinish={async (values) => {
|
||||||
const { ...params } = values;
|
const { ...params } = values;
|
||||||
|
|
@ -434,7 +434,7 @@ const SetComponent: React.FC<{
|
||||||
}
|
}
|
||||||
autoFocusFirstInput
|
autoFocusFirstInput
|
||||||
drawerProps={{
|
drawerProps={{
|
||||||
destroyOnClose: true,
|
destroyOnHidden: true,
|
||||||
}}
|
}}
|
||||||
onFinish={async ({ constitution }) => {
|
onFinish={async ({ constitution }) => {
|
||||||
try {
|
try {
|
||||||
|
|
|
||||||
|
|
@ -1,4 +1,6 @@
|
||||||
import { ORDER_STATUS_ENUM } from '@/constants';
|
import { ORDER_STATUS_ENUM } from '@/constants';
|
||||||
|
import { AddTag } from '@/pages/Customer/List';
|
||||||
|
import { customercontrollerDeltag } from '@/servers/api/customer';
|
||||||
import { sitecontrollerAll } from '@/servers/api/site';
|
import { sitecontrollerAll } from '@/servers/api/site';
|
||||||
import {
|
import {
|
||||||
statisticscontrollerGetorderbydate,
|
statisticscontrollerGetorderbydate,
|
||||||
|
|
@ -7,6 +9,7 @@ import {
|
||||||
} from '@/servers/api/statistics';
|
} from '@/servers/api/statistics';
|
||||||
import { formatSource } from '@/utils/format';
|
import { formatSource } from '@/utils/format';
|
||||||
import {
|
import {
|
||||||
|
ActionType,
|
||||||
ModalForm,
|
ModalForm,
|
||||||
PageContainer,
|
PageContainer,
|
||||||
ProForm,
|
ProForm,
|
||||||
|
|
@ -14,10 +17,10 @@ import {
|
||||||
ProFormSelect,
|
ProFormSelect,
|
||||||
ProTable,
|
ProTable,
|
||||||
} from '@ant-design/pro-components';
|
} from '@ant-design/pro-components';
|
||||||
import { Button } from 'antd';
|
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, useState } from 'react';
|
import { useEffect, useMemo, useRef, useState } from 'react';
|
||||||
|
|
||||||
const highlightText = (text: string, keyword: string) => {
|
const highlightText = (text: string, keyword: string) => {
|
||||||
if (!keyword) return text;
|
if (!keyword) return text;
|
||||||
|
|
@ -43,6 +46,7 @@ const ListPage: React.FC = () => {
|
||||||
tooltip: {
|
tooltip: {
|
||||||
trigger: 'axis',
|
trigger: 'axis',
|
||||||
formatter: function (params) {
|
formatter: function (params) {
|
||||||
|
const xValue = params[0].axisValue;
|
||||||
const items = params.map((item) => {
|
const items = params.map((item) => {
|
||||||
return `<span style="display: inline-block;width: 250px">${item.marker} ${item.seriesName}: ${item.value}</span>`;
|
return `<span style="display: inline-block;width: 250px">${item.marker} ${item.seriesName}: ${item.value}</span>`;
|
||||||
});
|
});
|
||||||
|
|
@ -53,7 +57,13 @@ const ListPage: React.FC = () => {
|
||||||
rows.push(items.slice(i, i + 4).join(''));
|
rows.push(items.slice(i, i + 4).join(''));
|
||||||
}
|
}
|
||||||
|
|
||||||
return rows.join('<br/>');
|
return (
|
||||||
|
`<div>${xValue} ${
|
||||||
|
['周日', '周一', '周二', '周三', '周四', '周五', '周六'][
|
||||||
|
dayjs(xValue).day()
|
||||||
|
]
|
||||||
|
}</div>` + rows.join('<br/>')
|
||||||
|
);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
legend: {
|
legend: {
|
||||||
|
|
@ -120,28 +130,6 @@ const ListPage: React.FC = () => {
|
||||||
const res = data?.sort(() => -1);
|
const res = data?.sort(() => -1);
|
||||||
setXAxis(res?.map((v) => dayjs(v.order_date).format('YYYY-MM-DD')));
|
setXAxis(res?.map((v) => dayjs(v.order_date).format('YYYY-MM-DD')));
|
||||||
setSeries([
|
setSeries([
|
||||||
{
|
|
||||||
name: '首购订单数',
|
|
||||||
type: 'line',
|
|
||||||
data: res?.map((v) => v.first_purchase_orders),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: '首购金额',
|
|
||||||
type: 'line',
|
|
||||||
yAxisIndex: 1,
|
|
||||||
data: res?.map((v) => v.first_purchase_total),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: '复购订单数',
|
|
||||||
type: 'line',
|
|
||||||
data: res?.map((v) => v.repeat_purchase_orders),
|
|
||||||
},
|
|
||||||
{
|
|
||||||
name: '复购金额',
|
|
||||||
type: 'line',
|
|
||||||
yAxisIndex: 1,
|
|
||||||
data: res?.map((v) => v.repeat_purchase_total),
|
|
||||||
},
|
|
||||||
{
|
{
|
||||||
name: 'TOGO CPC订单数',
|
name: 'TOGO CPC订单数',
|
||||||
type: 'line',
|
type: 'line',
|
||||||
|
|
@ -162,6 +150,50 @@ const ListPage: React.FC = () => {
|
||||||
type: 'line',
|
type: 'line',
|
||||||
data: res?.map((v) => v.non_can_cpc_orders),
|
data: res?.map((v) => v.non_can_cpc_orders),
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
name: '直达订单数',
|
||||||
|
type: 'line',
|
||||||
|
data: res?.map((v) => v.direct_orders),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '直达金额',
|
||||||
|
type: 'line',
|
||||||
|
yAxisIndex: 1,
|
||||||
|
data: res?.map((v) => v.direct_total),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '首购订单数',
|
||||||
|
type: 'line',
|
||||||
|
data: res?.map((v) => v.first_purchase_orders),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '首购金额',
|
||||||
|
type: 'line',
|
||||||
|
yAxisIndex: 1,
|
||||||
|
data: res?.map((v) => v.first_purchase_total),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'organic订单数',
|
||||||
|
type: 'line',
|
||||||
|
data: res?.map((v) => v.organic_orders),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: 'organic金额',
|
||||||
|
type: 'line',
|
||||||
|
yAxisIndex: 1,
|
||||||
|
data: res?.map((v) => v.organic_total),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '复购订单数',
|
||||||
|
type: 'line',
|
||||||
|
data: res?.map((v) => v.repeat_purchase_orders),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: '复购金额',
|
||||||
|
type: 'line',
|
||||||
|
yAxisIndex: 1,
|
||||||
|
data: res?.map((v) => v.repeat_purchase_total),
|
||||||
|
},
|
||||||
{
|
{
|
||||||
name: 'CPC订单数',
|
name: 'CPC订单数',
|
||||||
type: 'line',
|
type: 'line',
|
||||||
|
|
@ -612,6 +644,7 @@ const DailyOrders: React.FC<{
|
||||||
selectedDate;
|
selectedDate;
|
||||||
}> = ({ selectedDate }) => {
|
}> = ({ selectedDate }) => {
|
||||||
const [orders, setOrders] = useState([]);
|
const [orders, setOrders] = useState([]);
|
||||||
|
const actionRef = useRef<ActionType>();
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!selectedDate) {
|
if (!selectedDate) {
|
||||||
setOrders([]);
|
setOrders([]);
|
||||||
|
|
@ -683,6 +716,12 @@ const DailyOrders: React.FC<{
|
||||||
valueType: 'dateTime',
|
valueType: 'dateTime',
|
||||||
sorter: true,
|
sorter: true,
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
title: '尾单时间',
|
||||||
|
dataIndex: 'last_purchase_date',
|
||||||
|
valueType: 'dateTime',
|
||||||
|
sorter: true,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
title: '支付时间',
|
title: '支付时间',
|
||||||
dataIndex: 'date_paid',
|
dataIndex: 'date_paid',
|
||||||
|
|
@ -784,12 +823,53 @@ const DailyOrders: React.FC<{
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
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: '操作',
|
title: '操作',
|
||||||
dataIndex: 'action',
|
dataIndex: 'action',
|
||||||
valueType: 'option',
|
valueType: 'option',
|
||||||
render: (_, record) => {
|
render: (_, record) => {
|
||||||
return <HistoryOrder email={record.customer_email} />;
|
return (
|
||||||
|
<Space>
|
||||||
|
<AddTag
|
||||||
|
email={record.customer_email}
|
||||||
|
tags={record.tags}
|
||||||
|
tableRef={actionRef}
|
||||||
|
/>
|
||||||
|
<HistoryOrder
|
||||||
|
email={record.customer_email}
|
||||||
|
tags={record.tags}
|
||||||
|
tableRef={actionRef}
|
||||||
|
/>
|
||||||
|
</Space>
|
||||||
|
);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
]}
|
]}
|
||||||
|
|
@ -799,15 +879,19 @@ const DailyOrders: React.FC<{
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
const HistoryOrder: React.FC<{
|
export const HistoryOrder: React.FC<{
|
||||||
email: string;
|
email: string;
|
||||||
}> = ({ email }) => {
|
tags?: string[];
|
||||||
|
tableRef: React.MutableRefObject<ActionType | undefined>;
|
||||||
|
}> = ({ email, tags, tableRef }) => {
|
||||||
|
const actionRef = useRef<ActionType>();
|
||||||
return (
|
return (
|
||||||
<ModalForm
|
<ModalForm
|
||||||
title={`历史订单${email}`}
|
title={`历史订单${email}`}
|
||||||
trigger={<Button type="primary">历史订单</Button>}
|
trigger={<Button type="primary">历史订单</Button>}
|
||||||
modalProps={{ destroyOnClose: true, footer: null }}
|
modalProps={{ destroyOnHidden: true, footer: null }}
|
||||||
width="80vw"
|
width="80vw"
|
||||||
|
submitter={false}
|
||||||
>
|
>
|
||||||
<ProTable
|
<ProTable
|
||||||
search={false}
|
search={false}
|
||||||
|
|
@ -869,6 +953,34 @@ const HistoryOrder: React.FC<{
|
||||||
);
|
);
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
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,
|
||||||
|
tag,
|
||||||
|
});
|
||||||
|
return false;
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
{tag}
|
||||||
|
</Tag>
|
||||||
|
);
|
||||||
|
})}
|
||||||
|
</Space>
|
||||||
|
);
|
||||||
|
},
|
||||||
|
},
|
||||||
]}
|
]}
|
||||||
request={async () => {
|
request={async () => {
|
||||||
const { data, success } = await statisticscontrollerGetorderbyemail({
|
const { data, success } = await statisticscontrollerGetorderbyemail({
|
||||||
|
|
@ -879,6 +991,9 @@ const HistoryOrder: React.FC<{
|
||||||
success,
|
success,
|
||||||
};
|
};
|
||||||
}}
|
}}
|
||||||
|
toolBarRender={() => [
|
||||||
|
<AddTag email={email} tags={tags} tableRef={actionRef} />,
|
||||||
|
]}
|
||||||
/>
|
/>
|
||||||
</ModalForm>
|
</ModalForm>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,257 @@
|
||||||
|
import React, { useEffect, useState, useMemo, useRef } from "react"
|
||||||
|
|
||||||
|
import {
|
||||||
|
ActionType,
|
||||||
|
PageContainer, ProColumns, ProTable,
|
||||||
|
} from '@ant-design/pro-components';
|
||||||
|
import { statisticscontrollerGetordersorce, statisticscontrollerGetinativeusersbymonth } from "@/servers/api/statistics";
|
||||||
|
import ReactECharts from 'echarts-for-react';
|
||||||
|
import { App, Button, Space, Tag } from 'antd';
|
||||||
|
import { HistoryOrder } from "../Order";
|
||||||
|
import dayjs from 'dayjs';
|
||||||
|
const ListPage: React.FC = () => {
|
||||||
|
|
||||||
|
const [data, setData] = useState({});
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
statisticscontrollerGetordersorce().then(({ data, success }) => {
|
||||||
|
if(success) setData(data)
|
||||||
|
});
|
||||||
|
}, []);
|
||||||
|
|
||||||
|
const option = useMemo(() => {
|
||||||
|
if(!data.inactiveRes) return {}
|
||||||
|
const xAxisData = data?.inactiveRes?.map(v=> v.order_month)?.sort(_=>-1)
|
||||||
|
const arr = data?.res?.map(v=>v.first_order_month_group)
|
||||||
|
const uniqueArr = arr.filter((item, index) => arr.indexOf(item) === index)
|
||||||
|
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: '用户名',
|
||||||
|
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;
|
||||||
|
|
@ -7,11 +7,15 @@ import {
|
||||||
ProFormSwitch,
|
ProFormSwitch,
|
||||||
ProTable,
|
ProTable,
|
||||||
} from '@ant-design/pro-components';
|
} from '@ant-design/pro-components';
|
||||||
|
import { Button } from 'antd';
|
||||||
import dayjs from 'dayjs';
|
import dayjs from 'dayjs';
|
||||||
|
import { saveAs } from 'file-saver';
|
||||||
import { useRef, useState } from 'react';
|
import { useRef, useState } from 'react';
|
||||||
|
import * as XLSX from 'xlsx';
|
||||||
|
|
||||||
const ListPage: React.FC = () => {
|
const ListPage: React.FC = () => {
|
||||||
const actionRef = useRef<ActionType>();
|
const actionRef = useRef<ActionType>();
|
||||||
|
const formRef = useRef();
|
||||||
const [total, setTotal] = useState(0);
|
const [total, setTotal] = useState(0);
|
||||||
const [isSource, setIsSource] = useState(false);
|
const [isSource, setIsSource] = useState(false);
|
||||||
const [yooneTotal, setYooneTotal] = useState({});
|
const [yooneTotal, setYooneTotal] = useState({});
|
||||||
|
|
@ -106,6 +110,7 @@ const ListPage: React.FC = () => {
|
||||||
<ProTable
|
<ProTable
|
||||||
headerTitle="查询表格"
|
headerTitle="查询表格"
|
||||||
actionRef={actionRef}
|
actionRef={actionRef}
|
||||||
|
formRef={formRef}
|
||||||
rowKey="id"
|
rowKey="id"
|
||||||
params={{ isSource }}
|
params={{ isSource }}
|
||||||
form={{
|
form={{
|
||||||
|
|
@ -145,6 +150,43 @@ const ListPage: React.FC = () => {
|
||||||
dateFormatter="number"
|
dateFormatter="number"
|
||||||
footer={() => `总计: ${total}`}
|
footer={() => `总计: ${total}`}
|
||||||
toolBarRender={() => [
|
toolBarRender={() => [
|
||||||
|
<Button
|
||||||
|
type="primary"
|
||||||
|
onClick={async () => {
|
||||||
|
const { dateRange, param } = formRef.current?.getFieldsValue();
|
||||||
|
const [startDate, endDate] = dateRange.values();
|
||||||
|
const { data, success } = await ordercontrollerGetordersales({
|
||||||
|
startDate: dayjs(startDate).valueOf(),
|
||||||
|
endDate: dayjs(endDate).valueOf(),
|
||||||
|
...param,
|
||||||
|
current: 1,
|
||||||
|
pageSize: 20000,
|
||||||
|
});
|
||||||
|
if (!success) return;
|
||||||
|
// 表头
|
||||||
|
const headers = ['产品名', '数量'];
|
||||||
|
|
||||||
|
// 数据行
|
||||||
|
const rows = (data?.items || []).map((item) => {
|
||||||
|
return [item.name, item.totalQuantity];
|
||||||
|
});
|
||||||
|
|
||||||
|
// 导出
|
||||||
|
const sheet = XLSX.utils.aoa_to_sheet([headers, ...rows]);
|
||||||
|
const book = XLSX.utils.book_new();
|
||||||
|
XLSX.utils.book_append_sheet(book, sheet, '销售');
|
||||||
|
const buffer = XLSX.write(book, {
|
||||||
|
bookType: 'xlsx',
|
||||||
|
type: 'array',
|
||||||
|
});
|
||||||
|
const blob = new Blob([buffer], {
|
||||||
|
type: 'application/octet-stream',
|
||||||
|
});
|
||||||
|
saveAs(blob, '销售.xlsx');
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
导出
|
||||||
|
</Button>,
|
||||||
<ProFormSwitch
|
<ProFormSwitch
|
||||||
label="原产品"
|
label="原产品"
|
||||||
fieldProps={{
|
fieldProps={{
|
||||||
|
|
|
||||||
|
|
@ -8,8 +8,10 @@ import {
|
||||||
ProColumns,
|
ProColumns,
|
||||||
ProTable,
|
ProTable,
|
||||||
} from '@ant-design/pro-components';
|
} from '@ant-design/pro-components';
|
||||||
import { App } from 'antd';
|
import { App, Button } from 'antd';
|
||||||
|
import { saveAs } from 'file-saver';
|
||||||
import { useEffect, useRef, useState } from 'react';
|
import { useEffect, useRef, useState } from 'react';
|
||||||
|
import * as XLSX from 'xlsx';
|
||||||
|
|
||||||
const ListPage: React.FC = () => {
|
const ListPage: React.FC = () => {
|
||||||
const { message } = App.useApp();
|
const { message } = App.useApp();
|
||||||
|
|
@ -25,6 +27,11 @@ const ListPage: React.FC = () => {
|
||||||
title: '产品名称',
|
title: '产品名称',
|
||||||
dataIndex: 'productName',
|
dataIndex: 'productName',
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
title: '中文名',
|
||||||
|
dataIndex: 'productNameCn',
|
||||||
|
hideInSearch: true,
|
||||||
|
},
|
||||||
{
|
{
|
||||||
title: 'SKU',
|
title: 'SKU',
|
||||||
dataIndex: 'productSku',
|
dataIndex: 'productSku',
|
||||||
|
|
@ -76,7 +83,44 @@ const ListPage: React.FC = () => {
|
||||||
};
|
};
|
||||||
}}
|
}}
|
||||||
columns={columns}
|
columns={columns}
|
||||||
// toolBarRender={() => [<CreateForm tableRef={actionRef} />]}
|
toolBarRender={() => [
|
||||||
|
<Button
|
||||||
|
type="primary"
|
||||||
|
onClick={async () => {
|
||||||
|
const { data, success } = await stockcontrollerGetstocks({
|
||||||
|
current: 1,
|
||||||
|
pageSize: 20000,
|
||||||
|
});
|
||||||
|
if (!success) return;
|
||||||
|
// 表头
|
||||||
|
const headers = ['产品名', 'SKU', ...points.map((p) => p.name)];
|
||||||
|
|
||||||
|
// 数据行
|
||||||
|
const rows = (data?.items || []).map((item) => {
|
||||||
|
const stockMap = new Map(
|
||||||
|
item.stockPoint.map((sp) => [sp.id, sp.quantity]),
|
||||||
|
);
|
||||||
|
const stockRow = points.map((p) => stockMap.get(p.id) || 0);
|
||||||
|
return [item.productName, item.productSku, ...stockRow];
|
||||||
|
});
|
||||||
|
|
||||||
|
// 导出
|
||||||
|
const sheet = XLSX.utils.aoa_to_sheet([headers, ...rows]);
|
||||||
|
const book = XLSX.utils.book_new();
|
||||||
|
XLSX.utils.book_append_sheet(book, sheet, '库存');
|
||||||
|
const buffer = XLSX.write(book, {
|
||||||
|
bookType: 'xlsx',
|
||||||
|
type: 'array',
|
||||||
|
});
|
||||||
|
const blob = new Blob([buffer], {
|
||||||
|
type: 'application/octet-stream',
|
||||||
|
});
|
||||||
|
saveAs(blob, '库存.xlsx');
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
导出
|
||||||
|
</Button>,
|
||||||
|
]}
|
||||||
/>
|
/>
|
||||||
</PageContainer>
|
</PageContainer>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -57,6 +57,7 @@ const PurchaseOrderPage: React.FC = () => {
|
||||||
title: '数量',
|
title: '数量',
|
||||||
hideInSearch: true,
|
hideInSearch: true,
|
||||||
render(_, record) {
|
render(_, record) {
|
||||||
|
if (!record.items) return 0;
|
||||||
return record.items.reduce((cur, next) => {
|
return record.items.reduce((cur, next) => {
|
||||||
return cur + next.quantity;
|
return cur + next.quantity;
|
||||||
}, 0);
|
}, 0);
|
||||||
|
|
@ -187,7 +188,7 @@ const CreateForm: React.FC<{
|
||||||
autoFocusFirstInput
|
autoFocusFirstInput
|
||||||
layout="vertical"
|
layout="vertical"
|
||||||
drawerProps={{
|
drawerProps={{
|
||||||
destroyOnClose: true,
|
destroyOnHidden: true,
|
||||||
}}
|
}}
|
||||||
onFinish={async (values) => {
|
onFinish={async (values) => {
|
||||||
try {
|
try {
|
||||||
|
|
@ -267,7 +268,7 @@ const CreateForm: React.FC<{
|
||||||
<div key={idx}>
|
<div key={idx}>
|
||||||
<ProFormSelect
|
<ProFormSelect
|
||||||
request={async ({ keyWords }) => {
|
request={async ({ keyWords }) => {
|
||||||
if (keyWords.length < 3) return [];
|
if (keyWords.length < 2) return [];
|
||||||
try {
|
try {
|
||||||
const { data } = await productcontrollerSearchproducts({
|
const { data } = await productcontrollerSearchproducts({
|
||||||
name: keyWords,
|
name: keyWords,
|
||||||
|
|
@ -275,7 +276,7 @@ const CreateForm: React.FC<{
|
||||||
return (
|
return (
|
||||||
data?.map((item) => {
|
data?.map((item) => {
|
||||||
return {
|
return {
|
||||||
label: item.name,
|
label: `${item.name} - ${item.nameCn}`,
|
||||||
value: item.sku,
|
value: item.sku,
|
||||||
};
|
};
|
||||||
}) || []
|
}) || []
|
||||||
|
|
@ -365,7 +366,7 @@ const UpdateForm: React.FC<{
|
||||||
}
|
}
|
||||||
autoFocusFirstInput
|
autoFocusFirstInput
|
||||||
drawerProps={{
|
drawerProps={{
|
||||||
destroyOnClose: true,
|
destroyOnHidden: true,
|
||||||
}}
|
}}
|
||||||
onFinish={async (values) => {
|
onFinish={async (values) => {
|
||||||
try {
|
try {
|
||||||
|
|
@ -450,7 +451,7 @@ const UpdateForm: React.FC<{
|
||||||
<div key={idx}>
|
<div key={idx}>
|
||||||
<ProFormSelect
|
<ProFormSelect
|
||||||
request={async ({ keyWords }) => {
|
request={async ({ keyWords }) => {
|
||||||
if (keyWords.length < 3) return [];
|
if (keyWords.length < 2) return [];
|
||||||
try {
|
try {
|
||||||
const { data } = await productcontrollerSearchproducts({
|
const { data } = await productcontrollerSearchproducts({
|
||||||
name: keyWords,
|
name: keyWords,
|
||||||
|
|
@ -458,7 +459,7 @@ const UpdateForm: React.FC<{
|
||||||
return (
|
return (
|
||||||
data?.map((item) => {
|
data?.map((item) => {
|
||||||
return {
|
return {
|
||||||
label: item.name,
|
label: `${item.name} - ${item.nameCn}`,
|
||||||
value: item.sku,
|
value: item.sku,
|
||||||
};
|
};
|
||||||
}) || []
|
}) || []
|
||||||
|
|
@ -544,7 +545,7 @@ const DetailForm: React.FC<{
|
||||||
trigger={<Button type="primary">详情</Button>}
|
trigger={<Button type="primary">详情</Button>}
|
||||||
autoFocusFirstInput
|
autoFocusFirstInput
|
||||||
drawerProps={{
|
drawerProps={{
|
||||||
destroyOnClose: true,
|
destroyOnHidden: true,
|
||||||
}}
|
}}
|
||||||
readonly={true}
|
readonly={true}
|
||||||
layout="vertical"
|
layout="vertical"
|
||||||
|
|
@ -614,7 +615,7 @@ const DetailForm: React.FC<{
|
||||||
<ProForm.Group>
|
<ProForm.Group>
|
||||||
<ProFormSelect
|
<ProFormSelect
|
||||||
request={async ({ keyWords }) => {
|
request={async ({ keyWords }) => {
|
||||||
if (keyWords.length < 3) return [];
|
if (keyWords.length < 2) return [];
|
||||||
try {
|
try {
|
||||||
const { data } = await productcontrollerSearchproducts({
|
const { data } = await productcontrollerSearchproducts({
|
||||||
name: keyWords,
|
name: keyWords,
|
||||||
|
|
@ -622,7 +623,7 @@ const DetailForm: React.FC<{
|
||||||
return (
|
return (
|
||||||
data?.map((item) => {
|
data?.map((item) => {
|
||||||
return {
|
return {
|
||||||
label: item.name,
|
label: `${item.name} - ${item.nameCn}`,
|
||||||
value: item.sku,
|
value: item.sku,
|
||||||
};
|
};
|
||||||
}) || []
|
}) || []
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ import {
|
||||||
stockcontrollerCanceltransfer,
|
stockcontrollerCanceltransfer,
|
||||||
stockcontrollerCreatetransfer,
|
stockcontrollerCreatetransfer,
|
||||||
stockcontrollerGetallstockpoints,
|
stockcontrollerGetallstockpoints,
|
||||||
|
stockcontrollerGetpurchaseorder,
|
||||||
stockcontrollerGettransfers,
|
stockcontrollerGettransfers,
|
||||||
stockcontrollerLosttransfer,
|
stockcontrollerLosttransfer,
|
||||||
stockcontrollerReceivetransfer,
|
stockcontrollerReceivetransfer,
|
||||||
|
|
@ -60,7 +61,7 @@ const TransferPage: React.FC = () => {
|
||||||
title: '数量',
|
title: '数量',
|
||||||
hideInSearch: true,
|
hideInSearch: true,
|
||||||
render(_, record) {
|
render(_, record) {
|
||||||
return record.items.reduce((cur, next) => {
|
return record?.items?.reduce?.((cur, next) => {
|
||||||
return cur + next.quantity;
|
return cur + next.quantity;
|
||||||
}, 0);
|
}, 0);
|
||||||
},
|
},
|
||||||
|
|
@ -204,11 +205,10 @@ const CreateForm: React.FC<{
|
||||||
autoFocusFirstInput
|
autoFocusFirstInput
|
||||||
layout="vertical"
|
layout="vertical"
|
||||||
drawerProps={{
|
drawerProps={{
|
||||||
destroyOnClose: true,
|
destroyOnHidden: true,
|
||||||
}}
|
}}
|
||||||
onFinish={async (values) => {
|
onFinish={async ({orderNumber,...values}) => {
|
||||||
try {
|
try {
|
||||||
console.log(values);
|
|
||||||
const { success, message: errMsg } =
|
const { success, message: errMsg } =
|
||||||
await stockcontrollerCreatetransfer(values);
|
await stockcontrollerCreatetransfer(values);
|
||||||
if (!success) {
|
if (!success) {
|
||||||
|
|
@ -272,9 +272,24 @@ 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 () => {
|
||||||
|
const orderNumber = await form.getFieldValue('orderNumber')
|
||||||
|
const { data } = await stockcontrollerGetpurchaseorder({orderNumber})
|
||||||
|
form.setFieldsValue({
|
||||||
|
items: data?.map(
|
||||||
|
(item: { productName: string; productSku: string }) => ({
|
||||||
|
...item,
|
||||||
|
productSku: {
|
||||||
|
label: item.productName,
|
||||||
|
value: item.productSku,
|
||||||
|
},
|
||||||
|
}),
|
||||||
|
),
|
||||||
|
})
|
||||||
|
}}>引用</Button>} />
|
||||||
<ProFormDependency name={['items']}>
|
<ProFormDependency name={['items']}>
|
||||||
{({ items }) => {
|
{({ items }) => {
|
||||||
return '数量:' + items?.reduce((acc, cur) => acc + cur.quantity, 0);
|
return '数量:' + (items?.reduce?.((acc, cur) => acc + cur.quantity, 0)||0);
|
||||||
}}
|
}}
|
||||||
</ProFormDependency>
|
</ProFormDependency>
|
||||||
<ProFormList
|
<ProFormList
|
||||||
|
|
@ -297,7 +312,7 @@ const CreateForm: React.FC<{
|
||||||
<div key={idx}>
|
<div key={idx}>
|
||||||
<ProFormSelect
|
<ProFormSelect
|
||||||
request={async ({ keyWords }) => {
|
request={async ({ keyWords }) => {
|
||||||
if (keyWords.length < 3) return [];
|
if (keyWords.length < 2) return [];
|
||||||
try {
|
try {
|
||||||
const { data } = await productcontrollerSearchproducts({
|
const { data } = await productcontrollerSearchproducts({
|
||||||
name: keyWords,
|
name: keyWords,
|
||||||
|
|
@ -305,7 +320,7 @@ const CreateForm: React.FC<{
|
||||||
return (
|
return (
|
||||||
data?.map((item) => {
|
data?.map((item) => {
|
||||||
return {
|
return {
|
||||||
label: item.name,
|
label: `${item.name} - ${item.nameCn}`,
|
||||||
value: item.sku,
|
value: item.sku,
|
||||||
};
|
};
|
||||||
}) || []
|
}) || []
|
||||||
|
|
@ -382,7 +397,7 @@ const UpdateForm: React.FC<{
|
||||||
}
|
}
|
||||||
autoFocusFirstInput
|
autoFocusFirstInput
|
||||||
drawerProps={{
|
drawerProps={{
|
||||||
destroyOnClose: true,
|
destroyOnHidden: true,
|
||||||
}}
|
}}
|
||||||
onFinish={async (values) => {
|
onFinish={async (values) => {
|
||||||
try {
|
try {
|
||||||
|
|
@ -450,7 +465,7 @@ const UpdateForm: React.FC<{
|
||||||
<ProFormTextArea name="note" label="备注" />
|
<ProFormTextArea name="note" label="备注" />
|
||||||
<ProFormDependency name={['items']}>
|
<ProFormDependency name={['items']}>
|
||||||
{({ items }) => {
|
{({ items }) => {
|
||||||
return '数量:' + items?.reduce((acc, cur) => acc + cur.quantity, 0);
|
return '数量:' + items?.reduce?.((acc, cur) => acc + cur.quantity, 0);
|
||||||
}}
|
}}
|
||||||
</ProFormDependency>
|
</ProFormDependency>
|
||||||
<ProFormList
|
<ProFormList
|
||||||
|
|
@ -473,7 +488,7 @@ const UpdateForm: React.FC<{
|
||||||
<div key={idx}>
|
<div key={idx}>
|
||||||
<ProFormSelect
|
<ProFormSelect
|
||||||
request={async ({ keyWords }) => {
|
request={async ({ keyWords }) => {
|
||||||
if (keyWords.length < 3) return [];
|
if (keyWords.length < 2) return [];
|
||||||
try {
|
try {
|
||||||
const { data } = await productcontrollerSearchproducts({
|
const { data } = await productcontrollerSearchproducts({
|
||||||
name: keyWords,
|
name: keyWords,
|
||||||
|
|
@ -481,7 +496,7 @@ const UpdateForm: React.FC<{
|
||||||
return (
|
return (
|
||||||
data?.map((item) => {
|
data?.map((item) => {
|
||||||
return {
|
return {
|
||||||
label: item.name,
|
label: `${item.name} - ${item.nameCn}`,
|
||||||
value: item.sku,
|
value: item.sku,
|
||||||
};
|
};
|
||||||
}) || []
|
}) || []
|
||||||
|
|
@ -553,7 +568,7 @@ const DetailForm: React.FC<{
|
||||||
trigger={<Button type="primary">详情</Button>}
|
trigger={<Button type="primary">详情</Button>}
|
||||||
autoFocusFirstInput
|
autoFocusFirstInput
|
||||||
drawerProps={{
|
drawerProps={{
|
||||||
destroyOnClose: true,
|
destroyOnHidden: true,
|
||||||
}}
|
}}
|
||||||
readonly={true}
|
readonly={true}
|
||||||
layout="vertical"
|
layout="vertical"
|
||||||
|
|
@ -630,7 +645,7 @@ const DetailForm: React.FC<{
|
||||||
<ProForm.Group>
|
<ProForm.Group>
|
||||||
<ProFormSelect
|
<ProFormSelect
|
||||||
request={async ({ keyWords }) => {
|
request={async ({ keyWords }) => {
|
||||||
if (keyWords.length < 3) return [];
|
if (keyWords.length < 2) return [];
|
||||||
try {
|
try {
|
||||||
const { data } = await productcontrollerSearchproducts({
|
const { data } = await productcontrollerSearchproducts({
|
||||||
name: keyWords,
|
name: keyWords,
|
||||||
|
|
@ -638,7 +653,7 @@ const DetailForm: React.FC<{
|
||||||
return (
|
return (
|
||||||
data?.map((item) => {
|
data?.map((item) => {
|
||||||
return {
|
return {
|
||||||
label: item.name,
|
label: `${item.name} - ${item.nameCn}`,
|
||||||
value: item.sku,
|
value: item.sku,
|
||||||
};
|
};
|
||||||
}) || []
|
}) || []
|
||||||
|
|
|
||||||
|
|
@ -114,7 +114,7 @@ const CreateForm: React.FC<{
|
||||||
}
|
}
|
||||||
autoFocusFirstInput
|
autoFocusFirstInput
|
||||||
drawerProps={{
|
drawerProps={{
|
||||||
destroyOnClose: true,
|
destroyOnHidden: true,
|
||||||
}}
|
}}
|
||||||
onFinish={async (values) => {
|
onFinish={async (values) => {
|
||||||
try {
|
try {
|
||||||
|
|
@ -182,7 +182,7 @@ const UpdateForm: React.FC<{
|
||||||
}
|
}
|
||||||
autoFocusFirstInput
|
autoFocusFirstInput
|
||||||
drawerProps={{
|
drawerProps={{
|
||||||
destroyOnClose: true,
|
destroyOnHidden: true,
|
||||||
}}
|
}}
|
||||||
onFinish={async (values) => {
|
onFinish={async (values) => {
|
||||||
try {
|
try {
|
||||||
|
|
|
||||||
|
|
@ -4,7 +4,6 @@ import {
|
||||||
} 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';
|
||||||
import { Col, Row } from 'antd';
|
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
|
|
||||||
const TrackPage: React.FC = () => {
|
const TrackPage: React.FC = () => {
|
||||||
|
|
@ -19,21 +18,15 @@ const TrackPage: React.FC = () => {
|
||||||
if (!keyWords || keyWords.length < 3) return [];
|
if (!keyWords || keyWords.length < 3) return [];
|
||||||
const { data: trackList } =
|
const { data: trackList } =
|
||||||
await logisticscontrollerGettrackingnumber({ number: keyWords });
|
await logisticscontrollerGettrackingnumber({ number: keyWords });
|
||||||
return trackList?.map(
|
return trackList?.map((v) => {
|
||||||
(v: {
|
return {
|
||||||
tracking_provider: string;
|
label: v.siteName + ' ' + v.externalOrderId,
|
||||||
primary_tracking_number: string;
|
value: v.id,
|
||||||
id: string;
|
};
|
||||||
}) => {
|
});
|
||||||
return {
|
|
||||||
label: v.tracking_provider + ' ' + v.primary_tracking_number,
|
|
||||||
value: v.id,
|
|
||||||
};
|
|
||||||
},
|
|
||||||
);
|
|
||||||
}}
|
}}
|
||||||
fieldProps={{
|
fieldProps={{
|
||||||
prefix: '追踪号',
|
prefix: '订单号',
|
||||||
onChange(value: string) {
|
onChange(value: string) {
|
||||||
setId(value);
|
setId(value);
|
||||||
},
|
},
|
||||||
|
|
@ -53,28 +46,22 @@ const TrackPage: React.FC = () => {
|
||||||
),
|
),
|
||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<Row>
|
<div>
|
||||||
<Col span={24}>原订单</Col>
|
{data.map((item) => (
|
||||||
<Col span={18}>产品</Col>
|
<div>
|
||||||
<Col span={6}>数量</Col>
|
<h4>
|
||||||
{data?.orderItem?.map((v: any) => (
|
{item.name} * {item.quantity}
|
||||||
<>
|
</h4>
|
||||||
<Col span={18}>{v.name}</Col>
|
<div style={{ paddingLeft: 20, color: 'blue' }}>
|
||||||
<Col span={6}>{v.quantity}</Col>
|
{item.constitution.map((v) => (
|
||||||
</>
|
<div>
|
||||||
|
{v.name} * {v.quantity * item.quantity}
|
||||||
|
</div>
|
||||||
|
))}
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
))}
|
))}
|
||||||
</Row>
|
</div>
|
||||||
|
|
||||||
<Row style={{ marginTop: '30px' }}>
|
|
||||||
<Col span={18}>产品</Col>
|
|
||||||
<Col span={6}>数量</Col>
|
|
||||||
{data?.shipmentItem?.map((v: any) => (
|
|
||||||
<>
|
|
||||||
<Col span={18}>{v.name}</Col>
|
|
||||||
<Col span={6}>{v.quantity}</Col>
|
|
||||||
</>
|
|
||||||
))}
|
|
||||||
</Row>
|
|
||||||
</PageContainer>
|
</PageContainer>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,58 @@
|
||||||
|
// @ts-ignore
|
||||||
|
/* eslint-disable */
|
||||||
|
import { request } from 'umi';
|
||||||
|
|
||||||
|
/** 此处后端没有提供注释 GET /customer/list */
|
||||||
|
export async function customercontrollerGetcustomerlist(
|
||||||
|
// 叠加生成的Param类型 (非body参数swagger默认没有生成对象)
|
||||||
|
params: API.customercontrollerGetcustomerlistParams,
|
||||||
|
options?: { [key: string]: any },
|
||||||
|
) {
|
||||||
|
return request<any>('/customer/list', {
|
||||||
|
method: 'GET',
|
||||||
|
params: {
|
||||||
|
...params,
|
||||||
|
},
|
||||||
|
...(options || {}),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 此处后端没有提供注释 POST /customer/tag/add */
|
||||||
|
export async function customercontrollerAddtag(
|
||||||
|
body: API.CustomerTagDTO,
|
||||||
|
options?: { [key: string]: any },
|
||||||
|
) {
|
||||||
|
return request<API.BooleanRes>('/customer/tag/add', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
data: body,
|
||||||
|
...(options || {}),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 此处后端没有提供注释 DELETE /customer/tag/del */
|
||||||
|
export async function customercontrollerDeltag(
|
||||||
|
body: API.CustomerTagDTO,
|
||||||
|
options?: { [key: string]: any },
|
||||||
|
) {
|
||||||
|
return request<API.BooleanRes>('/customer/tag/del', {
|
||||||
|
method: 'DELETE',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'application/json',
|
||||||
|
},
|
||||||
|
data: body,
|
||||||
|
...(options || {}),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 此处后端没有提供注释 GET /customer/tags */
|
||||||
|
export async function customercontrollerGettags(options?: {
|
||||||
|
[key: string]: any;
|
||||||
|
}) {
|
||||||
|
return request<any>('/customer/tags', {
|
||||||
|
method: 'GET',
|
||||||
|
...(options || {}),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
@ -2,6 +2,7 @@
|
||||||
/* eslint-disable */
|
/* eslint-disable */
|
||||||
// API 更新时间:
|
// API 更新时间:
|
||||||
// API 唯一标识:
|
// API 唯一标识:
|
||||||
|
import * as customer from './customer';
|
||||||
import * as logistics from './logistics';
|
import * as logistics from './logistics';
|
||||||
import * as order from './order';
|
import * as order from './order';
|
||||||
import * as product from './product';
|
import * as product from './product';
|
||||||
|
|
@ -12,6 +13,7 @@ import * as user from './user';
|
||||||
import * as webhook from './webhook';
|
import * as webhook from './webhook';
|
||||||
import * as wpProduct from './wpProduct';
|
import * as wpProduct from './wpProduct';
|
||||||
export default {
|
export default {
|
||||||
|
customer,
|
||||||
logistics,
|
logistics,
|
||||||
order,
|
order,
|
||||||
product,
|
product,
|
||||||
|
|
|
||||||
|
|
@ -133,6 +133,21 @@ export async function ordercontrollerCreateorder(
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 此处后端没有提供注释 POST /order/order/pengding/items */
|
||||||
|
export async function ordercontrollerPengdingitems(
|
||||||
|
body: Record<string, any>,
|
||||||
|
options?: { [key: string]: any },
|
||||||
|
) {
|
||||||
|
return request<any>('/order/order/pengding/items', {
|
||||||
|
method: 'POST',
|
||||||
|
headers: {
|
||||||
|
'Content-Type': 'text/plain',
|
||||||
|
},
|
||||||
|
data: body,
|
||||||
|
...(options || {}),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/** 此处后端没有提供注释 POST /order/order/refund/${param0} */
|
/** 此处后端没有提供注释 POST /order/order/refund/${param0} */
|
||||||
export async function ordercontrollerRefundorder(
|
export async function ordercontrollerRefundorder(
|
||||||
// 叠加生成的Param类型 (非body参数swagger默认没有生成对象)
|
// 叠加生成的Param类型 (非body参数swagger默认没有生成对象)
|
||||||
|
|
|
||||||
|
|
@ -327,3 +327,17 @@ export async function productcontrollerGetstrengthall(options?: {
|
||||||
...(options || {}),
|
...(options || {}),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 此处后端没有提供注释 PUT /productupdateNameCn/${param1}/${param0} */
|
||||||
|
export async function productcontrollerUpdateproductnamecn(
|
||||||
|
// 叠加生成的Param类型 (非body参数swagger默认没有生成对象)
|
||||||
|
params: API.productcontrollerUpdateproductnamecnParams,
|
||||||
|
options?: { [key: string]: any },
|
||||||
|
) {
|
||||||
|
const { nameCn: param0, id: param1, ...queryParams } = params;
|
||||||
|
return request<API.ProductRes>(`/productupdateNameCn/${param1}/${param0}`, {
|
||||||
|
method: 'PUT',
|
||||||
|
params: { ...queryParams },
|
||||||
|
...(options || {}),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
|
||||||
|
|
@ -17,6 +17,21 @@ export async function statisticscontrollerGetcustomerorders(
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 此处后端没有提供注释 GET /statistics/inactiveUsersByMonth */
|
||||||
|
export async function statisticscontrollerGetinativeusersbymonth(
|
||||||
|
// 叠加生成的Param类型 (非body参数swagger默认没有生成对象)
|
||||||
|
params: API.statisticscontrollerGetinativeusersbymonthParams,
|
||||||
|
options?: { [key: string]: any },
|
||||||
|
) {
|
||||||
|
return request<any>('/statistics/inactiveUsersByMonth', {
|
||||||
|
method: 'GET',
|
||||||
|
params: {
|
||||||
|
...params,
|
||||||
|
},
|
||||||
|
...(options || {}),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/** 此处后端没有提供注释 POST /statistics/order */
|
/** 此处后端没有提供注释 POST /statistics/order */
|
||||||
export async function statisticscontrollerGetorderstatistics(
|
export async function statisticscontrollerGetorderstatistics(
|
||||||
body: API.OrderStatisticsParams,
|
body: API.OrderStatisticsParams,
|
||||||
|
|
@ -62,6 +77,16 @@ export async function statisticscontrollerGetorderbyemail(
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 此处后端没有提供注释 GET /statistics/orderSource */
|
||||||
|
export async function statisticscontrollerGetordersorce(options?: {
|
||||||
|
[key: string]: any;
|
||||||
|
}) {
|
||||||
|
return request<any>('/statistics/orderSource', {
|
||||||
|
method: 'GET',
|
||||||
|
...(options || {}),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/** 此处后端没有提供注释 POST /statistics/restocking */
|
/** 此处后端没有提供注释 POST /statistics/restocking */
|
||||||
export async function statisticscontrollerRestocking(
|
export async function statisticscontrollerRestocking(
|
||||||
body: Record<string, any>,
|
body: Record<string, any>,
|
||||||
|
|
|
||||||
|
|
@ -122,6 +122,20 @@ export async function stockcontrollerDelpurchaseorder(
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 此处后端没有提供注释 GET /stock/purchase-order/${param0} */
|
||||||
|
export async function stockcontrollerGetpurchaseorder(
|
||||||
|
// 叠加生成的Param类型 (非body参数swagger默认没有生成对象)
|
||||||
|
params: API.stockcontrollerGetpurchaseorderParams,
|
||||||
|
options?: { [key: string]: any },
|
||||||
|
) {
|
||||||
|
const { orderNumber: param0, ...queryParams } = params;
|
||||||
|
return request<API.BooleanRes>(`/stock/purchase-order/${param0}`, {
|
||||||
|
method: 'GET',
|
||||||
|
params: { ...queryParams },
|
||||||
|
...(options || {}),
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
/** 此处后端没有提供注释 PUT /stock/receiveTransfer/${param0} */
|
/** 此处后端没有提供注释 PUT /stock/receiveTransfer/${param0} */
|
||||||
export async function stockcontrollerUpdatetransfer(
|
export async function stockcontrollerUpdatetransfer(
|
||||||
// 叠加生成的Param类型 (非body参数swagger默认没有生成对象)
|
// 叠加生成的Param类型 (非body参数swagger默认没有生成对象)
|
||||||
|
|
|
||||||
|
|
@ -67,10 +67,11 @@ declare namespace API {
|
||||||
name: string;
|
name: string;
|
||||||
/** 产品描述 */
|
/** 产品描述 */
|
||||||
description?: string;
|
description?: string;
|
||||||
/** sku */
|
|
||||||
sku?: string;
|
|
||||||
/** 分类 ID */
|
/** 分类 ID */
|
||||||
categoryId?: number;
|
categoryId?: number;
|
||||||
|
strengthId?: number;
|
||||||
|
flavorsId?: number;
|
||||||
|
humidity?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
type CreatePurchaseOrderDTO = {
|
type CreatePurchaseOrderDTO = {
|
||||||
|
|
@ -100,6 +101,22 @@ declare namespace API {
|
||||||
unit?: string;
|
unit?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
type customercontrollerGetcustomerlistParams = {
|
||||||
|
current?: string;
|
||||||
|
pageSize?: string;
|
||||||
|
email?: string;
|
||||||
|
tags?: string;
|
||||||
|
sorterKey?: string;
|
||||||
|
sorterValue?: string;
|
||||||
|
state?: string;
|
||||||
|
first_purchase_date?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
type CustomerTagDTO = {
|
||||||
|
email?: string;
|
||||||
|
tag?: string;
|
||||||
|
};
|
||||||
|
|
||||||
type Date = {
|
type Date = {
|
||||||
year?: string;
|
year?: string;
|
||||||
month?: string;
|
month?: string;
|
||||||
|
|
@ -552,12 +569,14 @@ declare namespace API {
|
||||||
id: number;
|
id: number;
|
||||||
/** 产品名称 */
|
/** 产品名称 */
|
||||||
name: string;
|
name: string;
|
||||||
|
nameCn?: string;
|
||||||
/** 产品描述 */
|
/** 产品描述 */
|
||||||
description?: string;
|
description?: string;
|
||||||
/** 分类 ID */
|
/** 分类 ID */
|
||||||
categoryId?: number;
|
categoryId?: number;
|
||||||
flavorsId?: number;
|
flavorsId?: number;
|
||||||
strengthId?: number;
|
strengthId?: number;
|
||||||
|
humidity?: string;
|
||||||
/** sku */
|
/** sku */
|
||||||
sku?: string;
|
sku?: string;
|
||||||
/** 创建时间 */
|
/** 创建时间 */
|
||||||
|
|
@ -658,6 +677,11 @@ declare namespace API {
|
||||||
id: number;
|
id: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
type productcontrollerUpdateproductnamecnParams = {
|
||||||
|
nameCn: string;
|
||||||
|
id: number;
|
||||||
|
};
|
||||||
|
|
||||||
type productcontrollerUpdateproductParams = {
|
type productcontrollerUpdateproductParams = {
|
||||||
id: number;
|
id: number;
|
||||||
};
|
};
|
||||||
|
|
@ -765,6 +789,17 @@ declare namespace API {
|
||||||
name?: string;
|
name?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
type QueryCustomerListDTO = {
|
||||||
|
current?: string;
|
||||||
|
pageSize?: string;
|
||||||
|
email?: string;
|
||||||
|
tags?: string;
|
||||||
|
sorterKey?: string;
|
||||||
|
sorterValue?: string;
|
||||||
|
state?: string;
|
||||||
|
first_purchase_date?: string;
|
||||||
|
};
|
||||||
|
|
||||||
type QueryFlavorsDTO = {
|
type QueryFlavorsDTO = {
|
||||||
/** 页码 */
|
/** 页码 */
|
||||||
current?: number;
|
current?: number;
|
||||||
|
|
@ -1027,6 +1062,10 @@ declare namespace API {
|
||||||
sku?: string;
|
sku?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
type statisticscontrollerGetinativeusersbymonthParams = {
|
||||||
|
month?: string;
|
||||||
|
};
|
||||||
|
|
||||||
type stockcontrollerCanceltransferParams = {
|
type stockcontrollerCanceltransferParams = {
|
||||||
id: number;
|
id: number;
|
||||||
};
|
};
|
||||||
|
|
@ -1039,6 +1078,10 @@ declare namespace API {
|
||||||
id: number;
|
id: number;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
type stockcontrollerGetpurchaseorderParams = {
|
||||||
|
orderNumber: string;
|
||||||
|
};
|
||||||
|
|
||||||
type stockcontrollerGetpurchaseordersParams = {
|
type stockcontrollerGetpurchaseordersParams = {
|
||||||
/** 页码 */
|
/** 页码 */
|
||||||
current?: number;
|
current?: number;
|
||||||
|
|
@ -1261,10 +1304,11 @@ declare namespace API {
|
||||||
name?: string;
|
name?: string;
|
||||||
/** 产品描述 */
|
/** 产品描述 */
|
||||||
description?: string;
|
description?: string;
|
||||||
/** sku */
|
|
||||||
sku?: string;
|
|
||||||
/** 分类 ID */
|
/** 分类 ID */
|
||||||
categoryId?: number;
|
categoryId?: number;
|
||||||
|
strengthId?: number;
|
||||||
|
flavorsId?: number;
|
||||||
|
humidity?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
type UpdatePurchaseOrderDTO = {
|
type UpdatePurchaseOrderDTO = {
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue