@_lan/web-libs
Version:
<div align="center"> <img src="./public/favicon.svg" width="160" /> <h1>SoybeanAdmin AntDesign</h1> <span>中文 | <a href="./README.en_US.md">English</a></span> </div>
264 lines (220 loc) • 6.33 kB
text/typescript
import { computed, effectScope, onScopeDispose, reactive, ref, shallowRef, toValue, watch } from 'vue';
import type { MaybeRef, Ref } from 'vue';
import { useElementSize } from '@vueuse/core';
import type { TablePaginationConfig } from 'ant-design-vue';
import type { TableRowSelection } from 'ant-design-vue/es/table/interface';
import { useBoolean, useHookTable } from '@sa/hooks';
import { jsonClone } from '@sa/utils';
import { useAppStore } from '@/store/modules/app';
import { $t } from '@/locales';
type TableData = AntDesign.TableData;
type GetTableData<A extends AntDesign.TableApiFn> = AntDesign.GetTableData<A>;
type TableColumn<T> = AntDesign.TableColumn<T>;
export function useTable<A extends AntDesign.TableApiFn>(config: AntDesign.AntDesignTableConfig<A>) {
const scope = effectScope();
const appStore = useAppStore();
const { apiFn, apiParams, immediate } = config;
const {
loading,
empty,
data,
columns,
columnChecks,
reloadColumns,
getData,
searchParams,
updateSearchParams,
resetSearchParams
} = useHookTable<A, GetTableData<A>, TableColumn<AntDesign.TableDataWithIndex<GetTableData<A>>>>({
apiFn,
apiParams,
columns: config.columns,
transformer: res => {
const { records = [], current = 1, size = 10, total = 0 } = res.data || {};
// Ensure that the size is greater than 0, If it is less than 0, it will cause paging calculation errors.
const pageSize = size <= 0 ? 10 : size;
const recordsWithIndex = records.map((item, index) => {
return {
...item,
index: (current - 1) * pageSize + index + 1
};
});
return {
data: recordsWithIndex,
pageNum: current,
pageSize,
total
};
},
getColumnChecks: cols => {
const checks: AntDesign.TableColumnCheck[] = [];
cols.forEach(column => {
if (column.key) {
checks.push({
key: column.key as string,
title: column.title as string,
checked: true
});
}
});
return checks;
},
getColumns: (cols, checks) => {
const columnMap = new Map<string, TableColumn<GetTableData<A>>>();
cols.forEach(column => {
if (column.key) {
columnMap.set(column.key as string, column);
}
});
const filteredColumns = checks
.filter(item => item.checked)
.map(check => columnMap.get(check.key) as TableColumn<GetTableData<A>>);
return filteredColumns;
},
onFetched: async transformed => {
const { pageNum, pageSize, total } = transformed;
updatePagination({
current: pageNum,
pageSize,
total
});
},
immediate
});
const pagination: TablePaginationConfig = reactive({
current: 1,
pageSize: 10,
showSizeChanger: true,
pageSizeOptions: ['10', '15', '20', '25', '30'],
total: 0,
onChange: async (current: number, size: number) => {
pagination.current = current;
updateSearchParams({
current,
size
});
getData();
}
});
// this is for mobile, if the system does not support mobile, you can use `pagination` directly
const mobilePagination = computed(() => {
const p: TablePaginationConfig = {
...pagination,
simple: appStore.isMobile
};
return p;
});
function updatePagination(update: Partial<TablePaginationConfig>) {
Object.assign(pagination, update);
}
/**
* get data by page number
*
* @param pageNum the page number. default is 1
*/
async function getDataByPage(pageNum: number = 1) {
updatePagination({
current: pageNum
});
updateSearchParams({
current: pageNum,
size: pagination.pageSize!
});
await getData();
}
scope.run(() => {
watch(
() => appStore.locale,
() => {
reloadColumns();
}
);
});
onScopeDispose(() => {
scope.stop();
});
return {
loading,
empty,
data,
columns,
columnChecks,
reloadColumns,
pagination,
mobilePagination,
updatePagination,
getData,
getDataByPage,
searchParams,
updateSearchParams,
resetSearchParams
};
}
export function useTableOperate<T extends TableData = TableData>(data: Ref<T[]>, getData: () => Promise<void>) {
const { bool: drawerVisible, setTrue: openDrawer, setFalse: closeDrawer } = useBoolean();
const operateType = ref<AntDesign.TableOperateType>('add');
function handleAdd() {
operateType.value = 'add';
openDrawer();
}
/** the editing row data */
const editingData: Ref<T | null> = ref(null);
function handleEdit(id: T['id']) {
operateType.value = 'edit';
const findItem = data.value.find(item => item.id === id) || null;
editingData.value = jsonClone(findItem);
openDrawer();
}
/** the checked row keys of table */
const checkedRowKeys: Ref<T['id'][]> = ref([]);
function onSelectChange(keys: (string | number)[]) {
checkedRowKeys.value = keys as T['id'][];
}
const rowSelection = computed<TableRowSelection<T>>(() => {
return {
columnWidth: 48,
type: 'checkbox',
selectedRowKeys: checkedRowKeys.value,
onChange: onSelectChange
};
});
/** the hook after the batch delete operation is completed */
async function onBatchDeleted() {
window.$message?.success($t('common.deleteSuccess'));
checkedRowKeys.value = [];
await getData();
}
/** the hook after the delete operation is completed */
async function onDeleted() {
window.$message?.success($t('common.deleteSuccess'));
await getData();
}
return {
drawerVisible,
openDrawer,
closeDrawer,
operateType,
handleAdd,
editingData,
handleEdit,
checkedRowKeys,
onSelectChange,
rowSelection,
onBatchDeleted,
onDeleted
};
}
export function useTableScroll(scrollX: MaybeRef<number> = 702) {
const tableWrapperRef = shallowRef<HTMLElement | null>(null);
const { height: wrapperElHeight } = useElementSize(tableWrapperRef);
const scrollConfig = computed(() => {
return {
y: wrapperElHeight.value - 72,
x: toValue(scrollX)
};
});
return {
tableWrapperRef,
scrollConfig
};
}