通用管理后台组件库-8-表格组件

表格组件

说明:el-table的二次封装,使用scheme配置的方式设置表格字段。

1.实现效果,与el-table一致

2.类型文件types.d.ts

yaml 复制代码
import type { PaginationProps, TableColumnCtx, TableProps } from 'element-plus'
import { Component } from 'vue'
export interface TableColumnType extends TableColumnCtx<any> {
  defaultSlot?: typeof Component
  headerSlot?: typeof Component
  children?: TableColumnType[]
}

export interface PaginationType extends Partial<PaginationProps> {
  align?: 'left' | 'center' | 'right'
  total: number
  defaultSlot?: typeof Component
}

export interface VTableProps extends TableProps<any> {
  columns: TableColumnType[]
  pagination?: PaginationType
}

export type TableEventsType = {
  select: [selection: any, row: any]
  'select-all': [selection: any]
  'selection-change': [selection: any]
  'cell-mouse-enter': [row: any, column: any, cell: any, event: Event]
  'cell-mouse-leave': [row: any, column: any, cell: any, event: Event]
  'cell-click': [row: any, column: any, cell: any, event: Event]
  'cell-dblclick': [row: any, column: any, cell: any, event: Event]
  'cell-contextmenu': [row: any, column: any, cell: any, event: Event]
  'row-click': [row: any, column: any, event: Event]
  'row-contextmenu': [row: any, column: any, event: Event]
  'row-dblclick': [row: any, column: any, event: Event]
  'header-click': [column: any, event: Event]
  'header-contextmenu': [column: any, event: Event]
  'sort-change': [{ column: any; prop: string; order: string }]
  'filter-change': [{ key: string }, filters: any[]]
  'current-change': [currentRow: any, oldCurrentRow: any]
  'header-draged': [newWidth: number, oldWidth: number, column: any, event: Event]
  'expand-change': [row: any, expandedRows: any[] | boolean]
}

// 分页中事件的回调函数
type PaginationCallFunc = (value: number) => void
// 分页组件emit出事件,因pagination中事件与table中事件重名,故重命名page-xx
export type PaginationEventsType = {
  'page-size-change': [PaginationCallFunc]
  'page-current-change': [PaginationCallFunc]
  'page-prev-click': [PaginationCallFunc]
  'page-next-click': [PaginationCallFunc]
}

// table+pagination emit出事件
export type VTableEmitsType = TableEventsType & PaginationEventsType

3.el-table-column 封装 VTableColumn.vue

xml 复制代码
<template>
  <el-table-column v-bind="props">
    <!-- 设置el-table-column插槽,可以在scheme中配置对应的插槽内容 -->
    <template #default="scope" v-if="defaultSlot">
      <component v-bind="scope" :is="defaultSlot"></component>
    </template>
    <template #header="scope" v-if="headerSlot">
      <component v-bind="scope" :is="headerSlot"></component>
    </template>
    <!-- 处理嵌套的多级表头 -->
    <template v-if="children && children.length">
      <v-table-column v-bind="item" v-for="(item, index) in children" :key="index"></v-table-column>
    </template>
  </el-table-column>
</template>

<script setup lang="ts">
import type { TableColumnType } from './types'

const props = defineProps<TableColumnType>()
</script>

<style scoped></style>

4.el-table封装VTable.vue

xml 复制代码
<template>
  <el-table ref="tableRef" v-bind="props" v-on="events" style="width: 100%">
    <v-table-column
      v-for="(column, index) in props.columns"
      :key="index"
      v-bind="setColumnDefault(column)"
    ></v-table-column>
    <!-- 设置el-table默认插槽 -->
    <slot></slot>
    <!-- 设置el-table的empty和append插槽 -->
    <template #empty>
      <slot name="empty"></slot>
    </template>
    <template #append>
      <slot name="append"></slot>
    </template>
  </el-table>
  <div :class="['flex p-2', paginationClass]" v-if="isDefined(pagination)">
    <el-pagination v-bind="pagination" v-on="pageEvents">
      <template #default="scope" v-if="pagination.defaultSlot">
        <component v-bind="scope" :is="pagination.defaultSlot"></component>
      </template>
    </el-pagination>
  </div>
