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

带完善内容


提示:二合一,封装el-table以及给表头配置类自定义筛选和排序


一、pandas是什么?

el-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 class="table-header-div">
              <span class="title-span">{{ item.title }}</span>

              <!-- 控制筛选按钮显隐 -->
              <template v-if="item.field !== 'actionBtn'">
                <el-popover :visible="visible && item.field === showKey" :virtual-ref="refName" placement="bottom"
                  :width="'fit-content'">
                  <template #reference>
                    <el-icon class="icon-span" :id="item.field" @click.stop="toggleNameFilter(item.field)"
                      style="cursor: pointer; margin-left: 10px;"
                      :style="{ color: searchData[item.field] ? '#007d7b' : '#85888e' }">
                      <Filter />
                    </el-icon>
                  </template>
                  <div>
                    <!-- 输入 -->
                    <el-input v-if="item.formType === 'input'" v-model="searchData[item.field]" placeholder="输入框"
                      @input="applyNameFilter" clearable style="margin-top: 10px; width: 150px;" />
                    <!-- 下拉 -->
                    <el-select v-if="item.formType === 'select' || item.formType === 'radio'" filterable
                      v-model="searchData[item.field]" placeholder="Select Tag" @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 collapse-tags
                      v-model="searchData[item.field]" placeholder="Select Tag" 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-date-picker v-if="item.formType === 'daterange' || item.formType === 'datetimerange'"
                      style="width: 240px;" 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>


    <pre>{{ {...searchData,...sortObj} }}</pre>
    <pre>{{ tableData }}</pre>
  </div>
</template>

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


defineOptions({
  name: "JgTable"
})

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

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


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

let searchData = ref({}) // 搜索参数 每个表头字段
let sortObj = ref({}) // 排序

// 时间格式化
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
    }
  }

  applyNameFilter()
  applyTagFilter()

  getData()
}

// 触发筛选
const toggleNameFilter = (key) => {
  if (showKey.value !== key) {
    visible.value = false
    getData()
  }


  // console.log(11, document.querySelector(`[ref="${key}"]`));
  // showKey.value = document.querySelector(`[ref="${key}"]`)
  // console.log(document.getElementById(key));
  refName.value = document.getElementById(key)
  // refName.value = document.querySelector(`[ref="${key}"]`)
  // refName.value = key
  showKey.value = key
  visible.value = !visible.value



}

// 点击其他元素
const handleClickOutside = () => {
  // visible.value = false;
};

// 重置
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, ...sortObj.value });
  emit('setSearchData', { ...searchData.value, ...sortObj.value })
}


watch([() => props.tableDataObj, () => props.rulesData], ([tableDataObj, rulesData]) => {
  // console.log('监听');
  tableFlag.value = false
  tableData.value = _.cloneDeep(tableDataObj) // 使用lodash的深拷贝方法
  tableFlag.value = true
  tableDataObj?.columnsData.forEach(ele => {
    if (ele.field) {
      searchData.value[ele.field] = undefined
    }
  })
}, { deep: true, immediate: true })


// 多选
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, val.prop, val.order);
  sortObj.value = {}
  if (!_.isEmpty(val)) {
    sortObj.value[val.prop + 'Sort'] = val.order
  }
  getData() // 直接统一在筛选里加入排序字段
  // emit('update:sortChange', sortObj.value) // 不再单独传递排序字段
}




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

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

        flex: 1;
        display: flex;
        // white-space: nowrap;

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

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

      .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>

index.vue

<template>
  <JgTable :tableDataObj="tableData" @setSearchData="setSearchData" @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', 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: 140 },
    { title: '缺陷发布时间2', field: 'cc2', formType: 'datetimerange', sortable: 'custom', dateType: 'YYYY-MM-DD HH:mm:ss', width: 150 },
    // { title: '缺陷发布时间',  field: 'cc1', formType: 'datetime',sortable: 'custom',  dateType: 'YYYY-MM-DD HH:mm:ss' },
    {
      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: 'ee', formType: 'input', sortable: 'custom', },
    { title: '设备版本', field: 'ff', formType: 'input', sortable: 'custom', 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', sortable: 'custom', minWidth: 200, 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', sortable: 'custom',
      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: 'actionBtn', 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',
      dd1: Math.floor(Math.random() * 5),
      dd2: [1, 2],
      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 setSearchData = (data) => {
  console.log(data);
}

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

相关推荐

  1. vue3封装el-table实现表头定义筛选

    2024-07-19 07:56:01       20 阅读
  2. vue3el-table实现表格合计行

    2024-07-19 07:56:01       60 阅读

最近更新

  1. docker php8.1+nginx base 镜像 dockerfile 配置

    2024-07-19 07:56:01       67 阅读
  2. Could not load dynamic library ‘cudart64_100.dll‘

    2024-07-19 07:56:01       72 阅读
  3. 在Django里面运行非项目文件

    2024-07-19 07:56:01       58 阅读
  4. Python语言-面向对象

    2024-07-19 07:56:01       69 阅读

热门阅读

  1. jEasyUI 显示海量数据

    2024-07-19 07:56:01       19 阅读
  2. 团队高效地使用 Git 进行协同开发

    2024-07-19 07:56:01       20 阅读
  3. ArrayList

    2024-07-19 07:56:01       21 阅读
  4. vue项目使用iview☞Modal后页面不能滚动的诡异问题

    2024-07-19 07:56:01       21 阅读
  5. STM32 | 看门狗+RTC源码解析

    2024-07-19 07:56:01       21 阅读
  6. 富文本中提取信息并去除其中的HTML或XML标签

    2024-07-19 07:56:01       21 阅读
  7. 2024前端面试真题【手写篇】

    2024-07-19 07:56:01       18 阅读
  8. 聊聊最近在看的一本书-《从极简到极致》

    2024-07-19 07:56:01       20 阅读
  9. Python数据获取(网页视频、音频版)

    2024-07-19 07:56:01       22 阅读