index.vue 9.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327
  1. <template>
  2. <ContentWrap>
  3. <!-- 列表 -->
  4. <XTable @register="registerTable">
  5. <!-- 操作:新增 -->
  6. <template #toolbar_buttons>
  7. <XButton
  8. type="primary"
  9. preIcon="ep:zoom-in"
  10. :title="t('action.add')"
  11. v-hasPermi="['system:role:create']"
  12. @click="handleCreate()"
  13. />
  14. </template>
  15. <template #actionbtns_default="{ row }">
  16. <!-- 操作:编辑 -->
  17. <XTextButton
  18. preIcon="ep:edit"
  19. :title="t('action.edit')"
  20. v-hasPermi="['system:role:update']"
  21. @click="handleUpdate(row.id)"
  22. />
  23. <!-- 操作:详情 -->
  24. <XTextButton
  25. preIcon="ep:view"
  26. :title="t('action.detail')"
  27. v-hasPermi="['system:role:query']"
  28. @click="handleDetail(row.id)"
  29. />
  30. <!-- 操作:菜单权限 -->
  31. <XTextButton
  32. preIcon="ep:basketball"
  33. title="菜单权限"
  34. v-hasPermi="['system:permission:assign-role-menu']"
  35. @click="handleScope('menu', row)"
  36. />
  37. <!-- 操作:数据权限 -->
  38. <XTextButton
  39. preIcon="ep:coin"
  40. title="数据权限"
  41. v-hasPermi="['system:permission:assign-role-data-scope']"
  42. @click="handleScope('data', row)"
  43. />
  44. <!-- 操作:删除 -->
  45. <XTextButton
  46. preIcon="ep:delete"
  47. :title="t('action.del')"
  48. v-hasPermi="['system:role:delete']"
  49. @click="deleteData(row.id)"
  50. />
  51. </template>
  52. </XTable>
  53. </ContentWrap>
  54. <XModal v-model="dialogVisible" :title="dialogTitle">
  55. <!-- 对话框(添加 / 修改) -->
  56. <Form
  57. v-if="['create', 'update'].includes(actionType)"
  58. :schema="allSchemas.formSchema"
  59. :rules="rules"
  60. ref="formRef"
  61. />
  62. <!-- 对话框(详情) -->
  63. <Descriptions
  64. v-if="actionType === 'detail'"
  65. :schema="allSchemas.detailSchema"
  66. :data="detailData"
  67. />
  68. <!-- 操作按钮 -->
  69. <template #footer>
  70. <XButton
  71. v-if="['create', 'update'].includes(actionType)"
  72. type="primary"
  73. :title="t('action.save')"
  74. :loading="actionLoading"
  75. @click="submitForm()"
  76. />
  77. <XButton :loading="actionLoading" :title="t('dialog.close')" @click="dialogVisible = false" />
  78. </template>
  79. </XModal>
  80. <XModal v-model="dialogScopeVisible" :title="dialogScopeTitle">
  81. <el-form :model="dataScopeForm" label-width="140px" :inline="true">
  82. <el-form-item label="角色名称">
  83. <el-tag>{{ dataScopeForm.name }}</el-tag>
  84. </el-form-item>
  85. <el-form-item label="角色标识">
  86. <el-tag>{{ dataScopeForm.code }}</el-tag>
  87. </el-form-item>
  88. <!-- 分配角色的数据权限对话框 -->
  89. <el-form-item label="权限范围" v-if="actionScopeType === 'data'">
  90. <el-select v-model="dataScopeForm.dataScope">
  91. <el-option
  92. v-for="item in dataScopeDictDatas"
  93. :key="item.value"
  94. :label="item.label"
  95. :value="item.value"
  96. />
  97. </el-select>
  98. </el-form-item>
  99. <!-- 分配角色的菜单权限对话框 -->
  100. <el-form-item
  101. label="权限范围"
  102. v-if="
  103. actionScopeType === 'menu' || dataScopeForm.dataScope === SystemDataScopeEnum.DEPT_CUSTOM
  104. "
  105. >
  106. <el-card shadow="never">
  107. <template #header>
  108. 父子联动(选中父节点,自动选择子节点):
  109. <el-switch v-model="checkStrictly" inline-prompt active-text="是" inactive-text="否" />
  110. 全选/全不选:
  111. <el-switch
  112. v-model="treeNodeAll"
  113. inline-prompt
  114. active-text="是"
  115. inactive-text="否"
  116. @change="handleCheckedTreeNodeAll()"
  117. />
  118. </template>
  119. <el-tree
  120. ref="treeRef"
  121. node-key="id"
  122. show-checkbox
  123. :default-checked-keys="defaultCheckedKeys"
  124. :check-strictly="!checkStrictly"
  125. :props="defaultProps"
  126. :data="treeOptions"
  127. empty-text="加载中,请稍后"
  128. />
  129. </el-card>
  130. </el-form-item>
  131. </el-form>
  132. <!-- 操作按钮 -->
  133. <template #footer>
  134. <XButton
  135. type="primary"
  136. :title="t('action.save')"
  137. :loading="actionLoading"
  138. @click="submitScope()"
  139. />
  140. <XButton
  141. :loading="actionLoading"
  142. :title="t('dialog.close')"
  143. @click="dialogScopeVisible = false"
  144. />
  145. </template>
  146. </XModal>
  147. </template>
  148. <script setup lang="ts" name="Role">
  149. import { onMounted, reactive, ref, unref } from 'vue'
  150. import {
  151. ElForm,
  152. ElFormItem,
  153. ElSelect,
  154. ElOption,
  155. ElTree,
  156. ElCard,
  157. ElSwitch,
  158. ElTag
  159. } from 'element-plus'
  160. import { FormExpose } from '@/components/Form'
  161. import { useI18n } from '@/hooks/web/useI18n'
  162. import { useMessage } from '@/hooks/web/useMessage'
  163. import { useXTable } from '@/hooks/web/useXTable'
  164. import { handleTree, defaultProps } from '@/utils/tree'
  165. import { SystemDataScopeEnum } from '@/utils/constants'
  166. import { DICT_TYPE, getIntDictOptions } from '@/utils/dict'
  167. import { rules, allSchemas } from './role.data'
  168. import * as RoleApi from '@/api/system/role'
  169. import { listSimpleMenusApi } from '@/api/system/menu'
  170. import { listSimpleDeptApi } from '@/api/system/dept'
  171. import * as PermissionApi from '@/api/system/permission'
  172. const { t } = useI18n() // 国际化
  173. const message = useMessage() // 消息弹窗
  174. // 列表相关的变量
  175. const [registerTable, { reload, deleteData }] = useXTable({
  176. allSchemas: allSchemas,
  177. getListApi: RoleApi.getRolePageApi,
  178. deleteApi: RoleApi.deleteRoleApi
  179. })
  180. // ========== CRUD 相关 ==========
  181. const actionLoading = ref(false) // 遮罩层
  182. const actionType = ref('') // 操作按钮的类型
  183. const dialogVisible = ref(false) // 是否显示弹出层
  184. const dialogTitle = ref('edit') // 弹出层标题
  185. const formRef = ref<FormExpose>() // 表单 Ref
  186. const detailData = ref() // 详情 Ref
  187. // 设置标题
  188. const setDialogTile = (type: string) => {
  189. dialogTitle.value = t('action.' + type)
  190. actionType.value = type
  191. dialogVisible.value = true
  192. }
  193. // 新增操作
  194. const handleCreate = () => {
  195. setDialogTile('create')
  196. }
  197. // 修改操作
  198. const handleUpdate = async (rowId: number) => {
  199. setDialogTile('update')
  200. // 设置数据
  201. const res = await RoleApi.getRoleApi(rowId)
  202. unref(formRef)?.setValues(res)
  203. }
  204. // 详情操作
  205. const handleDetail = async (rowId: number) => {
  206. setDialogTile('detail')
  207. // 设置数据
  208. const res = await RoleApi.getRoleApi(rowId)
  209. detailData.value = res
  210. }
  211. // 提交按钮
  212. const submitForm = async () => {
  213. const elForm = unref(formRef)?.getElFormRef()
  214. if (!elForm) return
  215. elForm.validate(async (valid) => {
  216. if (valid) {
  217. actionLoading.value = true
  218. // 提交请求
  219. try {
  220. const data = unref(formRef)?.formModel as RoleApi.RoleVO
  221. if (actionType.value === 'create') {
  222. await RoleApi.createRoleApi(data)
  223. message.success(t('common.createSuccess'))
  224. } else {
  225. await RoleApi.updateRoleApi(data)
  226. message.success(t('common.updateSuccess'))
  227. }
  228. dialogVisible.value = false
  229. } finally {
  230. actionLoading.value = false
  231. // 刷新列表
  232. await reload()
  233. }
  234. }
  235. })
  236. }
  237. // ========== 数据权限 ==========
  238. const dataScopeForm = reactive({
  239. id: 0,
  240. name: '',
  241. code: '',
  242. dataScope: 0,
  243. checkList: []
  244. })
  245. const treeOptions = ref<any[]>([]) // 菜单树形结构
  246. const treeRef = ref<InstanceType<typeof ElTree>>()
  247. const dialogScopeVisible = ref(false)
  248. const dialogScopeTitle = ref('数据权限')
  249. const actionScopeType = ref('')
  250. const dataScopeDictDatas = ref()
  251. const defaultCheckedKeys = ref()
  252. // 选项
  253. const checkStrictly = ref(true)
  254. const treeNodeAll = ref(false)
  255. // 全选/全不选
  256. const handleCheckedTreeNodeAll = () => {
  257. treeRef.value!.setCheckedNodes(treeNodeAll.value ? treeOptions.value : [])
  258. }
  259. // 权限操作
  260. const handleScope = async (type: string, row: RoleApi.RoleVO) => {
  261. dataScopeForm.id = row.id
  262. dataScopeForm.name = row.name
  263. dataScopeForm.code = row.code
  264. if (type === 'menu') {
  265. const menuRes = await listSimpleMenusApi()
  266. treeOptions.value = handleTree(menuRes)
  267. const role = await PermissionApi.listRoleMenusApi(row.id)
  268. if (role) {
  269. // treeRef.value!.setCheckedKeys(role as unknown as Array<number>)
  270. defaultCheckedKeys.value = role
  271. }
  272. } else if (type === 'data') {
  273. const deptRes = await listSimpleDeptApi()
  274. treeOptions.value = handleTree(deptRes)
  275. const role = await RoleApi.getRoleApi(row.id)
  276. dataScopeForm.dataScope = role.dataScope
  277. if (role.dataScopeDeptIds) {
  278. // treeRef.value!.setCheckedKeys(role.dataScopeDeptIds as unknown as Array<number>, false)
  279. defaultCheckedKeys.value = role.dataScopeDeptIds
  280. }
  281. }
  282. actionScopeType.value = type
  283. dialogScopeVisible.value = true
  284. }
  285. // 保存权限
  286. const submitScope = async () => {
  287. if ('data' === actionScopeType.value) {
  288. const data = ref<PermissionApi.PermissionAssignRoleDataScopeReqVO>({
  289. roleId: dataScopeForm.id,
  290. dataScope: dataScopeForm.dataScope,
  291. dataScopeDeptIds:
  292. dataScopeForm.dataScope !== SystemDataScopeEnum.DEPT_CUSTOM
  293. ? []
  294. : (treeRef.value!.getCheckedKeys(false) as unknown as Array<number>)
  295. })
  296. await PermissionApi.assignRoleDataScopeApi(data.value)
  297. } else if ('menu' === actionScopeType.value) {
  298. const data = ref<PermissionApi.PermissionAssignRoleMenuReqVO>({
  299. roleId: dataScopeForm.id,
  300. menuIds: [
  301. ...(treeRef.value!.getCheckedKeys(false) as unknown as Array<number>),
  302. ...(treeRef.value!.getHalfCheckedKeys() as unknown as Array<number>)
  303. ]
  304. })
  305. await PermissionApi.assignRoleMenuApi(data.value)
  306. }
  307. message.success(t('common.updateSuccess'))
  308. dialogScopeVisible.value = false
  309. }
  310. const init = () => {
  311. dataScopeDictDatas.value = getIntDictOptions(DICT_TYPE.SYSTEM_DATA_SCOPE)
  312. }
  313. // ========== 初始化 ==========
  314. onMounted(() => {
  315. init()
  316. })
  317. </script>