</template>

<script lang="ts" setup>
import { isDefined } from '@vueuse/core'
import type { VTableEmitsType, VTableProps } from './types'
import VTableColumn from './VTableColumn.vue'
import { exposeEventsUtils, forwardEventsUtils } from '@/utils/format'
import type { TableColumnCtx } from 'element-plus'
// 接收传入的数据
const props = withDefaults(defineProps<VTableProps>(), {
  pagination: () => ({
    align: 'right',
    size: 'default',
    small: false,
    background: false,
    layout: 'total, sizes, prev, pager, next, jumper',
    pageSizes: [10, 20, 30, 40, 50, 100],
    total: 0
  }),
  stripe: false,
  border: true,
  size: 'default',
  fit: true,
  showHeader: true,
  highlightCurrentRow: false,
  emptyText: 'No data',
  defaultExpandAll: false,
  tooltipEffect: 'dark',
  showSummary: false,
  flexible: false,
  selectOnIndeterminate: true,
  indent: 16,
  tableLayout: 'fixed',
  scrollbarAlwaysOn: false
})
const tableRef = ref()
// 传出el-table事件,在封装的组件上直接使用即可
const emits = defineEmits<VTableEmitsType>()
// el-table所有事件名称
const eventsName = [
  'select',
  'select-all',
  'selection-change',
  'cell-mouse-enter',
  'cell-mouse-leave',
  'cell-contextmenu',
  'cell-click',
  'cell-dblclick',
  'row-click',
  'row-contextmenu',
  'row-dblclick',
  'header-click',
  'header-contextmenu',
  'sort-change',
  'filter-change',
  'current-change',
  'header-dragend',
  'expand-change'
]
// el-pagination分页事件名称
const pageEventsName = ['size-change', 'current-change', 'prev-click', 'next-click']
// el-table所有方法名称,可以在封装的组件上直接使用
const exposeEvents = [
  'clearSelection',
  'getSelectionRows',
  'toggleRowSelection',
  'toggleAllSelection',
  'toggleRowExpansion',
  'setCurrentRow',
  'clearSort',
  'clearFilter',
  'doLayout',
  'sort',
  'scrollTo',
  'setScrollTop',
  'setScrollLeft'
]
// 获取所有事件集合为events对象
const events = forwardEventsUtils(emits, eventsName)
// 获取所有分页事件集合为pageEvents对象
const pageEvents = forwardEventsUtils(emits, pageEventsName, 'page-')
// 暴露table所有方法,可以在封装的组件中使用ref直接使用
const expose = exposeEventsUtils(tableRef, exposeEvents)
defineExpose({ ...expose })

const paginationClass = computed(() => {
  let defaultsClass = 'justify-center'
  if (props.pagination && props.pagination.align) {
    if (props.pagination.align === 'left') {
      defaultsClass = 'justify-start'
    }
    if (props.pagination.align === 'right') {
      defaultsClass = 'justify-end'
    }
  }
  return defaultsClass
})

// 设置table-columns默认的属性值
const columnDefaults = {
  sortable: false,
  'sort-orders': ['ascending', 'descending', null],
  resizable: true,
  align: 'left',
  'reserve-selection': false,
  'filter-multiple': true
}
// 默认值的属性添加到table-column上
function setColumnDefault(column: TableColumnCtx) {
  return { ...columnDefaults, ...column }
}
</script>

5.工具函数 format.ts

typescript 复制代码
import type { Ref } from 'vue'
/**
 * 将kebab-case(短横线命名法)转换为camelCase(驼峰命名法)
 * @param str 需要转换的字符串,格式为kebab-case(如:hello-world)
 * @returns 转换后的camelCase格式字符串(如:helloWorld)
 */
export function kebabToCamel(str: string): string {
  // 使用正则表达式匹配所有短横线后跟小写字母的情况
  // 替换函数将匹配到的字母转换为大写
  return str.replace(/-([a-z])/g, function (g) {
    // g是匹配到的结果,g[1]是短横线后的第一个小写字母
    // 将该字母转换为大写并返回
    return g[1].toUpperCase()
  })
}

