在开发后台管理系统中,经常会碰到合计行的需求,element有直接的属性可以使用,antdvue的需要手动去生成
如图为实现合计后的效果
1。首先给table配置column的时候,要明确哪些字段需要使用合计,如上图,只有总板数需要使用合计,那给总板数的配置加上标识符 即可,我这里为了方便辨识,直接增加了 summary:true
const columns = ref([
...,
{ title: '总板数', resizable: true, dataIndex: 'totalBoard', width: 150, align: 'center', ellipsis: true, summary: true }
])
2.搭好基础结构,直接上代码
2.1当需要合计时才去遍历表格数据使用reduce计算总合计数量
<a-table :columns='columns' :dataSource='tableData'>
<template #summary>
<a-table-summary fixed>
<a-table-summary-row>
<a-table-summary-cell :index="0"> 合计</a-table-summary-cell>
<a-table-summary-cell v-for="(item, index) in columns" :key="index" :index="1">
// 当列配置了summary为true才合计
<template v-if="item?.summary">
<a-typography-text>
// 总合计计算公式
{{ dataSource.reduce((prev: number, next: : any }) => {
return prev + next.totalBoard
}, 0)}}
</a-typography-text>
</template>
</a-table-summary-cell>
</a-table-summary-row>
</a-table-summary>
</template>
<a-table>
2.2代码优化
当字段为动态时,并且模板里写太多ts语法看起来太臃肿,所以抽成计算属性更好维护
// 模板
<template #summary>
<a-table-summary fixed>
<a-table-summary-row>
<a-table-summary-cell :index="0"> 合计</a-table-summary-cell>
<a-table-summary-cell v-for="(item, index) in contentTableParam.columns" :key="index" :index="1">
<template v-if="item?.summary">
<a-typography-text>
{{ combinedNums(item.dataIndex) }}
</a-typography-text>
</template>
</a-table-summary-cell>
</a-table-summary-row>
</a-table-summary>
</template>
/**
* @returns 计算合计行
*/
const combinedNums = computed(() => (field: string) => {
return contentTableParam.dataSource.reduce((prev: number, next: { [x: string]: any }) => {
return prev + next[field]
}, 0)
})
3.组件抽取部分
首先组件封装思路:因为业务的表格涉及到部分表格开启了选择框,部分未开启,选择框会坐固定,所以要分两种情况来判断
3.1组件需要接收的参数ts类型
import type { TablePaginationConfig } from 'ant-design-vue'
import type { Rule } from 'ant-design-vue/es/form'
// 列的类型
export type ColumnType = {
title: string // 标题
dataIndex?: string // 列数据在数据项中对应的路径
key?: string // Vue 需要的 key,如果已经设置了唯一的 dataIndex,可以忽略这个属性
resizable?: boolean // 是否可拖动调整宽度,此时 width 必须是 number 类型
width: number // 列宽度
align?: string // 对齐方式
ellipsis?: boolean // 超过宽度将自动省略
sorter?: boolean // 开启排序
sortDirections?: Array<string> // 支持的排序方式,取值为 'ascend' 'descend'
summary?: boolean // 是否开启列合计
fixed?: 'left' | 'right' // 对其方式
isSummaryAll?:boolean // 是否开启总合计
customSummary?:boolean // 是否需要自定义列
}
// 表格配置的类型
export type interContentTable<T = Record<string, any>> = {
name?: string // 使用Table排序必填
isOper: boolean // table扩展项
rowSelection?: boolean // 是否开启选择框
rowSelectionType?: 'radio' | 'checkbox' // 是否开启选择框
tableConfig?: boolean // table排序
loading?: boolean
rowKey: string // key
isCalcHeight?: boolean // table高度自适应
selectedRowKeys?: Array<string | number> // 选中key
pagination?: TablePaginationConfig
columns: ColumnType[]
dataSource: T[]
}
3.2组件template和ts代码
<template>
<!-- 如果开启了选择框,则遍历列 -->
<template v-if="origin.rowSelection">
<a-table-summary fixed>
<a-table-summary-row>
<a-table-summary-cell :index="0"> 合计</a-table-summary-cell>
<a-table-summary-cell v-for="(item, index) in origin.columns" :key="item.dataIndex" :index="index + 1">
<template v-if="item?.summary">
<a-typography-text>
{{ combinedNums(item.dataIndex as string).toFixed(item?.summaryNums ?? summarytoFixed) }}
</a-typography-text>
</template>
<template v-if="item?.customSummary">
<!-- 是否开启自定义插槽 -->
<slot name="custom" :column="item"></slot>
</template>
</a-table-summary-cell>
</a-table-summary-row>
<template v-if="origin.isSummaryAll">
<a-table-summary-row>
<a-table-summary-cell :index="0"> 总合计</a-table-summary-cell>
<a-table-summary-cell v-for="(item, index) in origin.columns" :key="item.dataIndex" :index="index + 1">
<template v-if="item?.isSummaryAll">
<a-typography-text>
{{ getAllSummaryData(item.dataIndex as string).toFixed(item?.summaryNums! ?? summarytoFixed) }}
</a-typography-text>
</template>
</a-table-summary-cell>
</a-table-summary-row>
</template>
</a-table-summary>
</template>
<!-- 如果未开启选择框,循环长度 -->
<template v-if="!origin.rowSelection">
<a-table-summary fixed>
<a-table-summary-row>
<a-table-summary-cell :index="0"> 合计</a-table-summary-cell>
<a-table-summary-cell v-for="i in origin.columns.length" :key="i" :index="i">
<template v-if="origin.columns[i]?.summary">
<a-typography-text>
{{ combinedNums(origin.columns[i].dataIndex as string).toFixed(origin.columns[i]?.summaryNums ?? summarytoFixed) }}
</a-typography-text>
</template>
<template v-if="origin.columns[i]?.customSummary">
<!-- 是否开启自定义插槽 -->
<slot name="custom" :column="origin.columns[i]"></slot>
</template>
</a-table-summary-cell>
</a-table-summary-row>
<template v-if="origin.isSummaryAll">
<a-table-summary-row>
<a-table-summary-cell :index="0"> 总合计</a-table-summary-cell>
<a-table-summary-cell v-for="i in origin.columns.length" :key="i" :index="i">
<template v-if="origin.columns[i]?.isSummaryAll">
<a-typography-text>
{{ getAllSummaryData(origin.columns[i].dataIndex as string).toFixed(origin.columns[i]?.summaryNums! ?? summarytoFixed) }}
</a-typography-text>
</template>
</a-table-summary-cell>
</a-table-summary-row>
</template>
</a-table-summary>
</template>
</template>
<script setup lang="ts">
import type { interContentTable } from '@/type/interface/content'
import { withDefaults, computed } from 'vue'
interface IProps {
origin: interContentTable
summarytoFixed?: string | number
}
const _props = withDefaults(defineProps<IProps>(), {
origin: () => ({ isOper: false, loading: false, rowKey: '', columns: [], dataSource: [], summaryColumns: [] }),
summarytoFixed: 3, // 默认toFixed小数位
})
/**
* @returns 计算合计行
*/
const combinedNums = computed(() => (field: string) => {
return _props.origin.dataSource.reduce((prev: number, next: { [x: string]: number | string }) => {
return prev + Number(next[field])
}, 0)
})
/**
* @method 计算总合计
*/
const getAllSummaryData = computed(() => (dataIndex: string) => {
return Number(_props.origin.summaryColumns?.[dataIndex] || 0)
})
</script>
3.3 使用组件
// 使用封装好的table
<base-table :tableConfig='tableConfig'>
<template #summary>
// 使用合计行组件
<TemplateSummary :origin="contentTableParam" />
</template>
</base-table>
import TemplateSummary from '@/components/template-summary/index.vue'
const contentTableParam = reactive({
isOper: true,
emptyText: true, // 是否自定义空数据展示
rowSelection: true, // 选择框
rowKey: 'id',
columnWidth: 80,
selectedRowKeys: [] as string[],
name: 'GODOWNPLAN_LIST_MAIN',
tableConfig: true, // 表单配置
isCalcHeight: true, // 是否自动计算table高度
loading: false,
isSlotOption: ['summary'],
pagination: {
pageSize: 20,
total: 0,
current: 1,
},
scroll: {
scrollToFirstRowOnChange: false,
y: 300,
},
columns: [
{ title: '单号', dataIndex: 'planCode', resizable: true, width: 250, align: 'center', ellipsis: true },
{ title: '状态', resizable: true, key: 'statusName', dataIndex: 'statusName', width: 120, align: 'center', ellipsis: true },
{ title: '类型', resizable: true, dataIndex: 'typeName', width: 150, align: 'center', ellipsis: true },
{ title: '仓库', key: 'warehouse', dataIndex: 'warehouseCode', resizable: true, width: 200, align: 'center', ellipsis: true },
{ title: '货主', resizable: true, key: 'cargoOwner', dataIndex: 'cargoOwnerCode', width: 230, align: 'center', ellipsis: true },
{ title: '操作', key: 'operation', fixed: 'right', width: 150, align: 'center' },
] as ColumnType[],
dataSource: [],
})