vue3封装el-table及实现表头自定义筛选

效果图:
在这里插入图片描述


注意:
同时封装了el-table列表和表头


一、封装内容

1.封装el-table组件

1.通过:formatter="item.formatter"渲染列表单元格,使用jsx写dom
2.表格结构在tableData.value.columnsData下,表格数据在tableData.value.data
3.表格封装了多选、单选、点击表头、点击排序(多列)、点击某列、点击操作类事件、插槽等
4.title:名称 field:key type:单选复选框 formType:筛选方式 sortable:排序  needGetList:需要远程搜索 needJavaList:需要获取后端下拉数据
 width minWidth maxWidth showOverflowTooltip  formatter:格式化内容  fixed: 'right' 固定列		

2.封装表头筛选、排序和下拉数据接口获取

1.通过遍历表格结构,拿到每列类型formType,渲染不同筛选
2.目前支持输入框、单选框、下拉多选框、远程下拉多选框、时间范围筛选和多列的排序
3.筛选类型formType:input 输入框  select 单选  selectMultiple 多选 daterange 年月日范围 formType 年月日时分秒范围
4.sortable: 'custom' 排序    needGetList: true 需要远程获取下拉筛选数据

3.某列举例

 {
      title: '姓名', field: 'name', formType: 'selectMultiple', selectList: nameList, needGetList: true, sortable: 'custom', showOverflowTooltip: true, minWidth: 100,
      formatter: (row) => {
        const val = filters(row.name, nameList.value)
        return [<span>{val}</span>]
      }
    },

姓名列,key是name,该列多选筛选,下拉数据取nameList数组,下拉数据需要通过接口获取,可排序,当内容过长被隐藏时显示 tooltip,最小宽度100px,通过value回显渲染出对用name

4.needGetListt: true需要远程搜索

1.注意:获取下拉数据的逻辑,去table.vue组件内自己写,在 remoteMethod 里写逻辑

5.needJavaList: true 需要后端返回下拉数据

1.搜索needJavaList即可

6.单列排序和多列排序

查看sortChange部分

二、代码

1.封装整个表格和表头组件table.vue

table.vue

