index.vue 8.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280
  1. <template>
  2. <div class="hc-table-ref-box" :class="ui">
  3. <el-table ref="tableRef" hc :data="tableData" :height="heights" v-loading="isLoading" stripe :row-key="rowKey" :border="isBorder" :highlight-current-row="isCurrentRow" @selection-change="tableSelectionChange"
  4. @row-click="tableRowClick" @row-dblclick="tableRowDblClick" @row-contextmenu="tableRowContextmenu" @cell-click="tableCellClick" @cell-dblclick="tableCellDblClick"
  5. @cell-contextmenu="tableCellContextmenu" style="width: 100%;">
  6. <el-table-column type="selection" width="50" v-if="isCheck"/>
  7. <el-table-column type="index" prop="num" label="序号" width="80" v-if="isIndex && !isSlotNum">
  8. <template #default="scope">
  9. <div class="table-column-sort">
  10. <span class="mr-3">{{scope.$index + 1}}</span>
  11. <template v-if="isDataSort">
  12. <span class="text-link text-lg">
  13. <HcIcon name="arrow-up" @click="upSortClick(scope.$index)"/>
  14. </span>
  15. <span class="text-link text-lg">
  16. <HcIcon name="arrow-down" @click="downSortClick(scope.$index)"/>
  17. </span>
  18. </template>
  19. </div>
  20. </template>
  21. </el-table-column>
  22. <el-table-column type="index" prop="num" width="100" v-if="isIndex && isSlotNum">
  23. <template #header>
  24. <div class="table-column-header-num">
  25. <span class="mr-3">序号</span>
  26. <slot name='table-column-header-num'></slot>
  27. </div>
  28. </template>
  29. </el-table-column>
  30. <template v-for="item in columns">
  31. <el-table-column :prop="item.key" :label="item.name" :align="item.align ?? 'left'" :width="item.width ?? ''" :fixed="item.fixed ?? false">
  32. <template #default="scope" v-if="item.isSlot">
  33. <slot :name='item.key' :row="scope.row" :index="scope.$index"/>
  34. </template>
  35. </el-table-column>
  36. </template>
  37. </el-table>
  38. </div>
  39. </template>
  40. <script setup>
  41. import {ref,watch,nextTick,useSlots} from "vue";
  42. import Sortable from 'sortablejs'
  43. const props = defineProps({
  44. ui: {
  45. type: String,
  46. default: ''
  47. },
  48. column: {
  49. type: Array,
  50. default: () => ([])
  51. },
  52. datas: {
  53. type: Array,
  54. default: () => ([])
  55. },
  56. loading: {
  57. type: Boolean,
  58. default: false
  59. },
  60. isCheck: {
  61. type: Boolean,
  62. default: false
  63. },
  64. isIndex: {
  65. type: Boolean,
  66. default: true
  67. },
  68. rowKey: {
  69. type: String,
  70. default: 'id'
  71. },
  72. border: {
  73. type: Boolean,
  74. default: false
  75. },
  76. isRowDrop: {
  77. type: Boolean,
  78. default: false
  79. },
  80. isCurrentRow: {
  81. type: Boolean,
  82. default: false
  83. },
  84. heights: {
  85. type: String,
  86. default: '100%'
  87. },
  88. isSort: {
  89. type: Boolean,
  90. default: false
  91. },
  92. })
  93. //初始变量
  94. const tableRef = ref(null)
  95. const columns = ref(props.column)
  96. const tableData = ref(props.datas)
  97. const isLoading = ref(props.loading)
  98. const isBorder = ref(props.border)
  99. const isDataSort = ref(props.isSort)
  100. //监听
  101. watch(() => [
  102. props.datas,
  103. props.loading,
  104. props.border,
  105. props.isSort,
  106. ], ([datas, loading, border, isSort]) => {
  107. tableData.value = datas;
  108. isLoading.value = loading;
  109. isBorder.value = border;
  110. isDataSort.value = isSort;
  111. })
  112. //监听表头
  113. watch(() => [
  114. props.column
  115. ], ([column]) => {
  116. columns.value = column;
  117. setIsSlots()
  118. })
  119. //加载完成
  120. nextTick(()=>{
  121. setIsSlots()
  122. if (props.isRowDrop && props.ui) {
  123. tableRowDropInit(props.ui)
  124. }
  125. })
  126. //判断<slot>是否有传值
  127. const slots = useSlots()
  128. const isSlotNum = !!slots['table-column-header-num']
  129. const setIsSlots = () => {
  130. let arr = columns.value
  131. for (let i = 0; i < arr.length; i++) {
  132. arr[i].isSlot = !!slots[arr[i].key]
  133. }
  134. columns.value = arr
  135. }
  136. //事件
  137. const emit = defineEmits([
  138. 'selection-change', 'row-drop', 'row-sort',
  139. 'row-click', 'row-dblclick', 'row-contextmenu',
  140. 'cell-click', 'cell-dblclick', 'cell-contextmenu'
  141. ])
  142. //清空多选
  143. const clearSelection = () => {
  144. tableRef.value?.clearSelection()
  145. emit('selection-change', [])
  146. }
  147. //返回当前选中的行
  148. const getSelectionRows = () => {
  149. tableRef.value?.getSelectionRows()
  150. }
  151. //用于多选表格,切换某一行的选中状态, 如果使用了第二个参数,则可直接设置这一行选中与否
  152. const toggleRowSelection = (row, selected) => {
  153. tableRef.value?.toggleRowSelection(row, selected)
  154. }
  155. //多选
  156. const tableSelectionChange = (rows) => {
  157. let tableRows = rows.filter((item) => {
  158. return (item??'') !== '';
  159. })
  160. emit('selection-change', tableRows)
  161. }
  162. //拖动排序处理
  163. const tableRowDropInit = (ui) => {
  164. const tbody = document.querySelector(`.${ui} .el-table__body-wrapper tbody`)
  165. Sortable.create(tbody, {
  166. onEnd({ newIndex, oldIndex }) {
  167. const rows = tableData.value
  168. const currRow = rows.splice(oldIndex, 1)[0]
  169. rows.splice(newIndex, 0, currRow)
  170. tableData.value = rows
  171. emit('row-drop', rows)
  172. }
  173. })
  174. }
  175. //当某一行被点击时会触发该事件
  176. const tableRowClick = (row, column, event) => {
  177. emit('row-click', {row, column, event})
  178. }
  179. //当某一行被双击时会触发该事件
  180. const tableRowDblClick = (row, column, event) => {
  181. emit('row-dblclick', {row, column, event})
  182. }
  183. //当某一行被鼠标右键点击时会触发该事件
  184. const tableRowContextmenu = (row, column, event) => {
  185. emit('row-contextmenu', {row, column, event})
  186. }
  187. //当某个单元格被点击时会触发该事件
  188. const tableCellClick = (row, column, cell, event) => {
  189. emit('cell-click', {row, column, cell, event})
  190. }
  191. //当某个单元格被双击击时会触发该事件
  192. const tableCellDblClick = (row, column, cell, event) => {
  193. emit('cell-dblclick', {row, column, cell, event})
  194. }
  195. //当某个单元格被鼠标右键点击时会触发该事件
  196. const tableCellContextmenu = (row, column, cell, event) => {
  197. emit('cell-contextmenu', {row, column, cell, event})
  198. }
  199. //向上排序
  200. const upSortClick = (index) => {
  201. const data = tableData.value
  202. if(index !== 0) {
  203. const tmp = data.splice(index - 1,1);
  204. tableData.value.splice(index, 0, tmp[0]);
  205. emit('row-sort', tableData.value)
  206. } else {
  207. window?.$message?.warning('已经处于置顶,无法上移')
  208. }
  209. }
  210. //向下排序
  211. const downSortClick = (index) => {
  212. const indexs = index + 1
  213. const data = tableData.value
  214. if(indexs !== data.length) {
  215. const tmp = data.splice(indexs, 1);
  216. tableData.value.splice(index, 0, tmp[0]);
  217. emit('row-sort', tableData.value)
  218. } else {
  219. window?.$message?.warning('已经处于置底,无法下移')
  220. }
  221. }
  222. // 暴露出去
  223. defineExpose({
  224. clearSelection,
  225. getSelectionRows,
  226. toggleRowSelection
  227. })
  228. </script>
  229. <style lang="scss">
  230. .hc-table-ref-box {
  231. height: 100%;
  232. .el-scrollbar .el-scrollbar__bar.is-vertical {
  233. right: 0;
  234. }
  235. .el-scrollbar .el-scrollbar__bar.is-horizontal {
  236. bottom: 2px;
  237. }
  238. .el-table__body-wrapper tr td.el-table-fixed-column--left,
  239. .el-table__body-wrapper tr td.el-table-fixed-column--right,
  240. .el-table__body-wrapper tr th.el-table-fixed-column--left,
  241. .el-table__body-wrapper tr th.el-table-fixed-column--right,
  242. .el-table__footer-wrapper tr td.el-table-fixed-column--left,
  243. .el-table__footer-wrapper tr td.el-table-fixed-column--right,
  244. .el-table__footer-wrapper tr th.el-table-fixed-column--left,
  245. .el-table__footer-wrapper tr th.el-table-fixed-column--right,
  246. .el-table__header-wrapper tr td.el-table-fixed-column--left,
  247. .el-table__header-wrapper tr td.el-table-fixed-column--right,
  248. .el-table__header-wrapper tr th.el-table-fixed-column--left,
  249. .el-table__header-wrapper tr th.el-table-fixed-column--right {
  250. --el-bg-color: #ebeef0;
  251. }
  252. .table-column-header-num {
  253. position: relative;
  254. display: flex;
  255. align-items: center;
  256. }
  257. }
  258. </style>