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
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>
|
|
);
|
|
}
|
|
}
|
|
|