<template>
  <div class="page-view flex-column" @click="handleClickOutside">
    <div class="flex-between mb">
      <el-space size="large">
        <el-button type="primary" @click="resetFilters">重置</el-button>
      </el-space>
      <slot name="btnContent"></slot>
    </div>

    <div style="flex: 1;overflow-y: auto;">
      <el-table v-loading="tableData.tableLoading" ref="myTableRef" class="jg-table" :data="tableData.data"
        style="width: 100%;height: 100%;" :border="Boolean(tableData.mergeKey)" :span-method="objectSpanMethod"
        @selection-change="handleSelectionChange" @row-click="rowClick" @sort-change="sortChange"
        @header-click="handleHeaderClick" :header-cell-class-name="handleHeadAddClass">

        <template v-for="(item, index) in  tableData.columnsData " :key="index">
          <!-- 多选 -->
          <el-table-column v-if="item.type === 'selection'" :type="item.type" :width="item.width" fixed />
          <!-- 单选 -->
          <el-table-column v-else-if="item.type === 'radio'" :width="item.width" fixed 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" :fixed="item.fixed">
            <template #header>
              <div class="table-header-div">
                <span class="title-span">{{ item.title }}</span>

                <!-- 控制筛选按钮显隐 -->
                <template v-if="item.field !== 'actionBtn' && item.formType">
                  <el-popover :visible="visible && item.field === showKey" :virtual-ref="refName" placement="bottom"
                    :width="'fit-content'">
                    <template #reference>

                      <!-- 两个筛选icon -->
                      <svg v-if="searchData[item.field] || searchData[item.field] === 0" :id="item.field"
                        @click.stop="toggleNameFilter(item.field, item)" t="1721807387166" class="icon icon-span"
                        viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg" p-id="5539" width="20"
                        height="20">
                        <path
                          d="M609.507556 463.246222H414.492444L170.666667 170.666667h682.666666l-243.825777 292.579555z m0 48.753778v212.878222L414.492444 853.333333V512h195.015112z"
                          fill="#11716f" p-id="5540"></path>
                      </svg>
                      <svg v-else :id="item.field" @click.stop="toggleNameFilter(item.field, item)" t="1721807387166"
                        class="icon icon-span" viewBox="0 0 1024 1024" version="1.1" xmlns="http://www.w3.org/2000/svg"
                        p-id="5539" width="20" height="20">
                        <path
                          d="M609.507556 463.246222H414.492444L170.666667 170.666667h682.666666l-243.825777 292.579555z m0 48.753778v212.878222L414.492444 853.333333V512h195.015112z"
                          fill="#c1c2c6" p-id="5540"></path>
                      </svg>
                      <!-- <el-icon class="icon-span" :id="item.field" @click.stop="toggleNameFilter(item.field, item)"
                        style="cursor: pointer; margin-left: 10px;"
                        :style="{ color: searchData[item.field] ? '#11716f' : '#85888e' }">
                        <Filter />
                      </el-icon> -->
                    </template>
                    <div>
                      <!-- 输入 -->
                      <el-input v-if="item.formType === 'input'" v-model="searchData[item.field]" :autofocus="true"
                        placeholder="请输入" @input="applyNameFilter" clearable style="margin-top: 10px; width: 150px;" />

                      <template v-if="!item.needGetList">
                        <!-- 下拉 -->
                        <el-select v-if="item.formType === 'select' || item.formType === 'radio'" filterable
                          :reserve-keyword="false" v-model="searchData[item.field]" placeholder="请选择"
                          @change="applyTagFilter" clearable style="margin-top: 10px; width: 150px;">
                          <el-option v-for="item in item.selectList" :key="item" :value="item.value"
                            :label="item.name" />
                        </el-select>
                        <!-- 多选下拉 -->
                        <el-select v-if="item.formType === 'selectMultiple'" multiple filterable
                          :reserve-keyword="false" v-model="searchData[item.field]" placeholder="请选择" clearable
                          style="margin-top: 10px; width: 150px;">
                          <el-option v-for="item in item.selectList" :key="item" :value="item.value"
                            :label="item.name" />
                        </el-select>
                      </template>
                      <!-- 多选下拉 需要搜索获取后端接口数据 -->
                      <el-select
                        v-if="(item.formType === 'select' || item.formType === 'selectMultiple') && item.needGetList"
                        multiple filterable remote :reserve-keyword="false" :loading="listLoading" remote-show-suffix
                        :remote-method="remoteMethod" v-model="searchData[item.field]" placeholder="请搜索后选择" clearable
                        style="margin-top: 10px; width: 150px;">
                        <el-option v-for="item in javaList" :key="item.value" :value="item.value" :label="item.name" />
                      </el-select>
                      <!-- 日期 && 日期时间 -->
                      <el-date-picker v-if="item.formType === 'daterange' || item.formType === 'datetimerange'"
                        style="width: 360px;" v-model="searchData[item.field]" :type="item.formType" range-separator="~"
                        start-placeholder="开始时间" end-placeholder="结束时间" :format="item.dateType"
                        @change="changeData($event, item.field, item.dateType)" />
                      <div class="mt" style="text-align: right;">
                        <el-button type="info" link @click="cancelFilter">重置</el-button>
                        <el-button type="primary" link @click="searchFilter">筛选</el-button>
                      </div>
                    </div>
                  </el-popover>
                </template>

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

      </el-table>
    </div>


    <pre>筛选和排序参数:{{ { ...searchData, ...sortJavaField } }}</pre>
    <!-- <pre>表格默认多列排序集合:{{ sortField }}</pre> -->
    <!-- <pre>表格参数:{{ tableData }}</pre> -->
  </div>
</template>

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


defineOptions({
  name: "JgTable"
})

