vue3封装element-plus的el-table


前置条件

需要引入使用jsx
为了渲染表格的单元格内容formatter,jsx引入使用看这篇

一、效果

封装的el-table公共组件table.vue
注意深拷贝用的lodash方法,以及使用了jsx插件渲染列的标签和事件
在这里插入图片描述

二、语法糖setup封装版本

封装table.vue

<template>
  <div class="page-view">
    <el-table v-loading="tableData.tableLoading" ref="multipleTableRef" class="jg-table" :data="tableData.data"
      style="width: 100%;" @selection-change="handleSelectionChange" @row-click="rowClick" @sort-change="sortChange">

      <template v-for="(item, index) in  tableData.columnsData " :key="index">

        <!-- 多选 -->
        <el-table-column v-if="item.type === 'selection'" :type="item.type" :width="item.width" />
        <!-- 单选 -->
        <el-table-column v-else-if="item.type === 'radio'" :width="item.width" class-name="no-radio-label">
          <template #default="{ row, $index }">
            <el-radio @change="changeRadio" v-model="radioIndex" :label="$index"></el-radio>
          </template>
        </el-table-column>
        <!-- 渲染formatter -->
        <!-- <el-table-column v-else :property="item.field" :label="item.title" :width="item.width"
          :min-width="item.minWidth" :max-idth="item.maxWidth" :showOverflowTooltip="item.showOverflowTooltip"
          :sortable="item.sortable" :formatter="item.formatter" /> -->
        <el-table-column v-else :property="item.field" :label="item.title" :width="item.width"
          :min-width="item.minWidth" :max-idth="item.maxWidth" :showOverflowTooltip="item.showOverflowTooltip"
          :sortable="item.sortable" :formatter="item.formatter">
          <template #header>
            <div style="display: inline-block; align-items: center;">
              <span>{{ item.title }}</span>
            </div>
          </template>
        </el-table-column>
      </template>

    </el-table>


    <pre>{{ tableData }}</pre>
  </div>
</template>

<script setup lang="jsx">
import { ref, defineOptions, defineEmits, defineProps, watch } from 'vue' // 按需引入ref函数
import _ from 'lodash'


defineOptions({
  name: "JgTable"
})

const emit = defineEmits(['update:editItem', 'update:handleSelectionChange', 'update:changeRadio', 'update:sortChange'])

const props = defineProps({
  description: {
    type: String,
    default: () => {
      return ' '
    }
  },
  tableDataObj: {
    type: Object,
    default: () => {
      return {}
    }
  },
})



let tableData = ref({})
let radioIndex = ref(null)

watch([() => props.tableDataObj, () => props.rulesData], ([tableDataObj, rulesData]) => {
  // console.log('监听');
  tableData.value = _.cloneDeep(tableDataObj) // 使用lodash的深拷贝方法

}, { deep: true, immediate: true })


// 编辑-暂不使用 目前由子组件自己触发
function editItem(row, type) {
  console.log(row, type,);

  // 子传父
  emit('update:editItem', { row: row, type: type });
}


// 多选
const handleSelectionChange = (val) => {
  // console.log(val);
  // 子传父
  emit('update:handleSelectionChange', val);
}
// 单选
const changeRadio = (val) => {
  // console.log('单选', val, radioIndex.value.aa);
  let item = tableData.value.data?.[val] || {}
  emit('update:changeRadio', item);
}

// 表格点击事件
const rowClick = (val) => {
  // console.log(val);
}

// 排序
const sortChange = (val) => {
  // console.log(val);
  emit('update:sortChange', val);
}

</script>
<style lang="scss" scoped>
// 去除单选框的内容
:deep(.no-radio-label) {
  .el-radio__label {
    display: none;
  }
}
</style>

使用index.vue

<template>
  <JgTable :tableDataObj="tableData" @update:editItem="updateEditItem"
    @update:handleSelectionChange="updateHandleSelectionChange" @update:changeRadio="updateChangeRadio"
    @update:sortChange="updateSortChange"></JgTable>
