当前位置: 首页 > news >正文

vue2+element 通用表格组件封装

vue3的通用表格我也封装了,是下面链接喔~ 

TS + vue3.2 + vite2 + element-plus 通用表格组件封装

1、父组件调用方式:

        <common-table
          show-index
          show-check-box
          :loading="loading"
          :table-label="tableHeaderData"
          :data="tableData"
          :option="tableOptionsData"
          @operation="operationHandler"
          @handle-selection-change="handleSelectionChange"
        >
          <template slot="expand" slot-scope="{ row }">
            <making-status v-if="row.status === 1" :list="row.processList || []" />
            <div v-else class="making-status-none">
              <span>未制作</span>
            </div>
          </template>
        </common-table>

2、参数详解: 

其实我定义的参数还蛮多的,基本能够把常用的功能包含进去了,我着重讲几个:

 

1、tableLabel:表格头部标题

有以下四个参数,最重要的是render,他的参数是从slot-scope抛出,可以进行判断显示

    {
      label: '制作格式',
      prop: 'handleType',
      width: 150,
      render(row) {
        return `<span>${row.handleType === 1 ? '查题格式' : (row.handleType === 2 ? '拍题格式' : '未设置')}</span>`
      }
    }

2、showCheckBox: 显示复选框

与之对应的回调函数是 @handle-selection-change,我们可以从中取得当前选中的复选框数组

3、showExpand: 拓展插槽

在 slot="expand" 即可以使用

        <template slot="expand" slot-scope="{ row }">
            // xxx
        </template>

4、showIndex: 是否展示序号

这里序号不是你自己的自定义id喔~是当前数组索引值 + 1

5、option: 配置需要显示的操作菜单

{
    label: '操作',
    width: '300',
    fixed: 'right',
    children: [
      {
        label: '查看制作详情',
        icon: 'el-icon-view',
        methods: 'view',
        permission: 'xxx',
        render(row) {
          return row.status !== 0
        }
      },
      {
        type: 'drop',
        icon: 'el-icon-paperclip',
        permission: 'xxx',
        children: [
          {
            label: '切题查看模式',
            methods: '1'
          },
          {
            label: '编题查看模式',
            methods: '2'
          }
        ]
      }
    ]
  }

我们主要看children里的,主要参数有4个

label: 显示名字

icon: 显示图标

permission: 即v-permission,根据按钮是否有权限再进行展示,可为字符也可为数组,这种自定义指令我就不单独写出来了,可以参考我博客,链接如下

vue学习(6)自定义指令详解及常见自定义指令,里面有个checkPermission函数,所有判断你皆可自行定义

type: 为drop 则相当于 el-dropdown,把很多按钮收缩在一起

render: 这个和上面tableLabel不一样的是,这里render返回值为true或false来决定v-show的值(只在type不为drop生效)
methods: 点击后回调触发的方法,由 @operation={row, type} 抛出,type即为methods对应值(只在type不为drop生效)

children: drop展示子数组(label和methods与上面一致)(在type为drop生效)

3、组件源码 :

