refactor: table
parent
8472c25633
commit
a6a270b44a
|
@ -0,0 +1,21 @@
|
|||
type RecordType = Record<string, any>;
|
||||
|
||||
function extendsObject<T extends RecordType>(...list: T[]) {
|
||||
const result: RecordType = { ...list[0] };
|
||||
|
||||
for (let i = 1; i < list.length; i++) {
|
||||
const obj = list[i];
|
||||
if (obj) {
|
||||
Object.keys(obj).forEach(key => {
|
||||
const val = obj[key];
|
||||
if (val !== undefined) {
|
||||
result[key] = val;
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
export default extendsObject;
|
|
@ -32,6 +32,7 @@ function renderExpandIcon(locale: TableLocale) {
|
|||
[`${iconPrefix}-collapsed`]: expandable && !expanded,
|
||||
})}
|
||||
aria-label={expanded ? locale.collapse : locale.expand}
|
||||
aria-expanded={expanded}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -383,9 +383,19 @@ const InteralTable = defineComponent<
|
|||
|
||||
const [transformBasicColumns] = useColumns(toRef(props, 'contextSlots'));
|
||||
|
||||
const columnTitleProps = computed(() => ({
|
||||
...sorterTitleProps.value,
|
||||
}));
|
||||
const columnTitleProps = computed(() => {
|
||||
const mergedFilters: Record<string, FilterValue> = {};
|
||||
const filtersValue = filters.value;
|
||||
Object.keys(filtersValue).forEach(filterKey => {
|
||||
if (filtersValue[filterKey] !== null) {
|
||||
mergedFilters[filterKey] = filtersValue[filterKey]!;
|
||||
}
|
||||
});
|
||||
return {
|
||||
...sorterTitleProps.value,
|
||||
filters: mergedFilters,
|
||||
};
|
||||
});
|
||||
const [transformTitleColumns] = useTitleColumns(columnTitleProps);
|
||||
|
||||
// ========================== Pagination ==========================
|
||||
|
@ -413,7 +423,7 @@ const InteralTable = defineComponent<
|
|||
changeEventInfo.pagination =
|
||||
props.pagination === false
|
||||
? {}
|
||||
: getPaginationParam(props.pagination, mergedPagination.value);
|
||||
: getPaginationParam(mergedPagination.value, props.pagination);
|
||||
|
||||
changeEventInfo.resetPagination = resetPagination;
|
||||
});
|
||||
|
@ -556,8 +566,8 @@ const InteralTable = defineComponent<
|
|||
const defaultPosition = direction.value === 'rtl' ? 'left' : 'right';
|
||||
const { position } = mergedPagination.value;
|
||||
if (position !== null && Array.isArray(position)) {
|
||||
const topPos = position.find(p => p.indexOf('top') !== -1);
|
||||
const bottomPos = position.find(p => p.indexOf('bottom') !== -1);
|
||||
const topPos = position.find(p => p.includes('top'));
|
||||
const bottomPos = position.find(p => p.includes('bottom'));
|
||||
const isDisable = position.every(p => `${p}` === 'none');
|
||||
if (!topPos && !bottomPos && !isDisable) {
|
||||
bottomPaginationNode = renderPagination(defaultPosition);
|
||||
|
|
|
@ -140,14 +140,14 @@ describe('Table.filter', () => {
|
|||
});
|
||||
});
|
||||
// TODO
|
||||
xit('can be controlled by filterDropdownVisible', done => {
|
||||
xit('can be controlled by filterDropdownOpen', done => {
|
||||
const wrapper = mount(
|
||||
Table,
|
||||
getTableOptions({
|
||||
columns: [
|
||||
{
|
||||
...column,
|
||||
filterDropdownVisible: true,
|
||||
filterDropdownOpen: true,
|
||||
},
|
||||
],
|
||||
}),
|
||||
|
@ -160,7 +160,7 @@ describe('Table.filter', () => {
|
|||
columns: [
|
||||
{
|
||||
...column,
|
||||
filterDropdownVisible: false,
|
||||
filterDropdownOpen: false,
|
||||
},
|
||||
],
|
||||
});
|
||||
|
@ -179,7 +179,7 @@ describe('Table.filter', () => {
|
|||
columns: [
|
||||
{
|
||||
...column,
|
||||
onFilterDropdownVisibleChange: handleChange,
|
||||
onFilterDropdownOpenChange: handleChange,
|
||||
},
|
||||
],
|
||||
}),
|
||||
|
|
|
@ -525,7 +525,7 @@ describe('Table.rowSelection', () => {
|
|||
value: 'Lucy',
|
||||
},
|
||||
],
|
||||
filterDropdownVisible: true,
|
||||
filterDropdownOpen: true,
|
||||
onFilter: (value, record) => record.name.indexOf(value) === 0,
|
||||
},
|
||||
];
|
||||
|
|
|
@ -123,7 +123,7 @@ export default defineComponent({
|
|||
customFilterDropdown: true,
|
||||
onFilter: (value, record) =>
|
||||
record.name.toString().toLowerCase().includes(value.toLowerCase()),
|
||||
onFilterDropdownVisibleChange: visible => {
|
||||
onFilterDropdownOpenChange: visible => {
|
||||
if (visible) {
|
||||
setTimeout(() => {
|
||||
searchInput.value.focus();
|
||||
|
@ -143,7 +143,7 @@ export default defineComponent({
|
|||
customFilterDropdown: true,
|
||||
onFilter: (value, record) =>
|
||||
record.address.toString().toLowerCase().includes(value.toLowerCase()),
|
||||
onFilterDropdownVisibleChange: visible => {
|
||||
onFilterDropdownOpenChange: visible => {
|
||||
if (visible) {
|
||||
setTimeout(() => {
|
||||
searchInput.value.focus();
|
||||
|
|
|
@ -1,4 +1,3 @@
|
|||
import isEqual from 'lodash-es/isEqual';
|
||||
import FilterFilled from '@ant-design/icons-vue/FilterFilled';
|
||||
import Button from '../../../button';
|
||||
import Menu from '../../../menu';
|
||||
|
@ -26,6 +25,8 @@ import type { EventHandler } from '../../../_util/EventInterface';
|
|||
import FilterSearch from './FilterSearch';
|
||||
import Tree from '../../../tree';
|
||||
import type { CheckboxChangeEvent } from '../../../checkbox/interface';
|
||||
import devWarning from '../../../vc-util/devWarning';
|
||||
import isEqual from '../../../vc-util/isEqual';
|
||||
|
||||
interface FilterRestProps {
|
||||
confirm?: Boolean;
|
||||
|
@ -99,7 +100,7 @@ function renderFilterItems({
|
|||
return item;
|
||||
});
|
||||
}
|
||||
|
||||
export type TreeColumnFilterItem = ColumnFilterItem;
|
||||
export interface FilterDropdownProps<RecordType> {
|
||||
tablePrefixCls: string;
|
||||
prefixCls: string;
|
||||
|
@ -108,7 +109,7 @@ export interface FilterDropdownProps<RecordType> {
|
|||
filterState?: FilterState<RecordType>;
|
||||
filterMultiple: boolean;
|
||||
filterMode?: 'menu' | 'tree';
|
||||
filterSearch?: FilterSearchType;
|
||||
filterSearch?: FilterSearchType<ColumnFilterItem | TreeColumnFilterItem>;
|
||||
columnKey: Key;
|
||||
triggerFilter: (filterState: FilterState<RecordType>) => void;
|
||||
locale: TableLocale;
|
||||
|
@ -136,7 +137,29 @@ export default defineComponent<FilterDropdownProps<any>>({
|
|||
const contextSlots = useInjectSlots();
|
||||
const filterMode = computed(() => props.filterMode ?? 'menu');
|
||||
const filterSearch = computed(() => props.filterSearch ?? false);
|
||||
const filterDropdownVisible = computed(() => props.column.filterDropdownVisible);
|
||||
const filterDropdownOpen = computed(
|
||||
() => props.column.filterDropdownOpen || props.column.filterDropdownVisible,
|
||||
);
|
||||
const onFilterDropdownOpenChange = computed(
|
||||
() => props.column.onFilterDropdownOpenChange || props.column.onFilterDropdownVisibleChange,
|
||||
);
|
||||
|
||||
if (process.env.NODE_ENV !== 'production') {
|
||||
[
|
||||
['filterDropdownVisible', 'filterDropdownOpen', props.column.filterDropdownVisible],
|
||||
[
|
||||
'onFilterDropdownVisibleChange',
|
||||
'onFilterDropdownOpenChange',
|
||||
props.column.onFilterDropdownVisibleChange,
|
||||
],
|
||||
].forEach(([deprecatedName, newName, prop]) => {
|
||||
devWarning(
|
||||
prop === undefined || prop === null,
|
||||
'Table',
|
||||
`\`${deprecatedName}\` is deprecated. Please use \`${newName}\` instead.`,
|
||||
);
|
||||
});
|
||||
}
|
||||
const visible = ref(false);
|
||||
const filtered = computed(
|
||||
() =>
|
||||
|
@ -166,13 +189,11 @@ export default defineComponent<FilterDropdownProps<any>>({
|
|||
|
||||
const triggerVisible = (newVisible: boolean) => {
|
||||
visible.value = newVisible;
|
||||
props.column.onFilterDropdownVisibleChange?.(newVisible);
|
||||
onFilterDropdownOpenChange.value?.(newVisible);
|
||||
};
|
||||
|
||||
const mergedVisible = computed(() =>
|
||||
typeof filterDropdownVisible.value === 'boolean'
|
||||
? filterDropdownVisible.value
|
||||
: visible.value,
|
||||
typeof filterDropdownOpen.value === 'boolean' ? filterDropdownOpen.value : visible.value,
|
||||
);
|
||||
|
||||
const propFilteredKeys = computed(() => props.filterState?.filteredKeys);
|
||||
|
@ -234,14 +255,14 @@ export default defineComponent<FilterDropdownProps<any>>({
|
|||
});
|
||||
|
||||
// ======================= Submit ========================
|
||||
const internalTriggerFilter = (keys: Key[] | undefined | null) => {
|
||||
const internalTriggerFilter = (keys?: Key[]) => {
|
||||
const { column, columnKey, filterState } = props;
|
||||
const mergedKeys = keys && keys.length ? keys : null;
|
||||
if (mergedKeys === null && (!filterState || !filterState.filteredKeys)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
if (isEqual(mergedKeys, filterState?.filteredKeys)) {
|
||||
if (isEqual(mergedKeys, filterState?.filteredKeys, true)) {
|
||||
return null;
|
||||
}
|
||||
|
||||
|
@ -318,6 +339,13 @@ export default defineComponent<FilterDropdownProps<any>>({
|
|||
return item;
|
||||
});
|
||||
|
||||
const getFilterData = (node: any): TreeColumnFilterItem => ({
|
||||
...node,
|
||||
text: node.title,
|
||||
value: node.key,
|
||||
children: node.children?.map(item => getFilterData(item)) || [],
|
||||
});
|
||||
|
||||
const treeData = computed(() => getTreeData({ filters: props.column.filters }));
|
||||
// ======================== Style ========================
|
||||
const dropdownMenuClass = computed(() =>
|
||||
|
@ -394,7 +422,12 @@ export default defineComponent<FilterDropdownProps<any>>({
|
|||
// onExpand={onExpandChange}
|
||||
filterTreeNode={
|
||||
searchValue.value.trim()
|
||||
? node => searchValueMatched(searchValue.value, node.title)
|
||||
? node => {
|
||||
if (typeof filterSearch.value === 'function') {
|
||||
return filterSearch.value(searchValue.value, getFilterData(node));
|
||||
}
|
||||
return searchValueMatched(searchValue.value, node.title);
|
||||
}
|
||||
: undefined
|
||||
}
|
||||
/>
|
||||
|
@ -443,6 +476,7 @@ export default defineComponent<FilterDropdownProps<any>>({
|
|||
return isEqual(
|
||||
(props.column.defaultFilteredValue || []).map(key => String(key)),
|
||||
selectedKeys,
|
||||
true,
|
||||
);
|
||||
}
|
||||
|
||||
|
@ -464,6 +498,9 @@ export default defineComponent<FilterDropdownProps<any>>({
|
|||
filters: column.filters,
|
||||
visible: mergedVisible.value,
|
||||
column: column.__originColumn__,
|
||||
close: () => {
|
||||
triggerVisible(false);
|
||||
},
|
||||
});
|
||||
} else if (filterDropdownRef.value) {
|
||||
dropdownContent = filterDropdownRef.value;
|
||||
|
@ -512,8 +549,8 @@ export default defineComponent<FilterDropdownProps<any>>({
|
|||
<Dropdown
|
||||
overlay={menu}
|
||||
trigger={['click']}
|
||||
visible={mergedVisible.value}
|
||||
onVisibleChange={onVisibleChange}
|
||||
open={mergedVisible.value}
|
||||
onOpenChange={onVisibleChange}
|
||||
getPopupContainer={getPopupContainer}
|
||||
placement={direction.value === 'rtl' ? 'bottomLeft' : 'bottomRight'}
|
||||
>
|
||||
|
|
|
@ -1,5 +1,16 @@
|
|||
import KeyCode from '../../../_util/KeyCode';
|
||||
import type { KeyboardEventHandler } from '../../../_util/EventInterface';
|
||||
|
||||
const onKeyDown: KeyboardEventHandler = event => {
|
||||
const { keyCode } = event;
|
||||
if (keyCode === KeyCode.ENTER) {
|
||||
event.stopPropagation();
|
||||
}
|
||||
};
|
||||
const FilterDropdownMenuWrapper = (_props, { slots }) => (
|
||||
<div onClick={e => e.stopPropagation()}>{slots.default?.()}</div>
|
||||
<div onClick={e => e.stopPropagation()} onKeydown={onKeyDown}>
|
||||
{slots.default?.()}
|
||||
</div>
|
||||
);
|
||||
|
||||
export default FilterDropdownMenuWrapper;
|
||||
|
|
|
@ -74,9 +74,9 @@ function injectFilter<RecordType>(
|
|||
dropdownPrefixCls: string,
|
||||
columns: ColumnsType<RecordType>,
|
||||
filterStates: FilterState<RecordType>[],
|
||||
triggerFilter: (filterState: FilterState<RecordType>) => void,
|
||||
getPopupContainer: GetPopupContainer | undefined,
|
||||
locale: TableLocale,
|
||||
triggerFilter: (filterState: FilterState<RecordType>) => void,
|
||||
getPopupContainer?: GetPopupContainer | undefined,
|
||||
pos?: string,
|
||||
): ColumnsType<RecordType> {
|
||||
return columns.map((column, index) => {
|
||||
|
@ -121,9 +121,9 @@ function injectFilter<RecordType>(
|
|||
dropdownPrefixCls,
|
||||
newColumn.children,
|
||||
filterStates,
|
||||
locale,
|
||||
triggerFilter,
|
||||
getPopupContainer,
|
||||
locale,
|
||||
columnPos,
|
||||
),
|
||||
};
|
||||
|
@ -217,7 +217,9 @@ function useFilter<RecordType>({
|
|||
|
||||
const mergedFilterStates = computed(() => {
|
||||
const collectedStates = collectFilterStates(mergedColumns.value, false);
|
||||
|
||||
if (collectedStates.length === 0) {
|
||||
return collectedStates;
|
||||
}
|
||||
let filteredKeysIsAllNotControlled = true;
|
||||
let filteredKeysIsAllControlled = true;
|
||||
collectedStates.forEach(({ filteredKeys }) => {
|
||||
|
@ -230,7 +232,23 @@ function useFilter<RecordType>({
|
|||
|
||||
// Return if not controlled
|
||||
if (filteredKeysIsAllNotControlled) {
|
||||
return filterStates.value;
|
||||
// Filter column may have been removed
|
||||
const keyList = (mergedColumns.value || []).map((column, index) =>
|
||||
getColumnKey(column, getColumnPos(index)),
|
||||
);
|
||||
return filterStates.value
|
||||
.filter(({ key }) => keyList.includes(key))
|
||||
.map(item => {
|
||||
const col = mergedColumns.value[keyList.findIndex(key => key === item.key)];
|
||||
return {
|
||||
...item,
|
||||
column: {
|
||||
...item.column,
|
||||
...col,
|
||||
},
|
||||
forceFiltered: col.filtered,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
devWarning(
|
||||
|
@ -257,9 +275,9 @@ function useFilter<RecordType>({
|
|||
dropdownPrefixCls.value,
|
||||
innerColumns,
|
||||
mergedFilterStates.value,
|
||||
locale.value,
|
||||
triggerFilter,
|
||||
getPopupContainer.value,
|
||||
locale.value,
|
||||
);
|
||||
};
|
||||
return [transformColumns, mergedFilterStates, filters];
|
||||
|
|
|
@ -3,12 +3,13 @@ import type { Ref } from 'vue';
|
|||
import { computed } from 'vue';
|
||||
import type { PaginationProps } from '../../pagination';
|
||||
import type { TablePaginationConfig } from '../interface';
|
||||
import extendsObject from '../../_util/extendsObject';
|
||||
|
||||
export const DEFAULT_PAGE_SIZE = 10;
|
||||
|
||||
export function getPaginationParam(
|
||||
pagination: TablePaginationConfig | boolean | undefined,
|
||||
mergedPagination: TablePaginationConfig,
|
||||
pagination: TablePaginationConfig | boolean | undefined,
|
||||
) {
|
||||
const param: any = {
|
||||
current: mergedPagination.current,
|
||||
|
@ -27,23 +28,6 @@ export function getPaginationParam(
|
|||
return param;
|
||||
}
|
||||
|
||||
function extendsObject<T extends Object>(...list: T[]) {
|
||||
const result: T = {} as T;
|
||||
|
||||
list.forEach(obj => {
|
||||
if (obj) {
|
||||
Object.keys(obj).forEach(key => {
|
||||
const val = (obj as any)[key];
|
||||
if (val !== undefined) {
|
||||
(result as any)[key] = val;
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
export default function usePagination(
|
||||
totalRef: Ref<number>,
|
||||
paginationRef: Ref<TablePaginationConfig | false | undefined>,
|
||||
|
@ -81,7 +65,7 @@ export default function usePagination(
|
|||
});
|
||||
|
||||
const refreshPagination = (current?: number, pageSize?: number) => {
|
||||
if (pagination.value === false) return;
|
||||
if (paginationRef.value === false) return;
|
||||
setInnerPagination({
|
||||
current: current ?? 1,
|
||||
pageSize: pageSize || mergedPagination.value.pageSize,
|
||||
|
@ -89,7 +73,7 @@ export default function usePagination(
|
|||
};
|
||||
|
||||
const onInternalChange: PaginationProps['onChange'] = (current, pageSize) => {
|
||||
if (pagination.value) {
|
||||
if (paginationRef.value) {
|
||||
pagination.value.onChange?.(current, pageSize);
|
||||
}
|
||||
refreshPagination(current, pageSize);
|
||||
|
@ -98,7 +82,7 @@ export default function usePagination(
|
|||
|
||||
return [
|
||||
computed(() => {
|
||||
return pagination.value === false
|
||||
return paginationRef.value === false
|
||||
? {}
|
||||
: { ...mergedPagination.value, onChange: onInternalChange };
|
||||
}),
|
||||
|
|
|
@ -55,10 +55,7 @@ export type INTERNAL_SELECTION_ITEM =
|
|||
| typeof SELECTION_INVERT
|
||||
| typeof SELECTION_NONE;
|
||||
|
||||
function flattenData<RecordType>(
|
||||
data: RecordType[] | undefined,
|
||||
childrenColumnName: string,
|
||||
): RecordType[] {
|
||||
function flattenData<RecordType>(childrenColumnName: string, data: RecordType[]): RecordType[] {
|
||||
let list: RecordType[] = [];
|
||||
(data || []).forEach(record => {
|
||||
list.push(record);
|
||||
|
@ -66,7 +63,7 @@ function flattenData<RecordType>(
|
|||
if (record && typeof record === 'object' && childrenColumnName in record) {
|
||||
list = [
|
||||
...list,
|
||||
...flattenData<RecordType>((record as any)[childrenColumnName], childrenColumnName),
|
||||
...flattenData<RecordType>(childrenColumnName, (record as any)[childrenColumnName]),
|
||||
];
|
||||
}
|
||||
});
|
||||
|
@ -130,7 +127,7 @@ export default function useSelection<RecordType>(
|
|||
|
||||
// Get flatten data
|
||||
const flattedData = computed(() =>
|
||||
flattenData(configRef.pageData.value, configRef.childrenColumnName.value),
|
||||
flattenData(configRef.childrenColumnName.value, configRef.pageData.value),
|
||||
);
|
||||
|
||||
// Get all checkbox props
|
||||
|
@ -448,6 +445,7 @@ export default function useSelection<RecordType>(
|
|||
}
|
||||
onChange={onSelectAllChange}
|
||||
disabled={flattedDataLength.value === 0 || allDisabled}
|
||||
aria-label={customizeSelections ? 'Custom selection' : 'Select all'}
|
||||
skipGroup
|
||||
/>
|
||||
{customizeSelections}
|
||||
|
|
|
@ -135,10 +135,12 @@ function injectSorter<RecordType>(
|
|||
class={classNames(`${prefixCls}-column-sorter-up`, {
|
||||
active: sorterOrder === ASCEND,
|
||||
})}
|
||||
role="presentation"
|
||||
/>
|
||||
);
|
||||
const downNode = sortDirections.includes(DESCEND) && (
|
||||
<CaretDownOutlined
|
||||
role="presentation"
|
||||
class={classNames(`${prefixCls}-column-sorter-down`, {
|
||||
active: sorterOrder === DESCEND,
|
||||
})}
|
||||
|
@ -210,11 +212,7 @@ function injectSorter<RecordType>(
|
|||
|
||||
// Inform the screen-reader so it can tell the visually impaired user which column is sorted
|
||||
if (sorterOrder) {
|
||||
if (sorterOrder === 'ascend') {
|
||||
cell['aria-sort'] = 'ascending';
|
||||
} else {
|
||||
cell['aria-sort'] = 'descending';
|
||||
}
|
||||
cell['aria-sort'] = sorterOrder === 'ascend' ? 'ascending' : 'descending';
|
||||
}
|
||||
|
||||
cell.class = classNames(cell.class, `${prefixCls}-column-has-sorters`);
|
||||
|
|
|
@ -85,6 +85,7 @@ Specify `dataSource` of Table as an array of data.
|
|||
| expandedRowKeys(v-model) | Current expanded row keys | string\[] | - | |
|
||||
| expandedRowRender | Expanded container render for each row | Function({record, index, indent, expanded}):VNode\|v-slot | - | |
|
||||
| expandFixed | Set column to be fixed: `true`(same as left) `'left'` `'right'` | boolean \| string | false | 3.0 |
|
||||
| expandColumnTitle | Set the title of the expand column | v-slot | - | 4.0.0 |
|
||||
| expandIcon | Customize row expand Icon. | Function(props):VNode \| v-slot:expandIcon="props" | - | |
|
||||
| expandRowByClick | Whether to expand row by clicking anywhere in the whole row | boolean | `false` | |
|
||||
| footer | Table footer renderer | Function(currentPageData)\| v-slot:footer="currentPageData" | | |
|
||||
|
@ -93,7 +94,7 @@ Specify `dataSource` of Table as an array of data.
|
|||
| indentSize | Indent size in pixels of tree data | number | 15 | |
|
||||
| loading | Loading status of table | boolean\|[object](/components/spin) | `false` | |
|
||||
| locale | i18n text including filter, sort, empty text, etc | object | filterConfirm: 'Ok' <br /> filterReset: 'Reset' <br /> emptyText: 'No Data' | |
|
||||
| pagination | Config of pagination. You can ref table pagination [config](#pagination) or full [`pagination`](/components/pagination/) document, hide it by setting it to `false` | object | | |
|
||||
| pagination | Config of pagination. You can ref table pagination [config](#pagination) or full [`pagination`](/components/pagination/) document, hide it by setting it to `false` | object \| `false` | | |
|
||||
| rowClassName | Row's className | Function(record, index):string | - | |
|
||||
| rowExpandable | Enable row can be expandable | (record) => boolean | - | |
|
||||
| rowKey | Row's unique key, could be a string or function that returns a string | string\|Function(record, index):string | `key` | |
|
||||
|
@ -102,7 +103,7 @@ Specify `dataSource` of Table as an array of data.
|
|||
| showExpandColumn | Show expand column | boolean | true | 3.0 |
|
||||
| showHeader | Whether to show table header | boolean | `true` | |
|
||||
| showSorterTooltip | The header show next sorter direction tooltip. It will be set as the property of Tooltip if its type is object | boolean \| [Tooltip props](/components/tooltip/#API) | true | 3.0 |
|
||||
| size | Size of table | `default` \| `middle` \| `small` \| `large` | `default` | |
|
||||
| size | Size of table | `middle` \| `small` \| `large` | `large` | |
|
||||
| sortDirections | Supported sort way, could be `ascend`, `descend` | Array | \[`ascend`, `descend`] | 3.0 |
|
||||
| sticky | Set sticky header and scroll bar | boolean \| `{offsetHeader?: number, offsetScroll?: number, getContainer?: () => HTMLElement}` | - | 3.0 |
|
||||
| summary | Summary content | v-slot:summary | - | 3.0 |
|
||||
|
@ -162,7 +163,7 @@ One of the Table `columns` prop for describing the table's columns, Column has t
|
|||
| ellipsis | ellipsize cell content, not working with sorter and filters for now.<br />tableLayout would be `fixed` when `ellipsis` is true. | boolean | false | 1.5.0 |
|
||||
| ellipsis | The ellipsis cell content, not working with sorter and filters for now.<br />tableLayout would be `fixed` when `ellipsis` is `true` or `{ showTitle?: boolean }` | boolean \| {showTitle?: boolean } | false | 3.0 |
|
||||
| filterDropdown | Customized filter overlay | VNode \| (props: FilterDropdownProps) => VNode | - | |
|
||||
| filterDropdownVisible | Whether `filterDropdown` is visible | boolean | - | |
|
||||
| filterDropdownOpen | Whether `filterDropdown` is open | boolean | - | 4.0 |
|
||||
| filtered | Whether the `dataSource` is filtered | boolean | `false` | |
|
||||
| filteredValue | Controlled filtered value, filter icon will highlight | string\[] | - | |
|
||||
| filterIcon | Customized filter icon | ({filtered: boolean, column: Column}) | `false` | |
|
||||
|
@ -176,13 +177,14 @@ One of the Table `columns` prop for describing the table's columns, Column has t
|
|||
| minWidth | Drag the minimum width of the column, it will be affected by the automatic adjustment and distribution of the table width | number | 50 | 3.0 |
|
||||
| resizable | Whether the width can be adjusted by dragging, at this time width must be number type | boolean | - | 3.0 |
|
||||
| responsive | The list of breakpoints at which to display this column. Always visible if not set. | [Breakpoint](#Breakpoint)\[] | - | 3.0 |
|
||||
| rowScope | Set scope attribute for all cells in this column | `row` \| `rowgroup` | - | 4.0 |
|
||||
| sortDirections | supported sort way, could be `'ascend'`, `'descend'` | Array | `['ascend', 'descend']` | 1.5.0 |
|
||||
| sorter | Sort function for local sort, see [Array.sort](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort)'s compareFunction. If you need sort buttons only, set to `true` | Function\|boolean | - | |
|
||||
| sortOrder | Order of sorted values: `'ascend'` `'descend'` `false` | boolean\|string | - | |
|
||||
| sortOrder | Order of sorted values: `'ascend'` `'descend'` `null` | string | - | |
|
||||
| title | Title of this column | string | - | |
|
||||
| width | Width of this column | string\|number | - | |
|
||||
| onFilter | Callback executed when the confirm filter button is clicked, Use as a `filter` event when using template or jsx | Function | - | |
|
||||
| onFilterDropdownVisibleChange | Callback executed when `filterDropdownVisible` is changed, Use as a `filterDropdownVisible` event when using template or jsx | function(visible) {} | - | |
|
||||
| onFilterDropdownOpenChange | Callback executed when `filterDropdownOpen` is changed, Use as a `filterDropdownOpen` event when using template or jsx | function(open) {} | - | 4.0 |
|
||||
|
||||
#### Breakpoint
|
||||
|
||||
|
@ -275,12 +277,6 @@ return <Table rowKey="uid" />;
|
|||
return <Table rowKey={record => record.uid} />;
|
||||
```
|
||||
|
||||
## Migrate to v3
|
||||
|
||||
Table deprecated `column.slots`, added `v-slot:bodyCell`, `v-slot:headerCell`, custom cells, and added `column.customFilterDropdown` `v-slot:customFilterDropdown`, custom filtering Menu, added `v-slot:customFilterIcon` custom filter button, but `column.slots` is still available, we will remove it in the next major version.
|
||||
|
||||
Besides, the breaking change is changing `dataIndex` from nest string path like `user.age` to string array path like `['user', 'age']`. This help to resolve developer should additional work on the field which contains `.`.
|
||||
|
||||
## FAQ
|
||||
|
||||
### How to hide pagination when single page or no data?
|
||||
|
|
|
@ -90,6 +90,7 @@ cover: https://mdn.alipayobjects.com/huamei_7uahnr/afts/img/A*3yz3QqMlShYAAAAAAA
|
|||
| expandedRowKeys(v-model) | 展开的行,控制属性 | string\[] | - | |
|
||||
| expandedRowRender | 额外的展开行 | Function(record, index, indent, expanded):VNode \| v-slot:expandedRowRender="{record, index, indent, expanded}" | - | |
|
||||
| expandFixed | 控制展开图标是否固定,可选 true `left` `right` | boolean \| string | false | 3.0 |
|
||||
| expandColumnTitle | 自定义展开列表头 | v-slot | - | 4.0.0 |
|
||||
| expandIcon | 自定义展开图标 | Function(props):VNode \| v-slot:expandIcon="props" | - | |
|
||||
| expandRowByClick | 通过点击行来展开子行 | boolean | `false` | |
|
||||
| footer | 表格尾部 | Function(currentPageData)\|v-slot:footer="currentPageData" | | |
|
||||
|
@ -98,7 +99,7 @@ cover: https://mdn.alipayobjects.com/huamei_7uahnr/afts/img/A*3yz3QqMlShYAAAAAAA
|
|||
| indentSize | 展示树形数据时,每层缩进的宽度,以 px 为单位 | number | 15 | |
|
||||
| loading | 页面是否加载中 | boolean\|[object](/components/spin-cn) | false | |
|
||||
| locale | 默认文案设置,目前包括排序、过滤、空数据文案 | object | filterConfirm: `确定` <br> filterReset: `重置` <br> emptyText: `暂无数据` | |
|
||||
| pagination | 分页器,参考[配置项](#pagination)或 [pagination](/components/pagination-cn/)文档,设为 false 时不展示和进行分页 | object | | |
|
||||
| pagination | 分页器,参考[配置项](#pagination)或 [pagination](/components/pagination-cn/)文档,设为 false 时不展示和进行分页 | object \| `false` | | |
|
||||
| rowClassName | 表格行的类名 | Function(record, index):string | - | |
|
||||
| rowExpandable | 设置是否允许行展开 | (record) => boolean | - | 3.0 |
|
||||
| rowKey | 表格行 key 的取值,可以是字符串或一个函数 | string\|Function(record):string | 'key' | |
|
||||
|
@ -107,7 +108,7 @@ cover: https://mdn.alipayobjects.com/huamei_7uahnr/afts/img/A*3yz3QqMlShYAAAAAAA
|
|||
| showExpandColumn | 设置是否展示行展开列 | boolean | true | 3.0 |
|
||||
| showHeader | 是否显示表头 | boolean | true | |
|
||||
| showSorterTooltip | 表头是否显示下一次排序的 tooltip 提示。当参数类型为对象时,将被设置为 Tooltip 的属性 | boolean \| [Tooltip props](/components/tooltip/) | true | 3.0 |
|
||||
| size | 表格大小 | default \| middle \| small | default | |
|
||||
| size | 表格大小 | `large` \| `middle` \| `small` | `large` | |
|
||||
| sortDirections | 支持的排序方式,取值为 `ascend` `descend` | Array | \[`ascend`, `descend`] | |
|
||||
| sticky | 设置粘性头部和滚动条 | boolean \| `{offsetHeader?: number, offsetScroll?: number, getContainer?: () => HTMLElement}` | - | 3.0 |
|
||||
| summary | 总结栏 | v-slot:summary | - | 3.0 |
|
||||
|
@ -166,7 +167,7 @@ cover: https://mdn.alipayobjects.com/huamei_7uahnr/afts/img/A*3yz3QqMlShYAAAAAAA
|
|||
| defaultSortOrder | 默认排序顺序 | `ascend` \| `descend` | - | |
|
||||
| ellipsis | 超过宽度将自动省略,暂不支持和排序筛选一起使用。<br />设置为 `true` 或 `{ showTitle?: boolean }` 时,表格布局将变成 `tableLayout="fixed"`。 | boolean \| { showTitle?: boolean } | false | 3.0 |
|
||||
| filterDropdown | 可以自定义筛选菜单,此函数只负责渲染图层,需要自行编写各种交互 | VNode \| (props: FilterDropdownProps) => VNode | - | |
|
||||
| filterDropdownVisible | 用于控制自定义筛选菜单是否可见 | boolean | - | |
|
||||
| filterDropdownOpen | 用于控制自定义筛选菜单是否可见 | boolean | - | |
|
||||
| filtered | 标识数据是否经过过滤,筛选图标会高亮 | boolean | false | |
|
||||
| filteredValue | 筛选的受控属性,外界可用此控制列的筛选状态,值为已筛选的 value 数组 | string\[] | - | |
|
||||
| filterIcon | 自定义 filter 图标。 | VNode \| ({filtered: boolean, column: Column}) => vNode | false | |
|
||||
|
@ -180,14 +181,15 @@ cover: https://mdn.alipayobjects.com/huamei_7uahnr/afts/img/A*3yz3QqMlShYAAAAAAA
|
|||
| minWidth | 拖动列最小宽度,会受到表格自动调整分配宽度影响 | number | 50 | 3.0 |
|
||||
| resizable | 是否可拖动调整宽度,此时 width 必须是 number 类型 | boolean | - | 3.0 |
|
||||
| responsive | 响应式 breakpoint 配置列表。未设置则始终可见。 | [Breakpoint](#Breakpoint)\[] | - | 3.0 |
|
||||
| rowScope | 设置列范围 | `row` \| `rowgroup` | - | 4.0 |
|
||||
| showSorterTooltip | 表头显示下一次排序的 tooltip 提示, 覆盖 table 中 `showSorterTooltip` | boolean \| [Tooltip props](/components/tooltip/#API) | true | |
|
||||
| sortDirections | 支持的排序方式,取值为 `'ascend'` `'descend'` | Array | `['ascend', 'descend']` | 1.5.0 |
|
||||
| sorter | 排序函数,本地排序使用一个函数(参考 [Array.sort](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/sort) 的 compareFunction),需要服务端排序可设为 true | Function\|boolean | - | |
|
||||
| sortOrder | 排序的受控属性,外界可用此控制列的排序,可设置为 `'ascend'` `'descend'` `false` | boolean\|string | - | |
|
||||
| sortOrder | 排序的受控属性,外界可用此控制列的排序,可设置为 `'ascend'` `'descend'` `null` | string | - | |
|
||||
| title | 列头显示文字 | string | - | |
|
||||
| width | 列宽度 | string\|number | - | |
|
||||
| onFilter | 本地模式下,确定筛选的运行函数, 使用 template 或 jsx 时作为`filter`事件使用 | Function | - | |
|
||||
| onFilterDropdownVisibleChange | 自定义筛选菜单可见变化时调用,使用 template 或 jsx 时作为`filterDropdownVisibleChange`事件使用 | function(visible) {} | - | |
|
||||
| onFilterDropdownOpenChange | 自定义筛选菜单可见变化时调用,使用 template 或 jsx 时作为`filterDropdownOpenChange`事件使用 | function(open) {} | - | 4.0 |
|
||||
|
||||
#### Breakpoint
|
||||
|
||||
|
@ -280,12 +282,6 @@ return <Table rowKey="uid" />;
|
|||
return <Table rowKey={record => record.uid} />;
|
||||
```
|
||||
|
||||
## 从 v1 / v2 升级到 v3
|
||||
|
||||
Table 废弃了 `column.slots`, 新增 `v-slot:bodyCell`、`v-slot:headerCell`,自定义单元格,新增 `column.customFilterDropdown` `v-slot:customFilterDropdown`,自定义筛选菜单,新增了 `v-slot:customFilterIcon` 自定义筛选按钮,但 `column.slots` 还可用,我们会在下一个大版本时移除。
|
||||
|
||||
此外,比较重大的改动为 `dataIndex` 从支持路径嵌套如 `user.age` 改成了数组路径如 `['user', 'age']`。以解决过去属性名带 `.` 需要额外的数据转化问题。
|
||||
|
||||
## FAQ
|
||||
|
||||
### 如何在没有数据或只有一页数据时隐藏分页栏
|
||||
|
|
|
@ -49,7 +49,7 @@ export interface TableLocale {
|
|||
export type SortOrder = 'descend' | 'ascend' | null;
|
||||
|
||||
const TableActions = tuple('paginate', 'sort', 'filter');
|
||||
export type TableAction = typeof TableActions[number];
|
||||
export type TableAction = (typeof TableActions)[number];
|
||||
|
||||
export type CompareFn<T> = (a: T, b: T, sortOrder?: SortOrder) => number;
|
||||
|
||||
|
@ -58,7 +58,6 @@ export interface ColumnFilterItem {
|
|||
value: string | number | boolean;
|
||||
children?: ColumnFilterItem[];
|
||||
}
|
||||
|
||||
export interface ColumnTitleProps<RecordType> {
|
||||
/** @deprecated Please use `sorterColumns` instead. */
|
||||
sortOrder?: SortOrder;
|
||||
|
@ -66,14 +65,16 @@ export interface ColumnTitleProps<RecordType> {
|
|||
sortColumn?: ColumnType<RecordType>;
|
||||
sortColumns?: { column: ColumnType<RecordType>; order: SortOrder }[];
|
||||
|
||||
filters?: Record<string, string[]>;
|
||||
filters?: Record<string, FilterValue>;
|
||||
}
|
||||
|
||||
export type ColumnTitle<RecordType> = VueNode | ((props: ColumnTitleProps<RecordType>) => VueNode);
|
||||
|
||||
export type FilterValue = (Key | boolean)[];
|
||||
export type FilterKey = Key[] | null;
|
||||
export type FilterSearchType = boolean | ((input: string, record: ColumnFilterItem) => boolean);
|
||||
export type FilterSearchType<RecordType = Record<string, any>> =
|
||||
| boolean
|
||||
| ((input: string, record: RecordType) => boolean);
|
||||
export interface FilterConfirmProps {
|
||||
closeDropdown: boolean;
|
||||
}
|
||||
|
@ -85,6 +86,8 @@ export interface FilterDropdownProps<RecordType> {
|
|||
confirm: (param?: FilterConfirmProps) => void;
|
||||
clearFilters?: () => void;
|
||||
filters?: ColumnFilterItem[];
|
||||
/** Only close filterDropdown */
|
||||
close: () => void;
|
||||
visible: boolean;
|
||||
column: ColumnType<RecordType>;
|
||||
}
|
||||
|
@ -115,13 +118,19 @@ export interface ColumnType<RecordType = DefaultRecordType>
|
|||
defaultFilteredValue?: FilterValue | null;
|
||||
filterIcon?: VueNode | ((opt: { filtered: boolean; column: ColumnType }) => VueNode);
|
||||
filterMode?: 'menu' | 'tree';
|
||||
filterSearch?: FilterSearchType;
|
||||
filterSearch?: FilterSearchType<ColumnFilterItem>;
|
||||
onFilter?: (value: string | number | boolean, record: RecordType) => boolean;
|
||||
filterDropdownVisible?: boolean;
|
||||
onFilterDropdownVisibleChange?: (visible: boolean) => void;
|
||||
filterDropdownOpen?: boolean;
|
||||
onFilterDropdownOpenChange?: (visible: boolean) => void;
|
||||
filterResetToDefaultFilteredValue?: boolean;
|
||||
// Responsive
|
||||
responsive?: Breakpoint[];
|
||||
|
||||
// Deprecated
|
||||
/** @deprecated Please use `filterDropdownOpen` instead */
|
||||
filterDropdownVisible?: boolean;
|
||||
/** @deprecated Please use `onFilterDropdownOpenChange` instead */
|
||||
onFilterDropdownVisibleChange?: (visible: boolean) => void;
|
||||
}
|
||||
|
||||
export interface ColumnGroupType<RecordType> extends Omit<ColumnType<RecordType>, 'dataIndex'> {
|
||||
|
|
|
@ -2,7 +2,7 @@
|
|||
* ColumnType which applied in antd: https://ant.design/components/table-cn/#Column
|
||||
* - defaultSortOrder
|
||||
* - filterDropdown
|
||||
* - filterDropdownVisible
|
||||
* - filterDropdownOpen
|
||||
* - filtered
|
||||
* - filteredValue
|
||||
* - filterIcon
|
||||
|
@ -12,7 +12,7 @@
|
|||
* - sortOrder
|
||||
* - sortDirections
|
||||
* - onFilter
|
||||
* - onFilterDropdownVisibleChange
|
||||
* - onFilterDropdownOpenChange
|
||||
*/
|
||||
|
||||
import type { CSSProperties, Ref, TdHTMLAttributes } from 'vue';
|
||||
|
|
Loading…
Reference in New Issue