logo

公共Hooks封装之表格数据useTableData

Oct 21, 2022 · 9 min

写在前面#

对于经常需要开发企业管理后台的前端开发来说,必不可少的需要使用表格对于数据进行操作,在对于现有项目进行代码优化时,封装一些公共的Hooks. 本篇文章为useTableData.js/useTableData.ts

基于个人项目环境进行封装的Hooks,仅以本文介绍封装Hooks思想心得,故相关代码可能不适用他人

项目环境#

Vue3.x + Ant Design Vue3.x + Vite3.x

封装分解:声明变量#

import { ref, shallowRef, isRef } from 'vue';

const loading = ref(false);        // 表格数据UI交互Loading
const tableData = shallowRef([]);  // 表格数据ShallowRef全响应式
const totalElements = ref(0);      // 总数据量totalElements

无论是使用过Ant Design Vue 还是Element UI 亦或是其他UI FrameWork 框架,表格里需要的必定有tableData、totalElements。

封装分解:请求接口#

以下代码仅解释封装思想

const getTableData = async() => {
  ***
  const { data ||
    data: { content, totalElements: total }
  } = await API(QueryParams)   // 请求接口,
  ***
  tableData.value = data ||
  data.content ||
  data.content.map(item => {***})  //解构data进行赋值
  totalElements.value = total
}

接口请求表格数据,最基础的”增删改查”之一,针对后端提供接口和相关代码产出规范约定俗成后,封装Hooks同时考虑,tableData是否能够直接由data解构的content赋值,或需要进行二次处理

封装分解:筛选查询 及重置查询#

const search = async () => {
  queryParams.value.pageIndex = 1; // 根据筛选想查询数据,重置页码为1,调用接口
  await getTableData();
};

const resetSearch = async () => {
  if (isFunction(options.resetParams)) {
    options.resetParams();       // 重置请求参数
    await getTableData();
  }
};

封装分解:获取分页器配置#

以下封装基于项目环境Ant Design Vue 3.x

const getPaginationOptions = () => {
  return {
    total: totalElements.value,
    current: queryParams.value.pageIndex,
    pageSize: queryParams.value.pageSize,
    showQuickJumper: true,
    showSizeChanger: true,
    showTotal: _total => `${_total} 条数据`,
  };
};

封装分解:表格change事件(分页、排序、筛选变化触发)#

以下封装基于项目环境Ant Design Vue 3.x

const onTableChange = async (page, _, sorter) => {   // page: 页码, sorter: 排序字段
  queryParams.value.pageIndex = page.current || 1;
  queryParams.value.pageSize = page.pageSize || 10;
  if (sorter && Object.keys(sorter).length > 0) {
    if (sorter.order) {
      queryParams.value.orderBy = sorter.field;      // asc \ desc
      queryParams.value.direction = sorter.order.slice(0, -3);
    } else {
      queryParams.value.orderBy = '';
      queryParams.value.direction = '';
    }
  }
  await getTableData();
};

useTableData.js完整代码#

import { ref, shallowRef, isRef } from 'vue';
import { isFunction } from 'lodash-es';

