elementPlus的table二次封装

一、简介

公司业务可能需要进行一些组件的封装,基于第三方elementPlus框架,进行符合UI设计原型的组件封装,这篇主要讲解Table表格的封装,目的主要是梳理封装的思路,下面的代码并不是提供完整的源码,因此不包含样式代码。

二、环境准备

webpack+vue3+elementPlus

官方地址:https://element-plus.gitee.io/zh-CN/component/table.html

三、实现步骤

1,成果示例

2,需求分析

(1)表格组件内包含表格数据展示+分页器+搜索input;
(2)表格每列的内容自定义修改,并且支持自定义标签;
(3)搜索和分页器都需要和表格数据联动;
(4)checkbox勾选状态和分页器联动;
(5)搜索栏支持自定义;

3,具体代码

(1)子组件
  • 最终代码示例:
    <el-table
      ref="table"
      :data="tableData"
      :height="height"
      :style="{ fontSize: fontSize, fontFamily: fontFamily }"
      :border="border"
      :stripe="stripe"
      :row-key="
        (row) => {
          return row.id;
        }
      "
      :header-cell-style="{ 'text-align': headerCellStyle }"
      :cell-style="cellStyle"
      @selection-change="handleSelectionChange"
    >
      <!-- checkbox列 -->
      <el-table-column
        v-if="selection"
        type="selection"
        width="56"
        align="center"
        :fixed="fixed"
      >
      </el-table-column>
      <!-- 序号列 -->
      <el-table-column
        v-if="index"
        type="index"
        width="56"
        label="序号"
        :fixed="fixed"
      ></el-table-column>
      <!-- columnData属性列 -->
      <template v-for="item in columnData" :key="item.id">
        <el-table-column
          v-if="item.type === 'link'"
          :label="item.label"
          :prop="item.prop"
          :width="item.width"
          :align="item.align"
          :fixed="item.fixed"
          :sortable="item.sort"
          :formatter="item.formatter"
        >
          <template #default="scope">
            <slot :name="item.prop" v-bind="scope"></slot>
          </template>
        </el-table-column>
        <el-table-column
          v-else-if="item.type === 'tag'"
          :label="item.label"
          :prop="item.prop"
          :width="item.width"
          :align="item.align"
          :fixed="item.fixed"
          :sortable="item.sort"
          :filters="item.filters"
          :filter-method="filterTag"
          :formatter="item.formatter"
        >
          <template #default="scope">
            <slot :name="item.prop" v-bind="scope"></slot>
          </template>
        </el-table-column>
        <el-table-column
          v-else
          :label="item.label"
          :prop="item.prop"
          :width="item.width"
          :align="item.align"
          :fixed="item.fixed"
          :sortable="item.sort"
          :formatter="item.formatter"
        >
        </el-table-column>
      </template>
    </el-table>
  • 实现思路:
1)table的属性和column的属性要分开;
<!--摘自elementPlus表格(基础用法)-->
<template>
  <el-table :data="tableData" style="width: 100%">
    <el-table-column prop="date" label="Date" width="180" />
    <el-table-column prop="name" label="Name" width="180" />
    <el-table-column prop="address" label="Address" />
  </el-table>
</template>

原因:我们来分析一下上面的代码,首先我们需要用组件封装的思想思考。el-table标签上的属性都可以通过父子传值的方式传进组件。但是el-table-column到底有几个我们不确定,那我们很自然的就会想到组件内部需要用到v-for循环,循环的数据也是通过外部传进来。

所以子组件内部发展为:

    <el-table
      ref="table"
      :data="tableData"
    >
      <!-- columnData属性列 -->
      <template v-for="item in columnData" :key="item.id">
        <el-table-column
          :label="item.label" //每列标题内容,参考elemengPlus
          :prop="item.prop" //每列字段名称,参考elemengPlus
          :width="item.width" //每列宽度,参考elemengPlus
          :align="item.align" //每列对齐方式,参考elemengPlus
          :fixed="item.fixed" //首列和标题固定,参考elemengPlus
          :sortable="item.sort" //排序,参考elemengPlus
          :formatter="item.formatter" //支持内容自定义方法,参考elemengPlus
        >
        </el-table-column>
      </template>
    </el-table>
    <script setup>
        import {defineProps} from 'vue'
        defineProps({
            tableData:{
                type:Array
            },
            columnData:{
                type:Array
            }
        })
    </script>

