index.vue 8.7 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274
  1. <template>
  2. <div class="system-dict-container layout-padding">
  3. <div class="layout-padding-auto layout-padding-view pd20">
  4. <splitpanes class="h100" Vertical>
  5. <pane min-size="16" max-size="25" size="16" class="orgTree">
  6. <el-input v-model="filterText" placeholder="类型名称" clearable />
  7. <el-scrollbar style="height: calc(100% - 55px)" class="mt10">
  8. <el-auto-resizer class="table" v-loading="state.tableLoading">
  9. <template #default="{ height, width }">
  10. <el-tree-v2
  11. :data="state.dicTypeList"
  12. highlight-current
  13. :expand-on-click-node="false"
  14. node-key="id"
  15. :props="{ children: 'children', label: 'dicTypeName' }"
  16. @node-click="handleNodeClick"
  17. :current-node-key="state.dicTypeList[0]?.id"
  18. :filter-method="filterNode"
  19. ref="treeRef"
  20. :height="height"
  21. :item-size="36"
  22. empty-text="暂无数据"
  23. >
  24. <template #default="{ node }">
  25. <span>{{ node.label }}</span>
  26. </template>
  27. </el-tree-v2>
  28. </template>
  29. </el-auto-resizer>
  30. </el-scrollbar>
  31. </pane>
  32. <pane class="rightContent">
  33. <div class="flex-column">
  34. <el-form :model="state.queryParams" ref="ruleFormRef" inline @submit.native.prevent>
  35. <el-form-item label="关键字" prop="keyword">
  36. <el-input
  37. v-model="state.queryParams.keyword"
  38. placeholder="字典值名称/字典值"
  39. clearable
  40. @keyup.enter="handleQuery"
  41. class="keyword-input"
  42. />
  43. </el-form-item>
  44. <el-form-item>
  45. <el-button type="primary" @click="handleQuery" :loading="state.tableLoading" v-waves>
  46. <SvgIcon name="ele-Search" class="mr5" />查询
  47. </el-button>
  48. <el-button @click="resetQuery(ruleFormRef)" v-waves class="default-button" :loading="state.tableLoading">
  49. <SvgIcon name="ele-Refresh" class="mr5" />重置
  50. </el-button>
  51. </el-form-item>
  52. </el-form>
  53. <vxe-toolbar
  54. ref="toolbarRef"
  55. :loading="state.tableLoading"
  56. custom
  57. :refresh="{
  58. queryMethod: handleQuery,
  59. }"
  60. >
  61. <template #buttons>
  62. <el-button type="primary" @click="onOpenAddUser" v-auth="'system:dict:add'"> <SvgIcon name="ele-Plus" class="mr5" />新增 </el-button>
  63. </template>
  64. </vxe-toolbar>
  65. <div style="overflow: hidden; width: 100%; height: 100%; flex: 1">
  66. <vxe-table
  67. :loading="state.tableLoading"
  68. :data="list"
  69. :column-config="{ resizable: true, useKey: true }"
  70. :row-config="{ isCurrent: true, isHover: true, useKey: true, height: 36 }"
  71. ref="tableRef"
  72. height="auto"
  73. auto-resize
  74. show-overflow
  75. :scrollY="{ enabled: true, gt: 0 }"
  76. id="systemDict"
  77. :custom-config="{ storage: true }"
  78. showHeaderOverflow
  79. :tree-config="{
  80. childrenField: 'children',
  81. transform: true,
  82. rowField: 'id',
  83. parentField: 'parentId',
  84. }"
  85. >
  86. <vxe-column field="dicDataName" title="字典名称" min-width="350" treeNode>
  87. <template #default="{ row }">
  88. <p style="display: flex; align-items: center">
  89. <span class="pl5" v-html="row.dicDataName"></span>
  90. </p>
  91. </template>
  92. </vxe-column>
  93. <vxe-column field="dicDataValue" title="字典值" min-width="150">
  94. <template #default="{ row }">
  95. <p style="display: flex; align-items: center">
  96. <span class="pl5" v-html="row.dicDataValue"></span>
  97. </p>
  98. </template>
  99. </vxe-column>
  100. <vxe-column field="dicTypeCode" title="字典code" width="170"></vxe-column>
  101. <vxe-column field="sort" title="排序" width="120"></vxe-column>
  102. <vxe-column field="creationTime" title="更新时间" width="160" align="center">
  103. <template #default="{ row }">
  104. <span>{{ formatDate(row.creationTime, 'YYYY-mm-dd HH:MM:SS') }}</span>
  105. </template>
  106. </vxe-column>
  107. <vxe-column title="操作" fixed="right" width="80" align="center">
  108. <template #default="{ row }">
  109. <el-button link type="primary" @click="onOpenEditUser(row)" title="修改" v-auth="'system:dict:edit'"> 修改 </el-button>
  110. </template>
  111. </vxe-column>
  112. </vxe-table>
  113. </div>
  114. </div>
  115. </pane>
  116. </splitpanes>
  117. </div>
  118. <dict-add ref="dictAddRef" @updateList="handleQuery" :dicTypeList="state.dicTypeList" />
  119. <dict-edit ref="dickEditRef" @updateList="handleQuery" :dicTypeList="state.dicTypeList" />
  120. </div>
  121. </template>
  122. <script lang="tsx" setup name="systemDict">
  123. import { defineAsyncComponent, nextTick, onMounted, reactive, ref, watch } from 'vue';
  124. import type { FormInstance } from 'element-plus';
  125. import { formatDate } from '@/utils/formatTime';
  126. import { dicTypeList, getDataByTypeId } from '@/api/system/dict';
  127. import { Splitpanes, Pane } from 'splitpanes';
  128. import 'splitpanes/dist/splitpanes.css';
  129. import XEUtils from 'xe-utils';
  130. // 引入组件
  131. const DictAdd = defineAsyncComponent(() => import('@/views/system/config/dict/components/Dict-add.vue')); // 新增组件
  132. const DictEdit = defineAsyncComponent(() => import('@/views/system/config/dict/components/Dict-edit.vue')); // 编辑组件
  133. // 定义变量内容
  134. const state = reactive<any>({
  135. queryParams: {
  136. keyword: null, // 关键字
  137. typeid: null, // 字典类型
  138. },
  139. total: 0, // 总条数
  140. loading: false, // 加载状态
  141. tableLoading: false, // 表格加载状态
  142. dicTypeList: [], // 字典类型列表
  143. });
  144. const ruleFormRef = ref<RefType>(); // 表单组件
  145. /** 搜索按钮操作 节流操作 */
  146. const handleQuery = () => {
  147. queryList();
  148. };
  149. // 获取字典类型列表
  150. const getDictTypeList = () => {
  151. state.loading = true;
  152. dicTypeList()
  153. .then((res: any) => {
  154. state.dicTypeList = res?.result ?? [];
  155. state.queryParams.typeid = res?.result[0].id ?? '';
  156. queryList();
  157. state.loading = false;
  158. })
  159. .catch(() => {
  160. state.loading = false;
  161. });
  162. };
  163. /* 获取字典列表 */
  164. const tableRef = ref<RefType>();
  165. const tableData = ref<EmptyArrayType>([]);
  166. const list = ref<any[]>([]);
  167. const queryList = () => {
  168. state.tableLoading = true;
  169. getDataByTypeId(state.queryParams)
  170. .then((res: any) => {
  171. tableData.value = res?.result ?? [];
  172. const filterVal = XEUtils.toValueString(state.queryParams.keyword)?.trim().toLowerCase();
  173. if (filterVal) {
  174. const filterRE = new RegExp(filterVal, 'gi');
  175. const options = { children: 'children' };
  176. const searchProps = ['dicDataName', 'dicDataValue'];
  177. // 搜索为克隆数据,不会污染源数据
  178. const rest = XEUtils.searchTree(
  179. tableData.value,
  180. (item) => searchProps.some((key) => String(item[key]).toLowerCase().indexOf(filterVal) > -1),
  181. options
  182. );
  183. XEUtils.eachTree(
  184. rest,
  185. (item) => {
  186. searchProps.forEach((key) => {
  187. item[key] = String(item[key]).replace(filterRE, (match) => `<span class="keyword-highlight">${match}</span>`);
  188. });
  189. },
  190. options
  191. );
  192. list.value = rest;
  193. list.value = XEUtils.toTreeArray(list.value);
  194. nextTick(() => {
  195. // 搜索之后默认展开所有子节点
  196. if (tableRef.value) {
  197. tableRef.value.setAllTreeExpand(true);
  198. }
  199. });
  200. } else {
  201. list.value = tableData.value;
  202. list.value = XEUtils.toTreeArray(list.value);
  203. }
  204. state.tableLoading = false;
  205. })
  206. .catch(() => {
  207. state.loading = false;
  208. });
  209. };
  210. // 点击节点
  211. const handleNodeClick = (data: any) => {
  212. state.queryParams.typeid = data.id;
  213. state.isExpand = false;
  214. state.expandedRowKeys = [];
  215. queryList();
  216. };
  217. // 打开新增字典弹窗
  218. const dictAddRef = ref<RefType>(); //新增字典
  219. const onOpenAddUser = () => {
  220. dictAddRef.value.openDialog(state.queryParams.typeid);
  221. };
  222. // 打开修改字典弹窗
  223. const dickEditRef = ref<RefType>(); //修改字典
  224. const onOpenEditUser = (row: any) => {
  225. dickEditRef.value.openDialog(row, state.tableData);
  226. };
  227. /** 重置按钮操作 */
  228. const resetQuery = (formEl: FormInstance | undefined) => {
  229. if (!formEl) return;
  230. formEl.resetFields();
  231. queryList();
  232. };
  233. const filterText = ref('');
  234. const treeRef = ref<RefType>();
  235. watch(filterText, (val) => {
  236. treeRef.value!.filter(val);
  237. });
  238. const filterNode = (value: string, data: any) => {
  239. if (!value) return true;
  240. return data.dicTypeName.includes(value);
  241. };
  242. const toolbarRef = ref<RefType>();
  243. onMounted(() => {
  244. getDictTypeList();
  245. if (tableRef.value && toolbarRef.value) {
  246. tableRef.value.connect(toolbarRef.value);
  247. }
  248. });
  249. </script>
  250. <style lang="scss" scoped>
  251. .system-dict-container {
  252. .orgTree {
  253. height: 100%;
  254. }
  255. .rightContent {
  256. height: 100%;
  257. .flex-column {
  258. display: flex;
  259. flex-direction: column;
  260. height: 100%;
  261. .table {
  262. flex: 1;
  263. }
  264. }
  265. }
  266. :deep(.el-tree-node__content) {
  267. height: 40px;
  268. }
  269. }
  270. </style>