const emit = defineEmits(['setSearchData', 'updateHandleSelectionChange', 'updateChangeRadio', 'updateSortChange'])

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


let tableFlag = ref(false)//确保拿到父传子的表格数据再渲染
let tableData = ref({})
let myTableRef = ref(null) // 表格实例
let radioIndex = ref(null)

let searchData = ref({}) // 搜索参数 每个表头字段
// let sortObj = ref({}) // 单列排序
const sortField = ref({}) // 表格默认多列排序集合
const sortJavaField = ref({
  sortVOList: []
}) // 后端要的排序集合

// 时间格式化
const changeData = (value, key, dateType) => {
  // console.log(value, key, dateType);
  if (!_.isEmpty(value)) {
    searchData.value[key][0] = value[0] ? moment(value[0]).format(dateType) : ''
    searchData.value[key][1] = value[1] ? moment(value[1]).format(dateType) : ''
  } else {
    searchData.value[key] = []
  }
}

const showKey = ref(undefined) // 当前展示哪个筛选窗
const visible = ref(false) // 手动控制筛选窗显隐
const refName = ref(null) // 动态绑定在哪个表头图标下


// 全局重置
const resetFilters = () => {
  // searchData.value.nameFilter = {}
  for (const key in searchData.value) {
    if (Object.hasOwnProperty.call(searchData.value, key)) {
      searchData.value[key] = undefined
    }
  }
  visible.value = false

  sortField.value = {}
  sortJavaField.value.sortVOList = []
  // 因为无法通过element ui的api来清除排序样式,所以只能通过原生js来清除
  myTableRef.value.$el.querySelectorAll(".is-sortable").forEach((item) => {
    // 移除table表头中的排序样式descending和ascending
    item.classList.remove("descending");
    item.classList.remove("ascending");
  }) // 清除多个排序
  // myTableRef.value.clearSort() // 清除单个排序

  applyNameFilter()
  applyTagFilter()

  getData()
}

// 某些下拉数据 例如下拉大数据有500条 直接赋值会导致表格卡顿 通过点击表头再去接口获取数据赋值就不会影响表格的渲染
// (列里面数据回显使用后端给的中文,或者将javaList赋值给item的selectList)
let javaList = ref([])
const getJavaList = async (item) => {
  // 模拟几口
  await setTimeout(() => {
    item.selectList = Array.from({ length: 500 }).map((ele, index) => {
      return {
        name: index,
        value: index,
      }
    })
  }, 500);
}

const listLoading = ref(false)
// 远程搜索接口获取下拉数据
const remoteMethod = (query) => {
  if (query) {
    listLoading.value = true
    // getUserList({
    //   pageNum: 1,
    //   pageSize: 1000,
    //   chineseName: query
    // }).then((res = []) => {
    //   javaList.value = (res || []).map(ele => {
    //     return {
    //       name: ele.chineseName,
    //       value: ele.id,
    //     }
    //   })

    // }).finally(() => {
    //   listLoading.value = false
    // })

    // 模拟接口
    setTimeout(() => {
      javaList.value = Array.from({ length: 3 }).map((ele, index) => {
        return {
          name: '模拟' + index,
          value: index,
        }
      })
      listLoading.value = false
    }, 500);

  } else {
    // javaList.value = []
  }
}

// 触发筛选
const toggleNameFilter = async (key, item) => {
  // console.log(key, item);
  if (item.needJavaList) {
    await getJavaList(item) // 调用后端接口 获取下拉数据
  }

  if (visible.value && showKey.value && showKey.value !== key) {
    visible.value = false
    getData()
  }

  refName.value = document.getElementById(key)
  showKey.value = key
  visible.value = !visible.value
}

// 点击其他元素
const handleClickOutside = () => {
  // 且有筛选打开即去查询
  if (visible.value) {
    visible.value = false
    getData()
  }
};

