ProTable.vue 7.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299
  1. <script lang="ts">
  2. export default {
  3. inheritAttrs: false
  4. }
  5. </script>
  6. <script setup lang="ts">
  7. import router from '@/router'
  8. import { ElMessage, ElMessageBox, type DialogProps } from 'element-plus'
  9. import type { AdvancedForm, BasicForm, ICRUD } from '@/types/form'
  10. import type { VXEComponent, VxeToolbarProps, VxeToolbarEventProps } from 'vxe-table'
  11. interface CustomTable {
  12. showOperate?: boolean
  13. showEdit?: boolean
  14. showDelete?: boolean
  15. operateWidth?: number
  16. }
  17. interface Props {
  18. crud: ICRUD
  19. pageSize?: number
  20. selection?: boolean
  21. formConfig: BasicForm | AdvancedForm
  22. dialogConfig?: DialogProps
  23. tableConfig?: CustomTable
  24. toolbarConfig?: VXEComponent<VxeToolbarProps, VxeToolbarEventProps>
  25. showToolbar?: boolean
  26. height?: string
  27. }
  28. const props = withDefaults(defineProps<Props>(), {
  29. pageSize: 10,
  30. selection: true,
  31. showToolbar: true
  32. })
  33. const emits = defineEmits(['click-create', 'click-edit'])
  34. const slots = useSlots()
  35. // ============== 查询部分开始 ===============
  36. const query = ref<any>({})
  37. const searchList = ref<any>([])
  38. watch(
  39. () => props.formConfig.formItems,
  40. val => {
  41. val.forEach((item: any) => {
  42. if (item.group) {
  43. searchList.value = searchList.value.concat(item.group.filter((item: any) => item.search))
  44. } else {
  45. item.search && searchList.value.push(item)
  46. }
  47. })
  48. },
  49. { immediate: true }
  50. )
  51. const handleQuery = () => {
  52. curPage.value = 1
  53. getTableData()
  54. }
  55. const handleReset = () => {
  56. query.value = {}
  57. handleQuery()
  58. }
  59. // ============== 查询部分结束 ===============
  60. // ============== 表格部分开始 ===============
  61. const tableData = ref([])
  62. const total = ref(0)
  63. const curPage = ref(1)
  64. const loading = ref(false)
  65. const tableConfig = ref<CustomTable>({
  66. showOperate: true,
  67. showEdit: true,
  68. showDelete: true,
  69. operateWidth: 140,
  70. ...props.tableConfig
  71. })
  72. const xTable = ref<any>()
  73. const xToolbar = ref<any>()
  74. nextTick(() => {
  75. // 将表格和工具栏进行关联
  76. const $table = xTable.value
  77. const $toolbar = xToolbar.value
  78. $toolbar && $table.connect($toolbar)
  79. })
  80. const getTableData = () => {
  81. loading.value = true
  82. props.crud
  83. ?.getList({
  84. ...query.value,
  85. pageSize: props.pageSize,
  86. pageNo: curPage.value
  87. })
  88. .then((res: any) => {
  89. tableData.value = res.list || res.rows
  90. total.value = res.total
  91. })
  92. .finally(() => {
  93. loading.value = false
  94. })
  95. }
  96. watch(
  97. curPage,
  98. () => {
  99. getTableData()
  100. },
  101. {
  102. immediate: true
  103. }
  104. )
  105. const refresh = () => {
  106. curPage.value = 1
  107. getTableData()
  108. }
  109. const multipleSelection = ref<any[]>([])
  110. const handleSelectionChange = () => {
  111. multipleSelection.value = xTable.value.getCheckboxRecords()
  112. }
  113. // ============== 表格部分结束 ===============
  114. // ============== crud部分开始 ===============
  115. const formRoute = ref<any>(props.formConfig.route)
  116. const handleCreate = () => {
  117. emits('click-create')
  118. if (formRoute.value) {
  119. router.push(formRoute.value)
  120. } else {
  121. formData.value = {}
  122. dialogVisible.value = true
  123. }
  124. }
  125. const handleUpdate = (row: any) => {
  126. emits('click-edit', row)
  127. if (formRoute.value) {
  128. router.push(formRoute.value)
  129. } else {
  130. if (props.crud?.getRecord) {
  131. props.crud.getRecord({ id: row.id }).then((res: any) => {
  132. formData.value = res.data
  133. })
  134. } else {
  135. formData.value = { ...row }
  136. }
  137. dialogVisible.value = true
  138. }
  139. }
  140. const handleDelete = (id: string | number) => {
  141. ElMessageBox.confirm('您确定要删除该项吗', '提示', {
  142. type: 'warning'
  143. }).then(async () => {
  144. await props.crud?.delete({ id })
  145. getTableData()
  146. ElMessage({
  147. type: 'success',
  148. message: '删除成功'
  149. })
  150. })
  151. }
  152. const handleBatchDelete = () => {
  153. ElMessageBox.confirm('您确定要删除吗', '提示', {
  154. type: 'warning'
  155. }).then(async () => {
  156. if (props.crud.deleteBatch) {
  157. await props.crud?.deleteBatch({
  158. ids: multipleSelection.value.map(item => item.id).join(',')
  159. })
  160. getTableData()
  161. ElMessage({
  162. type: 'success',
  163. message: '删除成功'
  164. })
  165. } else {
  166. ElMessage({
  167. type: 'error',
  168. message: '未提供deleteBatch方法'
  169. })
  170. }
  171. })
  172. }
  173. // ============== crud部分结束 ===============
  174. // ============== 表单部分开始 ===============
  175. const formData = ref<any>({})
  176. const dialogVisible = ref(false)
  177. const handleFormSuccess = () => {
  178. getTableData()
  179. }
  180. // ============== 表单部分结束 ===============
  181. defineExpose({
  182. handleCreate,
  183. handleDelete,
  184. handleUpdate,
  185. refresh
  186. })
  187. </script>
  188. <template>
  189. <div class="flex flex-col" :style="{ height: height || 'calc(100vh - 101px - var(--main-padding) * 2)' }">
  190. <el-card class="mb-4" shadow="never" v-if="searchList.length || slots.query">
  191. <el-form :inline="true">
  192. <el-form-item :label="item.label" v-for="(item, index) in searchList" :key="index">
  193. <form-comp :item="item" v-model="query[item.name]"></form-comp>
  194. </el-form-item>
  195. <slot name="query" :query="query"></slot>
  196. <el-form-item>
  197. <el-button type="primary" icon="Search" @click="handleQuery">查询</el-button>
  198. <el-button icon="Refresh" @click="handleReset">重置</el-button>
  199. </el-form-item>
  200. </el-form>
  201. </el-card>
  202. <el-card class="h-full flex-grow-1" :body-style="{ height: '100%' }" shadow="never">
  203. <div class="flex flex-col h-full">
  204. <slot name="header"></slot>
  205. <vxe-toolbar ref="xToolbar" v-bind="toolbarConfig" v-if="showToolbar">
  206. <template #buttons>
  207. <el-button type="primary" icon="Plus" @click="handleCreate">新增</el-button>
  208. <el-button
  209. type="danger"
  210. icon="Delete"
  211. @click="handleBatchDelete"
  212. :disabled="!multipleSelection.length"
  213. v-if="selection"
  214. >
  215. 删除
  216. </el-button>
  217. <slot name="toolbar" :selection="multipleSelection"></slot>
  218. </template>
  219. </vxe-toolbar>
  220. <div class="h-full flex-grow">
  221. <vxe-table
  222. ref="xTable"
  223. id="xProTable"
  224. size="medium"
  225. height="100%"
  226. :data="tableData"
  227. :row-config="{ isHover: true }"
  228. v-loading="loading"
  229. v-bind="$attrs"
  230. @checkbox-change="handleSelectionChange"
  231. @checkbox-all="handleSelectionChange"
  232. >
  233. <vxe-column type="checkbox" width="50" v-if="selection"></vxe-column>
  234. <slot></slot>
  235. <vxe-column fixed="right" title="操作" :width="tableConfig.operateWidth" v-if="tableConfig.showOperate">
  236. <template #default="{ row }">
  237. <slot name="operateBefore" :row="row"></slot>
  238. <el-button type="primary" size="small" @click="handleUpdate(row)" v-if="tableConfig.showEdit">
  239. 编辑
  240. </el-button>
  241. <el-button type="danger" size="small" @click="handleDelete(row.id)" v-if="tableConfig.showDelete">
  242. 删除
  243. </el-button>
  244. <slot name="operateAfter" :row="row"></slot>
  245. </template>
  246. </vxe-column>
  247. </vxe-table>
  248. </div>
  249. <div class="flex justify-end shrink-0">
  250. <el-pagination
  251. background
  252. layout="prev, pager, next, jumper, total"
  253. v-model:current-page="curPage"
  254. :page-size="pageSize"
  255. :total="total"
  256. class="mt-16px"
  257. />
  258. </div>
  259. </div>
  260. </el-card>
  261. <dialog-form
  262. v-model="dialogVisible"
  263. :dialogConfig="dialogConfig"
  264. :formConfig="formConfig"
  265. :formData="formData"
  266. :create="crud.create"
  267. :update="crud.update"
  268. @success="handleFormSuccess"
  269. v-if="dialogVisible"
  270. />
  271. </div>
  272. </template>
  273. <style scoped>
  274. .vxe-toolbar {
  275. padding-top: 0;
  276. }
  277. </style>