数据看板模块初步完成

This commit is contained in:
vance 2022-07-28 16:53:51 +08:00
parent 53ca93a288
commit 43c2cdb98b
11 changed files with 834 additions and 0 deletions

View File

@ -0,0 +1,61 @@
import React, { useRef, useState } from 'react';
import Table, { ProColumns, ActionType } from '@/components/Table';
import { fetchTableData } from '@/utils/table';
const ActiveAnalysis: React.FC = () => {
const tableRef = useRef<ActionType>();
const [visible, setVisible] = useState(false);
const columns: ProColumns<any>[] = [
{
title: '日期',
dataIndex: 'time',
width: '10%',
hideInSearch: true,
},
{
title: '活跃用户数',
dataIndex: 'address',
hideInSearch: true,
width: '20%',
},
{
title: '活跃老玩家数量',
dataIndex: 'address',
hideInSearch: true,
width: '20%',
},
{
title: 'MAU',
dataIndex: 'address',
hideInSearch: true,
width: '20%',
},
{
title: 'WAU',
dataIndex: 'arpu',
hideInSearch: true,
width: '20%',
},
{
title: 'DAU',
dataIndex: 'arppu',
hideInSearch: true,
width: '20%',
},
];
return (
<div>
<Table
columns={columns}
rowKey="id"
actionRef={tableRef}
// request={async (params) => {
// // return fetchTableData(, params);
// }}
/>
</div>
);
};
export default ActiveAnalysis;

View File

@ -0,0 +1,174 @@
import React, { useRef, useState } from 'react';
import Table, { ProColumns, ActionType } from '@/components/Table';
import { getCoreData } from '@/services/DataBorad';
import { RedoOutlined } from '@ant-design/icons';
import moment from 'moment';
import { Card, Col, Row, DatePicker, Button } from 'antd';
// import TimeText from '@/components/Typography/TimeText';
const CoreData: React.FC = () => {
const tableRef = useRef<ActionType>();
const [startTimeValue, setStartTimeValue] = useState(Number);
const [endTimeValue, setEndTimeValue] = useState(Number);
const getTimedata = (data: any) => {
setStartTimeValue(moment(data[0]).valueOf());
setEndTimeValue(moment(data[1]).valueOf());
console.log(startTimeValue, endTimeValue);
};
const carddata = {
name: 1,
time: 2,
user_num: 2,
};
const [date, setDate] = useState(carddata);
const columns: ProColumns<any>[] = [
{
title: '日期',
dataIndex: 'time',
width: '10%',
renderFormItem: (item, { type, defaultRender, ...rest }, form) => {
return (
<DatePicker.RangePicker
style={{ width: '300px' }}
defaultValue={[moment(), moment()]}
format={'YYYY-MM-DD'}
onChange={getTimedata}
/>
);
},
},
{
title: '活跃用户数',
dataIndex: 'address',
hideInSearch: true,
width: '20%',
},
{
title: '新用户数量',
dataIndex: 'new_user',
width: 150,
hideInSearch: true,
},
{
title: '当日充值总额',
dataIndex: 'address',
hideInSearch: true,
width: '20%',
},
{
title: '付费用户数',
dataIndex: 'address',
hideInSearch: true,
width: '20%',
},
{
title: '付费率',
dataIndex: 'address',
hideInSearch: true,
width: '20%',
},
{
title: 'ARPU',
dataIndex: 'arpu',
hideInSearch: true,
width: '20%',
},
{
title: 'ARPPU',
dataIndex: 'arppu',
hideInSearch: true,
width: '20%',
},
];
return (
<div>
<Row gutter={24} style={{ marginBottom: 10 }}>
<Col span={6}>
<Card style={{ height: '140px' }}>
<div>
<p>
<Button
type="link"
icon={
<RedoOutlined
onClick={async () => {
const res = await getCoreData({});
setDate(res.data);
}}
/>
}
/>
</p>
<p>{date.user_num}</p>
</div>
</Card>
</Col>
<Col span={6}>
<Card style={{ height: '140px' }}>
<div>
<p>
<Button
type="link"
icon={
<RedoOutlined
onClick={async () => {
const res = await getCoreData({});
setDate(res.data);
}}
/>
}
/>
</p>
<p>{date.user_num}</p>
</div>
</Card>
</Col>
<Col span={6}>
<Card style={{ height: '140px' }}>
<div>
<p>
<Button
type="link"
icon={
<RedoOutlined
onClick={async () => {
const res = await getCoreData({});
setDate(res.data);
}}
/>
}
/>
</p>
<p>{date.name}</p>
</div>
</Card>
</Col>
<Col span={6}>
<Card style={{ height: '140px' }}>
<div>
<p></p>
<p>{date.user_num}</p>
</div>
</Card>
</Col>
</Row>
<Table
columns={columns}
rowKey="id"
actionRef={tableRef}
request={async (params) => {
params.start_time = startTimeValue;
params.end_time = endTimeValue;
return getCoreData(params);
}}
/>
</div>
);
};
export default CoreData;

