index.vue 4.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202
  1. <script lang="ts" setup generic="T">
  2. import type { TableInstance, TableProps } from 'element-plus'
  3. import { FilterPayload, SortField, SortOrder, TableContextKey } from './token'
  4. interface Props extends /* @vue-ignore */ Partial<Omit<TableProps<T>, 'data'>> {
  5. data: T[]
  6. loading: boolean
  7. handleQuery?: (payload?: FilterPayload) => void
  8. sortingFields?: SortField[]
  9. sortFn?: (prop: string, order: SortOrder | null) => void
  10. customClass?: boolean
  11. showBorder?: boolean
  12. }
  13. const props = defineProps<Props>()
  14. const emits = defineEmits<{
  15. 'update:sortingFields': [fields: SortField[]]
  16. }>()
  17. const attrs = useAttrs()
  18. const tableRef = ref<TableInstance>()
  19. const defaultOptions: Partial<Props> = {
  20. size: 'default',
  21. stripe: true,
  22. border: true,
  23. highlightCurrentRow: true,
  24. showOverflowTooltip: true,
  25. scrollbarAlwaysOn: true,
  26. showBorder: false,
  27. customClass: false,
  28. tooltipOptions: {
  29. popperClass: 'max-w-120'
  30. }
  31. }
  32. const bindProps = computed(() => {
  33. const { data, sortingFields, ...otherProps } = props
  34. return {
  35. ...defaultOptions,
  36. ...attrs,
  37. ...otherProps,
  38. data: data || []
  39. }
  40. })
  41. const handleDefaultSort = (prop: string, order: SortOrder | null) => {
  42. const newFields = [...(props.sortingFields || [])]
  43. const idx = newFields.findIndex((f) => f.field === prop)
  44. if (order === null) {
  45. if (idx > -1) {
  46. newFields.splice(idx, 1)
  47. }
  48. } else {
  49. if (idx > -1) {
  50. newFields[idx] = { ...newFields[idx], order }
  51. } else {
  52. newFields.push({ field: prop, order })
  53. }
  54. }
  55. emits('update:sortingFields', newFields)
  56. props.handleQuery?.()
  57. }
  58. const safeSortingFields = computed(() => props.sortingFields || [])
  59. const safeData = computed(() => props.data || [])
  60. const safeLoading = computed(() => props.loading)
  61. provide(TableContextKey, {
  62. onQuery: (payload) => props.handleQuery?.(payload),
  63. onSort: (prop, order) => {
  64. if (props.sortFn) {
  65. props.sortFn(prop, order)
  66. } else {
  67. handleDefaultSort(prop, order)
  68. }
  69. },
  70. // 关键:传递响应式的 data 和 sortingFields
  71. data: safeData,
  72. sortingFields: safeSortingFields,
  73. loading: safeLoading
  74. })
  75. defineExpose({
  76. elTableRef: tableRef
  77. })
  78. </script>
  79. <template>
  80. <el-table
  81. ref="tableRef"
  82. v-loading="loading"
  83. :class="{ 'zm-table': !customClass, 'show-border': showBorder }"
  84. v-bind="bindProps"
  85. :data="data"
  86. >
  87. <template v-for="(_, name) in $slots" #[name]="slotData">
  88. <slot :name="name" v-bind="slotData || {}"></slot>
  89. </template>
  90. </el-table>
  91. </template>
  92. <style>
  93. .zm-table {
  94. border-radius: 8px;
  95. &::before,
  96. &::after {
  97. display: none;
  98. }
  99. .el-table__inner-wrapper {
  100. &::before,
  101. &::after {
  102. display: none;
  103. }
  104. }
  105. .el-table__border-left-patch {
  106. display: none;
  107. }
  108. .el-table__cell {
  109. height: 52px;
  110. &:last-child {
  111. border-right: none !important;
  112. }
  113. }
  114. .el-table__header {
  115. border-bottom-right-radius: 8px;
  116. border-bottom-left-radius: 8px;
  117. .el-table__cell {
  118. background: var(--el-fill-color-light) !important;
  119. &:last-child {
  120. .cell {
  121. border-right: none;
  122. }
  123. }
  124. &:first-child {
  125. border-bottom-left-radius: 8px;
  126. }
  127. &:last-child {
  128. border-bottom-right-radius: 8px;
  129. }
  130. }
  131. }
  132. .el-table__body-wrapper {
  133. .el-table__cell {
  134. &:last-child {
  135. border-top-right-radius: 8px;
  136. border-bottom-right-radius: 8px;
  137. }
  138. &:first-child {
  139. border-bottom-left-radius: 8px;
  140. border-top-left-radius: 8px;
  141. }
  142. }
  143. }
  144. }
  145. .zm-table:not(.show-border) {
  146. .el-table__cell {
  147. border: none !important;
  148. }
  149. .el-table__header {
  150. .el-table__cell {
  151. .cell {
  152. border-right: var(--el-table-border);
  153. border-color: var(--el-table-header-text-color);
  154. }
  155. &:last-child {
  156. .cell {
  157. border-right: none;
  158. }
  159. }
  160. }
  161. }
  162. .el-table__row {
  163. &:last-child {
  164. .el-table__cell {
  165. border-bottom: none;
  166. }
  167. }
  168. }
  169. }
  170. </style>