Bootstrap

vue中用Element-ui封装表单(form)和表格(table)组件动态渲染数据展示

老样子,先来个效果图

查询表单数据和表格的列信息以及表格数据都是从后端拿到后自动渲染成下面的格式,
在这里插入图片描述

一、先创建表格和表单组件

下面的form和table组件,可以直接复制使用的,如果你需要!

form.vue
<template>
<div class="ces-search">
    <el-form class="form" ref="refForm"  :size="size" inline :label-width="labelWidth">
        <el-form-item v-for='item in searchForm' :label="item.label" :key='item.prop' class="formItem">
            <!-- 输入框 -->
            <el-input v-if="item.type==='Input'"  v-model="searchData[item.prop]"  size="mini"  :style="{width:item.width}"></el-input>
            <!-- 下拉框 -->
            <el-select v-if="item.type==='Select'" v-model="searchData[item.prop]" size="mini" @change="item.change(searchData[item.prop])"  :style="{width:item.width}">
                <el-option v-for="op in item.options" :label="op.label" :value="op.value" :key="op.value"></el-option>
            </el-select>
            <!-- 单选 -->
            <el-radio-group v-if="item.type==='Radio'" v-model="searchData[item.prop]">
                <el-radio v-for="ra in item.radios" :label="ra.value" :key="ra.value">{{ra.label}}</el-radio>
            </el-radio-group>
            <!-- 单选按钮 -->
            <el-radio-group v-if="item.type==='RadioButton'" v-model="searchData[item.prop]" @change="item.change && item.change(searchData[item.prop])">
                <el-radio-button v-for="ra in item.radios" :label="ra.value" :key="ra.value">{{ra.label}}</el-radio-button>
            </el-radio-group>
            <!-- 复选框 -->
            <el-checkbox-group v-if="item.type==='Checkbox'" v-model="searchData[item.prop]" >
                <el-checkbox v-for="ch in item.checkboxs" :label="ch.value" :key="ch.value">{{ch.label}}</el-checkbox>
            </el-checkbox-group>
            <!-- 日期 -->
            <el-date-picker v-if="item.type==='Date'" v-model="searchData[item.prop]" ></el-date-picker>
            <!-- 时间 -->
            <el-time-select v-if="item.type==='Time'" v-model="searchData[item.prop]" type=''></el-time-select>
            <!-- 日期时间 -->
            <el-date-picker v-if="item.type==='DateTime'" type='datetime' v-model="searchData[item.prop]" :disabled="item.disable && item.disable(searchData[item.prop])"></el-date-picker>
             <!-- 日期范围 -->
            <el-date-picker v-if="item.type==='datetimerange'" type='datetimerange' v-model="searchData[item.prop]"  range-separator="至"  start-placeholder="开始日期" end-placeholder="结束日期"></el-date-picker>
            <!-- 滑块 -->
            <!-- <el-slider v-if="item.type==='Slider'" v-model="searchData[item.prop]"></el-slider> -->
            <!-- 开关 -->
            <el-switch v-if="item.type==='Switch'" v-model="searchData[item.prop]" ></el-switch>
        </el-form-item>
    </el-form>
    <el-form class="formT"  inline v-if='isHandle'>
        <el-form-item v-for='(item , index) in searchHandle' :key="index">
            <el-button :type="item.type" :size="item.size || size" @click='item.handle()'>{{item.label}}</el-button>
        </el-form-item>
    </el-form>
</div>
</template>

<script>
export default {
    props:{
        isHandle:{
            type:Boolean,
            default:true
        },
        labelWidth:{
            type:String,
            default:'50px'
        },
        size:{
            type:String,
            default:'mini'
        },
        searchForm:{
            type:Array, 
            default:[]
        },
        searchHandle:{
            type:Array,
            default:()=>[]
        },
        searchData:{
            type:Object,
            default:{}
        }
    },
    data () {
        return {
        };
    },
    methods:{
      
    }

}

</script>
<style lang="scss" >
 .ces-search{
    display: flex;
    justify-content: space-between;
    .formItem{
        .el-form-item__label{
            width: 80px !important;
        }
    }
 }


