permission-content.vue 5.42 KB
<script setup lang="ts">
  import { Collapse, CollapseItem, Tree, TreeNodeData, CheckboxGroup } from '@arco-design/web-vue';
  import { computed, onMounted, ref, createVNode } from 'vue';
  import { usePermissionApi } from '@/http/role';
  import { SystemPermission } from '@/types/system-permission';
  import { arrayToTree, findTreeParentIds, findTreeChildIds } from '@/utils';
  import * as Icon from '@arco-design/web-vue/es/icon';
  import { camelCase, find, set, upperFirst, pull } from 'lodash';
  import { useVModel } from '@vueuse/core';

  const props = defineProps<{ modelValue: [] }>();
  const emit = defineEmits(['update:modelValue']);
  const checkIds = useVModel(props, 'modelValue', emit);

  const activeKey = ref<string[]>([]);

  const permissionData = ref<SystemPermission[]>([]);
  const btnPermissionData = ref<SystemPermission[]>([]);

  const adminPermissionRef = ref<InstanceType<typeof Tree>>();
  const appPermissionRef = ref<InstanceType<typeof Tree>>();

  const adminPermissionTree = computed((): TreeNodeData[] => {
    // return arrayToTree(permissionData.value?.filter((item) => item.guard === 'Admin'));
    return arrayToTree(permissionData.value?.filter((item) => item.guard === 'Admin' && item.is_show_line));
  });

  const appPermissionTree = computed(() => {
    return arrayToTree(permissionData.value?.filter((item) => item.guard === 'App' && item.is_show_line));
  });

  onMounted(async () => {
    await usePermissionApi.get({ fetchType: 'all', sortBy: 'weight', sortType: 'desc' }).then(({ data }) => {
      btnPermissionData.value = data.filter((item) => !item.is_show_line);
      permissionData.value = data.map((item) => {
        const key = upperFirst(camelCase(item.icon));
        // @ts-ignore
        set(item, 'icon', () => createVNode(Icon[key] || 'span'));
        return item;
      });
      activeKey.value = ['Admin'];
    });
  });

  const getNodeCheckOption = (node: SystemPermission) => {
    return btnPermissionData.value?.filter((btn) => btn.parent_id === node.id)?.map((btn) => ({ value: btn.id, label: btn.label }));
  };

  const onSelect = (treeRef: any, data: any) => {
    if (data.node?.children?.length === 0) {
      treeRef.checkNode(data.node.id, !find(treeRef.getCheckedNodes(), { id: data.node.id }));
    }
    // else {
    //   treeRef.expandNode(data.node.id, !find(treeRef.getExpandedNodes(), { id: data.node.id }));
    // }
  };

  const onCheck = (checkedKeys: Array<string | number>, data: any) => {
    if (data.checked) {
      if (data.node.parent_id === 0 || data.node.type === 'Menu' || data.node.is_show_line) {
        checkedKeys.push(
          ...findTreeParentIds(permissionData.value, data.node.parent_id),
          ...findTreeChildIds(permissionData.value, data.node.id)
        );
      } else {
        checkedKeys.push(...findTreeParentIds(permissionData.value, data.node.parent_id));
      }
    } else {
      pull(checkedKeys, ...findTreeChildIds(permissionData.value, data.node.id));
    }
  };

  const onBtnCheck = (value: number[], ev: Event, node: SystemPermission) => {
    // @ts-ignore
    if (ev.target && !checkIds.value.includes(ev.target?.value)) {
      value.push(...findTreeParentIds(permissionData.value, node.id));
    }
  };
</script>

<template>
  <Collapse v-model:active-key="activeKey" :bordered="false" :accordion="true">
    <CollapseItem key="Admin" header="管理后台">
      <div style="max-height: 340px; overflow: auto">
        <Tree
          ref="adminPermissionRef"
          v-model:checked-keys="checkIds"
          size="small"
          :data="adminPermissionTree"
          :field-names="{ key: 'id', title: 'label', children: 'children', icon: 'icn' }"
          :checkable="true"
          :block-node="true"
          :check-strictly="true"
          :only-check-leaf="false"
          :auto-expand-parent="false"
          action-on-node-click="expand"
          @select="(selectedKeys:Array<string | number>, data: any) => onSelect(adminPermissionRef, data)"
          @check="onCheck"
        >
          <template #extra="nodeData">
            <CheckboxGroup
              v-model="checkIds"
              :options="getNodeCheckOption(nodeData)"
              @change="(value: any[], ev: Event) => onBtnCheck(value as number[], ev, nodeData)"
            />
          </template>
        </Tree>
      </div>
    </CollapseItem>
    <CollapseItem key="App" header="移动端">
      <div style="max-height: 340px; overflow: auto">
        <Tree
          ref="appPermissionRef"
          v-model:checked-keys="checkIds"
          size="small"
          :data="appPermissionTree"
          :field-names="{ key: 'id', title: 'label', children: 'children', icon: 'icon' }"
          :checkable="true"
          :block-node="true"
          :check-strictly="true"
          :only-check-leaf="false"
          :auto-expand-parent="false"
          action-on-node-click="expand"
          @select="(selectedKeys:Array<string | number>, data:any) => onSelect(appPermissionRef, data)"
          @check="onCheck"
        >
          <template #extra="nodeData">
            <CheckboxGroup
              v-model="checkIds"
              :options="getNodeCheckOption(nodeData)"
              @change="(value: any[], ev:Event) => onBtnCheck(value as number[], ev, nodeData)"
            />
          </template>
        </Tree>
      </div>
    </CollapseItem>
  </Collapse>
</template>

<style lang="less" scoped>
  :deep(.arco-tree-node-selected .arco-tree-node-title) {
    color: unset;
  }
</style>