View File

@ -0,0 +1,91 @@
import React, { useRef, useState } from 'react';
import Table, { ProColumns, ActionType } from '@/components/Table';
import { getCoreData } from '@/services/DataBorad';
const Address: React.FC = () => {
const tableRef = useRef<ActionType>();
const [visible, setVisible] = useState(false);
const columns: ProColumns<any>[] = [
{
title: '日期',
dataIndex: 'time',
width: '10%',
hideInSearch: true,
},
{
title: '活跃用户数',
dataIndex: 'address',
hideInSearch: true,
width: '20%',
},
{
title: '新用户数量',
dataIndex: 'new_user',
width: 150,
hideInSearch: true,
},
{
title: '当日充值总额',
dataIndex: 'address',
hideInSearch: true,
width: '20%',
},
{
title: '付费用户数',
dataIndex: 'address',
hideInSearch: true,
width: '20%',
},
{
title: '付费率',
dataIndex: 'address',
hideInSearch: true,
width: '20%',
},
{
title: 'ARPU',
dataIndex: 'arpu',
hideInSearch: true,
width: '20%',
},
{
title: 'ARPPU',
dataIndex: 'arppu',
hideInSearch: true,
width: '20%',
},
];
return (
<div>
<Table
columns={columns}
rowKey="id"
actionRef={tableRef}
search={false}
request={async (params) => {
return getCoreData(params);
}}
/>
<Table
columns={columns}
rowKey="id"
actionRef={tableRef}
search={false}
toolBarActions={[
{
type: 'add',
onConfirm: () => {
setVisible(true);
},
},
]}
request={async (params) => {
return getCoreData(params);
}}
/>
</div>
);
};
export default Address;

View File

@ -0,0 +1,85 @@
import React, { useRef } from 'react';
import Modal, { ModalProps } from '@/components/Modal';
import { fetchTableData } from '@/utils/table';
import { getchannelRetentionData } from '@/services/DataBorad';
import Table, { ProColumns, ActionType } from '@/components/Table';
import TimeText from '@/components/Typography/TimeText';
interface TableModalPropsType extends ModalProps {
modalData: any;
onCancel: () => void;
}
const TableModal = ({ onCancel, modalData, ...rest }: TableModalPropsType) => {
const tableRef = useRef<ActionType>();
const columns: ProColumns<any>[] = [
{
title: '时间',
dataIndex: 'time',
hideInSearch: true,
width: '20%',
render: (_, row) => [<TimeText value={row} format={'YYYY-MM-DD'} />],
},
{
title: '渠道类型',
dataIndex: 'login_user',
hideInSearch: true,
ellipsis: true,
},
{
title: '当日',
dataIndex: 'key',
hideInSearch: true,
},
{
title: '第一日',
dataIndex: 'key',
hideInSearch: true,
},
{
title: '第二日',
dataIndex: 'key',
hideInSearch: true,
},
{
title: '第四日',
dataIndex: 'key',
hideInSearch: true,
},
{
title: '第五日',
dataIndex: 'key',
hideInSearch: true,
},
{
title: '第六日',
dataIndex: 'key',
hideInSearch: true,
},
{
title: '第七日',
dataIndex: 'key',
hideInSearch: true,
},
];
const handleCancel = () => {
onCancel();
};
return (
<Modal title="渠道活跃分析" onCancel={handleCancel} width={800} {...rest}>
<Table
columns={columns}
rowKey="id"
search={false}
actionRef={tableRef}
request={async (params) => {
params.time = modalData;
return fetchTableData(getchannelRetentionData, params);
}}
/>
</Modal>
);
};
export default TableModal;

View File

