|
| 1 | +import type { TableColumnCtx } from 'element-plus' |
| 2 | +import type { FunctionalComponent, ObjectEmitsOptions, VNodeChild } from 'vue' |
| 3 | +import type { ComponentProps } from 'vue-component-type-helpers' |
| 4 | +import { isString, objectMap } from '@antfu/utils' |
| 5 | +import { ElTableColumn } from 'element-plus' |
| 6 | + |
| 7 | +/** |
| 8 | + * `ElTableColumn` 的插槽类型 |
| 9 | + */ |
| 10 | +export interface ColumnSlots<T extends { [K: PropertyKey]: any } = any> { |
| 11 | + 'default'?: (data: { |
| 12 | + row: T |
| 13 | + column: TableColumnCtx<T> |
| 14 | + $index: number |
| 15 | + }) => VNodeChild |
| 16 | + |
| 17 | + 'header'?: (data: { column: TableColumnCtx<T>, $index: number }) => VNodeChild |
| 18 | + |
| 19 | + 'filter-icon'?: (data: { filterOpened: boolean }) => VNodeChild |
| 20 | + |
| 21 | + 'expand'?: (data: { expanded: boolean }) => VNodeChild |
| 22 | +} |
| 23 | + |
| 24 | +export type ObjectColumnProps<T extends { [K: PropertyKey]: any } = any> = |
| 25 | + ComponentProps<typeof ElTableColumn> & { |
| 26 | + /** |
| 27 | + * 正常情况下应该是通过 `ComponentSlots<typeof ElTableColumn>` 提取出来, |
| 28 | + * 但是 `ElTableColumn` 类型定义存在缺失,这里根据官网进行补充并优化。 |
| 29 | + * |
| 30 | + * 若想在 `<template>` 中使用,则需要使用 `<template #[slotType]:[slotName]>`, |
| 31 | + * 其中 `[slotName]` 为 `slots.[slotType]` 设置的名称(仅支持字符串), |
| 32 | + * 但是模板中会丢失 `ColumnProps<T>` 的泛型提示! |
| 33 | + * |
| 34 | + * @example |
| 35 | + * ```ts |
| 36 | + * // script 部分 |
| 37 | + * const columns: ColumnProps[] = [ |
| 38 | + * { |
| 39 | + * prop: 'name', |
| 40 | + * label: 'Name', |
| 41 | + * slots: { |
| 42 | + * // 引用模板中的插槽 |
| 43 | + * default: 'name', |
| 44 | + * |
| 45 | + * // 使用自定义渲染函数 |
| 46 | + * header({ column }) { |
| 47 | + * return <h3>{ column.label } - { Date.now() }</h3> |
| 48 | + * } |
| 49 | + * }, |
| 50 | + * }, |
| 51 | + * ] |
| 52 | + * ``` |
| 53 | + * |
| 54 | + * ```html |
| 55 | + * <!-- template 部分 --> |
| 56 | + * <ColumnsRender :columns="columns"> |
| 57 | + * <template #default:name="{ row }"> |
| 58 | + * {{ row.name }} |
| 59 | + * </template> |
| 60 | + * </ColumnsRender> |
| 61 | + * ``` |
| 62 | + */ |
| 63 | + slots?: { |
| 64 | + [K in keyof ColumnSlots<T>]: string | ColumnSlots<T>[K] |
| 65 | + } & { |
| 66 | + [K: string]: string | ((data: any) => VNodeChild) |
| 67 | + } |
| 68 | + |
| 69 | + /** |
| 70 | + * 子列定义 |
| 71 | + */ |
| 72 | + children?: ColumnProps[] |
| 73 | + } |
| 74 | + |
| 75 | +type FalsyColumnProps = false | null | undefined |
| 76 | + |
| 77 | +export type ColumnProps<T extends { [K: PropertyKey]: any } = any> = |
| 78 | + | FalsyColumnProps | |
| 79 | + ObjectColumnProps<T> |
| 80 | + |
| 81 | +export interface ColumnsRenderProps { |
| 82 | + /** |
| 83 | + * 列定义,同 `ElTableColumn` 的属性,额外支持 `children` 用于定义子列, |
| 84 | + * 列的 `slots` 同 `ElTableColumn` 的 `slots` |
| 85 | + */ |
| 86 | + columns: ColumnProps[] |
| 87 | +} |
| 88 | + |
| 89 | +/** |
| 90 | + * 由于 `ElTableColumn` 没有 `emit` 事件,这里只是为了类型占位 |
| 91 | + */ |
| 92 | +interface ColumnsRenderEmits extends ObjectEmitsOptions {} |
| 93 | + |
| 94 | +export type ColumnsRenderSlots = { |
| 95 | + [K in keyof ColumnSlots<any> as `${K}:${string}`]: NonNullable< |
| 96 | + ColumnSlots<any>[K] |
| 97 | + > |
| 98 | +} & { |
| 99 | + [K: string]: (data: any) => VNodeChild |
| 100 | +} |
| 101 | + |
| 102 | +export const ColumnsRender: FunctionalComponent< |
| 103 | + ColumnsRenderProps, |
| 104 | + ColumnsRenderEmits, |
| 105 | + ColumnsRenderSlots |
| 106 | +> = ({ columns }, { slots }) => { |
| 107 | + const vNodes: VNodeChild = [] |
| 108 | + |
| 109 | + for (const column of columns) { |
| 110 | + if (!column) { |
| 111 | + continue |
| 112 | + } |
| 113 | + |
| 114 | + const { children, slots: userSlots = {}, ...columnProps } = column |
| 115 | + const finalSlots = objectMap(userSlots, (key, value) => [ |
| 116 | + key, |
| 117 | + isString(value) ? slots[`${key}:${value}`] : value, |
| 118 | + ]) |
| 119 | + |
| 120 | + vNodes.push( |
| 121 | + <ElTableColumn {...columnProps}> |
| 122 | + {{ |
| 123 | + ...finalSlots, |
| 124 | + default: |
| 125 | + children && !finalSlots.default |
| 126 | + ? () => <ColumnsRender columns={children} /> |
| 127 | + : finalSlots.default, |
| 128 | + }} |
| 129 | + </ElTableColumn>, |
| 130 | + ) |
| 131 | + } |
| 132 | + |
| 133 | + return vNodes |
| 134 | +} |
| 135 | + |
| 136 | +ColumnsRender.props = /* @__PURE__ */ { |
| 137 | + columns: { |
| 138 | + type: Array, |
| 139 | + required: true, |
| 140 | + }, |
| 141 | +} |
0 commit comments