vue左侧漏斗切换 echart图表动态更新

这个需求是根据点击左侧的箭头部分,右侧图表切换,左侧选中数据高亮(图片用的svg)

一、效果图

 

 二、vue组件

<template>
  <div class="funnel_wrap">
    <div class="flex_between">
      <div class="sec_title">测试</div>
        <!-- <el-checkbox label="平均值" v-model="averageCheck"  @change="changeAverage"></el-checkbox> -->
    </div>
    <div class="flex_center funnel_con" v-if="flowList.items&&flowList.items.length>0">
      <div class="flow_con">
        <div class="left_position pointer">
          <svg-icon icon-class="arrow" :style="`color:${chooseId==5||chooseId==6||chooseId==7?'green':'#DFE1EB'};position:absolute;right:0;bottom:-3px;width: 6px;height: 6px;`"></svg-icon>
          <div @click="changeFunnel(7)" :class="['left_top',chooseId==7?'leftactive':'']">
            <div class="left_title">{{radioList[6]}}%</div>
          </div>
          <div @click="changeFunnel(6)" :class="['left_cen',chooseId==6?'leftactive':'']"  :style="`${chooseId==7?'border-bottom:0':''}`">
            <div class="left_title">{{radioList[5]}}%</div>
          </div>
          <div @click="changeFunnel(5)" :class="['left_bot',chooseId==5?'leftactive':'']" :style="`${chooseId==7||chooseId==6?'border-bottom:0':''}`">
            <div class="left_title">{{radioList[4]}}%</div>
          </div>
        </div>
        <div :class="['flow_item',chooseList.includes(item.id)?'active':''] " :style="'width:'+(255-(12*index))+'px'" v-for="(item,index) in flowList.items" :key="index">
          <div class="item_lef">{{item.title}}</div>
          <div class="item_rig" >{{item.newValue}}
            <div class="funnle"></div>
          </div>
         <div class="svg_box" v-if="index!==4" :style="`color:${chooseId==index?'green':'#DFE1EB'};right:-${(15+12*index)}px`" >
          <svg-icon @click="changeFunnel(index)" class="pointer" :style="'height:42px;width:'+(26+index*12)+'px'" :icon-class="'funnel'+index"></svg-icon>
          <div class="title"  :style="`left:${(30+index*12)}px;color:${chooseId==index?'green':'#212848'}`">{{radioList[index]}}%</div>
         </div>
        </div>
      </div>
      <div class="flow_echart">
        <line-vue v-if="lineOpt.id" :opt="lineOpt" :heightNum="300"></line-vue>
     </div>
    </div>
    <div v-else class="none">暂无数据</div>
  </div>
</template>

