Browse Source

feat: add generic support for Table

main
刘凯 1 year ago
parent
commit
68955d3a0b
  1. 18
      src/components/Table/src/BasicTable.vue
  2. 19
      src/components/Table/src/hooks/useDataSource.ts
  3. 21
      src/components/Table/src/props.ts
  4. 10
      src/components/Table/src/types/table.ts

18
src/components/Table/src/BasicTable.vue

@ -1,9 +1,9 @@
<!-- eslint-disable no-useless-call --> <!-- eslint-disable no-useless-call -->
<script lang="ts" setup> <script lang="ts" setup generic="T extends Recordable = Recordable">
import { computed, inject, ref, toRaw, unref, useAttrs, useSlots, watchEffect } from 'vue' import { computed, inject, ref, toRaw, unref, useAttrs, useSlots, watchEffect } from 'vue'
import { Table } from 'ant-design-vue' import { Table } from 'ant-design-vue'
import { omit } from 'lodash-es' import { omit } from 'lodash-es'
import type { BasicTableProps, ColumnChangeParam, InnerHandlers, SizeType, TableActionType } from './types/table' import type { BasicTableProps, ColumnChangeParam, InnerHandlers, SizeType, SlotBodyCellProps, TableActionType } from './types/table'
import HeaderCell from './components/HeaderCell.vue' import HeaderCell from './components/HeaderCell.vue'
import { usePagination } from './hooks/usePagination' import { usePagination } from './hooks/usePagination'
@ -20,7 +20,7 @@ import { useTableExpand } from './hooks/useTableExpand'
import { createTableContext } from './hooks/useTableContext' import { createTableContext } from './hooks/useTableContext'
import { useTableFooter } from './hooks/useTableFooter' import { useTableFooter } from './hooks/useTableFooter'
import { useTableForm } from './hooks/useTableForm' import { useTableForm } from './hooks/useTableForm'
import { basicProps } from './props' import { defineTableProps } from './props'
import { useDesign } from '@/hooks/web/useDesign' import { useDesign } from '@/hooks/web/useDesign'
import { PageWrapperFixedHeightKey } from '@/enums/pageEnum' import { PageWrapperFixedHeightKey } from '@/enums/pageEnum'
@ -30,7 +30,7 @@ import { warn } from '@/utils/log'
defineOptions({ name: 'BasicTable' }) defineOptions({ name: 'BasicTable' })
const props = defineProps(basicProps) const props = defineProps(defineTableProps<T>())
const emit = defineEmits([ const emit = defineEmits([
'fetch-success', 'fetch-success',
@ -109,7 +109,7 @@ const {
reload, reload,
getAutoCreateKey, getAutoCreateKey,
updateTableData, updateTableData,
} = useDataSource( } = useDataSource<T>(
getProps, getProps,
{ {
tableData, tableData,
@ -239,7 +239,7 @@ function setProps(props: Partial<BasicTableProps>) {
innerPropsRef.value = { ...unref(innerPropsRef), ...props } innerPropsRef.value = { ...unref(innerPropsRef), ...props }
} }
const tableAction: TableActionType = { const tableAction: TableActionType<T> = {
reload: async params => void reload(params), reload: async params => void reload(params),
getSelectRows, getSelectRows,
setSelectedRows, setSelectedRows,
@ -312,7 +312,8 @@ emit('register', tableAction, formActions)
@resize-column="setColumnWidth" @resize-column="setColumnWidth"
> >
<template v-for="item in Object.keys($slots)" #[item]="data" :key="item"> <template v-for="item in Object.keys($slots)" #[item]="data" :key="item">
<slot :name="item" v-bind="data || {}" /> <!-- eslint-disable-next-line vue/no-extra-parens -->
<slot :name="item" v-bind="((data || {}) as SlotBodyCellProps<T>)" />
</template> </template>
<template #headerCell="{ column }"> <template #headerCell="{ column }">
<slot name="headerCell" v-bind="{ column }"> <slot name="headerCell" v-bind="{ column }">
@ -321,7 +322,8 @@ emit('register', tableAction, formActions)
</template> </template>
<!-- 增加对antdv3.x兼容 --> <!-- 增加对antdv3.x兼容 -->
<template #bodyCell="data"> <template #bodyCell="data">
<slot name="bodyCell" v-bind="data || {}" /> <!-- eslint-disable-next-line vue/no-extra-parens -->
<slot name="bodyCell" v-bind="((data || {}) as SlotBodyCellProps<T>)" />
</template> </template>
<!-- <template #[`header-${column.dataIndex}`] v-for="(column, index) in columns" :key="index"> --> <!-- <template #[`header-${column.dataIndex}`] v-for="(column, index) in columns" :key="index"> -->
<!-- <HeaderCell :column="column" /> --> <!-- <HeaderCell :column="column" /> -->

19
src/components/Table/src/hooks/useDataSource.ts

@ -5,6 +5,7 @@ import { cloneDeep, get, merge } from 'lodash-es'
import type { PaginationProps } from '../types/pagination' import type { PaginationProps } from '../types/pagination'
import type { BasicTableProps, FetchParams, SorterResult } from '../types/table' import type { BasicTableProps, FetchParams, SorterResult } from '../types/table'
import { FETCH_SETTING, PAGE_SIZE, ROW_KEY } from '../const' import { FETCH_SETTING, PAGE_SIZE, ROW_KEY } from '../const'
import type { EditRecordRow } from '../components/editable'
import { isBoolean, isFunction, isObject } from '@/utils/is' import { isBoolean, isFunction, isObject } from '@/utils/is'
import { buildUUID } from '@/utils/uuid' import { buildUUID } from '@/utils/uuid'
@ -21,7 +22,7 @@ interface SearchState {
sortInfo: Recordable sortInfo: Recordable
filterInfo: Record<string, string[]> filterInfo: Record<string, string[]>
} }
export function useDataSource( export function useDataSource<T extends Recordable = Recordable>(
propsRef: ComputedRef<BasicTableProps>, propsRef: ComputedRef<BasicTableProps>,
{ {
getPaginationInfo, getPaginationInfo,
@ -128,18 +129,18 @@ export function useDataSource(
return unref(dataSourceRef) return unref(dataSourceRef)
}) })
async function updateTableData(index: number, key: string, value: any) { async function updateTableData<K extends keyof T>(index: number, key: K, value: T[K]): Promise<EditRecordRow<T>> {
const record = dataSourceRef.value[index] const record = dataSourceRef.value[index]
if (record) if (record)
dataSourceRef.value[index][key] = value (dataSourceRef as any).value[index][key] = value
return dataSourceRef.value[index] return dataSourceRef.value[index] as any
} }
function updateTableDataRecord( function updateTableDataRecord(
rowKey: string | number, rowKey: string | number,
record: Recordable, record: T,
): Recordable | undefined { ): EditRecordRow<T> | undefined {
const row = findTableDataRecord(rowKey) const row = findTableDataRecord(rowKey)
if (row) { if (row) {
@ -200,14 +201,14 @@ export function useDataSource(
} }
function insertTableDataRecord( function insertTableDataRecord(
record: Recordable | Recordable[], record: T | T[],
index?: number, index?: number,
): Recordable[] | undefined { ): EditRecordRow<T>[] | undefined {
// if (!dataSourceRef.value || dataSourceRef.value.length == 0) return; // if (!dataSourceRef.value || dataSourceRef.value.length == 0) return;
index = index ?? dataSourceRef.value?.length index = index ?? dataSourceRef.value?.length
const _record = isObject(record) ? [record as Recordable] : (record as Recordable[]) const _record = isObject(record) ? [record as Recordable] : (record as Recordable[])
unref(dataSourceRef).splice(index, 0, ..._record) unref(dataSourceRef).splice(index, 0, ..._record)
return unref(dataSourceRef) return unref(dataSourceRef) as any
} }
function findTableDataRecord(rowKey: string | number) { function findTableDataRecord(rowKey: string | number) {

21
src/components/Table/src/props.ts

@ -10,11 +10,13 @@ import type {
TableSetting, TableSetting,
} from './types/table' } from './types/table'
import { DEFAULT_FILTER_FN, DEFAULT_SIZE, DEFAULT_SORT_FN, FETCH_SETTING } from './const' import { DEFAULT_FILTER_FN, DEFAULT_SIZE, DEFAULT_SORT_FN, FETCH_SETTING } from './const'
import type { EditRecordRow } from './components/editable'
import type { FormProps } from '@/components/Form' import type { FormProps } from '@/components/Form'
import { propTypes } from '@/utils/propTypes' import { propTypes } from '@/utils/propTypes'
export const basicProps = { export function defineTableProps<T>() {
return {
clickToRowSelect: { type: Boolean, default: true }, clickToRowSelect: { type: Boolean, default: true },
isTreeTable: Boolean, isTreeTable: Boolean,
tableSetting: propTypes.shape<TableSetting>({}), tableSetting: propTypes.shape<TableSetting>({}),
@ -42,7 +44,12 @@ export const basicProps = {
indentSize: propTypes.number.def(24), indentSize: propTypes.number.def(24),
canColDrag: { type: Boolean, default: true }, canColDrag: { type: Boolean, default: true },
api: { api: {
type: Function as PropType<(...arg: any[]) => Promise<any>>, type: Function as PropType<
(...arg: any[]) => Promise<
| T[]
| { list: T[], pageNo?: number, pageSize?: number, total?: number }
>
>,
default: null, default: null,
}, },
beforeFetch: { beforeFetch: {
@ -102,7 +109,7 @@ export const basicProps = {
clearSelectOnPageChange: propTypes.bool, clearSelectOnPageChange: propTypes.bool,
resizeHeightOffset: propTypes.number.def(0), resizeHeightOffset: propTypes.number.def(0),
rowSelection: { rowSelection: {
type: Object as PropType<TableRowSelection | null>, type: Object as PropType<TableRowSelection<EditRecordRow<T>> | null>,
default: null, default: null,
}, },
title: { title: {
@ -114,11 +121,12 @@ export const basicProps = {
}, },
maxHeight: propTypes.number, maxHeight: propTypes.number,
dataSource: { dataSource: {
type: Array as PropType<Recordable[]>, type: Array as PropType<T[]>,
default: null, default: null,
}, },
rowKey: { rowKey: {
type: [String, Function] as PropType<string | ((record: Recordable) => string)>, // eslint-disable-next-line ts/ban-types
type: [String, Function] as PropType<keyof T | ((record: T) => keyof T) | (string & {})>,
default: '', default: '',
}, },
bordered: propTypes.bool, bordered: propTypes.bool,
@ -137,7 +145,7 @@ export const basicProps = {
beforeEditSubmit: { beforeEditSubmit: {
type: Function as PropType< type: Function as PropType<
(data: { (data: {
record: Recordable record: T
index: number index: number
key: string | number key: string | number
value: any value: any
@ -148,4 +156,5 @@ export const basicProps = {
type: String as PropType<SizeType>, type: String as PropType<SizeType>,
default: DEFAULT_SIZE, default: DEFAULT_SIZE,
}, },
}
} }

10
src/components/Table/src/types/table.ts

@ -1,6 +1,7 @@
import type { VNodeChild } from 'vue' import type { VNodeChild } from 'vue'
import type { TableRowSelection as ITableRowSelection, Key } from 'ant-design-vue/lib/table/interface' import type { TableRowSelection as ITableRowSelection, Key } from 'ant-design-vue/lib/table/interface'
import type { ColumnProps } from 'ant-design-vue/lib/table' import type { ColumnProps } from 'ant-design-vue/lib/table'
import type { Table } from 'ant-design-vue'
import type { EditRecordRow } from '../components/editable' import type { EditRecordRow } from '../components/editable'
import type { PaginationProps } from './pagination' import type { PaginationProps } from './pagination'
@ -210,7 +211,8 @@ export interface BasicTableProps<T = Recordable<any>> {
// 在分页改变的时候清空选项 // 在分页改变的时候清空选项
clearSelectOnPageChange?: boolean clearSelectOnPageChange?: boolean
// //
rowKey?: keyof T | ((record: T) => keyof T) // eslint-disable-next-line ts/ban-types
rowKey?: keyof T | ((record: T) => keyof T) | (string & {})
// 数据 // 数据
dataSource?: T[] dataSource?: T[]
// 标题右侧提示 // 标题右侧提示
@ -382,7 +384,7 @@ export interface BasicTableProps<T = Recordable<any>> {
* The cell will not submit data while callback return false * The cell will not submit data while callback return false
*/ */
beforeEditSubmit?: (data: { beforeEditSubmit?: (data: {
record: Recordable record: T
index: number index: number
key: string | number key: string | number
value: any value: any
@ -500,3 +502,7 @@ export interface ColumnChangeParam {
export interface InnerHandlers { export interface InnerHandlers {
onColumnsChange: (data: ColumnChangeParam[]) => void onColumnsChange: (data: ColumnChangeParam[]) => void
} }
export type SlotBodyCellProps<T> =
& Omit<Parameters<Required<InstanceType<typeof Table>['$slots']>['bodyCell']>[0], 'record'>
& { record: EditRecordRow<T> }