element-ui表格二次封装

在平常的vue项目开发中,我们经常会遇见现有的UI无法满足需求的情况。今天就给大家分享下,如何对element-ui的表格组件,进行二级封装改造。

文章目录

  • 前言
  • 一、可编辑表格是什么?
  • 二、页面效果
  • 三、使用示例
    • 1.创建一个vue项目
    • 2.安装项目依赖
    • 3. 封装组件
    • 4. 使用示例
  • 总结


前言

例如:随着项目功能的不断变化,简单的element-ui表格已经不能满足需求。客户要求表格不再只是展示数据,而是要进行数据编辑,并支持日期时间、数字、多选树时。我们就要对现有UI组件进行二次改造。


一、可编辑表格是什么?

可编辑表格是在Element-ui里Table表格的基础上进行二次封装,使其支持多种输入框类型,支持输入框类型如下:普通类型(input)、数字类型(number)、下拉选(select)、单选(radio)、日期框(datetime || date)、下拉树(selectTree:引用的为vue第三方插件vue-treeselect)。

二、页面效果

element-ui表格二次封装_第1张图片

三、使用示例

1.创建一个vue项目

文件如下(示例):

src
├── ...
├── main.js
├── App.vue
├── components
    ├── EditTable //可编辑表格组件
        ├── index.vue //主要内容
    ├── SelectTree //多选树组件
        ├── index.vue //主要内容
├── utils // 定义的公共方法
├── views // 页面
    ├── Table 
        ├── index.vue //使用组件

2.安装项目依赖

代码如下:

多选树组件使用的是 @riophae/vue-treeselect
npm install @riophae/vue-treeselect

element-ui、vue等自行安装

3. 封装组件

代码如下:在components/SelectTree/index.vue中新增以下代码(对@riophae/vue-treeselect的二次封装,方便使用)

<template>
  <treeselect v-model="formInline[selectTreeParams.code]" :multiple="selectTreeParams.options.multiple"
    :disabled="disabled" :limit="selectTreeParams.options.limit" :appendToBody="
      selectTreeParams.options.appendToBody
        ? selectTreeParams.options.appendToBody
        : false
    " :normalizer="normalizer" :options="selectTreeParams.data" :placeholder="'请选择' + selectTreeParams.title"
    :class="disabled ? 'treeSelect-new' : ''" />
template>

<script>
// 引入多选树依赖
import Treeselect from "@riophae/vue-treeselect";
import "@riophae/vue-treeselect/dist/vue-treeselect.css";
export default {
  props: {
    formInline: {
      type: Object,
      required: false,
      default: () => ({}),
    },
    selectTreeParams: {
      type: Object,
      required: false,
      default: () => ({
        title: "运维单位",
        code: "opsDeptId",
        data: [], //数据源
        labelCode: {
          id: "id",
          label: "shortName",
          children: "children",
        },
        options: {
          multiple: false, //是否多选
          limit: 1,
          // disableBranchNodes: true,
        },
      }),
    },
    disabled: {
      type: Boolean,
      default: false,
    },
  },
  watch: {},
  components: {
    Treeselect,
  },
  data() {
    return {};
  },
  created() {
    let that = this;
  },
  mounted() { },
  methods: {
    /** 转换组织机构树数据结构 */
    normalizer(node) {
      let that = this;
      // console.log('node',node);
      let children = that.selectTreeParams.labelCode.children;
      let label = that.selectTreeParams.labelCode.label;
      let id = that.selectTreeParams.labelCode.id;
      if (node[children] && !node[children].length) {
        delete node[children];
      }
      return {
        id: node[id],
        label: node[label],
        children: node[children],
      };
    },
  },
};
script>
<style scoped>
style>

代码如下:在components/EditTable/index.vue中新增以下代码