// 重置
const cancelFilter = () => {
  searchData.value[showKey.value] = undefined
  visible.value = false;
  getData()
}
// 筛选
const searchFilter = () => {
  visible.value = false;
  getData()
}

// 单独过滤
const applyNameFilter = () => {
  // Filtering logic can be customized if needed
}
const applyTagFilter = () => {
  // Filtering logic can be customized if needed
}

const getData = () => {
  console.log('筛选参数', { ...searchData.value, ...sortJavaField.value });
  emit('setSearchData', { ...searchData.value, ...sortJavaField.value })
}


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

  // 判断空 也就是初次渲染时候需要排列出查询参数 后续数据变动不在初始化不在设置为undefined 否则会导致每次查询清空掉上次筛选条件
  if (_.isEmpty(searchData.value)) {
    tableDataObj?.columnsData.forEach(ele => {
      if (ele.field) {
        searchData.value[ele.field] = undefined
      }
    })
  }
}, { deep: true, immediate: true })

// 合并单元格
const objectSpanMethod = ({
  row,
  column,
  rowIndex,
}) => {
  // console.log(row, column, rowIndex, columnIndex,);
  const fields = tableData.value.mergeKey || []
  const cellValue = row[column.property]
  if (cellValue && fields.includes(column.property)) {
    const prevRow = tableData.value.data[rowIndex - 1]
    let nextRow = tableData.value.data[rowIndex + 1]
    if (prevRow && prevRow[column.property] === cellValue) {
      return { rowspan: 0, colspan: 0 }
    } else {
      let countRowspan = 1
      while (nextRow && nextRow[column.property] === cellValue) {
        nextRow = tableData.value.data[++countRowspan + rowIndex]
      }
      if (countRowspan > 1) {
        return { rowspan: countRowspan, colspan: 1 }
      }
    }
  }

}

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

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


// 单列排序
// const sortChange = (val) => {
//   console.log(val, val.prop, val.order);
//   sortObj.value = {}
//   if (!_.isEmpty(val)) {
//     // sortObj.value[val.prop + 'Sort'] = val.order
//     sortObj.value['sortName'] = val.order
//     sortObj.value['sortType'] = val.prop
//   }
//   getData() // 直接统一在筛选里加入排序字段
//   // emit('updateSortChange', sortObj.value) // 不再单独传递排序字段
// }

// 多列排序
const sortChange = ({ order, prop }) => {
  visible.value = false
  //触发的排序和缓存的排序相同时,取消该字段的排序
  if (!order || sortField.value[prop] === order) {
    sortField.value[prop] = null;
  } else {
    sortField.value[prop] = order;
  }

  sortJavaField.value.sortVOList = []
  for (const key in sortField.value) {
    if (Object.hasOwnProperty.call(sortField.value, key)) {
      if (sortField.value[key]) {
        sortJavaField.value.sortVOList.push({
          sortName: key,
          sortType: sortField.value[key] === 'ascending' ? 'ASC' : sortField.value[key] === 'descending' ? 'DESC' : undefined,
        })
      }
    }
  }
  // console.log(sortField.value)
  getData() // 直接统一在筛选里加入排序字段
  // emit('updateSortChange', sortObj.value) // 不再单独传递排序字段
}
// 多列排序-类名样式保存
const handleHeadAddClass = ({ column }) => {
  if (sortField.value[column.property]) {
    column.order = sortField.value[column.property];
  }
}

// 表头点击事件
const handleHeaderClick = (column, event) => {
  // visible.value = false
  // console.log(column, event);
  // // 如果点击的是排序图标,不阻止事件,否则阻止默认的排序行为
  // const target = event.target
  // event.preventDefault();
  // event.stopPropagation();
  // event.cancelBubble = true
  // if (!target.classList.contains('caret-wrapper') && !target.classList.contains('ascending') && !target.classList.contains('descending')) {
  // }
}

</script>

<style lang="scss">
@import "./index.scss";
</style>

