使用echarts绘制曲线图

 

<template>
  <div class="home-container">
    <total-product-info
      :merchantCount="merchantCount"
      :todayConsume="todayConsume"
      :todayRecharge="todayRecharge"
      :todayFeeYuan="todayFeeYuan"
    ></total-product-info>

    <div id="curve">
      <div class="content">
        <div class="header">
          <span>产品-消费金额</span>
          <el-form ref="form" :rules="formRules" :model="query">
            <el-row :gutter="24">
              <el-col class="joker-col">
                <el-form-item
                  label="产品名称:"
                  prop="productId"
                  label-width="110px"
                >
                  <el-select
                    v-model="query.productId"
                    placeholder="请选择"
                    clearable
                    @change="productChange"
                  >
                    <el-option
                      v-for="item in productListData"
                      :key="item.id"
                      :label="item.name"
                      :value="item.id"
                    >
                    </el-option>
                  </el-select>
                </el-form-item>
              </el-col>
              <el-col class="joker-col">
                <el-form-item
                  prop="time"
                  label="查询时间:"
                  label-width="110px"
                >
                  <el-date-picker
                    v-model="query.time"
                    type="daterange"
                    range-separator="-"
                    start-placeholder="查询时间"
                    end-placeholder="查询时间"
                    value-format="yyyy-MM-dd"
                    :picker-options="pickerOptions"
                    @change="timeChange"
                  ></el-date-picker>
                </el-form-item>
              </el-col>
            </el-row>
          </el-form>
        </div>
        <div id="paint"></div>
      </div>
    </div>
    <div class="pie-box">
      <div class="content">
        <div class="search">
          <el-form ref="formpie" :rules="formRules" :model="query">
            <el-row :gutter="24">
              <el-col class="joker-col">
                <el-form-item
                  prop="timePie"
                  label="查询时间:"
                  label-width="110px"
                >
                  <el-date-picker
                    v-model="query.timePie"
                    type="daterange"
                    range-separator="-"
                    start-placeholder="查询时间"
                    end-placeholder="查询时间"
                    value-format="yyyy-MM-dd"
                    :picker-options="pickerOptions"
                    @change="timePieChange"
                  ></el-date-picker>
                </el-form-item>
              </el-col>
            </el-row>
          </el-form>
        </div>
        <el-row class="section-m" :gutter="24">
          <el-col :span="12" :lg="12" :sm="24" :xs="24">
            <div class="left">
              <div class="title">各产品-消费总额占比图</div>
              <div id="pie"></div>
            </div>
          </el-col>
          <el-col :span="12" :lg="12" :sm="24" :xs="24">
            <div class="right">
              <div class="title">各产品-技术服务费用占比图</div>
              <div class="line"></div>
              <ul>
                <li v-for="item in barList" :key="item.name">
                  <el-row class="section-m" :gutter="24">
                    <el-col :span="4">
                      <span class="name">{{ item.name }}</span>
                    </el-col>
                    <el-col :span="12">
                      <el-progress
                        type="line"
                        :text-inside="true"
                        :stroke-width="14"
                        :percentage="item.rate"
                        :color="item.color"
                      ></el-progress>
                    </el-col>
                    <el-col :span="8">
                      <span class="right-desc">消费₦{{ item.value }}</span>
                      <span class="right-desc" :style="{ color: item.color }"
                        >占{{ item.rate }}%</span
                      >
                    </el-col>
                  </el-row>
                </li>
              </ul>
            </div>
          </el-col>
        </el-row>
      </div>
    </div>
  </div>
</template>
<script>
import api from '@/api/api.js';
import TotalProductInfo from './totalProductInfo';
import currency from 'currency.js';
import * as d3 from 'd3';
import moment from 'moment';
import { getRateCalculate } from '@/utils';
import { debounce } from 'lodash';
import { mapGetters } from 'vuex';
const echarts = require('echarts');

const D30 = 3600 * 1000 * 24 * 30;
const colors = [
  '#30D2FF',
  '#347DFF',
  '#F2A821',
  '#47D568',
  '#07BDB4',
  '#6C74FF',
];

