refactor: table
parent
a64ed0d623
commit
915100e3bb
|
@ -0,0 +1,42 @@
|
|||
import { isRef, reactive } from 'vue';
|
||||
import type { Ref } from 'vue';
|
||||
type MaybeRef<T> = T | Ref<T>;
|
||||
/**
|
||||
* Converts ref to reactive.
|
||||
*
|
||||
* @see https://vueuse.org/toReactive
|
||||
* @param objectRef A ref of object
|
||||
*/
|
||||
export function toReactive<T extends object>(objectRef: MaybeRef<T>): T {
|
||||
if (!isRef(objectRef)) return reactive(objectRef) as T;
|
||||
|
||||
const proxy = new Proxy(
|
||||
{},
|
||||
{
|
||||
get(_, p, receiver) {
|
||||
return Reflect.get(objectRef.value, p, receiver);
|
||||
},
|
||||
set(_, p, value) {
|
||||
(objectRef.value as any)[p] = value;
|
||||
return true;
|
||||
},
|
||||
deleteProperty(_, p) {
|
||||
return Reflect.deleteProperty(objectRef.value, p);
|
||||
},
|
||||
has(_, p) {
|
||||
return Reflect.has(objectRef.value, p);
|
||||
},
|
||||
ownKeys() {
|
||||
return Object.keys(objectRef.value);
|
||||
},
|
||||
getOwnPropertyDescriptor() {
|
||||
return {
|
||||
enumerable: true,
|
||||
configurable: true,
|
||||
};
|
||||
},
|
||||
},
|
||||
);
|
||||
|
||||
return reactive(proxy) as T;
|
||||
}
|
|
@ -50,6 +50,7 @@ import { useLocaleReceiver } from '../locale-provider/LocaleReceiver';
|
|||
import classNames from '../_util/classNames';
|
||||
import omit from '../_util/omit';
|
||||
import { initDefaultProps } from '../_util/props-util';
|
||||
import { ContextSlots, useProvideSlots } from './context';
|
||||
|
||||
export type { ColumnsType, TablePaginationConfig };
|
||||
|
||||
|
@ -106,46 +107,78 @@ export interface TableProps<RecordType = DefaultRecordType>
|
|||
sortDirections?: SortOrder[];
|
||||
showSorterTooltip?: boolean | TooltipProps;
|
||||
}
|
||||
|
||||
const tableProps = () => {
|
||||
return {
|
||||
prefixCls: { type: String as PropType<string> },
|
||||
columns: { type: Array as PropType<ColumnsType> },
|
||||
rowKey: { type: [String, Function] as PropType<TableProps['rowKey']> },
|
||||
tableLayout: { type: String as PropType<TableProps['tableLayout']> },
|
||||
rowClassName: { type: String as PropType<TableProps['rowClassName']> },
|
||||
title: { type: Function as PropType<TableProps['title']> },
|
||||
footer: { type: Function as PropType<TableProps['footer']> },
|
||||
id: { type: String as PropType<TableProps['id']> },
|
||||
showHeader: { type: Boolean as PropType<TableProps['showHeader']> },
|
||||
components: { type: Object as PropType<TableProps['components']> },
|
||||
customRow: { type: Function as PropType<TableProps['customRow']> },
|
||||
customHeaderRow: { type: Function as PropType<TableProps['customHeaderRow']> },
|
||||
direction: { type: String as PropType<TableProps['direction']> },
|
||||
expandFixed: { type: Boolean as PropType<TableProps['expandFixed']> },
|
||||
expandColumnWidth: { type: Number as PropType<TableProps['expandColumnWidth']> },
|
||||
expandedRowKeys: { type: Array as PropType<TableProps['expandedRowKeys']> },
|
||||
defaultExpandedRowKeys: { type: Array as PropType<TableProps['defaultExpandedRowKeys']> },
|
||||
expandedRowRender: { type: Function as PropType<TableProps['expandedRowRender']> },
|
||||
expandRowByClick: { type: Boolean as PropType<TableProps['expandRowByClick']> },
|
||||
expandIcon: { type: Function as PropType<TableProps['expandIcon']> },
|
||||
onExpand: { type: Function as PropType<TableProps['onExpand']> },
|
||||
onExpandedRowsChange: { type: Function as PropType<TableProps['onExpandedRowsChange']> },
|
||||
defaultExpandAllRows: { type: Boolean as PropType<TableProps['defaultExpandAllRows']> },
|
||||
indentSize: { type: Number as PropType<TableProps['indentSize']> },
|
||||
expandIconColumnIndex: { type: Number as PropType<TableProps['expandIconColumnIndex']> },
|
||||
expandedRowClassName: { type: Function as PropType<TableProps['expandedRowClassName']> },
|
||||
childrenColumnName: { type: String as PropType<TableProps['childrenColumnName']> },
|
||||
rowExpandable: { type: Function as PropType<TableProps['rowExpandable']> },
|
||||
sticky: { type: String as PropType<TableProps['sticky']> },
|
||||
prefixCls: { type: String as PropType<string>, default: undefined },
|
||||
columns: { type: Array as PropType<ColumnsType>, default: undefined },
|
||||
rowKey: { type: [String, Function] as PropType<TableProps['rowKey']>, default: undefined },
|
||||
tableLayout: { type: String as PropType<TableProps['tableLayout']>, default: undefined },
|
||||
rowClassName: { type: String as PropType<TableProps['rowClassName']>, default: undefined },
|
||||
title: { type: Function as PropType<TableProps['title']>, default: undefined },
|
||||
footer: { type: Function as PropType<TableProps['footer']>, default: undefined },
|
||||
id: { type: String as PropType<TableProps['id']>, default: undefined },
|
||||
showHeader: { type: Boolean as PropType<TableProps['showHeader']>, default: undefined },
|
||||
components: { type: Object as PropType<TableProps['components']>, default: undefined },
|
||||
customRow: { type: Function as PropType<TableProps['customRow']>, default: undefined },
|
||||
customHeaderRow: {
|
||||
type: Function as PropType<TableProps['customHeaderRow']>,
|
||||
default: undefined,
|
||||
},
|
||||
direction: { type: String as PropType<TableProps['direction']>, default: undefined },
|
||||
expandFixed: { type: Boolean as PropType<TableProps['expandFixed']>, default: undefined },
|
||||
expandColumnWidth: {
|
||||
type: Number as PropType<TableProps['expandColumnWidth']>,
|
||||
default: undefined,
|
||||
},
|
||||
expandedRowKeys: { type: Array as PropType<TableProps['expandedRowKeys']>, default: undefined },
|
||||
defaultExpandedRowKeys: {
|
||||
type: Array as PropType<TableProps['defaultExpandedRowKeys']>,
|
||||
default: undefined,
|
||||
},
|
||||
expandedRowRender: {
|
||||
type: Function as PropType<TableProps['expandedRowRender']>,
|
||||
default: undefined,
|
||||
},
|
||||
expandRowByClick: {
|
||||
type: Boolean as PropType<TableProps['expandRowByClick']>,
|
||||
default: undefined,
|
||||
},
|
||||
expandIcon: { type: Function as PropType<TableProps['expandIcon']>, default: undefined },
|
||||
onExpand: { type: Function as PropType<TableProps['onExpand']>, default: undefined },
|
||||
onExpandedRowsChange: {
|
||||
type: Function as PropType<TableProps['onExpandedRowsChange']>,
|
||||
default: undefined,
|
||||
},
|
||||
defaultExpandAllRows: {
|
||||
type: Boolean as PropType<TableProps['defaultExpandAllRows']>,
|
||||
default: undefined,
|
||||
},
|
||||
indentSize: { type: Number as PropType<TableProps['indentSize']>, default: undefined },
|
||||
expandIconColumnIndex: {
|
||||
type: Number as PropType<TableProps['expandIconColumnIndex']>,
|
||||
default: undefined,
|
||||
},
|
||||
expandedRowClassName: {
|
||||
type: Function as PropType<TableProps['expandedRowClassName']>,
|
||||
default: undefined,
|
||||
},
|
||||
childrenColumnName: {
|
||||
type: String as PropType<TableProps['childrenColumnName']>,
|
||||
default: undefined,
|
||||
},
|
||||
rowExpandable: { type: Function as PropType<TableProps['rowExpandable']>, default: undefined },
|
||||
sticky: { type: String as PropType<TableProps['sticky']>, default: undefined },
|
||||
|
||||
dropdownPrefixCls: String,
|
||||
dataSource: { type: Array as PropType<RcTableProps['data']> },
|
||||
pagination: { type: [Boolean, Object] as PropType<false | TablePaginationConfig> },
|
||||
loading: { type: [Boolean, Object] as PropType<false | SpinProps> },
|
||||
size: { type: String as PropType<SizeType> },
|
||||
dataSource: { type: Array as PropType<RcTableProps['data']>, default: undefined },
|
||||
pagination: {
|
||||
type: [Boolean, Object] as PropType<false | TablePaginationConfig>,
|
||||
default: undefined,
|
||||
},
|
||||
loading: { type: [Boolean, Object] as PropType<false | SpinProps>, default: undefined },
|
||||
size: { type: String as PropType<SizeType>, default: undefined },
|
||||
bordered: Boolean,
|
||||
locale: { type: Object as PropType<TableLocale> },
|
||||
locale: { type: Object as PropType<TableLocale>, default: undefined },
|
||||
|
||||
onChange: {
|
||||
type: Function as PropType<
|
||||
|
@ -156,30 +189,53 @@ const tableProps = () => {
|
|||
extra: TableCurrentDataSource,
|
||||
) => void
|
||||
>,
|
||||
default: undefined,
|
||||
},
|
||||
|
||||
rowSelection: { type: Object as PropType<TableRowSelection> },
|
||||
getPopupContainer: { type: Function as PropType<GetPopupContainer> },
|
||||
rowSelection: { type: Object as PropType<TableRowSelection>, default: undefined },
|
||||
getPopupContainer: { type: Function as PropType<GetPopupContainer>, default: undefined },
|
||||
scroll: {
|
||||
type: Object as PropType<
|
||||
RcTableProps['scroll'] & {
|
||||
scrollToFirstRowOnChange?: boolean;
|
||||
}
|
||||
>,
|
||||
default: undefined,
|
||||
},
|
||||
sortDirections: { type: Array as PropType<SortOrder[]>, default: undefined },
|
||||
showSorterTooltip: {
|
||||
type: [Boolean, Object] as PropType<boolean | TooltipProps>,
|
||||
default: undefined,
|
||||
},
|
||||
contextSlots: {
|
||||
type: Object as PropType<ContextSlots>,
|
||||
},
|
||||
sortDirections: { type: Array as PropType<SortOrder[]> },
|
||||
showSorterTooltip: { type: [Boolean, Object] as PropType<boolean | TooltipProps> },
|
||||
};
|
||||
};
|
||||
|
||||
const InteralTable = defineComponent<TableProps>({
|
||||
const InteralTable = defineComponent<
|
||||
TableProps & {
|
||||
contextSlots: ContextSlots;
|
||||
}
|
||||
>({
|
||||
name: 'InteralTable',
|
||||
props: initDefaultProps(tableProps(), {
|
||||
rowKey: 'key',
|
||||
}) as any,
|
||||
inheritAttrs: false,
|
||||
emits: [],
|
||||
slots: ['emptyText', 'expandIcon', 'title', 'footer', 'summary'],
|
||||
slots: [
|
||||
'emptyText',
|
||||
'expandIcon',
|
||||
'title',
|
||||
'footer',
|
||||
'summary',
|
||||
'expandedRowRender',
|
||||
'bodyCell',
|
||||
'headerCell',
|
||||
'customFilterIcon',
|
||||
'customFilterDropdown',
|
||||
],
|
||||
setup(props, { attrs, slots, emit }) {
|
||||
devWarning(
|
||||
!(typeof props.rowKey === 'function' && props.rowKey.length > 1),
|
||||
|
@ -187,6 +243,8 @@ const InteralTable = defineComponent<TableProps>({
|
|||
'`index` parameter of `rowKey` function is deprecated. There is no guarantee that it will work as expected.',
|
||||
);
|
||||
|
||||
useProvideSlots(computed(() => props.contextSlots));
|
||||
|
||||
const screens = useBreakpoint();
|
||||
|
||||
const mergedColumns = computed(() => {
|
||||
|
@ -339,7 +397,7 @@ const InteralTable = defineComponent<TableProps>({
|
|||
const columnTitleProps = computed(() => ({
|
||||
...sorterTitleProps.value,
|
||||
}));
|
||||
const [transformTitleColumns] = useTitleColumns(columnTitleProps);
|
||||
const [transformTitleColumns] = useTitleColumns(columnTitleProps, toRef(props, 'contextSlots'));
|
||||
|
||||
// ========================== Pagination ==========================
|
||||
const onPaginationChange = (current: number, pageSize: number) => {
|
||||
|
@ -445,10 +503,12 @@ const InteralTable = defineComponent<TableProps>({
|
|||
return typeof props.indentSize === 'number' ? props.indentSize : 15;
|
||||
});
|
||||
|
||||
const transformColumns = (innerColumns: ColumnsType<any>): ColumnsType<any> =>
|
||||
transformTitleColumns(
|
||||
const transformColumns = (innerColumns: ColumnsType<any>): ColumnsType<any> => {
|
||||
const res = transformTitleColumns(
|
||||
transformSelectionColumns(transformFilterColumns(transformSorterColumns(innerColumns))),
|
||||
);
|
||||
return res;
|
||||
};
|
||||
|
||||
return () => {
|
||||
const {
|
||||
|
@ -522,6 +582,7 @@ const InteralTable = defineComponent<TableProps>({
|
|||
<Spin spinning={false} {...spinProps}>
|
||||
{topPaginationNode}
|
||||
<RcTable
|
||||
{...attrs}
|
||||
{...tableProps}
|
||||
expandIconColumnIndex={expandIconColumnIndex.value}
|
||||
indentSize={indentSize.value}
|
||||
|
@ -562,7 +623,16 @@ const Table = defineComponent<TableProps>({
|
|||
setup(_props, { attrs, slots }) {
|
||||
return () => {
|
||||
const columns = (attrs.columns || convertChildrenToColumns(slots.default?.())) as ColumnsType;
|
||||
return <InteralTable {...attrs} columns={columns || []} v-slots={slots} />;
|
||||
console.log('slots', slots);
|
||||
return (
|
||||
<InteralTable
|
||||
{...attrs}
|
||||
columns={columns || []}
|
||||
expandedRowRender={slots.expandedRowRender}
|
||||
contextSlots={{ ...slots }} // use new object, 否则slot热更新失效,原因需进一步探究
|
||||
v-slots={slots}
|
||||
/>
|
||||
);
|
||||
};
|
||||
},
|
||||
});
|
||||
|
|
|
@ -0,0 +1,29 @@
|
|||
import type { ComputedRef, InjectionKey } from 'vue';
|
||||
import { computed } from 'vue';
|
||||
import { inject, provide } from 'vue';
|
||||
|
||||
export type ContextSlots = {
|
||||
emptyText?: (...args: any[]) => void;
|
||||
expandIcon?: (...args: any[]) => void;
|
||||
title?: (...args: any[]) => void;
|
||||
footer?: (...args: any[]) => void;
|
||||
summary?: (...args: any[]) => void;
|
||||
bodyCell?: (...args: any[]) => void;
|
||||
headerCell?: (...args: any[]) => void;
|
||||
customFilterIcon?: (...args: any[]) => void;
|
||||
customFilterDropdown?: (...args: any[]) => void;
|
||||
// 兼容 2.x 的 columns slots 配置
|
||||
[key: string]: (...args: any[]) => void;
|
||||
};
|
||||
|
||||
export type ContextProps = ComputedRef<ContextSlots>;
|
||||
|
||||
export const ContextKey: InjectionKey<ContextProps> = Symbol('ContextProps');
|
||||
|
||||
export const useProvideSlots = (props: ContextProps) => {
|
||||
provide(ContextKey, props);
|
||||
};
|
||||
|
||||
export const useInjectSlots = () => {
|
||||
return inject(ContextKey, computed(() => ({})) as ContextProps);
|
||||
};
|
|
@ -18,6 +18,7 @@ import type { FilterState } from '.';
|
|||
import { computed, defineComponent, onBeforeUnmount, ref } from 'vue';
|
||||
import classNames from '../../../_util/classNames';
|
||||
import useConfigInject from '../../../_util/hooks/useConfigInject';
|
||||
import { useInjectSlots } from '../../context';
|
||||
|
||||
const { SubMenu, Item: MenuItem } = Menu;
|
||||
|
||||
|
@ -119,6 +120,7 @@ export default defineComponent<FilterDropdownProps<any>>({
|
|||
'getPopupContainer',
|
||||
] as any,
|
||||
setup(props, { slots }) {
|
||||
const contextSlots = useInjectSlots();
|
||||
const filterDropdownVisible = computed(() => props.column.filterDropdownVisible);
|
||||
const visible = ref(false);
|
||||
const filtered = computed(
|
||||
|
@ -294,9 +296,11 @@ export default defineComponent<FilterDropdownProps<any>>({
|
|||
|
||||
let filterIcon;
|
||||
if (typeof column.filterIcon === 'function') {
|
||||
filterIcon = column.filterIcon(filtered.value);
|
||||
filterIcon = column.filterIcon({ filtered: filtered.value, column });
|
||||
} else if (column.filterIcon) {
|
||||
filterIcon = column.filterIcon;
|
||||
} else if (contextSlots.value.customFilterIcon) {
|
||||
filterIcon = contextSlots.value.customFilterIcon({ filtered: filtered.value, column });
|
||||
} else {
|
||||
filterIcon = <FilterFilled />;
|
||||
}
|
||||
|
|
|
@ -143,6 +143,7 @@ function generateFilterInfo<RecordType>(filterStates: FilterState<RecordType>[])
|
|||
const currentFilters: Record<string, FilterValue | null> = {};
|
||||
|
||||
filterStates.forEach(({ key, filteredKeys, column }) => {
|
||||
console.log(column);
|
||||
const { filters, filterDropdown } = column;
|
||||
if (filterDropdown) {
|
||||
currentFilters[key] = filteredKeys || null;
|
||||
|
|
|
@ -41,6 +41,7 @@ export default function useLazyKVMap<RecordType>(
|
|||
},
|
||||
{
|
||||
deep: false,
|
||||
immediate: true,
|
||||
},
|
||||
);
|
||||
function getRecordByKey(key: Key): RecordType {
|
||||
|
|
|
@ -81,17 +81,19 @@ export default function useSelection<RecordType>(
|
|||
): [TransformColumns<RecordType>, Ref<Set<Key>>] {
|
||||
// ======================== Caches ========================
|
||||
const preserveRecordsRef = ref(new Map<Key, RecordType>());
|
||||
|
||||
const mergedRowSelection = computed(() => rowSelectionRef.value || {});
|
||||
// ========================= Keys =========================
|
||||
const [mergedSelectedKeys, setMergedSelectedKeys] = useMergedState(
|
||||
rowSelectionRef.value.selectedRowKeys || rowSelectionRef.value.defaultSelectedRowKeys || [],
|
||||
mergedRowSelection.value.selectedRowKeys ||
|
||||
mergedRowSelection.value.defaultSelectedRowKeys ||
|
||||
[],
|
||||
{
|
||||
value: computed(() => rowSelectionRef.value.selectedRowKeys),
|
||||
value: computed(() => mergedRowSelection.value.selectedRowKeys),
|
||||
},
|
||||
);
|
||||
|
||||
const keyEntities = computed(() =>
|
||||
rowSelectionRef.value.checkStrictly
|
||||
mergedRowSelection.value.checkStrictly
|
||||
? { keyEntities: null }
|
||||
: convertDataToEntities(configRef.data.value as unknown as DataNode[], {
|
||||
externalGetKey: configRef.getRowKey.value as any,
|
||||
|
@ -108,7 +110,7 @@ export default function useSelection<RecordType>(
|
|||
const checkboxPropsMap = computed(() => {
|
||||
const map = new Map<Key, Partial<CheckboxProps>>();
|
||||
const getRowKey = configRef.getRowKey.value;
|
||||
const getCheckboxProps = rowSelectionRef.value.getCheckboxProps;
|
||||
const getCheckboxProps = mergedRowSelection.value.getCheckboxProps;
|
||||
flattedData.value.forEach((record, index) => {
|
||||
const key = getRowKey(record, index);
|
||||
const checkboxProps = (getCheckboxProps ? getCheckboxProps(record) : null) || {};
|
||||
|
@ -132,13 +134,13 @@ export default function useSelection<RecordType>(
|
|||
!!checkboxPropsMap.value.get(configRef.getRowKey.value(r))?.disabled;
|
||||
|
||||
const selectKeysState = computed(() => {
|
||||
if (rowSelectionRef.value.checkStrictly) {
|
||||
if (mergedRowSelection.value.checkStrictly) {
|
||||
return [mergedSelectedKeys.value || [], []];
|
||||
}
|
||||
const { checkedKeys, halfCheckedKeys } = conductCheck(
|
||||
mergedSelectedKeys.value,
|
||||
true,
|
||||
keyEntities as any,
|
||||
keyEntities.value,
|
||||
isCheckboxDisabled as any,
|
||||
);
|
||||
return [checkedKeys || [], halfCheckedKeys];
|
||||
|
@ -149,13 +151,13 @@ export default function useSelection<RecordType>(
|
|||
|
||||
const derivedSelectedKeySet = computed<Set<Key>>(() => {
|
||||
const keys =
|
||||
rowSelectionRef.value.type === 'radio'
|
||||
mergedRowSelection.value.type === 'radio'
|
||||
? derivedSelectedKeys.value.slice(0, 1)
|
||||
: derivedSelectedKeys.value;
|
||||
return new Set(keys);
|
||||
});
|
||||
const derivedHalfSelectedKeySet = computed(() =>
|
||||
rowSelectionRef.value.type === 'radio' ? new Set() : new Set(derivedHalfSelectedKeys.value),
|
||||
mergedRowSelection.value.type === 'radio' ? new Set() : new Set(derivedHalfSelectedKeys.value),
|
||||
);
|
||||
|
||||
// Save last selected key to enable range selection
|
||||
|
@ -171,7 +173,7 @@ export default function useSelection<RecordType>(
|
|||
const setSelectedKeys = (keys: Key[]) => {
|
||||
let availableKeys: Key[];
|
||||
let records: RecordType[];
|
||||
const { preserveSelectedRowKeys, onChange: onSelectionChange } = rowSelectionRef.value || {};
|
||||
const { preserveSelectedRowKeys, onChange: onSelectionChange } = mergedRowSelection.value;
|
||||
const { getRecordByKey } = configRef;
|
||||
if (preserveSelectedRowKeys) {
|
||||
// Keep key if mark as preserveSelectedRowKeys
|
||||
|
@ -213,7 +215,7 @@ export default function useSelection<RecordType>(
|
|||
// ====================== Selections ======================
|
||||
// Trigger single `onSelect` event
|
||||
const triggerSingleSelection = (key: Key, selected: boolean, keys: Key[], event: Event) => {
|
||||
const { onSelect } = rowSelectionRef.value || {};
|
||||
const { onSelect } = mergedRowSelection.value;
|
||||
const { getRecordByKey } = configRef || {};
|
||||
if (onSelect) {
|
||||
const rows = keys.map(k => getRecordByKey(k));
|
||||
|
@ -224,7 +226,7 @@ export default function useSelection<RecordType>(
|
|||
};
|
||||
|
||||
const mergedSelections = computed(() => {
|
||||
const { onSelectInvert, onSelectNone, selections, hideSelectAll } = rowSelectionRef.value || {};
|
||||
const { onSelectInvert, onSelectNone, selections, hideSelectAll } = mergedRowSelection.value;
|
||||
|
||||
const { data, pageData, getRowKey, locale: tableLocale } = configRef;
|
||||
|
||||
|
@ -300,7 +302,7 @@ export default function useSelection<RecordType>(
|
|||
renderCell: customizeRenderCell,
|
||||
hideSelectAll,
|
||||
checkStrictly = true,
|
||||
} = rowSelectionRef.value || {};
|
||||
} = mergedRowSelection.value;
|
||||
|
||||
const {
|
||||
prefixCls,
|
||||
|
@ -376,7 +378,7 @@ export default function useSelection<RecordType>(
|
|||
</Menu>
|
||||
);
|
||||
customizeSelections = (
|
||||
<div class={`${prefixCls}-selection-extra`}>
|
||||
<div class={`${prefixCls.value}-selection-extra`}>
|
||||
<Dropdown overlay={menu} getPopupContainer={getPopupContainer.value}>
|
||||
<span>
|
||||
<DownOutlined />
|
||||
|
@ -401,7 +403,7 @@ export default function useSelection<RecordType>(
|
|||
const allDisabledSomeChecked = allDisabled && allDisabledData.some(({ checked }) => checked);
|
||||
|
||||
title = !hideSelectAll && (
|
||||
<div class={`${prefixCls}-selection`}>
|
||||
<div class={`${prefixCls.value}-selection`}>
|
||||
<Checkbox
|
||||
checked={
|
||||
!allDisabled ? !!flattedDataLength.value && checkedCurrentAll : allDisabledAndChecked
|
||||
|
@ -539,7 +541,7 @@ export default function useSelection<RecordType>(
|
|||
const result = conductCheck(
|
||||
[...originCheckedKeys, key],
|
||||
true,
|
||||
keyEntities as any,
|
||||
keyEntities.value,
|
||||
isCheckboxDisabled as any,
|
||||
);
|
||||
const { checkedKeys, halfCheckedKeys } = result;
|
||||
|
@ -552,7 +554,7 @@ export default function useSelection<RecordType>(
|
|||
nextCheckedKeys = conductCheck(
|
||||
Array.from(tempKeySet),
|
||||
{ checked: false, halfCheckedKeys },
|
||||
keyEntities as any,
|
||||
keyEntities.value,
|
||||
isCheckboxDisabled as any,
|
||||
).checkedKeys;
|
||||
}
|
||||
|
@ -583,15 +585,15 @@ export default function useSelection<RecordType>(
|
|||
// Columns
|
||||
const selectionColumn = {
|
||||
width: selectionColWidth,
|
||||
className: `${prefixCls}-selection-column`,
|
||||
title: rowSelectionRef.value.columnTitle || title,
|
||||
className: `${prefixCls.value}-selection-column`,
|
||||
title: mergedRowSelection.value.columnTitle || title,
|
||||
render: renderSelectionCell,
|
||||
[INTERNAL_COL_DEFINE]: {
|
||||
class: `${prefixCls}-selection-col`,
|
||||
class: `${prefixCls.value}-selection-col`,
|
||||
},
|
||||
};
|
||||
|
||||
if (expandType.value === 'row' && columns.length && !expandIconColumnIndex) {
|
||||
if (expandType.value === 'row' && columns.length && !expandIconColumnIndex.value) {
|
||||
const [expandColumn, ...restColumns] = columns;
|
||||
const selectionFixed = fixed || getFixedType(restColumns[0]);
|
||||
if (selectionFixed) {
|
||||
|
|
|
@ -1,18 +1,28 @@
|
|||
import type { Ref } from 'vue';
|
||||
import { ContextSlots } from '../context';
|
||||
import type { TransformColumns, ColumnTitleProps, ColumnsType } from '../interface';
|
||||
import { renderColumnTitle } from '../util';
|
||||
|
||||
function fillTitle<RecordType>(
|
||||
columns: ColumnsType<RecordType>,
|
||||
columnTitleProps: ColumnTitleProps<RecordType>,
|
||||
contextColumns: Ref<ContextSlots>,
|
||||
) {
|
||||
const $slots = contextColumns.value;
|
||||
return columns.map(column => {
|
||||
const cloneColumn = { ...column };
|
||||
const { slots = {} } = cloneColumn;
|
||||
|
||||
cloneColumn.title = renderColumnTitle(column.title, columnTitleProps);
|
||||
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);
|
||||
|
||||
if ('children' in cloneColumn) {
|
||||
cloneColumn.children = fillTitle(cloneColumn.children, columnTitleProps);
|
||||
cloneColumn.children = fillTitle(cloneColumn.children, columnTitleProps, contextColumns);
|
||||
}
|
||||
|
||||
return cloneColumn;
|
||||
|
@ -21,9 +31,10 @@ function fillTitle<RecordType>(
|
|||
|
||||
export default function useTitleColumns<RecordType>(
|
||||
columnTitleProps: Ref<ColumnTitleProps<RecordType>>,
|
||||
contextColumns: Ref<ContextSlots>,
|
||||
): [TransformColumns<RecordType>] {
|
||||
const filledColumns = (columns: ColumnsType<RecordType>) =>
|
||||
fillTitle(columns, columnTitleProps.value);
|
||||
fillTitle(columns, columnTitleProps.value, contextColumns);
|
||||
|
||||
return [filledColumns];
|
||||
}
|
||||
|
|
|
@ -106,7 +106,7 @@ export interface ColumnType<RecordType = DefaultRecordType> extends RcColumnType
|
|||
filterMultiple?: boolean;
|
||||
filteredValue?: FilterValue | null;
|
||||
defaultFilteredValue?: FilterValue | null;
|
||||
filterIcon?: VueNode | ((filtered: boolean) => VueNode);
|
||||
filterIcon?: VueNode | ((opt: { filtered: boolean; column: ColumnType }) => VueNode);
|
||||
onFilter?: (value: string | number | boolean, record: RecordType) => boolean;
|
||||
filterDropdownVisible?: boolean;
|
||||
onFilterDropdownVisibleChange?: (visible: boolean) => void;
|
||||
|
|
|
@ -149,6 +149,7 @@ export default defineComponent<BodyRowProps<unknown>>({
|
|||
|
||||
return (
|
||||
<Cell
|
||||
cellType="body"
|
||||
class={columnClassName}
|
||||
ellipsis={column.ellipsis}
|
||||
align={column.align}
|
||||
|
@ -193,7 +194,12 @@ export default defineComponent<BodyRowProps<unknown>>({
|
|||
// ======================== Expand Row =========================
|
||||
let expandRowNode;
|
||||
if (rowSupportExpand.value && (expandRended.value || expanded.value)) {
|
||||
const expandContent = expandedRowRender(record, index, indent + 1, expanded.value);
|
||||
const expandContent = expandedRowRender({
|
||||
record,
|
||||
index,
|
||||
indent: indent + 1,
|
||||
expanded: expanded.value,
|
||||
});
|
||||
const computedExpandedRowClassName =
|
||||
expandedRowClassName && expandedRowClassName(record, index, indent);
|
||||
expandRowNode = (
|
||||
|
|
|
@ -14,6 +14,7 @@ import type {
|
|||
CellEllipsisType,
|
||||
} from '../interface';
|
||||
import { getPathValue, validateValue } from '../utils/valueUtil';
|
||||
import { useInjectSlots } from '../../table/context';
|
||||
|
||||
function isRenderCell<RecordType = DefaultRecordType>(
|
||||
data: RenderedCell<RecordType>,
|
||||
|
@ -53,6 +54,8 @@ export interface CellProps<RecordType = DefaultRecordType> {
|
|||
isSticky?: boolean;
|
||||
|
||||
column?: ColumnType<RecordType>;
|
||||
|
||||
cellType?: 'header' | 'body';
|
||||
}
|
||||
export default defineComponent<CellProps>({
|
||||
name: 'Cell',
|
||||
|
@ -62,7 +65,6 @@ export default defineComponent<CellProps>({
|
|||
'index',
|
||||
'dataIndex',
|
||||
'customRender',
|
||||
'children',
|
||||
'component',
|
||||
'colSpan',
|
||||
'rowSpan',
|
||||
|
@ -79,9 +81,11 @@ export default defineComponent<CellProps>({
|
|||
'rowType',
|
||||
'isSticky',
|
||||
'column',
|
||||
'cellType',
|
||||
] as any,
|
||||
slots: ['appendNode'],
|
||||
setup(props, { slots }) {
|
||||
const contextSlots = useInjectSlots();
|
||||
return () => {
|
||||
const {
|
||||
prefixCls,
|
||||
|
@ -105,6 +109,7 @@ export default defineComponent<CellProps>({
|
|||
rowType,
|
||||
isSticky,
|
||||
column,
|
||||
cellType,
|
||||
} = props;
|
||||
const cellPrefixCls = `${prefixCls}-cell`;
|
||||
|
||||
|
@ -112,8 +117,11 @@ export default defineComponent<CellProps>({
|
|||
let cellProps: CellType;
|
||||
let childNode;
|
||||
const children = slots.default?.();
|
||||
if (validateValue(children)) {
|
||||
if (validateValue(children) || cellType === 'header') {
|
||||
childNode = children;
|
||||
if (cellType === 'header' && contextSlots.value.headerCell) {
|
||||
childNode = contextSlots.value.headerCell({ title: column.title, index, column });
|
||||
}
|
||||
} else {
|
||||
const value = getPathValue(record, dataIndex);
|
||||
|
||||
|
@ -129,6 +137,10 @@ export default defineComponent<CellProps>({
|
|||
childNode = renderData;
|
||||
}
|
||||
}
|
||||
|
||||
if (cellType === 'body' && contextSlots.value.bodyCell) {
|
||||
childNode = contextSlots.value.bodyCell({ text: value, value, record, index, column });
|
||||
}
|
||||
}
|
||||
|
||||
// Not crash if final `childNode` is not validate ReactNode
|
||||
|
|
|
@ -92,6 +92,7 @@ export interface HeaderProps<RecordType = DefaultRecordType> {
|
|||
|
||||
export default defineComponent<HeaderProps>({
|
||||
name: 'Header',
|
||||
inheritAttrs: false,
|
||||
props: ['columns', 'flattenColumns', 'stickyOffsets', 'customHeaderRow'] as any,
|
||||
setup(props) {
|
||||
const tableContext = useInjectTable();
|
||||
|
|
|
@ -77,6 +77,7 @@ export default defineComponent<RowProps>({
|
|||
return (
|
||||
<Cell
|
||||
{...cell}
|
||||
cellType="header"
|
||||
ellipsis={column.ellipsis}
|
||||
align={column.align}
|
||||
component={CellComponent}
|
||||
|
|
|
@ -54,6 +54,7 @@ import VCResizeObserver from '../vc-resize-observer';
|
|||
import { useProvideTable } from './context/TableContext';
|
||||
import { useProvideBody } from './context/BodyContext';
|
||||
import { useProvideResize } from './context/ResizeContext';
|
||||
import { getDataAndAriaProps } from './utils/legacyUtil';
|
||||
|
||||
// Used for conditions cache
|
||||
const EMPTY_DATA = [];
|
||||
|
@ -178,7 +179,8 @@ export default defineComponent<TableProps>({
|
|||
'internalRefs',
|
||||
'canExpandable',
|
||||
] as any,
|
||||
setup(props, { slots, emit }) {
|
||||
inheritAttrs: false,
|
||||
setup(props, { attrs, slots, emit }) {
|
||||
const mergedData = computed(() => props.data || EMPTY_DATA);
|
||||
const hasData = computed(() => !!mergedData.value.length);
|
||||
|
||||
|
@ -749,9 +751,10 @@ export default defineComponent<TableProps>({
|
|||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
const ariaProps = getDataAndAriaProps(attrs);
|
||||
let fullTable = (
|
||||
<div
|
||||
{...ariaProps}
|
||||
class={classNames(prefixCls, {
|
||||
[`${prefixCls}-rtl`]: direction === 'rtl',
|
||||
[`${prefixCls}-ping-left`]: pingedLeft.value,
|
||||
|
@ -765,7 +768,9 @@ export default defineComponent<TableProps>({
|
|||
[`${prefixCls}-has-fix-right`]:
|
||||
flattenColumns.value[columnCount.value - 1] &&
|
||||
flattenColumns.value[columnCount.value - 1].fixed === 'right',
|
||||
[attrs.class as string]: attrs.class,
|
||||
})}
|
||||
style={attrs.style}
|
||||
id={id}
|
||||
ref={fullTableRef}
|
||||
>
|
||||
|
|
|
@ -20,12 +20,12 @@ export interface TableContextProps {
|
|||
summaryCollect: (uniKey: string, fixed: boolean | string) => void;
|
||||
}
|
||||
|
||||
export const BodyContextKey: InjectionKey<TableContextProps> = Symbol('TableContextProps');
|
||||
export const TableContextKey: InjectionKey<TableContextProps> = Symbol('TableContextProps');
|
||||
|
||||
export const useProvideTable = (props: TableContextProps) => {
|
||||
provide(BodyContextKey, props);
|
||||
provide(TableContextKey, props);
|
||||
};
|
||||
|
||||
export const useInjectTable = () => {
|
||||
return inject(BodyContextKey, {} as TableContextProps);
|
||||
return inject(TableContextKey, {} as TableContextProps);
|
||||
};
|
||||
|
|
|
@ -66,6 +66,14 @@ interface ColumnSharedType<RecordType> {
|
|||
customHeaderCell?: GetComponentProps<ColumnsType<RecordType>[number]>;
|
||||
ellipsis?: CellEllipsisType;
|
||||
align?: AlignType;
|
||||
|
||||
/** @deprecated Please use `v-slot:filterIcon` `v-slot:bodyCell` `v-slot:headerCell` instead */
|
||||
slots?: {
|
||||
filterIcon?: string;
|
||||
filterDropdown?: string;
|
||||
customRender?: string;
|
||||
title?: string;
|
||||
};
|
||||
}
|
||||
|
||||
export interface ColumnGroupType<RecordType> extends ColumnSharedType<RecordType> {
|
||||
|
@ -176,12 +184,12 @@ export interface LegacyExpandableProps<RecordType> {
|
|||
rowExpandable?: (record: RecordType) => boolean;
|
||||
}
|
||||
|
||||
export type ExpandedRowRender<ValueType> = (
|
||||
record: ValueType,
|
||||
index: number,
|
||||
indent: number,
|
||||
expanded: boolean,
|
||||
) => any;
|
||||
export type ExpandedRowRender<ValueType> = (opt: {
|
||||
record: ValueType;
|
||||
index: number;
|
||||
indent: number;
|
||||
expanded: boolean;
|
||||
}) => any;
|
||||
|
||||
export interface RenderExpandIconProps<RecordType> {
|
||||
prefixCls: string;
|
||||
|
|
|
@ -421,8 +421,8 @@ export default defineComponent({
|
|||
let titleNode: any;
|
||||
if (typeof title === 'function') {
|
||||
titleNode = title(renderArgsData.value);
|
||||
} else if (contextSlots.titleRender) {
|
||||
titleNode = contextSlots.titleRender(renderArgsData.value);
|
||||
} else if (contextSlots.valuetitleRender) {
|
||||
titleNode = contextSlots.valuetitleRender(renderArgsData.value);
|
||||
} else {
|
||||
titleNode = title;
|
||||
}
|
||||
|
|
|
@ -1,8 +1,5 @@
|
|||
<template>
|
||||
<a-table :columns="columns" :data-source="data">
|
||||
<template #name="{ text }">
|
||||
<a>{{ text }}</a>
|
||||
</template>
|
||||
<template #customTitle>
|
||||
<span>
|
||||
<smile-outlined />
|
||||
|
@ -36,9 +33,10 @@
|
|||
</template>
|
||||
<script lang="ts">
|
||||
import { SmileOutlined, DownOutlined } from '@ant-design/icons-vue';
|
||||
import { defineComponent } from 'vue';
|
||||
import { defineComponent, ref } from 'vue';
|
||||
const columns = [
|
||||
{
|
||||
title: 'Name',
|
||||
dataIndex: 'name',
|
||||
key: 'name',
|
||||
slots: { title: 'customTitle', customRender: 'name' },
|
||||
|
@ -96,9 +94,12 @@ export default defineComponent({
|
|||
DownOutlined,
|
||||
},
|
||||
setup() {
|
||||
const test = ref('111');
|
||||
window.test = test;
|
||||
return {
|
||||
data,
|
||||
columns,
|
||||
test,
|
||||
};
|
||||
},
|
||||
});
|
||||
|
|
Loading…
Reference in New Issue