vue3.0组合式API-useTable

5,182 阅读6分钟

更多文章

前言

继续组合式API,今天分享的是useTable,其实之前已经分享过useTable了,但是之前的没有业务验证,经过一段时间的业务实践,这里分享一版比之前更加完整的

介绍

useTable是对列表类基础逻辑的封装,UI表现形式可能为tablelist等形式,对于扩展如useTableEdituseTableColumn等均是在useTable的基础上完成,另外列表与分页信息如影随形,所以useTable中包含了分页的信息

基础使用


<template>
<div>
  <Table
    :dataSource="dataSource"
    :columns="columns"
    :loading="loading"
  />
  <Pagination :pagination="pagination" />
</div>
</template>
<script>
import Table from '@/components/table'
import Pagination from '@/components/qjd/pagination'
import useTable from '@/hooks/useTable'
import { columns } from './config'

export default {
  components: { Table, Pagination },
  setup() {
    // table  dataSource:列表数据,pagination:分页信息,request:接口请求
    const { dataSource, pagination, loading } = useTable({ request })

    return {
      dataSource,
      pagination,
      loading,
      columns
    }
  }
}

</script>

初始化不调用

对于某些场景初始化时不需要触发接口调用,将isInit设置为false即可,默认为true


const { dataSource } = useTable({ request, isInit: false })

默认参数

可通过defaultParams设置初始化的默认参数,默认为{}


const { dataSource } = useTable({ request, defaultParams: { id: 1 } })

是否有分页

isPage默认为true,若不需要分页将其设置为false


const { dataSource } = useTable({ request, isPage: false })

接口返回数据不符合需要

按照约定,通常情况下后端返回的数据符合useTable的处理,但不排除不符合的情况,可以通过callback对数据进行处理

返回数据格式须符合{ totalCount, dataSource }格式


const { dataSource } = useTable({
  request,
  callback: ({ code, data }) => {
    return code === '0' ? { totalCount: data?.total || 0, dataSource: data?.data || [] } : {}
  }
})

动态表头

当表头为动态时,入参需要传一个columnsisActiveColumn,出参会多一个响应式columns和更新这个响应式数据的方法setColumnsisActiveColumn的作用是告诉useTable混入useTableColumn逻辑


const {
  columns,
  dataSource,
  setColumns
} = useTable({
  request,
  isActiveColumn: true,
  columns: defaultColumns
})
// 设置表头
const changeColumns = cols = > setColumns(cols)

可编辑table

有分页的列表且编辑时不会实时向后端发起请求存储数据时,当切换分页请求数据时会导致编辑的数据丢失,而可编辑table正是对此问题的处理。和动态表头一样,可编辑table不属于基础table功能,需要单独拉一份use函数完成,并通过入参方式混入useTable

将设置edittrue告知useTable混入useTableEdit逻辑


const { dataSource, setEditChange } = useTable({ request, edit: true })
// 假设table中有一个input,监听input数据变化的事件为onChange,则在onChange事件触发时存储编辑数据即可
// setEditChange:存储当前编辑数据,存储数据后分页在切换时会携带上之前编辑的数据
const onChange = () => setEditChange(dataSource)

多选table

目前多选table使用element的table组件自带的checkbox以及功能,但若是自定义checkbox可拉一份新的use函数将原有的checkbox逻辑(在useTableCheckBox中目前已经注释)拿过来即可。多选table同样是不属于基础table功能,同样是为了应对分页切换的问题,element内置了此功能,开启reserve-selection即可

checkbox设置为true开启多选模式


const { dataSource, selectionChange  } = useTable({ request, checkbox: true })
// 设置当前勾选数据,selectionHandle为element-table的selection-change事件
const selectionHandle = vals => selectionChange(vals)

源码

不属于基础table功能的逻辑都是单独抽出一个use函数实现,在引入useTable中,这里只放出基础table的相关逻辑


import { reactive, toRefs, ref, onUnmounted } from '@vue/composition-api'
import { deepCopy } from '@/utils/qjd'
import useAsync from './useAsync'
// 动态表头-逻辑
import useTableColumn from './useTableColumn'
// checkbox-逻辑
import useTableCheckBox from './useTableCheckBox'
// 编辑table-逻辑
import useTableEdit from './useTableEdit'

/** 适用于通用table & page使用,配合components/qjd/table、pagination使用,目前组件仅有开发遇到的场景,缺啥补啥,根据实际场景扩展useTbale
 * @param request 接口或Array<any>
 * @param defaultParams 默认入参
 * @param isInit 是否初始化调用
 * @param isPage 是否有分页
 * @param checkbox 是否开启多选模式,混入多选逻辑
 * @param edit 是否开启编辑模式,混入编辑逻辑
 * @function callback 若接口返回数据不满足需求提供callback容错机制
 * @param columns 为动态表头时需传入,后续使用useTable表头,若表头为静态不需要传入
 * @param isActiveColumn 是否开启动态表单模式
 */

const defaultCallBack = (data = {}) => {
  const { totalCount = 0, pagedRecords = [] } = data || {}
  return {
    totalCount,
    dataSource: pagedRecords
  }
}

