Bootstrap

vue+element-ui el-table组件二次封装(2024-01-31 TTabel组件新增单击编辑单元格功能)

2024-01-31 TTabel组件新增单击开启编辑模式功能

在这里插入图片描述

所有示例效果

在这里插入图片描述

1. 简介

HTML一行代码,可以实现表格编辑/分页/表格内/外按钮操作/行内文字变色/动态字典展示/filters格式化数据/排序/显示隐藏表格内操作按钮等

点击查看文档实例demo

代码示例:

   <t-table
       :table="table"
        :columns="table.columns"
        @size-change="handlesSizeChange"
        @page-change="handlesCurrentChange"
      />

2、配置参数(Table Attributes)

参数说明类型默认值
table表格数据对象Object{}
—rules规则(可依据 elementUI el-form 配置————对应 columns 的 prop 值)Object-
—data展示数据Array[]
—toolbar表格外操作栏选中表格某行,可以将其数据传出Array-
—operator表格内操作栏数据Array[]
-------hasPermi表格内操作栏按钮权限资源(结合 btnPermissions 属性使用)String-
-------show表格内操作栏根据状态显示Object-
-------noshow表格内操作栏根据多种状态不显示Array-
-------renderrender函数渲染使用的 Function(val) 可以用 jsx 方式Function-
-------bind继承el-button所有Attributes(默认值{ type:‘text’,size:‘mini’,})Object-
-------fun事件名function-
—operatorConfig表格内操作栏样式Object{}
--------fixed列是否固定在左侧或者右侧。 true 表示固定在左侧(true / ‘left’ / ‘right’)string / boolean-
--------label显示的标题string‘操作’
--------width对应列的宽度(固定的)string / number-
--------minWidth对应列的最小宽度(会把剩余宽度按比例分配给设置了 min-width 的列)string / number-
--------align对齐方式 (left / center / right)string‘center’
--------bindel-table-column AttributesObject-
—changeColor整行文字颜色样式控制Object{}
—firstColumn表格首列(序号 index,复选框 selection,单选 radio)排列object{}
—total数据总条数Number-
—pageSize页数量Number-
—currentPage是否需要显示切换页条数Number-
columns表头信息Array[]
----sort排序 (设置:sort:true)Booleanfalse
----headerRequired是否显示表头必填’*’Booleanfalse
----renderHeader列标题 Label 区域渲染使用的可以用 jsx 方式Function-
----bindel-table-column AttributesObject-
----width对应列的宽度(固定的)string / number-
----minWidth对应列的最小宽度(会把剩余宽度按比例分配给设置了 min-width 的列)string / number-
----noShowTip是否换行 (设置:noShowTip:true);开启虚拟列表不会换行Booleanfalse
----isShowHidden是否动态显示隐藏列设置(隐藏/显示列)Booleanfalse
----render返回三个参数(text:当前值,row:当前整条数据 ,index:当前行)function-
----slotName插槽显示此列数据(其值是具名作用域插槽)String-
----slotNameMerge合并表头插槽显示此列数据(其值是具名作用域插槽)String-
----------param具名插槽获取此行数据必须用解构接收{param}Object当前行数据
----canEdit是否开启单元格编辑功能Booleanfalse
----filters字典过滤Object-
----------listlistTypeInfo 里面对应的下拉数据源命名String-
----------key数据源的 key 字段(默认:dictValue)String‘dictValue’
----------label数据源的 label 字段(默认:dictLabel)String‘dictLabel’
----configEdit表格编辑配置(开启编辑功能有效)Object-
----------rules规则(可依据 elementUI el-form 配置————对应 columns 的 prop 值)Object-
----------labelplaceholder 显示String-
----------editComponent组件名称可直接指定全局注册的组件,也可引入第三方组件Stringinput
----------bind第三方 UI 的 AttributesObject-
----------eventHandle第三方 UI 的 事件(返回两个参数,第一个自己自带,第二个 scope)Object-
----------event触发 handleEvent 事件的标志String-
----------type下拉或者复选框显示(select-arr/select-obj/checkbox)String-
----------list下拉选择数据源名称String-
----------arrLabeltype:select-arr 时对应显示的中文字段String-
----------arrKeytype:select-arr 时对应显示的数字字段String-
listTypeInfo下拉选择数据源{单元格编辑时需要}Object{}
title表格左上标题String /slot‘’
toolbar表格外操作栏 (显示在 table 右侧)slot-
footer底部操作区(默认隐藏,使用插槽展示“保存”按钮)slot-
isShowFooterBtn是否显示保存按钮(一般整行编辑才会开启)Booleanfalse
isEditCell是否开启编辑模式(整行编辑模式)Booleanfalse
isEdit是否显示添加按钮(在 table 的下面)Booleanfalse
highlightCurrentRow是否高亮选中行Booleanfalse
isShowTips开启单元格编辑时鼠标移入是否显示“单击可编辑”tipsBooleanfalse
columnSetting是否显示设置(隐藏/显示列)Booleanfalse
isShowTreeStyle是否开启 tree 树形结构样式Booleanfalse
isMergedCell是否开启合并单元格Booleanfalse
comparisonOperator多列行合并比较运算符String==
mergeCol第几列合并进行行合并(默认第一列)Number0
isObjShowProp是否开启对象模式渲染数据 功能(适用于一层对象数据)Booleanfalse
isShowPagination是否显示分页(默认显示分页)Booleanfalse
pageSizes每页显示个数选择器的选项设置number[][10, 20, 50, 100]
isCopy是否允许双击单元格复制Booleanfalse
spanMethod是否自定义编写合并单元格方法(跟 element 一样)funtion-
rowClickRadio是否开启点击整行选中单选框Booleanfalse
isTableColumnHidden是否开启合计行隐藏复选框/单选框/序列Booleanfalse
sortable是否所有 table 列都开启排序 若值 ‘custom’,需要监听 Table 的 sort-change 事件Boolean/String-
isSortable是否开启组件排序功能(仅有升序和降序)Booleanfalse
notSortJudge不排序条件判断规则String-
isKeyup单元格编辑是否开启键盘事件(向上、向下、回车横向的下一个输入框)Booleanfalse
defaultRadioCol设置默认选中项(单选)defaultRadioCol 值必须大于 0!Number-
btnPermissions按钮权限 store.getters 接收字段String-
isRowSort是否开启行拖拽(结合row-key配置)Booleanfalse
columnSetBind列设置按钮配置(继承el-button所有属性)Object-
----btnTxt按钮显示文字String‘列设置’
----title点击按钮下拉显示titleString‘列设置’
----sizeel-button的sizeString‘small’
----iconel-button的iconString‘el-icon-s-operation’
onlyIconSort是否开启仅点击排序图标才排序Booleanfalse
useVirtual是否开启虚拟列表Booleanfalse
maxHeightTable 的最大高度。合法的值为数字或者单位为 px 的高度。(开启虚拟列表是其值默认540)String/Numberfalse
isPaginationCumulative序列号显示是否分页累加Booleanfalse