</template>
<script setup lang="jsx">
import { ref, defineComponent, toRefs, reactive, onMounted, getCurrentInstance } from 'vue'
import { Delete, Edit, Download, QuestionFilled, EditPen, Tickets } from '@element-plus/icons-vue'
import { ElMessage, ElMessageBox } from 'element-plus'

defineOptions({
  name: 'EquipmentDefect'
})
const nameList = ref([])


let tableData = ref({
  tableLoading: false,
  // title:名称 field:key type:单选复选框 width minWidth maxWidth showOverflowTooltip formatter:格式化内容
  columnsData: [
    { title: '多选', type: 'selection', width: 55 },
    { title: '单选', type: 'radio', width: 55 },
    { title: '缺陷名称', field: 'aa', formType: 'input', width: 120 },
    { title: '缺陷描述', field: 'bb', formType: 'input', showOverflowTooltip: true },
    { title: '缺陷发布时间', field: 'cc1', formType: 'date', dateType: 'YYYY-MM-DD', width: 140 },
    { title: '缺陷发布时间2', field: 'cc2', formType: 'dateTime', dateType: 'YYYY-MM-DD HH:mm:ss', width: 150 },
    // { title: '缺陷发布时间',  field: 'cc1', formType: 'datetime', dateType: 'YYYY-MM-DD HH:mm:ss' },
    {
      title: '设备类型', field: 'dd', formType: 'select', width: 120, selectList: nameList,

      formatter: (row) => {
        const val = nameList.value.find(item => item.value === row.dd)?.name || ''
        return [<span>{val}</span>]
      }
    },
    { title: '设备厂家', field: 'ee', formType: 'input' },
    { title: '设备版本', field: 'ff', formType: 'input', width: 160, showOverflowTooltip: true },
    {
      title: '缺陷设备数量', field: 'gg', formType: 'input', sortable: 'custom',width: 160,
      formatter: (row, column) => {
        // console.log(112,row, column); // 直接{row}或{column} 不会显示内容
        return [<span onClick={() => clickItem(row, 'view')}> {row.gg} </span>]
      }
    },
    {
      title: '缺陷反馈状态', field: 'hh', formType: 'radio', minWidth: 100, selectList: [
        { name: '已反馈', value: 1 },
        { name: '未反馈', value: 0 },
      ],
      formatter: (row) => (
        row ? <el-tag style="border: none;" class="cursor" type={row.hh === 1 ? 'primary' : 'warning'}>{row.hh === 1 ? '已反馈' : row.hh === 0 ? '未反馈' : ''}</el-tag> : ''
      )
    },
    {
      title: '缺陷处置进度', field: 'ii', formType: 'progress',
      formatter: (row) => (
        <el-progress class="table_progress" width={13} stroke-width={2} type="circle" percentage={row.ii} />
      )
    },
    // {
    //   title: '测试', field: 'ceshi',width: 200,
    //   formatter: (row, column) => {
    //     // console.log(112,row, column); // 直接{row}或{column} 不会显示内容
    //     return  [<el-tag type={row.ceshi == "可发放" ? "success" : "danger"} onClick={() => editItem(row, 'view')}> {row.ceshi} </el-tag>, <span>{column.label}</span>]
    //   }
    // },
    {
      title: '操作', field: 'caozuo', width: 220,
      formatter: (row) => (
        [
          <el-button link type="primary" size="small" icon={Tickets} onClick={() => editItem(row, 'view')} >查看</el-button >,
          <el-button link type="primary" size="small" icon={EditPen} onClick={() => editItem(row, 'edit')} >编辑</el-button >,
          <el-button link type="primary" size="small" icon={Delete} onClick={() => editItem(row, 'delete')} >删除</el-button >,
        ]
      )
    },

  ],
  data: Array.from({ length: 7 }).map((ele, index) => {
    return {
      aa: '客户端不适配',
      bb: '客户端无法适配凝思 4.2.40',
      cc1: '2023-7-20',
      cc2: '2023-7-20 10:10:10',
      dd: Math.floor(Math.random() * 5),
      ee: '帕拉迪',
      ff: 'PLD Sys 61012100',
      gg: Math.floor(Math.random() * 20),
      hh: Math.floor(Math.random() * 2),
      ii: Math.floor(Math.random() * 101),
      ceshi: 123,
    }
  })
})