<template>
  <div class="mhy-app-editTable glfg">
    <div class="mhy-editTable-table">
      <el-table :data="tableOptions.data" ref="tableDataRef"
        :height="tableOptions.height ? tableOptions.height : 'auto'" border>
        <el-table-column type="selection" width="55" align="center" fixed="left" />
        <el-table-column label="序号" type="index" width="60" align="center" fixed="left">
        el-table-column>

        <el-table-column align="center" :prop="item.code" :width="item.width != undefined ? item.width : ''"
          :fixed="item.fixed != undefined ? item.fixed : false" :label="item.title"
          v-for="(item, i) in tableOptions.title" :key="'item_' + i">
          <template slot="header" slot-scope="{ column, $index }">
            <i v-if="item.required" style="color: red">*i>
            <span>{{ item.title }}span>
          template>
          <template slot-scope="scope">
            
            <div class="editTable-input" v-if="item.type == 'input'">
              <el-input v-model.trim="scope.row[item.code]" :placeholder="'请输入' + item.title"
                :disabled="cellDisabled(item, scope.row)" @change="cellEvent(item, scope)">el-input>
            div>

            
            
            <div class="editTable-input" v-if="item.type == 'number'">
              <el-input-number v-model.trim="scope.row[item.code]" :placeholder="'请输入' + item.title"
                :disabled="cellDisabled(item, scope.row)" :controls="false" :min="0" :precision="2"
                @change="cellEvent(item, scope)" @blur="cellEvent(item, scope)">el-input-number>
            div>

            
            <div class="editTable-input" v-if="item.type == 'select' && item.code != 'county'">
              <el-select v-model="scope.row[item.code]" :placeholder="'请选择' + item.title"
                :disabled="cellDisabled(item, scope.row)" @change="cellEvent(item, scope)" clearable>
                <el-option v-for="item3 in tableOptions.options[item.options].data"
                  :key="item3[tableOptions.options[item.options].value]"
                  :label="item3[tableOptions.options[item.options].label]"
                  :value="item3[tableOptions.options[item.options].value]">
                el-option>
              el-select>
            div>

            
            <div class="editTable-input" v-if="item.type == 'select' && item.code == 'county'">
              <el-select v-model="scope.row[item.code]" :placeholder="'请选择' + item.title"
                :disabled="cellDisabled(item, scope.row)" @change="cellEvent(item, scope)" clearable>
                <template v-for="item3 in tableOptions.options[item.options].data">
                  <el-option v-if="item3.parentCode == scope.row['city']"
                    :key="item3[tableOptions.options[item.options].value]"
                    :label="item3[tableOptions.options[item.options].label]"
                    :value="item3[tableOptions.options[item.options].value]">
                  el-option>
                template>
              el-select>
            div>

            
            <div class="editTable-input" v-if="item.type == 'radio'">
              <el-radio v-model="scope.row[item.code]" v-for="(item1, i1) in item.options" :label="item1.value"
                :key="'item1_' + i1">{{ item1.label }}el-radio>
            div>

            
            <div class="editTable-input" v-if="item.type == 'selectTree'">
              <SelectTree ref="selectTreeRef" :disabled="disabled" :formInline="scope.row"
                :selectTreeParams="getSelectTreeParams(item)" />
            div>

            
            <div class="editTable-input" v-if="item.type == 'date' || item.type == 'datetime'">
              <el-date-picker v-model="scope.row[item.code]" :type="item.type" :disabled="cellDisabled(item, scope.row)"
                :value-format="
                  item.type == 'date' ? 'yyyy-MM-dd' : 'yyyy-MM-dd HH:mm:ss'
                " placeholder="请选择日期">
              el-date-picker>
            div>
          template>
        el-table-column>

        <el-table-column v-if="tableOptions.btns.list.length !== 0" label="操作" align="center"
          class-name="small-padding fixed-width" :width="tableOptions.btns.width" fixed="right">
          <template slot-scope="scope">
            <el-button v-for="(item2, i2) in tableOptions.btns.list" :key="'item2_' + i2" :icon="item2.icon"
              :disabled="Disabled(scope.row)" @click="columnEvent(item2.type, scope)" type="success" plain size="mini">
              {{ item2.name }}
            el-button>
          template>
        el-table-column>
      el-table>
    div>
  div>
template>

<script>
import SelectTree from "@/components/SelectTree/index.vue"; //下拉选择树