<template>
  <div v-loading="loading">
    <el-table
      ref="commonTable"
      :data="data"
      border
      style="width: 100%"
      :row-class-name="tabRowClassName"
      :row-style="rowStyle"
      :cell-class-name="cellClassName"
      header-row-class-name="custom-table-header"
      :row-key="keyId"
      @select="handleSelectionChange"
      @select-all="handleSelectionChange"
    >
      <!-- 单选框 -->
      <el-table-column
        v-if="showCheckBox"
        key="showCheckBox"
        width="55"
        type="selection"
        :reserve-selection="keep"
        :class-name="turnRadio ? `checkBoxRadio` : ``"
        align="center"
      />
      <!-- 展开 -->
      <el-table-column
        v-if="showExpand"
        key="showExpand"
        type="expand"
      >
        <template slot-scope="{ row }">
          <slot name="expand" :row="row" />
        </template>
      </el-table-column>
      <!-- 序号 -->
      <el-table-column v-if="showIndex" align="center" label="序号" width="50">
        <template slot-scope="{ $index }">{{ $index + 1 }}</template>
      </el-table-column>
      <!-- 表格 -->
      <el-table-column
        v-for="item in tableLabel.filter((item) => item.label)"
        :key="item[keyId]"
        :width="item.width ? item.width : ''"
        :align="!!item.align ? item.align : 'center'"
        :label="item.label"
        :show-overflow-tooltip="overflowText"
        :fixed="item.fixed"
        :prop="item.prop"
      >
        <template slot-scope="{ row }">
          <template v-if="item.Image">
            <div>
              <el-image class="table-img" :src="row.attachment" :preview-src-list="[row.attachment]">
                <template slot="error">
                  <span>无</span>
                </template>
              </el-image>
            </div>
          </template>

          <!--进度条-->
          <template v-else-if="item.progress">
            <el-progress class="progress-line" :text-inside="true" :stroke-width="26" :percentage="row[item.prop]" />
          </template>

          <template v-else-if="item.render">
            <div style="cursor: pointer" @click="!!item.methods && handleClickon(item.methods, row)" v-html="item.render(row)" />
          </template>

          <template v-else>
            <div class="text-no-wrap" @click="!!item.methods && handleClickon(item.methods, row)">
              {{ Object.prototype.toString.call(item.prop) == '[object Array]' ? propFilter(item.prop, row) : (row[item.prop] ? row[item.prop] : '--') }}
            </div>
          </template>
        </template>
      </el-table-column>
      <el-table-column v-if="!!option" :width="option.width" :label="option.label" :fixed="option.fixed" align="center">
        <div
          v-if="!!option.children"
          slot-scope="{row}"
          class="flex-box"
        >
          <div
            v-for="(item, index) in option.children"
            v-show="item.render ? item.render(row) : true"
            :key="index"
            class="item"
          >
            <el-tooltip
              v-if="!item.type"
              v-permission="{ permission: item.permission }"
              effect="light"
              popper-class="yxp-tooltip-primary"
              :content="item.label"
              placement="top"
            >
              <i
                :class="['yxp-tooltip-icon', item.icon]"
                :plain="true"
                @click="handleTableButton(row, item.methods)"
              />
            </el-tooltip>
            <el-dropdown
              v-if="item.type==='drop'"
              v-permission="{ permission: item.permission }"
              @command="(command) => {handleTableButton(row, 'command', command)}"
            >
              <span class="el-dropdown-link">
                <i :class="['yxp-tooltip-icon', item.icon]" />
              </span>
              <el-dropdown-menu slot="dropdown">
                <el-dropdown-item
                  v-for="itm in item.children"
                  :key="itm.methods"
                  v-permission="{ permission: item.permission }"
                  :command="itm.methods"
                >
                  <i
                    v-if="itm.icon"
                    :class="['yxp-tooltip-icon', itm.icon]"
                    :plain="true"
                  />
                  {{ itm.label }}
                </el-dropdown-item>
              </el-dropdown-menu>
            </el-dropdown>
          </div>
        </div>
      </el-table-column>
    </el-table>
  </div>
</template>

