fix: 少量关于产品和订单的修改 #47
|
|
@ -134,9 +134,14 @@ export default defineConfig({
|
|||
},
|
||||
{
|
||||
name: '数据分析列表',
|
||||
path: '/customer/statistic',
|
||||
component: './Customer/Statistic',
|
||||
path: '/customer/statistic/list',
|
||||
component: './Customer/StatisticList',
|
||||
},
|
||||
// {
|
||||
// name: '客户统计',
|
||||
// path: '/customer/statistic/home',
|
||||
// component: './Customer/Statistic',
|
||||
// }
|
||||
],
|
||||
},
|
||||
{
|
||||
|
|
|
|||
|
|
@ -16,6 +16,8 @@
|
|||
"@ant-design/charts": "^2.2.6",
|
||||
"@ant-design/icons": "^5.0.1",
|
||||
"@ant-design/pro-components": "^2.4.4",
|
||||
"@cubejs-client/core": "^1.6.4",
|
||||
"@cubejs-client/react": "^1.6.4",
|
||||
"@fingerprintjs/fingerprintjs": "^4.6.2",
|
||||
"@monaco-editor/react": "^4.7.0",
|
||||
"@tinymce/tinymce-react": "^6.3.0",
|
||||
|
|
|
|||
|
|
@ -1,289 +0,0 @@
|
|||
import { HistoryOrder } from '@/pages/Statistics/Order';
|
||||
import {
|
||||
customercontrollerAddtag,
|
||||
customercontrollerDeltag,
|
||||
customercontrollerGetcustomerlist,
|
||||
customercontrollerGettags,
|
||||
customercontrollerSetrate,
|
||||
} from '@/servers/api/customer';
|
||||
import {
|
||||
ActionType,
|
||||
ModalForm,
|
||||
PageContainer,
|
||||
ProColumns,
|
||||
ProFormSelect,
|
||||
ProTable,
|
||||
} from '@ant-design/pro-components';
|
||||
import { App, Button, Rate, Space, Tag } from 'antd';
|
||||
import dayjs from 'dayjs';
|
||||
import { useRef, useState } from 'react';
|
||||
|
||||
const ListPage: React.FC = () => {
|
||||
const actionRef = useRef<ActionType>();
|
||||
const { message } = App.useApp();
|
||||
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: '等级',
|
||||
hideInSearch: true,
|
||||
render: (_, record) => {
|
||||
if (!record.yoone_orders || !record.yoone_total) return '-';
|
||||
if (Number(record.yoone_orders) === 1 && Number(record.yoone_total) > 0)
|
||||
return 'B';
|
||||
return '-';
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '评星',
|
||||
dataIndex: 'rate',
|
||||
width: 200,
|
||||
render: (_, record) => {
|
||||
return (
|
||||
<Rate
|
||||
onChange={async (val) => {
|
||||
try {
|
||||
const { success, message: msg } =
|
||||
await customercontrollerSetrate({
|
||||
id: record.customerId,
|
||||
rate: val,
|
||||
});
|
||||
if (success) {
|
||||
message.success(msg);
|
||||
actionRef.current?.reload();
|
||||
}
|
||||
} catch (e) {
|
||||
message.error(e.message);
|
||||
}
|
||||
}}
|
||||
value={record.rate}
|
||||
/>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '联系电话',
|
||||
dataIndex: 'phone',
|
||||
hideInSearch: true,
|
||||
render: (_, record) => record?.billing.phone || record?.shipping.phone,
|
||||
},
|
||||
{
|
||||
title: '账单地址',
|
||||
dataIndex: 'billing',
|
||||
render: (_, record) =>
|
||||
JSON.stringify(record?.billing || record?.shipping),
|
||||
},
|
||||
{
|
||||
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',
|
||||
fixed: 'right',
|
||||
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
|
||||
scroll={{ x: 'max-content' }}
|
||||
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;
|
||||
|
|
@ -0,0 +1,289 @@
|
|||
import { HistoryOrder } from '@/pages/Statistics/Order';
|
||||
import {
|
||||
customercontrollerAddtag,
|
||||
customercontrollerDeltag,
|
||||
customercontrollerGetcustomerlist,
|
||||
customercontrollerGettags,
|
||||
customercontrollerSetrate,
|
||||
} from '@/servers/api/customer';
|
||||
import {
|
||||
ActionType,
|
||||
ModalForm,
|
||||
PageContainer,
|
||||
ProColumns,
|
||||
ProFormSelect,
|
||||
ProTable,
|
||||
} from '@ant-design/pro-components';
|
||||
import { App, Button, Rate, Space, Tag } from 'antd';
|
||||
import dayjs from 'dayjs';
|
||||
import { useRef, useState } from 'react';
|
||||
|
||||
const ListPage: React.FC = () => {
|
||||
const actionRef = useRef<ActionType>();
|
||||
const { message } = App.useApp();
|
||||
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: '等级',
|
||||
hideInSearch: true,
|
||||
render: (_, record) => {
|
||||
if (!record.yoone_orders || !record.yoone_total) return '-';
|
||||
if (Number(record.yoone_orders) === 1 && Number(record.yoone_total) > 0)
|
||||
return 'B';
|
||||
return '-';
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '评星',
|
||||
dataIndex: 'rate',
|
||||
width: 200,
|
||||
render: (_, record) => {
|
||||
return (
|
||||
<Rate
|
||||
onChange={async (val) => {
|
||||
try {
|
||||
const { success, message: msg } =
|
||||
await customercontrollerSetrate({
|
||||
id: record.customerId,
|
||||
rate: val,
|
||||
});
|
||||
if (success) {
|
||||
message.success(msg);
|
||||
actionRef.current?.reload();
|
||||
}
|
||||
} catch (e) {
|
||||
message.error(e.message);
|
||||
}
|
||||
}}
|
||||
value={record.rate}
|
||||
/>
|
||||
);
|
||||
},
|
||||
},
|
||||
{
|
||||
title: '联系电话',
|
||||
dataIndex: 'phone',
|
||||
hideInSearch: true,
|
||||
render: (_, record) => record?.billing.phone || record?.shipping.phone,
|
||||
},
|
||||
{
|
||||
title: '账单地址',
|
||||
dataIndex: 'billing',
|
||||
render: (_, record) =>
|
||||
JSON.stringify(record?.billing || record?.shipping),
|
||||
},
|
||||
{
|
||||
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',
|
||||
fixed: 'right',
|
||||
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
|
||||
scroll={{ x: 'max-content' }}
|
||||
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;
|
||||
|
|
@ -95,7 +95,7 @@ const ShopLayout: React.FC = () => {
|
|||
<Row gutter={16} style={{ height: 'calc(100vh - 100px)' }}>
|
||||
<Col span={4} style={{ height: '100%' }}>
|
||||
<Sider
|
||||
style={{ background: 'white', height: '100%', overflow: 'hidden' }}
|
||||
style={{ background: 'white', height: '100%', overflow: 'hidden',zIndex:1 }}
|
||||
>
|
||||
<div style={{ padding: '0 10px 16px' }}>
|
||||
<div
|
||||
|
|
|
|||
Loading…
Reference in New Issue