父页面传入的数据发展为:

    const columnData = ref([
      {
        id: 0,
        prop: "contractName",
        // width: "140",
        align: "left",
        label: "常规名称长字段",
      },
      {
        id: 1,
        prop: "contractDate",
        // width: "200",
        align: "left",
        label: "合同日期",
      },
      {
        id: 2,
        prop: "contractAmout",
        // width: "130",
        align: "right",
        label: "合同金额",
        sort: true,
        formatter: (row) => {
          return <div>{row.contractAmout}万</div>;
        },
      },
      {
        id: 3,
        prop: "contractNum",
        // width: "100",
        align: "left",
        label: "合同编号",
      },
      {
        id: 4,
        prop: "operation",
        width: "150",
        align: "left",
        label: "操作",
        type: "slot",
        formatter: (row,column,cellValue,index) => {
          //编辑按钮事件
          const handleEdit = (val) => {
            console.log('row',row);
            console.log('column',column);
            console.log('cellValue',cellValue);
            console.log('index',index);
            // console.log('val',val);
            alert(val.contractName);
          };
          //删除按钮事件
          const handleDelete = (val) => {
            console.log('row',row);
            console.log('column',column);
            console.log('cellValue',cellValue);
            console.log('index',index);
            console.log('row',row);
            // console.log('val',val);
            alert(val.contractName);
          };
          return (
            <div>
              <el-button
                type="primary"
                size="small"
                onClick={() => {
                  handleEdit(row,column,cellValue,index);
                }}
              >
                编辑
              </el-button>
              <el-button
                type="danger"
                size="small"
                onClick={() => {
                  handleDelete(row,column,cellValue,index);
                }}
              >
                删除
              </el-button>
            </div>
          );
        },
      },
    ]);
//表格数据的key键名要和columnData中的prop保持一致
    const tableData = ref([
      {
        "id": "1",
        "contractName": "医院设备1",
        "contractDate": "2023-01-30 14:44:19",
        "contractAmout": "50000",
        "contractNum": "1",
      },
      {
        "id": "2",
        "contractName": "医院设备2",
        "contractDate": "2023-01-29 14:44:19",
        "contractAmout": "30000",
        "contractNum": "2",
      },
      {
        "id": "3",
        "contractName": "医院设备3",
        "contractDate": "2023-01-28 15:44:19",
        "contractAmout": "75000",
        "contractNum": "3",
      },
      {
        "id": "4",
        "contractName": "医院设备4",
        "contractDate": "2023-01-27 15:44:19",
        "contractAmout": "80000",
        "contractNum": "4",
      },
      {
        "id": "5",
        "contractName": "医院设备5",
        "contractDate": "2023-01-26 15:44:19",
        "contractAmout": "80000",
        "contractNum": "5",
      },
      {
        "id": "6",
        "contractName": "医院设备6",
        "contractDate": "2023-01-25 15:44:19",
        "contractAmout": "80000",
        "contractNum": "6",
      },
      {
        "id": "7",
        "contractName": "医院设备7",
        "contractDate": "2023-01-24 15:44:19",
        "contractAmout": "80000",
        "contractNum": "7",
      },
      {
        "id": "8",
        "contractName": "医院设备8",
        "contractDate": "2023-01-23 15:44:19",
        "contractAmout": "80000",
        "contractNum": "8",
      },
      {
        "id": "9",
        "contractName": "医院设备9",
        "contractDate": "2023-01-22 15:44:19",
        "contractAmout": "80000",
        "contractNum": "9",
      },
      {
        "id": "10",
        "contractName": "医院设备10",
        "contractDate": "2023-01-21 15:44:19",
        "contractAmout": "80000",
        "contractNum": "10",
      }
    ]);

注意:表格数据的key键名必须和columnData中的prop保持一致;

2)表格的checkbox列和序号列由外部决定是否需要;

也就是外部传入个布尔值去控制是否渲染这两列即可,所以子组件再前面的基础上再加上:

      <!-- checkbox列 -->
      <el-table-column
        v-if="selection"
        type="selection"
        width="56"
        align="center"
        :fixed="fixed"
      >
      </el-table-column>

      <!-- 序号列 -->
      <el-table-column
        v-if="index"
        type="index"
        width="56"
        label="序号"
        :fixed="fixed"
      ></el-table-column>

    <script setup>
        import {defineProps} from 'vue'
        defineProps({
            tableData:{
                type:Array
            },
            columnData:{
                type:Array
            },
            selection: Boolean,
            index: Boolean,
        })
    </script>

如果需要渲染这两列,父页面使用的时候直接传入相应的布尔值即可,如下示例:

    <flex-table
      ref="flexTable"
      :columnData="columnData"
      :tableData="tableData"
      selection
      index
    >
3)插槽支持更多框架内容自定义;

由于formatter的方法内部是react写法,不支持template标签。如果自定义的内容包含el-tag标签或其他可扩展内容时,可以通过插槽实现;