export function useTableData(apiInterface, queryParams, options = {}) {
  if (!isRef(queryParams)) throw new Error('queryParams 参数必修为 Ref 类型');
  const loading = ref(false);
  const tableData = shallowRef([]);
  const totalElements = ref(0);

  const getTableData = async () => {
    try {
      loading.value = true;
      const {
        data: { content, totalElements: total },
      } = await apiInterface(queryParams.value);
      if (isFunction(options.handleContent)) {
        tableData.value = content.map(options.handleContent);
      } else {
        tableData.value = content;
      }
      if (isFunction(options.callback)) {
        options.callback(content);
      }
      totalElements.value = total;
    } finally {
      loading.value = false;
    }
  };

  const onTableChange = async (page, _, sorter) => {
    queryParams.value.pageIndex = page.current || 1;
    queryParams.value.pageSize = page.pageSize || 10;
    if (sorter && Object.keys(sorter).length > 0) {
      if (sorter.order) {
        queryParams.value.orderBy = sorter.field;
        queryParams.value.direction = sorter.order.slice(0, -3);
      } else {
        queryParams.value.orderBy = '';
        queryParams.value.direction = '';
      }
    }
    await getTableData();
  };

  const search = async () => {
    queryParams.value.pageIndex = 1;
    await getTableData();
  };

  const resetSearch = async () => {
    if (isFunction(options.resetParams)) {
      options.resetParams();
      await getTableData();
    }
  };

  const getPaginationOptions = () => {
    return {
      total: totalElements.value,
      current: queryParams.value.pageIndex,
      pageSize: queryParams.value.pageSize,
      showQuickJumper: true,
      showSizeChanger: true,
      showTotal: _total => `${_total} 条数据`,
    };
  };

  return {
    loading,
    totalElements,
    tableData,
    search,
    resetSearch,
    getPaginationOptions,
    getTableData,
    onTableChange,
  };
}

后记 | useTableData.ts完整代码#

基于来自掘金的读者反馈,现更新ts版本的代码,以供使用,原文已说明相关封装思路~

import { ref, shallowRef, Ref } from 'vue';
import { isFunction } from 'lodash-es';
import { TableProps } from 'ant-design-vue';

interface Options {
  handleContent?(content: Record<string, unknown>[]): void;
  callback?(content: Record<string, unknown>[]): void;
  resetParams?(): void;
  contentKey?: string;
  totalElementsKey?: string;
  pageSizeOptions?: string[];
}

export function useTableData(apiInterface: Function, queryParams: Ref<Record<string, unknown>>, options?: Options) {
  const loading = ref<boolean>(false);
  const tableData = shallowRef<Record<string, unknown>[]>([]);
  const totalElements = ref<number>(0);

  const getTableData = async () => {
    try {
      loading.value = true;
      const { data } = await apiInterface(queryParams.value);
      const content = data[options?.contentKey ? options.contentKey : 'content'];
      if (isFunction(options?.handleContent)) {
        tableData.value = content.map(options?.handleContent);
      } else {
        tableData.value = content;
      }
      isFunction(options?.callback) && options?.callback(content);
      totalElements.value = options?.totalElementsKey ? data[options.totalElementsKey] : data.totalElements;
    } finally {
      loading.value = false;
    }
  };

  const onTableChange: TableProps['onChange'] = async (page, _, sorter) => {
    queryParams.value.pageIndex = page.current || 1;
    queryParams.value.pageSize = page.pageSize || 10;
    if (sorter && Object.keys(sorter).length > 0) {
      // @ts-ignore
      if (sorter.order) {
        // @ts-ignore
        queryParams.value.orderBy = sorter.field as string;
        // @ts-ignore
        queryParams.value.direction = sorter.order.slice(0, -3) as string;
      } else {
        queryParams.value.orderBy = '';
        queryParams.value.direction = '';
      }
    }
    await getTableData();
  };

  const search = async () => {
    queryParams.value.pageIndex = 1;
    await getTableData();
  };

  const resetSearch = async () => {
    if (isFunction(options?.resetParams)) {
      options?.resetParams();
      await getTableData();
    }
  };

  const getPaginationOptions = () => {
    return {
      total: totalElements.value,
      current: queryParams.value.pageIndex,
      pageSize: queryParams.value.pageSize,
      showQuickJumper: true,
      showSizeChanger: true,
      pageSizeOptions: options?.pageSizeOptions ? options.pageSizeOptions : ['10', '20', '30', '40'],
      showTotal: _total => `${_total} 条数据`,
    };
  };

  return {
    loading,
    totalElements,
    tableData,
    getTableData,
    onTableChange,
    search,
    resetSearch,
    getPaginationOptions,
  };
}
logo
> cd ..