const clickItem = (row, type) => {
  console.log(row, type);
  ElMessage({
    type: 'success',
    message: '点击',
  })
}
// 编辑或查看
const editItem = (row, type) => {
  switch (type) {
    case 'view':
    case 'edit':
      console.log(row, type);
      break;
    case 'delete':
      deleteItem(row, type)
      break;

    default:
      break;
  }
}

// 删除
const deleteItem = (row, type) => {
  ElMessageBox.confirm(
    '确定删除该列?',
    {
      confirmButtonText: '确认',
      cancelButtonText: '取消',
      type: 'warning',
    }
  )
    .then(() => {
      ElMessage({
        type: 'success',
        message: 'Delete completed',
      })
    })
    .catch(() => {
    })
}

// 子组件调用父组件的测试事件
const updateEditItem = (data) => {
  console.log('子组件调用父组件的测试事件', data);
  editItem(data.row, data.type)
}
const updateHandleSelectionChange = (data) => {
  console.log('updateHandleSelectionChange', data);
}
const updateChangeRadio = (data) => {
  console.log('updateChangeRadio', data);
}
const updateSortChange = (data) => {
  console.log(data);
}


onMounted(() => {
  setTimeout(() => {
    nameList.value = [
      { name: '横向隔离', value: 0 },
      { name: '纵向加密', value: 1 },
      { name: '防火墙', value: 2 },
      { name: '监测装置', value: 3 },
      { name: '运维网关', value: 4 },
      { name: '其他', value: 5 },
    ]
  }, 1000);
})
// 将变量和函数返回,以便在模版中使用

</script>
<style lang="scss" scoped></style>

三、vue原生语法版本

封装的table.vue


<template>
  <div class="page-view flex-center">
    <el-table v-loading="tableData.tableLoading" ref="multipleTableRef" class="jg-table" :data="tableData.data"
      style="width: 100%;" @selection-change="handleSelectionChange" @row-click="rowClick" @sort-change="sortChange">

      <template v-for="(item, index) in  tableData.columnsData " :key="index">

        <!-- 多选 -->
        <el-table-column v-if="item.type === 'selection'" :type="item.type" :width="item.width" />
        <!-- 单选 -->
        <el-table-column v-else-if="item.type === 'radio'" :width="item.width" class-name="no-radio-label">
          <template #default="{ row, $index }">
            <el-radio @change="changeRadio" v-model="radioIndex" :label="row"></el-radio>
          </template>
        </el-table-column>
        <!-- 渲染formatter -->
        <el-table-column v-else :property="item.field" :label="item.title" :width="item.width" :min-width="item.minWidth"
          :max-idth="item.maxWidth" :showOverflowTooltip="item.showOverflowTooltip" :sortable="item.sortable"
          :formatter="item.formatter" />
      </template>

    </el-table>
  </div>
</template>

<script lang="jsx">
import { ref, defineComponent, getCurrentInstance, h } from 'vue' // 按需引入ref函数

import { Edit, Download, QuestionFilled, EditPen, Tickets, Delete } from '@element-plus/icons-vue'


