refactor: table

pull/4639/head
tangjinzhou 2021-09-07 22:28:34 +08:00
parent 8d60808662
commit 10cb279dfc
15 changed files with 213 additions and 123 deletions

View File

@ -164,6 +164,14 @@ export { default as Steps, Step } from './steps';
export type { SwitchProps } from './switch'; export type { SwitchProps } from './switch';
export { default as Switch } from './switch'; export { default as Switch } from './switch';
export type {
TableProps,
TablePaginationConfig,
ColumnGroupType as TableColumnGroupType,
ColumnType as TableColumnType,
ColumnProps as TableColumnProps,
ColumnsType as TableColumnsType,
} from './table';
export { default as Table, TableColumn, TableColumnGroup } from './table'; export { default as Table, TableColumn, TableColumnGroup } from './table';
export type { TransferProps } from './transfer'; export type { TransferProps } from './transfer';

View File

@ -51,6 +51,7 @@ import classNames from '../_util/classNames';
import omit from '../_util/omit'; import omit from '../_util/omit';
import { initDefaultProps } from '../_util/props-util'; import { initDefaultProps } from '../_util/props-util';
import { ContextSlots, useProvideSlots } from './context'; import { ContextSlots, useProvideSlots } from './context';
import useColumns from './hooks/useColumns';
export type { ColumnsType, TablePaginationConfig }; export type { ColumnsType, TablePaginationConfig };
@ -107,7 +108,7 @@ export interface TableProps<RecordType = DefaultRecordType>
sortDirections?: SortOrder[]; sortDirections?: SortOrder[];
showSorterTooltip?: boolean | TooltipProps; showSorterTooltip?: boolean | TooltipProps;
} }
const tableProps = () => { export const tableProps = () => {
return { return {
prefixCls: { type: String as PropType<string>, default: undefined }, prefixCls: { type: String as PropType<string>, default: undefined },
columns: { type: Array as PropType<ColumnsType>, default: undefined }, columns: { type: Array as PropType<ColumnsType>, default: undefined },
@ -394,10 +395,13 @@ const InteralTable = defineComponent<
}); });
const mergedData = computed(() => getFilterData(sortedData.value, filterStates.value)); const mergedData = computed(() => getFilterData(sortedData.value, filterStates.value));
// ============================ Column ============================ // ============================ Column ============================
const [transformBasicColumns] = useColumns(toRef(props, 'contextSlots'));
const columnTitleProps = computed(() => ({ const columnTitleProps = computed(() => ({
...sorterTitleProps.value, ...sorterTitleProps.value,
})); }));
const [transformTitleColumns] = useTitleColumns(columnTitleProps, toRef(props, 'contextSlots')); const [transformTitleColumns] = useTitleColumns(columnTitleProps);
// ========================== Pagination ========================== // ========================== Pagination ==========================
const onPaginationChange = (current: number, pageSize: number) => { const onPaginationChange = (current: number, pageSize: number) => {
@ -505,7 +509,9 @@ const InteralTable = defineComponent<
const transformColumns = (innerColumns: ColumnsType<any>): ColumnsType<any> => { const transformColumns = (innerColumns: ColumnsType<any>): ColumnsType<any> => {
const res = transformTitleColumns( const res = transformTitleColumns(
transformSelectionColumns(transformFilterColumns(transformSorterColumns(innerColumns))), transformSelectionColumns(
transformFilterColumns(transformSorterColumns(transformBasicColumns(innerColumns))),
),
); );
return res; return res;
}; };

View File

@ -28,11 +28,14 @@ This example shows how to fetch and present data from a remote server, and how t
:loading="loading" :loading="loading"
@change="handleTableChange" @change="handleTableChange"
> >
<template #name="{ text }">{{ text.first }} {{ text.last }}</template> <template #bodyCell="{ column, text }">
<template v-if="column.dataIndex === 'name'">{{ text.first }} {{ text.last }}</template>
<template v-else>{{ text }}</template>
</template>
</a-table> </a-table>
</template> </template>
<script lang="ts"> <script lang="ts">
import { TableState, TableStateFilters } from 'ant-design-vue/es/table/interface'; import type { TableProps } from 'ant-design-vue';
import { usePagination } from 'vue-request'; import { usePagination } from 'vue-request';
import { computed, defineComponent } from 'vue'; import { computed, defineComponent } from 'vue';
const columns = [ const columns = [
@ -41,7 +44,6 @@ const columns = [
dataIndex: 'name', dataIndex: 'name',
sorter: true, sorter: true,
width: '20%', width: '20%',
slots: { customRender: 'name' },
}, },
{ {
title: 'Gender', title: 'Gender',
@ -58,7 +60,6 @@ const columns = [
}, },
]; ];
type Pagination = TableState['pagination'];
type APIParams = { type APIParams = {
results: number; results: number;
page?: number; page?: number;
@ -80,16 +81,19 @@ const queryData = (params: APIParams) => {
export default defineComponent({ export default defineComponent({
setup() { setup() {
const { data: dataSource, run, loading, current, pageSize } = usePagination<APIResult>( const {
queryData, data: dataSource,
{ run,
formatResult: res => res.results, loading,
pagination: { current,
currentKey: 'page', pageSize,
pageSizeKey: 'results', } = usePagination<APIResult>(queryData, {
}, formatResult: res => res.results,
pagination: {
currentKey: 'page',
pageSizeKey: 'results',
}, },
); });
const pagination = computed(() => ({ const pagination = computed(() => ({
total: 200, total: 200,
@ -97,9 +101,13 @@ export default defineComponent({
pageSize: pageSize.value, pageSize: pageSize.value,
})); }));
const handleTableChange = (pag: Pagination, filters: TableStateFilters, sorter: any) => { const handleTableChange: TableProps['onChange'] = (
pag: { pageSize: number; current: number },
filters: any,
sorter: any,
) => {
run({ run({
results: pag!.pageSize!, results: pag.pageSize!,
page: pag?.current, page: pag?.current,
sortField: sorter.field, sortField: sorter.field,
sortOrder: sorter.order, sortOrder: sorter.order,

View File

@ -17,8 +17,11 @@ Add border, title and footer for table.
<template> <template>
<a-table :columns="columns" :data-source="data" bordered> <a-table :columns="columns" :data-source="data" bordered>
<template #name="{ text }"> <template #bodyCell="{ column, text }">
<a>{{ text }}</a> <template v-if="column.dataIndex === 'name'">
<a>{{ text }}</a>
</template>
<template v-else>{{ text }}</template>
</template> </template>
<template #title>Header</template> <template #title>Header</template>
<template #footer>Footer</template> <template #footer>Footer</template>
@ -31,7 +34,6 @@ const columns = [
{ {
title: 'Name', title: 'Name',
dataIndex: 'name', dataIndex: 'name',
slots: { customRender: 'name' },
}, },
{ {
title: 'Cash Assets', title: 'Cash Assets',

View File

@ -20,19 +20,21 @@ Table cell supports `colSpan` and `rowSpan` that set in render return object. Wh
<template> <template>
<a-table :columns="columns" :data-source="data" bordered> <a-table :columns="columns" :data-source="data" bordered>
<template #name="{ text }"> <template #bodyCell="{ column, text }">
<a>{{ text }}</a> <template v-if="column.dataIndex === 'name'">
<a href="javascript:;">{{ text }}</a>
</template>
<template v-else>{{ text }}</template>
</template> </template>
</a-table> </a-table>
</template> </template>
<script lang="ts"> <script lang="ts">
import { defineComponent, h } from 'vue'; import { defineComponent } from 'vue';
import { ColumnProps } from 'ant-design-vue/es/table/interface'; import { TableColumnType } from 'ant-design-vue';
// In the fifth row, other columns are merged into first column // In the fifth row, other columns are merged into first column
// by setting it's colSpan to be 0 // by setting it's colSpan to be 0
const renderContent = ({ text, index }: any) => { const renderContent = ({ index }: any) => {
const obj = { const obj = {
children: text,
props: {} as any, props: {} as any,
}; };
if (index === 4) { if (index === 4) {
@ -86,16 +88,15 @@ const data = [
export default defineComponent({ export default defineComponent({
setup() { setup() {
const columns: ColumnProps[] = [ const columns: TableColumnType[] = [
{ {
title: 'Name', title: 'Name',
dataIndex: 'name', dataIndex: 'name',
customRender: ({ text, index }) => { customRender: ({ index }) => {
if (index < 4) { if (index < 4) {
return h('a', { href: 'javascript:;' }, text); return;
} }
return { return {
children: h('a', { href: 'javascript:;' }, text),
props: { props: {
colSpan: 5, colSpan: 5,
}, },
@ -111,9 +112,8 @@ export default defineComponent({
title: 'Home phone', title: 'Home phone',
colSpan: 2, colSpan: 2,
dataIndex: 'tel', dataIndex: 'tel',
customRender: ({ text, index }) => { customRender: ({ index }) => {
const obj = { const obj = {
children: text,
props: {} as any, props: {} as any,
}; };
if (index === 2) { if (index === 2) {

View File

@ -8,17 +8,25 @@ title:
## zh-CN ## zh-CN
通过 `filterDropdown` 定义自定义的列筛选功能并实现一个搜索列的示例 通过 `customFilterDropdown` 定义自定义的列筛选功能并实现一个搜索列的示例
## en-US ## en-US
Implement a customized column search example via `filterDropdown`. Implement a customized column search example via `customFilterDropdown`.
</docs> </docs>
<template> <template>
<a-table :data-source="data" :columns="columns"> <a-table :data-source="data" :columns="columns">
<template #filterDropdown="{ setSelectedKeys, selectedKeys, confirm, clearFilters, column }"> <template #headerCell="{ column }">
<template v-if="column.key === 'name'">
<span style="color: #1890ff">Name</span>
</template>
<template v-else>{{ column.title }}</template>
</template>
<template
#customFilterDropdown="{ setSelectedKeys, selectedKeys, confirm, clearFilters, column }"
>
<div style="padding: 8px"> <div style="padding: 8px">
<a-input <a-input
ref="searchInput" ref="searchInput"
@ -42,11 +50,12 @@ Implement a customized column search example via `filterDropdown`.
</a-button> </a-button>
</div> </div>
</template> </template>
<template #filterIcon="filtered"> <template #customFilterIcon="{ filtered }">
<search-outlined :style="{ color: filtered ? '#108ee9' : undefined }" /> <search-outlined :style="{ color: filtered ? '#108ee9' : undefined }" />
</template> </template>
<template #customRender="{ text, column }"> <template #bodyCell="{ text, column }">
<span v-if="searchText && searchedColumn === column.dataIndex"> <span v-if="searchText && searchedColumn === column.dataIndex">
{{ searchText }}{{ searchedColumn }} {{ column.dataIndex }}
<template <template
v-for="(fragment, i) in text v-for="(fragment, i) in text
.toString() .toString()
@ -116,17 +125,12 @@ export default defineComponent({
title: 'Name', title: 'Name',
dataIndex: 'name', dataIndex: 'name',
key: 'name', key: 'name',
slots: { customFilterDropdown: true,
filterDropdown: 'filterDropdown',
filterIcon: 'filterIcon',
customRender: 'customRender',
},
onFilter: (value, record) => onFilter: (value, record) =>
record.name.toString().toLowerCase().includes(value.toLowerCase()), record.name.toString().toLowerCase().includes(value.toLowerCase()),
onFilterDropdownVisibleChange: visible => { onFilterDropdownVisibleChange: visible => {
if (visible) { if (visible) {
setTimeout(() => { setTimeout(() => {
console.log(searchInput.value);
searchInput.value.focus(); searchInput.value.focus();
}, 100); }, 100);
} }
@ -136,30 +140,12 @@ export default defineComponent({
title: 'Age', title: 'Age',
dataIndex: 'age', dataIndex: 'age',
key: 'age', key: 'age',
slots: {
filterDropdown: 'filterDropdown',
filterIcon: 'filterIcon',
customRender: 'customRender',
},
onFilter: (value, record) =>
record.age.toString().toLowerCase().includes(value.toLowerCase()),
onFilterDropdownVisibleChange: visible => {
if (visible) {
setTimeout(() => {
searchInput.value.focus();
}, 100);
}
},
}, },
{ {
title: 'Address', title: 'Address',
dataIndex: 'address', dataIndex: 'address',
key: 'address', key: 'address',
slots: { customFilterDropdown: true,
filterDropdown: 'filterDropdown',
filterIcon: 'filterIcon',
customRender: 'customRender',
},
onFilter: (value, record) => onFilter: (value, record) =>
record.address.toString().toLowerCase().includes(value.toLowerCase()), record.address.toString().toLowerCase().includes(value.toLowerCase()),
onFilterDropdownVisibleChange: visible => { onFilterDropdownVisibleChange: visible => {

View File

@ -0,0 +1,45 @@
import devWarning from '../../vc-util/devWarning';
import type { Ref } from 'vue';
import { ContextSlots } from '../context';
import type { TransformColumns, ColumnsType } from '../interface';
function fillSlots<RecordType>(columns: ColumnsType<RecordType>, contextSlots: Ref<ContextSlots>) {
const $slots = contextSlots.value;
return columns.map(column => {
const cloneColumn = { ...column };
const { slots = {} } = cloneColumn;
cloneColumn.__originColumn__ = column;
devWarning(
!('slots' in cloneColumn),
'Table',
'`column.slots` is deprecated. Please use `v-slot:headerCell` `v-slot:bodyCell` instead.',
);
Object.keys(slots).forEach(key => {
const name = slots[key];
if (cloneColumn[key] === undefined && $slots[name]) {
cloneColumn[key] = $slots[name];
}
});
if (contextSlots.value.headerCell && !column.slots?.title) {
cloneColumn.title = contextSlots.value.headerCell({
title: column.title,
column,
});
}
if ('children' in cloneColumn) {
cloneColumn.children = fillSlots(cloneColumn.children, contextSlots);
}
return cloneColumn;
});
}
export default function useColumns<RecordType>(
contextSlots: Ref<ContextSlots>,
): [TransformColumns<RecordType>] {
const filledColumns = (columns: ColumnsType<RecordType>) => fillSlots(columns, contextSlots);
return [filledColumns];
}

View File

@ -15,7 +15,7 @@ import type {
} from '../../interface'; } from '../../interface';
import FilterDropdownMenuWrapper from './FilterWrapper'; import FilterDropdownMenuWrapper from './FilterWrapper';
import type { FilterState } from '.'; import type { FilterState } from '.';
import { computed, defineComponent, onBeforeUnmount, ref } from 'vue'; import { computed, defineComponent, nextTick, onBeforeUnmount, ref, watch } from 'vue';
import classNames from '../../../_util/classNames'; import classNames from '../../../_util/classNames';
import useConfigInject from '../../../_util/hooks/useConfigInject'; import useConfigInject from '../../../_util/hooks/useConfigInject';
import { useInjectSlots } from '../../context'; import { useInjectSlots } from '../../context';
@ -131,6 +131,24 @@ export default defineComponent<FilterDropdownProps<any>>({
), ),
); );
const filterDropdownRef = computed(() => {
const { filterDropdown, slots = {}, customFilterDropdown } = props.column;
return (
filterDropdown ||
(slots.filterDropdown && contextSlots.value[slots.filterDropdown]) ||
(customFilterDropdown && contextSlots.value.customFilterDropdown)
);
});
const filterIconRef = computed(() => {
const { filterIcon, slots = {} } = props.column;
return (
filterIcon ||
(slots.filterIcon && contextSlots.value[slots.filterIcon]) ||
contextSlots.value.customFilterIcon
);
});
const triggerVisible = (newVisible: boolean) => { const triggerVisible = (newVisible: boolean) => {
visible.value = newVisible; visible.value = newVisible;
props.column.onFilterDropdownVisibleChange?.(newVisible); props.column.onFilterDropdownVisibleChange?.(newVisible);
@ -142,16 +160,22 @@ export default defineComponent<FilterDropdownProps<any>>({
: visible.value, : visible.value,
); );
const filteredKeys = ref([]); const propFilteredKeys = computed(() => props.filterState?.filteredKeys);
const mergedFilteredKeys = computed( const filteredKeys = ref([]);
() => props.filterState?.filteredKeys || filteredKeys.value || [],
);
const onSelectKeys = ({ selectedKeys }: { selectedKeys?: Key[] }) => { const onSelectKeys = ({ selectedKeys }: { selectedKeys?: Key[] }) => {
filteredKeys.value = selectedKeys; filteredKeys.value = selectedKeys;
}; };
watch(
propFilteredKeys,
() => {
onSelectKeys({ selectedKeys: propFilteredKeys.value || [] });
},
{ immediate: true },
);
const openKeys = ref([]); const openKeys = ref([]);
const openRef = ref(); const openRef = ref();
@ -190,7 +214,7 @@ export default defineComponent<FilterDropdownProps<any>>({
const onConfirm = () => { const onConfirm = () => {
triggerVisible(false); triggerVisible(false);
internalTriggerFilter(mergedFilteredKeys.value); internalTriggerFilter(filteredKeys.value);
}; };
const onReset = () => { const onReset = () => {
@ -203,14 +227,18 @@ export default defineComponent<FilterDropdownProps<any>>({
if (closeDropdown) { if (closeDropdown) {
triggerVisible(false); triggerVisible(false);
} }
internalTriggerFilter(mergedFilteredKeys.value); internalTriggerFilter(filteredKeys.value);
}; };
const onVisibleChange = (newVisible: boolean) => { const onVisibleChange = (newVisible: boolean) => {
if (newVisible && propFilteredKeys.value !== undefined) {
// Sync filteredKeys on appear in controlled mode (propFilteredKeys.value !== undefiend)
filteredKeys.value = propFilteredKeys.value || [];
}
triggerVisible(newVisible); triggerVisible(newVisible);
// Default will filter when closed // Default will filter when closed
if (!newVisible && !props.column.filterDropdown) { if (!newVisible && !filterDropdownRef.value) {
onConfirm(); onConfirm();
} }
}; };
@ -233,20 +261,21 @@ export default defineComponent<FilterDropdownProps<any>>({
let dropdownContent; let dropdownContent;
if (typeof column.filterDropdown === 'function') { if (typeof filterDropdownRef.value === 'function') {
dropdownContent = column.filterDropdown({ dropdownContent = filterDropdownRef.value({
prefixCls: `${dropdownPrefixCls}-custom`, prefixCls: `${dropdownPrefixCls}-custom`,
setSelectedKeys: (selectedKeys: Key[]) => onSelectKeys({ selectedKeys }), setSelectedKeys: (selectedKeys: Key[]) => onSelectKeys({ selectedKeys }),
selectedKeys: mergedFilteredKeys.value, selectedKeys: filteredKeys.value,
confirm: doFilter, confirm: doFilter,
clearFilters: onReset, clearFilters: onReset,
filters: column.filters, filters: column.filters,
visible: mergedVisible.value, visible: mergedVisible.value,
column: column.__originColumn__,
}); });
} else if (column.filterDropdown) { } else if (filterDropdownRef.value) {
dropdownContent = column.filterDropdown; dropdownContent = filterDropdownRef.value;
} else { } else {
const selectedKeys = mergedFilteredKeys.value as any; const selectedKeys = filteredKeys.value as any;
dropdownContent = ( dropdownContent = (
<> <>
<Menu <Menu
@ -265,7 +294,7 @@ export default defineComponent<FilterDropdownProps<any>>({
renderFilterItems({ renderFilterItems({
filters: column.filters || [], filters: column.filters || [],
prefixCls, prefixCls,
filteredKeys: mergedFilteredKeys.value, filteredKeys: filteredKeys.value,
filterMultiple, filterMultiple,
locale, locale,
}), }),
@ -295,19 +324,20 @@ export default defineComponent<FilterDropdownProps<any>>({
); );
let filterIcon; let filterIcon;
if (typeof column.filterIcon === 'function') { if (typeof filterIconRef.value === 'function') {
filterIcon = column.filterIcon({ filtered: filtered.value, column }); filterIcon = filterIconRef.value({
} else if (column.filterIcon) { filtered: filtered.value,
filterIcon = column.filterIcon; column: column.__originColumn__,
} else if (contextSlots.value.customFilterIcon) { });
filterIcon = contextSlots.value.customFilterIcon({ filtered: filtered.value, column }); } else if (filterIconRef.value) {
filterIcon = filterIconRef.value;
} else { } else {
filterIcon = <FilterFilled />; filterIcon = <FilterFilled />;
} }
return ( return (
<div class={`${prefixCls}-column`}> <div class={`${prefixCls}-column`}>
<span class={`${tablePrefixCls}-column-title`}>{slots.defalut?.()}</span> <span class={`${tablePrefixCls}-column-title`}>{slots.default?.()}</span>
<Dropdown <Dropdown
overlay={menu} overlay={menu}
trigger={['click']} trigger={['click']}

View File

@ -34,14 +34,13 @@ function collectFilterStates<RecordType>(
(columns || []).forEach((column, index) => { (columns || []).forEach((column, index) => {
const columnPos = getColumnPos(index, pos); const columnPos = getColumnPos(index, pos);
const hasFilterDropdown =
if ('children' in column) { column.filterDropdown || column?.slots?.filterDropdown || column.customFilterDropdown;
filterStates = [...filterStates, ...collectFilterStates(column.children, init, columnPos)]; if (column.filters || hasFilterDropdown || 'onFilter' in column) {
} else if (column.filters || 'filterDropdown' in column || 'onFilter' in column) {
if ('filteredValue' in column) { if ('filteredValue' in column) {
// Controlled // Controlled
let filteredValues = column.filteredValue; let filteredValues = column.filteredValue;
if (!('filterDropdown' in column)) { if (!hasFilterDropdown) {
filteredValues = filteredValues?.map(String) ?? filteredValues; filteredValues = filteredValues?.map(String) ?? filteredValues;
} }
filterStates.push({ filterStates.push({
@ -62,6 +61,9 @@ function collectFilterStates<RecordType>(
}); });
} }
} }
if ('children' in column) {
filterStates = [...filterStates, ...collectFilterStates(column.children, init, columnPos)];
}
}); });
return filterStates; return filterStates;
@ -82,8 +84,9 @@ function injectFilter<RecordType>(
const { filterMultiple = true } = column as ColumnType<RecordType>; const { filterMultiple = true } = column as ColumnType<RecordType>;
let newColumn: ColumnsType<RecordType>[number] = column; let newColumn: ColumnsType<RecordType>[number] = column;
const hasFilterDropdown =
if (newColumn.filters || newColumn.filterDropdown) { column.filterDropdown || column?.slots?.filterDropdown || column.customFilterDropdown;
if (newColumn.filters || hasFilterDropdown) {
const columnKey = getColumnKey(newColumn, columnPos); const columnKey = getColumnKey(newColumn, columnPos);
const filterState = filterStates.find(({ key }) => columnKey === key); const filterState = filterStates.find(({ key }) => columnKey === key);
@ -143,9 +146,10 @@ function generateFilterInfo<RecordType>(filterStates: FilterState<RecordType>[])
const currentFilters: Record<string, FilterValue | null> = {}; const currentFilters: Record<string, FilterValue | null> = {};
filterStates.forEach(({ key, filteredKeys, column }) => { filterStates.forEach(({ key, filteredKeys, column }) => {
console.log(column); const hasFilterDropdown =
const { filters, filterDropdown } = column; column.filterDropdown || column?.slots?.filterDropdown || column.customFilterDropdown;
if (filterDropdown) { const { filters } = column;
if (hasFilterDropdown) {
currentFilters[key] = filteredKeys || null; currentFilters[key] = filteredKeys || null;
} else if (Array.isArray(filteredKeys)) { } else if (Array.isArray(filteredKeys)) {
const keys = flattenKeys(filters); const keys = flattenKeys(filters);

View File

@ -1,35 +1,18 @@
import devWarning from '../../vc-util/devWarning';
import type { Ref } from 'vue'; import type { Ref } from 'vue';
import { ContextSlots } from '../context';
import type { TransformColumns, ColumnTitleProps, ColumnsType } from '../interface'; import type { TransformColumns, ColumnTitleProps, ColumnsType } from '../interface';
import { renderColumnTitle } from '../util'; import { renderColumnTitle } from '../util';
function fillTitle<RecordType>( function fillTitle<RecordType>(
columns: ColumnsType<RecordType>, columns: ColumnsType<RecordType>,
columnTitleProps: ColumnTitleProps<RecordType>, columnTitleProps: ColumnTitleProps<RecordType>,
contextColumns: Ref<ContextSlots>,
) { ) {
const $slots = contextColumns.value;
return columns.map(column => { return columns.map(column => {
const cloneColumn = { ...column }; const cloneColumn = { ...column };
const { slots = {} } = cloneColumn;
devWarning(
!('slots' in cloneColumn),
'Table',
'`column.slots` is deprecated. Please use `v-slot:headerCell` `v-slot:bodyCell` instead.',
);
Object.keys(slots).forEach(key => {
const name = slots[key];
if (cloneColumn[key] === undefined && $slots[name]) {
cloneColumn[key] = $slots[name];
}
});
cloneColumn.title = renderColumnTitle(cloneColumn.title, columnTitleProps); cloneColumn.title = renderColumnTitle(cloneColumn.title, columnTitleProps);
if ('children' in cloneColumn) { if ('children' in cloneColumn) {
cloneColumn.children = fillTitle(cloneColumn.children, columnTitleProps, contextColumns); cloneColumn.children = fillTitle(cloneColumn.children, columnTitleProps);
} }
return cloneColumn; return cloneColumn;
@ -38,10 +21,9 @@ function fillTitle<RecordType>(
export default function useTitleColumns<RecordType>( export default function useTitleColumns<RecordType>(
columnTitleProps: Ref<ColumnTitleProps<RecordType>>, columnTitleProps: Ref<ColumnTitleProps<RecordType>>,
contextColumns: Ref<ContextSlots>,
): [TransformColumns<RecordType>] { ): [TransformColumns<RecordType>] {
const filledColumns = (columns: ColumnsType<RecordType>) => const filledColumns = (columns: ColumnsType<RecordType>) =>
fillTitle(columns, columnTitleProps.value, contextColumns); fillTitle(columns, columnTitleProps.value);
return [filledColumns]; return [filledColumns];
} }

View File

@ -1,4 +1,4 @@
import Table from './Table'; import Table, { tableProps } from './Table';
import type Column from './Column'; import type Column from './Column';
import type ColumnGroup from './ColumnGroup'; import type ColumnGroup from './ColumnGroup';
import type { TableProps, TablePaginationConfig } from './Table'; import type { TableProps, TablePaginationConfig } from './Table';
@ -18,6 +18,7 @@ Table.install = function (app: App) {
export const TableColumn = Table.Column; export const TableColumn = Table.Column;
export const TableColumnGroup = Table.ColumnGroup; export const TableColumnGroup = Table.ColumnGroup;
export { tableProps };
export default Table as typeof Table & export default Table as typeof Table &
Plugin & { Plugin & {

View File

@ -119,7 +119,7 @@ export interface ColumnGroupType<RecordType> extends Omit<ColumnType<RecordType>
children: ColumnsType<RecordType>; children: ColumnsType<RecordType>;
} }
export type ColumnsType<RecordType = unknown> = ( export type ColumnsType<RecordType = DefaultRecordType> = (
| ColumnGroupType<RecordType> | ColumnGroupType<RecordType>
| ColumnType<RecordType> | ColumnType<RecordType>
)[]; )[];

View File

@ -119,16 +119,19 @@ export default defineComponent<CellProps>({
const children = slots.default?.(); const children = slots.default?.();
if (validateValue(children) || cellType === 'header') { if (validateValue(children) || cellType === 'header') {
childNode = children; childNode = children;
if (cellType === 'header' && contextSlots.value.headerCell && !column.slots?.title) {
childNode = contextSlots.value.headerCell({ title: column.title, index, column });
}
} else { } else {
const value = getPathValue(record, dataIndex); const value = getPathValue(record, dataIndex);
// Customize render node // Customize render node
childNode = value; childNode = value;
if (customRender) { if (customRender) {
const renderData = customRender({ text: value, value, record, index, column }); const renderData = customRender({
text: value,
value,
record,
index,
column: column.__originColumn__,
});
if (isRenderCell(renderData)) { if (isRenderCell(renderData)) {
childNode = renderData.children; childNode = renderData.children;
@ -139,7 +142,13 @@ export default defineComponent<CellProps>({
} }
if (cellType === 'body' && contextSlots.value.bodyCell && !column.slots?.customRender) { if (cellType === 'body' && contextSlots.value.bodyCell && !column.slots?.customRender) {
childNode = contextSlots.value.bodyCell({ text: value, value, record, index, column }); childNode = contextSlots.value.bodyCell({
text: value,
value,
record,
index,
column: column.__originColumn__,
});
} }
} }

View File

@ -67,6 +67,8 @@ interface ColumnSharedType<RecordType> {
ellipsis?: CellEllipsisType; ellipsis?: CellEllipsisType;
align?: AlignType; align?: AlignType;
customFilterDropdown?: boolean;
/** @deprecated Please use `v-slot:filterIcon` `v-slot:bodyCell` `v-slot:headerCell` instead */ /** @deprecated Please use `v-slot:filterIcon` `v-slot:bodyCell` `v-slot:headerCell` instead */
slots?: { slots?: {
filterIcon?: string; filterIcon?: string;
@ -74,6 +76,13 @@ interface ColumnSharedType<RecordType> {
customRender?: string; customRender?: string;
title?: string; title?: string;
}; };
/**
* @private Internal usage.
*
* !!! DO NOT USE IN PRODUCTION ENVIRONMENT !!!
*/
__originColumn__?: any;
} }
export interface ColumnGroupType<RecordType> extends ColumnSharedType<RecordType> { export interface ColumnGroupType<RecordType> extends ColumnSharedType<RecordType> {

View File

@ -1,5 +1,5 @@
// debugger tsx // debugger tsx
import Demo from '../../components/table/demo/ajax.vue'; import Demo from '../../components/table/demo/custom-filter-panel.vue';
export default { export default {
render() { render() {