3、events 其他事件按照el-table直接使用(如sort-change排序事件)

事件名说明返回值
page-change当前页码当前选中的页码
radioChange单选选中事件返回当前选中的整行数据
add新增按钮-
save保存按钮编辑后的所有数据
validateError单元格编辑保存校验不通过触发返回校验不通过的 prop–label 集合
handleEvent单元格编辑时触发事件configEdit 中的 event 值和对应输入的 value 值
sort-change当表格的排序条件发生变化的时候会触发该事件{ column, prop, order }
rowSort行拖拽排序后触发事件返回排序后的table数据

4、Methods 方法(继承el-table的所有方法)

事件名说明参数
save保存方法(返回编辑后的所有数据)-
resetFields对表单进行重置,并移除校验结果(单元格编辑时生效)
clearValidate清空校验规则(单元格编辑时生效)-

5、 Slots插槽

插槽名说明参数
titleTTable 左侧Title-
toolbarTTable 右侧toolbar-
expandtable.firstColumn.type:expand 展开行插槽scope
-el-table-column某列自定义插槽(slotName命名)scope
-el-table-column单元格编辑插槽(editSlotName命名)scope
-el-table-column表头合并插槽(slotNameMerge命名)scope
-操作列前一列自定义默认内容插槽-
footer底部操作区(默认隐藏,使用插槽展示“保存”按钮)-