子组件内部就变成了:

      <!-- columnData属性列 -->
      <template v-for="item in columnData" :key="item.id">
        <!-- 扩展列 -->
        <el-table-column
          v-if="item.type === 'link'"
          :label="item.label"
          :prop="item.prop"
          :width="item.width"
          :align="item.align"
          :fixed="item.fixed"
          :sortable="item.sort"
          :formatter="item.formatter"
        >
          <template #default="scope">
            <slot :name="item.prop" v-bind="scope"></slot>
          </template>
        </el-table-column>
        <!-- 标签列 -->
        <el-table-column
          v-else-if="item.type === 'tag'"
          :label="item.label"
          :prop="item.prop"
          :width="item.width"
          :align="item.align"
          :fixed="item.fixed"
          :sortable="item.sort"
          :filters="item.filters"
          :filter-method="filterTag"
          :formatter="item.formatter"
        >
          <template #default="scope">
            <slot :name="item.prop" v-bind="scope"></slot>
          </template>
        </el-table-column>
        <!-- 其他正常列 -->
        <el-table-column
          v-else
          :label="item.label"
          :prop="item.prop"
          :width="item.width"
          :align="item.align"
          :fixed="item.fixed"
          :sortable="item.sort"
          :formatter="item.formatter"
        >
        </el-table-column>
      </template>

父页面使用的时候就可以通过插槽的名称来实现内容的自定义:

tag标签插槽:

<template #state="{ row }">
  <el-tag v-if="row.state === '进行中'" type="primary">{{
    row.state
    }}</el-tag>
  <el-tag v-if="row.state === '已完成'" type="success">{{
    row.state
    }}</el-tag>
  <el-tag v-if="row.state === '已终止'" type="danger">{{
    row.state
    }}</el-tag>
  <el-tag v-if="row.state === '已中止'" type="warning">{{
    row.state
    }}</el-tag>
  <el-tag v-if="row.state === '已取消'" type="info">{{
    row.state
    }}</el-tag>
</template>

link扩展链接插槽:

<template #link="{ row }">
    <ul class="linkMenu">
        <li>
          <el-link type="primary" v-if="row.link === 1">催填</el-link>
          <el-link type="primary" v-if="row.link === 2">汇总</el-link>
          <el-link type="primary" v-if="row.link === 3">重新汇总</el-link>
          <el-link type="primary" v-if="row.link === 4">重新确认</el-link>
          <el-link type="primary" v-if="row.link === 5">调整</el-link>
        </li>
    </ul>
</template>
4)配置搜索表单
<!--labelWidth和labelPosition是el-form的属性,参考elementPlus-->
<el-form :label-width="labelWidth" :label-position="labelPosition">
    <slot name="search"></slot>
</el-form>

在el-table标签上方加上如上代码,使用插槽支持自定义;

在父页面使用的时候:

      <!-- 搜索插槽 -->
      <template v-slot:search>
        <div class="ipt-btn">
          <!-- 输入框 -->
          <el-row gutter="0">
            <!-- 第一行 -->
            <el-col :span="8">
              <el-form-item label="标题标题">
                <el-input
                  placeholder="请输入"
                  v-model="form.value1"
                  @keyup.enter="handleFilter(form)"
                />
              </el-form-item>
            </el-col>
            <el-col :span="8">
              <el-form-item label="标题标题">
                <el-input
                  placeholder="请输入"
                  v-model="form.value2"
                  @keyup.enter="handleFilter(form)"
                />
              </el-form-item>
            </el-col>
            <el-col :span="8">
              <el-form-item label="标题标题">
                <el-input
                  placeholder="请输入"
                  v-model="form.value3"
                  @keyup.enter="handleFilter(form)"
                />
              </el-form-item>
            </el-col>
            <!-- 第二行 -->
            <el-col :span="8">
              <el-form-item label="标题标题" v-show="isShow">
                <el-select v-model="form.value5" @change="handleFilter(form)">
                  <el-option label="选项一" value="选项一">选项一</el-option>
                  <el-option label="选项二" value="选项二">选项二</el-option>
                </el-select>
              </el-form-item>
            </el-col>
            <el-col :span="8">
              <el-form-item label="标题标题" v-show="isShow">
                <el-date-picker
                  value-format="YYYY-MM-DD"
                  placeholder="请选择"
                  v-model="form.value6"
                  @change="handleFilter(form)"
                ></el-date-picker>
              </el-form-item>
            </el-col>
            <el-col :span="8">
              <el-form-item label="标题标题" v-show="isShow">
                <el-date-picker
                  type="datetime"
                  placeholder="请选择"
                  v-model="form.value7"
                  value-format="YYYY-MM-DD HH:mm:ss"
                  @change="handleFilter(form)"
                ></el-date-picker>
              </el-form-item>
            </el-col>
          </el-row>
          <!-- 按钮 -->
          <div class="btn">
            <el-form-item class="btns">
              <el-button
                class="arrow"
                :icon="isShow ? ArrowUp : ArrowDown"
                @click="isShow = !isShow"
              ></el-button>
              <el-button
                class="reset"
                @click="resetForm"
                >重置</el-button
              >
              <el-button
                class="confirm"
                type="primary"
                @click="onSubmit"
                >确定</el-button
              >
            </el-form-item>
          </div>
        </div>
      </template>
    <script setup>
    import {ref, reactive} from 'vue'
    const isShow = ref(false);
    const form = reactive({
      value1: "张三",
      value2: "10",
      value3: "",
      value4: "",
      value5: "",
      value6: "",
      value7: "",
    });
    </script>

