123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436 |
- <script lang="tsx">
- import { ElTable, ElTableColumn, ElPagination, ComponentSize, ElTooltipProps } from 'element-plus'
- import { defineComponent, PropType, ref, computed, unref, watch, onMounted } from 'vue'
- import { propTypes } from '@/utils/propTypes'
- import { setIndex } from './helper'
- import type { TableProps, TableColumn, Pagination, TableSetProps } from './types'
- import { set } from 'lodash-es'
- import { CSSProperties } from 'vue'
- import { getSlot } from '@/utils/tsxHelper'
- export default defineComponent({
- name: 'Table',
- props: {
- pageSize: propTypes.number.def(10),
- currentPage: propTypes.number.def(1),
- // 是否所有的超出隐藏,优先级低于schema中的showOverflowTooltip,
- showOverflowTooltip: propTypes.bool.def(true),
- // 表头
- columns: {
- type: Array as PropType<TableColumn[]>,
- default: () => []
- },
- // 展开行
- // expand: propTypes.bool.def(false),
- // 是否展示分页
- pagination: {
- type: Object as PropType<Pagination>,
- default: (): Pagination | undefined => undefined
- },
- // 仅对 type=selection 的列有效,类型为 Boolean,为 true 则会在数据更新之后保留之前选中的数据(需指定 row-key)
- reserveSelection: propTypes.bool.def(false),
- // 加载状态
- loading: propTypes.bool.def(false),
- // 是否叠加索引
- reserveIndex: propTypes.bool.def(false),
- // 对齐方式
- align: propTypes.string
- .validate((v: string) => ['left', 'center', 'right'].includes(v))
- .def('left'),
- // 表头对齐方式
- headerAlign: propTypes.string
- .validate((v: string) => ['left', 'center', 'right'].includes(v))
- .def('left'),
- data: {
- type: Array as PropType<Recordable[]>,
- default: () => []
- },
- height: propTypes.oneOfType([Number, String]),
- maxHeight: propTypes.oneOfType([Number, String]),
- stripe: propTypes.bool.def(false),
- border: propTypes.bool.def(true),
- size: {
- type: String as PropType<ComponentSize>,
- validator: (v: ComponentSize) => ['medium', 'small', 'mini'].includes(v)
- },
- fit: propTypes.bool.def(true),
- showHeader: propTypes.bool.def(true),
- highlightCurrentRow: propTypes.bool.def(false),
- currentRowKey: propTypes.oneOfType([Number, String]),
- // row-class-name, 类型为 (row: Recordable, rowIndex: number) => string | string
- rowClassName: {
- type: [Function, String] as PropType<(row: Recordable, rowIndex: number) => string | string>,
- default: ''
- },
- rowStyle: {
- type: [Function, Object] as PropType<
- (row: Recordable, rowIndex: number) => Recordable | CSSProperties
- >,
- default: () => undefined
- },
- cellClassName: {
- type: [Function, String] as PropType<
- (row: Recordable, column: any, rowIndex: number) => string | string
- >,
- default: ''
- },
- cellStyle: {
- type: [Function, Object] as PropType<
- (row: Recordable, column: any, rowIndex: number) => Recordable | CSSProperties
- >,
- default: () => undefined
- },
- headerRowClassName: {
- type: [Function, String] as PropType<(row: Recordable, rowIndex: number) => string | string>,
- default: ''
- },
- headerRowStyle: {
- type: [Function, Object] as PropType<
- (row: Recordable, rowIndex: number) => Recordable | CSSProperties
- >,
- default: () => undefined
- },
- headerCellClassName: {
- type: [Function, String] as PropType<
- (row: Recordable, column: any, rowIndex: number) => string | string
- >,
- default: ''
- },
- headerCellStyle: {
- type: [Function, Object] as PropType<
- (row: Recordable, column: any, rowIndex: number) => Recordable | CSSProperties
- >,
- default: () => undefined
- },
- rowKey: propTypes.string.def('id'),
- emptyText: propTypes.string.def('No Data'),
- defaultExpandAll: propTypes.bool.def(false),
- expandRowKeys: {
- type: Array as PropType<string[]>,
- default: () => []
- },
- defaultSort: {
- type: Object as PropType<{ prop: string; order: string }>,
- default: () => ({})
- },
- tooltipEffect: {
- type: String as PropType<'dark' | 'light'>,
- default: 'dark'
- },
- tooltipOptions: {
- type: Object as PropType<
- Pick<
- ElTooltipProps,
- | 'effect'
- | 'enterable'
- | 'hideAfter'
- | 'offset'
- | 'placement'
- | 'popperClass'
- | 'popperOptions'
- | 'showAfter'
- | 'showArrow'
- >
- >,
- default: () => ({
- enterable: true,
- placement: 'top',
- showArrow: true,
- hideAfter: 200,
- popperOptions: { strategy: 'fixed' }
- })
- },
- showSummary: propTypes.bool.def(false),
- sumText: propTypes.string.def('Sum'),
- summaryMethod: {
- type: Function as PropType<(param: { columns: any[]; data: any[] }) => any[]>,
- default: () => undefined
- },
- spanMethod: {
- type: Function as PropType<
- (param: { row: any; column: any; rowIndex: number; columnIndex: number }) => any[]
- >,
- default: () => undefined
- },
- selectOnIndeterminate: propTypes.bool.def(true),
- indent: propTypes.number.def(16),
- lazy: propTypes.bool.def(false),
- load: {
- type: Function as PropType<(row: Recordable, treeNode: any, resolve: Function) => void>,
- default: () => undefined
- },
- treeProps: {
- type: Object as PropType<{ hasChildren?: string; children?: string; label?: string }>,
- default: () => ({ hasChildren: 'hasChildren', children: 'children', label: 'label' })
- },
- tableLayout: {
- type: String as PropType<'auto' | 'fixed'>,
- default: 'fixed'
- },
- scrollbarAlwaysOn: propTypes.bool.def(false),
- flexible: propTypes.bool.def(false)
- },
- emits: ['update:pageSize', 'update:currentPage', 'register'],
- setup(props, { attrs, emit, slots, expose }) {
- const elTableRef = ref<ComponentRef<typeof ElTable>>()
- // 注册
- onMounted(() => {
- const tableRef = unref(elTableRef)
- emit('register', tableRef?.$parent, elTableRef)
- })
- const pageSizeRef = ref(props.pageSize)
- const currentPageRef = ref(props.currentPage)
- // useTable传入的props
- const outsideProps = ref<TableProps>({})
- const mergeProps = ref<TableProps>({})
- const getProps = computed(() => {
- const propsObj = { ...props }
- Object.assign(propsObj, unref(mergeProps))
- return propsObj
- })
- const setProps = (props: TableProps = {}) => {
- mergeProps.value = Object.assign(unref(mergeProps), props)
- outsideProps.value = { ...props } as any
- }
- const setColumn = (columnProps: TableSetProps[], 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)
- }
- }
- }
- }
- const addColumn = (column: TableColumn, index?: number) => {
- const { columns } = unref(getProps)
- if (index) {
- columns.splice(index, 0, column)
- } else {
- columns.push(column)
- }
- }
- const delColumn = (field: string) => {
- const { columns } = unref(getProps)
- const index = columns.findIndex((item) => item.field === field)
- if (index > -1) {
- columns.splice(index, 1)
- }
- }
- const selections = ref<Recordable[]>([])
- const selectionChange = (selection: Recordable[]) => {
- selections.value = selection
- }
- expose({
- setProps,
- setColumn,
- delColumn,
- addColumn,
- selections,
- elTableRef
- })
- const pagination = computed(() => {
- return Object.assign(
- {
- small: false,
- background: false,
- pagerCount: 7,
- layout: 'sizes, prev, pager, next, jumper, ->, total',
- pageSizes: [10, 20, 30, 40, 50, 100],
- disabled: false,
- hideOnSinglePage: false,
- total: 10
- },
- unref(getProps).pagination
- )
- })
- watch(
- () => unref(getProps).pageSize,
- (val: number) => {
- pageSizeRef.value = val
- }
- )
- watch(
- () => unref(getProps).currentPage,
- (val: number) => {
- currentPageRef.value = val
- }
- )
- watch(
- () => pageSizeRef.value,
- (val: number) => {
- emit('update:pageSize', val)
- }
- )
- watch(
- () => currentPageRef.value,
- (val: number) => {
- emit('update:currentPage', val)
- }
- )
- const getBindValue = computed(() => {
- const bindValue: Recordable = { ...attrs, ...unref(getProps) }
- delete bindValue.columns
- delete bindValue.data
- return bindValue
- })
- const renderTreeTableColumn = (columnsChildren: TableColumn[]) => {
- const { align, headerAlign, showOverflowTooltip } = unref(getProps)
- return columnsChildren.map((v) => {
- if (v.hidden) return null
- const props = { ...v } as any
- if (props.children) delete props.children
- const children = v.children
- const slots = {
- default: (...args: any[]) => {
- const data = args[0]
- return children && children.length
- ? renderTreeTableColumn(children)
- : props?.slots?.default
- ? props.slots.default(args)
- : v?.formatter?.(data.row, data.column, data.row[v.field], data.$index) ||
- data.row[v.field]
- }
- }
- if (props?.slots?.header) {
- slots['header'] = (...args: any[]) => props.slots.header(args)
- }
- return (
- <ElTableColumn
- showOverflowTooltip={showOverflowTooltip}
- align={align}
- headerAlign={headerAlign}
- {...props}
- prop={v.field}
- >
- {slots}
- </ElTableColumn>
- )
- })
- }
- const renderTableColumn = (columnsChildren?: TableColumn[]) => {
- const {
- columns,
- reserveIndex,
- pageSize,
- currentPage,
- align,
- headerAlign,
- showOverflowTooltip,
- reserveSelection
- } = unref(getProps)
- return (columnsChildren || columns).map((v) => {
- if (v.hidden) return null
- if (v.type === 'index') {
- return (
- <ElTableColumn
- type="index"
- index={
- v.index ? v.index : (index) => setIndex(reserveIndex, index, pageSize, currentPage)
- }
- align={v.align || align}
- headerAlign={v.headerAlign || headerAlign}
- label={v.label}
- width="65px"
- ></ElTableColumn>
- )
- } else if (v.type === 'selection') {
- return (
- <ElTableColumn
- type="selection"
- reserveSelection={reserveSelection}
- align={align}
- headerAlign={headerAlign}
- width="50"
- ></ElTableColumn>
- )
- } else {
- const props = { ...v } as any
- if (props.children) delete props.children
- const children = v.children
- const slots = {
- default: (...args: any[]) => {
- const data = args[0]
- return children && children.length
- ? renderTreeTableColumn(children)
- : props?.slots?.default
- ? props.slots.default(args)
- : v?.formatter?.(data.row, data.column, data.row[v.field], data.$index) ||
- data.row[v.field]
- }
- }
- if (props?.slots?.header) {
- slots['header'] = (...args: any[]) => props.slots.header(args)
- }
- return (
- <ElTableColumn
- showOverflowTooltip={showOverflowTooltip}
- align={align}
- headerAlign={headerAlign}
- {...props}
- prop={v.field}
- >
- {slots}
- </ElTableColumn>
- )
- }
- })
- }
- return () => (
- <div v-loading={unref(getProps).loading}>
- <ElTable
- ref={elTableRef}
- data={unref(getProps).data}
- onSelection-change={selectionChange}
- {...unref(getBindValue)}
- >
- {{
- default: () => renderTableColumn(),
- empty: () => getSlot(slots, 'empty') || unref(getProps).emptyText,
- append: () => getSlot(slots, 'append')
- }}
- </ElTable>
- {unref(getProps).pagination ? (
- <ElPagination
- v-model:pageSize={pageSizeRef.value}
- v-model:currentPage={currentPageRef.value}
- class="mt-10px"
- {...unref(pagination)}
- ></ElPagination>
- ) : undefined}
- </div>
- )
- }
- })
- </script>
|