一个简单的可配置化的element-ui table封装

2,035 阅读3分钟

最近在写一个后台的管理系统,每个页面的结构基本差不多,这次就其中的表格来简单说说吧。大家都在说组件化开发,共同的东西抽离出来。其实element-ui 中的表格挺好用了,但是我们对比下它和iview的表格就会发现,element的表格需要在模板里写很多的el-table-column,而iview则很简单,只需通过js配置每列的数据数据属性即可,复杂一点的数据列则可以通过render函数来实现。受iview的影响,下面就通过json配置的方式来进一步封装element-ui中的table。

下面就以一个实际的页面来说吧,比如我们现在要实现下面的表格:

改进前

<el-table
  v-loading="loading"
  ref="table"
  :data="data"
  size="small"
  style="width: 100%;"
>
  <el-table-column prop="addr" label="IP地址" />
  <el-table-column prop="rate" label="监控频率(分钟/次)" />
  <el-table-column prop="group_name" label="告警接收组" />
  <el-table-column prop="notify_mode" label="通知方式" />
  <el-table-column prop="latest_run_time" label="上次运行时间" />
  <el-table-column label="状态">
    <template slot-scope="scope">
      <el-tag v-if="scope.row.latest_status === '0'" type="danger">状态异常</el-tag>
      <el-tag v-else-if="scope.row.latest_status === '1'" type="success">状态正常</el-tag>
      <el-tag v-else-if="scope.row.latest_status === '2'" type="warning">待检测</el-tag>
    </template>
  </el-table-column>
  <el-table-column label="是否监控">
    <template slot-scope="scope">
      <el-switch
        v-model="scope.row.is_active"
        on-color="#00A854"
        on-text="启动"
        on-value="1"
        off-color="#F04134"
        off-text="禁止"
        off-value="0"
        @change="changeSwitch(scope.row)"
      >
      </el-switch>
    </template>
  </el-table-column>
  <el-table-column label="操作">
    <template slot-scope="scope">
      <el-button slot="reference" @click="handleEdit(scope.row)">编辑</el-button>
        <el-button slot="reference" @click="handleDel(scope.row)">删除</el-button>
    </template>
  </el-table-column>
</el-table>

这是直接拷贝了别人的代码,可能在头一年工作的时候,大部分人都会这么写,但是后台系统很多这样的表格,是不是每个页面都这样拷贝一下,改改属性呢,这种重复的代码为何不往上封装一层呢,单独抽离出一个组件,这样其他页面都能共用这个组件,只需要json配置,就能实现所需要的表格了。看过那些牛人的代码,就会觉得自己的代码不能见人,我也不例外,我就觉得我师父的代码高级又简洁。一步步来,慢慢网上爬吧,下面来看下如果改进吧:

改进后

首先分析下:表格中的列可以分为3类,一类是最简单的,直接展示属性值就可以,不需要做任何处理,第二类是需要根据属性值来展示不同的状态,比如上面的状态有异常、正常、待检测,还有是否监控一栏是根据属性值来动态展示switch的开和关。第三类就是操作列了。

针对第一列和第二类我们可以用一个el-table-column,根据所传对象有无插槽,来决定是直接展示还是展示父组件的插槽内容吧,第三列可以另外设置一个el-table-column来专门展示操作列。直接上代码:common-table

  <el-table
      v-loading="loading"
      ref="table"
      :data="tableData"
      size="small"
      style="width: 100%;"
      :max-height="maxHeight"
      @cell-mouse-enter="handleEnter"
      @cell-mouse-leave="handleLeave"
    >
      <el-table-column
        v-for="(item, index) in tableColumn"
        :key="index"
        :prop="item.value"
        :label="item.label"
        :min-width="item.minWidth || '100px'"
        show-overflow-tooltip
      >
        <template v-slot="scope">
          <!-- solt 自定义列-->
          <template v-if="item.type === 'slot'">
            <slot :name="item.slotType" :row="scope.row" />
          </template>
          <span v-else>
            {{ scope.row[item.value] }}
          </span>
        </template>
      </el-table-column>
      <el-table-column
        v-if="handle"
        :key="'handle'"
        :label="handle.label"
        :min-width="handle.minWidth"
      >
        <template v-slot="scope">
          <template v-for="(item, index) in handle.btList">
            <el-button
              type="default"
              :key="index"
              @click="handleClick(item.event, scope.row)"
              class="net-btn"
              >{{ item.label }}</el-button
            >
          </template>
        </template>
      </el-table-column>
    </el-table>

表格组件大概这样封装好了,来看看父组件是如何引用的吧:

引用

<common-table
      :loading="loading"
      :table-column="tableColumn"
      :table-data="tableData"
      :handle="tableHandle"
      @optTable="handleOpt"
      @mouserEnter="handleMouseEnter"
      @mouserLeave="handleMouseLeave"
    >
      <!-- 自定义插槽显示状态 -->
      <template #status="scope">
        <span v-if="scope.row.latest_status === 0">异常</span>
        <span v-else-if="scope.row.latest_status === 1">正常</span>
        <span v-else-if="scope.row.latest_status === 2">待检测</span>
      </template>
      <!-- 自定义插槽是否监控 -->
      <template #switch="scope">
        <el-switch
          v-model="scope.row.is_active"
          @change="changeSwitch(scope.row)"
        >
        </el-switch>
      </template>
    </common-table>
    
    // 其中数据传递部分:
     loading: false,
      tableColumn: [
        { label: "IP地址", value: "addr", minWidth: 150 },
        { label: "监控频率(分钟/次)", value: "rate", minWidth: 150 },
        { label: "告警接收组", value: "group_name", minWidth: 100 },
        { label: "通知方式", value: "notify_mode", minWidth: 120 },
        { label: "上次运行时间", value: "latest_run_time", minWidth: 120 },
        {
          label: "状态",
          value: "latest_status",
          minWidth: 80,
          type: "slot",
          slotType: "status"
        },
        {
          label: "是否监控",
          value: "is_active",
          minWidth: 100,
          type: "slot",
          slotType: "switch"
        }
      ],
      tableHandle: {
        label: "操作",
        minWidth: "170",
        btList: [
          { label: "编辑", event: "edit" },
          { label: "删除", event: "delete" }
        ]
      },
      tableData: [],

在上面数据的表格列的json配置中,将value传给prop,以有无type来决定是展示插槽内容还是直接显示属性内容,然后根据slotType的不同---带有作用域的具名插槽来展示具体的内容,比如上面的状态,switch开关 ;操作列则是单独的 tableHandle,并在btList中用event区分了事件的类型,在父组件中可以根据这个来处理不同的事件逻辑。

这样能够满足一般的表格需求,当然可以对有无复选框进行进一步扩展,或者某一列更复杂,需要单独显示一个组件等。