vue+element-ui中实现多层级复杂的维度根据数据自动生成的表头

表头主要复杂在:
1,有三层,一层是大类,第二层是具体项,第三层是标准值/对比值
2,首列和末尾列是一层
3,整个表格的维度是根据数据的输入自己生成,也就是动态的

下面是在 vue + eleUI 中的解决方案:

<el-table :data="tableData" border style="width: 100%" max-height="700" v-loading="areaLoading" element-loading-text="拼命加载中">
    <el-table-column fixed label="IP" prop="ip" width="180"></el-table-column>
    <el-table-column v-for="(item, index) in itemList" :key="index" :label="item.label">
        <el-table-column :label="standard_name" v-for="(standard,standard_name) in item.standard" width="150">
            <el-table-column :label="standard" min-width="150">
                <template scope="scope">
                    <span>{{scope.row.list[item.label][standard_name]['value']}}</span>
                </template>
           </el-table-column>
        </el-table-column>
    </el-table-column>
    <el-table-column fixed="right" label="验收结果" width="100">
        <template scope="scope">
              <span :class="{'unqualified-color': !scope.row.result}">{{scope.row.result ? '合格' : '不合格'}}</span>
        </template>
    </el-table-column>

</el-table>


其对应data:
//整理出所有的label和标准对象的属性
itemList: [],
//实际数据

tableData:[]

《方法二》

vue+element
html配置:

< div id= "table" >{{tableData}}
< el-table : data= "tabledata01" : span-method= "tableSpanMethod" max-height= "420" >
< el-table-column v-for= 'item in tableConfig' : label= "item.label" : prop= 'item.prop' : width= 'item.width' : key= "item.id" >
< el-table-column v-if= 'item.children||item.children.length>0' v-for= "item1 in item.children"
: label= "item1.label" : prop= 'item1.prop' : width= 'item1.width' : key= "item1.id" >
< el-table-column v-if= 'item1.children||item1.children.length>0' v-for= "item2 in item1.children"
: label= "item2.label" : prop= 'item2.prop' : width= 'item2.width' : key= "item2.id" >
</ el-table-column >
</ el-table-column >
</ el-table-column >
</ el-table >
</ div >