展开效果图:

隐藏效果图:

5)配置分页器

效果图:

直接运用el-pagination,html代码示例:

    <div class="bottom_box">
    <!-- 左侧统计checkbox选中状态 -->
      <ul class="flex-ul">
        <li>
          已选择<span style="color: #3480fb" v-if="checkNumber > 0">{{
            checkNumber
          }}</span
          >项
        </li>
        <li>|</li>
        <li class="cancelCheckBox" @click="toggleSelection">
          <span style="color: #3480fb">取消</span>
        </li>
      </ul>
    <!-- 右侧分页器组件 -->
      <el-pagination
        :small="small" //是否启用小型分页器,字号12px
        :background="background" //页码选中是否有背景色
        layout="total, prev, pager, next, sizes, jumper" //分页器各组件
        :total="total" //总条数
        :pager-count="4" //设置最大按钮数
        :page-size="pageSize" //每页显示条数
        :page-sizes="pageSizes" //每页显示个数选择器的选项设置
        @size-change="sizeChange" //page-size 改变时触发
        @current-change="handleCurrentChange" //页码发生变化触发事件
      />
    </div>

参考elementPlus的方法,结合vue3的写法,js代码示例:

    //checkbox点击事件
    const instance = getCurrentInstance();
    const table = ref(null);
    const multipleSelection = ref([]);
    const checkNumber = ref(null);
    //取消勾选事件
    const toggleSelection = () => {
      var ultipleTabInstance = toRefs(instance.refs.table);
      ultipleTabInstance.clearSelection.value();
    };
    //勾选checkbox事件
    const handleSelectionChange = (val) => {
      console.log("val", val);
      multipleSelection.value = val;
      checkNumber.value = multipleSelection.value.length;
    };