export default {
  props: {
    tableOptions: {
      type: Object,
      required: false,
      default: () => ({}),
    },
    type: {
      type: String,
      required: false,
      default: "dd",
    },
    disabled: {
      type: Boolean,
      default: false
    }
  },
  components: { SelectTree },
  data() {
    return {
      //选择运维单位参数
      selectTreeParams: {
        title: "运维单位",
        code: "",
        data: [], //数据源
        labelCode: {
          id: "id",
          label: "shortName",
          children: "children",
        },
        options: {
          multiple: false, //是否多选
          disableBranchNodes: true, //是否阻止选择分支
          appendToBody: true,
        },
      },
    };
  },
  created() {
    let that = this;
    that.init();
  },
  mounted() { },
  methods: {
    init() {
      let that = this;
    },
    //点击表格操作列按钮
    columnEvent(type, scope) {
      let that = this;
      that.$emit("columnEvent", {
        type: type,
        row: scope.row,
        index: scope.$index,
      });
    },

    //表格操作单元格事件
    cellEvent(item, scope) {
      let that = this;
      if (item.change) {
        that.$emit("cellEvent", {
          code: item.code,
          type: item.type,
          row: scope.row,
          index: scope.$index,
        });
      }
    },
    //获取表格选中行
    getTableSelect() {
      let that = this;
      return that.$refs.tableDataRef.selection;
    },

    //单元格是否可编辑
    cellDisabled(item, row) {
      // console.log(row);
      if (item.disable == true) {
        return true;
      } else {
        if (item.isIdDisable && item.isIdDisable.bool == true) {
          // alert(row[item.isIdDisable.code]);
          return (
            row[item.isIdDisable.code] != undefined &&
            row[item.isIdDisable.code] != ""
          );
        }
      }
      return false;
    },

    // 操作列按钮是否可编辑
    Disabled(row) {
      let that = this;
      if (that.type == "ht") {
        if (row.orderId != null && row.orderId) {
          return true;
        } else {
          return false;
        }
      } else if (that.type == "dd") {
        if (row.reqId != null && row.reqId) {
          return true;
        } else {
          return false;
        }
      }
    },

    //获取下拉树参数
    getSelectTreeParams(item) {
      let that = this;
      that.selectTreeParams.code = item.code;
      that.selectTreeParams.data = that.tableOptions.options[item.options].data;
      return that.selectTreeParams;
    },

    //验证表格参数
    validateTableCell() {
      let that = this;
      let resultJson = { result: false, msg: '' };
      let tableData = that.tableOptions.data;//表格数据
      // let tableCellValidate=that.tableOptions.title.
      // console.log("验证:"+JSON.stringify(tableData));

      //循环验证
      for (let i = 0; i < tableData.length; i++) {
        let tempTableRow = tableData[i];
        for (let rowCell in tempTableRow) {
          let tempValue = tempTableRow[rowCell];
          let cellJson = that.getCellValidate(rowCell);
          let cellValidate = cellJson.rulesJson;
          let resultMsg = that.tableOptions.name + '第' + (i + 1) + '行' + cellJson.title + ',';
          // console.log("当前列:"+JSON.stringify(cellJson));
          let input = ((tempValue == '' || tempValue == null) && cellJson.type === 'input')
          let select = ((tempValue == '' || tempValue == null) && cellJson.type === 'select')
          let date = (tempValue == null && cellJson.type === 'date')
          if (cellJson.required && (input || select || date)) {
            resultJson.result = true;
            resultJson.msg = (resultMsg + '不能为空');//错误信息
            return resultJson;
          }
          else {
            if (cellValidate != undefined) {
              for (let j = 0; j < cellValidate.length; j++) {
                //循环验证规则
                let reg = cellValidate[j].reg;//正则
                if (reg != undefined) {
                  let tempReg = new RegExp(reg);
                  if (!tempReg.test(tempValue)) {
                    // console.log("验证成果");
                    resultJson.result = true;
                    resultJson.msg = (resultMsg + cellValidate[j].msg);//错误信息
                    return resultJson;
                  }
                  else {
                  }
                } else {
                  //执行自定义验证
                  let callbackResult = cellValidate[j].callback(tempValue);
                  if (callbackResult.result) {
                    callbackResult.msg = (resultMsg + callbackResult.msg);
                    resultJson = callbackResult;
                    return resultJson;
                  }
                }
              }
            }
          }
        }
      }
      return resultJson;
    },
    getCellValidate(code) {
      let that = this;
      let tableValidate = that.tableOptions.title;//表格验证
      let cellValidate = {};
      for (let i = 0; i < tableValidate.length; i++) {
        let tempCode = tableValidate[i].code;
        if (tempCode == code) {
          cellValidate = tableValidate[i];
        }
      }
      return cellValidate;
    }
  },
};
script>
<style scoped>
style>

4. 使用示例

代码示例如下:

<template>
  <div class="app-container">
    <div class="edit-table">
      <p class="title">1.可编辑表格p>
      <p class="describe">
        可编辑表格是在Element-ui里Table表格的基础上进行二次封装,使其支持多种输入框类型,支持输入框类型如下:普通类型(input)、数字类型(number)、下拉选(select)、单选(radio)、日期框(datetime
        || date)、下拉树(selectTree:引用的为vue第三方插件vue-treeselect)p>
      
      <div class="editTable-btns app-flex">
        <el-button type="success" icon="el-icon-edit" @click="addRow">添加行
        el-button>
        <el-button type="danger" icon="el-icon-delete" @click="deleteRow">删除行
        el-button>
        <el-button type="primary" icon="el-icon-check" @click="submitTable">保存
        el-button>
      div>
      <EditTable ref="editTableRef" :table-options="tableOptions" @cellEvent="cellEvent" @columnEvent="columnEvent" />
    div>
  div>
template>

<script>

import EditTable from '@/components/EditTable/index.vue'; // 可编辑表格