export default {
  components: { TotalProductInfo },
  data() {
    const timeRange = (rule, value, callback) => {
      if (!value) {
        callback(new Error('时间不能为空'));
      }
      const start = value[0];
      const end = value[1];
      if (moment(end).diff(moment(start)) > D30) {
        callback(new Error('选择时间不大于30天,请重新选择时间'));
      } else {
        callback();
      }
    };
    return {
      pickerOptions: {
        disabledDate(time) {
          return time.getTime() > Date.now() - 8.64e7; // 设置选择今天之前的日期(不能选择当天)
        },
      },
      productListData: [],
      query: {
        productId: '',
        time: [],
        timePie: [],
      },
      formRules: {
        time: [{ validator: timeRange, trigger: 'blur' }],
        timePie: [{ validator: timeRange, trigger: 'blur' }],
      },
      //
      merchantCount: '',
      todayConsume: '',
      todayRecharge: '',
      todayFeeYuan: '',
      barList: [],
      pieCanvas: null,
      splinesCanvas: null,
    };
  },
  computed: {
    ...mapGetters(['sidebar']),
  },
  watch: {
    sidebar: {
      handler(val) {
        setTimeout(() => {
          this.resize();
        }, 300);
      },
      deep: true,
    },
  },
  created() {
    this.initTime();
    this.init();
  },
  mounted() {
    let that = this;
    window.addEventListener('resize', debounce(that.resize, 30), true);
  },
  methods: {
    async init() {
      await this.productList();
      this.getData();
      this.getPieData();
    },
    initTime() {
      let end = new Date();
      let start = new Date();
      start.setTime(start.getTime() - 3600 * 1000 * 24 * 31);
      end.setTime(end.getTime() - 3600 * 1000 * 24 * 1);
      this.query.time = [
        moment(start).format('YYYY-MM-DD'),
        moment(end).format('YYYY-MM-DD'),
      ];
      this.query.timePie = [
        moment(start).format('YYYY-MM-DD'),
        moment(end).format('YYYY-MM-DD'),
      ];
    },
    getData() {
      api.homeProductInfo().then(res => {
        if (res.code == 200) {
          this.merchantCount = res.data.merchantCount;
          this.todayConsume = currency(res.data.consume).format({
            symbol: '',
            separator: '',
          });
          this.todayRecharge = currency(res.data.recharge).format({
            symbol: '',
            separator: '',
          });
          this.todayFeeYuan = currency(res.data.serviceFee).format({
            symbol: '',
            separator: '',
          });
        }
      });
    },
    getSplinesData() {
      api
        .productConsume({
          productId: this.query.productId,
          startDate: this.query.time?.[0],
          endDate: this.query.time?.[1],
        })
        .then(res => {
          if (res.code == 200) {
            const amountYuan = res.data.map(
              item => item.amountYuan || item.amount,
            );
            const date = res.data.map(item => item.date);
            this.paint(amountYuan, date);
          }
        });
    },
    getPieData() {
      api
        .consumePercent({
          startDate: this.query.timePie?.[0],
          endDate: this.query.timePie?.[1],
        })
        .then(res => {
          if (res.code == 200) {
            const piedata = res.data.productConsumePercent.map(item => {
              for (let i = 0; i < this.productListData.length; i++) {
                const id = this.productListData[i].id;
                if (item.id == id) {
                  item.name = this.productListData[i].name;
                  item.value = item.amountYuan || item.amount;
                  return item;
                }
              }
            });
            this.pie(piedata);

            this.barList = res.data.productServiceFeePercent.map(
              (item, index) => {
                for (let i = 0; i < this.productListData.length; i++) {
                  const id = this.productListData[i].id;
                  if (item.id == id) {
                    item.name = this.productListData[i].name;
                    item.value = item.amount;
                    item.color = this.productListData[i].color;
                    return item;
                  }
                }
              },
            );
            getRateCalculate(this.barList.map(_ => _.value)).forEach((v, i) => {
              this.barList[i].rate = v;
            });
          }
        });
    },
    async productList() {
      await api.productList().then(res => {
        if (res.code == 200) {
          this.productListData = res.data.map((item, i) => {
            item.color = colors[i];
            return item;
          });
          if (res.data.length) {
            this.query.productId = res.data[0].id;
            this.getSplinesData();
          }
        }
      });
    },
    paint(value, xAxisData) {
      let option = {
        backgroundColor: '#fff',
        xAxis: {
          name: '日期',
          type: 'category',
          boundaryGap: false,
          axisTick: {
            show: false,
          },
          axisLabel: {
            color: '#AFAEC0',
          },
          nameTextStyle: {
            color: '#666666',
            fontSize: 16,
            align: 'right',
            verticalAlign: 'top',
            lineHeight: 76,
          },
          axisLine: {
            lineStyle: {
              type: 'dashed',
              color: '#D1D1D1',
              width: 1,
            },
          },
        },
        yAxis: {
          name: '消费金额(元)',
          type: 'value',
          axisTick: {
            show: false, //不显示刻度线
          },
          axisLabel: {
            color: '#AFAEC0', //y轴上的字体颜色
          },
          axisLine: {
            lineStyle: {
              width: 1,
              color: '#D1D1D1', //y轴的轴线的宽度和颜色
            },
          },
          nameTextStyle: {
            color: '#666666',
            fontSize: 16,
            lineHeight: 50,
          },
          splitLine: {
            show: true,
            lineStyle: {
              type: 'dashed',
              width: 1,
              color: '#D1D1D1',
            },
          },
        },
        tooltip: {
          trigger: 'axis',
          axisPointer: {
            type: 'cross',
            label: {
              show: false,
            },
          },
          formatter: function (params, a, b) {
            const value = params[0].value;
            return `<div style="text-align:center;">
                      <b style="font-size:30px;color:#333333;margin-bottom:2px;">${value}</b>
                      <div style="font-size:14px;color:#999999;">消费金额</div>
                    </div>`;
          },
        },
        grid: {
          left: '80',
          right: '50',
        },
        series: [
          {
            type: 'line',
            symbol: 'circle',
            symbolSize: 1,
            smooth: true,
            itemStyle: {
              color: '#5995FF',
            },
            areaStyle: {
              color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [
                {
                  offset: 0,
                  color: '#347DFF',
                },
                {
                  offset: 1,
                  color: '#FFFFFF',
                },
              ]),
            },
          },
        ],
      };
      option.xAxis.data = xAxisData;
      option.series[0].data = value;
      let chart = this.getSplines();
      chart.setOption(option);
    },
    pie(data) {
      const names = data.map(_ => _.name);
      let rich = {
        tt: {
          fontWeight: 'bold',
          fontSize: 18,
          color: '#337DFF',
        },
      };

      let option = {
        title: {
          // text: '各产品-消费总额占比图',
          subtext: '',
          left: 50,
          top: 110,
          borderLeftWidth: 1,
          borderColor: 'red',
        },
        tooltip: {
          trigger: 'item',
          formatter: '{b} : {c} ({d}%)',
        },
        legend: {
          icon: 'circle',
          itemHeight: 6,
          right: 43,
          bottom: 21,
          textStyle: {
            fontSize: 12,
            color: '#999',
          },
          borderColor: '#F0EFEF',
          borderWidth: 1,
          padding: [16, 23, 16, 23],
          data: names,
        },
        color: colors,
        series: [
          {
            type: 'pie',
            selectedMode: 'single',
            radius: ['30%', '50%'],
            center: ['50%', '50%'],
            label: {
              show: true,
              formatter: '{tt|{d}%}\n消费¥{c}',
              rich: rich,
              lineHeight: 22,
              fontSize: 14,
              color: '#333333',
            },
            emphasis: {
              itemStyle: {
                shadowBlur: 10,
                shadowOffsetX: 0,
                shadowColor: 'rgba(0, 0, 0, 0.5)',
              },
            },
          },
        ],
      };
      option.series[0].data = data; // item.selected: true
      let chart = this.getPie();
      chart.setOption(option);
    },
    timeChange() {
      this.getPaintData();
    },
    productChange() {
      this.getPaintData();
    },
    getPaintData() {
      this.$refs['form'].validate(valid => {
        if (valid) {
          this.getSplinesData();
        }
      });
    },
    timePieChange() {
      this.$refs['formpie'].validate(valid => {
        if (valid) {
          this.getPieData();
        }
      });
    },
    getPie() {
      if (this.pieCanvas) {
        return this.pieCanvas;
      }
      this.pieCanvas = echarts.init(document.getElementById('pie'));
      return this.pieCanvas;
    },
    getSplines() {
      if (this.splinesCanvas) {
        return this.splinesCanvas;
      }
      this.splinesCanvas = echarts.init(document.getElementById('paint'));
      return this.splinesCanvas;
    },
    resize() {
      this.pieCanvas && this.pieCanvas.resize();
      this.splinesCanvas && this.splinesCanvas.resize();
    },
  },
  destrbeforeDestroy() {
    window.removeEventListener('resize', this.resize, true);
  },
};
</script>

