提交 3549043f 编写于 作者: V vben

wip(table): perf table

上级 a305e591
......@@ -12,10 +12,13 @@
- form: 新增远程下拉`ApiSelect`及示例
- form: 新增`autoFocusFirstItem`配置。用于配置是否聚焦表单第一个输入框
- useForm: 支持动态改变参数。可以传入`Ref`类型与`Computed`类型进行动态更改
- table: 新增`clickToRowSelect`属性。用于控制点击行是否选中勾选狂
- table: 监听行点击事件
### ⚡ Performance Improvements
- 优化`modal``drawer`滚动条组件
- table: 移除 `isTreeTable`属性
### 🎫 Chores
......
......@@ -225,6 +225,10 @@
padding: 16px !important;
margin-bottom: 0 !important;
}
> .scrollbar > .scrollbar__bar.is-horizontal {
display: none;
}
}
}
......
......@@ -43,7 +43,7 @@
if (el) {
await nextTick();
const icon = unref(getIconRef);
if (!icon) return;
const svg = Iconify.renderSVG(icon, {});
if (svg) {
......@@ -74,7 +74,7 @@
}
);
watch(() => props.icon, update, { flush: 'post' });
// watch(() => props.icon, update, { flush: 'post' });
onMounted(update);
......
......@@ -40,6 +40,10 @@
.ant-modal-body {
padding: 0;
> .scrollbar > .scrollbar__bar.is-horizontal {
display: none;
}
}
&-large {
......
......@@ -11,7 +11,7 @@
:submitOnReset="true"
v-bind="getFormProps"
v-if="getBindValues.useSearchForm"
:submitButtonOptions="{ loading }"
:submitButtonOptions="{ loading: getLoading }"
:tableAction="tableAction"
@register="registerForm"
@submit="handleSearchInfoChange"
......@@ -35,18 +35,10 @@
</div>
</template>
<script lang="ts">
import type {
BasicTableProps,
FetchParams,
GetColumnsParams,
TableActionType,
SizeType,
SorterResult,
TableCustomRecord,
} from './types/table';
import type { BasicTableProps, TableActionType, SizeType, SorterResult } from './types/table';
import { PaginationProps } from './types/pagination';
import { defineComponent, ref, computed, unref, watch, nextTick, toRaw } from 'vue';
import { defineComponent, ref, computed, unref, watch, nextTick } from 'vue';
import { Table } from 'ant-design-vue';
import renderTitle from './components/renderTitle';
import renderFooter from './components/renderFooter';
......@@ -64,51 +56,64 @@
import { useRowSelection } from './hooks/useRowSelection';
import { useTableScroll } from './hooks/useTableScroll';
import { provideTable } from './hooks/useProvinceTable';
import { useCustomRow } from './hooks/useCustomRow';
import { useTableStyle } from './hooks/useTableStyle';
import { useEventListener } from '/@/hooks/event/useEventListener';
import { basicProps } from './props';
import { ROW_KEY } from './const';
import { useExpose } from '/@/hooks/core/useExpose';
import './style/index.less';
export default defineComponent({
props: basicProps,
components: { Table, BasicForm },
emits: ['fetch-success', 'fetch-error', 'selection-change', 'register'],
emits: [
'fetch-success',
'fetch-error',
'selection-change',
'register',
'row-click',
'row-dbClick',
'row-contextmenu',
'row-mouseenter',
'row-mouseleave',
],
setup(props, { attrs, emit, slots }) {
const tableElRef = ref<ComponentRef>(null);
const wrapRef = ref<Nullable<HTMLDivElement>>(null);
const innerPropsRef = ref<Partial<BasicTableProps>>();
const [registerForm, { getFieldsValue }] = useForm();
const getMergeProps = computed(() => {
return {
...props,
...unref(innerPropsRef),
} as BasicTableProps;
const getProps = computed(() => {
return { ...props, ...unref(innerPropsRef) } as BasicTableProps;
});
// const getProps = computed(
// (): FormProps => {
// return deepMerge(toRaw(props), unref(innerPropsRef));
// }
// );
const { loadingRef } = useLoading(getMergeProps);
const { getPaginationRef, setPagination } = usePagination(getMergeProps);
const { getColumnsRef, setColumns } = useColumns(getMergeProps, getPaginationRef);
const { getDataSourceRef, setTableData, fetch, getAutoCreateKey } = useDataSource(
getMergeProps,
const { getLoading, setLoading } = useLoading(getProps);
const { getPaginationInfo, getPagination, setPagination } = usePagination(getProps);
const { getColumnsRef, getColumns, setColumns } = useColumns(getProps, getPaginationInfo);
const {
getDataSourceRef,
getDataSource,
setTableData,
fetch,
getRowKey,
reload,
getAutoCreateKey,
} = useDataSource(
getProps,
{
getPaginationRef,
loadingRef,
getPaginationInfo,
setLoading,
setPagination,
getFieldsValue,
},
emit
);
const { getScrollRef, redoHeight } = useTableScroll(getMergeProps, tableElRef);
const { getScrollRef, redoHeight } = useTableScroll(getProps, tableElRef);
const {
getRowSelectionRef,
getSelectRows,
......@@ -116,55 +121,58 @@
getSelectRowKeys,
deleteSelectRowByKey,
setSelectedRowKeys,
} = useRowSelection(getMergeProps, emit);
const getRowKey = computed(() => {
const { rowKey } = unref(getMergeProps);
} = useRowSelection(getProps, emit);
return unref(getAutoCreateKey) ? ROW_KEY : rowKey;
const { customRow } = useCustomRow(getProps, {
setSelectedRowKeys,
getSelectRowKeys,
clearSelectedRowKeys,
getAutoCreateKey,
emit,
});
const { getRowClassName } = useTableStyle(getProps);
const getTitleProps = computed(
(): Recordable => {
const { title, showTableSetting, titleHelpMessage, tableSetting } = unref(getProps);
const hideTitle = !slots.tableTitle && !title && !slots.toolbar && !showTableSetting;
if (hideTitle && !isString(title)) {
return {};
}
return {
title: hideTitle
? null
: renderTitle.bind(
null,
title,
titleHelpMessage,
slots,
showTableSetting,
tableSetting
),
};
}
);
const getBindValues = computed(() => {
const { title, titleHelpMessage, showSummary, showTableSetting, tableSetting } = unref(
getMergeProps
);
const hideTitle = !slots.tableTitle && !title && !slots.toolbar && !showTableSetting;
const titleData: Recordable =
hideTitle && !isString(title)
? {}
: {
title: hideTitle
? null
: renderTitle.bind(
null,
title,
titleHelpMessage,
slots,
showTableSetting,
tableSetting
),
};
const pagination = unref(getPaginationRef);
const rowSelection = unref(getRowSelectionRef);
const scroll = unref(getScrollRef);
const loading = unref(loadingRef);
const rowKey = unref(getRowKey);
const columns = unref(getColumnsRef);
const dataSource = unref(getDataSourceRef);
let propsData = {
const { showSummary } = unref(getProps);
let propsData: Recordable = {
size: 'middle',
...(slots.expandedRowRender ? { expandIcon: renderExpandIcon() } : {}),
...attrs,
...unref(getMergeProps),
...titleData,
scroll,
loading,
customRow,
...unref(getProps),
...unref(getTitleProps),
scroll: unref(getScrollRef),
loading: unref(getLoading),
tableLayout: 'fixed',
rowSelection,
rowKey,
columns,
pagination,
dataSource,
rowSelection: unref(getRowSelectionRef),
rowKey: unref(getRowKey),
columns: unref(getColumnsRef),
pagination: unref(getPaginationInfo),
dataSource: unref(getDataSourceRef),
};
if (slots.expandedRowRender) {
propsData = omit(propsData, 'scroll');
......@@ -173,7 +181,7 @@
propsData.footer = renderFooter.bind(null, {
scroll: scroll as any,
columnsRef: getColumnsRef,
summaryFunc: unref(getMergeProps).summaryFunc,
summaryFunc: unref(getProps).summaryFunc,
dataSourceRef: getDataSourceRef,
rowSelectionRef: getRowSelectionRef,
});
......@@ -182,17 +190,17 @@
});
const getFormProps = computed(() => {
const { formConfig } = unref(getBindValues);
const formProps: FormProps = {
const { formConfig } = unref(getProps);
const formProps: Partial<FormProps> = {
showAdvancedButton: true,
...(formConfig as FormProps),
...formConfig,
compact: true,
};
return formProps;
});
const getEmptyDataIsShowTable = computed(() => {
const { emptyDataIsShowTable, useSearchForm } = unref(getMergeProps);
const { emptyDataIsShowTable, useSearchForm } = unref(getProps);
if (emptyDataIsShowTable || !useSearchForm) {
return true;
}
......@@ -207,17 +215,8 @@
{ immediate: true }
);
function getRowClassName(record: TableCustomRecord, index: number) {
const { striped, rowClassName } = unref(getMergeProps);
if (!striped) return;
if (rowClassName && isFunction(rowClassName)) {
return rowClassName(record);
}
return (index || 0) % 2 === 1 ? 'basic-table-row__striped' : '';
}
function handleSearchInfoChange(info: any) {
const { handleSearchInfoFn } = unref(getMergeProps);
const { handleSearchInfoFn } = unref(getProps);
if (handleSearchInfoFn && isFunction(handleSearchInfoFn)) {
info = handleSearchInfoFn(info) || info;
}
......@@ -230,7 +229,7 @@
filters: Partial<Recordable<string[]>>,
sorter: SorterResult
) {
const { clearSelectOnPageChange, sortFn } = unref(getMergeProps);
const { clearSelectOnPageChange, sortFn } = unref(getProps);
if (clearSelectOnPageChange) {
clearSelectedRowKeys();
}
......@@ -245,7 +244,7 @@
}
function handleSummary() {
if (unref(getMergeProps).showSummary) {
if (unref(getProps).showSummary) {
nextTick(() => {
const tableEl = unref(tableElRef);
if (!tableEl) return;
......@@ -273,9 +272,7 @@
}
const tableAction: TableActionType = {
reload: async (opt?: FetchParams) => {
await fetch(opt);
},
reload,
getSelectRows,
clearSelectedRowKeys,
getSelectRowKeys,
......@@ -285,27 +282,11 @@
redoHeight,
setSelectedRowKeys,
setColumns,
getPaginationRef: () => {
return unref(getPaginationRef);
},
getColumns: (opt?: GetColumnsParams) => {
const { ignoreIndex, ignoreAction } = opt || {};
let columns = toRaw(unref(getColumnsRef));
if (ignoreIndex) {
columns = columns.filter((item) => item.flag !== 'INDEX');
}
if (ignoreAction) {
columns = columns.filter((item) => item.flag !== 'ACTION');
}
return columns;
},
getDataSource: () => {
return unref(getDataSourceRef);
},
setLoading: (loading: boolean) => {
loadingRef.value = loading;
},
setLoading,
getDataSource,
setProps,
getPaginationRef: getPagination,
getColumns,
getSize: () => {
return unref(getBindValues).size as SizeType;
},
......@@ -323,7 +304,7 @@
return {
tableElRef,
getBindValues,
loading: loadingRef,
getLoading,
registerForm,
handleSearchInfoChange,
getFormProps,
......
......@@ -31,3 +31,9 @@ export function DEFAULT_SORT_FN(sortInfo: SorterResult) {
order,
};
}
// 表格单元格默认布局
export const DEFAULT_ALIGN = 'center';
export const INDEX_COLUMN_FLAG = 'INDEX';
export const ACTION_COLUMN_FLAG = 'ACTION';
import { BasicColumn, BasicTableProps } from '../types/table';
import { BasicColumn, BasicTableProps, GetColumnsParams } from '../types/table';
import { PaginationProps } from '../types/pagination';
import { unref, ComputedRef, Ref, computed, watchEffect, ref, toRaw } from 'vue';
import { isBoolean, isArray, isObject } from '/@/utils/is';
import { PAGE_SIZE } from '../const';
import { useProps } from './useProps';
import { DEFAULT_ALIGN, PAGE_SIZE, INDEX_COLUMN_FLAG, ACTION_COLUMN_FLAG } from '../const';
import { useI18n } from '/@/hooks/web/useI18n';
const { t } = useI18n();
function handleItem(item: BasicColumn, ellipsis: boolean) {
const { key, dataIndex, children } = item;
item.align = item.align || DEFAULT_ALIGN;
if (ellipsis) {
if (!key) {
item.key = dataIndex;
}
if (!isBoolean(item.ellipsis)) {
Object.assign(item, {
ellipsis,
});
}
}
if (children && children.length) {
handleChildren(children, !!ellipsis);
}
}
function handleChildren(children: BasicColumn[] | undefined, ellipsis: boolean) {
if (!children) return;
children.forEach((item) => {
const { children } = item;
handleItem(item, ellipsis);
handleChildren(children, ellipsis);
});
}
function handleIndexColumn(
propsRef: ComputedRef<BasicTableProps>,
getPaginationRef: ComputedRef<boolean | PaginationProps>,
columns: BasicColumn[]
) {
const { showIndexColumn, indexColumnProps, ellipsis } = unref(propsRef);
let pushIndexColumns = false;
columns.forEach((item) => {
const { children } = item;
handleItem(item, !!ellipsis);
const isTreeTable = children && children.length;
const indIndex = columns.findIndex((column) => column.flag === INDEX_COLUMN_FLAG);
if (showIndexColumn && !isTreeTable) {
pushIndexColumns = indIndex === -1;
} else if (!showIndexColumn && !isTreeTable && indIndex !== -1) {
columns.splice(indIndex, 1);
}
});
if (!pushIndexColumns) return;
const isFixedLeft = columns.some((item) => item.fixed === 'left');
columns.unshift({
flag: INDEX_COLUMN_FLAG,
width: 50,
title: t('component.table.index'),
align: 'center',
customRender: ({ index }) => {
const getPagination = unref(getPaginationRef);
if (isBoolean(getPagination)) {
return `${index + 1}`;
}
const { current = 1, pageSize = PAGE_SIZE } = getPagination;
const currentIndex = (current - 1) * pageSize + index + 1;
return currentIndex;
},
...(isFixedLeft
? {
fixed: 'left',
}
: {}),
...indexColumnProps,
});
}
function handleActionColumn(propsRef: ComputedRef<BasicTableProps>, columns: BasicColumn[]) {
const { actionColumn } = unref(propsRef);
if (!actionColumn) return;
const hasIndex = columns.findIndex((column) => column.flag === ACTION_COLUMN_FLAG);
if (hasIndex === -1) {
columns.push({
...columns[hasIndex],
fixed: 'right',
...actionColumn,
flag: ACTION_COLUMN_FLAG,
});
}
}
export function useColumns(
refProps: ComputedRef<BasicTableProps>,
getPaginationRef: ComputedRef<false | PaginationProps>
propsRef: ComputedRef<BasicTableProps>,
getPaginationRef: ComputedRef<boolean | PaginationProps>
) {
const { propsRef } = useProps(refProps);
const columnsRef = (ref(unref(propsRef).columns) as unknown) as Ref<BasicColumn[]>;
const cacheColumnsRef = (ref(unref(propsRef).columns) as unknown) as Ref<BasicColumn[]>;
let cacheColumns = unref(propsRef).columns;
const getColumnsRef = computed(() => {
const props = unref(propsRef);
const { showIndexColumn, indexColumnProps, ellipsis, actionColumn, isTreeTable } = props;
const columns = unref(columnsRef);
if (!columns) {
return [];
}
let pushIndexColumns = false;
columns.forEach((item) => {
const { children } = item;
handleItem(item, !!ellipsis);
handleChildren(children, !!ellipsis);
const indIndex = columns.findIndex((column) => column.flag === 'INDEX');
if (showIndexColumn && !isTreeTable) {
pushIndexColumns = indIndex === -1;
} else if (!showIndexColumn && !isTreeTable && indIndex !== -1) {
columns.splice(indIndex, 1);
}
});
if (pushIndexColumns) {
const isFixedLeft = columns.some((item) => item.fixed === 'left');
columns.unshift({
flag: 'INDEX',
width: 50,
title: t('component.table.index'),
align: 'center',
customRender: ({ index }) => {
const getPagination = unref(getPaginationRef);
if (isBoolean(getPagination)) {
return `${index + 1}`;
}
const { current = 1, pageSize = PAGE_SIZE } = getPagination;
const currentIndex = (current - 1) * pageSize + index + 1;
return currentIndex;
},
...(isFixedLeft
? {
fixed: 'left',
}
: {}),
...indexColumnProps,
});
}
if (actionColumn) {
const hasIndex = columns.findIndex((column) => column.flag === 'ACTION');
if (hasIndex === -1) {
columns.push({
...columns[hasIndex],
fixed: 'right',
...actionColumn,
flag: 'ACTION',
});
}
}
handleIndexColumn(propsRef, getPaginationRef, columns);
handleActionColumn(propsRef, columns);
return columns;
});
watchEffect(() => {
const columns = toRaw(unref(propsRef).columns);
columnsRef.value = columns;
cacheColumnsRef.value = columns;
cacheColumns = columns;
});
function handleItem(item: BasicColumn, ellipsis: boolean) {
const { key, dataIndex } = item;
item.align = item.align || 'center';
if (ellipsis) {
if (!key) {
item.key = dataIndex;
}
if (!isBoolean(item.ellipsis)) {
Object.assign(item, {
ellipsis,
});
}
}
}
function handleChildren(children: BasicColumn[] | undefined, ellipsis: boolean) {
if (!children) return;
children.forEach((item) => {
const { children } = item;
handleItem(item, ellipsis);
handleChildren(children, ellipsis);
});
}
function setColumns(columns: BasicColumn[] | string[]) {
/**
* set columns
* @param columns key|column
*/
function setColumns(columns: Partial<BasicColumn>[] | string[]) {
if (!isArray(columns)) return;
if (columns.length <= 0) {
......@@ -116,15 +136,30 @@ export function useColumns(
}
const firstColumn = columns[0];
if (isObject(firstColumn)) {
columnsRef.value = columns as any;
columnsRef.value = columns as BasicColumn[];
} else {
const newColumns = unref(cacheColumnsRef).filter((item) =>
(columns as string[]).includes(`${item.key}`! || item.dataIndex!)
const newColumns = cacheColumns.filter(
(item) =>
(item.dataIndex || `${item.key}`) &&
(columns as string[]).includes(`${item.key}`! || item.dataIndex!)
);
columnsRef.value = newColumns;
}
}
return { getColumnsRef, setColumns };
function getColumns(opt?: GetColumnsParams) {
const { ignoreIndex, ignoreAction } = opt || {};
let columns = toRaw(unref(getColumnsRef));
if (ignoreIndex) {
columns = columns.filter((item) => item.flag !== INDEX_COLUMN_FLAG);
}
if (ignoreAction) {
columns = columns.filter((item) => item.flag !== ACTION_COLUMN_FLAG);
}
return columns;
}
return { getColumnsRef, getColumns, setColumns };
}
import type { ComputedRef } from 'vue';
import type { BasicTableProps } from '../types/table';
import { unref } from 'vue';
import { ROW_KEY } from '../const';
import { isString, isFunction } from '/@/utils/is';
interface Options {
setSelectedRowKeys: (keys: string[]) => void;
getSelectRowKeys: () => string[];
clearSelectedRowKeys: () => void;
emit: EmitType;
getAutoCreateKey: ComputedRef<boolean | undefined>;
}
function getKey(
record: Recordable,
rowKey: string | ((record: Record<string, any>) => string) | undefined,
autoCreateKey?: boolean
) {
if (!rowKey || autoCreateKey) {
return record[ROW_KEY];
}
if (isString(rowKey)) {
return record[rowKey];
}
if (isFunction(rowKey)) {
return record[rowKey(record)];
}
return null;
}
export function useCustomRow(
propsRef: ComputedRef<BasicTableProps>,
{ setSelectedRowKeys, getSelectRowKeys, getAutoCreateKey, clearSelectedRowKeys, emit }: Options
) {
const customRow = (record: Recordable, index: number) => {
return {
onClick: (e: Event) => {
emit('row-click', record, index, e);
e?.stopPropagation();
const { rowSelection, rowKey, clickToRowSelect } = unref(propsRef);
if (!rowSelection || !clickToRowSelect) return;
const keys = getSelectRowKeys();
const key = getKey(record, rowKey, unref(getAutoCreateKey));
if (!key) return;
const isCheckbox = rowSelection.type === 'checkbox';
if (isCheckbox) {
if (!keys.includes(key)) {
setSelectedRowKeys([...keys, key]);
return;
}
const keyIndex = keys.findIndex((item) => item === key);
keys.splice(keyIndex, 1);
setSelectedRowKeys(keys);
return;
}
const isRadio = rowSelection.type === 'radio';
if (isRadio) {
if (!keys.includes(key)) {
if (keys.length) {
clearSelectedRowKeys();
}
setSelectedRowKeys([key]);
return;
}
clearSelectedRowKeys();
}
},
onDblclick: (event: Event) => {
emit('row-dbClick', record, index, event);
},
onContextmenu: (event: Event) => {
emit('row-contextmenu', record, index, event);
},
onMouseenter: (event: Event) => {
emit('row-mouseenter', record, index, event);
},
onMouseleave: (event: Event) => {
emit('row-mouseleave', record, index, event);
},
};
};
return {
customRow,
};
}
import type { BasicTableProps, FetchParams } from '../types/table';
import type { PaginationProps } from '../types/pagination';
import { watch, ref, unref, ComputedRef, computed, onMounted, Ref } from 'vue';
import { ref, unref, ComputedRef, computed, onMounted, watchEffect } from 'vue';
import { useTimeoutFn } from '/@/hooks/core/useTimeout';
......@@ -9,39 +9,28 @@ import { buildUUID } from '/@/utils/uuid';
import { isFunction, isBoolean } from '/@/utils/is';
import { get } from 'lodash-es';
import { useProps } from './useProps';
import { FETCH_SETTING, ROW_KEY, PAGE_SIZE } from '../const';
import { FETCH_SETTING, ROW_KEY } from '../const';
interface ActionType {
getPaginationRef: ComputedRef<false | PaginationProps>;
getPaginationInfo: ComputedRef<boolean | PaginationProps>;
setPagination: (info: Partial<PaginationProps>) => void;
loadingRef: Ref<boolean | undefined>;
getFieldsValue: () => {
[field: string]: any;
};
setLoading: (loading: boolean) => void;
getFieldsValue: () => Recordable;
}
export function useDataSource(
refProps: ComputedRef<BasicTableProps>,
{ getPaginationRef, setPagination, loadingRef, getFieldsValue }: ActionType,
propsRef: ComputedRef<BasicTableProps>,
{ getPaginationInfo, setPagination, setLoading, getFieldsValue }: ActionType,
emit: EmitType
) {
const { propsRef } = useProps(refProps);
const dataSourceRef = ref<any[]>([]);
const dataSourceRef = ref<Recordable[]>([]);
watch(
() => unref(propsRef).dataSource,
(data: any[]) => {
const { api } = unref(propsRef);
!api && (dataSourceRef.value = data);
},
{ immediate: true }
);
watchEffect(() => {
const { dataSource, api } = unref(propsRef);
!api && dataSource && (dataSourceRef.value = dataSource);
});
function setTableKey(items: any[]) {
if (!items || !Array.isArray(items)) {
return;
}
if (!items || !Array.isArray(items)) return;
items.forEach((item) => {
if (!item[ROW_KEY]) {
item[ROW_KEY] = buildUUID();
......@@ -51,10 +40,16 @@ export function useDataSource(
}
});
}
const getAutoCreateKey = computed(() => {
return unref(propsRef).autoCreateKey && !unref(propsRef).rowKey;
});
const getRowKey = computed(() => {
const { rowKey } = unref(propsRef);
return unref(getAutoCreateKey) ? ROW_KEY : rowKey;
});
const getDataSourceRef = computed(() => {
const dataSource = unref(dataSourceRef);
if (!dataSource || dataSource.length === 0) {
......@@ -86,20 +81,20 @@ export function useDataSource(
);
if (!api || !isFunction(api)) return;
try {
loadingRef.value = true;
setLoading(true);
const { pageField, sizeField, listField, totalField } = fetchSetting || FETCH_SETTING;
let pageParams: any = {};
const { current, pageSize } = unref(getPaginationRef) as PaginationProps;
if (isBoolean(getPaginationRef)) {
let pageParams: Recordable = {};
const { current = 1, pageSize = PAGE_SIZE } = unref(getPaginationInfo) as PaginationProps;
if (isBoolean(getPaginationInfo)) {
pageParams = {};
} else {
pageParams[pageField] = (opt && opt.page) || current;
pageParams[sizeField] = pageSize;
}
let params: any = {
let params: Recordable = {
...pageParams,
...(useSearchForm ? getFieldsValue() : {}),
...searchInfo,
......@@ -112,18 +107,21 @@ export function useDataSource(
}
const res = await api(params);
let resultItems: any[] = get(res, listField);
const resultTotal: number = get(res, totalField);
const isArrayResult = Array.isArray(res);
let resultItems: Recordable[] = isArrayResult ? res : get(res, listField);
const resultTotal: number = isArrayResult ? 0 : get(res, totalField);
// 假如数据变少,导致总页数变少并小于当前选中页码,通过getPaginationRef获取到的页码是不正确的,需获取正确的页码再次执行
var currentTotalPage = Math.ceil(resultTotal / pageSize);
const currentTotalPage = Math.ceil(resultTotal / pageSize);
if (current > currentTotalPage) {
setPagination({
current: currentTotalPage,
});
fetch(opt);
setPagination({
current: currentTotalPage,
});
fetch(opt);
}
if (afterFetch && isFunction(afterFetch)) {
resultItems = afterFetch(resultItems) || resultItems;
}
......@@ -147,20 +145,35 @@ export function useDataSource(
total: 0,
});
} finally {
loadingRef.value = false;
// setSearchFormLoading(false);
setLoading(false);
}
}
function setTableData(values: any[]) {
function setTableData<T = Recordable>(values: T[]) {
dataSourceRef.value = values;
}
function getDataSource<T = Recordable>() {
return getDataSourceRef.value as T[];
}
async function reload(opt?: FetchParams) {
await fetch(opt);
}
onMounted(() => {
// 转异步任务
useTimeoutFn(() => {
unref(propsRef).immediate && fetch();
}, 0);
});
return { getDataSourceRef, setTableData, getAutoCreateKey, fetch: fetch };
return {
getDataSourceRef,
getDataSource,
getRowKey,
setTableData,
getAutoCreateKey,
fetch,
reload,
};
}
import { watch, ref, ComputedRef, unref } from 'vue';
import { BasicTableProps } from '../types/table';
import { useProps } from './useProps';
export function useLoading(refProps: ComputedRef<BasicTableProps>) {
const { propsRef } = useProps(refProps);
const loadingRef = ref(unref(propsRef).loading);
watch(
() => unref(propsRef).loading,
(v: boolean) => {
loadingRef.value = v;
}
);
return { loadingRef };
import { ref, ComputedRef, unref, computed, watchEffect } from 'vue';
import type { BasicTableProps } from '../types/table';
export function useLoading(props: ComputedRef<BasicTableProps>) {
const loadingRef = ref(unref(props).loading);
watchEffect(() => {
loadingRef.value = unref(props).loading;
});
const getLoading = computed(() => {
return unref(loadingRef);
});
function setLoading(loading: boolean) {
loadingRef.value = loading;
}
return { getLoading, setLoading };
}
......@@ -7,16 +7,30 @@ import { LeftOutlined, RightOutlined } from '@ant-design/icons-vue';
import { isBoolean } from '/@/utils/is';
import { PAGE_SIZE, PAGE_SIZE_OPTIONS } from '../const';
import { useProps } from './useProps';
import { useI18n } from '/@/hooks/web/useI18n';
const { t } = useI18n();
interface ItemRender {
page: number;
type: 'page' | 'prev' | 'next';
originalElement: any;
}
function itemRender({ page, type, originalElement }: ItemRender) {
if (type === 'prev') {
return page === 0 ? null : <LeftOutlined />;
} else if (type === 'next') {
return page === 1 ? null : <RightOutlined />;
}
return originalElement;
}
export function usePagination(refProps: ComputedRef<BasicTableProps>) {
const configRef = ref<PaginationProps>({});
const { propsRef } = useProps(refProps);
const getPaginationRef = computed((): PaginationProps | false => {
const { pagination } = unref(propsRef);
const { t } = useI18n();
const getPaginationInfo = computed((): PaginationProps | boolean => {
const { pagination } = unref(refProps);
if (isBoolean(pagination) && !pagination) {
return false;
}
......@@ -28,20 +42,7 @@ export function usePagination(refProps: ComputedRef<BasicTableProps>) {
showTotal: (total) => t('component.table.total', { total }),
showSizeChanger: true,
pageSizeOptions: PAGE_SIZE_OPTIONS,
itemRender: ({ page, type, originalElement }) => {
if (type === 'prev') {
if (page === 0) {
return null;
}
return <LeftOutlined />;
} else if (type === 'next') {
if (page === 1) {
return null;
}
return <RightOutlined />;
}
return originalElement;
},
itemRender: itemRender,
showQuickJumper: true,
...(isBoolean(pagination) ? {} : pagination),
...unref(configRef),
......@@ -49,10 +50,15 @@ export function usePagination(refProps: ComputedRef<BasicTableProps>) {
});
function setPagination(info: Partial<PaginationProps>) {
const paginationInfo = unref(getPaginationInfo);
configRef.value = {
...unref(getPaginationRef),
...(!isBoolean(paginationInfo) ? paginationInfo : {}),
...info,
};
}
return { getPaginationRef, setPagination };
function getPagination() {
return unref(getPaginationInfo);
}
return { getPagination, getPaginationInfo, setPagination };
}
import { Ref, ref, watch, unref } from 'vue';
import type { BasicTableProps } from '../types/table';
/**
* @description:
* @Date: 2020-05-12 13:20:37
*/
export function useProps(props: Readonly<Ref<BasicTableProps>>) {
const propsRef = (ref<BasicTableProps>(unref(props)) as unknown) as Ref<BasicTableProps>;
watch(
() => props.value,
(v) => {
propsRef.value = unref(v);
},
{
immediate: false,
}
);
return { propsRef };
}
import type { BasicTableProps, TableRowSelection } from '../types/table';
import { computed, ref, unref, ComputedRef } from 'vue';
import { useProps } from './useProps';
/* eslint-disable */
export function useRowSelection(refProps: ComputedRef<BasicTableProps>, emit: EmitType) {
const { propsRef } = useProps(refProps);
export function useRowSelection(propsRef: ComputedRef<BasicTableProps>, emit: EmitType) {
const selectedRowKeysRef = ref<string[]>([]);
const selectedRowRef = ref<any[]>([]);
const selectedRowRef = ref<Recordable[]>([]);
const getRowSelectionRef = computed((): TableRowSelection | null => {
const rowSelection = unref(propsRef).rowSelection;
const { rowSelection } = unref(propsRef);
if (!rowSelection) {
return null;
}
......@@ -46,11 +43,14 @@ export function useRowSelection(refProps: ComputedRef<BasicTableProps>, emit: Em
unref(selectedRowKeysRef).splice(index, 1);
}
}
function getSelectRowKeys() {
return unref(selectedRowKeysRef);
}
function getSelectRows() {
return unref(selectedRowRef);
function getSelectRows<T = Recordable>() {
// const ret = toRaw(unref(selectedRowRef)).map((item) => toRaw(item));
return unref(selectedRowRef) as T[];
}
return {
......
......@@ -14,10 +14,11 @@ export function useTable(
const loadedRef = ref<Nullable<boolean>>(false);
function register(instance: TableActionType) {
onUnmounted(() => {
tableRef.value = null;
loadedRef.value = null;
});
isProdMode() &&
onUnmounted(() => {
tableRef.value = null;
loadedRef.value = null;
});
if (unref(loadedRef) && isProdMode() && instance === unref(tableRef)) {
return;
......
import type { BasicTableProps } from '../types/table';
import { computed, Ref, onMounted, unref, ref, nextTick, ComputedRef, watch } from 'vue';
import type { Ref, ComputedRef } from 'vue';
import { computed, unref, ref, nextTick, watchEffect } from 'vue';
import { getViewportOffset } from '/@/utils/domUtils';
import { isBoolean } from '/@/utils/is';
import { useWindowSizeFn } from '/@/hooks/event/useWindowSizeFn';
import { useProps } from './useProps';
import { useModalContext } from '/@/components/Modal';
export function useTableScroll(refProps: ComputedRef<BasicTableProps>, tableElRef: Ref<any>) {
const { propsRef } = useProps(refProps);
const tableHeightRef: Ref<number | null> = ref(null);
export function useTableScroll(
propsRef: ComputedRef<BasicTableProps>,
tableElRef: Ref<ComponentRef>
) {
const tableHeightRef: Ref<Nullable<number>> = ref(null);
const modalFn = useModalContext();
watch(
() => unref(propsRef).canResize,
() => {
redoHeight();
}
);
const getCanResize = computed(() => {
const { canResize, scroll } = unref(propsRef);
return canResize && !(scroll || {}).y;
});
function redoHeight() {
const { canResize } = unref(propsRef);
watchEffect(() => {
redoHeight();
});
if (!canResize) return;
calcTableHeight();
function redoHeight() {
if (unref(getCanResize)) {
nextTick(() => {
calcTableHeight();
});
}
}
// No need to repeat queries
let paginationEl: HTMLElement | null;
let footerEl: HTMLElement | null;
async function calcTableHeight() {
const { canResize, resizeHeightOffset, pagination, maxHeight } = unref(propsRef);
if (!canResize) return;
const { resizeHeightOffset, pagination, maxHeight } = unref(propsRef);
if (!unref(getCanResize)) return;
await nextTick();
const table = unref(tableElRef) as any;
const table = unref(tableElRef);
if (!table) return;
const tableEl: Element = table.$el;
if (!tableEl) return;
const headEl = tableEl.querySelector('.ant-table-thead ');
if (!headEl) return;
// 表格距离底部高度
// Table height from bottom
const { bottomIncludeBody } = getViewportOffset(headEl);
// 表格高度+距离底部高度-自定义偏移量
// Table height from bottom height-custom offset
const paddingHeight = 32;
const borderHeight = 2 * 2;
// 分页器高度
// Pager height
let paginationHeight = 2;
if (!isBoolean(pagination)) {
if (!paginationEl) {
......@@ -61,7 +67,7 @@ export function useTableScroll(refProps: ComputedRef<BasicTableProps>, tableElRe
const offsetHeight = paginationEl.offsetHeight;
paginationHeight += offsetHeight || 0;
} else {
// TODO 先固定24
// TODO First fix 24
paginationHeight += 24;
}
}
......@@ -75,11 +81,13 @@ export function useTableScroll(refProps: ComputedRef<BasicTableProps>, tableElRe
footerHeight += offsetHeight || 0;
}
}
let headerHeight = 0;
if (headEl) {
headerHeight = (headEl as HTMLElement).offsetHeight;
}
tableHeightRef.value =
const height =
bottomIncludeBody -
(resizeHeightOffset || 0) -
paddingHeight -
......@@ -89,27 +97,14 @@ export function useTableScroll(refProps: ComputedRef<BasicTableProps>, tableElRe
headerHeight;
setTimeout(() => {
tableHeightRef.value =
tableHeightRef.value! > maxHeight! ? (maxHeight as number) : tableHeightRef.value;
// 解决表格放modal内的时候,modal自适应高度计算问题
tableHeightRef.value = (height > maxHeight! ? (maxHeight as number) : height) ?? height;
// Solve the problem of modal adaptive height calculation when the form is placed in the modal
modalFn?.redoModalHeight?.();
}, 16);
}, 0);
}
const getCanResize = computed(() => {
const { canResize, scroll } = unref(propsRef);
return canResize && !(scroll || {}).y;
});
useWindowSizeFn(calcTableHeight, 100);
onMounted(() => {
if (unref(getCanResize)) {
nextTick(() => {
calcTableHeight();
});
}
});
const getScrollRef = computed(() => {
const tableHeight = unref(tableHeightRef);
const { canResize, scroll } = unref(propsRef);
......@@ -121,5 +116,6 @@ export function useTableScroll(refProps: ComputedRef<BasicTableProps>, tableElRe
...scroll,
};
});
return { getScrollRef, redoHeight };
}
import type { ComputedRef } from 'vue';
import type { BasicTableProps, TableCustomRecord } from '../types/table';
import { unref } from 'vue';
import { isFunction } from '/@/utils/is';
export function useTableStyle(propsRef: ComputedRef<BasicTableProps>) {
function getRowClassName(record: TableCustomRecord, index: number) {
const { striped, rowClassName } = unref(propsRef);
if (!striped) return;
if (rowClassName && isFunction(rowClassName)) {
return rowClassName(record);
}
return (index || 0) % 2 === 1 ? 'basic-table-row__striped' : '';
}
return {
getRowClassName,
};
}
......@@ -14,6 +14,7 @@ import { propTypes } from '/@/utils/propTypes';
// 注释看 types/table
export const basicProps = {
clickToRowSelect: propTypes.bool.def(true),
tableSetting: {
type: Object as PropType<TableSetting>,
},
......@@ -34,7 +35,6 @@ export const basicProps = {
},
canColDrag: propTypes.bool.def(true),
isTreeTable: propTypes.bool,
api: {
type: Function as PropType<(...arg: any[]) => Promise<any>>,
default: null,
......
......@@ -167,20 +167,6 @@
}
}
.ant-radio {
&-inner {
border-color: @text-color-base;
}
}
.ant-checkbox {
&:not(.ant-checkbox-checked) {
.ant-checkbox-inner {
border-color: @text-color-base;
}
}
}
.ant-table-bordered .ant-table-thead > tr:not(:last-child) > th,
.ant-table-tbody > tr > td {
word-break: break-word;
......
......@@ -124,6 +124,8 @@ export interface TableSetting {
}
export interface BasicTableProps<T = any> {
// 点击行选中
clickToRowSelect?: boolean;
// 自定义排序方法
sortFn?: (sortInfo: SorterResult) => any;
// 取消表格的默认padding
......@@ -141,8 +143,6 @@ export interface BasicTableProps<T = any> {
showSummary?: boolean;
// 是否可拖拽列
canColDrag?: boolean;
// 是否树表
isTreeTable?: boolean;
// 接口请求对象
api?: (...arg: any) => Promise<any>;
// 请求之前处理参数
......@@ -158,7 +158,7 @@ export interface BasicTableProps<T = any> {
// 在开起搜索表单的时候,如果没有数据是否显示表格
emptyDataIsShowTable?: boolean;
// 额外的请求参数
searchInfo?: any;
searchInfo?: Recordable;
// 使用搜索表单
useSearchForm?: boolean;
// 表单配置
......@@ -180,9 +180,9 @@ export interface BasicTableProps<T = any> {
// 在分页改变的时候清空选项
clearSelectOnPageChange?: boolean;
//
rowKey?: string | ((record: any) => string);
rowKey?: string | ((record: Recordable) => string);
// 数据
dataSource?: any[];
dataSource?: Recordable[];
// 标题右侧提示
titleHelpMessage?: string | string[];
// 表格滚动最大高度
......
......@@ -9,7 +9,8 @@
:loading="loading"
:striped="striped"
:bordered="border"
:pagination="{ pageSize: 20 }"
showTableSetting
:pagination="pagination"
>
<template #toolbar>
<a-button type="primary" @click="toggleCanResize">
......@@ -38,6 +39,7 @@
const loading = ref(false);
const striped = ref(true);
const border = ref(true);
const pagination = ref<any>(false);
function toggleCanResize() {
canResize.value = !canResize.value;
}
......@@ -48,6 +50,7 @@
loading.value = true;
setTimeout(() => {
loading.value = false;
pagination.value = { pageSize: 20 };
}, 3000);
}
function toggleBorder() {
......@@ -64,6 +67,7 @@
toggleCanResize,
toggleLoading,
toggleBorder,
pagination,
};
},
});
......
......@@ -74,10 +74,10 @@
slots: { customRender: 'action' },
},
});
function handleDelete(record: any) {
function handleDelete(record: Recordable) {
console.log('点击了删除', record);
}
function handleOpen(record: any) {
function handleOpen(record: Recordable) {
console.log('点击了启用', record);
}
return {
......
......@@ -2,7 +2,6 @@
<div class="p-4">
<BasicTable
:rowSelection="{ type: 'checkbox' }"
:isTreeTable="true"
title="树形表格"
titleHelpMessage="树形组件不能和序列号列同时存在"
:columns="columns"
......
......@@ -44,12 +44,13 @@
clearSelectedRowKeys,
},
] = useTable({
canResize: false,
canResize: true,
title: 'useTable示例',
titleHelpMessage: '使用useTable调用表格内方法',
api: demoListApi,
columns: getBasicColumns(),
rowKey: 'id',
showTableSetting: true,
rowSelection: {
type: 'checkbox',
},
......
......@@ -5,13 +5,13 @@ export function getBasicColumns(): BasicColumn[] {
return [
{
title: 'ID',
width: 150,
dataIndex: 'id',
width: 150,
},
{
title: '姓名',
dataIndex: 'name',
width: 120,
width: 150,
},
{
title: '地址',
......@@ -20,14 +20,16 @@ export function getBasicColumns(): BasicColumn[] {
{
title: '编号',
dataIndex: 'no',
width: 80,
width: 150,
},
{
title: '开始时间',
width: 120,
dataIndex: 'beginTime',
},
{
title: '结束时间',
width: 120,
sorter: true,
dataIndex: 'endTime',
},
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册