<script>
import { defineComponent, onMounted, computed,reactive,ref } from 'vue'
import { useRouter, useRoute } from 'vue-router'
import { roomflow } from "@/api/analyze/index.js";
import lineVue from "@/components/echartsChange/lineVue.vue";
import { thousandthis, valueTransfer,processingData,$formatTime,millionTransfer } from "@/utils/utils";
export default defineComponent({
  components:{
    lineVue
  },
  props: {
    opt: {
      type: Object,
      default: () => {
        return {
          roomId:'',
          userId:''
        };
      },
    },
    optIds: {
      type: Object,
      default: () => {
        return {
          lineId: "funnel_data_id", // echarts图表默认id 同一个页面多次引用当前组件 id不能相同
        };
      },
    },
  },
  setup(props,context) {
  const router = useRouter(),
    route = useRoute()
    let averageCheck=ref(false)//平均值flag
    const industryAvg=ref('')//平均值
    const formatTime=$formatTime
     const lineOpt = ref({});
    const chooseList=ref([0,1])//选中id
    const chooseListArr=ref([])//选中趋势图
    const flowList=ref([])//漏斗列表
    const trendList=ref([])//曲线图列表
    const chooseId=ref('0')
    let radioList=ref([])//占比
      let colors = [
      "#4CAF50",
      "#556FFD",
      "#91CC75",
      "#EA8533",
      "#283E81",
      "#097C38",
      "#48D9D9",
      "#93BEFF",
    ];
    let renderColors = colors;
    const initData = (res,arrline) => {
      let data = res;
      if (data && data.length > 0) {
        let xList = [];
        let seriesList = [];
        let maxArr=[]
        data.forEach((element, index) => {
          element.points=element.points||[]
          maxArr.push(element.points.length)
          let arrnew=[]
          // if(element.points.length>0){
             arrnew = element.points
            .map((obj) => {
              return obj.value;
            })
            .join(",")
            .split(",");
          // }     
          seriesList.push({
            name: element.title,
            type: "line",
            showSymbol: false,
            symbolSize: 6,
            seriesLayoutBy: "row",
            emphasis: { focus: "series" },
            data: [...arrnew],
            markLine : {
                symbol: ['none'],
                data : arrline?arrline:[],
                emphasis: {
                  lineStyle: {
                    width: 1,	// hover时的折线宽度
                  }
                },
            },
            lineStyle: {
              width: 1,
            },         
          });
        });
         let max=Math.max(...maxArr)
        let maxIndex=maxArr.map(item => item).indexOf(max)
        xList = data[maxIndex].points.map((item) => {
         return formatTime(item.date,'HH:mm');
        });
        lineOpt.value = {
          id: props.optIds.lineId,
          resize:true,
          options: {
            color: renderColors,
            title: {
              text: "",
            },
            legend: {
              icon: "circle",
              // selectedMode:'single',
              itemHeight: 6,
              itemWidth: 6,
              left: "0px",
              itemGap: 24,
              // top:'bottom',
              textStyle: {
                //图例文字的样式
                color: "#596076",
                fontSize: 14,
                padding: [0, 0, 0, 0], //文字与图形之间的左右间距
              },
              // data: ["签约汇总", "计划招募", "计划孵化"],
            },
            tooltip: {
              // 鼠标移入的展示
              trigger: "axis",
              // axisPointer: {
              //   type: "cross",
              //   label: {
              //     backgroundColor: "#6a7985",
              //   },
              // },
              formatter: function (params) {
                 let res = params[0].name+'分析数据\n';
                for (let i = 0; i < params.length; i++) {
                  res += `<div style="margin-top: 4px;font-size: 14px;line-height: 22px;color: #596076;">${
                    params[i].marker
                  } ${params[i].seriesName}:${thousandthis(
                    params[i].data
                  )}</div>`;
                }
                return res;
              },
              backgroundColor: "rgba(255,255,255,.9)",
              borderColor: "#E2E6F5",
              borderWidth: 1,
              padding: [12, 16, 16, 16],
            },
            grid: {
              // 图表距离容器的距离
              left: "1%",
              right: '4%',
              bottom: "3%",
              top:'22%',
              containLabel: true, // 是否显示刻度,默认不显示
            },
            xAxis: [
              {
                type: "category",
                boundaryGap: false,
                axisLabel: {
                  color: "#9095A7",
                  fontSize: 12,
                  margin: 13,
                },
                axisLine: {
                  lineStyle: {
                    color: "#DFE1EB",
                  },
                },
                axisTick: {
                  show: false,
                },
                data: xList,
              },
            ],
            yAxis: [
              {
                type: "value",
                // min: 0,
                // max: function (value) {
                //   return value.max < 400 ? 400 : value.max;
                // },
                // interval: 1000,
                // splitNumber: 4,
                axisLabel: {
                  color: "#9095A7",
                  formatter(v) {
                    return valueTransfer(Math.abs(v), 0, "w", true);
                  },
                },
                splitLine: {
                  lineStyle: {
                    type: "dashed", //虚线
                  },
                },
              },
            ],
            series: seriesList,
          },
        };
      }
    };
    //格式化选中线条
    const initChoosrArr=(arrList)=>{
      let arr=[]
      chooseList.value.forEach((ele)=>{
         arr.push(arrList[ele])
       })
      return arr
    }
   //获取漏斗列表
    const getList = () => {
      let param = {
        userId:props.opt.userId,
        roomId: props.opt.roomId,
      };
      // roomflow(param).then((res) => { 
        let res=    {
      "code": 200,
      "msg": "ok",
      "data": {
        "flowRank": {
            "name": "flowRank",
            "title": "测试数据",
            "items": [
                {
                    "title": "数据1",
                    "ratio": 1.0,
                    "value": 2833543
                },
                {
                    "title": "数据2",
                    "ratio": 0.12883587790974055,
                    "value": 365062
                },
                {
                    "title": "数据3",
                    "ratio": 0.85563822035709,
                    "value": 312361
                },
                {
                    "title": "数据4",
                    "ratio": 0.09972755881816232,
                    "value": 31151
                },
                {
                    "title": "数据5",
                    "ratio": 0.016532374562614364,
                    "value": 515
                }
            ],
        },
        "trends": [
            {
                "title": "数据1",
                "points": [
                    {
                        "value": 30000,
                        "date": "2023-07-02 09:25:00"
                    },
                    {
                        "value": 35000,
                        "date": "2023-07-02 09:30:00"
                    },
                    {
                        "value": 50000,
                        "date": "2023-07-02 09:35:00"
                    },
                    {
                        "value": 100000,
                        "date": "2023-07-02 09:40:00"
                    },
                    {
                        "value": 130003,
                        "date": "2023-07-02 09:45:00"
                    },
                    {
                        "value": 190000,
                        "date": "2023-07-02 09:50:00"
                    },
                    {
                        "value": 230000,
                        "date": "2023-07-02 09:55:00"
                    },
                    {
                        "value": 250000,
                        "date": "2023-07-02 10:00:00"
                    },
                ]
            },
            {
                "title": "数据2",
                "points": [
                    {
                        "value": 6000,
                        "date": "2023-07-02 09:25:00"
                    },
                    {
                        "value": 7000,
                        "date": "2023-07-02 09:30:00"
                    },
                    {
                        "value": 8000,
                        "date": "2023-07-02 09:35:00"
                    },
                    {
                        "value": 9000,
                        "date": "2023-07-02 09:40:00"
                    },
                    {
                        "value": 10000,
                        "date": "2023-07-02 09:45:00"
                    },
                    {
                        "value": 11000,
                        "date": "2023-07-02 09:50:00"
                    },
                    {
                        "value": 12000,
                        "date": "2023-07-02 09:55:00"
                    },
                    {
                        "value": 21810,
                        "date": "2023-07-02 10:00:00"
                    },
                ]
            },
            {
                "title": "数据3",
                "points": [
                    {
                        "value": 4500,
                        "date": "2023-07-02 09:25:00"
                    },
                    {
                        "value": 4700,
                        "date": "2023-07-02 09:30:00"
                    },
                    {
                        "value": 10000,
                        "date": "2023-07-02 09:35:00"
                    },
                    {
                        "value": 10214,
                        "date": "2023-07-02 09:40:00"
                    },
                    {
                        "value": 12000,
                        "date": "2023-07-02 09:45:00"
                    },
                    {
                        "value": 13000,
                        "date": "2023-07-02 09:50:00"
                    },
                    {
                        "value": 14000,
                        "date": "2023-07-02 09:55:00"
                    },
                    {
                        "value": 15000,
                        "date": "2023-07-02 10:00:00"
                    },
                ]
            },
            {
                "title": "数据4",
                "points": [
                    {
                        "value": 400,
                        "date": "2023-07-02 09:25:00"
                    },
                    {
                        "value": 800,
                        "date": "2023-07-02 09:30:00"
                    },
                    {
                        "value": 1100,
                        "date": "2023-07-02 09:35:00"
                    },
                    {
                        "value": 1200,
                        "date": "2023-07-02 09:40:00"
                    },
                    {
                        "value": 1400,
                        "date": "2023-07-02 09:45:00"
                    },
                    {
                        "value": 1600,
                        "date": "2023-07-02 09:50:00"
                    },
                    {
                        "value": 1800,
                        "date": "2023-07-02 09:55:00"
                    },
                    {
                        "value": 2000,
                        "date": "2023-07-02 10:00:00"
                    },
                ]
            },
            {
                "title": "数据5",
                "points": [
                    {
                        "value": 0,
                        "date": "2023-07-02 09:25:00"
                    },
                    {
                        "value": 2,
                        "date": "2023-07-02 09:30:00"
                    },
                    {
                        "value": 13,
                        "date": "2023-07-02 09:35:00"
                    },
                    {
                        "value": 14,
                        "date": "2023-07-02 09:40:00"
                    },
                    {
                        "value": 34,
                        "date": "2023-07-02 09:45:00"
                    },
                    {
                        "value": 40,
                        "date": "2023-07-02 09:50:00"
                    },
                    {
                        "value": 53,
                        "date": "2023-07-02 09:55:00"
                    },
                    {
                        "value": 63,
                        "date": "2023-07-02 10:00:00"
                    },
                ]
            }
        ],
        "industryAvg": 100
    }
}
        if (res.data) {
          if(res.data.flowRank.items){
            radioList.value=[]
          //格式漏斗右侧返回占比
          res.data.flowRank.items.forEach((ele,index) => {
            ele.id=index
            ele.newValue=millionTransfer(ele.value)
            if(index!==0){
              radioList.value.push(processingData(ele.ratio*100,2))
            }
          });
          // 漏斗左侧百分比计算
          radioList.value.push(processingData((res.data.flowRank.items[4].value/res.data.flowRank.items[2].value)*100,2))
          radioList.value.push(processingData((res.data.flowRank.items[4].value/res.data.flowRank.items[1].value)*100,2))          
          radioList.value.push(processingData((res.data.flowRank.items[4].value/res.data.flowRank.items[0].value)*100,2))
          }
          //绘制图表
          if(res.data.trends){
            chooseListArr.value=initChoosrArr(res.data.trends)
            initData(chooseListArr.value)
          }
          flowList.value=res.data.flowRank
          trendList.value=res.data.trends
          industryAvg.value=res.data.industryAvg
        }
      // });
    };
    //点击漏斗
    const changeFunnel=(val)=>{
      chooseId.value=val;
      if(val<5){
        chooseList.value=[val,val+1]
      }else if(val==5){
        chooseList.value=[2,4]
      }if(val==6){
        chooseList.value=[1,4]
      }if(val==7){
        chooseList.value=[0,4]
      }
      chooseListArr.value=initChoosrArr(trendList.value)
      // 先判断是否有平均线再重绘图表
      changeAverage(averageCheck.value)
    }
    //点击平均值 val=true有平均线
    const changeAverage=(val)=>{
      if(val){
        let arrline=[{
          symbol: "none",
          silent:false, //鼠标悬停事件 true没有,false有
          lineStyle:{ //警戒线的样式 ,虚实 颜色
                type:"dashed", //样式  ‘solid’和'dotted'
                color:"#E98433",
                width: 1   //宽度
            },
          label:{
            show:false,
            color:"#E98433",
            position:'middle',
            // padding: ['0', '0', '0',tableWidth.value],
            formatter: function (params) {
                let res = "";
                res += `${params.name}:${params.value}`;
                return res;
              },
          },
          name:'平均值',
          yAxis:industryAvg.value
        }]
        initData(chooseListArr.value,arrline)
      }else{
         initData(chooseListArr.value)
      }
    }
  watch(
    props,
    (newValue) => {
      console.log(newValue);
      if (newValue && newValue.opt && newValue.opt.roomId) {
          getList()
      }
    },
    {  deep: true }
  );
    onMounted(()=>{
      // getList()
    })
    return {
      flowList,
      chooseList,
      changeFunnel,
      chooseId,
      radioList,
       trendList,
      lineOpt,
      averageCheck,
      changeAverage
    }
  }
})
</script>