@ -0,0 +1,122 @@
import React, { useRef, useState } from 'react';
import Table, { ProColumns, ActionType } from '@/components/Table';
import { DatePicker } from 'antd';
import moment from 'moment';
import { fetchTableData } from '@/utils/table';
import { getRetentionData } from '@/services/DataBorad';
import TableModal from '@/pages/DataBoard/RetentionAnalysis/List/components/TableModal';
const RetentionAnalysis = () => {
const [startTimeValue, setStartTimeValue] = useState(Number);
const [endTimeValue, setEndTimeValue] = useState(Number);
const [visible, setVisible] = useState(false);
const [requestData, setRequestData] = useState({});
const getTimedata = (data) => {
setStartTimeValue(moment(data[0]).valueOf());
setEndTimeValue(moment(data[1]).valueOf());
console.log(startTimeValue, endTimeValue);
};
const sendData = (row: any) => {
setVisible(true);
setRequestData(row.time);
};
const tableRef = useRef<ActionType>();
const columns: ProColumns<any>[] = [
{
title: '时间',
dataIndex: 'time',
width: '20%',
renderFormItem: (item, { type, defaultRender, ...rest }, form) => {
return (
<DatePicker.RangePicker
style={{ width: '200px' }}
defaultValue={[moment(), moment()]}
format={'YYYY-MM-DD'}
onChange={getTimedata}
/>
);
},
},
{
title: '登录用户数',
dataIndex: 'login_user',
hideInSearch: true,
ellipsis: true,
},
{
title: '当日',
dataIndex: 'key',
hideInSearch: true,
},
{
title: '第一日',
dataIndex: 'key',
hideInSearch: true,
},
{
title: '第二日',
dataIndex: 'key',
hideInSearch: true,
},
{
title: '第四日',
dataIndex: 'key',
hideInSearch: true,
},
{
title: '第五日',
dataIndex: 'key',
hideInSearch: true,
},
{
title: '第六日',
dataIndex: 'key',
hideInSearch: true,
},
{
title: '第七日',
dataIndex: 'key',
hideInSearch: true,
},
{
title: '操作',
valueType: 'option',
width: 150,
render: (_, row) => [
<button
key="edit"
onClick={() => {
sendData(row);
}}
>
</button>,
],
},
];
return (
<div>
<Table
columns={columns}
rowKey="id"
actionRef={tableRef}
request={async (params) => {
params.start_time = startTimeValue;
params.end_time = endTimeValue;
return fetchTableData(getRetentionData, params);
}}
/>
<TableModal
visible={visible}
modalData={requestData}
onCancel={function () {
setVisible(false);
}}
/>
</div>
);
};
export default RetentionAnalysis;

View File

@ -0,0 +1,63 @@
import React, { useRef } from 'react';
import { createForm } from '@formily/core';
import { createSchemaField } from '@formily/react';
import Modal, { ModalProps } from '@/components/Modal';
// import { fetchTableData } from '@/utils/table';
import { Form, FormItem, Input, NumberPicker } from '@formily/antd';
interface AddNftContractModalPropsType extends ModalProps {
onOk: (val: any) => void;
onCancel: () => void;
}
const SchemaField = createSchemaField({
components: {
FormItem,
Input,
NumberPicker,
},
});
const form = createForm({});
const AddNftContractModal = ({ onOk, onCancel, ...rest }: AddNftContractModalPropsType) => {
const handleOk = () => {
const formState = form.getFormState();
onOk(formState.values);
};
const handleCancel = () => {
onCancel();
};
return (
<Modal title="添加地址" onOk={handleOk} onCancel={handleCancel} width={800} {...rest}>
<Form form={form} labelCol={4} wrapperCol={18}>
<SchemaField>
<SchemaField.String
name="nft_name"
title="名称"
required
x-decorator="FormItem"
x-component="Input"
/>
<SchemaField.Number
name="address"
title=""
required
x-decorator="FormItem"
x-component="NumberPicker"
/>
<SchemaField.String
name="contract"
title="量"
required
x-decorator="FormItem"
x-component="NumberPicker"
/>
</SchemaField>
</Form>
</Modal>
);
};
export default AddNftContractModal;

View File

@ -0,0 +1,70 @@
import React, { useRef } from 'react';
import { createForm } from '@formily/core';
import { createSchemaField } from '@formily/react';
import Modal, { ModalProps } from '@/components/Modal';
// import { fetchTableData } from '@/utils/table';
import { Form, FormItem, Input, NumberPicker } from '@formily/antd';
interface AddNftModalPropsType extends ModalProps {
onOk: (val: any) => void;
onCancel: () => void;
}
const SchemaField = createSchemaField({
components: {
FormItem,
Input,
NumberPicker,
},
});
const form = createForm({});
const AddNftModal = ({ onOk, onCancel, ...rest }: AddNftModalPropsType) => {
const handleOk = () => {
const formState = form.getFormState();
onOk(formState.values);
};
const handleCancel = () => {
onCancel();
};
return (
<Modal title="添加地址" onOk={handleOk} onCancel={handleCancel} width={800} {...rest}>
<Form form={form} labelCol={4} wrapperCol={18}>
<SchemaField>
<SchemaField.String
name="nft_name"
title="名称"
required
x-decorator="FormItem"
x-component="Input"
/>
<SchemaField.Number
name="contract"
title="合约"
required
x-decorator="FormItem"
x-component="NumberPicker"
/>
<SchemaField.Number
name="description"
title="描述"
required
x-decorator="FormItem"
x-component="NumberPicker"
/>
<SchemaField.Number
name="img"
title="图片"
required
x-decorator="FormItem"
x-component="NumberPicker"
/>
</SchemaField>
</Form>
</Modal>
);
};
export default AddNftModal;

