ProTable.vue 7.5 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262
  1. <script setup lang="ts">
  2. defineOptions({
  3. inheritAttrs: false
  4. })
  5. import { useForm } from '@/hooks/useForm'
  6. import { useTableQuery } from '@/hooks/useTableQuery'
  7. import { useTable } from '@/hooks/useTable'
  8. import type { DialogProps, DrawerProps } from 'element-plus'
  9. import type { BasicForm, ICRUD } from '@/types/form'
  10. import type { VxeToolbarProps } from 'vxe-table'
  11. interface CustomTable {
  12. showOperate?: boolean
  13. showEdit?: boolean
  14. showView?: boolean
  15. showDelete?: boolean
  16. operateWidth?: number
  17. }
  18. interface Props {
  19. crud: ICRUD
  20. pageSize?: number
  21. selection?: boolean
  22. formConfig: BasicForm
  23. dialogConfig?: Partial<DialogProps>
  24. drawerConfig?: Partial<DrawerProps>
  25. tableConfig?: CustomTable
  26. toolbarConfig?: VxeToolbarProps
  27. showToolbar?: boolean
  28. height?: string
  29. formMode?: 'dialog' | 'drawer'
  30. beforeCreate?: Function
  31. }
  32. const props = withDefaults(defineProps<Props>(), {
  33. pageSize: 10,
  34. selection: true,
  35. showToolbar: true,
  36. formMode: 'dialog'
  37. })
  38. const emits = defineEmits(['click-create', 'click-edit', 'click-view', 'checkbox-change', 'click-reset'])
  39. const slots = useSlots()
  40. // ============== 查询部分开始 ===============
  41. const { query, searchList, handleQuery, handleReset } = useTableQuery(props, emits)
  42. // ============== 查询部分结束 ===============
  43. // ============== 表格部分开始 ===============
  44. const {
  45. tableData,
  46. formData,
  47. total,
  48. curPage,
  49. loading,
  50. formVisible,
  51. multipleSelection,
  52. refresh,
  53. handleCreate,
  54. handleDelete,
  55. handleBatchDelete,
  56. handleUpdate,
  57. handleView,
  58. handleFormSuccess
  59. } = useTable(props, emits)
  60. const tableConfig = computed<CustomTable>(() => ({
  61. showOperate: true,
  62. showView: false,
  63. showEdit: true,
  64. showDelete: true,
  65. operateWidth: 140,
  66. ...props.tableConfig
  67. }))
  68. const xTable = ref<any>()
  69. const xToolbar = ref<any>()
  70. nextTick(() => {
  71. // 将表格和工具栏进行关联
  72. const $table = xTable.value
  73. const $toolbar = xToolbar.value
  74. $toolbar && $table.connect($toolbar)
  75. })
  76. const handleSelectionChange = () => {
  77. multipleSelection.value = xTable.value.getCheckboxRecords()
  78. emits('checkbox-change', multipleSelection.value)
  79. }
  80. // 动态计算table高度
  81. const toolbarRef = ref<any>(null)
  82. const paginationRef = ref<any>(null)
  83. const tableHeight = ref()
  84. const calcHeight = () => {
  85. const toolbarHeight = toolbarRef.value ? toolbarRef.value.offsetHeight : 0
  86. const paginationHeight = paginationRef.value ? paginationRef.value.offsetHeight : 0
  87. tableHeight.value = `calc(100% - ${toolbarHeight + paginationHeight}px)`
  88. }
  89. nextTick(() => {
  90. calcHeight()
  91. })
  92. watch(
  93. () => props.showToolbar,
  94. () => {
  95. nextTick(() => {
  96. calcHeight()
  97. })
  98. }
  99. )
  100. // ============== 表格部分结束 ===============
  101. // 构造表单插槽
  102. const { formSlots } = useForm(props)
  103. defineExpose({
  104. handleCreate,
  105. handleDelete,
  106. handleUpdate,
  107. handleView,
  108. handleQuery,
  109. handleReset,
  110. refresh,
  111. formData,
  112. query,
  113. table: xTable
  114. })
  115. </script>
  116. <template>
  117. <div class="flex flex-col" style="max-height: 100vh" :style="{ height: height || '100%' }">
  118. <el-card
  119. class="mb-4 shrink-0"
  120. shadow="never"
  121. :body-style="{ 'padding-bottom': '0' }"
  122. v-if="searchList.length || slots.query"
  123. >
  124. <el-form :inline="true">
  125. <el-form-item :label="item.label" v-for="(item, index) in searchList" :key="index">
  126. <form-comp :item="item" v-model="query[item.name]">
  127. <template #[slot.alias] v-for="slot in item.slots" :key="slot.alias">
  128. <slot :name="slot.alias"></slot>
  129. </template>
  130. </form-comp>
  131. </el-form-item>
  132. <slot name="query" :query="query"></slot>
  133. <el-form-item>
  134. <el-button type="primary" icon="Search" @click="handleQuery">查询</el-button>
  135. <el-button icon="Refresh" @click="handleReset">重置</el-button>
  136. </el-form-item>
  137. </el-form>
  138. </el-card>
  139. <el-card class="h-full flex-grow" :body-style="{ height: '100%' }" shadow="never">
  140. <div class="flex flex-col h-full">
  141. <div ref="toolbarRef" class="shrink-0">
  142. <slot name="header"></slot>
  143. <vxe-toolbar ref="xToolbar" v-bind="toolbarConfig" v-if="showToolbar">
  144. <template #buttons>
  145. <el-button type="primary" icon="Plus" @click="handleCreate">新增</el-button>
  146. <el-button
  147. type="danger"
  148. icon="Delete"
  149. @click="handleBatchDelete"
  150. :disabled="!multipleSelection.length"
  151. v-if="selection"
  152. >
  153. 删除
  154. </el-button>
  155. <slot name="toolbar" :selection="multipleSelection"></slot>
  156. </template>
  157. </vxe-toolbar>
  158. </div>
  159. <div :style="{ height: tableHeight }">
  160. <vxe-table
  161. ref="xTable"
  162. id="xProTable"
  163. size="medium"
  164. auto-resize
  165. height="auto"
  166. :data="tableData"
  167. :row-config="{ isHover: true }"
  168. v-loading="loading"
  169. v-bind="$attrs"
  170. @checkbox-change="handleSelectionChange"
  171. @checkbox-all="handleSelectionChange"
  172. >
  173. <vxe-column type="checkbox" width="50" v-if="selection"></vxe-column>
  174. <slot></slot>
  175. <vxe-column fixed="right" title="操作" :width="tableConfig.operateWidth" v-if="tableConfig.showOperate">
  176. <template #default="{ row }">
  177. <slot name="operateBefore" :row="row"></slot>
  178. <el-button size="small" @click="handleView(row)" v-if="tableConfig.showView"> 查看 </el-button>
  179. <el-button type="primary" size="small" @click="handleUpdate(row)" v-if="tableConfig.showEdit">
  180. 编辑
  181. </el-button>
  182. <el-button type="danger" size="small" @click="handleDelete(row.id)" v-if="tableConfig.showDelete">
  183. 删除
  184. </el-button>
  185. <slot name="operateAfter" :row="row"></slot>
  186. </template>
  187. </vxe-column>
  188. </vxe-table>
  189. </div>
  190. <div ref="paginationRef">
  191. <el-pagination
  192. background
  193. layout="->, prev, pager, next, total"
  194. v-model:current-page="curPage"
  195. :page-size="pageSize"
  196. :total="total"
  197. class="mt-16px"
  198. />
  199. </div>
  200. </div>
  201. </el-card>
  202. </div>
  203. <template v-if="formMode === 'dialog'">
  204. <dialog-form
  205. v-model="formVisible"
  206. v-if="formVisible"
  207. :dialogConfig="dialogConfig"
  208. :formConfig="formConfig"
  209. :formData="formData"
  210. :formSlots="formSlots"
  211. :create="crud.create"
  212. :update="crud.update"
  213. @success="handleFormSuccess"
  214. >
  215. <template #[slot.alias]="slotProps" v-for="slot in formSlots" :key="slot.alias">
  216. <slot :name="slot.alias" v-bind="slotProps"></slot>
  217. </template>
  218. </dialog-form>
  219. </template>
  220. <template v-else>
  221. <drawer-form
  222. v-model="formVisible"
  223. v-if="formVisible"
  224. :drawerConfig="drawerConfig"
  225. :formConfig="formConfig"
  226. :formData="formData"
  227. :formSlots="formSlots"
  228. :create="crud.create"
  229. :update="crud.update"
  230. @success="handleFormSuccess"
  231. >
  232. <template #[slot.alias]="slotProps" v-for="slot in formSlots" :key="slot.alias">
  233. <slot :name="slot.alias" v-bind="slotProps"></slot>
  234. </template>
  235. </drawer-form>
  236. </template>
  237. </template>
  238. <style scoped>
  239. .vxe-toolbar {
  240. padding-top: 0;
  241. }
  242. :deep(.el-card) {
  243. --el-card-padding: 16px;
  244. border: none !important;
  245. }
  246. </style>