</style>
table.vue
<!--表格组件 -->
<template>
<section class="ces-table-page">
  <!-- 表格操作按钮 -->
    <section class="ces-handle" v-if='isHandle'>
      <el-button v-for='(item , index) in tableHandles' :size="item.size || size" :type="item.type" :icon='item.icon' @click="item.handle()" :key="index">{{item.label}}</el-button>
    </section>
    <!-- 数据表格 -->
    <section class="ces-table">
        <el-table 
         :data='tableData' 
         :size='size'
          :border  ='isBorder'
          @select='select' 
          @select-all='selectAll'
          v-loading='loading' 
          header-row-class-name="header_row_style" 
          :defaultSelections='defaultSelections'
          ref="cesTable">
            <el-table-column v-if="isSelection" type="selection" align="center" width="50" ></el-table-column>
            <el-table-column v-if="isIndex" type="index" :label="indexLabel" align="center" width="50"></el-table-column>
            <!-- 数据栏 -->
            <el-table-column v-for="item in tableCols" 
              :key="item.id"
              :prop="item.prop" 
              :label="item.label" 
              :width="item.width"
              :align="item.align" 
               show-overflow-tooltip
              :render-header="item.require?renderHeader:null"
              >
                <template slot-scope="scope" >
                  <!-- html -->
                  <span v-if="item.type==='Html'" v-html="item.html(scope.row)"></span>
                  <!-- 按钮 -->
                  <span v-if="item.type==='Button'" >
                    <el-button v-for="(btn , index)  in item.btnList" 
                      :disabled="btn.isDisabled && btn.isDisabled(scope.row)"
                      :type="btn.type" 
                      :size="btn.size || size" 
                      :icon="btn.icon" 
                      :key="index"
                      @click="btn.handle(scope.row)">{{btn.label}}</el-button>
                    </span>
                  <!-- 输入框 -->
                  <el-input v-if="item.type==='Input'" v-model="scope.row[item.prop]" :size="size" 
                    :disabled="btn.isDisabled && btn.isDisabled(scope.row)"
                    @focus="item.focus && item.focus(scope.row)"></el-input>
                  <!-- 下拉框 -->
                  <el-select v-if="item.type==='Select'" v-model="scope.row[item.prop]" :size="size" :props="item.props"
                    :disabled="btn.isDisabled && btn.isDisabled(scope.row)" 
                    @change='item.change && item.change(scope.row)'>
                      <el-option v-for="op in item.options" :label="op[item.props.label]" :value="op[item.props.value]" :key="op[item.props.value]"></el-option>
                  </el-select>
                  <!-- 单选 -->
                  <el-radio-group v-if="item.type==='Radio'" v-model="scope.row[item.prop]"
                    :disabled="btn.isDisabled && btn.isDisabled(scope.row)"
                    @change='item.change && item.change(scope.row)'>
                      <el-radio v-for="(ra , index) in item.radios" :label="ra.value" :key="index">{{ra.label}}</el-radio>
                  </el-radio-group>
                  <!-- 复选框 -->
                  <el-checkbox-group v-if="item.type==='Checkbox'" v-model="scope.row[item.prop]" 
                    :disabled="btn.isDisabled && btn.isDisabled(scope.row)"
                    @change='item.change && item.change(scope.row)'>
                      <el-checkbox v-for="(ra ,index) in item.checkboxs" :label="ra.value" :key="index">{{ra.label}}</el-checkbox>
                  </el-checkbox-group>
                  <!-- 评价 -->
                  <el-rate v-if="item.type==='Rate'" v-model="scope.row[item.prop]"
                    :disabled="btn.isDisabled && btn.isDisabled(scope.row)"
                    @change='item.change && item.change(scope.row)'></el-rate>
                  <!-- 开关 -->
                  <el-switch v-if="item.type==='Switch'" v-model="scope.row[item.prop]"
                    :disabled="btn.isDisabled && btn.isDisabled(scope.row)"
                    @change='item.change && item.change(scope.row)'></el-switch>
                  <!-- 图像 -->
                  <img v-if="item.type==='Image'" :src="scope.row[item.prop]" @click="item.handle && item.handle(scope.row)"/>
                  <!-- 滑块 -->
                  <el-slider v-if="item.type==='Slider'" v-model="scope.row[item.prop]" 
                  :disabled="btn.isDisabled && btn.isDisabled(scope.row)"
                    @change='item.change && item.change(scope.row)'></el-slider>
                  <!-- 默认 -->
                  <span v-if="!item.type" 
                    :style="item.itemStyle && item.itemStyle(scope.row)" 
                    :class="item.itemClass && item.item.itemClass(scope.row)">{{(item.formatter && item.formatter(scope.row)) || scope.row[item.prop]}}</span>
                </template>
              </el-table-column>
        </el-table>
    </section>
    <!-- 分页 -->
    <section class="ces-pagination"  v-if='isPagination'>
        <el-pagination style='display: flex;justify-content: center;height: 100%;align-items: center;'
            @current-change="handleCurrentChange"
            @size-change="handleSizeChange"
            layout="total,sizes ,prev, pager, next,jumper"
            :page-size="pagination.pageSize"
            :current-page="pagination.pageNum"
            :total="pagination.total"
        ></el-pagination>
    </section>
</section>
</template>

<script>