View File

@ -0,0 +1,98 @@
import React, { useRef, useState } from 'react';
import Table, { ProColumns, ActionType } from '@/components/Table';
import { message } from 'antd';
// import { fetchTableData } from '@/utils/table';
import AddNftModal from '@/pages/Nft/NftContractManage/List/components/addNftModel';
import AddNftContractModal from '@/pages/Nft/NftContractManage/List/components/addNftContract';
const Address: React.FC = () => {
const tableRef = useRef<ActionType>();
const [NftModal, setNftModal] = useState(false);
const [, setVisible] = useState(false);
const columns: ProColumns<any>[] = [
{
title: '合约名称',
dataIndex: 'time',
width: '10%',
hideInSearch: true,
},
{
title: '合约地址',
dataIndex: 'address',
hideInSearch: true,
width: '20%',
},
{
title: 'NFT数量',
dataIndex: 'nft_num',
width: 150,
hideInSearch: true,
},
];
return (
<div>
<Table
columns={columns}
rowKey="id"
actionRef={tableRef}
toolBarActions={[
{
type: 'add',
text: '添加NFT',
onConfirm: () => {
setVisible(true);
},
},
{
type: 'add',
text: '新建NFT合约',
onConfirm: () => {
setVisible(true);
},
},
]}
// request={async (params) => {
// // return fetchTableData(, params);
// }}
/>
<AddNftModal
visible={NftModal}
onCancel={function () {
setVisible(false);
}}
onOk={async function (val: any): Promise<void> {
try {
const params = { ...val };
// await creatNftAddress(params);
message.success('添加成功');
} catch (e) {
console.log(e);
message.success('发生错误');
setVisible(false);
}
}}
/>
<AddNftContractModal
visible={NftModal}
onCancel={function () {
setVisible(false);
}}
onOk={async function (val: any): Promise<void> {
try {
const params = { ...val };
// await creatNftAddress(params);
message.success('添加成功');
} catch (e) {
console.log(e);
message.success('发生错误');
setVisible(false);
}
}}
/>
</div>
);
};
export default Address;

View File

@ -33,6 +33,37 @@ export default [
}, },
], ],
}, },
{
name: '数据看板',
path: RoutePath.DATABOARD,
routes: [
{
path: RoutePath.DATABOARD,
redirect: RoutePath.COREDATA.LIST,
hideInMenu: true,
},
{
name: '核心看板',
path: RoutePath.COREDATA.LIST,
component: './DataBoard/CoreData/List',
},
{
name: '活跃分析',
path: RoutePath.ACTIVEANALYSIS.LIST,
component: './DataBoard/ActiveAnalysis/List',
},
{
name: '留存分析',
path: RoutePath.RETENTIONANALYSIS.LIST,
component: './DataBoard/RetentionAnalysis/List',
},
{
name: '用户充值分析',
path: RoutePath.RECHARGEANALYSIS.LIST,
component: './DataBoard/RechargeAnalysis/List',
},
],
},
{ {
path: '/', path: '/',
redirect: RoutePath.WORK.LIST, redirect: RoutePath.WORK.LIST,

View File

@ -1,7 +1,21 @@
const SETTING = '/setting'; const SETTING = '/setting';
const DATABOARD = '/databoard';
const RoutePath = { const RoutePath = {
LOGIN: '/login', LOGIN: '/login',
SETTING: SETTING, SETTING: SETTING,
DATABOARD: DATABOARD,
COREDATA: {
LIST: '${DATABOARD}/coredata',
},
ACTIVEANALYSIS: {
LIST: '${DATABOARD}/activeanalysis',
},
RETENTIONANALYSIS: {
LIST: '${DATABOARD}/retentionanalysis',
},
RECHARGEANALYSIS: {
LIST: '${DATABOARD}/rechargeanalysis',
},
WORK: { WORK: {
LIST: `${SETTING}/work`, LIST: `${SETTING}/work`,
EDIT: `${SETTING}/work/edit`, EDIT: `${SETTING}/work/edit`,

25
src/services/DataBorad.ts Normal file
View File

@ -0,0 +1,25 @@
import request from '@/utils/request';
export const getCoreData = (data) => {
return request.request({
url: '/tgb/api/v1/data/get',
method: 'post',
data,
});
};
// 活跃分析
export const getRetentionData = (data) => {
return request.request({
url: '/tgb/api/v1/data/get',
method: 'post',
data,
});
};
// 渠道活跃分析
export const getchannelRetentionData = (data) => {
return request.request({
url: '/tgb/api/v1/data/get',
method: 'post',
data,
});
};