ProTable.vue 6.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261
  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 } from '@/types/form'
  10. interface CRUD {
  11. create: Function
  12. update: Function
  13. delete: Function
  14. deleteBatch: Function
  15. getList: Function
  16. getRecord: Function
  17. }
  18. interface CustomTable {
  19. showOperate?: boolean
  20. operateWidth?: number
  21. }
  22. interface Props {
  23. crud: CRUD
  24. pageSize?: number
  25. selection?: boolean
  26. formConfig: BasicForm | AdvancedForm
  27. dialogConfig?: DialogProps
  28. tableConfig?: CustomTable
  29. showToolbar?: boolean
  30. }
  31. const props = withDefaults(defineProps<Props>(), {
  32. pageSize: 10,
  33. selection: true,
  34. showToolbar: true
  35. })
  36. // ============== 查询部分开始 ===============
  37. const query = ref<any>({})
  38. const searchList = ref<any>([])
  39. watch(
  40. () => props.formConfig.formItems,
  41. val => {
  42. val.forEach((item: any) => {
  43. if (item.group) {
  44. searchList.value = searchList.value.concat(item.group.filter((item: any) => item.search))
  45. } else {
  46. item.search && searchList.value.push(item)
  47. }
  48. })
  49. },
  50. { immediate: true }
  51. )
  52. const handleQuery = () => {
  53. curPage.value = 1
  54. getTableData()
  55. }
  56. const handleReset = () => {
  57. query.value = {}
  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. operateWidth: 120,
  68. ...props.tableConfig
  69. })
  70. const getTableData = () => {
  71. loading.value = true
  72. props.crud
  73. ?.getList({
  74. ...query.value,
  75. pageSize: props.pageSize,
  76. pageNo: curPage.value
  77. })
  78. .then((res: any) => {
  79. tableData.value = res.data
  80. total.value = res.total
  81. })
  82. .finally(() => {
  83. loading.value = false
  84. })
  85. }
  86. getTableData()
  87. const pageChange = (val: number) => {
  88. curPage.value = val
  89. getTableData()
  90. }
  91. const refresh = () => {
  92. curPage.value = 1
  93. getTableData()
  94. }
  95. const multipleSelection = ref<any[]>([])
  96. const handleSelectionChange = (columns: any[]) => {
  97. multipleSelection.value = columns
  98. }
  99. // ============== 表格部分结束 ===============
  100. // ============== crud部分开始 ===============
  101. const formRoute = ref<any>(props.formConfig.route)
  102. const handleCreate = () => {
  103. if (formRoute.value) {
  104. router.push(formRoute.value)
  105. } else {
  106. formData.value = {}
  107. dialogVisible.value = true
  108. }
  109. }
  110. const handleUpdate = (row: any) => {
  111. if (formRoute.value) {
  112. router.push(formRoute.value)
  113. } else {
  114. if (props.crud?.getRecord) {
  115. props.crud.getRecord({ id: row.id }).then((res: any) => {
  116. formData.value = res.data
  117. })
  118. } else {
  119. formData.value = { ...row }
  120. }
  121. dialogVisible.value = true
  122. }
  123. }
  124. const handleDelete = (id: string | number) => {
  125. ElMessageBox.confirm('您确定要删除该项吗', '提示', {
  126. type: 'warning'
  127. }).then(async () => {
  128. await props.crud?.delete({ id })
  129. getTableData()
  130. ElMessage({
  131. type: 'success',
  132. message: '删除成功'
  133. })
  134. })
  135. }
  136. const handleBatchDelete = () => {
  137. ElMessageBox.confirm('您确定要删除吗', '提示', {
  138. type: 'warning'
  139. }).then(async () => {
  140. await props.crud?.deleteBatch({
  141. ids: multipleSelection.value.map(item => item.id).join(',')
  142. })
  143. getTableData()
  144. ElMessage({
  145. type: 'success',
  146. message: '删除成功'
  147. })
  148. })
  149. }
  150. // ============== crud部分结束 ===============
  151. // ============== 表单部分开始 ===============
  152. const formData = ref<any>({})
  153. const dialogVisible = ref(false)
  154. const handleFormSuccess = () => {
  155. getTableData()
  156. }
  157. // ============== 表单部分结束 ===============
  158. defineExpose({
  159. handleCreate,
  160. handleDelete,
  161. handleUpdate,
  162. refresh
  163. })
  164. </script>
  165. <template>
  166. <div class="flex flex-col" style="height: calc(100vh - 101px - var(--main-padding) * 2)">
  167. <el-card class="mb-4" shadow="never" v-if="searchList.length">
  168. <el-form :inline="true">
  169. <el-form-item :label="item.label" v-for="(item, index) in searchList" :key="index">
  170. <form-comp :item="item" v-model="query[item.name]"></form-comp>
  171. </el-form-item>
  172. <slot name="query" :query="query"></slot>
  173. <el-form-item>
  174. <el-button type="primary" icon="Search" @click="handleQuery">查询</el-button>
  175. <el-button icon="Refresh" @click="handleReset">重置</el-button>
  176. </el-form-item>
  177. </el-form>
  178. </el-card>
  179. <el-card class="h-full flex-grow-1" :body-style="{ height: '100%' }" shadow="never">
  180. <div class="flex flex-col h-full">
  181. <slot name="header"></slot>
  182. <div class="flex justify-between mb-20px" v-if="showToolbar">
  183. <div>
  184. <el-button type="primary" icon="Plus" @click="handleCreate">新增</el-button>
  185. <el-button
  186. type="danger"
  187. icon="Delete"
  188. @click="handleBatchDelete"
  189. :disabled="!multipleSelection.length"
  190. v-if="selection"
  191. >
  192. 删除
  193. </el-button>
  194. <slot name="toolbar"></slot>
  195. </div>
  196. </div>
  197. <el-table
  198. class="flex-grow-1 h-full"
  199. :data="tableData"
  200. v-loading="loading"
  201. v-bind="$attrs"
  202. @selection-change="handleSelectionChange"
  203. >
  204. <el-table-column type="selection" width="50" v-if="selection"></el-table-column>
  205. <slot></slot>
  206. <el-table-column fixed="right" label="操作" :width="tableConfig.operateWidth" v-if="tableConfig.showOperate">
  207. <template #default="{ row }">
  208. <slot name="operateBefore" :row="row"></slot>
  209. <el-button link type="primary" size="small" @click="handleUpdate(row)">编辑</el-button>
  210. <el-button link type="danger" size="small" @click="handleDelete(row.id)">删除</el-button>
  211. <slot name="operateAfter" :row="row"></slot>
  212. </template>
  213. </el-table-column>
  214. </el-table>
  215. <div class="flex justify-end shrink-0">
  216. <el-pagination
  217. background
  218. layout="prev, pager, next, jumper, total"
  219. :page-size="pageSize"
  220. :total="total"
  221. @current-change="pageChange"
  222. class="mt-16px"
  223. />
  224. </div>
  225. </div>
  226. </el-card>
  227. <dialog-form
  228. v-model="dialogVisible"
  229. :dialogConfig="dialogConfig"
  230. :formConfig="formConfig"
  231. :formData="formData"
  232. :create="crud.create"
  233. :update="crud.update"
  234. @success="handleFormSuccess"
  235. v-if="dialogVisible"
  236. />
  237. </div>
  238. </template>
  239. <style scoped>
  240. :deep(.el-table th.el-table__cell) {
  241. background-color: #f5f7fa;
  242. color: rgb(31, 34, 37);
  243. }
  244. </style>