<style lang="scss" scoped>
:deep(.el-table) {
  thead {
    .cell {
      // background-color: #1fff;
      display: flex;
      min-width: 100px;
      position: relative;

      .table-header-div {
        // flex: 1;
        display: flex;

        .title-span {
          flex-grow: 1;
          word-break: break-word;
          /* 允许单词在任何地方断行 */
        }

        .el-only-child__content {
          height: 23px;
        }

        .icon-span {
          flex-shrink: 0;
          margin-top: 1px;
          margin-left: 5px;
          cursor: pointer;
          /* 防止缩小 */
          /* 给图标和标题之间添加一些间距 */
        }
      }

      .caret-wrapper {
        margin-top: 5px;
      }
    }
  }
}


// :deep(.el-table) {
//   thead {
//     .cell {

//       .table-header-div {
//         display: inline-block;

//         .title-span {
//         }
//       }

//       .caret-wrapper {
//         margin-top: -2px;
//       }
//     }
//   }
// }


// :deep(.el-table) {
//   thead {
//     .cell {
//       // display: flex;

//       .table-header-div {
//         // flex: 1;
//         display: flex;

//         .title-span {
//           // flex: 1;
//           white-space: nowrap!important;
//           text-overflow: ellipsis!important;
//           overflow: hidden!important;
//           word-break: break-all!important;
//         }

//         .icon-span {}
//       }

//       .caret-wrapper {
//         margin-top: 5px;

//       }
//     }
//   }
// }

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

2.index.vue 使用

<template>
  <div>

    <!-- JgTable就是封装的公共组件 注意自己需要引入使用 -->
    <JgTable :tableDataObj="tableData" @setSearchData="setSearchData" @updateEditItem="updateEditItem"
      @updateHandleSelectionChange="updateHandleSelectionChange" @updateChangeRadio="updateChangeRadio"
      @updateSortChange="updateSortChange">

      <!-- 右侧插槽内容 可不写 -->
      <template v-slot:btnContent>
        <div>
          <el-button :icon="Plus" type="primary">右侧插槽内容</el-button>
        </div>
      </template>

    </JgTable>

  </div>