data:
tableConfig:[
{id: 100,label: '一级表头',prop: '',width: '',children:[
{id: 110,label: '二级表头1',prop: 'districtName',width: ''},
{id: 120,label: '二级表头2',prop: 'timeDimension',width: ''}
]},
{id: 200,label: '一级表头',prop: '',width: '',children:[
{id: 210,label: '二级表头',prop: '',width: '',children:[
{id: 211,label: '三级表头',prop: 'residentPopNum',width: '110'},
{id: 212,label: '三级表头',prop: 'residentPopDst',width: '110'}
]}
]},
{id: 300,label: '一级表头',prop: '',width: '',children:[
{id: 310,label: '二级表头',prop: '',width: '',children:[
{id: 311,label: '三级表头',prop: 'liveLandArea',width: '110'},
{id: 312,label: '三级表头',prop: 'liveLandDst',width: '110'}
],
},
{id: 320,label: '二级表头',prop: '',width: '',children:[
{id: 321,label: '三级表头(km²)',prop: 'employmentLandArea',width: '110'},
{id: 322,label: '三级表头',prop: 'employmentLandDst',width: '110'}
],
}
]},
{id: 400,label: '一级表头',prop: '',width: '',children:[
{id: 410,label: '二级表头',prop: '',width: '',children:[
{id: 411,label: '三级表头(个)',prop: 'regionTrafficHubNum',width: '110'},
{id: 412,label: '三级表头(人次/km²)',prop: 'regionTrafficHubFlow',width: '140'}
],
},
{id: 420,label: '二级表头',prop: '',width: '',children:[
{id: 421,label: '三级表头(个)',prop: 'highSpeedNum',width: '110'},
{id: 422,label: '三级表头(个/km²)',prop: 'highSpeedDst',width: '140'}
],
},{id: 430,label: '二级表头',prop: '',width: '',children:[
{id: 431,label: '三级表头(个)',prop: 'trackTrafficSpotNum',width: '140'},
{id: 432,label: '三级表头(个/km²)',prop: 'trackTrafficSpotDst',width: '140'}
],
},
{id: 440,label: '二级表头',prop: '',width: '',children:[
{id: 441,label: '三级表头(km)',prop: 'trackTrafficNetNum',width: '110'},
{id: 442,label: '三级表头(km/km²)',prop: 'trackTrafficNetDst',width: '140'}
],
},
{id: 450,label: '二级表头',prop: '',width: '',children:[
{id: 451,label: '三级表头(个)',prop: 'cityTrafficHubNum',width: '110'},
{id: 452,label: '三级表头(个/km²)',prop: 'cityTrafficHubDst',width: '110'},
{id: 453,label: '三级表头(人次/km²)',prop: 'cityTrafficHubFlow',width: '140'}
],
},
{id: 460,label: '二级表头',prop: '',width: '',children:[
{id: 461,label: '三级表头(km)',prop: 'cityTrafficNetNum',width: '110'},
{id: 462,label: '三级表头',prop: 'cityTrafficNetDst',width: '140'}
],
},
]},
{id: 500,label: '一级表头',prop: '',width: '',children:[
{id: 510,label: '二级表头',prop: '',width: '',children:[
{id: 511,label: '三级表头(km²)',prop: 'pubServeLandArea',width: '110'},
{id: 512,label: '三级表头',prop: 'pubServeLandDst',width: '110'}
],
},
{id: 520,label: '二级表头',prop: '',width: '',children:[
{id: 521,label: '三级表头(个)',prop: 'hospitalResourcesNum',width: '110'},
{id: 522,label: '三级表头(km²)',prop: 'hospitalResourcesArea',width: '110'},
{id: 523,label: '三级表头(个/km²)',prop: 'hospitalResourcesDst',width: '110'}
],
},{id: 530,label: '二级表头',prop: '',width: '',children:[
{id: 531,label: '三级表头(个)',prop: 'schoolResourcesNum',width: '110'},
{id: 532,label: '三级表头(km²)',prop: 'schoolResourcesArea',width: '110'},
{id: 533,label: '三级表头(个/km²)',prop: 'schoolResourcesDst',width: '110'}
],
},
{id: 540,label: '二级表头',prop: '',width: '',children:[
{id: 541,label: '三级表头(个)',prop: 'humanResourcesNum',width: '110'},
{id: 542,label: '三级表头(个/km²)',prop: 'humanResourcesDst',width: '110'}
],
},
{id: 550,label: '二级表头',prop: '',width: '',children:[
{id: 551,label: '三级表头(个)',prop: 'businessResourcesNum',width: '110'},
{id: 552,label: '三级表头(个/km²)',prop: 'businessResourcesDst',width: '110'}
],
},
{id: 560,label: '二级表头',prop: '',width: '',children:[
{id: 561,label: '三级表头(个)',prop: 'environResourcesNum',width: '110'},
{id: 562,label: '三级表头(个/km²)',prop: 'environResourcesDst',width: '110'}
],
},
]},
{id: 600,label: '一级表头',prop: '',width: '',children:[
{id: 610,label: '二级表头',prop: '',width: '',children:[
{id: 611,label: '三级表头(分)',prop: 'populationScore',width: '110'},
],
},
{id: 620,label: '二级表头',prop: '',width: '',children:[
{id: 621,label: '三级表头(分)',prop: 'landScore',width: '110'},
],
},{id: 630,label: '二级表头',prop: '',width: '',children:[
{id: 631,label: '三级表头(分)',prop: 'trafficScore',width: '110'},
],
},
{id: 640,label: '二级表头',prop: '',width: '',children:[
{id: 641,label: '三级表头(分)',prop: 'communalFacilitiesScore',width: '110'},
],
},
{id: 650,label: '二级表头',prop: '',width: '',children:[
{id: 651,label: '三级表头(分)',prop: 'modelScore',width: '110'},
],
}
]},
],
注:动态生成表头必须配置"key"
view:

《elementUI使用指南》

开发环境


1.安装webpack

npm install -g webpack
  • 1

2.安装vue-cli

vue-cli是什么?

vue-cli 是vue.js的脚手架,用于自动生成vue.js模板工程的。