// 设置一个转换函数,将所有事件函数放在一个对象中,用v-on="对象"绑定到el-table上
export const forwardEventsUtils = (emits: any, array: string[], prefix: string = '') => {
  // 创建一个对象,用于存储所有事件函数。Record用于创建一个对象类型,其键和值都有指定的类型
  const forwardEvents: Record<string, (...args: any[]) => void> = {}
  array.forEach((eventName) => {
    // 将item从camel-case转换为camelCase
    const name = kebabToCamel(eventName)
    // (...args: any[]) => {}:定义一个接收任意参数的箭头函数
    forwardEvents[name] = (...args: any[]) => {
      // 触发对应的事件,并传递所有, 分页事件须加prefix前缀page-
      emits(prefix + eventName, ...args)
    }
  })
  return forwardEvents 
}

// 如上,再将table中的方法函数放在一个对象中,用expose(对象)供封装的组件使用
export const exposeEventsUtils = (ref: Ref<any>, array: string[]) => {
  // eslint-disable-next-line @typescript-eslint/no-unsafe-function-type
  const exposeMethods: Record<string, Function> = {}
  array.forEach((exposeName) => {
    // 例如:obj= {clearSelection:(...args)=> tabelRef.value.clearSelection(...args)}
    exposeMethods[exposeName] = (...args: any[]) => {
      if (ref.value && typeof ref.value[exposeName] === 'function') {
        return ref.value[exposeName](...args)
      }
    }
  })
  return exposeMethods
}

6.基础表格demo,index.vue

xml 复制代码
<template>
  <div class="m-4">
    <p>基础表格示例</p>
    <el-tabs v-model="activeName" class="demo-tabs">
      <el-tab-pane label="基础表格" name="first">
        <VTable :columns="columns" :data="tableData" :pagination="pagination" @page-current-change="handleCurrentChange" @page-size-change="handleCurrentChange"
        @page-next-change="handleCurrentChange"
        @page-prev-change="handleCurrentChange"
      />
      </el-tab-pane>
      <el-tab-pane label="插槽封装表格" name="second">
        <VTable :columns="columns" :data="tableData">
          <template #append> 我是append最后一行 </template>
        </VTable>
      </el-tab-pane>
      <el-tab-pane label="scheme配置表格" name="third">
        <VTable :columns="columns" :data="tableData">
          <!-- 可以使用el-table的默认插槽展示自定义内容,也可通过下边scheme配置自定义内容 -->
          <!-- <el-table-column fixed="right" label="Operations" min-width="120"> -->
          <!-- <template #default="scope"> -->
          <!-- <el-button link type="primary" size="small" @click="handleClick(scope.row)"> -->
          <!-- Detail -->
          <!-- </el-button> -->
          <!-- <el-button link type="primary" size="small">Edit</el-button> -->
          <!-- </template> -->
          <!-- </el-table-column> -->
        </VTable>
      </el-tab-pane>
      <el-tab-pane label="固定表头" name="fourth">
        <VTable :columns="columns" :data="tableData" :height="200"></VTable>
      </el-tab-pane>
      <el-tab-pane label="流体高度" name="23">
        <VTable :columns="columns" :data="tableData" :max-height="300"></VTable>
      </el-tab-pane>
      <el-tab-pane label="多级表头" name="233">
        <VTable :data="tableData1" style="width: 100%" :columns="columns1">
          <!-- <el-table-column prop="date" label="Date" width="150" /> -->
          <!-- <el-table-column label="Delivery Info"> -->
          <!-- <el-table-column prop="name" label="Name" width="120" /> -->
          <!-- <el-table-column label="Address Info"> -->
          <!-- <el-table-column prop="state" label="State" width="120" /> -->
          <!-- <el-table-column prop="city" label="City" width="120" /> -->
          <!-- <el-table-column prop="address" label="Address" /> -->
          <!-- <el-table-column prop="zip" label="Zip" width="120" /> -->
          <!-- </el-table-column> -->
          <!-- </el-table-column> -->
        </VTable>
      </el-tab-pane>
      <el-tab-pane label="事件封装表格" name="2332">
        <VTable @row-click="handleRowClick" :columns="columns" :data="tableData"></VTable>
      </el-tab-pane>
    </el-tabs>
  </div>
