首页 前端知识 数据可视化仪表板,vue-grid-layout echarts

数据可视化仪表板,vue-grid-layout echarts

2024-06-16 09:06:14 前端知识 前端哥 426 271 我要收藏

背景

我们经常在网上看到各种绚烂的数据展示仪表板,例如:
在这里插入图片描述

那我们如何实现这种效果了?
本文将讲叙如何利用vue-grid-layout和echarts制作数据可视化仪表板。
这是最终的效果图,
在这里插入图片描述

工具

vue-grid-layout:基于vue的栅格拖动布局组件。
echarts:一款用于图表可视化的插件,可以用来制作各种图表。
JavaScript:

代码

说明:以下代码只供参考,由于进行了一部分删减调整,直接拷贝肯定是不能够运行的。

<!DOCTYPE html>
<html>
<head>
    <title>仪表板</title>

    <script src="./js/vue.js" type="text/javascript"></script>
    <script src="./js/vue-grid-layout.umd.min.js" type="text/javascript"></script>
    <script src="./js/echarts.min.js"></script>
    <script src="./js/chart-style.js"></script>

    <script>

        var board = new Vue({
            el: '#dashboard',
            data: {
                editFlag: true,  //是否是编辑态
                layout: [],  //初始化卡片数组
                layoutMap: [],  //卡片二维地图
                resizeTimer: null,
                layoutColNum: 12 //列
            },
            methods:{
                layoutReadyEvent:function(){
                    this.initBoard();
                    this.layoutMap = this.genereatePlaneArr(this.layout);
                },
                // 当插件内容布局发生变化后  获取现在的二维地图树
                layoutUpdatedEvent: function() {
                    // console.log("Updated");
                    this.layoutMap = this.genereatePlaneArr(this.layout);
                },
                //调整卡片大小
                resizedEvent:function (index) {
                    if (this.resizeTimer) clearTimeout(this.resizeTimer);
                    var _this = this;
                    this.resizeTimer = setTimeout(function () {
                        var mychart = _this.layout[index]['myChart'];
                        mychart.resize();
                    }, 100);
                },
                //调整卡片大小
                windowResizeEvent:function(){
                    if (this.resizeTimer) clearTimeout(this.resizeTimer);
                    var _this = this;
                    this.resizeTimer = setTimeout(function () {
                        for(var i = 0; i<_this.layout.length; i++){
                            var mychart = _this.layout[i]['myChart'];
                            mychart.resize();
                        }
                    }, 100);
                },
                //仪表板初始化
                initBoard:function(){
                    for(var i = 0; i<this.layout.length; i++){
                        this.queryChartData(i, i*200);
                    }
                },
                //删除卡片
                deleteChartUnit:function (index) {
                    this.layout.splice(index,1);
                },
                //修改卡片参数
                editChartUnit:function(index){
                    // console.log(index);
                    this.layout[index].paramList = newParamList;
                    this.layout[index].showFlag = "0";
                    this.$nextTick(() => {this.queryChartData(index, 0);});
                },
                //添加卡片
                addChartUnit:function(){
                    var item = {};
                    item.chartUnitCname = "图一";
                    item.chartType = "line";
                    item.chartData = [];
                    item.showFlag = '0';
                    item.x = 0;
                    item.y = 0;
                    item.w = 6;
                    item.h = 4;

                    item.i = uuid(32, 36);

                    var itemW = item.w;
                    var itemH = item.h;
                    var addItem = item;
                    if(this.layoutMap.length){
                        // console.log(this.layoutMap.length);
                        for(let r = 0 , rLen =this.layoutMap.length ; r < rLen; r++){
                            for(let c = 0; c <= (this.layoutColNum-itemW); c++){
                                let res = this.regionalTest(c, r, itemW,rLen>(r+itemH)?itemH:rLen-r );
                                if(res.result){
                                    // 更新添加数据内容
                                    addItem.x = res.x;
                                    addItem.y = res.y;
                                    c = this.layoutColNum+1;
                                    r = rLen+1;
                                }else{
                                    c = res.offsetX;
                                }
                            }
                        }
                    }
                    // 更新二维数组地图
                    for(let itemR = 0 ; itemR < itemH ; itemR++){
                        for(let itemC = 0 ; itemC < itemW ; itemC++){
                            // 如果没有该行,初始化
                            if(!this.layoutMap[addItem.y+itemR]){
                                this.layoutMap[addItem.y+itemR] = new Array(this.layoutColNum);
                                for(let i = 0 ;i < this.layoutColNum ; i++){
                                    this.layoutMap[addItem.y+itemR][i] = 0;
                                }
                            }
                            // 标记点
                            this.layoutMap[addItem.y+itemR][addItem.x+itemC] = 1;
                        }
                    }

                    // console.log(this.layoutMap);
                    // 添加数据
                    this.layout.push(addItem);
                    this.$nextTick(() => {this.queryChartData(this.layout.length-1, 0);});
                },
                //获取后台数据
                queryChartData : function (index, time) {
                    setTimeout( () => {
                        //防止同时请求对后台造成压力
                        this.layout[index].chartData = {"dataX":['Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat', 'Sun'],
                                                        "dataY":[150, 230, 224, 218, 135, 147, 260]};
                        this.drawChart(index);
                    }, time);
                },
                //绘图-echarts
                drawChart:function (index) {
                    var elementId = this.layout[index].i;
                    var chartData = this.layout[index].chartData;
                    if(chartData.status == 1 ){
                        this.layout[index].showFlag = "1";
                        this.$nextTick(() => {
                            var chartType = this.layout[index].chartType;
                            var myChart = echarts.init(document.getElementById(elementId));
                            myChart.clear();
                            if(chartType === 'line'){
                                var option = {
                                    xAxis: {
                                        type: 'category',
                                        data: chartData.dataX
                                    },
                                    yAxis: {
                                        type: 'value'
                                    },
                                    series: [
                                        {
                                            type: 'line'
                                        }
                                    ]
                                };
                                //设置样式
                                myChart.setOption(option);
                                //设置数据
                                myChart.setOption({
                                    series: {
                                        data:  chartData.dataY
                                    }
                                });
                            }
                            this.layout[index]['myChart'] = myChart;
                        })
                    }else {
                        this.layout[index].showFlag = "-1";
                    }
                },
                //卡片地图
                regionalTest: function (x,y,w,h) {
                    // 定义返回 x,y 偏移 及 是否有空位置
                    let offsetX = 0,offsetY = 0,res = true;
                    // 按区域循环检测 二维数组地图
                    for(let r = 0; r < w ;r++){
                        for(let c = 0; c <= h ;c++){
                            let point = this.layoutMap[y+r]?this.layoutMap[y+r][x+c]:0;
                            // 如该点被占据 记录偏移值
                            if(point===1){
                                res = false;
                                offsetX = offsetX>(x+c)?offsetX:x+c;
                                offsetY = offsetY>(y+r)?offsetY:y+r;
                            }
                        }
                    }

                    return {
                        result: res,
                        offsetX: offsetX,
                        x: x,
                        y: y
                    };
                },
                //卡片地图
                genereatePlaneArr: function (data) {
                    var map = [];
                    if(Array.isArray(data)){
                        for(var i = 0; i<data.length; i ++){
                            var one = data[i];
                            // 循环行
                            for(var r = one.y ; r < ( one.y + one.h ) ; r++){
                                // 循环列
                                for(var c = one.x ; c < ( one.x + one.w) ; c++){
                                    // 检修当前行是否存在
                                    if(!map[r]){
                                        map[r] = new Array(this.layoutColNum);

                                        for(let i = 0 ; i < this.layoutColNum ; i++){
                                            map[r][i] = 0;
                                        }
                                    }
                                    // 占据为1
                                    map[r][c] = 1;
                                }
                            }
                        }
                    }
                    return map;
                },
                //比较卡片参数是否发生调整
                compareParams: function (oldParamList, newParamList ) {
                    if(oldParamList.length == 0){
                        return true;
                    }else {
                        for (var i=0; i<oldParamList.length; i++){
                            if(oldParamList[i].value != newParamList[i].value){
                                return false;
                            }
                        }
                        return true;
                    }
                }
            }

        });

        //页面窗口改变图大小调整
        window.onresize = function(){
            board.windowResizeEvent();
        };

        //uuid
        function uuid(len, radix) {
            var  chars =  '0123456789abcdefghijklmnopqrstuvwxyz'.split( '');
            var  uuid = [], i;
            radix = radix || chars.length;

            if  (len) {
                // Compact form
                for  (i = 0; i < len; i++) uuid[i] = chars[0 | Math.random()*radix];
            }  else  {
                // rfc4122, version 4 form
                var  r;

                // rfc4122 requires these characters
                uuid[8] = uuid[13] = uuid[18] = uuid[23] =  '-' ;
                uuid[14] =  '4' ;

                // Fill in random data.  At i==19 set the high bits of clock sequence as
                // per rfc4122, sec. 4.1.5
                for  (i = 0; i < 36; i++) {
                    if  (!uuid[i]) {
                        r = 0 | Math.random()*16;
                        uuid[i] = chars[(i == 19) ? (r & 0x3) | 0x8 : r];
                    }
                }
            }

            return  uuid.join( '' );
        }

    </script>

    <style>

        .isEdit {
            background: #efefef;
            background-image: linear-gradient(0deg, #f8f8f8 10px, transparent 0.1em), linear-gradient(90deg, #f8f8f8 10px, transparent 0.1em);
            background-size: calc(8.33333% - 2px) 100px;
            background-position-y: 10px;
            background-attachment: local, scroll;
            overflow-y: scroll;
        }

        .dashboard_content {
            align-self: stretch;
            height: 0;
            flex: 1 1 auto;
            overflow-y: auto;
            overflow-x: hidden;
            position: relative;
        }

        .vue-grid-layout > div {
            box-shadow: 1px 1px 5px #e3e3e3;
            background: #FFF;
        }

        .card {
            width: 100%;
            height: 100%;
            display: flex;
            flex-flow: column nowrap;
        }

        .card-top {
            margin-top: 10px;
            font-size: 16px;
            flex-basis: 20px;
            display: flex;
        }

        .card-title {
            float: left;
            margin-left: 8px;
            margin-right: 8px;
            flex: 1;
        }

        .card-top-icon {
            cursor: pointer;
            width: 20px;
            margin-right: 8px;
        }

        .card-title-content {
            white-space: nowrap;
            overflow: hidden;
            display: inline-block;
            text-overflow: ellipsis;
        }

        .card-body {
            width: 100%;
            flex: 1;
        }


        .loading {
            width: 100px;
            height: 50px;
            margin: 0 auto;
            margin-top: 100px;
        }

        .loading span {
            display: inline-block;
            width: 15px;
            height: 15px;
            margin-right: 5px;
            border-radius: 50%;
            background: lightblue;
            -webkit-animation: load 1.04s ease infinite;
        }

        .loading span:last-child {
            margin-right: 0px;
        }

        @-webkit-keyframes load {
            0% {
                opacity: 1;
            }
            100% {
                opacity: 0;
            }
        }

        .loading span:nth-child(1) {
            -webkit-animation-delay: 0.13s;
        }

        .loading span:nth-child(2) {
            -webkit-animation-delay: 0.26s;
        }

        .loading span:nth-child(3) {
            -webkit-animation-delay: 0.39s;
        }

        .loading span:nth-child(4) {
            -webkit-animation-delay: 0.52s;
        }

        .loading span:nth-child(5) {
            -webkit-animation-delay: 0.65s;
        }

    </style>

</head>
<body>

    <div style="width: 100%;height:100%;">
    <div id="dashboard" style="height: 100%;display: none;" v-bind:class="{ isEdit: editFlag }">
        <grid-layout
                :layout.sync="layout"
                :col-num="12"
                :row-height="90"
                :is-draggable="editFlag"
                :is-resizable="editFlag"
                :is-mirrored="false"
                :vertical-compact="true"
                :margin="[10, 10]"
                :use-css-transforms="true"
                @layout-updated="layoutUpdatedEvent"
                @layout-ready="layoutReadyEvent">

            <grid-item v-for="(item,index) in layout"
                       :x="item.x"
                       :y="item.y"
                       :w="item.w"
                       :h="item.h"
                       :i="item.i"
                       :max-w="12"
                       :key="item.i"
                       @resized="resizedEvent(index)">

                <div class="card">
                    <div class="card-top">
                        <div class="card-title card-title-content">&nbsp;{{item.chartUnitCname}}</div>
                        <div v-if="editFlag" class="card-top-icon" @click="editChartUnit(index)">
                            <i class="fa fa-cog" aria-hidden="true" title="编辑"
                               style="margin-right: 10px;"></i>
                        </div>
                        <div v-if="editFlag" class="card-top-icon" @click="deleteChartUnit(index)"
                             style='float:right;cursor:pointer'>
                            <i class="fa fa-trash" aria-hidden="true" title="删除"
                               style="margin-right: 10px;"></i>
                        </div>
                    </div>
                    <div class="card-body">
                        <div v-show="item.showFlag=='0'" class="loading">
                            <span></span><span></span><span></span><span></span><span></span></div>
                        <div v-show="item.showFlag=='1'" :id="item.i"
                             style="width: 100%;height: 100%"></div>
                        <div v-show="item.showFlag=='-1'" style="text-align: center;font-size: 14px;">
                            {{item.msg}}
                        </div>
                    </div>
                </div>

            </grid-item>
        </grid-layout>
    </div>
</div>

</body>
</html>

其它

卡片地图位置信息维护参考了网上前辈代码,找了好久没有找到该篇文章,就没贴文章链接了。。。

转载请注明出处或者链接地址:https://www.qianduange.cn//article/12383.html
标签
评论
发布的文章

JQuery中的load()、$

2024-05-10 08:05:15

大家推荐的文章
会员中心 联系我 留言建议 回顶部
复制成功!