(2)父组件
  • 最终代码示例:
    <flex-table
      ref="flexTable"
      size="large"
      disabled
      background
      prevIcon="ArrowLeft"
      nextIcon="ArrowRight"
      :url="apiUrl.tableList2"
      :requestData="request_data"
      :columnData="columnData"
      :pageSizes="pageSizes"
      fontSize="14px"
      fontFamily="PingFangSC-Regular"
      labelWidth="110"
      selection
      border
      fixed
    >
      <!-- 链接插槽 -->
      <template #link="{ row }">
        <ul class="linkMenu">
          <li>
            <el-link type="primary" v-if="row.link === 1">催填</el-link>
            <el-link type="primary" v-if="row.link === 2">汇总</el-link>
            <el-link type="primary" v-if="row.link === 3">重新汇总</el-link>
            <el-link type="primary" v-if="row.link === 4">重新确认</el-link>
            <el-link type="primary" v-if="row.link === 5">调整</el-link>
          </li>
          <li>|</li>
          <li v-if="row.link === 1">
            <el-dropdown>
              <span class="el-dropdown-link">
                二级配置
                <el-icon class="el-icon--right">
                  <arrow-down />
                </el-icon>
              </span>
              <template #dropdown>
                <el-dropdown-menu>
                  <el-dropdown-item>更多菜单一</el-dropdown-item>
                  <el-dropdown-item>更多菜单二</el-dropdown-item>
                  <el-dropdown-item>更多菜单三</el-dropdown-item>
                  <el-dropdown-item>更多菜单四</el-dropdown-item>
                </el-dropdown-menu>
              </template>
            </el-dropdown>
          </li>
          <li v-if="row.link === 2">
            <el-dropdown>
              <span class="el-dropdown-link">
                上会结束
                <el-icon class="el-icon--right">
                  <arrow-down />
                </el-icon>
              </span>
              <template #dropdown>
                <el-dropdown-menu>
                  <el-dropdown-item>更多菜单一</el-dropdown-item>
                  <el-dropdown-item>更多菜单二</el-dropdown-item>
                  <el-dropdown-item>更多菜单三</el-dropdown-item>
                  <el-dropdown-item>更多菜单四</el-dropdown-item>
                </el-dropdown-menu>
              </template>
            </el-dropdown>
          </li>
          <li v-if="row.link === 3">
            <el-dropdown>
              <span class="el-dropdown-link">
                二级配置
                <el-icon class="el-icon--right">
                  <arrow-down />
                </el-icon>
              </span>
              <template #dropdown>
                <el-dropdown-menu>
                  <el-dropdown-item>更多菜单一</el-dropdown-item>
                  <el-dropdown-item>更多菜单二</el-dropdown-item>
                  <el-dropdown-item>更多菜单三</el-dropdown-item>
                  <el-dropdown-item>更多菜单四</el-dropdown-item>
                </el-dropdown-menu>
              </template>
            </el-dropdown>
          </li>
          <li v-if="row.link === 4">
            <el-dropdown>
              <span class="el-dropdown-link">
                上会结束
                <el-icon class="el-icon--right">
                  <arrow-down />
                </el-icon>
              </span>
              <template #dropdown>
                <el-dropdown-menu>
                  <el-dropdown-item>更多菜单一</el-dropdown-item>
                  <el-dropdown-item>更多菜单二</el-dropdown-item>
                  <el-dropdown-item>更多菜单三</el-dropdown-item>
                  <el-dropdown-item>更多菜单四</el-dropdown-item>
                </el-dropdown-menu>
              </template>
            </el-dropdown>
          </li>
          <li v-if="row.link === 5">
            <el-dropdown>
              <span class="el-dropdown-link">
                二级配置
                <el-icon class="el-icon--right">
                  <arrow-down />
                </el-icon>
              </span>
              <template #dropdown>
                <el-dropdown-menu>
                  <el-dropdown-item>更多菜单一</el-dropdown-item>
                  <el-dropdown-item>更多菜单二</el-dropdown-item>
                  <el-dropdown-item>更多菜单三</el-dropdown-item>
                  <el-dropdown-item>更多菜单四</el-dropdown-item>
                </el-dropdown-menu>
              </template>
            </el-dropdown>
          </li>
        </ul>
      </template>
      <!-- 标签插槽 -->
      <template #state="{ row }">
        <el-tag v-if="row.state === '进行中'">{{ row.state }}</el-tag>
        <el-tag v-if="row.state === '已完成'" type="success">{{
          row.state
        }}</el-tag>
        <el-tag v-if="row.state === '已终止'" type="danger">{{
          row.state
        }}</el-tag>
        <el-tag v-if="row.state === '已中止'" type="warning">{{
          row.state
        }}</el-tag>
        <el-tag v-if="row.state === '已取消'" type="info">{{
          row.state
        }}</el-tag>
      </template>
      <!-- 搜索插槽 -->
      <template v-slot:search>
        <div class="ipt-btn">
          <!-- 输入框 -->
          <el-row gutter="0">
            <!-- 第一行 -->
            <el-col :span="8">
              <el-form-item label="标题标题">
                <el-input
                  placeholder="请输入"
                  v-model="form.value1"
                  @keyup.enter="handleFilter(form)"
                />
              </el-form-item>
            </el-col>
            <el-col :span="8">
              <el-form-item label="标题标题">
                <el-input
                  placeholder="请输入"
                  v-model="form.value2"
                  @keyup.enter="handleFilter(form)"
                />
              </el-form-item>
            </el-col>
            <el-col :span="8">
              <el-form-item label="标题标题">
                <el-input
                  placeholder="请输入"
                  v-model="form.value3"
                  @keyup.enter="handleFilter(form)"
                />
              </el-form-item>
            </el-col>
            <!-- <el-col :span="6">
              <el-form-item label="标题标题">
                <el-input
                  v-model="form.value4"
                  placeholder="请输入"
                  @keyup.enter="handleFilter(form)"
                ></el-input>
              </el-form-item>
            </el-col> -->
            <!-- 第二行 -->
            <el-col :span="8">
              <el-form-item label="标题标题" v-show="isShow">
                <el-select v-model="form.value5" @change="handleFilter(form)">
                  <el-option label="选项一" value="选项一">选项一</el-option>
                  <el-option label="选项二" value="选项二">选项二</el-option>
                </el-select>
              </el-form-item>
            </el-col>
            <el-col :span="8">
              <el-form-item label="标题标题" v-show="isShow">
                <el-date-picker
                  value-format="YYYY-MM-DD"
                  placeholder="请选择"
                  v-model="form.value6"
                  @change="handleFilter(form)"
                ></el-date-picker>
              </el-form-item>
            </el-col>
            <el-col :span="8">
              <el-form-item label="标题标题" v-show="isShow">
                <el-date-picker
                  type="datetime"
                  placeholder="请选择"
                  v-model="form.value7"
                  value-format="YYYY-MM-DD HH:mm:ss"
                  @change="handleFilter(form)"
                ></el-date-picker>
              </el-form-item>
            </el-col>
          </el-row>
          <!-- 按钮 -->
          <div class="btn">
            <el-form-item class="btns">
              <el-button
                style="
                  width: 30px;
                  height: 34px;
                  background: #eef1f5;
                  borderradius: 4px;
                "
                class="arrow"
                :icon="isShow ? ArrowUp : ArrowDown"
                @click="isShow = !isShow"
              ></el-button>
              <el-button
                class="reset"
                @click="resetForm"
                style="
                  width: 50px;
                  height: 34px;
                  border: 1px solid rgba(0, 0, 0, 0.2);
                  border-radius: 4px;
                "
                >重置</el-button
              >
              <el-button
                class="confirm"
                type="primary"
                @click="onSubmit"
                style="
                  width: 50px;
                  height: 34px;
                  background: #3480FB;
                  border-radius: 4px;
                "
                >确定</el-button
              >
            </el-form-item>
          </div>
        </div>
      </template>
    </flex-table>
  </div>

