vue3+elementPlus封装的一体表格

目录结构

源码

exportOptions.js

JavaScript 复制代码
export default reactive([
  {
    label: '导出本页',
    key: '1',
  },
  {
    label: '导出全部',
    key: '2',
  },
])

index.vue

html 复制代码
<template>
  <div class="flex flex-justify-between flex-items-end">
    <div>
      <el-button-group>
        <slot name="left"></slot>
        <el-dropdown trigger="click" v-if="$attrs.onExport" @command="exportTable">
          <el-button :size="size">
            导出<el-icon class="el-icon--right"><arrow-down /></el-icon>
          </el-button>
          <template #dropdown>
            <el-dropdown-menu>
              <el-dropdown-item v-for="item in exportOptions" :command="item.key">{{ item.label }}</el-dropdown-item>
            </el-dropdown-menu>
          </template>
        </el-dropdown>
        <el-dropdown trigger="click" v-if="$attrs.onPrint" @command="printTable">
          <el-button :size="size">
            打印<el-icon class="el-icon--right"><arrow-down /></el-icon>
          </el-button>
          <template #dropdown>
            <el-dropdown-menu>
              <el-dropdown-item v-for="item in printOptions" :command="item.key">{{ item.label }}</el-dropdown-item>
            </el-dropdown-menu>
          </template>
        </el-dropdown>
      </el-button-group>
    </div>
    <el-button-group :size="size">
      <slot name="right"></slot>
    </el-button-group>
  </div>
  <el-table ref="table" :header-cell-style="headerCellStyle" :cell-style="cellStyle" :row-class-name="rowClassName"
    :size="size" :data="data" :[heightProp]="height || maxHeight" :border="border" :row-key="rowKey"
    :empty-text="emptyText" v-loading="loading" :show-summary="showSummary" :summary-method="summaryMethod"
    tooltip-effect="dark" @row-click="rowClick" @cell-click="cellClick" @select="select" @select-all="selectAll"
    @selection-change="selectionChange" element-loading-text="拼命加载中" element-loading-background="rgba(0, 0, 0, 0.5)"
    @row-dblclick="rowDblclick">
    <el-table-column v-if="showSelection" fixed="left" type="selection" :reserve-selection="reserveSelection" width="40"
      align="center" :selectable="selecteisDisabled">
    </el-table-column>
    <el-table-column v-if="showIndex" fixed="left" type="index" label="序号" min-width="50">
      <template slot-scope="scope">
        {{ pageSize * (currentPage - 1) + (scope.$index + 1) }}
      </template>
    </el-table-column>
    <slot name="columns"></slot>
  </el-table>
  <el-pagination class="text-end mt-3" v-if="$attrs.handleCurrentChange || $attrs.onSizeChange" :size="size"
    @size-change="dataSizeChange" @current-change="handleCurrentChange" :page-sizes="[VITE_APP_DATA_SIZE, 20, 30, 50]"
    :page-size="pageSize" :disabled="total <= 0 || loading" :current-page="currentPage"
    layout="total, sizes, prev, pager, next, jumper" :total="total">
  </el-pagination>
</template>

<script setup>
import { ElButtonGroup, ElButton, ElDropdown, ElTable, ElPagination, ElNotification } from "element-plus";
import { exportExcel } from '@/utils/export-file.js'
import exportOptions from "./exportOptions.js"
import printOptions from "./printOptions.js"
import propsData from "./props.js"


const instance = getCurrentInstance();
const props = defineProps(propsData)
const VITE_APP_DATA_SIZE = import.meta.env.VITE_APP_DATA_SIZE;
const emits = defineEmits(['current-change', 'size-change', 'row-click', 'cell-click', 'row-dblclick', 'select', 'select-all', 'selection-change']);




const showSelection = computed(() => {
  return ["select", "selection-change", "select-all"].includes(Object.keys(instance.proxy.$attrs));
})
const heightProp = computed(() => {
  // 因为elementUI不能 height 和 max-height 同时存在,所以采用动态属性
  // console.log("height:", props.height, "maxHeight:", props.maxHeight);
  return props.height ? "height" : "max-height";
})

watch(() => props.total, (now) => {
  if (now === 0) {
    return
  }

  for (let index in exportOptions) {
    exportOptions[index].disabled = false
    printOptions[index].disabled = false
  }
})

// 翻页
function handleCurrentChange(val) {
  console.log(`当前页: ${val}`)
  emits('current-change', val)
}

// 页量
function dataSizeChange(val) {
  console.log(`每页 ${val} 条`)
  emits('size-change', val)
}

// 点击行
function rowClick(row, column, event) {
  // console.log(row, column, event);
  emits("row-click", row, column, event);
}

// 点击单元格
function cellClick(row, column, cell, event) {
  emits("cell-click", row, column, cell, event);
}

// 双击行
function rowDblclick(row, column, event) {
  emits("row-dblclick", row, column, event);
}

// 选择数据
function select(selection, row) {
  // console.log(election, row);
  emits("select", selection, row);

}
// 选择数据
function selectionChange(selection) {
  // console.log(selection);
  emits("selection-change", selection);

}
// 全选数据
function selectAll(selection) {
  console.log(selection);
  emits("select-all", selection);

}