<style scoped lang="scss">
.funnel_wrap{
  margin-top: 24px;
  padding: 24px;
  color: #212848;
  font-size: 14px;
  background-color: #fff;
  .sec_title{
    font-size: 18px;
    font-weight: 500;
  }
  .funnel_con{
    padding: 24px;

  }
  .flow_con{
    position: relative;
    padding-left: 100px;
    padding-right: 112px;
    .left_position{
      position: absolute;
      top: 30px;
      left:0;
      .left_top{
        width: 100px;height: 198px;color:#DFE1EB;border:1px solid #DFE1EB;border-right:0;
      }
      .left_cen{
        width: 72px;height: 146px;position:absolute;left:24px;bottom:-0.5px;color:#DFE1EB;border:1px solid #DFE1EB;border-right:0;
      }
      .left_bot{
        width: 25px;height: 92px;position:absolute;left:72px;color:#DFE1EB;bottom:0px;border:1px solid #DFE1EB;border-right:0;
      }
      .left_title{
          position: absolute;
          line-height: 20px;
          top: -24px;
          right: 0;       
          color: #212848;
        }
      .leftactive{
        border:1px solid green;
        border-right:0;
        color: green;
         .left_title{
          color: green;
        }
      }

    }
    .flow_item{
      background-color:#F8F9FB ;
      margin-bottom: 12px;
      height: 40px;
      line-height: 40px;
      display: flex;
      align-items: center;
      position: relative;
      .item_lef{
        width: 116px;
        text-align: center;
        box-sizing: border-box;
      }
      .item_rig{padding-left: 16px;
      position: relative;
      flex: 1;
      .funnle{
        position: absolute;
        border-bottom:40px solid #fff;
        border-left: 12px solid transparent;
        right: 0;
        top: 0;
        }
      }
    }
     .active{
      .item_lef{background-color: green;color: #fff;}
      .item_rig{background-color: #EEF1FF;}
    }
    .svg_box{
      position: absolute;
      top: 25px;
      right: -10px;
      .title{
        position: absolute;
        left:0;
        top: 0;
      }
    }
  }
  .flow_echart{
    flex: 1;
  }
  .none{
    margin-top: 12px;
    color: #9095A7;
    text-align: center;
    }
}
// .svg-icon {
//   height: 3em;
// }
</style>

三、utils.js方法

export function millionTransfer(
  value,
  digits = 4,
  unit = "w",
  decimal = 2,
  removeZero = false
) {
  // unit = unit || "w"
  const valueNum = Number(value)
  const transferNum = Math.pow(10, digits)
  if (!isNaN(valueNum)) {
    if (valueNum < transferNum && valueNum >= 0) {
      return value
    }
    const num = floatDivideMethod(valueNum, transferNum)
    if (removeZero) {
      return `${parseFloat(num.toFixed(decimal))}${unit}`
    }
    return `${num.toFixed(decimal)}${unit}`
  }
  return value
}

export function thousandthis(num) {
  if (!num && num !== 0) return null
  if (num === '--') return '--'
  if (!(!isNaN(Number(num)) && typeof Number(num) === 'number')) {
    return '0'
  }
  return (num || 0).toString().replace(/\d+/, function(n) {
    const len = n.length
    if (len % 3 === 0) {
      return n.replace(/(\d{3})/g, ',$1').slice(1)
    }
    return n.slice(0, len % 3) + n.slice(len % 3).replace(/(\d{3})/g, ',$1')
  })
}

/* 最早的数据没有亿,只有万,兼容之前数据,后面转换万和亿的数据用这个方法 */
export function valueTransfer(value, decimal = 2, unit = "万", removeZero = false) {
  let outputVal = value
  const valueNum = Number(value)
  const transferNum1 = Math.pow(10, 4)
  const transferNum2 = Math.pow(10, 8)
  if (!isNaN(valueNum)) {
    if (valueNum < transferNum1) {
      outputVal = value
    } else if (valueNum >= transferNum1 && valueNum < transferNum2) {
      outputVal = millionTransfer(value, 4, unit, decimal, removeZero)
    } else {
      outputVal = millionTransfer(value, 8, "亿", decimal, removeZero)
    }
  }
  return outputVal
}

//保留两位小数
export function processingData(data,length){
  data=Number(data);
  data=Number((parseInt(data * 100) / 100).toFixed((length!=undefined?length:2)))
  data=data+''
  return data
}

import moment from "moment"
export function $formatTime (time, format = "YYYY-MM-DD HH:mm:ss") {
  if (time && time !== "--") {
    if (format === "timestamp") {
      return Number(moment(time).utcOffset(8).format("x"))
    }
    return moment(time).format(format)
  }
  return time
}

四、父组件调用

import flowFunnel from "./components/flowFunnel.vue";

  components:{
    flowFunnel,
  },

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值