configMsg.vue 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520
  1. <template>
  2. <div class="permission-container">
  3. <el-form :model="configMsgForm" :rules="permissionRules" ref="configMsgFormRef" label-width="200px" class="demo-ruleForm">
  4. <el-form-item label="消息配置" prop="permission">
  5. <!-- <el-checkbox :indeterminate="allIndeterminate" v-model="allCheck" @change="handleCheckAll">全部选择</el-checkbox> -->
  6. <div v-for="item in permissions" :key="item.identifier" class="level_1">
  7. <el-row :gutter="20">
  8. <el-col :span="4">
  9. <el-checkbox
  10. v-model="item.has_select"
  11. :label="item.name"
  12. :indeterminate="checkIndeterminate(item.children)"
  13. @change="handleCheckboxChanged(item)"
  14. ></el-checkbox>
  15. </el-col>
  16. <el-col :span="20">
  17. <div v-for="_item in item.child" :key="_item.identifier" class="checkbox-dropdown">
  18. <el-popover placement="bottom" trigger="hover" :open-delay="300" :disabled="!hasChildren(_item)" :append-to-body="false">
  19. <template #reference>
  20. <el-checkbox
  21. v-model="_item.has_select"
  22. :indeterminate="checkIndeterminate(_item.child)"
  23. @click.stop
  24. @change="handleCheckboxChanged(_item, item)"
  25. >
  26. {{ _item.name }}
  27. <template v-if="hasChildren(_item)">
  28. <i class="el-icon-arrow-down"></i>
  29. </template>
  30. </el-checkbox>
  31. </template>
  32. <div class="role-dropdown__list" v-if="hasChildren(_item)">
  33. <div class="role-dropdown" v-for="__item in _item.child" :key="__item.identifier">
  34. <el-popover placement="right" trigger="hover" :open-delay="200" :disabled="!hasChildren(__item)" :append-to-body="false">
  35. <template #reference>
  36. <div class="role-dropdown__label">
  37. <el-checkbox
  38. v-model="__item.has_select"
  39. :indeterminate="checkIndeterminate(__item.child)"
  40. @click.stop
  41. @change="handleCheckboxChanged(__item, _item)"
  42. >
  43. {{ __item.name }}
  44. <template v-if="hasChildren(__item)">
  45. <i class="el-icon-arrow-right"></i>
  46. </template>
  47. </el-checkbox>
  48. </div>
  49. </template>
  50. <div class="role-dropdown__list" v-if="hasChildren(__item)">
  51. <div class="role-dropdown" v-for="___item in __item.child" :key="___item.identifier">
  52. <el-popover placement="right" trigger="hover" :open-delay="200" :disabled="!hasChildren(___item)" :append-to-body="false">
  53. <template #reference>
  54. <div class="role-dropdown__label">
  55. <el-checkbox
  56. v-model="___item.has_select"
  57. :indeterminate="checkIndeterminate(___item.child)"
  58. @click.stop
  59. @change="handleCheckboxChanged(___item, __item)"
  60. >{{ ___item.name }}</el-checkbox
  61. >
  62. </div>
  63. </template>
  64. </el-popover>
  65. </div>
  66. </div>
  67. </el-popover>
  68. </div>
  69. </div>
  70. </el-popover>
  71. </div>
  72. </el-col>
  73. </el-row>
  74. </div>
  75. </el-form-item>
  76. <el-form-item label="">
  77. <el-button type="primary" size="small" style="margin-top: 15px" @click="saveRolePermission">保存设置</el-button>
  78. </el-form-item>
  79. </el-form>
  80. </div>
  81. </template>
  82. <script lang="ts" setup>
  83. import { ref, reactive, onMounted, watch, nextTick } from 'vue';
  84. import { useRoute, useRouter } from 'vue-router';
  85. import { ElMessage } from 'element-plus';
  86. import * as API_Auth from '@/api/auth';
  87. import * as API_Menus from '@/api/menus';
  88. import * as API_Setting from '@/api/setting';
  89. import * as API_Company from '@/api/company';
  90. import { useStore } from 'vuex';
  91. const store = useStore();
  92. /* import { Foundation } from '../../composables/ui-utils'; */
  93. const route = useRoute();
  94. const router = useRouter();
  95. const props = {
  96. label: 'name',
  97. value: 'id',
  98. emitPath: false,
  99. multiple: true,
  100. checkStrictly: true,
  101. };
  102. const checked = ref(true);
  103. const deptList = ref<any>([]);
  104. const oneDimensionalList = ref<any>([]); // 源数据平铺成一级节点
  105. /** 权限 表单 */
  106. const configMsgForm = reactive({
  107. data_scope: 'ALL',
  108. role_name: '',
  109. role_describe: '',
  110. });
  111. /** 权限 表单规则 */
  112. const permissionRules = reactive({
  113. role_name: [
  114. { required: true, message: '请输入角色名称!', trigger: 'blur' },
  115. { min: 1, max: 10, message: '长度在 1 到 10 个字符', trigger: 'blur' },
  116. ],
  117. });
  118. const permissions = ref<any>([]);
  119. const allCheck = ref(false);
  120. const allIndeterminate = ref(false);
  121. const isAllSelected = ref(false);
  122. const role_id = ref<any>(route.query.id);
  123. const searchSelectAllNodeIndex = () => {
  124. let selectAllOptionIndex = -1;
  125. configMsgForm.dept_ids.forEach((res, index) => {
  126. if (res.length === 1) {
  127. selectAllOptionIndex = index;
  128. }
  129. });
  130. return selectAllOptionIndex;
  131. };
  132. const visibleChange = (visible) => {
  133. if (visible) {
  134. deptList.value.unshift({
  135. id: '全选',
  136. name: '全选',
  137. value: '全选',
  138. });
  139. } else {
  140. deptList.value.shift();
  141. }
  142. };
  143. // 选择级联选择器
  144. const selectHandle = async (e = []) => {
  145. const selectLength = e.filter((v) => v !== '全选').length;
  146. const has_all_option = e.some((v) => v === '全选');
  147. let dept_ids = [...configMsgForm.dept_ids];
  148. if (has_all_option && isAllSelected.value) {
  149. // 移除了选项中除全部外的某个节点
  150. dept_ids = dept_ids.filter((v) => v !== '全选');
  151. dept_ids.splice(searchSelectAllNodeIndex(), 1);
  152. configMsgForm.dept_ids = dept_ids;
  153. isAllSelected.value = false;
  154. } else if (has_all_option && !isAllSelected.value) {
  155. // 选中全部节点
  156. isAllSelected.value = true;
  157. const deptListData = deptList.value;
  158. const dept_ids = [];
  159. const loopSelectData = (list) => {
  160. list.forEach((e) => {
  161. dept_ids.push(e.id);
  162. if (e.children && e.children.length > 0) {
  163. loopSelectData(e.children);
  164. }
  165. });
  166. };
  167. loopSelectData(deptListData);
  168. configMsgForm.dept_ids = dept_ids;
  169. } else if (!has_all_option && isAllSelected.value) {
  170. // 取消选中全部节点
  171. isAllSelected.value = false;
  172. configMsgForm.dept_ids = [];
  173. } else if (selectLength === oneDimensionalList.value.length) {
  174. isAllSelected.value = true;
  175. dept_ids.unshift('全选');
  176. configMsgForm.dept_ids = dept_ids;
  177. }
  178. nextTick(() => {
  179. // 强制更新
  180. });
  181. };
  182. /* 处理树形数据 */
  183. let buildTree = function buildTree(list, parent_id) {
  184. const tree = [];
  185. for (let i = 0; i < list.length; i++) {
  186. if (list[i].parent_id === parent_id) {
  187. const node = {
  188. id: list[i].id,
  189. parent_id: list[i].parent_id,
  190. name: list[i].name,
  191. create_time: list[i].create_time,
  192. remark: list[i].remark,
  193. sn: list[i].sn,
  194. sort: list[i].sort,
  195. type: list[i].type,
  196. region_ids: list[i].region_ids,
  197. children: buildTree(list, list[i].id),
  198. };
  199. tree.push(node);
  200. }
  201. }
  202. return tree;
  203. };
  204. /** 获取部门 */
  205. const GET_DeptList = () => {
  206. API_Setting.getDeptList().then((response) => {
  207. deptList.value = buildTree(response, '0');
  208. const oneDimensionalListData = [];
  209. const loopData = (list) => {
  210. list.forEach((e) => {
  211. oneDimensionalListData.push(e);
  212. if (e.children && e.children.length > 0) {
  213. loopData(e.children);
  214. }
  215. });
  216. };
  217. loopData(response);
  218. oneDimensionalList.value = oneDimensionalListData;
  219. if (configMsgForm.dept_ids.length === oneDimensionalListData.length) {
  220. // 设置数据范围全选
  221. configMsgForm.dept_ids.push('全选');
  222. isAllSelected.value = true;
  223. }
  224. });
  225. };
  226. /** 判断是否还有子集 */
  227. const hasChildren = (item) => {
  228. return Array.isArray(item.children) && item.children.length !== 0;
  229. };
  230. /** 全选 */
  231. const handleCheckAll = (checked) => {
  232. allIndeterminate.value = false;
  233. permissions.value = setPermissionsCheck(permissions.value, checked);
  234. };
  235. /** 选择 */
  236. const handleCheckboxChanged = (item, parent) => {
  237. if (item.child && item.child.length) {
  238. item.child = setPermissionsCheck(item.child, item.checked);
  239. }
  240. /* countAllPermissions(permissions.value);
  241. countParentChecked(permissions.value); */
  242. };
  243. /** 设置权限状态 */
  244. const setPermissionsCheck = (permissions, checked) => {
  245. const perm = [...permissions];
  246. perm.map((item) => {
  247. item.checked = checked;
  248. if (item.children && item.children.length) {
  249. item.children = setPermissionsCheck(item.children, checked);
  250. }
  251. });
  252. return perm;
  253. };
  254. /** 检测是否有不确定性 */
  255. const checkIndeterminate = (permissions) => {
  256. if (!Array.isArray(permissions)) return false;
  257. const _len = permissions.length;
  258. const __len = permissions.filter((item) => item.checked).length;
  259. return __len !== 0 && _len !== __len;
  260. };
  261. /** 获取所有权限展开后的长度、被选中的长度 */
  262. const countAllPermissions = (perms?: any[]) => {
  263. const currentPermissions = perms || permissions.value;
  264. if (!Array.isArray(currentPermissions)) {
  265. console.warn('permissions is not an array:', currentPermissions);
  266. return [];
  267. }
  268. const _list = [];
  269. currentPermissions.forEach((item) => {
  270. _list.push(item);
  271. if (item.children) _list.push(...countAllPermissions(item.children));
  272. });
  273. const length = _list.length;
  274. const length_checked = _list.filter((_item) => _item.checked).length;
  275. allCheck.value = length === length_checked;
  276. allIndeterminate.value = length_checked !== 0 && length !== length_checked;
  277. return _list;
  278. };
  279. /** 计算所有父辈的选中状态 */
  280. const countParentChecked = (perms?: any[]) => {
  281. const currentPermissions = perms || permissions.value;
  282. currentPermissions.forEach((item) => {
  283. if (item.children && item.children.length) {
  284. countParentChecked(item.children);
  285. const length = item.children.length;
  286. const checked_length = item.children.filter((_item) => _item.checked).length;
  287. item.checked = !!checked_length;
  288. }
  289. });
  290. };
  291. /** 保存角色权限 */
  292. const configMsgFormRef = ref();
  293. const saveRolePermission = () => {
  294. configMsgFormRef.value.validate((valid) => {
  295. if (valid) {
  296. let params = { vos: permissions.value };
  297. /* if (!params.dept_ids) {
  298. ElMessage.error('请选择数据范围!');
  299. return;
  300. }
  301. params.dept_ids = params.dept_ids.filter((v) => v !== '全选');
  302. if (params.dept_ids) {
  303. if (isAllSelected.value) {
  304. params.data_scope = 'ALL';
  305. params.dept_ids = params.dept_ids.join(',');
  306. } else {
  307. params.data_scope = 'DEPT_CUSTOM';
  308. params.dept_ids = params.dept_ids.join(',');
  309. }
  310. } */
  311. API_Company.updateSelectMsg(params).then((res) => {
  312. ElMessage.success('保存成功!');
  313. GET_RolePermission();
  314. });
  315. /* const saveSuccess = () => {
  316. ElMessage.success('保存成功!');
  317. router.push({ name: 'roleManage' });
  318. };*/
  319. /* role_id.value === '0' ? API_Auth.addRole(params).then(() => saveSuccess()) : API_Auth.editRole(role_id.value, params).then(() => saveSuccess()); */
  320. } else {
  321. ElMessage.error('表单填写有误,请检查!');
  322. return false;
  323. }
  324. });
  325. };
  326. /** 获取消息树 */
  327. const GET_RolePermission = () => {
  328. API_Company.getSendNumByCompany({company_id: store.state.companId}).then((res) => {
  329. permissions.value = res;
  330. });
  331. /* API_Menus.getMenusChildren().then((res) => {
  332. if (role_id.value !== '0') {
  333. API_Auth.getRolePermission(role_id.value).then((response) => {
  334. const checkedIds = expandRouters(response.menus);
  335. permissions.value = filterRoleRouter(res, checkedIds);
  336. if (Array.isArray(permissions.value)) {
  337. countAllPermissions();
  338. } else {
  339. console.warn('Invalid permissions data:', permissions.value);
  340. }
  341. GET_DeptList();
  342. });
  343. } else {
  344. permissions.value = res;
  345. if (Array.isArray(permissions.value)) {
  346. countAllPermissions();
  347. } else {
  348. console.warn('Invalid permissions data:', permissions.value);
  349. }
  350. GET_DeptList();
  351. }
  352. });*/
  353. };
  354. /** 展开路由的identifier */
  355. const expandRouters = (menus) => {
  356. const routers = [];
  357. menus.forEach((item) => {
  358. item.checked && routers.push(item.identifier);
  359. if (item.children && item.children.length) {
  360. routers.push(...expandRouters(item.children));
  361. }
  362. });
  363. return routers;
  364. };
  365. /** 递归筛选被选中的路由 */
  366. const filterRoleRouter = (routers, ids) => {
  367. const _routers = [];
  368. routers.forEach((item) => {
  369. if (ids.includes(item.identifier) || item.hidden) {
  370. item.checked = true;
  371. } else {
  372. item.checked = false;
  373. }
  374. if (item.children) {
  375. item.children = filterRoleRouter(item.children, ids);
  376. }
  377. _routers.push(item);
  378. });
  379. return _routers;
  380. };
  381. onMounted(() => {
  382. GET_RolePermission();
  383. });
  384. /* watch(
  385. () => route.query.id,
  386. () => {
  387. role_id.value = route.query.id;
  388. GET_RolePermission();
  389. }
  390. ); */
  391. </script>
  392. <style scoped>
  393. .permission-container {
  394. padding: 10px;
  395. background-color: #fff;
  396. padding-bottom: 150px;
  397. }
  398. .level_1 {
  399. padding: 15px 0;
  400. border-bottom: 1px dashed #e7e7e7;
  401. }
  402. .level_1:last-child {
  403. border-bottom: none;
  404. }
  405. :deep(.el-form-item__label) {
  406. padding-top: 15px;
  407. }
  408. :deep(.el-form-item__content) {
  409. display: block;
  410. border-left: 1px solid #e7e7e7;
  411. padding-left: 20px;
  412. padding-top: 15px;
  413. }
  414. :deep(.el-form-item__content .el-form-item__error) {
  415. padding-left: 20px;
  416. }
  417. :deep(.el-form-item:last-child .el-form-item__content) {
  418. padding-top: 0;
  419. }
  420. :deep(.el-form-item:not(:first-child)) {
  421. border-top: 1px solid #e7e7e7;
  422. position: relative;
  423. }
  424. :deep(.el-form-item:not(:first-child)::after) {
  425. content: ' ';
  426. width: 1px;
  427. height: 22px;
  428. background-color: #e7e7e7;
  429. position: absolute;
  430. top: -22px;
  431. left: 200px;
  432. }
  433. :deep(.el-button-group) {
  434. display: inline-block;
  435. }
  436. :deep(.el-button-group .el-button) {
  437. display: inline-block;
  438. padding: 0;
  439. border: none;
  440. }
  441. :deep(.el-button-group .el-button:focus),
  442. :deep(.el-button-group .el-button:hover) {
  443. color: #606266;
  444. border-color: #fff;
  445. background-color: #fff;
  446. }
  447. .checkbox-dropdown {
  448. display: inline-block;
  449. min-width: 130px;
  450. cursor: pointer;
  451. }
  452. .checkbox-dropdown .checked {
  453. color: #409eff;
  454. }
  455. .role-dropdown {
  456. display: flex;
  457. align-items: center;
  458. width: 25%;
  459. height: 30px;
  460. margin-bottom: 10px;
  461. }
  462. .role-dropdown:last-child {
  463. margin-bottom: 0;
  464. }
  465. .role-dropdown__label {
  466. display: flex;
  467. align-items: center;
  468. width: 100%;
  469. }
  470. :deep(.role-dropdown__label .el-checkbox__label) {
  471. color: inherit;
  472. }
  473. .role-dropdown__list {
  474. max-height: 320px;
  475. overflow-y: auto;
  476. }
  477. </style>