👨🏻‍💻👩🏻‍💻 Use Ant Design like a Pro!
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

505 lines
15 KiB

import React, { Component } from 'react';
import { connect } from 'dva';
import {
Row,
Col,
Icon,
Card,
Tabs,
Table,
Radio,
DatePicker,
Tooltip,
Menu,
Dropdown,
} from 'antd';
import {
ChartCard,
MiniArea,
MiniBar,
MiniProgress,
Field,
Bar,
Pie,
TimelineChart,
} from 'components/Charts';
import Trend from 'components/Trend';
import NumberInfo from 'components/NumberInfo';
import numeral from 'numeral';
import GridContent from '../../layouts/GridContent';
import Yuan from '../../utils/Yuan';
import { getTimeDistance } from '../../utils/utils';
import styles from './Analysis.less';
const { TabPane } = Tabs;
const { RangePicker } = DatePicker;
const rankingListData = [];
for (let i = 0; i < 7; i += 1) {
rankingListData.push({
title: `工专路 ${i} 号店`,
total: 323234,
});
}
@connect(({ chart, loading }) => ({
chart,
loading: loading.effects['chart/fetch'],
}))
export default class Analysis extends Component {
state = {
salesType: 'all',
currentTabKey: '',
rangePickerValue: getTimeDistance('year'),
};
componentDidMount() {
const { dispatch } = this.props;
dispatch({
type: 'chart/fetch',
});
}
componentWillUnmount() {
const { dispatch } = this.props;
dispatch({
type: 'chart/clear',
});
}
handleChangeSalesType = e => {
this.setState({
salesType: e.target.value,
});
};
handleTabChange = key => {
this.setState({
currentTabKey: key,
});
};
handleRangePickerChange = rangePickerValue => {
const { dispatch } = this.props;
this.setState({
rangePickerValue,
});
dispatch({
type: 'chart/fetchSalesData',
});
};
selectDate = type => {
const { dispatch } = this.props;
this.setState({
rangePickerValue: getTimeDistance(type),
});
dispatch({
type: 'chart/fetchSalesData',
});
};
isActive(type) {
const { rangePickerValue } = this.state;
const value = getTimeDistance(type);
if (!rangePickerValue[0] || !rangePickerValue[1]) {
return;
}
if (
rangePickerValue[0].isSame(value[0], 'day') &&
rangePickerValue[1].isSame(value[1], 'day')
) {
return styles.currentDate;
}
}
render() {
const { rangePickerValue, salesType, currentTabKey } = this.state;
const { chart, loading } = this.props;
const {
visitData,
visitData2,
salesData,
searchData,
offlineData,
offlineChartData,
salesTypeData,
salesTypeDataOnline,
salesTypeDataOffline,
} = chart;
const salesPieData =
salesType === 'all'
? salesTypeData
: salesType === 'online'
? salesTypeDataOnline
: salesTypeDataOffline;
const menu = (
<Menu>
<Menu.Item>操作一</Menu.Item>
<Menu.Item>操作二</Menu.Item>
</Menu>
);
const iconGroup = (
<span className={styles.iconGroup}>
<Dropdown overlay={menu} placement="bottomRight">
<Icon type="ellipsis" />
</Dropdown>
</span>
);
const salesExtra = (
<div className={styles.salesExtraWrap}>
<div className={styles.salesExtra}>
<a className={this.isActive('today')} onClick={() => this.selectDate('today')}>
今日
</a>
<a className={this.isActive('week')} onClick={() => this.selectDate('week')}>
本周
</a>
<a className={this.isActive('month')} onClick={() => this.selectDate('month')}>
本月
</a>
<a className={this.isActive('year')} onClick={() => this.selectDate('year')}>
全年
</a>
</div>
<RangePicker
value={rangePickerValue}
onChange={this.handleRangePickerChange}
style={{ width: 256 }}
/>
</div>
);
const columns = [
{
title: '排名',
dataIndex: 'index',
key: 'index',
},
{
title: '搜索关键词',
dataIndex: 'keyword',
key: 'keyword',
render: text => <a href="/">{text}</a>,
},
{
title: '用户数',
dataIndex: 'count',
key: 'count',
sorter: (a, b) => a.count - b.count,
className: styles.alignRight,
},
{
title: '周涨幅',
dataIndex: 'range',
key: 'range',
sorter: (a, b) => a.range - b.range,
render: (text, record) => (
<Trend flag={record.status === 1 ? 'down' : 'up'}>
<span style={{ marginRight: 4 }}>
{text}
%
</span>
</Trend>
),
align: 'right',
},
];
const activeKey = currentTabKey || (offlineData[0] && offlineData[0].name);
const CustomTab = ({ data, currentTabKey: currentKey }) => (
<Row gutter={8} style={{ width: 138, margin: '8px 0' }}>
<Col span={12}>
<NumberInfo
title={data.name}
subTitle="转化率"
gap={2}
total={`${data.cvr * 100}%`}
theme={currentKey !== data.name && 'light'}
/>
</Col>
<Col span={12} style={{ paddingTop: 36 }}>
<Pie
animate={false}
color={currentKey !== data.name && '#BDE4FF'}
inner={0.55}
tooltip={false}
margin={[0, 0, 0, 0]}
percent={data.cvr * 100}
height={64}
/>
</Col>
</Row>
);
const topColResponsiveProps = {
xs: 24,
sm: 12,
md: 12,
lg: 12,
xl: 6,
style: { marginBottom: 24 },
};
return (
<GridContent>
<Row gutter={24}>
<Col {...topColResponsiveProps}>
<ChartCard
bordered={false}
title="总销售额"
action={
<Tooltip title="指标说明">
<Icon type="info-circle-o" />
</Tooltip>
}
loading={loading}
total={() => <Yuan>126560</Yuan>}
footer={<Field label="日均销售额" value={`${numeral(12423).format('0,0')}`} />}
contentHeight={46}
>
<Trend flag="up" style={{ marginRight: 16 }}>
周同比
<span className={styles.trendText}>12%</span>
</Trend>
<Trend flag="down">
日环比
<span className={styles.trendText}>11%</span>
</Trend>
</ChartCard>
</Col>
<Col {...topColResponsiveProps}>
<ChartCard
bordered={false}
loading={loading}
title="访问量"
action={
<Tooltip title="指标说明">
<Icon type="info-circle-o" />
</Tooltip>
}
total={numeral(8846).format('0,0')}
footer={<Field label="日访问量" value={numeral(1234).format('0,0')} />}
contentHeight={46}
>
<MiniArea color="#975FE4" data={visitData} />
</ChartCard>
</Col>
<Col {...topColResponsiveProps}>
<ChartCard
bordered={false}
loading={loading}
title="支付笔数"
action={
<Tooltip title="指标说明">
<Icon type="info-circle-o" />
</Tooltip>
}
total={numeral(6560).format('0,0')}
footer={<Field label="转化率" value="60%" />}
contentHeight={46}
>
<MiniBar data={visitData} />
</ChartCard>
</Col>
<Col {...topColResponsiveProps}>
<ChartCard
loading={loading}
bordered={false}
title="运营活动效果"
action={
<Tooltip title="指标说明">
<Icon type="info-circle-o" />
</Tooltip>
}
total="78%"
footer={
<div style={{ whiteSpace: 'nowrap', overflow: 'hidden' }}>
<Trend flag="up" style={{ marginRight: 16 }}>
周同比
<span className={styles.trendText}>12%</span>
</Trend>
<Trend flag="down">
日环比
<span className={styles.trendText}>11%</span>
</Trend>
</div>
}
contentHeight={46}
>
<MiniProgress percent={78} strokeWidth={8} target={80} color="#13C2C2" />
</ChartCard>
</Col>
</Row>
<Card loading={loading} bordered={false} bodyStyle={{ padding: 0 }}>
<div className={styles.salesCard}>
<Tabs tabBarExtraContent={salesExtra} size="large" tabBarStyle={{ marginBottom: 24 }}>
<TabPane tab="销售额" key="sales">
<Row>
<Col xl={16} lg={12} md={12} sm={24} xs={24}>
<div className={styles.salesBar}>
<Bar height={295} title="销售额趋势" data={salesData} />
</div>
</Col>
<Col xl={8} lg={12} md={12} sm={24} xs={24}>
<div className={styles.salesRank}>
<h4 className={styles.rankingTitle}>门店销售额排名</h4>
<ul className={styles.rankingList}>
{rankingListData.map((item, i) => (
<li key={item.title}>
<span className={i < 3 ? styles.active : ''}>{i + 1}</span>
<span>{item.title}</span>
<span>{numeral(item.total).format('0,0')}</span>
</li>
))}
</ul>
</div>
</Col>
</Row>
</TabPane>
<TabPane tab="访问量" key="views">
<Row>
<Col xl={16} lg={12} md={12} sm={24} xs={24}>
<div className={styles.salesBar}>
<Bar height={292} title="访问量趋势" data={salesData} />
</div>
</Col>
<Col xl={8} lg={12} md={12} sm={24} xs={24}>
<div className={styles.salesRank}>
<h4 className={styles.rankingTitle}>门店访问量排名</h4>
<ul className={styles.rankingList}>
{rankingListData.map((item, i) => (
<li key={item.title}>
<span className={i < 3 ? styles.active : ''}>{i + 1}</span>
<span>{item.title}</span>
<span>{numeral(item.total).format('0,0')}</span>
</li>
))}
</ul>
</div>
</Col>
</Row>
</TabPane>
</Tabs>
</div>
</Card>
<Row gutter={24}>
<Col xl={12} lg={24} md={24} sm={24} xs={24}>
<Card
loading={loading}
bordered={false}
title="线上热门搜索"
extra={iconGroup}
style={{ marginTop: 24 }}
>
<Row gutter={68}>
<Col sm={12} xs={24} style={{ marginBottom: 24 }}>
<NumberInfo
subTitle={
<span>
搜索用户数
<Tooltip title="指标文案">
<Icon style={{ marginLeft: 8 }} type="info-circle-o" />
</Tooltip>
</span>
}
gap={8}
total={numeral(12321).format('0,0')}
status="up"
subTotal={17.1}
/>
<MiniArea line height={45} data={visitData2} />
</Col>
<Col sm={12} xs={24} style={{ marginBottom: 24 }}>
<NumberInfo
subTitle="人均搜索次数"
total={2.7}
status="down"
subTotal={26.2}
gap={8}
/>
<MiniArea line height={45} data={visitData2} />
</Col>
</Row>
<Table
rowKey={record => record.index}
size="small"
columns={columns}
dataSource={searchData}
pagination={{
style: { marginBottom: 0 },
pageSize: 5,
}}
/>
</Card>
</Col>
<Col xl={12} lg={24} md={24} sm={24} xs={24}>
<Card
loading={loading}
className={styles.salesCard}
bordered={false}
title="销售额类别占比"
bodyStyle={{ padding: 24 }}
extra={
<div className={styles.salesCardExtra}>
{iconGroup}
<div className={styles.salesTypeRadio}>
<Radio.Group value={salesType} onChange={this.handleChangeSalesType}>
<Radio.Button value="all">全部渠道</Radio.Button>
<Radio.Button value="online">线上</Radio.Button>
<Radio.Button value="offline">门店</Radio.Button>
</Radio.Group>
</div>
</div>
}
style={{ marginTop: 24, minHeight: 509 }}
>
<h4 style={{ marginTop: 8, marginBottom: 32 }}>销售额</h4>
<Pie
hasLegend
subTitle="销售额"
total={() => <Yuan>{salesPieData.reduce((pre, now) => now.y + pre, 0)}</Yuan>}
data={salesPieData}
valueFormat={value => <Yuan>{value}</Yuan>}
height={248}
lineWidth={4}
/>
</Card>
</Col>
</Row>
<Card
loading={loading}
className={styles.offlineCard}
bordered={false}
bodyStyle={{ padding: '0 0 32px 0' }}
style={{ marginTop: 32 }}
>
<Tabs activeKey={activeKey} onChange={this.handleTabChange}>
{offlineData.map(shop => (
<TabPane tab={<CustomTab data={shop} currentTabKey={activeKey} />} key={shop.name}>
<div style={{ padding: '0 24px' }}>
<TimelineChart
height={400}
data={offlineChartData}
titleMap={{ y1: '客流量', y2: '支付笔数' }}
/>
</div>
</TabPane>
))}
</Tabs>
</Card>
</GridContent>
);
}
}