[issues/1196]Table 列头拖动时合计行错位; 修改合计行使用TableSummary方式渲染;支持slot自定义合计行展示

/issues/1196
1、Table 列头拖动时合计行错位;
2、修改合计行使用TableSummary方式渲染;
3、支持slot自定义合计行展示;
4、列添加customSummaryRender自定义渲染函数
pull/1201/head
Juliefuling 2024-04-18 15:30:31 +08:00
parent ec1649629d
commit ca1491a5c4
7 changed files with 224 additions and 4 deletions

View File

@ -30,6 +30,8 @@ const demoList = (() => {
avatar: Random.image('400x400', Random.color(), Random.color(), Random.first()),
imgArr: getRandomPics(Math.ceil(Math.random() * 3) + 1),
imgs: getRandomPics(Math.ceil(Math.random() * 3) + 1),
age: Math.ceil(Math.random() * 3) + 1,
score: Math.ceil(Math.random() * 80) + 1,
date: `@date('yyyy-MM-dd')`,
time: `@time('HH:mm')`,
'no|100000-10000000': 100000,

View File

@ -41,6 +41,11 @@
</template>
<!-- update-begin--author:liaozhiyang---date:22030717---forissues-179antd3 一些警告以及报错(针对表格) -->
</template>
<template v-if="showSummaryRef" #summary="data">
<slot name="summary" v-bind="data || {}">
<TableSummary v-bind="getSummaryProps" />
</slot>
</template>
</Table>
</a-form-item-rest>
</div>
@ -55,6 +60,7 @@
import CustomSelectHeader from './components/CustomSelectHeader.vue'
import expandIcon from './components/ExpandIcon';
import HeaderCell from './components/HeaderCell.vue';
import TableSummary from './components/TableSummary';
import { InnerHandlers } from './types/table';
import { usePagination } from './hooks/usePagination';
import { useColumns } from './hooks/useColumns';
@ -67,12 +73,12 @@
import { useTableHeader } from './hooks/useTableHeader';
import { useTableExpand } from './hooks/useTableExpand';
import { createTableContext } from './hooks/useTableContext';
import { useTableFooter } from './hooks/useTableFooter';
// import { useTableFooter } from './hooks/useTableFooter';
import { useTableForm } from './hooks/useTableForm';
import { useDesign } from '/@/hooks/web/useDesign';
import { useCustomSelection } from "./hooks/useCustomSelection";
import { omit } from 'lodash-es';
import { omit, pick } from 'lodash-es';
import { basicProps } from './props';
import { isFunction } from '/@/utils/is';
import { warn } from '/@/utils/log';
@ -82,6 +88,7 @@
Table,
BasicForm,
HeaderCell,
TableSummary,
CustomSelectHeader,
},
props: basicProps,
@ -227,7 +234,20 @@
const { getHeaderProps } = useTableHeader(getProps, slots, handlers);
const { getFooterProps } = useTableFooter(getProps, slots, getScrollRef, tableElRef, getDataSourceRef);
const getSummaryProps = computed(() => {
return pick(unref(getProps), ['summaryFunc', 'summaryData', 'hasExpandedRow', 'rowKey']);
});
const getIsEmptyData = computed(() => {
return (unref(getDataSourceRef) || []).length === 0;
});
const showSummaryRef = computed(() => {
const summaryProps = unref(getSummaryProps);
return (summaryProps.summaryFunc || summaryProps.summaryData) && !unref(getIsEmptyData);
});
// const { getFooterProps } = useTableFooter(getProps, slots, getScrollRef, tableElRef, getDataSourceRef);
const { getFormProps, replaceFormSlotKey, getFormSlotKeys, handleSearchInfoChange } = useTableForm(getProps, slots, fetch, getLoading);
@ -249,7 +269,7 @@
columns: toRaw(unref(getViewColumns)),
pagination: toRaw(unref(getPaginationInfo)),
dataSource,
footer: unref(getFooterProps),
// footer: unref(getFooterProps),
...unref(getExpandOption),
// QQYUN-5837 expandIconColumnIndex
expandIconColumnIndex: getExpandIconColumnIndex.value,
@ -407,6 +427,9 @@
isCustomSelection,
// update-end--author:sunjianlei---date:220230630---forQQYUN-5571
slotNamesGroup,
getSummaryProps,
getIsEmptyData,
showSummaryRef,
};
},
});

View File

