refactor: table
parent
8d60808662
commit
10cb279dfc
|
@ -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';
|
||||||
|
|
|
@ -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;
|
||||||
};
|
};
|
||||||
|
|
|
@ -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,
|
||||||
|
|
|
@ -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',
|
||||||
|
|
|
@ -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) {
|
||||||
|
|
|
@ -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 => {
|
||||||
|
|
|
@ -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];
|
||||||
|
}
|
|
@ -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']}
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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];
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 & {
|
||||||
|
|
|
@ -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>
|
||||||
)[];
|
)[];
|
||||||
|
|
|
@ -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__,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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> {
|
||||||
|
|
|
@ -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() {
|
||||||
|
|
Loading…
Reference in New Issue