四、真实业务对接

如果业务要求直接传入url就可以实现数据渲染的话,就需要在组件内部去进行数据请求,下面主要进行axios请求mock数据模拟,先测试可行性,再根据真实数据对接即可。

1,实现表格数据成功渲染

(1),步骤一:下载依赖

yarn add mockjs / npm i mockjs

yarn add axios/ npm i axios

(2),步骤二:mock数据模拟

在scr文件夹下新建mock文件夹,新建index.js文件,和mockData文件夹;

在mockData文件里新建table.json文件,写上tableData的数据

{
      "totalElements":10, //总条数
      "page":1, //当前页码
      "rows":5, //每行显示条数
      "content":[
      {
        "id": "1",
        "contractName": "医院设备1",
        "contractDate": "2023-01-30 14:44:19",
        "contractAmout": "50000",
        "contractNum": "1",
      },
      {
        "id": "2",
        "contractName": "医院设备2",
        "contractDate": "2023-01-29 14:44:19",
        "contractAmout": "30000",
        "contractNum": "2",
      },
      {
        "id": "3",
        "contractName": "医院设备3",
        "contractDate": "2023-01-28 15:44:19",
        "contractAmout": "75000",
        "contractNum": "3",
      },
      {
        "id": "4",
        "contractName": "医院设备4",
        "contractDate": "2023-01-27 15:44:19",
        "contractAmout": "80000",
        "contractNum": "4",
      },
      {
        "id": "5",
        "contractName": "医院设备5",
        "contractDate": "2023-01-26 15:44:19",
        "contractAmout": "80000",
        "contractNum": "5",
      },
      {
        "id": "6",
        "contractName": "医院设备6",
        "contractDate": "2023-01-25 15:44:19",
        "contractAmout": "80000",
        "contractNum": "6",
      },
      {
        "id": "7",
        "contractName": "医院设备7",
        "contractDate": "2023-01-24 15:44:19",
        "contractAmout": "80000",
        "contractNum": "7",
      },
      {
        "id": "8",
        "contractName": "医院设备8",
        "contractDate": "2023-01-23 15:44:19",
        "contractAmout": "80000",
        "contractNum": "8",
      },
      {
        "id": "9",
        "contractName": "医院设备9",
        "contractDate": "2023-01-22 15:44:19",
        "contractAmout": "80000",
        "contractNum": "9",
      },
      {
        "id": "10",
        "contractName": "医院设备10",
        "contractDate": "2023-01-21 15:44:19",
        "contractAmout": "80000",
        "contractNum": "10",
      }
      ]
    }
  ]
}

另外在index.js文件中配置mock数据请求地址:

import Mock from 'mockjs';
import tableData from './mockData/tableData.json';
Mock.mock("/getData",(config)=>{
  let params = Json.parse(config.body) //这里是请求的参数
  return {
        success:true,
        data:tableData
    }
})

最后一定要记得在main.js文件中引入mock文件夹下的index.js文件

import './mock/index'

(3),步骤三:封装axios请求,新建api文件夹
import axios from 'axios';
import {ElMessage} from 'element-plus';
//请求表格数据
const getTableData = (url, data)=>{
  console.log('post请求参数',data);
  return axios({
    method:'post',
    url:url,
    data:data
  }).then(
    res => {
      return res.data
    },
    error => {
      ElMessage({
        type:'error',
        message:error.message,
        showClose: true
      })
    }
  )
}
export default getTableData
(4),步骤四:组件中引入方法请求数据