</template>
<script setup lang="jsx">
import { ref, onMounted, } from 'vue'
import { Delete, Plus, 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:单选复选框 formType:筛选方式 sortable:排序 needGetList:需要远程搜索 width minWidth maxWidth showOverflowTooltip  formatter:格式化内容
  columnsData: [
    { title: '多选', type: 'selection', width: 40 },
    { title: '单选', type: 'radio', width: 40 },
    { title: '长文本不换行', field: 'aa', formType: 'input', sortable: 'custom', width: 120 },
    { title: '超出隐藏', field: 'bb', formType: 'input', sortable: 'custom', showOverflowTooltip: true },
    { title: '年月日', field: 'cc1', formType: 'daterange', sortable: 'custom', dateType: 'YYYY-MM-DD', width: 120 },
    { title: '年月日时分秒', field: 'cc2', formType: 'datetimerange', sortable: 'custom', dateType: 'YYYY-MM-DD HH:mm:ss', width: 150 },
    {
      title: '单选', field: 'dd1', formType: 'select', sortable: 'custom', selectList: nameList,
      formatter: (row) => {
        const val = nameList.value.find(item => item.value === row.dd1)?.name || ''
        return [<span>{val}</span>]
      }
    },
    {
      title: '多选', field: 'dd2', formType: 'selectMultiple', sortable: 'custom', width: 120, selectList: nameList,
      formatter: (row) => {
        const val = filters(row.dd2, nameList.value)
        return [<span>{val}</span>]
      }
    },
    {
      title: '远程搜索获取下拉数据', field: 'dd3', formType: 'selectMultiple', selectList: [], needGetList: true, sortable: 'custom', showOverflowTooltip: true, minWidth: 100,
      formatter: (row) => {
        // 如果这个下拉的数据几百条,使用过滤回显,会导致列表渲染卡住,所以这里建议直接使用name中文回显
        // 注意:获取下拉数据的逻辑,去table.vue组件内自己写,在 remoteMethod 里写逻辑

        // const val = filters(row.dd3, nameList.value)
        // return [<span>{val}</span>]
        return [<span>{row.dd3}</span>]
      }
    },
    {
      title: '接口直接获取下拉数据', field: 'dd4', formType: 'selectMultiple', selectList: [], needJavaList: true, sortable: 'custom', showOverflowTooltip: true, minWidth: 100,
      formatter: (row) => {
        return [<span>{row.dd4}</span>]
      }
    },
    { title: '无排序', field: 'ee', formType: 'input', },
    { title: '无筛选', field: 'ff', sortable: 'custom', width: 160, showOverflowTooltip: true },
    {
      title: '点击事件', field: 'gg', formType: 'input', sortable: 'custom',
      formatter: (row, column) => {
        // console.log(112,row, column); // 直接{row}或{column} 不会显示内容
        return [<span style="cursor: pointer;color:cyan;" onClick={() => clickItem(row, 'view')}> {row.gg} </span>]
      }
    },
    {
      title: '缺陷反馈状态', field: 'hh', formType: 'radio', sortable: 'custom', 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: 'jsx三元或遍历元素', field: 'ii', formType: 'input', sortable: 'custom',
      formatter: (row) => {
        let dom = []
        let arr = [1, 2, 3]
        arr.forEach(ele => {
          // jsx里使用v-show替换v-if
          dom.push(<el-tag v-show={Number(row.ii) !== Number(ele)}>{ele}</el-tag>)
        })
        return dom
      }
    },
    {
      title: '操作', field: 'actionBtn', width: 220, fixed: 'right',
      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="danger" size="small" icon={Delete} onClick={() => editItem(row, 'delete')} >删除</el-button >,
        ]
      )
    },

  ],
  data: Array.from({ length: 7 }).map((ele, index) => {
    return {
      aa: '长文本不换行长文本不换行',
      bb: '长文本隐藏隐藏隐藏隐藏隐藏',
      cc1: '2023-7-20',
      cc2: '2023-7-20 10:10:10',
      dd1: Math.floor(Math.random() * 5),
      dd2: [1, 2],
      dd3: '使用下拉name中文回显',
      dd4: '使用下拉name中文回显',
      ee: '帕拉迪',
      ff: 'PLD Sys 61012100',
      gg: Math.floor(Math.random() * 20),
      hh: Math.floor(Math.random() * 2),
      ii: Math.floor(Math.random() * 3),
    }
  })
})

const clickItem = (row, type) => {
  // console.log(row, type);
  ElMessage({
    type: 'success',
    message: '点击',
  })
}
// 编辑或查看
const editItem = (row, type) => {
  ElMessage({
    type: 'success',
    message: '点击',
  })
  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 setSearchData = (data) => {
  // console.log(data);

  tableData.value.tableLoading = true
  setTimeout(() => {
    ElMessage({
      type: 'success',
      message: '调用后端查询接口逻辑',
    })

    tableData.value.tableLoading = false
  }, 300);
}

// 子组件调用父组件的测试事件
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);
}

// 下拉过滤回显
const filters = (key, arr = []) => {
  const names = []
  const value = Array.isArray(key) ? key : [key]

  for (let i = 0; i < arr.length; i++) {
    if (value.includes(arr[i].value)) {
      names.push(arr[i].name)
    }
  }
  return names.join(';')
}


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>

3.index.scss

.page-view {
  height: 100%;
  width: 100%;
}

.flex-start {
  display: flex;
  justify-content: flex-start;
  align-items: center;
}

.flex-end {
  display: flex;
  justify-content: flex-end;
}

.flex-column {
  display: flex;
  flex-direction: column;
}

.flex-row {
  display: flex;
  flex-direction: row;
}

.flex-between {
  display: flex;
  justify-content: space-between;
  align-items: center;
}