</template>

<script setup lang="tsx">
import type { TableColumnType } from '@/components/Table/types'

definePage({
  meta: {
    title: 'pages.components.table-basic',
    icon: 'meteor-icons:table-layout'
  }
})
const pagination = ref({
  align: 'right',
  size: 'default',
  small: false,
  background: false,
  layout: 'total, sizes, prev, pager, next, jumper',
  pageSizes: [10, 20, 30, 40, 50, 100],
  total: 100
})
const activeName = ref('first')
const tableData = [
  {
    date: '2016-05-03',
    name: 'Tom',
    address: 'No. 189, Grove St, Los Angeles'
  },
  {
    date: '2016-05-02',
    name: 'Tom',
    address: 'No. 189, Grove St, Los Angeles'
  },
  {
    date: '2016-05-04',
    name: 'Tom',
    address: 'No. 189, Grove St, Los Angeles'
  },
  {
    date: '2016-05-01',
    name: 'Tom',
    address: 'No. 189, Grove St, Los Angeles'
  },
  {
    date: '2016-04-01',
    name: 'Tom',
    address: 'No. 189, Grove St, Los Angeles'
  },
  {
    date: '2016-05-08',
    name: 'Tom',
    address: 'No. 189, Grove St, Los Angeles'
  },
  {
    date: '2016-05-09',
    name: 'Tom',
    address: 'No. 189, Grove St, Los Angeles'
  },
  {
    date: '2016-05-10',
    name: 'Tom',
    address: 'No. 189, Grove St, Los Angeles'
  }
]
const columns = [
  {
    prop: 'date',
    label: '日期'
  },
  {
    prop: 'name',
    label: '姓名'
  },
  {
    prop: 'address',
    label: '地址'
  },
  {
    prop: 'Operations',
    label: '操作',
    width: 120,
    fixed: 'right',
    defaultSlot: (_props) => (
      <>
        <el-button link type="primary" size="small" onClick={() => handleClick(_props)}>
          detail
        </el-button>
        <el-button link type="primary" size="small">
          Edit
        </el-button>
      </>
    )
  }
] as TableColumnType[]
const tableData1 = [
  {
    date: '2016-05-03',
    name: 'Tom',
    state: 'California',
    city: 'Los Angeles',
    address: 'No. 189, Grove St, Los Angeles',
    zip: 'CA 90036'
  },
  {
    date: '2016-05-02',
    name: 'Tom',
    state: 'California',
    city: 'Los Angeles',
    address: 'No. 189, Grove St, Los Angeles',
    zip: 'CA 90036'
  },
  {
    date: '2016-05-04',
    name: 'Tom',
    state: 'California',
    city: 'Los Angeles',
    address: 'No. 189, Grove St, Los Angeles',
    zip: 'CA 90036'
  },
  {
    date: '2016-05-01',
    name: 'Tom',
    state: 'California',
    city: 'Los Angeles',
    address: 'No. 189, Grove St, Los Angeles',
    zip: 'CA 90036'
  },
  {
    date: '2016-05-08',
    name: 'Tom',
    state: 'California',
    city: 'Los Angeles',
    address: 'No. 189, Grove St, Los Angeles',
    zip: 'CA 90036'
  },
  {
    date: '2016-05-06',
    name: 'Tom',
    state: 'California',
    city: 'Los Angeles',
    address: 'No. 189, Grove St, Los Angeles',
    zip: 'CA 90036'
  },
  {
    date: '2016-05-07',
    name: 'Tom',
    state: 'California',
    city: 'Los Angeles',
    address: 'No. 189, Grove St, Los Angeles',
    zip: 'CA 90036'
  }
]
const columns1 = [
  {
    prop: 'date',
    label: 'Date',
    width: 150
  },
  {
    label: 'Delivery Info',
    children: [
      {
        prop: 'name',
        label: 'Name',
        width: 120
      },
      {
        label: 'Address Info',
        children: [
          {
            prop: 'state',
            label: 'State',
            width: 120
          },
          {
            prop: 'city',
            label: 'City',
            width: 120
          },
          {
            prop: 'address',
            label: 'Address'
          },
          {
            prop: 'zip',
            label: 'Zip',
            width: 120
          }
        ]
      }
    ]
  }
]