<style lang="scss" scoped>
.home-container {
  background-color: #f3f6f9;
}

.customer {
  margin: 0 30px;
  box-shadow: 0 7px 17px 0 #e5ecf0;
  border-radius: 4px;

  .content-box {
    min-width: 1500px;
    background-color: #fff;
    padding: 30px;
    margin: 0;
    box-sizing: border-box;
    .pagination {
      margin-top: 30px;
    }
  }
}

.el-form-item {
  margin-bottom: 0;
}
/deep/ .el-form-item__label {
  line-height: 40px !important;
  font-size: 16px;
}

#curve {
  padding: 0 30px;
  /deep/ .el-col-24 {
    width: auto;
  }
  .content {
    background-color: #fff;
    padding-top: 30px;
    margin-bottom: 30px;
    border-radius: 4px;
    #paint {
      width: 100%;
      height: 400px;
    }
  }
  .header {
    display: flex;
    padding: 0 45px 0 39px;
    justify-content: space-between;
    margin-bottom: 20px;
    span {
      font-size: 16px;
      font-weight: bold;
      line-height: 22px;
      color: #333;
    }
    span::before {
      content: ' ';
      display: inline-block;
      background-color: #347dff;
      width: 4px;
      height: 20px;
      vertical-align: middle;
      line-height: 22px;
      margin-right: 10px;
      position: relative;
      top: -2px;
    }
  }
}
.section-m {
  background: #fff;
  margin: 0 !important;
  width: 100%;
}
.pie-box {
  padding: 0 30px 30px;
  .content {
    background: #fff;
    padding-top: 40px;
    .search {
      display: flex;
      justify-content: flex-end;
      padding-right: 45px;
      margin-bottom: 20px;
    }
    .left {
      position: relative;
      #pie {
        width: 100%;
        height: 550px;
      }
      .title {
        position: absolute;
        left: 40px;
        top: 50px;
        font-size: 16px;
        color: #333;
        font-weight: bold;
        &::before {
          content: ' ';
          display: inline-block;
          width: 4px;
          height: 20px;
          background-color: #347dff;
          vertical-align: middle;
          margin-right: 10px;
          position: relative;
          top: -2px;
        }
      }
    }
    .right {
      width: 100%;
      height: 500px;
      padding-right: 45px;
      position: relative;
      .title {
        position: absolute;
        left: 40px;
        top: 50px;
        font-size: 16px;
        color: #333;
        font-weight: bold;
        &::before {
          content: ' ';
          display: inline-block;
          width: 4px;
          height: 20px;
          background-color: #347dff;
          vertical-align: middle;
          margin-right: 10px;
          position: relative;
          top: -2px;
        }
      }
      .line {
        position: absolute;
        top: 40px;
        left: 0;
        width: 1px;
        background-color: #d1d1d1;
        height: 412px;
      }
      ul {
        position: relative;
        top: 180px;
        left: 48px;
        width: 100%;
        li {
          width: 100%;
          display: flex;
          margin-bottom: 34px;
          span.name {
            width: 102px;
            color: #666;
            font-size: 14px;
          }
          .el-progress {
            height: 14px;
            /deep/ .el-progress-bar__outer,
            /deep/ .el-progress-bar__inner {
              border-radius: 0;
            }
          }
          .el-col {
            padding: 0 !important;
          }
          .right-desc {
            margin-left: 15px;
            word-break: keep-all;
          }
        }
      }
    }
  }
}

@media screen and (max-width: 1199px) {
  .line {
    display: none !important;
  }
}
</style>

 

  • 1
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值