组件代码:

import getTableData from "../api/table";
props: {
    //外部传入url,但也需要给个默认值
    url: {
      type: String,
      require: true,
      default: "/getData",
    },
}
//get请求数据方法
const getTableList = (url, options) => {
//判断url不存在不继续进行请求
  if (!url) {
      console.log("请求地址不存在!");
      return false;
   }
   getTableData(url, options).then((res) => {
      console.log("res", res);      
      const { data } = res;
      //处理请求数据内容渲染表格
      tableData.value = data.content;
    });
};
onMounted(() => {
  getTableList(props.url, props.requestData);
});

使用父页面:

<flex-table
    url="/getData"
    :columnData="columnData"
></flex-table>

阶段总结:基本上表格的数据已经可以渲染出来了,至于请求request拦截器,需要和后端配合去完善。这里主要是前端先负责渲染出数据和调整效果样式等。

2,实现分页器和搜索框请求参数

(1)步骤一:tableData.json数据中添加分页器参数
"totalElements":10, //总条数
"page":1, //当前页码
"rows":5, //每行显示条数
(2)步骤二:组件内处理请求参数
<el-pagination
        :small="small" //是否启用小型分页器,字号12px
        :background="background" //页码选中是否有背景色
        layout="total, prev, pager, next, sizes, jumper" //分页器各组件
        :total="total" //总条数
        :pager-count="4" //设置最大按钮数
        :page-size="pageSize" //每页显示条数
        :page-sizes="pageSizes" //每页显示个数选择器的选项设置
        @size-change="sizeChange" //page-size 改变时触发
        @current-change="handleCurrentChange" //页码发生变化触发事件
      />
const total = ref(10); //总条数,可以给个默认值
const pageSize = ref(10); //每页条数,可以给个默认值
const currentPage = ref(1); //当前页码,可以给个默认值

//get请求数据方法
const getTableList = (url, options) => {
//判断url不存在不继续进行请求
  if (!url) {
      console.log("请求地址不存在!");
      return false;
   }
   getTableData(url, options).then((res) => {
      console.log("res", res);      
      const { data } = res;
      //处理请求数据内容渲染表格
      tableData.value = data.content;
      //处理请求数据总条数 ---后端给的
      total.value = data.totalElements;
      //处理请求每页显示条数 ---后端给的
      pageSize.value = data.rows;
      //处理请求当前显示页码 ---后端给的
      currentPage.value = data.page;
    });
};
(3)步骤三:处理页码变化时,传入相应的值给参数进行请求数据

父组件传入请求分页器参数

<flex-table
    url="/getData"
    :columnData="columnData"
    :requestData="request_data"
></flex-table>
<script>
    const request_data = reactive({
      page: 1, //当前页码
      rows: 10, //每页显示条数
      searchVal: {}, //搜索框输入的值
    });
</script>
    import {
      ref,
      computed,
      onMounted,
      getCurrentInstance,
      toRefs,
      defineExpose,
      defineEmits,
    } from "vue";
    
    props: {
        //分页器请求参数
        requestData: {
          type: Object,
          default: () => ({
            page: 1, //当前页数
            rows: 10, //显示条数
            searchVal: {}, //搜索框的值
          }),
        },
    }

    //处理props值可以重新赋值
    const emits = defineEmits();    
    const pageParams = computed({
       get: () => {
          return props.requestData;
       },
       set: (val) => {
          emits("update:requestData", val);
       },
    });

    //当前页码变化事件
    const handleCurrentChange = (val) => {
      //把当前页码赋值给请求参数
      pageParams.value.page = val;
      //请求数据
      getTableList(props.url, pageParams.value);
    };
    //每页显示条数变化事件
    const sizeChange = (val) => {
      //把变化的每页显示条数赋值给请求参数
      pageParams.value.rows = val;
      //请求数据
      getTableList(props.url, pageParams.value)
    };
    //上一页点击事件
    const handlePreClick = (val) => {
      console.log(`handlePreClick,点击了上一页,当前第${val}页`);
    };
    //下一页点击事件
    const handleNextClick = (val) => {
      console.log(`handleNextClick,点击了下一页,当前第${val}页`);
    };

注意:这里为什么需要用外部传入的requestData?

因为外部使用的时候需要用到searchVal,也就是搜索栏输入框的值;也有可能从刚开始请求表格初始数据的时候,需要传入特定页码和特定关键词的数据,所以暴露在外面可以提高组件的灵活性。