export default defineComponent({
  name: 'JgTable',
  props: {
    description: {
      type: String,
      default: () => {
        return ' '
      }
    },
    tableDataObj: {
      type: Object,
      default: () => {
        return {}
      }
    },
  },
  emits: ['update:editItem'],
  setup(props, { emit }) {
    const { proxy } = getCurrentInstance()


    let tableData = ref({})
    let radioIndex = ref(null)

    watch([() => props.tableDataObj, () => props.rulesData], ([tableDataObj, rulesData]) => {
      // console.log('监听');
      tableData.value = proxy.$_.cloneDeep(tableDataObj) // 使用lodash的深拷贝方法

    }, { deep: true, immediate: true })


    function editItem(row, type) {
      console.log(row, type,);

      // 子传父
      emit('update:editItem', { row: row, type: type });
    }

    const myDiv = () => h('div', null, 'Hello, 1')

    // 多选
    const handleSelectionChange = (val) => {
      console.log(val);
      // 子传父
      emit('update:handleSelectionChange', val);
    }
    // 单选
    const changeRadio = (val) => {
      // console.log('单选', val, radioIndex.value.aa);
      emit('update:changeRadio', val);
    }

    // 表格点击事件
    const rowClick = (val) => {
      // console.log(val);
    }

    // 排序
    const sortChange = (val) => {
      // console.log(val);
      emit('update:sortChange', val);
    }




    // 将变量和函数返回,以便在模版中使用
    return {
      myDiv,
      radioIndex,

      tableData,

      editItem,
      handleSelectionChange,
      changeRadio,
      rowClick,
      sortChange,

      EditPen,
      Tickets,
      Delete,

    }
  }
})
</script>
<style lang="scss" scoped>
// 去除单选框的内容
:deep(.no-radio-label) {
  .el-radio__label {
    display: none;
  }
}
</style>

index.vue使用

<template>
  <JgTable :tableDataObj="tableData" @update:editItem="updateEditItem"
    @update:handleSelectionChange="updateHandleSelectionChange" @update:changeRadio="updateChangeRadio"
    @update:sortChange="updateSortChange"></JgTable>
</template>
<script lang="jsx">
import { ref, defineComponent, toRefs, reactive, onMounted, getCurrentInstance } from 'vue'
import { Delete, Edit, Download, QuestionFilled, EditPen, Tickets } from '@element-plus/icons-vue'
import { ElMessage, ElMessageBox } from 'element-plus'


