123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495 |
- import React, { Component, Fragment } from 'react';
- import { connect } from 'dva';
- import {
- Row,
- Col,
- Icon,
- Card,
- Tabs,
- Table,
- Radio,
- DatePicker,
- Tooltip,
- Menu,
- Dropdown,
- } from 'antd';
- import numeral from 'numeral';
- import {
- ChartCard,
- yuan,
- MiniArea,
- MiniBar,
- MiniProgress,
- Field,
- Bar,
- Pie,
- TimelineChart,
- } from 'components/Charts';
- import Trend from 'components/Trend';
- import NumberInfo from 'components/NumberInfo';
- 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() {
- 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 => {
- this.setState({
- rangePickerValue,
- });
-
- this.props.dispatch({
- type: 'chart/fetchSalesData',
- });
- };
-
- selectDate = type => {
- this.setState({
- rangePickerValue: getTimeDistance(type),
- });
-
- this.props.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 (
- <Fragment>
- <Row gutter={24}>
- <Col {...topColResponsiveProps}>
- <ChartCard
- bordered={false}
- title="总销售额"
- action={
- <Tooltip title="指标说明">
- <Icon type="info-circle-o" />
- </Tooltip>
- }
- total={() => <span dangerouslySetInnerHTML={{ __html: yuan(126560) }} />}
- 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}
- 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}
- 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
- 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={() => (
- <span
- dangerouslySetInnerHTML={{
- __html: yuan(salesPieData.reduce((pre, now) => now.y + pre, 0)),
- }}
- />
- )}
- data={salesPieData}
- valueFormat={val => <span dangerouslySetInnerHTML={{ __html: yuan(val) }} />}
- 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>
- </Fragment>
- );
- }
- }
|