index.vue 11 KB

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