基于Vue+Element Plus实现表格组件

在这里插入图片描述


前言

表格对于管理类项目是很重要的,可以只管的展示和比比较数据。使用Element Plus能解决一部分问题,但是还存在一些缺点和不足。

分析

  1. 浏览器上表格数据展示空间不足。
  2. 列显示太多不够直观。
  3. 完全依赖官方表格组件代码过于臃肿不利于管理和优化。

实现

根据以上分析我们做出了如下解决方案

  1. 我们将表格组件分为两区域,操作区域和表格区域。
<template>
  <el-card class="ve-table" shadow="never">
    <template #header>
      <div class="card-header">
        <el-space size="small">
          <el-icon style="font-size: 20px">
            <CarbonTableShortcut/>
          </el-icon>
          <span>{{ $t('root.tableDataArea') }}</span>
        </el-space>
        <div>
          <slot name="header"/>
        </div>
      </div>
    </template>
    <slot name="main"/>
  </el-card>
</template>
  1. 表格区我们依据Element Plus提供的表格组件再进一步封装。
  2. 解决表格再浏览器上展示数据空间不足的问题,我们提出了表格全屏的解决方案。
  3. 解决列显示太多不够直观的问题,我们提出了列显隐的解决方案。
  4. 想要还原表格效果我们做了一个刷新表格的功能。
<template>
  <el-table-column v-if="expand" type="expand" fixed>
    <template #default="scope">
      <slot name="expand" :row="scope.row"/>
    </template>
  </el-table-column>
  <el-table-column
      v-if="selection"
      fixed
      type="selection"/>
  <el-table-column
      v-if="index"
      fixed
      type="index"/>
  <template v-for="(item, index) of columnList"
            :key="index">
    <el-table-column
        v-if="hasColumn(item.prop)"
        :sortable="item.sortable"
        :prop="item.prop"
        :fixed="item.fixed"
        :min-width="item.minWidth"
        :label="item.label">
      <template v-if="item.coverColumn" #default="scope">
        <slot :name="item.prop" :row="scope.row"/>
      </template>
    </el-table-column>
  </template>
  <el-table-column
      v-if="operationShow"
      :width="operationWidth"
      fixed="right">
    <template #header>
      <label>{{ $t('root.operation') }}</label>
      <div class="operation-colum">
        <el-tooltip
            effect="dark"
            :content="tableFullScreen?$t('button.exitFullScreen'):$t('button.fullScreen')"
            placement="bottom-end">
          <el-link
              :underline="false"
              type="primary"
              :icon="tableFullScreen? RiFullscreenExitLine:RiFullscreenFill"
              @click="handleFullScreen"/>
        </el-tooltip>
        <el-tooltip
            effect="dark"
            :content="$t('button.refresh')"
            placement="bottom-end">
          <el-link
              :underline="false"
              type="primary"
              :icon="Refresh"
              @click="handleRefresh"/>
        </el-tooltip>
        <el-popover
            placement="bottom-end"
            :title="$t('button.columnSetting')"
            width="200px"
            trigger="hover">
          <template #reference>
            <el-link
                :underline="false"
                type="primary"
                :icon="Tools">
            </el-link>
          </template>
          <el-checkbox
              class="ve-column-checkbox"
              v-model="checkAll"
              :indeterminate="isIndeterminate"
              @change="handleCheckAllChange">{{ $t('button.checkAll') }}
          </el-checkbox>
          <div class="scrollbar">
            <el-checkbox-group v-model="checkedColumn" @change="handleCheckedColumnChange">
              <el-checkbox
                  class="ve-column-checkbox"
                  v-for="(item, index) of columnList"
                  :key="index"
                  :label="item.prop">
                {{ item.label }}
              </el-checkbox>
            </el-checkbox-group>
          </div>
        </el-popover>
      </div>
    </template>
    <template #default="scope">
      <slot name="default" :row="scope.row"/>
    </template>
  </el-table-column>
</template>
const props = defineProps({
  operationShow: {
    type: Boolean,
    required: false,
    default: () => true
  },
  operationWidth: {
    type: String,
    required: false,
    default: () => '262px'
  },
  expand: {
    type: Boolean,
    required: false,
    default: () => false
  },
  selection: {
    type: Boolean,
    required: false,
    default: () => false
  },
  index: {
    type: Boolean,
    required: false,
    default: () => false
  },
  columnList: {
    type: Array as () => Array<any>,
    required: true,
    default: () => []
  }
})
const checkAll = ref(true)
const isIndeterminate = ref(false)
const column = props.columnList.map(item => {
  return item.prop
})
const checkedColumn = ref(props.columnList.map(item => {
  if (!item.columnSetting) {
    return item.prop
  }
}))
const handleCheckAllChange = (val: boolean) => {
  checkedColumn.value = val ? column : []
  isIndeterminate.value = false
}
const handleCheckedColumnChange = (value: string[]) => {
  const checkedCount = value.length
  checkAll.value = checkedCount === column.length
  isIndeterminate.value = checkedCount > 0 && checkedCount < column.length
}
const hasColumn = (item) => {
  return checkedColumn.value.includes(item)
}

const handleRefresh = () => {
  emitter.emit('refresh')
}
const tableFullScreen = ref(false)
const handleFullScreen = () => {
  if (!tableFullScreen.value) {
    document.getElementsByClassName('ve-table')[0].classList.add('ve-tableFullScreen')
    const barHeight = document.getElementsByClassName('ve-table')[0].getElementsByClassName('el-card__header')[0].clientHeight
    tableHeight.value = document.body.clientHeight - barHeight - 78
  } else {
    document.getElementsByClassName('ve-table')[0].classList.remove('ve-tableFullScreen')
    getTableHeight()
  }
  tableFullScreen.value = !tableFullScreen.value
}