// 表格行对象
const BasicTableColumn = {
  id: null, //表格行ID
  input: null,
  radio: 1,
  select: null,
  number: null,
  selectTree: null,
  date: null
}
export default {
  name: 'Table',
  // 注册组件  
  components: {
    EditTable,
  },
  data() {
    return {
      // 可编辑表格配置项
      tableOptions: {
        // 表格名称
        name: '可编辑表格',
        // 表格高度  可不写,默认为auto
        height: 400,
        // 表格中select选择框、vue-treeselect选项树数据项
        options: {
          selectTreeOptions: { label: 'name', value: 'id', data: [] }, // 运维单位
          selectOptions: {
            label: 'name',
            value: 'code',
            data: [
              { name: '选项1', code: 1 },
              { name: '选项2', code: 2 },
              { name: '选项3', code: 3 }
            ]
          }
        },
        // 表格标题
        /*
         * title: 表头标题中文名称
         * code: 表头标题英文字段
         * required: 是否是必填项
         * type: 类型
         * width: 表头标题宽度  默认值auto
         * disable: 是否禁用
         * change: 编辑当前单元格是否触发事件,默认不开启,如需操作当前单元格进行事件处理时,需配置为true
         * isIdDisable: 项目特殊添加,是否根据前置条件禁用  此项目是在有外来数据带入的情况下,禁止修改表格当前行(*默认可不配置此选项)
         * rulesJson: 自定义校验规则 此配置可不填默认,当required为true时,默认非空验证,配置此项后,按当前配置进行表格验证
        */
        title: [
          {
            title: 'Input输入框',
            code: 'input',
            required: false,
            type: 'input',
            // width: 240,
            disable: false,
            change: true,
            isIdDisable: {
              bool: true,
              code: 'orderId'
            }
          },
          {
            title: 'Radio单选框',
            code: 'radio',
            required: false,
            type: 'radio',
            // width: 240,
            options: [
              { label: '是', value: 1 },
              { label: '否', value: 0 }
            ]
          },
          {
            title: 'Select选择器',
            code: 'select',
            required: true,
            type: 'select',
            // width: 240,
            options: 'selectOptions',
          },
          {
            title: 'InputNumber计数器',
            code: 'number',
            required: true,
            change: true,
            type: 'number',
            // width: 240,
            disable: false,
            rulesJson: [
              { reg: '^[0-9]*$', msg: "只能输入数字" }
            ]
          },
          {
            title: 'vue-treeselect多选树',
            code: 'selectTree',
            required: false,
            // width: 240,
            type: 'selectTree',
            options: 'selectTreeOptions',
          },
          {
            title: "DatePicker日期选择器",
            code: "date",
            required: false,
            type: "date",
            // width: 240,
            disable: false,
          },
        ],
        data: [],
        btns: {
          width: 300,
          list: [
            {
              name: '操作列按钮',
              icon: 'el-icon-tickets',
              type: 'glZy'
            }
          ]
        }
      },
    };
  },


  created() {
  },

  methods: {
    // 可编辑表格事件
    // 添加行
    addRow() {
      const that = this;
      // Utils为定义的公共方法,目的在于深拷贝数据
      /**
	   * 深拷贝json对象
	   * @param {JSON} obj
	   * @returns {string}
	   */
	  // copyArray(obj) { return JSON.parse(JSON.stringify(obj)) }
      that.tableOptions.data.push(that.Utils.copyArray(BasicTableColumn));
    },
    // 删除行
    deleteRow() {
      const that = this;
      const selectActive = that.$refs['editTableRef'].getTableSelect();
      if (selectActive.length == 0) {
        return that.$message.warning('请至少选择一个');
      }

      that.tableOptions.data = that.tableOptions.data.filter(
        (t) => !selectActive.some((s) => s === t)
      );
    },

    // 操作列按钮
    columnEvent(obj) {
      const that = this;
      const index = obj.index;
      alert('操作列按钮')
      // 书写表格尾部操作列的逻辑
    },

    // 单元格数据变化 
    // 只有表格头部字段的change属性设置为true时,修改编辑输入框时才会触发该事件
    cellEvent(obj) {
      const that = this;
      const code = obj.code;  // 当前操作行的字段
      const index = obj.index; // 当前操作行
      // 参数obj中包含当前操作行的数据,当前操作行的输入框字段、输入框类型,当前操作行的索引值
    },

    // 保存表格
    submitTable() {
      let that = this;
      //循环验证表格
      let resultBool = that.$refs.editTableRef.validateTableCell()
      if (resultBool.result) {
        that.$message.warning(resultBool.msg);
        return;
      }

      if(that.tableOptions.data.length === 0) {
        that.$message.warning('请至少添加一条数据!');
        return;
      }
    }
  }
}
script>
<style scoped>
style>


总结

例如:以上就是今天要讲的内容,本文仅仅简单介绍了对element-ui中的表格进行二次封装的使用,代码不够简化,大家可以多多提意见。

你可能感兴趣的:(vue.js,javascript,es6,前端,elementui)