ProTable.vue 7.7 KB

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