index.vue 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360
  1. <template>
  2. <div>
  3. <a-divider orientation="left">
  4. {{ $t('iam.res_policy') }}
  5. </a-divider>
  6. <a-tabs v-model="currentPolicyResTab" tab-position="left">
  7. <a-tab-pane v-for="item of policyResList" :key="item.key" :tab="item.label">
  8. <h6 class="policy-tab-title">
  9. {{ item.label }}
  10. <a-checkbox
  11. class="ml-4"
  12. :indeterminate="policyModuleCheckMap[item.key].indeterminate"
  13. :checked="policyModuleCheckMap[item.key].checkAll"
  14. @change="e => onCheckAllModuleChange(e, item.key)">
  15. {{ $t('common.checkAll') }}
  16. </a-checkbox>
  17. </h6>
  18. <div v-for="item2 of item.childrens" :key="item2.key">
  19. <div class="policy-group-title">{{ item2.label }}</div>
  20. <div v-for="item3 of item2.childrens" :key="item3.key">
  21. <div class="policy-menu-title">
  22. <span class="label">{{ item3.label }}</span>
  23. <a-checkbox
  24. class="ml-4"
  25. :indeterminate="policyResCheckedList[item3.key].indeterminate"
  26. :checked="policyResCheckedList[item3.key].checkAll"
  27. @change="e => onCheckAllMenuOptionsChange(e, item3.key)">
  28. {{ $t('common.checkAll') }}
  29. </a-checkbox>
  30. </div>
  31. <a-checkbox-group v-model="policyResCheckedList[item3.key].options">
  32. <ul class="policy-opts">
  33. <li v-for="item4 in item3.options" :key="item4.key">
  34. <a-checkbox :value="item4.key" @change="e => onCheckMenuOptionChange(e, item3.key)">{{ item4.label }}</a-checkbox>
  35. </li>
  36. </ul>
  37. </a-checkbox-group>
  38. </div>
  39. </div>
  40. </a-tab-pane>
  41. </a-tabs>
  42. <a-divider orientation="left">
  43. {{ $t('iam.other_policy') }}
  44. </a-divider>
  45. <div class="d-flex align-items-center">
  46. <a-checkbox
  47. :checked="checkAll"
  48. @change="handleCheckAllChange"
  49. :indeterminate="isIndeterminate"
  50. :disabled="checkAllDisabled">{{$t('system.text_321', [$t('dictionary.policy')])}}</a-checkbox>
  51. <a-input class="ml-2" v-model="searchString" :placeholder="$t('iam.policy_search_placeholder')" style="max-width:200px" allow-clear />
  52. </div>
  53. <div class="mt-1">
  54. <template v-for="(item, idx) of options">
  55. <group
  56. v-if="showGroup(item)"
  57. v-show="hasSearchString(item)"
  58. :key="idx"
  59. :group="item"
  60. @groupCheckChange="groupCheckChange"
  61. :permissions="permissions"
  62. :scope="scope"
  63. :policy="policy"
  64. :searchString="searchString" />
  65. </template>
  66. </div>
  67. </div>
  68. </template>
  69. <script>
  70. import * as R from 'ramda'
  71. import { SCOPES_MAP } from '@/constants'
  72. import { getPolicyResList, getPolicyResCheckedList } from '@/utils/policy/policy-res-list'
  73. import { POLICY_WHITE_LIST } from '@/constants/policy'
  74. import Group from './Group'
  75. export default {
  76. name: 'PolicyRuleCheckbox',
  77. components: {
  78. Group,
  79. },
  80. props: {
  81. data: {
  82. type: Array,
  83. required: true,
  84. },
  85. permissions: Object,
  86. scope: String,
  87. checkAllDisabled: Boolean,
  88. policy: Object,
  89. },
  90. data () {
  91. return {
  92. checkAll: false,
  93. isIndeterminate: false,
  94. options: this.data,
  95. searchString: '',
  96. currentPolicyResTab: '',
  97. policyResList: [],
  98. policyResCheckedList: getPolicyResCheckedList(this.policy),
  99. }
  100. },
  101. computed: {
  102. policyOptionsMap () {
  103. const plainOptionsMap = {}
  104. this.policyResList.forEach(item => {
  105. item.childrens.forEach(item => {
  106. item.childrens.forEach(item => {
  107. plainOptionsMap[item.key] = item.options
  108. })
  109. })
  110. })
  111. return plainOptionsMap
  112. },
  113. policyModuleCheckMap () {
  114. const policyModuleCheckMap = {}
  115. this.policyResList.forEach(item => {
  116. const menus = []
  117. item.childrens.forEach(item2 => {
  118. menus.push(...item2.childrens)
  119. })
  120. policyModuleCheckMap[item.key] = {}
  121. const noCheckData = menus.every(obj => {
  122. const exist = POLICY_WHITE_LIST.some(item => this.policyResCheckedList[obj.key].options.includes(item))
  123. return this.policyResCheckedList[obj.key].options.length === (exist ? POLICY_WHITE_LIST.length : 0)
  124. })
  125. policyModuleCheckMap[item.key].checkAll = !noCheckData && menus.every(obj => {
  126. return this.policyResCheckedList[obj.key].checkAll
  127. })
  128. policyModuleCheckMap[item.key].indeterminate = !noCheckData && menus.some(obj => {
  129. return !this.policyResCheckedList[obj.key].checkAll
  130. })
  131. })
  132. return policyModuleCheckMap
  133. },
  134. policyModuleResMap () {
  135. const policyModuleResMap = {}
  136. this.policyResList.forEach(item => {
  137. const policyResList = []
  138. item.childrens.forEach(item2 => {
  139. item2.childrens.forEach(item3 => {
  140. policyResList.push(item3.key)
  141. })
  142. })
  143. policyModuleResMap[item.key] = policyResList
  144. })
  145. return policyModuleResMap
  146. },
  147. },
  148. watch: {
  149. data: {
  150. handler (val) {
  151. this.options = val
  152. },
  153. immediate: true,
  154. },
  155. },
  156. created () {
  157. this.policyResList = getPolicyResList()
  158. this.currentPolicyResTab = this.policyResList[0].key
  159. },
  160. methods: {
  161. handleCheckAllChange (e) {
  162. const val = e.target.checked
  163. let totalCheckAll = true
  164. let totalIndeterminate = false
  165. R.forEach(opt => {
  166. let optCheckAll = true
  167. let optIndeterminate = false
  168. R.forEach(res => {
  169. const unDisabledActions = res.actions.filter(item => !item.disabled)
  170. const checked = val ? unDisabledActions.map(action => action.action) : []
  171. // let checked = []
  172. // if (val) {
  173. // if (res.isIndeterminate) {
  174. // checked = []
  175. // } else {
  176. // checked = unDisabledActions.map(action => action.action)
  177. // }
  178. // } else {
  179. // if (!res.isIndeterminate) {
  180. // checked = []
  181. // } else {
  182. // checked = unDisabledActions.map(action => action.action)
  183. // }
  184. // }
  185. const isIndeterminate = checked.length > 0 && checked.length < res.actions.length
  186. const checkAll = checked.length === res.actions.length
  187. let show = true
  188. if (this.scope === SCOPES_MAP.project.key) {
  189. if (res.isDomainRes || res.isSystemRes) show = false
  190. } else if (this.scope === SCOPES_MAP.domain.key) {
  191. if (res.isSystemRes) show = false
  192. }
  193. if (show) {
  194. if (isIndeterminate) {
  195. optIndeterminate = true
  196. }
  197. if (!checkAll) {
  198. optCheckAll = false
  199. }
  200. }
  201. this.$set(res, 'checkAll', checkAll)
  202. this.$set(res, 'isIndeterminate', isIndeterminate)
  203. this.$set(res, 'checked', checked)
  204. }, opt.resources)
  205. if (optIndeterminate) {
  206. totalIndeterminate = true
  207. }
  208. if (!optCheckAll) {
  209. totalCheckAll = false
  210. }
  211. this.$set(opt, 'checkAll', optCheckAll)
  212. this.$set(opt, 'isIndeterminate', optIndeterminate)
  213. }, this.options)
  214. this.checkAll = totalCheckAll
  215. this.isIndeterminate = totalIndeterminate
  216. },
  217. groupCheckChange () {
  218. let checkAll = true
  219. let allGroupTotal = 0
  220. let indeterminateGroupTotal = 0
  221. let checkAllGroupTotal = 0
  222. for (let i = 0, len = this.options.length; i < len; i++) {
  223. const group = this.options[i]
  224. if (this.showGroup(group)) {
  225. allGroupTotal++
  226. if (!group.checkAll) {
  227. checkAll = false
  228. } else {
  229. checkAllGroupTotal++
  230. }
  231. if (group.isIndeterminate) {
  232. indeterminateGroupTotal++
  233. }
  234. }
  235. }
  236. this.isIndeterminate = (indeterminateGroupTotal > 0 && indeterminateGroupTotal < allGroupTotal) || (checkAllGroupTotal > 0 && checkAllGroupTotal < allGroupTotal)
  237. this.checkAll = checkAll
  238. },
  239. showGroup (group) {
  240. const isShow = group.resources.some(resource => {
  241. // let flag = false
  242. // if (this.scope !== SCOPES_MAP.system.key && resource.isSystemRes) {
  243. // flag = true
  244. // }
  245. // if (this.scope !== SCOPES_MAP.domain.key && resource.isDomainRes) {
  246. // flag = true
  247. // }
  248. // if (flag) return false
  249. // return true
  250. let show = true
  251. if (this.scope === SCOPES_MAP.project.key) {
  252. if (resource.isDomainRes || resource.isSystemRes) show = false
  253. } else if (this.scope === SCOPES_MAP.domain.key) {
  254. if (resource.isSystemRes) show = false
  255. }
  256. return show
  257. })
  258. return isShow
  259. },
  260. hasSearchString (group) {
  261. if (!this.searchString) {
  262. return true
  263. }
  264. if (group.label && group.label.includes(this.searchString)) {
  265. return true
  266. }
  267. if (group.resources && group.resources.filter(item => {
  268. let show = true
  269. if (!item.label.includes(this.searchString)) {
  270. show = false
  271. } else {
  272. if (this.scope === SCOPES_MAP.project.key) {
  273. if (item.isDomainRes || item.isSystemRes) show = false
  274. } else if (this.scope === SCOPES_MAP.domain.key) {
  275. if (item.isSystemRes) show = false
  276. }
  277. }
  278. return show
  279. }).length
  280. ) {
  281. return true
  282. }
  283. return false
  284. },
  285. onCheckAllMenuOptionsChange (e, menuKey) {
  286. // 查找当前菜单选项的操作集合
  287. const plainOptions = this.policyOptionsMap[menuKey].map(item => item.key)
  288. Object.assign(this.policyResCheckedList[menuKey], {
  289. options: e.target.checked ? Array.from(new Set(plainOptions)) : [],
  290. indeterminate: false,
  291. checkAll: e.target.checked,
  292. })
  293. this.$nextTick(() => {
  294. this.$emit('checkMenuOptionsChange', this.policyResCheckedList)
  295. })
  296. },
  297. onCheckMenuOptionChange (e, menuKey) {
  298. // 查找当前菜单选项的操作集合
  299. let plainOptions = this.policyOptionsMap[menuKey].map(item => item.key)
  300. plainOptions = Array.from(new Set(plainOptions))
  301. this.$nextTick(() => {
  302. const len = this.policyResCheckedList[menuKey].options.length
  303. this.policyResCheckedList[menuKey].indeterminate = !!len && this.policyResCheckedList[menuKey].options.length < plainOptions.length
  304. this.policyResCheckedList[menuKey].checkAll = this.policyResCheckedList[menuKey].options.length === plainOptions.length
  305. this.$emit('checkMenuOptionsChange', this.policyResCheckedList)
  306. })
  307. },
  308. onCheckAllModuleChange (e, moduleKey) {
  309. const menuKeys = this.policyModuleResMap[moduleKey]
  310. menuKeys.forEach(menuKey => {
  311. this.onCheckAllMenuOptionsChange(e, menuKey)
  312. })
  313. },
  314. },
  315. }
  316. </script>
  317. <style lang="scss" scoped>
  318. .policy-tab-title {
  319. height: 40px;
  320. line-height: 40px;
  321. border: 1px solid #eee;
  322. padding-left: 14px;
  323. }
  324. .policy-group-title {
  325. font-size: 14px;
  326. font-weight: bold;
  327. padding: 10px 0 10px 0;
  328. border-bottom: 1px solid #eee;
  329. }
  330. .policy-menu-title {
  331. padding: 10px 0 10px 0;
  332. .label {
  333. border-left: 3px solid #999;
  334. padding-left: 6px;
  335. }
  336. }
  337. .policy-opts {
  338. padding: 0;
  339. margin: 0;
  340. list-style: none;
  341. display: flex;
  342. flex-wrap: wrap;
  343. li {
  344. width: 220px;
  345. padding: 10px 20px;
  346. }
  347. }
  348. </style>