function useTable({
  request,
  defaultParams = {},
  isInit = true,
  isPage = true,
  checkbox = false,
  edit = false,
  callback = defaultCallBack,
  columns = [],
  isActiveColumn = false
}) {
  const c = defaultParams && defaultParams.current ? defaultParams.current : 1
  const p = defaultParams && defaultParams.pageSize ? defaultParams.pageSize : 10

  const current = ref(c)
  const pageSize = ref(p)
  // 存储防抖函数
  const timer = ref(null)

  const state = reactive({
    params: isPage ? Object.assign({ page: current, pageSize: pageSize }, defaultParams) : defaultParams,
    searchInfo: {},
    dataSource: [],
    pagination: {
      current: current,
      pageSize: pageSize,
      total: 0,
      onChange: (page, pageSize) => pageChange(page, pageSize),
      onShowSizeChange: (current, size) => showSizeChange(current, size)
    }
  })

  // ----------------------------------------- 是否插入动态表头逻辑  start ----------------------------------------------
  // 此处插入useTableColumn逻辑
  // ----------------------------------------- 是否插入动态表头逻辑  end   ----------------------------------------------

  // ----------------------------------------- 是否插入checkbox多选逻辑  start ----------------------------------------------
  // 此处插入useTableCheckBox逻辑
  // ----------------------------------------- 是否插入checkbox多选逻辑 end   ----------------------------------------------

  // ----------------------------------------- 是否插入可编辑table逻辑 start ----------------------------------------------
  // 此处插入useTableEdit逻辑
  // ----------------------------------------- 是否插入可编辑table逻辑 end   ----------------------------------------------

  // 成功回调
  const successCallBack = ({ code, data }) => {
    if (code === '0') {
      // callback回调处理数据
      const result = callback ? callback(data) : data
      const { totalCount = 0, dataSource = [] } = result || {}
      state.pagination.total = totalCount || (dataSource ? dataSource.length : 0)
      state.dataSource = dataSource
      // 编辑模式下的数据回显
      edit && setEditDataSource(state.dataSource, current.value)
    }
  }
  // 接口
  const { doResult, loading } = useAsync({
    request,
    init: false,
    params: {},
    successCallBack
  })
  // api请求或json
  const _request = (params = {}) => {
    if (Object.prototype.toString.call(request) === '[object Array]') { // 使用定义时传入的json数据
      state.dataSource = request
      state.pagination.total = request.length
    } else if (Object.prototype.toString.call(request) === '[object Function]') { // 使用定义时传入的API请求
      doResult({ ...state.params, ...params })
    }
  }
  // 查询
  const searchHandle = (searchInfo = {}) => {
    // 拷贝数据,防止影响上层数据
    searchInfo = deepCopy(searchInfo)
    current.value = c
    pageSize.value = p
    state.pagination.current = searchInfo.page ? searchInfo.page : c
    state.pagination.pageSize = searchInfo.pageSize ? searchInfo.pageSize : p
    state.searchInfo = searchInfo
    _request(searchInfo)
  }
  // 切换条数时,若当前页不为第一页且切换后数据只有一页showSizeChange触发后会触发pageChange,添加防抖
  const _deferRequest = () => {
    timer.value && clearTimeout(timer.value)
    timer.value = setTimeout(() => {
      _request(state.searchInfo)
    }, 0)
  }
  // 切换页数
  const pageChange = (page, pageSize) => {
    current.value = page
    state.searchInfo.page = page
    _deferRequest()
  }
  // 切换条数
  const showSizeChange = (current, size) => {
    pageSize.value = current
    state.searchInfo.pageSize = current
    _deferRequest()
  }
  // 重置
  const resetHandle = () => {
    state.searchInfo = {}
    current.value = c
    pageSize.value = p
    _request({})
  }
  // 清空数据
  const clearHandle = () => {
    state.dataSource = []
    current.value = c
    pageSize.value = p
  }
  // 初始化数据
  isInit && _request(state.params)
  // 清除定时器
  onUnmounted(() => {
    if (timer.value) {
      clearTimeout(timer.value)
      timer.value = null
    }
  })

  return {
    ...toRefs(state),
    loading,
    searchHandle,
    resetHandle,
    clearHandle,
    // checkbox多选相关状态 & 接口
    ...checkBoxParams,
    // 可编辑table相关状态 & 接口
    ...editParams,
    // 动态表头相关状态 & 接口
    ...activeColParams,
  }
}

export default useTable

入参

参数说明类型可选值默认值
request接口请求Promise--
defaultParams接口请求默认入参Object-{}
isInit是否初始化发起接口请求Boolean-true
isPage是否有分页Boolean-true
callback接口请求数据处理回调Function(data)--
columns动态表头时需传入columns,参考elementArrary-[]
isActiveColumn是否开启动态表头Boolean-false
checkbox是否开启多选Boolean-false
edit是否开启可编辑tableBoolean-false

出参

参数说明类型可选值默认值
dataSource接口请求数据Arrary-[]
pagination分页信息Object--
searchInfo查询条件Object-{}
loadingloading状态Boolean-false
searchHandle主动触发接口调用方法Function(params)--
resetHandle重置(重置查询条件&dataSource)Function--
clearHandle清空数据&重置分页信息Function--

Pagination

key说明默认值
current当前页数1
pageSize当前条数10
total总条数0
onChange页数改变事件Function(page, pageSize)
onShowSizeChange条数改变事件Function(current, size)

开启isActiveColumn后增加的出参

参数说明类型可选值默认值
columnscolumns配置Arrary-[]
setColumns设置columnsFunction(data)--

开启checkbox后增加的出参

参数说明类型可选值默认值
currentSelects当前选中数据Arrary-[]
setCurrentSelects设置currentSelectsFunction(data)--
selectionChange设置currentSelects,未开启checkbox则不触发Function(data)--

开启edit后增加的出参

setEditChange是数据回显的关键,当数据编辑后需触发该方法来设置editDatas,以保证分页切换时数据可以正常回显

参数说明类型可选值默认值
editDatas编辑数据Object-{}
clearEdits清空编辑数据Function--
setEditChange设置编辑数据Function(data: any[])--

结语

table的功能比较庞杂,这里也会有部分的逻辑抽离,主要还是看业务中遇到什么样的需求,这里完成对这部分逻辑的封装之后在遇到直接用便好了,下篇文章分享useForm好了