const handleClick = (row) => {
  console.log('🚀 ~ handleClick ~ row:', row)
}
const handleRowClick = (...args) => {
  console.log('🚀 ~ handleRowClick ~ args:', args)
}
const handleCurrentChange = (val) => {
  console.log('🚀 ~ handleCurrentChange ~ val:', val)
}
</script>

<style scoped></style>

复杂列表格complex-table.vue

typescript 复制代码
<template>
  <div class="m-4">
    <p>复杂列表格示例</p>
    <el-tabs v-model="activeName" class="demo-tabs">
      <el-tab-pane label="单选表格" name="first">
        <VTable
          @row-click="handleRowClick"
          highlight-current-row
          :columns="columns"
          :data="tableData"
        ></VTable>
        <div class="mt-4 w-full">所选表格项:{{ singleSelection }}</div>
      </el-tab-pane>
      <el-tab-pane label="多选表格" name="second">
        <VTable
          ref="multipleTableRef"
          :columns="columns1"
          :data="tableData1"
          row-key="id"
          @selection-change="handleSelectionChange"
        >
        </VTable>
        <div class="mt-4 w-full">
          <el-button @click="toggleSelection([tableData1[1], tableData1[2]])">
            切换第二行和第三行的选中状态
          </el-button>
          <el-button @click="toggleSelection([tableData1[1], tableData1[2]], false)">
            根据可选状态切换选择状态
          </el-button>
          <el-button @click="toggleSelection()">清除选中项</el-button>
        </div>
      </el-tab-pane>
      <el-tab-pane label="排序表格" name="3">
        <VTable
          :columns="columns1"
          :data="tableData1"
          :default-sort="{ prop: 'date', order: 'descending' }"
        ></VTable>
      </el-tab-pane>
      <el-tab-pane label="筛选表格" name="4">
        <VTable
          ref="filterTableRef"
          :columns="columnsFilter"
          :data="tableDataFilter"
          row-key="date"
        ></VTable>
        <div>
          <el-button @click="resetDateFilter">重置 date 筛选</el-button>
          <el-button @click="clearFilter">重置所有筛选 </el-button>
        </div>
      </el-tab-pane>
      <el-tab-pane label="树型表格" name="5">
        <VTable
          :columns="columnsTree"
          :data="tableDataTree"
          row-key="id"
          border
          default-expand-all
        ></VTable>
      </el-tab-pane>
      <el-tab-pane label="表尾合计行" name="6">
        <VTable
          :columns="sumColumns"
          :data="sumTableData"
          border
          height="200"
          :summary-method="getSummaries"
          show-summary
        ></VTable>
      </el-tab-pane>
      <el-tab-pane label="合并行或列" name="7">
        <VTable
          :columns="sumColumns"
          :data="sumTableData1"
          border
          :span-method="arraySpanMethod"
        ></VTable>
        <VTable
          class="mt-10"
          :columns="sumColumns"
          :data="sumTableData1"
          border
          :span-method="objectSpanMethod"
        ></VTable>
      </el-tab-pane>
    </el-tabs>
  </div>
</template>

<script setup lang="tsx">
import type { TableColumnType } from '@/components/Table/types'

definePage({
  meta: {
    title: '复杂列表格',
    icon: 'mdi:grid'
  }
})