(4)步骤四:验证是否成功传入参数
import Mock from 'mockjs';
import tableData from './mockData/tableData.json';
Mock.mock("/getData",(config)=>{
  let params = Json.parse(config.body) 
  console.log(params) //这里打印验证是否更新参数
  return {
        success:true,
        data:tableData
    }
})
(5)步骤五:mock手动模拟切换数据的效果

目前分页器和搜索框的值就暴露到了请求数据的参数中。但是在mock中想要实现有切换数据的效果,我们需要根据返回的参数自己模拟mock返回的数据。

import tableData1 from './mockData/tableData1.json'
import tableData2 from './mockData/tableData2.json'

Mock.mock("/getData",(config)=>{
  let params = JSON.parse(config.body)
  //如果当前页码 = 2,返回第二页数据
  if(params.page == 2){
    return {
      success: true,
      data: tableData1,
    }
  }else if(params.page == 1){
    //如果当前页码 = 1,返回第一页数据
    return {
      success: true,
      data: tableData2,
    };
  }
})

这样简单的数据切换的效果就实现了,方便去调试传给后端的参数;

3,实现外部调用内部reload方法

因为搜索表单内容是插槽,外部自己写搜索表单的样式和布局,所以包括表单提交事件也是在组件外去操作的,那这个时候就需求去调用内部的请求去重新请求数据。vue3的话可以通过ref来实现。

(1)步骤一:父组件需要给子组件绑定ref
<flex-table ref="flexTable"></flex-table>
    const flexTable = ref(null)    
    //搜索表单v-model的值
    const form = reactive({
      value1: "张三",
      value2: "10",
      value3: "",
      value4: "",
      value5: "",
      value6: "",
      value7: "",
    });    
    //重置按钮
    const resetForm = () => {
      Object.keys(form).forEach((key) => (form[key] = ""));
      flexTable.value.reload(form);
    };
    //提交按钮
    const onSubmit = () => {
      flexTable.value.reload(form);
    };
    //输入框enter事件
    const handleFilter = (val) => {
      flexTable.value.reload(val);
    };
(2)步骤二:子组件处理reload方法
    //reload请求方法
    const reload = (val) => {
      pageParams.value.searchVal = val;
      getTableList(props.url, pageParams.value);
    };
(3)步骤三:子组件暴露出去reload方法
defineExpose({
  reload,
});

五,注意说明

1,关于代码写法

因为之前开发组件的时候需要用到name,所以用的写法是vue3的,没有用到setup语法糖。后面在完善的时候发现可以实现setup语法,也可以保留name。亲测有效。

<sctipt>
export default{
    name:"组件名"
}
</script>
<sctipt setup>
    //正常业务代码
</script>

2,关于组件内其他属性

这里主要讲解了一些重要的常用的属性,至于其他控制样式的属性可以参考elementPlus的组件属性表。

3,关于组件组件开发框架搭建

这个之前也是慢慢研究的,但是还没有整理,整理之后再单独更新吧。

4,关于对接真实接口

上面主要展示的是mock数据的对接模拟,至于真实数据需要对接自己的后端给的字段或者要求等;情况不一。但是大概的思路都可以参考的,大家可以根据自己的真实接口去灵活应用的。

5,关于组件需求

不同业务或许有不同的组件封装需求,以上主要是提供一些需求的一种解决方案,大家可以灵活参考。

  • 9
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 6
    评论
vxe-table是一个基于Vue.js的强大的表格组件库,它提供了丰富的功能和灵活的配置选项,可以用于展示和处理大量的数据。而vxe-table二次封装是指在vxe-table的基础上进行进一步的封装和定制,以满足特定项目或业务需求。 在进行vxe-table二次封装时,可以根据具体需求进行以下操作: 1. 封装常用的表格组件:根据项目需求,可以将常用的表格组件进行封装,以便在不同页面或模块中复用。例如,可以封装一个通用的表格组件,包含常用的列配置、分页、排序等功能。 2. 定制样式和主题:通过修改vxe-table的样式文件或者使用自定义主题,可以使表格组件与项目整体风格保持一致。这样可以提升用户体验,并增加项目的专属感。 3. 扩展功能和事件:根据项目需求,可以通过扩展vxe-table的功能和事件来满足特定的业务逻辑。例如,可以添加自定义的筛选、导出、编辑等功能,并监听相应的事件进行处理。 4. 封装数据请求和处理逻辑:在二次封装中,可以将数据请求和处理逻辑进行封装,以便在使用表格组件时更加方便地进行数据的获取和展示。可以通过封装API接口、数据转换等方式来实现。 5. 提供文档和示例:在进行vxe-table二次封装后,可以编写相应的文档和示例,以便其他开发人员能够更好地理解和使用封装后的表格组件。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值