<script>
export default {
  props: {
    /**
     * 表格最高高度
     */
    maxHeight: {
      type: [String, Number],
      default: 600
    },
    /**
     * 表格自定义属性展示
     */
    tableLabel: {
      type: Array,
      default: () => {
        return []
      }
    },
    /**
     * 表格数据源
     */
    data: {
      type: Array,
      default: () => {
        return []
      }
    },
    /**
     * 配置需要显示的操作菜单
     */
    option: {
      type: Object,
      default: () => {}
    },
    showCheckBox: {
      // 配置是否显示全选(复选框)
      type: Boolean,
      default: false
    },
    /**
     * 是否显示索引
     */
    showIndex: {
      type: Boolean,
      default: false
    },
    turnRadio: {
      type: Boolean,
      default: false
    },
    selectedIdArr: {
      type: Array,
      default: () => []
    },
    /**
     * 是否 隐藏文字过长
     */
    overflowText: {
      type: Boolean,
      default: false
    },
    /**
     * 加载提示
     */
    loading: {
      type: Boolean,
      default: false
    },
    /**
     * 是否保持之前复选框的数据
     */
    keep: {
      type: Boolean,
      default: false
    },
    /**
     * 动态绑定 key 值
     */
    keyId: {
      type: String,
      default: 'id'
    },
    /**
     * 行内自定义样式配置
     */
    rowStyle: {
      type: Object,
      default: () => {
        return {
          height: '40px'
        }
      }
    },
    /**
     * 是否展示展开按钮
     */
    showExpand: {
      type: Boolean,
      default: false
    }
  },
  data() {
    return {
      curPageCheck: [],
      radioId: '',
      showVertical: false
    }
  },
  watch: {
    data: {
      handler() {
        if (this.showCheckBox || this.turnRadio) {
          this.$nextTick(() => {
            this.$refs.commonTable.clearSelection()
            this.curPageCheck = []
            if (this.showCheckBox && this.turnRadio) {
              this.data.filter((item) => {
                if (item.id === this.selectedIdArr[0]) {
                  this.$refs.commonTable.toggleRowSelection(item, true)
                }
              })
            } else if (this.showCheckBox) {
              this.data.filter((item) => {
                if (this.selectedIdArr.includes(item.id)) {
                  this.$refs.commonTable.toggleRowSelection(item, true)
                  this.curPageCheck.push(item.id)
                }
              })
            }
          })
        }
      },
      deep: true,
      immediate: true
    },
    selectedIdArr: {
      handler(val) {
        if (this.showCheckBox || this.turnRadio) {
          this.$nextTick(() => {
            this.$refs.commonTable.clearSelection()
            this.curPageCheck = []
            if (this.showCheckBox && this.turnRadio) {
              this.data.filter((item) => {
                if (item.id === val[0]) {
                  this.$refs.commonTable.toggleRowSelection(item, true)
                }
              })
            } else if (this.showCheckBox) {
              this.data.filter((item) => {
                if (val.includes(item.id)) {
                  this.$refs.commonTable.toggleRowSelection(item, true)
                  this.curPageCheck.push(item.id)
                }
              })
            }
          })
        }
      },
      deep: true,
      immediate: true
    }
  },
  methods: {
    /**
     * prop 单值 或者 数组过滤(此处为针对时间组,不作为通用处理)
     */
    propFilter(prop, row) {
      const res = prop.reduce((total, cur) => {
        if (row[cur]) {
          return (total += row[cur] + '~')
        } else {
          return ''
        }
      }, '')
      return res ? res.replace(/~$/, '') : ''
    },
    handleTableButton(row, type, extraData) {
      this.$emit('operation', row, type, extraData);
    },
    /**
     * 后续扩展位
     * @param {*} methods
     * @param {*} row
     */
    handleClickon(methods, row) {
      this.$emit(methods, { methods, row })
    },
    handleSelectionChange(val) {
      if (this.showCheckBox && this.turnRadio) {
        // 选择项大于1时
        if (val.length > 1) {
          const del_row = val.shift()
          this.$refs.commonTable.toggleRowSelection(del_row, false)
        }
      }
      // 全选
      if (this.showCheckBox && this.selectedIdArr) {
        if (this.turnRadio) {
          this.$emit('handle-selection-change', val)
        } else {
          // 一般复选框都是走到这一步
          this.$emit('handle-selection-change', val)
        }
      } else {
        this.$emit('handle-selection-change', val)
      }
    },
    getRowKeys(row) {
      return row.id
    },
    selectAll(val) {
      if (this.showCheckBox && this.turnRadio) {
        // 选择项大于1时
        if (val.length > 1) {
          val.length = 1
        }
      }
      this.$emit('handle-selection-change', val)
    },
    // 斑马纹表格背景色
    tabRowClassName({ row, rowIndex }) {
      const index = rowIndex + 1
      if (index % 2 === 0) {
        return 'even-row'
      } else {
        return 'odd-row'
      }
    },
    cellClassName({ row, column, rowIndex, columnIndex }) {
      if (row.confirmTag === 2 && columnIndex < this.tableLabel.length) {
        return 'height_light_cell'
      } else {
        return ''
      }
    },
    buttonDisabled(item, row) {
      if (typeof item.disabled === 'function') return item.disabled(row) || false
      if (!item.disabled) return item.disabled
    },
    /**
     * 单选框选中事件
     */
    rowClick(row) {
      this.$emit('rowClick', row)
    }
  }
}
</script>