const activeName = ref('first')
// ------------------单选------------------
const tableData = [
  {
    date: '2016-05-03',
    name: 'Tom',
    address: 'No. 189, Grove St, Los Angeles'
  },
  {
    date: '2016-05-02',
    name: 'Tom',
    address: 'No. 189, Grove St, Los Angeles'
  },
  {
    date: '2016-05-04',
    name: 'Tom',
    address: 'No. 189, Grove St, Los Angeles'
  },
  {
    date: '2016-05-01',
    name: 'Tom',
    address: 'No. 189, Grove St, Los Angeles'
  },
  {
    date: '2016-04-01',
    name: 'Tom',
    address: 'No. 189, Grove St, Los Angeles'
  },
  {
    date: '2016-05-08',
    name: 'Tom',
    address: 'No. 189, Grove St, Los Angeles'
  },
  {
    date: '2016-05-09',
    name: 'Tom',
    address: 'No. 189, Grove St, Los Angeles'
  },
  {
    date: '2016-05-10',
    name: 'Tom',
    address: 'No. 189, Grove St, Los Angeles'
  }
]
const columns = [
  {
    prop: 'date',
    label: '日期'
  },
  {
    prop: 'name',
    label: '姓名'
  },
  {
    prop: 'address',
    label: '地址'
  },
  {
    prop: 'Operations',
    label: '操作',
    width: 120,
    fixed: 'right',
    defaultSlot: (_props) => (
      <>
        <el-button
          link
          type="primary"
          size="small"
          onClick={(e) => {
            e.stopPropagation()
            handleClick(_props.row, 'detail')
          }}
        >
          detail
        </el-button>
        <el-button
          link
          type="primary"
          size="small"
          onClick={(e) => {
            e.stopPropagation()
            handleClick(_props.row, 'edit')
          }}
        >
          Edit
        </el-button>
      </>
    )
  }
] as TableColumnType[]
const singleSelection = ref<any[]>()
const handleClick = (row, type: string) => {
  console.log('🚀 ~ handleClick ~ row:', row)
}
const handleRowClick = (...args) => {
  console.log('🚀 ~ handleRowClick ~ args:', args)
  singleSelection.value = args
}
// --------------------多选--------------------
import type { TableColumnCtx, TableInstance } from 'element-plus'

interface User {
  id?: number | string
  date: string
  name: string
  address: string
  tag?: string
  children?: User[]
}

const multipleTableRef = ref<TableInstance>()
const multipleSelection = ref<User[]>([])

const toggleSelection = (rows?: User[], ignoreSelectable?: boolean) => {
  if (rows) {
    rows.forEach((row) => {
      multipleTableRef.value!.toggleRowSelection(row, undefined, ignoreSelectable)
    })
  } else {
    multipleTableRef.value!.clearSelection()
  }
}
const handleSelectionChange = (val: User[]) => {
  multipleSelection.value = val
}
const columns1 = [
  {
    type: 'selection',
    width: 55,
    selectable: (row: User) => ![1, 2].includes(row.id! as number)
  },
  {
    prop: 'date',
    sortable: true,
    label: '日期'
  },
  {
    prop: 'name',
    label: '姓名'
  },
  {
    prop: 'address',
    label: '地址'
  },
  {
    prop: 'Operations',
    label: '操作',
    width: 120,
    fixed: 'right',
    defaultSlot: (_props) => (
      <>
        <el-button
          link
          type="primary"
          size="small"
          onClick={(e) => {
            e.stopPropagation()
            handleClick(_props.row, 'detail')
          }}
        >
          detail
        </el-button>
        <el-button
          link
          type="primary"
          size="small"
          onClick={(e) => {
            e.stopPropagation()
            handleClick(_props.row, 'edit')
          }}
        >
          Edit
        </el-button>
      </>
    )
  }
]
const tableData1: User[] = [
  {
    id: 1,
    date: '2016-05-03',
    name: 'Tom',
    address: 'No. 189, Grove St, Los Angeles'
  },
  {
    id: 2,
    date: '2016-05-02',
    name: 'Tom',
    address: 'No. 189, Grove St, Los Angeles'
  },
  {
    id: 3,
    date: '2016-05-04',
    name: 'Tom',
    address: 'No. 189, Grove St, Los Angeles'
  },
  {
    id: 4,
    date: '2016-05-01',
    name: 'Tom',
    address: 'No. 189, Grove St, Los Angeles'
  },
  {
    id: 5,
    date: '2016-05-08',
    name: 'Tom',
    address: 'No. 189, Grove St, Los Angeles'
  },
  {
    id: 6,
    date: '2016-05-06',
    name: 'Tom',
    address: 'No. 189, Grove St, Los Angeles'
  },
  {
    id: 7,
    date: '2016-05-07',
    name: 'Tom',
    address: 'No. 189, Grove St, Los Angeles'
  }
]

