TableFixed.vue 5.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161
  1. <script setup lang="ts">
  2. import { ref, defineProps, withDefaults, defineEmits, watch, onBeforeUnmount } from "vue"
  3. import type { Sort, Header } from "@/components/Table/table-partials/models"
  4. import TableHeadRow from "@/components/Table/table-partials/table-content/table-head/TableHeadRow.vue"
  5. import TableBodyRow from "@/components/Table/table-partials/table-content/table-body/TableBodyRow.vue"
  6. const props = withDefaults(
  7. defineProps<{
  8. table: any
  9. header: Array<Header>
  10. data: Array<any>
  11. selectedItems: Array<any>
  12. fixedNumber: number
  13. hasCheckbox?: boolean //是否有选择框
  14. checkboxField?: string //选择框字段
  15. checkMultiple?: boolean //是否多选
  16. multiplePageCheck?: boolean //跨页多选
  17. sortField?: string //排序字段
  18. sortOrder?: "asc" | "desc"
  19. tableBoxClass?: string
  20. tableClass?: string
  21. headerClass?: string
  22. bodyClass?: string
  23. thTrClass?: string
  24. tdTrClass?: string
  25. rowSpanSuffix?: string //rowsapn 的字段后缀 后台应该组成 `${字段}${后缀}`
  26. }>(),
  27. {
  28. hasCheckbox: true,
  29. checkMultiple: false,
  30. multiplePageCheck: false,
  31. checkboxField: "",
  32. sortOrder: "asc",
  33. tableBoxClass: "bg-white",
  34. rowSpanSuffix: "_rowSpan",
  35. }
  36. )
  37. const emits = defineEmits<{
  38. (e: "on-sort", v: Sort): void
  39. (e: "on-select-all", v: any): void
  40. (e: "update:selectedItems", v: Array<any>): void
  41. (e: "on-sort", v: Sort): void
  42. (e: "on-change", isChecked: boolean, v: any, row: any): void
  43. (e: "on-items-all-change", isChecked: boolean, rows: Array<any>): void
  44. }>()
  45. const fixedNum = ref(props.hasCheckbox ? props.fixedNumber + 1 : props.fixedNumber)
  46. const tableBox = ref()
  47. const selectedItems = ref(props.selectedItems)
  48. const allSelectedItems = ref<Array<unknown>>([])
  49. const check = ref<boolean>(false)
  50. const onSelectAll = (checked: any) => {
  51. check.value = checked
  52. if (checked) {
  53. selectedItems.value = [...new Set([...selectedItems.value, ...allSelectedItems.value])]
  54. } else {
  55. selectedItems.value = []
  56. }
  57. emits("on-select-all", check.value)
  58. }
  59. const onSelectItemsChange = (isChecked: boolean, v: any, row: any) => {
  60. emits("update:selectedItems", v)
  61. selectedItems.value = []
  62. v.forEach((item: any) => {
  63. if (!selectedItems.value.includes(item)) selectedItems.value.push(item)
  64. })
  65. check.value =
  66. Array.from(new Set([...selectedItems.value, ...allSelectedItems.value])).length ==
  67. Array.from(new Set([...selectedItems.value])).length
  68. emits("on-change", isChecked, v, row)
  69. }
  70. const onSort = (v: Sort) => {
  71. emits("on-sort", v)
  72. }
  73. function onScroll() {
  74. tableBox.value.className = tableBox.value.className.replace("no-shadow", "")
  75. if (props.table.parentElement) {
  76. const left = props.table.parentElement.scrollLeft
  77. if (left == 0) {
  78. tableBox.value.className += " no-shadow"
  79. }
  80. }
  81. }
  82. watch(
  83. () => props.data,
  84. () => {
  85. selectedItems.value = props.multiplePageCheck ? selectedItems.value : []
  86. allSelectedItems.value = []
  87. check.value = false
  88. props.data.forEach((item: any) => {
  89. if (item[props.checkboxField]) {
  90. allSelectedItems.value.push(item[props.checkboxField])
  91. }
  92. })
  93. //console.log("allSelectedItems:", allSelectedItems.value)
  94. }
  95. )
  96. watch(
  97. () => props.table,
  98. (val: HTMLElement) => {
  99. const tr = val.firstElementChild?.firstElementChild
  100. if (tr && tr.children) {
  101. let width = 0
  102. for (let i = 0; i < fixedNum.value; i++) {
  103. width += tr.children[i]?.clientWidth ?? 0
  104. }
  105. tableBox.value.style.width = width + "px"
  106. tableBox.value.firstElementChild.style.width = val.clientWidth + "px"
  107. }
  108. if (props.table.parentElement) {
  109. props.table.parentElement.addEventListener("scroll", onScroll, true)
  110. }
  111. }
  112. )
  113. onBeforeUnmount(() => {
  114. if (props.table?.parentElement) {
  115. props.table.parentElement.removeEventListener("scroll", onScroll, true)
  116. }
  117. })
  118. </script>
  119. <template>
  120. <div class="fixed-columns no-shadow" ref="tableBox">
  121. <table class="table align-middle fs-6 gy-5 no-footer" :class="tableClass">
  122. <TableHeadRow
  123. @on-sort="onSort"
  124. @on-select-all="onSelectAll"
  125. :checkbox-all-value="check"
  126. :has-checkbox="hasCheckbox"
  127. :check-multiple="checkMultiple"
  128. :sort-field="sortField"
  129. :sort-order="sortOrder"
  130. :header="header"
  131. :header-class="headerClass"
  132. :th-tr-class="thTrClass"
  133. >
  134. <template v-for="(_, name) in $slots" #[name]="{ row: item }">
  135. <slot v-if="name.toString().endsWith('_header')" :name="name" :row="item" />
  136. </template>
  137. </TableHeadRow>
  138. <TableBodyRow
  139. v-if="data.length !== 0"
  140. @on-change="onSelectItemsChange"
  141. v-model:currently-selected-items="selectedItems"
  142. :check-multiple="checkMultiple"
  143. :data="data"
  144. :header="header"
  145. :has-checkbox="hasCheckbox"
  146. :checkbox-field="checkboxField"
  147. :body-class="bodyClass"
  148. :td-tr-class="tdTrClass"
  149. :row-span-suffix="rowSpanSuffix"
  150. >
  151. <template v-for="(_, name) in $slots" v-slot:[name]="{ row: item }">
  152. <slot v-if="!name.toString().endsWith('_header')" :name="name" :row="item" />
  153. </template>
  154. </TableBodyRow>
  155. </table>
  156. </div>
  157. </template>