export default {
  props:{
    // 表格型号:mini,medium,small
    size:{type:String,default:'medium'},
    isBorder:{type:Boolean,default:true},
    loading:{type:Boolean,default:false},
    // 表格操作
    isHandle:{type:Boolean,default:false},
    tableHandles:{type:Array,default:()=>[]},
    // 表格数据
    tableData:{ type:Array,default:()=>[]},
    // 表格列配置
    tableCols:{ type:Array,default:()=>[]},
    // 是否显示表格复选框
    isSelection:{type:Boolean,default:false},
    defaultSelections:{ type:[Array,Object], default:()=>null},
    // 是否显示表格索引
    isIndex:{type:Boolean,default:false},
    indexLabel: {type:String,default:'序号'},
    // 是否显示分页
    isPagination:{type:Boolean,default:true},
    // 分页数据
    pagination:{ type:Object,default:()=>({pageSize:10,pageNum:1,total:0})},
    
  },
  data(){
    return {
    }
  },
  watch:{
    'defaultSelections'(val) {
        this.$nextTick(function(){
          if(Array.isArray(val)){
            val.forEach(row=>{
              this.$refs.cesTable.toggleRowSelection(row)
            })
          }else{
            this.$refs.cesTable.toggleRowSelection(val)
          }
        })      
    }
  },
  methods:{
    // 表格勾选
    select(rows,row){
      this.$emit('select',rows,row);
    },
    // 全选
    selectAll(rows){
      this.$emit('select',rows)
    },
    // 
    handleCurrentChange(val){
      this.pagination.pageNum = val;
      this.$emit('refresh');
    },
    handleSizeChange(val) {
      this.pagination.pageSize = val;
      this.$emit('refresh');
    },
    
    // tableRowClassName({rowIndex}) {
    //     if (rowIndex % 2 === 0) {
    //         return "stripe-row";
    //     }
    //     return "";
    // }
    renderHeader(h,obj) {
      return h('span',{class:'ces-table-require'},obj.column.label)
    },
  },
}
</script>
<style>
.ces-table-require::before{
  content:'*';
  color:red;
}
.el-pagination{
  float: right;
  margin-right: 10px;
  margin-top: 5px;
}
</style>
二、引用组件

下面是我项目的代码,有些多余的代码我就不删除了,,我会在代码里备注每个代码块的作用,也方便自己清楚。

<template> 
    <div class="ces-main">
        <search-form 
        ref="form"
        size='mini'
        labelWidth = '50px' 
        :searchData = "searchData"
        :searchForm = "searchForm"
        :searchHandle="searchHandle"></search-form>
        <ces-table 
        size='mini'
        :isSelection='true'
        :isIndex='true'
        :isPagination='true'
        :isHandle='true'
        :tableData='tableData' 
        :tableCols='tableCols' 
        :pagination='pagination'
        @refresh="handleCurrentChange"
        >
        </ces-table>
        </div>
</template>

<script>
import {
    getTableAndForm, //获取资产目录详情下的数据展示的动态表格和表单
} from '@/api/assetsManage/table/index.js';
import {
   getQuery //查询接口
} from '@/api/assetsManage/assets/index.js';

//引入组件
import SearchForm from '@/components/form.vue'
import cesTable from '@/components/table.vue'

export default {
    data(){
         //表单查询一般分为input,下拉框,和日期,下面是下拉框时的设置下拉信息的情况,我的项目没有用到,但是留着方便看
       //let sexs=[{label:'男',value:'M'},{label:'女',value:'F'}]
      // let sexProps={label:'label',value:'value'}
        return {
             searchData:{ //查询表单的对应的值
                // name:'',
                // sex:null,
            },
            searchForm:[ //这里是渲染查询表单的表头和类型的数据
                // {type:'Input',label:'姓名',prop:'name', width:'180px',placeholder:'请输入姓名...'},
                // {type:'Select',label:'性别',prop:'sex',width:'180px',options:sexs,props:sexProps,change:row=>'',placeholder:'请选择性别...'},
            ],
            searchHandle:[ //查询和重置按钮
                {label:'查询',type:'primary',handle:this.handleQuery},
                {label:'重置',type:'primary',handle:this.handleReset}
            ],
            apiId:'null',
            fieldList:[],
            // 表格
            tableData:[],//表格数据
            tableCols:[ //表格列数据
                // {label:'姓名',prop:'name'},
                // {label:'年龄',prop:'age'},
            ],
           // tableHandles:[  //这是表格和表单之间的一个按钮,我的项目不需要
            //    {label:'新增',type:'primary',handle:row=>''}
           // ],
            pagination:{ //分页数据
                pageSize:10,
                pageNum:1,
                total:0
            },
          
        }
    },

   components:{ //引入组件后注册组件
    cesTable, 
    SearchForm
   },
   mounted(){
      this.getTableAndForm()
   },
    methods:{
         //此方法是点击分页时触发的查询,
        handleCurrentChange(){
          this.getQuery()
        },
        //获取表格和表单数据
        async getTableAndForm(){
          //...         
         },
       

        //查询
        getQuery(){
           //...
        },
        handleQuery(){ //查询
            this.getQuery()
        },
        handleReset(){  /重置
          for(var i in this.searchData){
            this.$set( this.searchData, i, '') 
          }
          this.getQuery()
        }
  }
}
</script>

<style lang="scss" scoped>

</style>

好了,定义组件是为了方便使用,希望在记录自己学习的时候,也可以帮到你!

悦读

道可道,非常道;名可名,非常名。 无名,天地之始,有名,万物之母。 故常无欲,以观其妙,常有欲,以观其徼。 此两者,同出而异名,同谓之玄,玄之又玄,众妙之门。

;