// ----------------筛选----------------
const columnsFilter = [
  {
    prop: 'date',
    label: '日期',
    columnKey: 'date',
    filters: [
      { text: '2016-05-01', value: '2016-05-01' },
      { text: '2016-05-02', value: '2016-05-02' },
      { text: '2016-05-03', value: '2016-05-03' },
      { text: '2016-05-04', value: '2016-05-04' }
    ],
    filterMethod: (value: string, row: User, column: TableColumnCtx<User>) => {
      const property = column['property']
      return row[property] === value
    }
  },
  {
    prop: 'name',
    label: '姓名'
  },
  {
    prop: 'address',
    label: '地址'
  },
  {
    prop: 'tag',
    label: 'Tag',
    filters: [
      { text: 'Home', value: 'Home' },
      { text: 'Office', value: 'Office' }
    ],
    filterMethod: (value: string, row: User) => row.tag === value
  }
]
const tableDataFilter: User[] = [
  {
    date: '2016-05-03',
    name: 'Tom',
    address: 'No. 189, Grove St, Los Angeles',
    tag: 'Home'
  },
  {
    date: '2016-05-02',
    name: 'Tom',
    address: 'No. 189, Grove St, Los Angeles',
    tag: 'Office'
  },
  {
    date: '2016-05-04',
    name: 'Tom',
    address: 'No. 189, Grove St, Los Angeles',
    tag: 'Home'
  },
  {
    date: '2016-05-01',
    name: 'Tom',
    address: 'No. 189, Grove St, Los Angeles',
    tag: 'Office'
  }
]
const filterTableRef = ref<TableInstance>()
const resetDateFilter = () => {
  filterTableRef.value!.clearFilter(['date'])
}
const clearFilter = () => {
  filterTableRef.value!.clearFilter()
}
// -------------树型表格----------------
const tableDataTree: User[] = [
  {
    id: 1,
    date: '2016-05-02',
    name: 'wangxiaohu',
    address: 'No. 189, Grove St, Los Angeles'
  },
  {
    id: 2,
    date: '2016-05-04',
    name: 'wangxiaohu',
    address: 'No. 189, Grove St, Los Angeles'
  },
  {
    id: 3,
    date: '2016-05-01',
    name: 'wangxiaohu',
    address: 'No. 189, Grove St, Los Angeles',
    children: [
      {
        id: 31,
        date: '2016-05-01',
        name: 'wangxiaohu',
        address: 'No. 189, Grove St, Los Angeles'
      },
      {
        id: 32,
        date: '2016-05-01',
        name: 'wangxiaohu',
        address: 'No. 189, Grove St, Los Angeles'
      }
    ]
  },
  {
    id: 4,
    date: '2016-05-03',
    name: 'wangxiaohu',
    address: 'No. 189, Grove St, Los Angeles'
  }
]
const columnsTree = [
  {
    prop: 'date',
    label: '日期'
  },
  {
    prop: 'name',
    label: '姓名'
  },
  {
    prop: 'address',
    label: '地址'
  }
]