使用步骤:

  • 安装vue-cli:

    npm install -g vue-cli
    • 1
  • 使用vue-cli构建项目

    vue init webpack project-name  //创建一个基于webpack模板的名为project-name的项目
    • 1

    目前可用的模板包括:

    • browserify–全功能的Browserify + vueify,包括热加载,静态检测,单元测试。
    • browserify-simple–一个简易的Browserify + vueify,以便于快速开始。
    • webpack–全功能的Webpack + vueify,包括热加载,静态检测,单元测试。
    • webpack-simple–一个简易的Webpack + vueify,以便于快速开始。
  • 安装项目依赖

    cd project-name    //进入项目目录
    npm install        //安装项目依赖
    npm run dev        //运行项目
    • 1
    • 2
    • 3

此时在浏览器打开:localhost:8080即可看到欢迎页。

关于webpack和vue-cli的更多使用方法参见官方文档。

  • 但是这个只能在本地跑,要如何在我们自己的服务器上访问呢?此时需要执行

    npm run build
    • 1

搭建开发环境

本来想用vue-cli重新创建项目,试了几次总是出现各种问题,没办法成功。最后在仔细查看 Element-UI 的官方文档的 快速上手 部分的时候发现 饿了么 团队给了一个他们自己的 项目模板 。于是我就用这个模板来尝试了下,结果成功了。所以,如果你不想太折腾的话,建议还是使用官方给的项目模板,可以省很多事。

第一步:安装项目模板

  • 克隆/下载项目模板

  • 将下载的模板放到你项目的根目录下

  • 安装依赖

    npm install
    • 1
  • 运行项目模板

    npm run dev
    • 1

    此时在浏览器打开:localhost:8080即可看到欢迎页。

    欢迎页

项目模板里已经把需要配置的文件都配置好了。

第二步:安装element-ui

第一步,我们成功安装了项目模板,接下来,我们需要安装element-ui到项目下。

npm i element-ui@next -D
  • 1

开始使用

接下来我们就可以参照 Element-UI 的官方文档上手开发了。

例子

我们参照官方的按钮组件使用说明,在项目模板的基础上做一个按钮的例子。其它文件不需要改动,只修改App.vue文件的内容。代码如下:

App.vue

<template>
  <div id="app">
    <img src="./assets/logo.png">
    <h1>{{ msg }}</h1>
    <el-button @click.native="startHacking">Let's do it</el-button>
    <div class="block">
    <span class="demonstration">显示默认颜色</span>
    <span class="wrapper">
    <el-button type="success">成功按钮</el-button>
    <el-button type="warning">警告按钮</el-button>
    <el-button type="danger">危险按钮</el-button>
    <el-button type="info">信息按钮</el-button>
    </span>
    </div>
    <br/>
    <div class="block">
      <span class="demonstration">hover 显示颜色</span>
    <span class="wrapper">
    <el-button :plain="true" type="success">成功按钮</el-button>
    <el-button :plain="true" type="warning">警告按钮</el-button>
    <el-button :plain="true" type="danger">危险按钮</el-button>
    <el-button :plain="true" type="info">信息按钮</el-button>
    </span>
    </div>
  </div>
</template>
<script>
export default {
  data () {
    return {
      msg: 'Use Vue 2.0 Today!'
    }
  },

  methods: {
    startHacking () {
      this.$notify({
        title: 'It Works',
        message: 'We have laid the groundwork for you. Now it\'s your time to build something epic!',
        duration: 6000
      })
    }
  }
}
</script>

<style>
body {
  font-family: Helvetica, sans-serif;
}
</style>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40
  • 41
  • 42
  • 43
  • 44
  • 45
  • 46
  • 47
  • 48
  • 49
  • 50
  • 51
  • 52

效果如下图所示:

demo


使用过程中碰到的问题:

1. phantomjs安装失败

由于源的问题,安装phantomjs必须要“搭梯子”,使用内网无法下载。所以解决的方法有两种:

  • 方法一:通过科学上网,然后安装。

  • 方法二:对于不知道怎么“搭梯子”的同学,可以通过更改源来下载,操作方法如下:

    npm install phantomjs --phantomjs_cdnurl=http://cnpmjs.org/downloads
    • 1

2.打开页面乱码

通过 Element-UI 官方提供的项目模板开发,会发现在浏览器打开页面的时候,中文是乱码的。如下图所示:

