草庐IT

vue2+element-ui 通用表格组件封装

林大大哟 2024-07-03 原文

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%',
        borderBottom: maxHeight === 'auto' ? 'none' : '1px solid #e8eded'
      }"
      :max-height="maxHeight"
      :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.filter(item => item.render ? item.render(row) : true)"

            :key="index"
          >
            <el-tooltip
              v-if="!item.type"
              v-permission="{ permission: item.permission }"
              class="item"
              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 }"
              class="item"
              @command="(command) => {handleTableButton(row, 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: 'auto'
    },
    /**
     * 表格自定义属性展示
     */
    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) {
      this.$emit('operation', row, type);
    },
    /**
     * 后续扩展位
     * @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>

有关vue2+element-ui 通用表格组件封装的更多相关文章

  1. ruby - i18n Assets 管理/翻译 UI - 2

    我正在使用i18n从头开始​​构建一个多语言网络应用程序,虽然我自己可以处理一大堆yml文件,但我说的语言(非常)有限,最终我想寻求外部帮助帮助。我想知道这里是否有人在使用UI插件/gem(与django上的django-rosetta不同)来处理多个翻译器,其中一些翻译器不愿意或无法处理存储库中的100多个文件,处理语言数据。谢谢&问候,安德拉斯(如果您已经在ruby​​onrails-talk上遇到了这个问题,我们深表歉意) 最佳答案 有一个rails3branchofthetolkgem在github上。您可以通过在Gemfi

  2. ruby-on-rails - 如何在 Ruby on Rails 中实现由 JSF 2.0 (Primefaces) 驱动的 UI 魔法 - 2

    按照目前的情况,这个问题不适合我们的问答形式。我们希望答案得到事实、引用或专业知识的支持,但这个问题可能会引发辩论、争论、投票或扩展讨论。如果您觉得这个问题可以改进并可能重新打开,visitthehelpcenter指导。关闭10年前。问题1)我想知道ruby​​onrails是否有功能类似于primefaces的gem。我问的原因是如果您使用primefaces(http://www.primefaces.org/showcase-labs/ui/home.jsf),开发人员无需担心javascript或jquery的东西。据我所知,JSF是一个规范,基于规范的各种可用实现,prim

  3. ruby-on-rails - Prawn - 表格单元格内的链接 - 2

    我正在尝试用Prawn生成PDF。在我的PDF模板中,我有带单元格的表格。在其中一个单元格中,我有一个电子邮件地址:cell_email=pdf.make_cell(:content=>booking.user_email,:border_width=>0)我想让电子邮件链接到“mailto”链接。我知道我可以这样链接:pdf.formatted_text([{:text=>booking.user_email,:link=>"mailto:#{booking.user_email}"}])但是将这两行组合起来(将格式化文本作为内容)不起作用:cell_email=pdf.make_c

  4. 计算机毕业设计ssm+vue基本微信小程序的小学生兴趣延时班预约小程序 - 2

    项目介绍随着我国经济迅速发展,人们对手机的需求越来越大,各种手机软件也都在被广泛应用,但是对于手机进行数据信息管理,对于手机的各种软件也是备受用户的喜爱小学生兴趣延时班预约小程序的设计与开发被用户普遍使用,为方便用户能够可以随时进行小学生兴趣延时班预约小程序的设计与开发的数据信息管理,特开发了小程序的设计与开发的管理系统。小学生兴趣延时班预约小程序的设计与开发的开发利用现有的成熟技术参考,以源代码为模板,分析功能调整与小学生兴趣延时班预约小程序的设计与开发的实际需求相结合,讨论了小学生兴趣延时班预约小程序的设计与开发的使用。开发环境开发说明:前端使用微信微信小程序开发工具:后端使用ssm:VU

  5. ruby - 如何使用 Ruby 将 CSV 文件读入 HTML 表格? - 2

    我正在尝试将一个简单的CSV文件读入HTML表格以在浏览器中显示,但我遇到了麻烦。这就是我正在尝试的:Controller:defshow@csv=CSV.open("file.csv",:headers=>true)end查看:输出:NameStartDateEndDateQuantityPostalCode基本上我只获取标题,而不会读取和呈现CSV正文。 最佳答案 这最终成为最终解决方案:Controller:defshow#OpenaCSVfile,andthenreaditintoaCSV::Tableobjectforda

  6. ruby - 如何使用 Nokogiri 解析纯 HTML 表格? - 2

    我想用Nokogiri解析HTML页面。页面的一部分有一个表,它没有使用任何特定的ID。是否可以提取如下内容:Today,3,455,34Today,1,1300,3664Today,10,100000,3444,Yesterday,3454,5656,3Yesterday,3545,1000,10Yesterday,3411,36223,15来自这个HTML:TodayYesterdayQntySizeLengthLengthSizeQnty345534345456563113003664354510001010100000344434113622315

  7. ruby-on-rails - prawnto 显示新页面时不会中断的表格 - 2

    我有可变数量的表格和可变数量的行,我想让它们一个接一个地显示,但如果表格不适合当前页面,请将其放在下一页,然后继续。我已将表格放入事务中,以便我可以回滚然后打印它(如果高度适合当前页面),但我如何获得表格高度?我现在有这段代码pdf.transactiondopdf.table@data,:font_size=>12,:border_style=>:grid,:horizontal_padding=>10,:vertical_padding=>3,:border_width=>2,:position=>:left,:row_colors=>["FFFFFF","DDDDDD"]pdf.

  8. ruby - 如何以表格格式快速打印 Ruby 哈希值? - 2

    有没有办法快速将表格格式的ruby​​哈希打印到文件中?如:keyAkeyBkeyC...1232343451253474456...其中散列的值是不同大小的数组。还是使用双循环是唯一的方法?谢谢 最佳答案 试试我写的这个gem(在表中打印散列、ruby对象、ActiveRecord对象):http://github.com/arches/table_print 关于ruby-如何以表格格式快速打印Ruby哈希值?,我们在StackOverflow上找到一个类似的问题:

  9. (附源码)vue3.0+.NET6实现聊天室(实时聊天SignalR) - 2

    参考文章搭建文章gitte源码在线体验可以注册两个号来测试演示图:一.整体介绍  介绍SignalR一种通讯模型Hub(中心模型,或者叫集线器模型),调用这个模型写好的方法,去发送消息。  内容有:    ①:Hub模型的方法介绍    ②:服务器端代码介绍    ③:前端vue3安装并调用后端方法    ④:聊天室样例整体流程:1、进入网站->调用连接SignalR的方法2、与好友发送消息->调用SignalR的自定义方法 前端通过,signalR内置方法.invoke()  去请求接口3、监听接受方法(渲染消息)通过new signalR.HubConnectionBuilder().on

  10. ruby-on-rails - 为什么我不能在 Rails 的表格中创建一个数组作为列? - 2

    为什么我不能这样做:classCreateModels是否有其他方法可以使数组(“apples”)成为Fruit类实例的属性? 最佳答案 在Rails4中并使用PostgreSQL,您实际上可以在数据库中使用数组类型:迁移:classCreateSomething 关于ruby-on-rails-为什么我不能在Rails的表格中创建一个数组作为列?,我们在StackOverflow上找到一个类似的问题: https://stackoverflow.com/qu

随机推荐