基于vue3+Arco Design的table组件的hook二次封装

3,631 阅读2分钟

踩了一段时间arco的坑,总的来说arco的设计我还是比较喜欢的,组件库谈不上丰富,就将个烂就够用吧,最近在公司的后台管理生产环境中采用其集成框架, 预览地址 pro.arco.design/ ,table组件是后台管理的最最核心的组件,没有之一 arco table组件文档 arco.design/vue/compone…

这里面涉及到大量的table属性,方法绑定的复用,如果每个页面都去设置....那画面太美都不敢想象。遂封装之,这里依然是ts的操作,js是不可能再写了,这辈子都不可能再写了!

思路大概是通过vue3的hook函数(不得不说vue3+ts是很愉悦的开发体验,想起以前的mixin混入,浑身鸡皮疙瘩都要起,恐怖如斯!),将table的数据加载,分页请求,分页变换,显示属性做统一的封装,最大程度的降低单个在table上的代码量。

接下里看码:

// 核心的封装方法,详细参数看文档  https://arco.design/vue/component/table
// hook/table-props.ts

import { ref } from 'vue';
// @/types/global 这个自己根据类型定义一下就行了,这里不写
import { Pagination, PaginationRes } from '@/types/global';

type GetListFunc = <T>(v: object) => Promise<T>;
export default function useTbleProps(loadListFunc: GetListFunc) {
  const defaultProps = {
    'row-key': 'id',
    'bordered': { cell: true },
    'hover': true,
    'border': true,
    'stripe': true,
    'size': 'small',
    'column-resizable': true,
    // "scroll": { y: 550, x: '100%' },
    'expandable': false,
    'loading': true,
    'data': [] as any[],
    'pagination': {
      current: 1,
      pageSize: 20,
      total: 0,
      showPageSize: true,
    } as Pagination,
  };

  // 属性组
  const propsRes = ref(defaultProps);

  // 加载效果
  const setLoading = (status: boolean) => {
    propsRes.value.loading = status;
  };

  /**
   * 分页设置
   * @param current //当前页数
   * @param total //总页数默认是0条,可选
   * @param fetchData 获取列表数据,可选
   */
  interface SetPaginationPrams {
    current: number;
    total?: number;
  }

  const setPagination = ({ current, total }: SetPaginationPrams) => {
    propsRes.value.pagination.current = current;
    total && (propsRes.value.pagination.total = total);
  };

  // 单独设置默认属性
  const setProps = (params: object) => {
    for (const key in params) {
      defaultProps[key] = params[key];
    }
  };

  // 设置请求参数,如果出了分页参数还有搜索参数,在模板页面调用此方法,可以加入参数
  const loadListParams = ref<object>({});
  const setLoadListParams = (params?: object) => {
    loadListParams.value = params;
  };

  // 加载分页列表数据
  const loadList = async <T>() => {
    setLoading(true);
    let data = await loadListFunc<PaginationRes<T>>({
      ...propsRes.value.pagination,
      ...loadListParams.value,
    });
    propsRes.value.data = data.list as T[];
    setPagination({ current: data.current, total: data.total });
    setLoading(false);
    return data;
  };

  // 事件触发组
  const propsEvent = ref({
    //排序触发
    sorterChange: (dataIndex: string, direction: string) => {
      console.log(dataIndex, direction);
    },
    //分页触发
    pageChange: (current: number) => {
      setPagination({ current });
      loadList();
    },
    // 修改每页显示条数
    pageSizeChange:(pageSize:number)=>{
      propsRes.value.pagination.pageSize = pageSize;
      loadList();
    }
  });

  return {
    propsRes,
    propsEvent,
    setProps,
    setLoading,
    loadList,
    setPagination,
    setLoadListParams,
  };
}

<!--在页面模板中使用-->
<template>
  <div class="container">
      <a-table
        v-on="propsEvent"
        v-bind="propsRes">
        <template #columns>
          <a-table-column title="创建时间" data-index="createTime" />
          <!--其他变量自己绑定....-->
        </template>
      </a-table>

  </div>
</template>

<script lang="ts">
import { defineComponent, reactive, ref, toRefs, onMounted } from 'vue';
import { getRollingInfoList, GetRollingInfoList } from '@/fetch/apis';
import useTableProps from '@/hooks/table-props';

export default defineComponent({
  setup() {
      //这里开始引入使用,将请求方法默认传入
    const { propsRes, propsEvent, loadList } = useTableProps(getRollingInfoList);
    
    onMounted(()=>{fetchData()})
    // 数据获取
    const fetchData = async () => {
    //hook里面做了泛型的返回
      await loadList<GetRollingInfoList['Res']>();
    };
    
    return {
      propsRes,
      propsEvent
    };
  }, //setup

});
</script>

// /fetch/apis.ts 的封装

//这里Requset对axio做了二次封装,返回传入的泛型值
import Requset from '@/fetch/requset';
import { Pagination } from '@/types/global';

export interface GetRollingInfoList {
  Res:{
      id?: number;
      creatorName?: string;
      createTime?: string;
  }
}

export function getRollingInfoList<T>(data:Pagination) {//获取跑马灯列表
  return Requset<T>({
      method:'POST',
      url: `/yourUrl`,
      data
  });
}


其实核心的就是hook的统一封装,这样在业务页面中,我们只用关心对调用列表的方法跟对应字段即可。