中文乱码

html页面中已经设置了<meta chartset='utf-8'> 。

仔细查看该页面所涉及的文件的编码格式的时候,发现引用的App.vue 文件的编码格式是GBK ,所以把该文件编码格式改为UTF-8 即可。


使用VUE开发多级多选树形菜单组件

多级多选菜单组件 
要开发一个这样的多级多选菜单组件,功能是:

  • 点击父标题栏可以打开与折叠子列表
  • 点击父标题栏的勾选图标可以全选或取消子列表
  • 点击子列表的勾选图标达到全选时,父标题栏的勾选图标自动勾选;反之,没达到全选时,父标题栏的勾选图标自动取消勾选
  • 当所有父标题栏的勾选图标达到全选时,最底下那个全选框自动勾选;反之,没达到全选时,最底下那个全选框自动取消勾选
  • 点击最底下那个全选框可以全选或取消全部的勾选图标

    所以总结起来我们重点要利用子列表勾选了哪些项来计算出父标题与底下的全选框是自动勾选还是自动取消,并且父标题与底下的全选框在选择变化时子列表应有什么变化。要做到以上,实现过程是:

构建我们model层数据

datas : [
  {
    //用于判断是否展示子列表
    isShowListItem : false,
    //用于记录选中了哪些子项
    selected : [],
    //父标题
    listTitle : "保利南悦湾",
    //子列表
    listItem : [
      {
        id : 1,
        name : "李小龙"
      },
      {
        id : 2,
        name : "周星驰"
      },
      {
        id : 3,
        name : "周杰伦"
      }
    ]
  },
  {
    isShowListItem : false,
    selected : [],
    listTitle : "南庄万科城",
    listItem : [
      {
        id : 4,
        name : "大魔王"
      },
      {
        id : 5,
        name : "老妖怪"
      }
    ]
  }
]
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19
  • 20
  • 21
  • 22
  • 23
  • 24
  • 25
  • 26
  • 27
  • 28
  • 29
  • 30
  • 31
  • 32
  • 33
  • 34
  • 35
  • 36
  • 37
  • 38
  • 39
  • 40

记录子列表勾选了哪些项

子列表通过v-model=”data.selected”去双向绑定到selected数组中,当子列表项的checked发生变化时,就会把当前项的id记录到selected数组里去

<input 
type="checkbox"
:value="item.id"
v-model="data.selected"
>
  • 1
  • 2
  • 3
  • 4
  • 5

当父标题勾选变化时的处理方法 
自动处理父标题勾选图标的变化

在HTML里,通过绑定:checked=”isTitleChecked(data)”,这时,每当其他项变化时,父标题都会调用一下isTitleChecked这个方法去判断一下这时自己的checked状态是true还是false,从而达到根据子项选中数目不同,父标题自动变化的目的。 
通过@change=”changeTitleChecked(data,$event)”,每当父标题主动勾选或取消时触发 
父标题HTML

<input :id="data.listTitle"
  class="list-input"
  type="checkbox"
  :checked="isTitleChecked(data)"
  @change="changeTitleChecked(data,$event)"
>
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

父标题JS

  • changeTitleChecked方法:当主动触发父标题的勾选图标时,如果这次触发chaeked的状态是true,则要把子列表项中没选中的全部选中,即将它们全部加进selected数组中;如果是false,则要把子列表项全部取消选中,即清空selected数组。
  • isTitleChecked方法:当子列表项全部选中时自动勾选父标题。