export default {
  name: "EquipmentDefect",
  setup() {
    let tableData = ref({
      tableLoading: false,
      // title:名称 field:key type:单选复选框 width minWidth maxWidth showOverflowTooltip formatter:格式化内容
      columnsData: [
        { title: '多选', type: 'selection', width: 55 },
        { title: '单选', type: 'radio', width: 55 },
        { title: '缺陷名称', field: 'aa', formType: 'input', width: 120 },
        { title: '缺陷描述', field: 'bb', formType: 'input', showOverflowTooltip: true },
        { title: '缺陷发布时间', field: 'cc', formType: 'date', dateType: 'YYYY-MM-DD', width: 140 },
        // { title: '缺陷发布时间',  field: 'cc', formType: 'datetime', dateType: 'YYYY-MM-DD HH:mm:ss' },
        {
          title: '设备类型', field: 'dd', formType: 'select', width: 120, selectList: [
            { name: '横向隔离', value: 0 },
            { name: '纵向加密', value: 1 },
            { name: '防火墙', value: 2 },
            { name: '监测装置', value: 3 },
            { name: '运维网关', value: 4 },
            { name: '其他', value: 5 },
          ],

          formatter: (row) => {
            const val = [
              { name: '横向隔离', value: 0 },
              { name: '纵向加密', value: 1 },
              { name: '防火墙', value: 2 },
              { name: '监测装置', value: 3 },
              { name: '运维网关', value: 4 },
              { name: '其他', value: 5 },
            ].find(item => item.value === row.dd)?.name || ''
            return <span>{val}</span>
          }
        },
        { title: '设备厂家', field: 'ee', formType: 'input' },
        { title: '设备版本', field: 'ff', formType: 'input', width: 160, showOverflowTooltip: true },
        {
          title: '缺陷设备数量', field: 'gg', formType: 'input', sortable: 'custom',
          formatter: (row, column) => {
            // console.log(112,row, column); // 直接{row}{column} 不会显示内容
            return [<span onClick={() => clickItem(row, 'view')}> {row.gg} </span>]
          }
        },
        {
          title: '缺陷反馈状态', field: 'hh', formType: 'radio', minWidth: 100, selectList: [
            { name: '已反馈', value: 1 },
            { name: '未反馈', value: 0 },
          ],
          formatter: (row) => (
            row ? <el-tag style="border: none;" class="cursor" type={row.hh === 1 ? 'green' : 'warning'}>{row.hh === 1 ? '已反馈' : row.hh === 0 ? '未反馈' : ''}</el-tag> : ''
          )
        },
        {
          title: '缺陷处置进度', field: 'ii', formType: 'progress',
          formatter: (row) => (
            <el-progress class="table_progress" width="13" stroke-width="2" type="circle" percentage={row.ii} />
          )
        },
        // {
        //   title: '测试', field: 'ceshi',width: 200,
        //   formatter: (row, column) => {
        //     // console.log(112,row, column); // 直接{row}{column} 不会显示内容
        //     return  [<el-tag type={row.ceshi == "可发放" ? "success" : "danger"} onClick={() => editItem(row, 'view')}> {row.ceshi} </el-tag>, <span>{column.label}</span>]
        //   }
        // },
        {
          title: '操作', field: 'caozuo', width: 220,
          formatter: (row) => (
            [
              <el-button link type="primary" size="small" icon={Tickets} onClick={() => editItem(row, 'view')} >查看</el-button >,
              <el-button link type="primary" size="small" icon={EditPen} onClick={() => editItem(row, 'edit')} >编辑</el-button >,
              <el-button link type="primary" size="small" icon={Delete} onClick={() => editItem(row, 'delete')} >删除</el-button >,
            ]
          )
        },

      ],
      data: Array.from({ length: 7 }).map((ele, index) => {
        return {
          aa: '客户端不适配',
          bb: '客户端无法适配凝思 4.2.40',
          cc: '2023-7-20',
          dd: Math.floor(Math.random() * 5),
          ee: '帕拉迪',
          ff: 'PLD Sys 61012100',
          gg: Math.floor(Math.random() * 20),
          hh: Math.floor(Math.random() * 2),
          ii: Math.floor(Math.random() * 101),
          ceshi: 123,
        }
      })
    })

    const clickItem = (row, type) => {
      console.log(row, type);
      ElMessage({
        type: 'success',
        message: '点击',
      })
    }
    // 编辑或查看
    const editItem = (row, type) => {
      switch (type) {
        case 'view':
        case 'edit':
          console.log(row, type);
          break;
        case 'delete':
          deleteItem(row, type)
          break;

        default:
          break;
      }
    }

    // 删除
    const deleteItem = (row, type) => {
      ElMessageBox.confirm(
        '确定删除该列?',
        {
          confirmButtonText: '确认',
          cancelButtonText: '取消',
          type: 'warning',
        }
      )
        .then(() => {
          ElMessage({
            type: 'success',
            message: 'Delete completed',
          })
        })
        .catch(() => {
        })
    }

    // 子组件调用父组件的测试事件
    const updateEditItem = (data) => {
      console.log('子组件调用父组件的测试事件', data);
      editItem(data.row, data.type)
    }
    const updateHandleSelectionChange = (data) => {
      console.log('updateHandleSelectionChange', data);
    }
    const updateChangeRadio = (data) => {
      console.log('updateChangeRadio', data);
    }
    const updateSortChange = (data) => {
      console.log(data);
    }
    onMounted(() => {

    })
    // 将变量和函数返回,以便在模版中使用
    return {
      tableData,

      ElMessageBox,

      clickItem, // 点击
      editItem, // 查看编辑
      deleteItem, // 删除

      // 子传父表格事件
      updateEditItem,
      updateHandleSelectionChange,
      updateChangeRadio,
      updateSortChange,

      // icon
      Edit,
      Download,
      QuestionFilled,
      EditPen,
      Tickets,
      Delete,

    };
  },
};
</script>
<style lang="scss" scoped></style>

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值