基于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: {
    typeBoolean,
    requiredfalse,
    default() => true
  },
  operationWidth: {
    typeString,
    requiredfalse,
    default() => '262px'
  },
  expand: {
    typeBoolean,
    requiredfalse,
    default() => false
  },
  selection: {
    typeBoolean,
    requiredfalse,
    default() => false
  },
  index: {
    typeBoolean,
    requiredfalse,
    default() => false
  },
  columnList: {
    typeArray as () => Array<any>,
    requiredtrue,
    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: {
    typeBoolean,
    requiredtrue,
    default() => false
  },
  tableData: {
    typeArray as () => Array<UserInfoPageVo>,
    requiredtrue,
    default() => []
  }
})

const columnList = computed(() => [
  {
    fixedtrue,
    sortabletrue,
    prop'username',
    minWidth'130',
    label: t('user.username')
  },
  {
    sortabletrue,
    prop'nickname',
    minWidth'120',
    label: t('user.nickname')
  },
  {
    sortabletrue,
    prop'mobile',
    minWidth'120',
    label: t('user.mobile')
  },
  {
    sortabletrue,
    prop'createTime',
    minWidth'160',
    label: t('user.creationTime')
  },
  {
    sortabletrue,
    prop'isDisableCh',
    minWidth'100',
    coverColumntrue,
    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实现表格组件

版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。

文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/251408.html

(0)
小半的头像小半

相关推荐

发表回复

登录后才能评论
极客之音——专业性很强的中文编程技术网站,欢迎收藏到浏览器,订阅我们!