use-table.ts 3.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132
  1. import { computed, ref } from 'vue';
  2. import type { Ref, VNodeChild } from 'vue';
  3. import useBoolean from './use-boolean';
  4. import useLoading from './use-loading';
  5. export interface PaginationData<T> {
  6. records: T[];
  7. pages: number;
  8. size: number;
  9. total: number;
  10. }
  11. type GetApiData<ApiData, Pagination extends boolean> = Pagination extends true ? PaginationData<ApiData> : ApiData[];
  12. type Transform<ResponseData, ApiData, Pagination extends boolean> = (
  13. response: ResponseData
  14. ) => GetApiData<ApiData, Pagination>;
  15. export type TableColumnCheckTitle = string | ((...args: any) => VNodeChild);
  16. export type TableColumnCheck = {
  17. key: string;
  18. title: TableColumnCheckTitle;
  19. checked: boolean;
  20. visible: boolean;
  21. };
  22. export interface UseTableOptions<ResponseData, ApiData, Column, Pagination extends boolean> {
  23. /**
  24. * api function to get table data
  25. */
  26. api: () => Promise<ResponseData>;
  27. /**
  28. * whether to enable pagination
  29. */
  30. pagination?: Pagination;
  31. /**
  32. * transform api response to table data
  33. */
  34. transform: Transform<ResponseData, ApiData, Pagination>;
  35. /**
  36. * columns factory
  37. */
  38. columns: () => Column[];
  39. /**
  40. * get column checks
  41. */
  42. getColumnChecks: (columns: Column[]) => TableColumnCheck[];
  43. /**
  44. * get columns
  45. */
  46. getColumns: (columns: Column[], checks: TableColumnCheck[]) => Column[];
  47. /**
  48. * callback when response fetched
  49. */
  50. onFetched?: (data: GetApiData<ApiData, Pagination>) => void | Promise<void>;
  51. /**
  52. * whether to get data immediately
  53. *
  54. * @default true
  55. */
  56. immediate?: boolean;
  57. }
  58. export default function useTable<ResponseData, ApiData, Column, Pagination extends boolean>(
  59. options: UseTableOptions<ResponseData, ApiData, Column, Pagination>
  60. ) {
  61. const { loading, startLoading, endLoading } = useLoading();
  62. const { bool: empty, setBool: setEmpty } = useBoolean();
  63. const { api, pagination, transform, columns, getColumnChecks, getColumns, onFetched, immediate = true } = options;
  64. const data = ref([]) as Ref<ApiData[]>;
  65. const columnChecks = ref(getColumnChecks(columns())) as Ref<TableColumnCheck[]>;
  66. const $columns = computed(() => getColumns(columns(), columnChecks.value));
  67. function reloadColumns() {
  68. const checkMap = new Map(columnChecks.value.map(col => [col.key, col.checked]));
  69. const defaultChecks = getColumnChecks(columns());
  70. columnChecks.value = defaultChecks.map(col => ({
  71. ...col,
  72. checked: checkMap.get(col.key) ?? col.checked
  73. }));
  74. }
  75. async function getData() {
  76. try {
  77. startLoading();
  78. const response = await api();
  79. const transformed = transform(response);
  80. data.value = getTableData(transformed, pagination);
  81. setEmpty(data.value.length === 0);
  82. await onFetched?.(transformed);
  83. } finally {
  84. endLoading();
  85. }
  86. }
  87. if (immediate) {
  88. getData();
  89. }
  90. return {
  91. loading,
  92. empty,
  93. data,
  94. columns: $columns,
  95. columnChecks,
  96. reloadColumns,
  97. getData
  98. };
  99. }
  100. function getTableData<ApiData, Pagination extends boolean>(
  101. data: GetApiData<ApiData, Pagination>,
  102. pagination?: Pagination
  103. ) {
  104. if (pagination) {
  105. return (data as PaginationData<ApiData>).records;
  106. }
  107. return data as ApiData[];
  108. }