树形表格.vue 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357
  1. <template>
  2. <div class="layout-padding">
  3. <div class="layout-padding-auto layout-padding-view">
  4. #if($queryList)
  5. <!-- 查询表单区域 -->
  6. <el-row v-show="showSearch">
  7. <el-form :model="state.queryForm" ref="queryRef" :inline="true" @keyup.enter="getDataList">
  8. #foreach($field in $queryList)
  9. #if($field.queryFormType == 'select')
  10. <el-form-item label="#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end" prop="${field.attrName}">
  11. <el-select v-model="state.queryForm.${field.attrName}" placeholder="请选择#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end">
  12. #if($field.fieldDict)
  13. <el-option
  14. :label="item.label"
  15. :value="item.value"
  16. v-for="(item, index) in ${field.fieldDict}"
  17. :key="index"
  18. />
  19. #else
  20. <el-option label="请选择" value="0" />
  21. #end
  22. </el-select>
  23. </el-form-item>
  24. #elseif($field.queryFormType == 'date')
  25. <el-form-item label="#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end" prop="${field.attrName}">
  26. <el-date-picker
  27. type="date"
  28. placeholder="请输入#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end"
  29. v-model="state.queryForm.${field.attrName}"
  30. :value-format="dateStr"
  31. />
  32. </el-form-item>
  33. #elseif($field.queryFormType == 'datetime')
  34. <el-form-item label="#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end" prop="${field.attrName}">
  35. <el-date-picker
  36. type="datetime"
  37. placeholder="请输入#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end"
  38. v-model="state.queryForm.${field.attrName}"
  39. :value-format="dateTimeStr"
  40. />
  41. </el-form-item>
  42. #elseif($field.formType == 'radio')
  43. <el-form-item label="#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end" prop="${field.attrName}">
  44. <el-radio-group v-model="state.queryForm.${field.attrName}">
  45. #if($field.fieldDict)
  46. <el-radio
  47. :label="item.value"
  48. v-for="(item, index) in ${field.fieldDict}"
  49. border
  50. :key="index"
  51. >
  52. {{ item.label }}
  53. </el-radio>
  54. #else
  55. <el-radio label="${field.fieldComment}" border>
  56. ${field.fieldComment}
  57. </el-radio>
  58. #end
  59. </el-radio-group>
  60. </el-form-item>
  61. #else
  62. <el-form-item label="#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end" prop="${field.attrName}">
  63. <el-input
  64. placeholder="请输入#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end"
  65. v-model="state.queryForm.${field.attrName}"
  66. />
  67. </el-form-item>
  68. #end
  69. #end
  70. <el-form-item>
  71. <el-button icon="search" type="primary" @click="getDataList">
  72. 查询
  73. </el-button>
  74. <el-button icon="Refresh" @click="resetQuery">重置</el-button>
  75. </el-form-item>
  76. </el-form>
  77. </el-row>
  78. #end
  79. <!-- 操作按钮区域 -->
  80. <el-row>
  81. <div class="mb8" style="width: 100%">
  82. <el-button
  83. icon="folder-add"
  84. type="primary"
  85. class="ml10"
  86. @click="formDialogRef.openDialog()"
  87. v-auth="'${moduleName}_${functionName}_add'"
  88. >
  89. 新增
  90. </el-button>
  91. <el-button
  92. icon="sort"
  93. type="primary"
  94. class="ml10"
  95. @click="expandAll"
  96. >
  97. 展开/折叠
  98. </el-button>
  99. <el-button
  100. plain
  101. icon="upload-filled"
  102. type="primary"
  103. class="ml10"
  104. @click="excelUploadRef.show()"
  105. v-auth="'${moduleName}_${functionName}_add'"
  106. >
  107. 导入
  108. </el-button>
  109. <el-button
  110. plain
  111. :disabled="multiple"
  112. icon="Delete"
  113. type="primary"
  114. v-auth="'${moduleName}_${functionName}_del'"
  115. @click="handleDelete(selectObjs)"
  116. >
  117. 删除
  118. </el-button>
  119. <right-toolbar
  120. v-model:showSearch="showSearch"
  121. :export="'${moduleName}_${functionName}_export'"
  122. @exportExcel="exportExcel"
  123. class="ml10 mr20"
  124. style="float: right;"
  125. @queryTable="getDataList"
  126. />
  127. </div>
  128. </el-row>
  129. <!-- 树形数据表格区域 -->
  130. <el-table
  131. :data="state.dataList"
  132. v-loading="state.loading"
  133. border
  134. row-key="${pk.attrName}"
  135. :tree-props="{ children: 'children', hasChildren: 'hasChildren' }"
  136. :cell-style="tableStyle.cellStyle"
  137. :header-cell-style="tableStyle.headerCellStyle"
  138. @selection-change="selectionChangHandle"
  139. :default-expand-all="isExpandAll"
  140. >
  141. <el-table-column type="selection" width="40" align="center" />
  142. <el-table-column type="index" label="#" width="40" />
  143. #foreach($field in $gridList)
  144. #if($field.fieldDict)
  145. <el-table-column prop="${field.attrName}" label="#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end" show-overflow-tooltip>
  146. <template #default="scope">
  147. <dict-tag :options="$field.fieldDict" :value="scope.row.${field.attrName}" />
  148. </template>
  149. </el-table-column>
  150. #else
  151. <el-table-column
  152. prop="${field.attrName}"
  153. label="#if(${field.fieldComment})${field.fieldComment}#else${field.attrName}#end"
  154. show-overflow-tooltip
  155. #if($field == $gridList[0])
  156. width="200"
  157. #end
  158. />
  159. #end
  160. #end
  161. <el-table-column label="操作" width="200" fixed="right">
  162. <template #default="scope">
  163. <el-button
  164. icon="plus"
  165. text
  166. type="primary"
  167. v-auth="'${moduleName}_${functionName}_add'"
  168. @click="formDialogRef.openDialog('', scope.row.${pk.attrName})"
  169. >
  170. 新增
  171. </el-button>
  172. <el-button
  173. icon="edit-pen"
  174. text
  175. type="primary"
  176. v-auth="'${moduleName}_${functionName}_edit'"
  177. @click="formDialogRef.openDialog(scope.row.${pk.attrName})"
  178. >
  179. 编辑
  180. </el-button>
  181. <el-button
  182. icon="delete"
  183. text
  184. type="primary"
  185. v-auth="'${moduleName}_${functionName}_del'"
  186. @click="handleDelete([scope.row.${pk.attrName}])"
  187. >
  188. 删除
  189. </el-button>
  190. </template>
  191. </el-table-column>
  192. </el-table>
  193. </div>
  194. <!-- 编辑、新增弹窗 -->
  195. <form-dialog ref="formDialogRef" @refresh="getDataList(false)" />
  196. <!-- 导入excel弹窗 (需要在 upms-biz/resources/file 下维护模板) -->
  197. <upload-excel
  198. ref="excelUploadRef"
  199. title="导入"
  200. url="/${moduleName}/${functionName}/import"
  201. temp-url="/admin/sys-file/local/file/${functionName}.xlsx"
  202. @refreshDataList="getDataList"
  203. />
  204. </div>
  205. </template>
  206. <script setup lang="ts" name="system${ClassName}Tree">
  207. // ========== 导入声明 ==========
  208. import { BasicTableProps, useTable } from "/@/hooks/table";
  209. import { fetchTreeList, delObjs } from "/@/api/${moduleName}/${functionName}";
  210. import { useMessage, useMessageBox } from "/@/hooks/message";
  211. import { useDict } from '/@/hooks/dict';
  212. // ========== 组件声明 ==========
  213. // 异步加载表单弹窗组件
  214. const FormDialog = defineAsyncComponent(() => import('./form.vue'));
  215. // ========== 字典数据 ==========
  216. #set($fieldDict=[])
  217. #foreach($field in $queryList)
  218. #if($field.fieldDict)
  219. #set($void=$fieldDict.add($field.fieldDict))
  220. #end
  221. #end
  222. #foreach($field in $gridList)
  223. #if($field.fieldDict)
  224. #set($void=$fieldDict.add($field.fieldDict))
  225. #end
  226. #end
  227. #if($fieldDict)
  228. // 加载字典数据
  229. const { $dict.format($fieldDict) } = useDict($dict.quotation($fieldDict));
  230. #end
  231. // ========== 组件引用 ==========
  232. const formDialogRef = ref(); // 表单弹窗引用
  233. const excelUploadRef = ref(); // Excel上传弹窗引用
  234. const queryRef = ref(); // 查询表单引用
  235. // ========== 响应式数据 ==========
  236. const showSearch = ref(true); // 是否显示搜索区域
  237. const selectObjs = ref([]) as any; // 表格多选数据
  238. const multiple = ref(true); // 是否多选
  239. const isExpandAll = ref(false); // 是否展开所有节点
  240. // ========== 表格状态 ==========
  241. const state: BasicTableProps = reactive<BasicTableProps>({
  242. queryForm: {}, // 查询参数
  243. pageList: fetchTreeList, // 树形数据查询方法(不分页)
  244. loading: false, // 加载状态
  245. dataList: [] // 数据列表
  246. });
  247. // ========== Hook引用 ==========
  248. // 表格相关Hook (树形表格不使用分页)
  249. const {
  250. getDataList,
  251. downBlobFile,
  252. tableStyle
  253. } = useTable(state);
  254. // ========== 方法定义 ==========
  255. /**
  256. * 重置查询条件
  257. */
  258. const resetQuery = () => {
  259. // 清空搜索条件
  260. queryRef.value?.resetFields();
  261. // 清空多选
  262. selectObjs.value = [];
  263. // 重新查询
  264. getDataList();
  265. };
  266. /**
  267. * 展开/折叠所有节点
  268. */
  269. const expandAll = () => {
  270. isExpandAll.value = !isExpandAll.value;
  271. };
  272. /**
  273. * 导出Excel文件
  274. */
  275. const exportExcel = () => {
  276. downBlobFile(
  277. '/${moduleName}/${functionName}/export',
  278. Object.assign(state.queryForm, { ids: selectObjs }),
  279. '${functionName}.xlsx'
  280. );
  281. };
  282. /**
  283. * 表格多选事件处理
  284. * @param objs 选中的数据行
  285. */
  286. const selectionChangHandle = (objs: { $pk.attrName: string }[]) => {
  287. selectObjs.value = objs.map(({ $pk.attrName }) => $pk.attrName);
  288. multiple.value = !objs.length;
  289. };
  290. /**
  291. * 删除数据处理
  292. * @param ids 要删除的数据ID数组
  293. */
  294. const handleDelete = async (ids: string[]) => {
  295. try {
  296. await useMessageBox().confirm('此操作将永久删除选中数据及其子数据,是否继续?');
  297. } catch {
  298. return;
  299. }
  300. try {
  301. await delObjs(ids);
  302. getDataList();
  303. useMessage().success('删除成功');
  304. } catch (err: any) {
  305. useMessage().error(err.msg);
  306. }
  307. };
  308. // ========== 生命周期 ==========
  309. // 组件挂载时获取数据
  310. onMounted(() => {
  311. getDataList();
  312. });
  313. </script>
  314. <style scoped>
  315. .layout-padding {
  316. padding: 15px;
  317. }
  318. .layout-padding-auto {
  319. margin: 0 auto;
  320. }
  321. .layout-padding-view {
  322. min-height: calc(100vh - 50px);
  323. }
  324. .mb8 {
  325. margin-bottom: 8px;
  326. }
  327. .ml10 {
  328. margin-left: 10px;
  329. }
  330. .mr20 {
  331. margin-right: 20px;
  332. }
  333. </style>