对table组件的二次封装

一、Table组件

1、封装Table组件为tableList.vue文件如下:

javascript 复制代码
<template>
  <!-- 主列表 -->
  <el-table :data='tableData' :height='tableHeight' :show-header='showHeader' stripe @expand-change="expandSelect" :expand-row-keys="expands" @cell-click="handleCellClick" empty-text='暂无数据' class='el_tab_alage' :header-cell-style="cellHeaderStyle" :row-key='getRowKeys' @selection-change="handleSelectionChange">
    <!-- 单选框 -->
    <el-table-column align="center" width="50" label="" v-if="tableSelection.key === true && tableSelection.type === 'radio'">
      <template slot-scope="scope">
        <el-radio :label="scope.$index" v-model="radio" @change="handleTemplateRow(scope.$index, scope.row)">&nbsp;</el-radio>
      </template>
    </el-table-column>
    <!-- index索引 -->
    <el-table-column label="序号" type="index" width="50" align="center" v-if="tableSelection.key === true && tableSelection.type === 'index'"></el-table-column>
    <!-- 多选框 -->
    <el-table-column type="selection" width="50" align="center" v-if="tableSelection.key === true && tableSelection.type === 'selection'"></el-table-column>
    <!-- 列表表头-->
    <el-table-column type="expand" v-if="tableSelection.key === true && tableSelection.type === 'expand'">
      <template slot-scope="scope">
        <el-form label-position="left" inline class="demo-table-expand">
          <el-form-item :label="index.label" v-for="(index, item) in tableLabel" :key='item' v-show="index.type === 'expand'">
            <span>{{scope.row[index.list]}}</span>
          </el-form-item>
        </el-form>
      </template>
    </el-table-column>
    <template v-for="(index, item) in tableLabel">
      <el-table-column fit :align='index.tableAlign ? index.tableAlign : "center"' :key='item' :sortable='index.sort' v-if="index.type !== 'expand'" :label="index.label" :width="index.width" :show-overflow-tooltip="index.overflowShow === 'hidden' ? true : false" :prop="index.list">
        <template slot-scope="scope">
          <!-- 图片 -->
          <template v-if="index.type === 'image'">
            <el-image v-if="scope.row[index.list] !== ''" style="width: 100px; height: 50px;" :src="scope.row[index.list]">
            </el-image>
            <div v-else></div>
          </template>
          <!-- 头像 -->
          <template v-else-if="index.type === 'head'">
            <el-image v-if="!(scope.row[index.list] === '' || scope.row[index.list] === null)" style="width: 50px; height: 50px;" :src="scope.row[index.list]">
            </el-image>
            <div v-else></div>
          </template>
          <!-- 按钮 -->
          <template v-else-if="index.type === 'btn'">
            <el-button type="text" @click.native.prevent="index.method(scope.row, scope)">
              <u>{{scope.row[index.list]}}</u>
            </el-button>
          </template>
          <!-- 下拉 -->
          <template v-else-if="index.type === 'select'">
            <el-select v-model="scope.row[index.list]" @change="changeType($event, scope.row, item)" size="medium">
              <el-option v-for="item in index.options" :key="item.value" :label="item.label" :value="item.value">
              </el-option>
            </el-select>
          </template>
          <!-- 开关按钮 -->
          <template v-else-if="index.type === 'switch'">
            <div v-if="index.noSwitch(scope.row)">
              <el-switch @change="index.method(scope.row, scope)" v-model="scope.row[index.list]" :inactive-value="index.offValue ? index.offValue : 'off'" :active-value="index.onValue ? index.onValue : 'on'" :inactive-text="index.offText ? index.offText : ''" :active-text="index.onText ? index.onText : ''" :inactive-color="index.offColor ? index.offColor : '#ff4949'" :active-color="index.onColor ? index.onColor : '#13ce66'"></el-switch>
            </div>
          </template>
          <!-- 内容自定义 -->
          <template v-else-if="index.type === 'html'">
            <div v-html="index.code(scope.row)" class="theHtml"></div>
          </template>
          <!-- 正常显示 -->
          <template v-else>
            <!-- {{scope.row[index.list]}} -->
            {{scope.row[index.list]?scope.row[index.list]:'-'}}
          </template>
        </template>
      </el-table-column>
    </template>
    <!-- 正常按钮操作 -->
    <el-table-column fit align='center' :label="tableOption.label" :fixed="tableOption.fixed ? tableOption.fixed : false" :width="tableOption.width" v-if="tableOption.value === 0">
      <template style="margin-left: 30px;" slot-scope="scope">
        <el-button align='right' v-for="(value, item) in tableOption.options" :key='item' v-if="value.show ? value.show(scope.row) : true" :type="value.type ? value.type : 'text'" :style="value.style ? JSON.parse(value.style) : {}" :plain='value.plain ? value.plain : false' :round='value.round ? value.round : false' :size='value.size ? value.size : "medium"' :icon="value.icon" @click.native.prevent="value.method(scope.row, scope)">{{value.label}}
        </el-button>
      </template>
    </el-table-column>
  </el-table>