// ------------------表尾合计行-----------------
interface Product {
  id: string
  name: string
  amount1: string
  amount2: string
  amount3: number
}
const sumColumns = [
  {
    prop: 'id',
    label: 'id'
  },
  {
    prop: 'name',
    label: '姓名'
  },
  {
    prop: 'amount1',
    label: 'amount1'
  },
  {
    prop: 'amount2',
    label: 'amount2'
  },
  {
    prop: 'amount3',
    label: 'amount3'
  }
]
const sumTableData: Product[] = [
  {
    id: '12987122',
    name: 'Tom',
    amount1: '234',
    amount2: '3.2',
    amount3: 10
  },
  {
    id: '12987123',
    name: 'Tom',
    amount1: '165',
    amount2: '4.43',
    amount3: 12
  },
  {
    id: '12987124',
    name: 'Tom',
    amount1: '324',
    amount2: '1.9',
    amount3: 9
  },
  {
    id: '12987125',
    name: 'Tom',
    amount1: '621',
    amount2: '2.2',
    amount3: 17
  },
  {
    id: '12987126',
    name: 'Tom',
    amount1: '539',
    amount2: '4.1',
    amount3: 15
  }
]
interface SummaryMethodProps<T = Product> {
  columns: TableColumnType[]
  data: T[]
}

const getSummaries = (param: SummaryMethodProps) => {
  const { columns, data } = param
  const sums: (string | VNode)[] = []
  columns.forEach((column, index) => {
    if (index === 0) {
      sums[index] = h('div', { style: { textDecoration: 'underline' } }, ['Total Cost'])
      return
    }
    const values = data.map((item) => Number(item[column.property]))
    if (!values.every((value) => Number.isNaN(value))) {
      sums[index] = `$ ${values.reduce((prev, curr) => {
        const value = Number(curr)
        if (!Number.isNaN(value)) {
          return prev + curr
        } else {
          return prev
        }
      }, 0)}`
    } else {
      sums[index] = 'N/A'
    }
  })

  return sums
}

// -------------合并行或列-----------------
interface SpanMethodProps {
  row: User
  column: TableColumnCtx<User>
  rowIndex: number
  columnIndex: number
}

const arraySpanMethod = ({ row, column, rowIndex, columnIndex }: SpanMethodProps) => {
  if (rowIndex % 2 === 0) {
    if (columnIndex === 0) {
      return [1, 2]
    } else if (columnIndex === 1) {
      return [0, 0]
    }
  }
}
const objectSpanMethod = ({ row, column, rowIndex, columnIndex }: SpanMethodProps) => {
  if (columnIndex === 0) {
    if (rowIndex % 2 === 0) {
      return {
        rowspan: 2,
        colspan: 1
      }
    } else {
      return {
        rowspan: 0,
        colspan: 0
      }
    }
  }
}

const sumTableData1: Product[] = [
  {
    id: '12987122',
    name: 'Tom',
    amount1: '234',
    amount2: '3.2',
    amount3: 10
  },
  {
    id: '12987123',
    name: 'Tom',
    amount1: '165',
    amount2: '4.43',
    amount3: 12
  },
  {
    id: '12987124',
    name: 'Tom',
    amount1: '324',
    amount2: '1.9',
    amount3: 9
  },
  {
    id: '12987125',
    name: 'Tom',
    amount1: '621',
    amount2: '2.2',
    amount3: 17
  },
  {
    id: '12987126',
    name: 'Tom',
    amount1: '539',
    amount2: '4.1',
    amount3: 15
  }
]
</script>

<style scoped></style>
相关推荐
前端Hardy2 小时前
HTML&CSS&JS:打造丝滑的3D彩纸飘落特效
前端·javascript·css
布列瑟农的星空2 小时前
rsbuild mock 插件开发指南
前端
用泥种荷花3 小时前
【LangChain.js学习】 文档加载(Loader)与文本分割全解析
前端
cxxcode3 小时前
Vite 热更新(HMR)原理详解
前端
HelloReader3 小时前
Tauri 架构从“WebView + Rust”到完整工具链与生态
前端
Bigger4 小时前
告别版本焦虑:如何为 Hugo 项目定制专属构建环境
前端·架构·go
代码匠心5 小时前
AI 自动编程:一句话设计高颜值博客
前端·ai·ai编程·claude
_AaronWong7 小时前
Electron 实现仿豆包划词取词功能:从 AI 生成到落地踩坑记
前端·javascript·vue.js
cxxcode7 小时前
I/O 多路复用:从浏览器到 Linux 内核
前端