前言
继续组合式API,今天分享的是useTable,其实之前已经分享过useTable了,但是之前的没有业务验证,经过一段时间的业务实践,这里分享一版比之前更加完整的
介绍
useTable是对列表类基础逻辑的封装,UI表现形式可能为table、list等形式,对于扩展如useTableEdit、useTableColumn等均是在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 || [] } : {}
}
})
动态表头
当表头为动态时,入参需要传一个columns和isActiveColumn,出参会多一个响应式columns和更新这个响应式数据的方法setColumns,isActiveColumn的作用是告诉useTable混入useTableColumn逻辑
const {
columns,
dataSource,
setColumns
} = useTable({
request,
isActiveColumn: true,
columns: defaultColumns
})
// 设置表头
const changeColumns = cols = > setColumns(cols)
可编辑table
有分页的列表且编辑时不会实时向后端发起请求存储数据时,当切换分页请求数据时会导致编辑的数据丢失,而可编辑table正是对此问题的处理。和动态表头一样,可编辑table不属于基础table功能,需要单独拉一份use函数完成,并通过入参方式混入useTable
将设置edit为true告知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,参考element | Arrary | - | [] |
| isActiveColumn | 是否开启动态表头 | Boolean | - | false |
| checkbox | 是否开启多选 | Boolean | - | false |
| edit | 是否开启可编辑table | Boolean | - | false |
出参
| 参数 | 说明 | 类型 | 可选值 | 默认值 |
|---|---|---|---|---|
| dataSource | 接口请求数据 | Arrary | - | [] |
| pagination | 分页信息 | Object | - | - |
| searchInfo | 查询条件 | Object | - | {} |
| loading | loading状态 | 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后增加的出参
| 参数 | 说明 | 类型 | 可选值 | 默认值 |
|---|---|---|---|---|
| columns | columns配置 | Arrary | - | [] |
| setColumns | 设置columns | Function(data) | - | - |
开启checkbox后增加的出参
| 参数 | 说明 | 类型 | 可选值 | 默认值 |
|---|---|---|---|---|
| currentSelects | 当前选中数据 | Arrary | - | [] |
| setCurrentSelects | 设置currentSelects | Function(data) | - | - |
| selectionChange | 设置currentSelects,未开启checkbox则不触发 | Function(data) | - | - |
开启edit后增加的出参
setEditChange是数据回显的关键,当数据编辑后需触发该方法来设置editDatas,以保证分页切换时数据可以正常回显
| 参数 | 说明 | 类型 | 可选值 | 默认值 |
|---|---|---|---|---|
| editDatas | 编辑数据 | Object | - | {} |
| clearEdits | 清空编辑数据 | Function | - | - |
| setEditChange | 设置编辑数据 | Function(data: any[]) | - | - |
结语
table的功能比较庞杂,这里也会有部分的逻辑抽离,主要还是看业务中遇到什么样的需求,这里完成对这部分逻辑的封装之后在遇到直接用便好了,下篇文章分享useForm好了