/**
* 当父标题状态变化时的处理方法
* @param data  [当前项的data]
* @param event [当前项的event]
*/
changeTitleChecked : function (data,event) {
  if (event.target.checked === true) {
    data.listItem.forEach(function (item) {
    data.selected.indexOf(item.id) === -1 && data.selected.push(item.id);
    })
  }else {
    data.selected = [];
  }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
/**
* 判断父标题选择状态
* @param data        [当前项的data]
* @returns {boolean}
*/
isTitleChecked : function (data) {
  var _selected = data.selected;
  var _listItem = data.listItem;
  // 验证selected中是否含有全部的item的id 如果是 证明title要选中
  return _listItem.every(function (item) {
   return _selected.indexOf(item.id) != -1;
  });
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13

当底下的全选框变化时的处理方法 
自动处理底下的全选框的变化

全选框HTML:

<input id="all-checked"
  type="checkbox"
  :checked="isAllChecked()"
  @change="changeAllChecked($event)"
>
  • 1
  • 2
  • 3
  • 4
  • 5

全选框JS:

  • changeAllChecked方法:当主动触发全选框时,如果checked为true,则将全部的子项都放进selected数组里;反之则清空全部selected数组。
  • isAllChecked 方法:判断selected数组的长度是否等于全部子项数,如果相等,则全选框checked状态设为true。
/**
* 全选框change事件的回调处理方法
* @param event 
*/
changeAllChecked : function (event) {
  if (event.target.checked === true) {
    this.datas.forEach(function (data) {
      data.listItem.forEach(function (item) {
      data.selected.indexOf(item.id) === -1 && data.selected.push(item.id);
    })
  })
}else {
      this.datas.forEach(function (data) {
      data.selected = [];
    })
  }
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
/**
* 判断全选框选择状态
* @returns {boolean}
*/
isAllChecked : function () {
    return this.datas.every(function (data) {
        return data.selected.length === data.listItem.length;
    });
}


vue组件库的总结

UI组件

开发框架

实用库

服务端


  • nuxt.js - 用于服务器渲染Vue app的最小化框架
  • express-vue - 简单的使用服务器端渲染vue.js
  • vue-ssr - 非常简单的VueJS服务器端渲染模板
  • vue-ssr - 结合Express使用Vue2服务端渲染
  • vue-easy-renderer - Nodejs服务端渲染

辅助工具

应用实例

Demo示例

vue的组件开发手风琴菜单实例

小图标是引入font-awesome字体图标库绘制的。效果如下图所示: 
这里写图片描述

<!DOCTYPE html>
<html lang="en">

<head>
    <meta charset="UTF-8">
    <title>index</title>
    <link rel="stylesheet" href="css/font-awesome.min.css">
    <link rel="stylesheet" href="css/index.css">
    <script type="text/javascript" src="../lib/vue.min.js"></script>
    <script type="text/javascript" src="../lib/jquery-1.11.3.min.js"></script>
    <style>
        * {
            margin: 0;
            padding: 0;
        }

        body {
            padding-top: 100px;
        }

        #tabPanel {
            width: 120px;
            height: auto;
            margin: 0 auto;
        }

        #tabPanel .item .sildermun ul li {
            height: 40px;
            line-height: 40px;
            list-style: none;
        }

        #tabPanel .item .sildermun ul li:hover {
            background: #ccc;
        }

        #tabPanel .item .menutitle {
            height: 40px;
            line-height: 40px;
            text-align: center;
            background: #ccc;
        }

        #tabPanel .item .sildermun {
            text-align: center;
            background: #eee;
        }
    </style>
</head>

<body>

    <div id="tabItem">
        <slider-item></slider-item>
        <slider-item></slider-item>
        <slider-item></slider-item>
        <slider-item></slider-item>
    </div>

    <!--组件模板,也可以使用template标签方式引入模板-->
    <template id="tab">
        <div id="tabPanel">
            <div class="item">
                <div class="menutitle" @click="toggle()"><i class="icon-th-list"></i>&nbsp;{{parentItem}}</div>
                    <div class="sildermun" v-show="status">
                        <ul>
                            <li v-for="(index,v) in childItems"><i class="icon-star"></i>&nbsp;{{v+index}}</li>
                        </ul>
                    </div>
                </div>
            </div>
        </div>
    </template>
    <!--组件模板,也可以使用template标签方式引入模板-->
</body>

</html>
<script>
    var vue = new Vue({
        el: "#tabItem",
        data: {

        },
        components: {
            'slider-item': {
                template: '#tab',
                data: function() {
                    return {
                        status: false,
                        parentItem: "父级菜单",
                        childItems: ["子级菜单", "子级菜单", "子级菜单", "子级菜单"]
                    }
                },
                methods: {
                    //切换滑块
                    toggle: function() {
                        this.status = !this.status;
                    }
                }
            }
        }
    });
</script>

猜你喜欢

转载自blog.csdn.net/weixin_41646716/article/details/80410676
今日推荐