</template>
<script>
export default {
  data () {
    return {
      radio: '',
      cellHeaderStyle: {
        fontSize: "16px",
        color: "#606266"
      },
      expands: [],
      getRowKeys (row) {
        return row.id
      }
    }
  },
  props: {
    tableData: {
      type: Array,
      default: () => { }
    },
    tableHeight: {
      type: Number,
      default: () => {
        return null
      }
    },
    showHeader: {
      type: Boolean,
      default: () => {
        return true
      }
    },
    tableSelection: {
      type: Object,
      default: () => {
        return {
          key: false,
          type: '',
          detaile: false
        }
      }
    },
    tableLabel: {
      type: Array,
      default: () => { }
    },
    tableOption: {
      type: Object,
      default: () => {
        return {
          value: 999
        }
      }
    }
  },

  methods: {
    handleSelectionChange (val) {
      this.$emit('onHandleSelectionChange', val)
    },
    handleTemplateRow (index, row) {
      this.$emit('onHandleTemplateRow', row)
    },
    changeType (event, row) {
      this.$emit('onChangeType', event, row)
    },
    expandSelect (row, expandedRows) {
      const that = this
      if (expandedRows.length) {
        that.expands = []
        if (row) {
          that.expands.push(row.id)
        }
      } else {
        that.expands = []
      }
    },
    handleCellClick (row, column, cell, event) {
      this.$emit('onHandleCellClick', row, column)

    },
  }
}
</script>

<style lang="scss" scoped>
.demo-table-expand {
  font-size: 0;
}
/deep/ .gutter {
  display: inline !important;
}
.el-table thead th {
  background-color: #f9f9f9;
}
.demo-table-expand label {
  width: 90px !important;
  color: #99a9bf !important;
}
.demo-table-expand .el-form-item {
  margin-right: 0 !important;
  margin-bottom: 0 !important;
  width: 50%;
}

.el-table--scrollable-x .el-table__body-wrapper::-webkit-scrollbar {
  overflow-x: hidden;
}

.el-table .el-table__body-wrapper::-webkit-scrollbar-thumb {
  background-color: rgba(169, 178, 196, 0.3) !important;
}

.el-table__fixed-right {
  height: calc(100% - 27px) !important;
}

.el_tab_alage {
  border: 1px solid #f0f0f0 !important;
  border-bottom: none !important;
  margin: {
    bottom: 20px !important;
    top: 20px !important;
  }
  .theHtml {
    width: 100%;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
  }

  .el-table__row {
    .cell {
      -webkit-line-clamp: 3 !important;
      -webkit-box-orient: vertical !important;
    }
    .el-button {
      margin: {
        top: 0 !important;
        right: 5px !important;
        bottom: 0 !important;
        left: 0 !important;
      }
    }
  }
}
</style>

2、使用组件

html 复制代码
<Table :table-label="tableHeader" v-loading="isSubmitLoading" :table-data="tableData" :table-option="tableOption" :table-selection="tableSelection" @onHandleSelectionChange="handleSelectionChange"></Table>
javascript 复制代码
import Pagination from "@/components/table/Pagination.vue";
export default {
	components: { Table },
	data() {
		return {
		 	isSubmitLoading: false,
			tableHeader:[],
			tableData: [],
			tableOption: {},
			tableSelection: {}
		}
	},
	methods: {
		handleSelectionChange() {},
		changeStatus() {}
	}
}

tableHeader中每个itemlabel(标题)、type(类型,分别有image图片,head头像,btn按钮,select下拉,switch开关,html自定义内容等)、list(对应列内容的字段名)、code: (row) => { return }(对列数据操作后返回函数)、overflowShow: 'hidden'(当内容过长被隐藏时显示 tooltip) 等。

如:

javascript 复制代码
tableHeader:[
	{
    label: "姓名", type: "html", list: 'name', overflowShow: 'hidden', code: (row) => {
      return row.type === 0 ? row.name : row.name + '(管理员)'
    }
  },{
  	label: "手机号", type: "html", overflowShow: 'hidden', code: (row) => {
      return row.phone === '' || row.phone === null ? '-' : row.phone
     }
  },{ 
 	label: "创建时间", list: 'createTime', overflowShow: 'hidden' 
  },{
     type: "switch",
     label: '状态',
     list: "state",
     offText: '禁用',
     onText: '启用',
     offValue: "1",
     onValue: "0",
     noSwitch: (row) => {
       return true;
     },
     method: (row) => {
       this.changeStatus(row)
     },
   },
]

tableOption中有label(标题)、width(对应列的宽度)、options(操作项,类型为数组,每个item中有label(标题)、color(颜色)、type(类型)、icon(图标)、method: (row) => {}(点击后执行的方法)、show:(row) => {}(显示或者隐藏的判断条件)

如:

javascript 复制代码
tableOpction: {
  label: '操作',
  width: '160px',
  options: [{
    label: '删除',
    color: '#DB864E',
    type: "text",
    icon: "",
    method: (row) => {
      this.doDelete(row.id);
    },
    show: (row) => {
      return row.type == 1 ? true : false
    }
  },
  {
    label: '编辑',
    color: '#DB864E',
    key: 1,
    type: "text",
    icon: "",
    method: (row) => {
      this.doEdit(row.id);
    },
  }
  ]
}

tableSelection配置中有key(为true时显示)、type(有index(显示索引)、selection(显示多选框)),操作多选框时会触发相应的handleSelectionChange回调函数,可以拿到已选中的数据。

javascript 复制代码
data() {
	return {
		tableSelection: {
	       key: true,
	       type: "selection"
	    }
	}
},
methods: {
	handleSelectionChange(val) {}
}

二、Pagination组件

1、封装Pagination组件为Pagination.vue文件如下:

html 复制代码
<template>
  <div class="pagination">
    <el-pagination
      @size-change="handleSizeChange"
      @current-change="handleCurrentChange"
      :current-page="page.page"
      :page-sizes="pageSizes"
      :page-size="page.limit"
      layout="total, sizes, prev, pager, next, jumper" :total="total">
    </el-pagination>
  </div>
</template>

<script>
export default {
  props: {
    total: {
      type: Number,
      default: () => {}
    },
    pageSizes: {
      type: Array,
      default: () => {
        return [5, 10, 20, 100]
      }
    }
  },
  data() {
    return {
      page: {
        page: 1,
        limit: 20
      }
    }
  },

  methods: {
    Page(val) {
      this.page.page = val
    },
    handleSizeChange(val) {
      this.page.limit = val
      this.$emit('pageChange', this.page)
    },
    handleCurrentChange(val) {
      this.page.page = val
      this.$emit('pageChange', this.page)
    }
  }
}
</script>

<style scoped lang="scss">
  .pagination {
    float: right;
    padding: 1% 4% 2% 0px;
  }
</style>

2、使用组件

html 复制代码
<pagination ref="page" :total="total" @pageChange="pageChange"></pagination>
javascript 复制代码
import Pagination from "@/components/table/Pagination.vue";
export default {
	components: { Pagination },
	data() {
		return {
			total: 0,
			pageSize: 20,
			currentPage: 1
		}
	},
	methods: {
		pageChange (item) {
	      this.pageSize = item.limit;
	      this.currentPage = item.page;
	      this.fetchData(); // 请求数据
	    },
	}
}

total(总条目数)、pageChange(pageSize 改变时会触发)

相关推荐
ywf121525 分钟前
前端的dist包放到后端springboot项目下一起打包
前端·spring boot·后端
恋猫de小郭32 分钟前
2026,Android Compose 终于支持 Hot Reload 了,但是收费
android·前端·flutter
hpoenixf6 小时前
2026 年前端面试问什么
前端·面试
还是大剑师兰特7 小时前
Vue3 中的 defineExpose 完全指南
前端·javascript·vue.js
泯泷7 小时前
阶段一:从 0 看懂 JSVMP 架构,先在脑子里搭出一台最小 JSVM
前端·javascript·架构
mengchanmian8 小时前
前端node常用配置
前端
华洛8 小时前
利好打工人,openclaw不是企业提效工具,而是个人助理
前端·javascript·产品经理
xkxnq8 小时前
第六阶段:Vue生态高级整合与优化(第93天)Element Plus进阶:自定义主题(变量覆盖)+ 全局配置与组件按需加载优化
前端·javascript·vue.js
A黄俊辉A9 小时前
vue css中 :global的使用
前端·javascript·vue.js
小码哥_常9 小时前
被EdgeToEdge适配折磨疯了,谁懂!
前端