例子

<template>
  <TableArea>
    <template #header>
      <el-button
        v-if="$route.meta.hasBtnPermission.includes('inviteToJoin')"
        type="primary"
        :icon="Share"
        @click="handleClick('inviteToJoin')">
        {{ $t('button.inviteToJoin') }}
      </el-button>
      <el-button
        v-if="$route.meta.hasBtnPermission.includes('add')"
        type="primary"
        :icon="CirclePlus"
        @click="handleClick('add')">
        {{ $t('button.add') }}
      </el-button>
      <el-button
        v-if="$route.meta.hasBtnPermission.includes('batchDelete')"
        type="danger"
        plain
        :icon="Delete"
        @click="handleClick('batchDelete')">
        {{ $t('button.batchDelete') }}
      </el-button>
      <el-button
        v-if="$route.meta.hasBtnPermission.includes('export')"
        type="primary"
        plain
        :icon="Download"
        @click="handleClick('export')">
        {{ $t('button.export') }}
      </el-button>
    </template>
    <template #main>
      <el-table v-loading="props.loading"
        :data="props.tableData"
        border
        :default-sort="{ prop: 'createTime', order: 'ascending' }"
        @selection-change="onSelectionChange"
        :height="tableHeight">
        <TableColumn :columnList="columnList" :selection="true" :index="false">
          <template #isDisableCh="scope">
            <el-switch
              v-model="scope.row.isDisable"
              inline-prompt
              :active-value="false"
              :inactive-value="true"
              :active-text="$t('button.enable')"
              :inactive-text="$t('button.disabled')"
              @change="handleClick('changeDisable', scope.row)"
              />
          </template>
          <template #default="scope">
            <el-button
              v-if="$route.meta.hasBtnPermission.includes('details')"
              plain
              size="small"
              :icon="Document"
              @click="handleClick('details', scope.row.id)">
              {{ $t('button.details') }}
            </el-button>
            <el-button
              v-if="$route.meta.hasBtnPermission.includes('edit')"
              plain
              type="primary"
              size="small"
              :icon="Edit"
              @click="handleClick('edit', scope.row.id)">
              {{ $t('button.edit') }}
            </el-button>
            <el-button
              v-if="$route.meta.hasBtnPermission.includes('delete')"
              plain
              type="danger"
              size="small"
              :icon="Delete"
              @click="handleClick('delete', scope.row.id)">
              {{ $t('button.delete') }}
            </el-button>
          </template>
        </TableColumn>
      </el-table>
      <slot name="pagination"/>
    </template>
  </TableArea>
</template>
const props = defineProps({
  loading: {
    type: Boolean,
    required: true,
    default: () => false
  },
  tableData: {
    type: Array as () => Array<UserInfoPageVo>,
    required: true,
    default: () => []
  }
})

const columnList = computed(() => [
  {
    fixed: true,
    sortable: true,
    prop: 'username',
    minWidth: '130',
    label: t('user.username')
  },
  {
    sortable: true,
    prop: 'nickname',
    minWidth: '120',
    label: t('user.nickname')
  },
  {
    sortable: true,
    prop: 'mobile',
    minWidth: '120',
    label: t('user.mobile')
  },
  {
    sortable: true,
    prop: 'createTime',
    minWidth: '160',
    label: t('user.creationTime')
  },
  {
    sortable: true,
    prop: 'isDisableCh',
    minWidth: '100',
    coverColumn: true,
    label: t('user.isDisable')
  }
])

const selected = ref<Array<UserInfoPageVo>>([])

const onSelectionChange = (val: UserInfoPageVo[]) => {
  selected.value = val
}

const emits = defineEmits(['onTableClick'])

const handleClick = (type, id) => {
  if (type === 'batchDelete') {
    if (selected.value.length === 0) {
      ElMessage.warning(t('message.selectDelete'))
      return
    }
    let idArray = []
    selected.value.forEach(row => idArray.push(row.id))
    id = idArray.join(',')
  }
  emits('onTableClick', type, id)
}

效果图

在这里插入图片描述


Vue项目中使用Element Plus库实现表格头拖动排序功能,可以借助Element Plus提供的Table组件和它的可拖动列头(Draggable Column)功能。以下是实现该功能的基本步骤和代码示例: 1. 引入Element Plus的Table组件及其依赖的样式文件。 2. 在你的Vue组件中定义表格的列(columns)和数据(data)。 3. 在表格的属性中使用`draggable`属性来启用列头拖动功能。 4. 使用`default-sort`属性指定默认的排序列和排序方式。 示例代码如下: ```html <template> <el-table :data="tableData" style="width: 100%" border @sort-change="handleSortChange" default-sort="{ prop: 'date', order: 'descending' }"> <el-table-column v-for="column in columns" :key="column.prop" :prop="column.prop" :label="column.label" :width="column.width"> </el-table-column> </el-table> </template> <script> export default { data() { return { tableData: [ // ... 你的表格数据 ], columns: [ { prop: 'date', label: '日期', width: 180 }, { prop: 'name', label: '姓名', width: 180 }, { prop: 'address', label: '地址' } // ... 其他列 ] }; }, methods: { handleSortChange({ column, prop, order }) { // 处理排序变化的逻辑 } } }; </script> ``` 在上面的代码中,`el-table` 组件的 `draggable` 属性默认为 `true`,因此不需要显式设置。当用户拖动列头时,`handleSortChange` 方法会被触发,你可以在这里处理排序逻辑,例如根据用户的操作来更新 `tableData`。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

刘凌枫羽

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值