index.vue 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236
  1. <script setup lang="ts">
  2. import { useI18n } from '@/hooks/web/useI18n'
  3. import { ElInput, ElCard, ElTree, ElTreeSelect, ElSelect, ElOption } from 'element-plus'
  4. import { handleTree } from '@/utils/tree'
  5. import { onMounted, ref, unref, watch } from 'vue'
  6. import * as DeptApi from '@/api/system/dept'
  7. import { Form, FormExpose } from '@/components/Form'
  8. import { modelSchema, rules } from './dept.data'
  9. import { DeptVO } from '@/api/system/dept/types'
  10. import { useMessage } from '@/hooks/web/useMessage'
  11. import { getListSimpleUsersApi } from '@/api/system/user'
  12. const message = useMessage()
  13. interface Tree {
  14. id: number
  15. name: string
  16. children?: Tree[]
  17. }
  18. const defaultProps = {
  19. children: 'children',
  20. label: 'name',
  21. value: 'id'
  22. }
  23. const { t } = useI18n() // 国际化
  24. const loading = ref(false) // 遮罩层
  25. const dialogVisible = ref(false) // 是否显示弹出层
  26. const showForm = ref(false) // 显示form表单
  27. const formTitle = ref('部门信息') // 显示form标题
  28. const deptParentId = ref(0) // 上级ID
  29. // 创建form表单
  30. const formRef = ref<FormExpose>()
  31. // ========== 创建部门树结构 ==========
  32. const filterText = ref('')
  33. const deptOptions = ref() // 树形结构
  34. const treeRef = ref<InstanceType<typeof ElTree>>()
  35. const getTree = async () => {
  36. const res = await DeptApi.listSimpleDeptApi()
  37. deptOptions.value = handleTree(res)
  38. }
  39. const filterNode = (value: string, data: Tree) => {
  40. if (!value) return true
  41. return data.name.includes(value)
  42. }
  43. watch(filterText, (val) => {
  44. treeRef.value!.filter(val)
  45. })
  46. // 用户列表
  47. const userOption = ref()
  48. const leaderUserId = ref()
  49. const getUserList = async () => {
  50. const res = await getListSimpleUsersApi()
  51. userOption.value = res
  52. }
  53. // 新增
  54. const handleAdd = (data: { id: number }) => {
  55. // 重置表单
  56. deptParentId.value = data.id
  57. formTitle.value = '新增部门'
  58. unref(formRef)?.getElFormRef()?.resetFields()
  59. showForm.value = true
  60. }
  61. // 编辑
  62. const handleUpdate = async (data: { id: number }) => {
  63. const res = await DeptApi.getDeptApi(data.id)
  64. formTitle.value = '修改- ' + res?.name
  65. deptParentId.value = res.parentId
  66. leaderUserId.value = res.leaderUserId
  67. unref(formRef)?.setValues(res)
  68. showForm.value = true
  69. }
  70. // 删除
  71. const handleDelete = async (data: { id: number }) => {
  72. message
  73. .confirm(t('common.delDataMessage'), t('common.confirmTitle'))
  74. .then(async () => {
  75. await DeptApi.deleteDeptApi(data.id)
  76. message.success(t('common.delSuccess'))
  77. })
  78. .catch(() => {})
  79. await getTree()
  80. }
  81. // 提交按钮
  82. const submitForm = async () => {
  83. const elForm = unref(formRef)?.getElFormRef()
  84. if (!elForm) return
  85. elForm.validate(async (valid) => {
  86. if (valid) {
  87. loading.value = true
  88. // 提交请求
  89. try {
  90. const data = unref(formRef)?.formModel as DeptVO
  91. data.parentId = deptParentId.value
  92. data.leaderUserId = leaderUserId.value
  93. if (formTitle.value.startsWith('新增')) {
  94. await DeptApi.createDeptApi(data)
  95. } else if (formTitle.value.startsWith('修改')) {
  96. await DeptApi.updateDeptApi(data)
  97. }
  98. // 操作成功,重新加载列表
  99. dialogVisible.value = false
  100. } finally {
  101. loading.value = false
  102. }
  103. }
  104. })
  105. }
  106. onMounted(async () => {
  107. await getTree()
  108. await getUserList()
  109. })
  110. </script>
  111. <template>
  112. <div class="flex">
  113. <el-card class="w-1/3 dept" :gutter="12" shadow="always">
  114. <template #header>
  115. <div class="card-header">
  116. <span>部门列表</span>
  117. <el-button type="primary" v-hasPermi="['system:dept:create']" @click="handleAdd">
  118. 新增根节点
  119. </el-button>
  120. </div>
  121. </template>
  122. <div class="custom-tree-container">
  123. <!-- <p>部门列表</p> -->
  124. <!-- 操作工具栏 -->
  125. <el-input v-model="filterText" placeholder="搜索部门" />
  126. <el-tree
  127. ref="treeRef"
  128. node-key="id"
  129. :data="deptOptions"
  130. :props="defaultProps"
  131. :highlight-current="true"
  132. default-expand-all
  133. :filter-node-method="filterNode"
  134. :expand-on-click-node="false"
  135. >
  136. <template #default="{ node, data }">
  137. <span class="custom-tree-node">
  138. <span>{{ node.label }}</span>
  139. <span>
  140. <el-button
  141. link
  142. type="primary"
  143. v-hasPermi="['system:dept:create']"
  144. @click="handleAdd(data)"
  145. >
  146. <Icon icon="ep:zoom-in" class="mr-5px" /> {{ t('action.add') }}
  147. </el-button>
  148. <el-button
  149. link
  150. type="primary"
  151. v-hasPermi="['system:dept:update']"
  152. @click="handleUpdate(data)"
  153. >
  154. <Icon icon="ep:edit" class="mr-1px" /> {{ t('action.edit') }}
  155. </el-button>
  156. <el-button
  157. link
  158. type="primary"
  159. v-hasPermi="['system:dept:delete']"
  160. @click="handleDelete(data)"
  161. >
  162. <Icon icon="ep:delete" class="mr-1px" /> {{ t('action.del') }}
  163. </el-button>
  164. </span>
  165. </span>
  166. </template>
  167. </el-tree>
  168. </div>
  169. </el-card>
  170. <el-card class="w-2/3 dept" style="margin-left: 10px" :gutter="12" shadow="hover">
  171. <template #header>
  172. <div class="card-header">
  173. <span>{{ formTitle }}</span>
  174. </div>
  175. </template>
  176. <div v-if="!showForm">
  177. <span><p>请从左侧选择部门</p></span>
  178. </div>
  179. <div v-if="showForm">
  180. <!-- 操作工具栏 -->
  181. <Form ref="formRef" :schema="modelSchema" :rules="rules">
  182. <template #parentId>
  183. <el-tree-select
  184. node-key="id"
  185. v-model="deptParentId"
  186. :props="defaultProps"
  187. :data="deptOptions"
  188. check-strictly
  189. />
  190. </template>
  191. <template #leaderUserId>
  192. <el-select v-model="leaderUserId">
  193. <el-option
  194. v-for="item in userOption"
  195. :key="parseInt(item.id)"
  196. :label="item.nickname"
  197. :value="parseInt(item.id)"
  198. />
  199. </el-select>
  200. </template>
  201. </Form>
  202. <!-- 操作按钮 -->
  203. <el-button
  204. type="primary"
  205. v-hasPermi="['system:dept:update']"
  206. :loading="loading"
  207. @click="submitForm"
  208. >
  209. {{ t('action.save') }}
  210. </el-button>
  211. <el-button type="danger" @click="showForm = false">{{ t('common.cancel') }}</el-button>
  212. </div>
  213. </el-card>
  214. </div>
  215. </template>
  216. <style scoped>
  217. .dept {
  218. height: 600px;
  219. max-height: 1800px;
  220. }
  221. .card-header {
  222. display: flex;
  223. justify-content: space-between;
  224. align-items: center;
  225. }
  226. .custom-tree-node {
  227. flex: 1;
  228. display: flex;
  229. align-items: center;
  230. justify-content: space-between;
  231. font-size: 14px;
  232. padding-right: 8px;
  233. }
  234. </style>