<style lang="scss" scoped>

  ::v-deep .el-table__header,
  ::v-deep .el-table__body {
    margin: 0;
  }

  ::v-deep .el-table::before {
    height: 0;
  }

  ::v-deep .el-button {
    padding: 0;
    border: none;
    margin: 0 4px;
    padding: 0 4px 0 8px;
    border-left: 1px solid #e2e2e2;
    font-size: 14px;
    min-height: 14px;

    &:first-child {
      border-left: none;
    }
  }

  ::v-deep .el-button + .el-button {
    margin-left: 0;
  }

  ::v-deep .btn-right div {
    margin-right: 5px;
  }

  .btn-right div:empty {
    margin-right: 0px;
  }

  //斑马纹表格背景色
  ::v-deep .el-table .even-row {
    --el-table-tr-background-color: #f5fafb;
  }

  ::v-deep .el-table .odd-row {
    --el-table-tr-background-color: #ffffff;
  }

  .el-table--border::after,
  .el-table--group::after {
    width: 0;
  }

  ::v-deep .el-table__fixed-right::before,
  .el-table__fixed::before {
    background-color: transparent;
  }
  ::v-deep .custom-table-header {
    th {
      background-color: #fff4d9 !important;
    }
  }
  .progress-line {
    .el-progress-bar__outer {
      height: 16px !important;
    }

    .el-progress-bar__outer,
    .el-progress-bar__inner {
      border-radius: 0 !important;
    }
  }

  .text-no-wrap {
    cursor: pointer;
    display: inline;
  }

  ::v-deep .el-table {
    td.el-table__cell div,
    th.el-table__cell > .cell {
      font-size: 14px;
    }

    th.el-table__cell > .cell {
      font-weight: normal;
    }

    .cell {
      padding: 0 10px;
      line-height: 39px;
    }

    .el-table__header-wrapper .checkBoxRadio .el-checkbox {
      display: none;
    }

    .el-checkbox {
      display: flex;
      align-items: center;
      justify-content: center;
    }

    .table-img {
      width: 60px;
      height: 60px;
      object-fit: cover;
      padding: 6px 0;
      display: flex;
      align-items: center;
      margin: 0 auto;
      justify-content: center;
    }
  }

  ::v-deep .el-table--small .el-table__cell {
    padding: 0;
  }

  ::v-deep .el-dropdown-menu__item {
    padding: 5px 10px !important;
    .el-button {
      width: 100%;
      text-align: center;
      padding: 0 8px;
      margin: 0;
    }
  }
  .flex-box{
    display: flex;
    flex-flow: row nowrap;
    justify-content: flex-start;
    .item{
      margin: 0 10px;
    }
  }
</style>

相关文章:

  • Symbol Table
  • LeetCode所有大于等于节点的值之和
  • 基站天线交叉极化比测量的不确定度评定
  • RTL8367/N/RB/S/SC系列千兆交换机方案选型参考
  • GO 调用 python3 (基于ubuntu) 实现人脸识别
  • 澳利率攀升,加息步伐将在某个时候放缓
  • AOP实现系统告警
  • Spring Boot集成第三方登录之微博登录
  • 那么我们应该如何优化Youtube的视频呢?
  • 带你秒懂 SSR-服务端渲染
  • MindRecord-Windows下中文路径问题Unexpected error. Failed to open file
  • 基于随机森林实现特征选择降维及回归预测(Matlab代码实现)
  • 链表的奇偶重排
  • 华为ENSP网络设备配置实战2(较为复杂的ospf)
  • 干货 | 精准化测试原理简介与实践探索
  • JavaScript 如何正确处理 Unicode 编码问题!
  • android高仿小视频、应用锁、3种存储库、QQ小红点动画、仿支付宝图表等源码...
  • bootstrap创建登录注册页面
  • Computed property XXX was assigned to but it has no setter
  • create-react-app项目添加less配置
  • java2019面试题北京
  • Javascript编码规范
  • JavaScript设计模式系列一:工厂模式
  • JAVA多线程机制解析-volatilesynchronized
  • Java方法详解
  • Redis字符串类型内部编码剖析
  • Stream流与Lambda表达式(三) 静态工厂类Collectors
  • Vue源码解析(二)Vue的双向绑定讲解及实现
  • 测试如何在敏捷团队中工作?
  • 搭建gitbook 和 访问权限认证
  • 等保2.0 | 几维安全发布等保检测、等保加固专版 加速企业等保合规
  • 前端学习笔记之观察者模式
  • 设计模式 开闭原则
  • 思否第一天
  • 腾讯优测优分享 | Android碎片化问题小结——关于闪光灯的那些事儿
  • 通过几道题目学习二叉搜索树
  • 线性表及其算法(java实现)
  • 延迟脚本的方式
  • 正则与JS中的正则
  • ionic异常记录
  • ​ubuntu下安装kvm虚拟机
  • # centos7下FFmpeg环境部署记录
  • #HarmonyOS:软件安装window和mac预览Hello World
  • #include
  • #includecmath
  • $var=htmlencode(“‘);alert(‘2“); 的个人理解
  • (js)循环条件满足时终止循环
  • (附源码)spring boot火车票售卖系统 毕业设计 211004
  • (附源码)计算机毕业设计SSM在线影视购票系统
  • (十)【Jmeter】线程(Threads(Users))之jp@gc - Stepping Thread Group (deprecated)
  • (四)Linux Shell编程——输入输出重定向
  • (转)Android学习系列(31)--App自动化之使用Ant编译项目多渠道打包
  • (转)利用ant在Mac 下自动化打包签名Android程序
  • .Net 6.0 处理跨域的方式
  • .NET CLR基本术语