.mr {
  margin-right: 16px;
}

.ml {
  margin-left: 16px;
}

.mt {
  margin-top: 12px;
}

.mb {
  margin-bottom: 12px;
}

.m0 {
  margin: 0;
}


  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: Vue3中封装el-table可以通过以下步骤进行: 1. 首先,我们需要创建一个自定义组件,可以命名为MyTable。 2. 在MyTable组件中,我们需要导入el-tableel-table-column组件,可以使用import语句进行导入。 3. 在template标签中,我们可以使用el-table来渲染表格。可以设置属性如:data、border、stripe等。 4. 在el-table标签内部,我们可以使用el-table-column来定义表格的列。可以设置属性如:prop(对应数据源)、label(列名)、width(列宽)等。 5. 在script标签中,我们需要定义MyTable组件的props,用于接收父组件传递的数据。 6. 在script标签中,我们可以定义一些方法或者计算属性,用于处理表格的点击事件、排序、筛选等。 7. 最后,我们需要在父组件中使用MyTable组件,并传递数据和配置选项给MyTable组件的props,来渲染自定义的表格。 总结一下,Vue3中封装el-table的关键步骤包括创建自定义组件、导入el-tableel-table-column组件、在MyTable组件中使用el-tableel-table-column来渲染表格、定义props、定义方法和计算属性以及在父组件中使用MyTable组件。通过封装el-table,我们可以更好地复用和管理表格组件,并实现更灵活的表格功能。 ### 回答2: Vue3是一个用于构建用户界面的渐进式JavaScript框架。它具有轻量级、高效、易用的特点,并且在Vue3中可以灵活地封装el-table。 首先,为了封装el-table,我们可以创建一个自定义组件,命名为TableWrapper。在TableWrapper组件的模板中使用el-table,并将el-table的相关属性、事件和插槽通过props进行传递和接收。 在TableWrapper组件的props中,我们可以定义例如data、columns、pagination等与el-table相关的属性。这样,我们就可以通过在使用TableWrapper组件时传递这些属性来配置el-table的行为。 另外,我们还可以在TableWrapper组件中定义一些需要自定义的功能,例如表格的样式、表头的固定、排序功能等。这些功能可以通过在TableWrapper组件中添加相关的方法和事件来实现。 除了属性和方法外,我们还可以使用插槽在TableWrapper组件中自定义表格的各个部分,例如表头、表尾、表格内容等。通过在TableWrapper组件的模板中使用<slot>元素,并在使用TableWrapper组件时传递相应的内容,可以方便地自定义el-table的外观和布局。 总之,通过将el-table封装自定义组件TableWrapper中,我们可以更好地控制和定制el-table的行为和外观。这样,我们就能够根据实际需要快速构建出符合需求的数据表格。 ### 回答3: Vue3中封装el-table的步骤如下: 1. 首先,创建一个名为el-table-wrapper的组件,该组件用于封装el-table。 2. 在el-table-wrapper组件中,引入el-table组件,并在模板中使用el-table进行数据展示。 3. 在el-table-wrapper组件中,接收名为data的props属性,用于传递表格数据。 4. 在el-table-wrapper组件中,通过v-for指令遍历data数据,并使用el-table-column组件进行表格列的定义。 5. 在el-table-wrapper组件中,使用slot插槽来支持自定义表格内容,例如添加操作按钮等。 6. 在el-table-wrapper组件中,可以设置一些其他属性,如border、stripe等,以适应不同的需求。 7. 在el-table-wrapper组件中,可以使用事件监听器来捕获el-table的一些事件,例如选择行、排序等。 8. 在el-table-wrapper组件中,通过emit方法触发自定义事件,以便在父组件中处理表格的交互逻辑。 总结:在Vue3中封装el-table,需要通过创建一个包装组件,在其中引入el-table组件并定义相应的列和属性,同时支持自定义内容和事件。这样可以提高代码的复用性和可维护性,方便在不同的项目中使用。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值