6、 部分具体代码——代码较多,可以结合文档案例使用

 <template>
  <div class="t-table" id="t_table">
    <div class="header_wrap">
      <div class="header_title">
        {{ title }}
        <slot name="title" />
      </div>
      <div class="toolbar_top">
        <!-- 表格外操作 -->
        <div class="toolbar">
          <el-button
            v-for="(item, index) in getToolbarBtn"
            :key="index"
            @click="toolbarFun(item)"
            :icon="item.icon ? item.icon : ''"
            :type="item.type || 'primary'"
            size="small"
          >{{ item.text }}</el-button>
          <el-popover
            ref="popoverClose"
            popper-class="operator_popover operator_pop"
            class="operator_popover operator_pop"
            placement="bottom-start"
            trigger="hover"
            v-if="getToolbarDown.length"
          >
            <ul class="ulClose">
              <li
                v-for="(item, index) in getToolbarDown"
                :key="index"
                @click="toolbarFun(item)"
              >{{ item.text }}</li>
            </ul>
            <el-button size="small" type="primary" icon="el-icon-setting" slot="reference">
              操作
              <i class="el-icon-arrow-down"></i>
            </el-button>
          </el-popover>
        </div>
        <slot name="toolbar"></slot>
        <!--列设置按钮-->
        <div class="header_right_wrap" :style="{ marginLeft: isShow('toolbar') ? '12px' : 0 }">
          <slot name="btn" />
          <column-set
            v-if="columnSetting"
            v-bind="$attrs"
            :columns="columns"
            @columnSetting="(v) => (columnSet = v)"
          />
        </div>
      </div>
    </div>
    <el-table
      ref="el-table"
      :data="tableData"
      :class="{
        cursor: isCopy,
        row_sort: isRowSort,
        highlightCurrentRow: highlightCurrentRow,
        radioStyle: table.firstColumn && table.firstColumn.type === 'radio',
        treeProps: isShowTreeStyle,
        is_sort_icon:onlyIconSort
      }"
      :max-height="useVirtual?maxHeight||540:maxHeight"
      v-bind="$attrs"
      v-on="$listeners"
      :highlight-current-row="highlightCurrentRow"
      :border="table.border || isTableBorder"
      :span-method="spanMethod || objectSpanMethod"
      :cell-class-name="cellClassNameFuc"
      @sort-change="soltHandle"
      @row-click="rowClick"
      @cell-dblclick="cellDblclick"
    >
      <!-- 首列之 序列号/单选框/复选框 -->
      <div v-if="table.firstColumn">
        <!-- 复选框 -->
        <el-table-column
          :selectable="table.firstColumn.selectable"
          :type="table.firstColumn.type"
          :width="table.firstColumn.width || 50"
          :label="table.firstColumn.label"
          :fixed="table.firstColumn.fixed"
          :reserve-selection="table.firstColumn.isPaging || false"
          :align="table.firstColumn.align || 'center'"
          v-if="table.firstColumn.type === 'selection'"
        ></el-table-column>
        <!-- 单选框 -->
        <el-table-column
          :type="table.firstColumn.type"
          :width="table.firstColumn.width || 50"
          :label="table.firstColumn.label"
          :fixed="table.firstColumn.fixed"
          :align="table.firstColumn.align || 'center'"
          v-if="table.firstColumn.type === 'radio'"
        >
          <template slot-scope="scope">
            <el-radio
              v-model="radioVal"
              :label="scope.$index + 1"
              @click.native.prevent="radioChange(scope.row, scope.$index + 1)"
            ></el-radio>
          </template>
        </el-table-column>
        <!-- 序列号 -->
        <el-table-column
          :type="table.firstColumn.type"
          :width="table.firstColumn.width || 50"
          :label="table.firstColumn.label"
          :fixed="table.firstColumn.fixed"
          :align="table.firstColumn.align || 'center'"
          v-if="table.firstColumn.type === 'index'"
        >
          <template slot-scope="scope">
            <span>
              {{
              isShowPagination
              ? (table.currentPage - 1) * table.pageSize + scope.$index + 1
              : scope.$index + 1
              }}
            </span>
          </template>
        </el-table-column>
      </div>
      <!-- 主体内容 -->
      <template v-for="(item, index) in renderColumns">
        <template v-if="!item.children">
          <!-- 常规表头-->
          <el-table-column
            v-if="item.isShowCol === false ? item.isShowCol : true"
            :key="index + 'i'"
            :type="item.type"
            :label="item.label"
            :prop="item.prop"
            :min-width="item['min-width'] || item.minWidth || item.width"
            :sortable="item.sort || sortable"
            :align="item.align || 'center'"
            :fixed="item.fixed"
            :show-overflow-tooltip="useVirtual?true:item.noShowTip?false:true"
            v-bind="{ ...item.bind, ...$attrs }"
            v-on="$listeners"
          >
            <template #header v-if="item.headerRequired || item.renderHeader">
              <render-header v-if="item.renderHeader" :column="item" :render="item.renderHeader" />
              <div style="display: inline" v-if="item.headerRequired">
                <span style="color: #f56c6c; fontsize: 16px; marginright: 3px">*</span>
                <span>{{ item.label }}</span>
              </div>
            </template>
            <template slot-scope="scope">
              <template v-if="!isEditCell">
                <!-- render渲染 -->
                <template v-if="item.render">
                  <render-col
                    :column="item"
                    :row="scope.row"
                    :render="item.render"
                    :index="scope.$index"
                  />
                </template>
                <!-- customRender渲染 -->
                <template v-if="item.customRender">
                  <OptComponent
                    v-for="(comp, i) in item.customRender.comps"
                    :key="scope.$index + i.toString()"
                    v-bind="comp"
                    :scope="scope"
                  />
                </template>
                <!-- 自定义插槽 -->
                <template v-if="item.slotName">
                  <slot :name="item.slotName" :param="scope"></slot>
                </template>
                <!-- 单个单元格编辑 -->
                <template v-if="item.canEdit">
                  <el-form
                    :model="tableData[scope.$index]"
                    :rules="isEditRules ? table.rules : {}"
                    class="t_edit_cell_form"
                    :ref="`formRef-${scope.$index}-${
                      item.prop || scope.column.property
                    }`"
                    @submit.native.prevent
                  >
                    <single-edit-cell
                      :configEdit="item.configEdit"
                      v-model="scope.row[scope.column.property]"
                      :prop="item.prop"
                      :record="scope"
                      @handleEvent="
                        (event, model) =>
                          $emit('handleEvent', event, model, scope.$index)
                      "
                      @Keyup="handleKeyup"
                      v-on="$listeners"
                      v-bind="$attrs"
                      ref="editCell"
                    >
                      <!-- 遍历子组件非作用域插槽,并对父组件暴露 -->
                      <template v-for="(index, name) in $slots" v-slot:[name]>
                        <slot :name="name" />
                      </template>
                      <!-- 遍历子组件作用域插槽,并对父组件暴露 -->
                      <template v-for="(index, name) in $scopedSlots" v-slot:[name]="data">
                        <slot :name="name" v-bind="data"></slot>
                      </template>
                    </single-edit-cell>
                  </el-form>
                </template>
                <!-- 字典过滤 -->
                <template v-if="item.filters && item.filters.list">
                  {{
                  scope.row[item.prop]
                  | constantEscape(
                  table.listTypeInfo[item.filters.list],
                  item.filters.key || "dictValue",
                  item.filters.label || "dictLabel"
                  )
                  }}
                </template>
                <div
                  v-if="
                    !item.render &&
                    !item.slotName &&
                    !item.customRender &&
                    !item.canEdit &&
                    !item.filters
                  "
                  :style="{ color: txtChangeColor(scope) }"
                >
                  <span v-if="isObjShowProp">
                    {{
                    item.prop.includes(".")
                    ? scope.row[item.prop.split(".")[0]][
                    item.prop.split(".")[1]
                    ]
                    : scope.row[item.prop]
                    }}
                  </span>
                  <span v-else>{{ scope.row[item.prop] }}</span>
                </div>
              </template>
              <template v-else>
                <!-- 整行编辑 -->
                <edit-cell
                  :configEdit="item.configEdit"
                  v-model="scope.row[scope.column.property]"
                  v-bind="$attrs"
                  v-on="$listeners"
                  ref="editCell"
                >
                  <template v-for="(index, name) in $slots" :slot="name">
                    <slot :name="name" />
                  </template>
                </edit-cell>
              </template>
            </template>
          </el-table-column>
        </template>
        <!-- 表头合并单元格 -->
        <t-table-column v-else :key="index + 'i'" :item="item">
          <template v-for="(index, name) in $slots" v-slot:[name]>
            <slot :name="name" />
          </template>
          <template v-for="(index, name) in $scopedSlots" v-slot:[name]="data">
            <slot :name="name" v-bind="data"></slot>
          </template>
        </t-table-column>
      </template>
      <slot></slot>
      <!-- 操作按钮 -->
      <el-table-column
        v-if="table.operator"
        :fixed="table.operatorConfig && table.operatorConfig.fixed"
        :label="(table.operatorConfig && table.operatorConfig.label) || '操作'"
        :min-width="
          (table.operatorConfig &&
            (table.operatorConfig.width || table.operatorConfig.minWidth)) ||
          100
        "
        :align="
          (table.operatorConfig && table.operatorConfig.align) || 'center'
        "
        class-name="operator"
      >
        <template slot-scope="scope">
          <div class="operator_btn" :style="table.operatorConfig && table.operatorConfig.style">
            <el-button
              v-for="(item, index) in table.operator"
              :key="index"
              @click="item.fun && item.fun(scope.row, scope.$index, tableData)"
              :type="item.type || 'text'"
              :style="item.style"
              :icon="item.icon ? item.icon : ''"
              :disabled="item.disabled"
              :size="item.size||'mini'"
              v-show="checkIsShow(scope, item)"
            >
              <!-- customRender渲染 -->
              <template v-if="item.customRender">
                <OptComponent
                  v-for="(comp, i) in item.customRender.comps"
                  :key="scope.$index + i.toString()"
                  v-bind="comp"
                  :scope="scope"
                />
              </template>
              <!-- render渲染 -->
              <template v-if="item.render">
                <render-col
                  :column="item"
                  :row="scope.row"
                  :render="item.render"
                  :index="scope.$index"
                />
              </template>
              <span v-if="!item.render && !item.customRender">
                {{
                item.text
                }}
              </span>
            </el-button>
          </div>
        </template>
      </el-table-column>
    </el-table>
    <div v-if="isEdit" class="edit_cell">
      <el-button type="dashed" block size="small" @click="() => $emit('add')">
        {{
        cellEditBtnTxt
        }}
      </el-button>
    </div>
    <!-- 分页器 -->
    <el-pagination
      v-show="tableData && tableData.length && isShowPagination"
      :current-page="table.currentPage"
      @current-change="handlesCurrentChange"
      :page-sizes="pageSizes"
      :page-size="table.pageSize"
      :layout="
        layoutSize
          ? 'total, prev, pager, next'
          : 'total, sizes, prev, pager, next, jumper'
      "
      :total="table.total"
      v-bind="$attrs"
      v-on="$listeners"
      background
    ></el-pagination>
    <!-- 表格底部按钮 -->
    <footer class="handle_wrap" v-if="isShowFooterBtn && tableData && tableData.length > 0">
      <slot name="footer" />
      <div v-if="!$slots.footer">
        <el-button type="primary" @click="save">保存</el-button>
      </div>
    </footer>
  </div>
</template>

6、源码地址

gitHub组件地址

gitee码云组件地址


相关文章

基于ElementUi&Antd再次封装基础组件文档

vue3+ts基于Element-plus再次封装基础组件文档

;