feat: Add Table component and add useTable hook

This commit is contained in:
陈凯龙 2022-02-09 16:19:21 +08:00
parent b271e13227
commit 17e8e7cda9
10 changed files with 387 additions and 69 deletions

View File

@ -2,6 +2,7 @@ import Table from './src/Table.vue'
export interface TableExpose {
setProps: (props: Recordable) => void
setColumn: (columnProps: TableSetPropsType[]) => void
}
export { Table }

View File

@ -5,6 +5,7 @@ import { propTypes } from '@/utils/propTypes'
import { setIndex } from './helper'
import { getSlot } from '@/utils/tsxHelper'
import type { TableProps } from './types'
import { set } from 'lodash-es'
export default defineComponent({
name: 'Table',
@ -20,6 +21,8 @@ export default defineComponent({
type: Array as PropType<TableColumn[]>,
default: () => []
},
//
expand: propTypes.bool.def(false),
//
pagination: {
type: Object as PropType<Pagination>,
@ -73,8 +76,22 @@ export default defineComponent({
outsideProps.value = props
}
const setColumn = (columnProps: TableSetPropsType[], columnsChildren?: TableColumn[]) => {
const { columns } = unref(getProps)
for (const v of columnsChildren || columns) {
for (const item of columnProps) {
if (v.field === item.field) {
set(v, item.path, item.value)
} else if (v.children?.length) {
setColumn(columnProps, v.children)
}
}
}
}
expose({
setProps
setProps,
setColumn
})
const pagination = computed(() => {
@ -129,21 +146,73 @@ export default defineComponent({
})
const renderTableSelection = () => {
const { selection, reserveSelection, align, headerAlign } = unref(getProps)
//
return unref(getProps).selection ? (
return selection ? (
<ElTableColumn
type="selection"
reserveSelection={unref(getProps).reserveSelection}
align={unref(getProps).align}
headerAlign={unref(getProps).headerAlign}
reserveSelection={reserveSelection}
align={align}
headerAlign={headerAlign}
width="50"
></ElTableColumn>
) : undefined
}
const rnderTableColumn = (columns: TableColumn[]) => {
return [renderTableSelection()].concat(
columns.map((v) => {
const renderTableExpand = () => {
const { align, headerAlign } = unref(getProps)
//
return unref(getProps).expand ? (
<ElTableColumn type="expand" align={align} headerAlign={headerAlign}>
{{
// @ts-ignore
default: (data: TableSlotDefault) => getSlot(slots, 'expand', data)
}}
</ElTableColumn>
) : undefined
}
const rnderTreeTableColumn = (columnsChildren: TableColumn[]) => {
const { align, headerAlign, showOverflowTooltip } = unref(getProps)
return columnsChildren.map((v) => {
const props = { ...v }
if (props.children) delete props.children
return (
<ElTableColumn
showOverflowTooltip={showOverflowTooltip}
align={align}
headerAlign={headerAlign}
{...props}
prop={v.field}
>
{{
default: (data: TableSlotDefault) =>
v.children && v.children.length
? rnderTableColumn(v.children)
: // @ts-ignore
getSlot(slots, v.field, data) ||
v?.formatter?.(data.row, data.column, data.row[v.field], data.$index) ||
data.row[v.field],
// @ts-ignore
header: getSlot(slots, `${v.field}-header`)
}}
</ElTableColumn>
)
})
}
const rnderTableColumn = (columnsChildren?: TableColumn[]) => {
const {
columns,
reserveIndex,
pageSize,
currentPage,
align,
headerAlign,
showOverflowTooltip
} = unref(getProps)
return [...[renderTableExpand()], ...[renderTableSelection()]].concat(
(columnsChildren || columns).map((v) => {
//
if (v.type === 'index') {
return (
@ -152,35 +221,33 @@ export default defineComponent({
index={
v.index
? v.index
: (index) =>
setIndex(
unref(getProps).reserveIndex,
index,
unref(getProps).pageSize,
unref(getProps).currentPage
)
: (index) => setIndex(reserveIndex, index, pageSize, currentPage)
}
align={v.align || unref(getProps).align}
headerAlign={v.headerAlign || unref(getProps).headerAlign}
align={v.align || align}
headerAlign={v.headerAlign || headerAlign}
label={v.label}
width="100px"
></ElTableColumn>
)
} else {
const props = { ...v }
if (props.children) delete props.children
return (
<ElTableColumn
showOverflowTooltip={unref(getProps).showOverflowTooltip}
align={unref(getProps).align}
headerAlign={unref(getProps).headerAlign}
{...v}
showOverflowTooltip={showOverflowTooltip}
align={align}
headerAlign={headerAlign}
{...props}
prop={v.field}
>
{{
default: (data: TableSlotDefault) =>
// @ts-ignore
getSlot(slots, v.field, data) ||
v?.formatter?.(data.row, data.column, data.row[v.field], data.$index) ||
data.row[v.field],
v.children && v.children.length
? rnderTreeTableColumn(v.children)
: // @ts-ignore
getSlot(slots, v.field, data) ||
v?.formatter?.(data.row, data.column, data.row[v.field], data.$index) ||
data.row[v.field],
// @ts-ignore
header: getSlot(slots, `${v.field}-header`)
}}
@ -201,7 +268,7 @@ export default defineComponent({
v-loading={unref(getProps).loading}
>
{{
default: () => rnderTableColumn(unref(getProps).columns),
default: () => rnderTableColumn(),
// @ts-ignore
append: () => getSlot(slots, 'append')
}}

View File

@ -20,4 +20,5 @@ export type TableProps = {
// 表头对齐方式
headerAlign?: 'left' | 'center' | 'right'
data?: Recordable
expand?: boolean
} & Recordable

View File

@ -12,6 +12,7 @@ interface UseTableConfig<T, L> {
list: string
total?: string
}
props?: TableProps
}
interface TableObject<K, L> {
@ -88,7 +89,11 @@ export const useTable = <T, K, L extends AxiosConfig = AxiosConfig>(
return table
}
const methods = {
const methods: {
setProps: (props: Recordable) => void
getList: () => Promise<void>
setColumn: (columnProps: TableSetPropsType[]) => void
} = {
getList: async () => {
tableObject.loading = true
const res = await config
@ -105,11 +110,18 @@ export const useTable = <T, K, L extends AxiosConfig = AxiosConfig>(
setProps: async (props: TableProps = {}) => {
const table = await getTable()
table?.setProps(props)
},
setColumn: async (columnProps: TableSetPropsType[]) => {
const table = await getTable()
table?.setColumn(columnProps)
}
}
config?.props && methods.setProps(config.props)
return {
register,
elTableRef,
tableObject,
methods
}

View File

@ -324,9 +324,11 @@ export default {
pagination: 'pagination',
reserveIndex: 'Reserve index',
restoreIndex: 'Restore index',
showSelections: 'show selections',
hiddenSelections: 'restore selections',
showExpandedRows: 'show expanded rows',
hiddenExpandedRows: 'hidden expanded rows'
showSelections: 'Show selections',
hiddenSelections: 'Restore selections',
showExpandedRows: 'Show expanded rows',
hiddenExpandedRows: 'Hidden expanded rows',
changeTitle: 'Change title',
header: 'Header'
}
}

View File

@ -324,6 +324,8 @@ export default {
showSelections: '显示多选',
hiddenSelections: '隐藏多选',
showExpandedRows: '显示展开行',
hiddenExpandedRows: '隐藏展开行'
hiddenExpandedRows: '隐藏展开行',
changeTitle: '修改标题',
header: '头部'
}
}

View File

@ -155,6 +155,14 @@ export const asyncRouterMap: AppRouteRecordRaw[] = [
meta: {
title: 'UseTable'
}
},
{
path: 'ref-table',
component: () => import('@/views/Components/Table/RefTable.vue'),
name: 'RefTable',
meta: {
title: 'RefTable'
}
}
]
},

View File

@ -0,0 +1,181 @@
<script setup lang="ts">
import { ContentWrap } from '@/components/ContentWrap'
import { useI18n } from '@/hooks/web/useI18n'
import { Table, TableExpose } from '@/components/Table'
import { getTableListApi } from '@/api/table'
import { TableData } from '@/api/table/types'
import { ref, h, reactive, unref } from 'vue'
import { ElTag, ElButton } from 'element-plus'
import { useTable } from '@/hooks/web/useTable'
const { t } = useI18n()
const columns = reactive<TableColumn[]>([
{
field: 'index',
label: t('tableDemo.index'),
type: 'index'
},
{
field: 'content',
label: t('tableDemo.header'),
children: [
{
field: 'title',
label: t('tableDemo.title')
},
{
field: 'author',
label: t('tableDemo.author')
},
{
field: 'display_time',
label: t('tableDemo.displayTime')
},
{
field: 'importance',
label: t('tableDemo.importance'),
formatter: (_: Recordable, __: TableColumn, cellValue: number) => {
return h(
ElTag,
{
type: cellValue === 1 ? 'success' : cellValue === 2 ? 'warning' : 'danger'
},
() =>
cellValue === 1
? t('tableDemo.important')
: cellValue === 2
? t('tableDemo.good')
: t('tableDemo.commonly')
)
}
},
{
field: 'pageviews',
label: t('tableDemo.pageviews')
}
]
},
{
field: 'action',
label: t('tableDemo.action')
}
])
const { register, tableObject, methods } = useTable<
{
total: number
list: TableData[]
},
TableData
>({
getListApi: getTableListApi,
response: {
list: 'list',
total: 'total'
},
props: {
columns
}
})
const { getList } = methods
getList()
const tableRef = ref<TableExpose>()
const acitonFn = (data: TableSlotDefault) => {
console.log(data)
}
const paginationObj = ref<Pagination>()
const showPagination = (show: boolean) => {
if (show) {
paginationObj.value = {
total: tableObject.total
}
} else {
paginationObj.value = undefined
}
}
const reserveIndex = (custom: boolean) => {
unref(tableRef)?.setProps({
reserveIndex: custom
})
}
const showSelections = (show: boolean) => {
unref(tableRef)?.setProps({
selection: show
})
}
const index = ref(1)
const changeTitle = () => {
unref(tableRef)?.setColumn([
{
field: 'title',
path: 'label',
value: `${t('tableDemo.title')}${unref(index)}`
}
])
index.value++
}
const showExpandedRows = (show: boolean) => {
unref(tableRef)?.setProps({
expand: show
})
}
</script>
<template>
<ContentWrap :title="`RefTable ${t('tableDemo.operate')}`">
<ElButton @click="showPagination(true)">
{{ t('tableDemo.show') }} {{ t('tableDemo.pagination') }}
</ElButton>
<ElButton @click="showPagination(false)">
{{ t('tableDemo.hidden') }} {{ t('tableDemo.pagination') }}
</ElButton>
<ElButton @click="reserveIndex(true)">{{ t('tableDemo.reserveIndex') }}</ElButton>
<ElButton @click="reserveIndex(false)">{{ t('tableDemo.restoreIndex') }}</ElButton>
<ElButton @click="showSelections(true)">{{ t('tableDemo.showSelections') }}</ElButton>
<ElButton @click="showSelections(false)">{{ t('tableDemo.hiddenSelections') }}</ElButton>
<ElButton @click="changeTitle">{{ t('tableDemo.changeTitle') }}</ElButton>
<ElButton @click="showExpandedRows(true)">{{ t('tableDemo.showExpandedRows') }}</ElButton>
<ElButton @click="showExpandedRows(false)">{{ t('tableDemo.hiddenExpandedRows') }}</ElButton>
</ContentWrap>
<ContentWrap :title="`RefTable ${t('tableDemo.example')}`">
<Table
ref="tableRef"
v-model:pageSize="tableObject.pageSize"
v-model:currentPage="tableObject.currentPage"
:data="tableObject.tableList"
:loading="tableObject.loading"
:pagination="paginationObj"
@register="register"
>
<template #action="data">
<ElButton type="primary" @click="acitonFn(data as TableSlotDefault)">
{{ t('tableDemo.action') }}
</ElButton>
</template>
<template #expand="data">
<div class="ml-30px">
<div>{{ t('tableDemo.title') }}{{ data.row.title }}</div>
<div>{{ t('tableDemo.author') }}{{ data.row.author }}</div>
<div>{{ t('tableDemo.displayTime') }}{{ data.row.display_time }}</div>
</div>
</template>
</Table>
</ContentWrap>
</template>

View File

@ -4,7 +4,7 @@ import { useI18n } from '@/hooks/web/useI18n'
import { Table } from '@/components/Table'
import { getTableListApi } from '@/api/table'
import { TableData } from '@/api/table/types'
import { ref, h } from 'vue'
import { ref, h, reactive, unref } from 'vue'
import { ElTag, ElButton } from 'element-plus'
import { useTable } from '@/hooks/web/useTable'
@ -28,51 +28,57 @@ getList()
const { t } = useI18n()
const columns: TableColumn[] = [
const columns = reactive<TableColumn[]>([
{
field: 'index',
label: t('tableDemo.index'),
type: 'index'
},
{
field: 'title',
label: t('tableDemo.title')
},
{
field: 'author',
label: t('tableDemo.author')
},
{
field: 'display_time',
label: t('tableDemo.displayTime')
},
{
field: 'importance',
label: t('tableDemo.importance'),
formatter: (_: Recordable, __: TableColumn, cellValue: number) => {
return h(
ElTag,
{
type: cellValue === 1 ? 'success' : cellValue === 2 ? 'warning' : 'danger'
},
() =>
cellValue === 1
? t('tableDemo.important')
: cellValue === 2
? t('tableDemo.good')
: t('tableDemo.commonly')
)
}
},
{
field: 'pageviews',
label: t('tableDemo.pageviews')
field: 'content',
label: t('tableDemo.header'),
children: [
{
field: 'title',
label: t('tableDemo.title')
},
{
field: 'author',
label: t('tableDemo.author')
},
{
field: 'display_time',
label: t('tableDemo.displayTime')
},
{
field: 'importance',
label: t('tableDemo.importance'),
formatter: (_: Recordable, __: TableColumn, cellValue: number) => {
return h(
ElTag,
{
type: cellValue === 1 ? 'success' : cellValue === 2 ? 'warning' : 'danger'
},
() =>
cellValue === 1
? t('tableDemo.important')
: cellValue === 2
? t('tableDemo.good')
: t('tableDemo.commonly')
)
}
},
{
field: 'pageviews',
label: t('tableDemo.pageviews')
}
]
},
{
field: 'action',
label: t('tableDemo.action')
}
]
])
const acitonFn = (data: TableSlotDefault) => {
console.log(data)
@ -103,6 +109,27 @@ const showSelections = (show: boolean) => {
selection: show
})
}
const index = ref(1)
const changeTitle = () => {
const { setColumn } = methods
setColumn([
{
field: 'title',
path: 'label',
value: `${t('tableDemo.title')}${unref(index)}`
}
])
index.value++
}
const showExpandedRows = (show: boolean) => {
const { setProps } = methods
setProps({
expand: show
})
}
</script>
<template>
@ -120,8 +147,10 @@ const showSelections = (show: boolean) => {
<ElButton @click="showSelections(true)">{{ t('tableDemo.showSelections') }}</ElButton>
<ElButton @click="showSelections(false)">{{ t('tableDemo.hiddenSelections') }}</ElButton>
<ElButton @click="showSelections(true)">{{ t('tableDemo.showExpandedRows') }}</ElButton>
<ElButton @click="showSelections(false)">{{ t('tableDemo.hiddenExpandedRows') }}</ElButton>
<ElButton @click="changeTitle">{{ t('tableDemo.changeTitle') }}</ElButton>
<ElButton @click="showExpandedRows(true)">{{ t('tableDemo.showExpandedRows') }}</ElButton>
<ElButton @click="showExpandedRows(false)">{{ t('tableDemo.hiddenExpandedRows') }}</ElButton>
</ContentWrap>
<ContentWrap :title="`UseTable ${t('tableDemo.example')}`">
<Table
@ -138,6 +167,14 @@ const showSelections = (show: boolean) => {
{{ t('tableDemo.action') }}
</ElButton>
</template>
<template #expand="data">
<div class="ml-30px">
<div>{{ t('tableDemo.title') }}{{ data.row.title }}</div>
<div>{{ t('tableDemo.author') }}{{ data.row.author }}</div>
<div>{{ t('tableDemo.displayTime') }}{{ data.row.display_time }}</div>
</div>
</template>
</Table>
</ContentWrap>
</template>

View File

@ -1,6 +1,7 @@
declare type TableColumn = {
field: string
label?: string
children?: TableColumn[]
} & Recordable
declare type TableSlotDefault = {
@ -27,3 +28,9 @@ declare interface Pagination {
disabled?: boolean
hideOnSinglePage?: boolean
}
declare interface TableSetPropsType {
field: string
path: string
value: any
}