// 导出表格
function exportTable(command) {
  if (props.total < 1) {
    $message.warning('无数据可导')
    return
  }

  if (command === '1' || (command === '2' && props.total < 3000)) {
    exportingExcel(command)
  } else {
    $dialog.warning({
      content: '导出数据量过大,您可以选择联系开发人员导出',
      positiveText: '好',
      negativeText: '不导了',
      onPositiveClick: () => {
        window.open(
          import.meta.env.VITE_DEPARTMENT_SERVICE,
          '_blank',
          'location=no, status=no, menubar=no'
        )
      },
    })
  }
}
// 执行导出表格
async function exportingExcel(command) {
  const title = prompt('文件名称:', '表' || '')
  if (!title) return

  const notify = notification('开始下载', title)
  const res = await exporting(command)
  try {
    if (res.code === 0 && res.total > 0) {
      const columns = []
      props.columns.filter((item) => {
        if (item.key) {
          columns.push({
            header: item.title, // header是exceljs能识别的字段
            key: item.key,
          })
        }
      })

      if (props.showIndex) {
        res.rows = res.rows.map((item, index) => {
          return {
            index: index + 1,
            ...item,
          }
        })
      }
      exportExcel(columns, res.rows, title)
      notification('下载成功', title)
    }
  } catch {
    notification('数据响应失败', title)
  }

  try {
    notify.close()
  } catch {
    notify.destroy()
  }
}
function notification(content, title) {
  return Notification.permission === 'granted'
    ? new Notification(content, { body: title })
    : ElNotification.info({ content, meta: title })
}
// 打印表格数据
function printTable(command) {
  if (props.total < 1) {
    return $message.info('无数据打印')
  }

  emits('printing', command)
}

defineExpose({
  exportExcel
})
</script>

<style scoped lang="scss">
:deep(.n-data-table-tr .n-data-table-resize-button) {
  width: 1px;
  left: unset;
  right: 0;
}

:deep(.n-data-table-tr .n-data-table-resize-button::after) {
  width: 1px;
  right: -1px;
  left: unset;
}
</style>

printOptions.js

JavaScript 复制代码
export default reactive([
  {
    label: '打印本页',
    key: '1',
  },
  {
    label: '打印全部',
    key: '2',
  },
])

props.js

html 复制代码
export default {
  // 显示序号列
  showIndex: {
    type: Boolean,
    default: false,
  },
  data: {
    type: Array,
    default: () => {
      return []
    },
  },
  columns: {
    type: Array,
    default: () => {
      return []
    },
  },
  loading: {
    type: Boolean,
    default: true,
  },
  size: {
    type: String,
    default: 'medium',
  },
  emptyText: {
    type: String,
    default: '暂无数据',
  },
  border: {
    type: Boolean,
    default: true,
  },
  total: {
    type: Number,
    default: 0,
  },
  pageSize: {
    type: Number,
    default: Number(import.meta.env.VITE_APP_DATA_SIZE),
  },
  currentPage: {
    type: Number,
    default: 0,
  },
  height: {
    type: Number,
    default: 550,
  },
  maxHeight: {
    type: Number,
  },
  headerCellStyle: {
    type: [Function, Object],
  },
  cellStyle: {
    type: [Function, Object],
  },
  // 返回值用来决定这一行的 CheckBox 是否可以勾选
  selecteisDisabled: {
    type: Function
  },
  rowKey: {
    type: Function,
    default: undefined,
  },
  showSummary: {
    type: Boolean,
    default: false,
  },
  summaryMethod: {
    type: Function
  },
  exporting: {
    type: Function,
    default: undefined,
  },
}

使用说明

html 复制代码
 <q-table :data="tableData">
     <template v-slot:left>
         表头上左侧容器
     </template>
     <template v-slot:right>
         表头上右侧容器
     </template>
     <template v-slot:columns>
        数据表体部分
     </template>
</q-table>
  • 参数

    \props.js

  • 事件

    ['current-change', 'size-change', 'row-click', 'cell-click', 'row-dblclick', 'select', 'select-all', 'selection-change']

  • 导出

    传入onExporting事件,就会显示导出摁扭

  • 打印

    传入onPrinting事件,就会显示打印摁扭

  • 选择列

    传入'select', 'select-all', 'selection-change'其中一种事件,就会显示选择列

相关推荐
m0_748245342 分钟前
python——Django 框架
开发语言·python·django
曼巴UE57 分钟前
UE5.3 C++ TArray系列(一)
开发语言·c++·ue5
熬夜苦读学习18 分钟前
Linux文件系统
linux·运维·服务器·开发语言·后端
菜鸟一枚在这26 分钟前
深度解析建造者模式:复杂对象构建的优雅之道
java·开发语言·算法
化作繁星33 分钟前
如何在 React 中测试高阶组件?
前端·javascript·react.js
Au_ust40 分钟前
千峰React:函数组件使用(2)
前端·javascript·react.js
阿巴~阿巴~1 小时前
多源 BFS 算法详解:从原理到实现,高效解决多源最短路问题
开发语言·数据结构·c++·算法·宽度优先
GAMESLI-GIS1 小时前
【WebGL】fbo双pass案例
前端·javascript·webgl
bin91532 小时前
DeepSeek 助力 Vue 开发:打造丝滑的二维码生成(QR Code)
前端·javascript·vue.js·ecmascript·deepseek
@LitterFisher2 小时前
Excell 代码处理
前端·javascript·excel