@ -0,0 +1,155 @@
import type { PropType, VNode } from 'vue';
import { defineComponent, unref, computed, isVNode } from 'vue';
import { cloneDeep, pick } from 'lodash-es';
import { isFunction } from '/@/utils/is';
import type { BasicColumn } from '../types/table';
import { INDEX_COLUMN_FLAG } from '../const';
import { propTypes } from '/@/utils/propTypes';
import { useTableContext } from '../hooks/useTableContext';
import { TableSummary, TableSummaryRow, TableSummaryCell } from 'ant-design-vue';
const SUMMARY_ROW_KEY = '_row';
const SUMMARY_INDEX_KEY = '_index';
export default defineComponent({
name: 'BasicTableSummary',
components: { TableSummary, TableSummaryRow, TableSummaryCell },
props: {
summaryFunc: {
type: Function as PropType<Fn>,
},
summaryData: {
type: Array as PropType<Recordable[]>,
},
rowKey: propTypes.string.def('key'),
// 是否有展开列
hasExpandedRow: propTypes.bool,
},
setup(props) {
const table = useTableContext();
const getDataSource = computed((): Recordable[] => {
const { summaryFunc, summaryData } = props;
if (summaryData?.length) {
summaryData.forEach((item, i) => (item[props.rowKey] = `${i}`));
return summaryData;
}
if (!isFunction(summaryFunc)) {
return [];
}
let dataSource = cloneDeep(unref(table.getDataSource()));
dataSource = summaryFunc(dataSource);
dataSource.forEach((item, i) => {
item[props.rowKey] = `${i}`;
});
return dataSource;
});
const getColumns = computed(() => {
const dataSource = unref(getDataSource);
let columns: BasicColumn[] = cloneDeep(table.getColumns({ sort: true }));
columns = columns.filter((item) => !item.defaultHidden);
const index = columns.findIndex((item) => item.flag === INDEX_COLUMN_FLAG);
const hasRowSummary = dataSource.some((item) => Reflect.has(item, SUMMARY_ROW_KEY));
const hasIndexSummary = dataSource.some((item) => Reflect.has(item, SUMMARY_INDEX_KEY));
// 是否有序号列
let hasIndexCol = false;
// 是否有选择列
let hasSelection = table.getRowSelection() && hasRowSummary;
if (index !== -1) {
if (hasIndexSummary) {
hasIndexCol = true;
columns[index].customSummaryRender = ({ record }) => record[SUMMARY_INDEX_KEY];
columns[index].ellipsis = false;
} else {
Reflect.deleteProperty(columns[index], 'customSummaryRender');
}
}
if (hasSelection) {
const isFixed = columns.some((col) => col.fixed === 'left' || col.fixed === true);
columns.unshift({
width: 60,
title: 'selection',
key: 'selectionKey',
align: 'center',
...(isFixed ? { fixed: 'left' } : {}),
customSummaryRender: ({ record }) => hasIndexCol ? '' : record[SUMMARY_ROW_KEY],
});
}
if (props.hasExpandedRow) {
const isFixed = columns.some((col) => col.fixed === 'left');
columns.unshift({
width: 50,
title: 'expandedRow',
key: 'expandedRowKey',
align: 'center',
...(isFixed ? { fixed: 'left' } : {}),
customSummaryRender: () => '',
});
}
return columns;
});
function isRenderCell(data: any) {
return data && typeof data === 'object' && !Array.isArray(data) && !isVNode(data);
}
const getValues = (row: Recordable, col: BasicColumn, index: number) => {
const value = row[col.dataIndex as string];
let childNode: VNode | JSX.Element | string | number | undefined | null;
childNode = value;
if (col.customSummaryRender) {
const renderData = col.customSummaryRender({
text: value,
value,
record: row,
index,
column: cloneDeep(col)
})
if (isRenderCell(renderData)) {
childNode = renderData.children
} else {
childNode = renderData
}
if (typeof childNode === 'object' && !Array.isArray(childNode) && !isVNode(childNode)) {
childNode = null;
}
if (Array.isArray(childNode) && childNode.length === 1) {
childNode = childNode[0];
}
return childNode;
}
return childNode;
}
const getCellProps = (col: BasicColumn) => {
const cellProps = pick(col, ['colSpan', 'rowSpan', 'align']);
return {
...cellProps
}
}
return () => {
return (
<TableSummary fixed>
{(unref(getDataSource) || []).map(row => {
return <TableSummaryRow key={row[props.rowKey]}>
{unref(getColumns).map((col, index) => {
return <TableSummaryCell
{...getCellProps(col)}
index={index}
key={`${row[props.rowKey]}_${col.dataIndex}_${index}`}
>
{getValues(row, col, index)}
</TableSummaryCell>
})}
</TableSummaryRow>
})}
</TableSummary>
)
}
},
});

View File

@ -131,6 +131,8 @@ export interface ColumnProps<T> {
*/
customRender?: CustomRenderFunction<T> | VNodeChild | JSX.Element;
customSummaryRender?: CustomRenderFunction<T> | VNodeChild | JSX.Element;
/**
* Sort function for local sort, see Array.sort's compareFunction. If you need sort buttons only, set to true
* @type boolean | Function

View File

@ -453,6 +453,14 @@ export interface BasicColumn extends ColumnProps<Recordable> {
ifShow?: boolean | ((column: BasicColumn) => boolean);
//compType-用于记录类型
compType?: string;
customSummaryRender?: (opt: {
value: any;
text: any;
record: Recordable;
index: number;
renderIndex?: number;
column: BasicColumn;
}) => any | VNodeChild | JSX.Element;
}
export type ColumnChangeParam = {

View File

@ -18,16 +18,28 @@
prev += next.no;
return prev;
}, 0);
const totalAge = tableData.reduce((prev, next) => {
prev += next.age;
return prev;
}, 0);
const totalScore = tableData.reduce((prev, next) => {
prev += next.score;
return prev;
}, 0);
return [
{
_row: '合计',
_index: '平均值',
no: totalNo,
age: Math.round(totalAge / tableData.length),
score: Math.round(totalScore / tableData.length)
},
{
_row: '合计',
_index: '平均值',
no: totalNo,
age: Math.round(totalAge / tableData.length),
score: Math.round(totalScore / tableData.length)
},
];
}

View File

@ -8,6 +8,7 @@ export function getBasicColumns(): BasicColumn[] {
dataIndex: 'id',
fixed: 'left',
width: 200,
resizable: true
},
{
title: '姓名',
@ -18,6 +19,23 @@ export function getBasicColumns(): BasicColumn[] {
{ text: 'Female', value: 'female' },
],
},
{
title: '年龄',
dataIndex: 'age',
width: 100,
resizable: true,
customSummaryRender: ({text}) => {
return (
<span style="color: red;">{text}</span>
)
}
},
{
title: '得分',
dataIndex: 'score',
width: 100,
resizable: true
},
{
title: '地址',
dataIndex: 'address',