Procházet zdrojové kódy

feat: 修改权限

DESKTOP-USV654P\pc před 9 měsíci
rodič
revize
6a78ebfd98

+ 83 - 46
apps/web-baicai/src/components/bc-tree/src/bc-tree.vue

@@ -7,7 +7,7 @@ import type { PropType, StyleValue } from 'vue';
 
 import type { ApiConfig, FieldNames } from '#/components/form/types';
 
-import { computed, reactive, ref, unref, watch, watchEffect } from 'vue';
+import { computed, reactive, ref, toRaw, unref, watch, watchEffect } from 'vue';
 
 import { VbenLoading, VbenScrollbar } from '@vben/common-ui';
 import { EmptyIcon } from '@vben/icons';
@@ -65,16 +65,34 @@ const props = defineProps({
     type: Boolean,
     default: false,
   },
+  checkedKeys: {
+    type: Array as PropType<Key[]>,
+    default: () => [],
+  },
+  checkStrictly: {
+    type: Boolean,
+    default: false,
+  },
 });
-
-const emit = defineEmits(['update:searchValue', 'change']);
+const emit = defineEmits([
+  'update:searchValue',
+  'change',
+  'update:value',
+  'check',
+]);
 
 const state = reactive<{
   autoExpandParent: boolean;
+  checkedKeys: (number | string)[];
+  checkStrictly: boolean;
   expandedKeys: (number | string)[];
+  lastLeafKeys: (number | string)[];
 }>({
   expandedKeys: [],
   autoExpandParent: true,
+  checkedKeys: [],
+  lastLeafKeys: [],
+  checkStrictly: props.checkStrictly,
 });
 
 const toolbarList = computed(() => {
@@ -91,15 +109,15 @@ const toolbarList = computed(() => {
 
   return checkable
     ? [
+        ...defaultToolbarList,
         { label: '选择全部', value: ToolbarEnum.SELECT_ALL },
         {
           label: '取消选择',
           value: ToolbarEnum.UN_SELECT_ALL,
           divider: checkable,
         },
-        ...defaultToolbarList,
-        { label: '层级关联', value: ToolbarEnum.CHECK_STRICTLY },
-        { label: '层级独立', value: ToolbarEnum.CHECK_UN_STRICTLY },
+        // { label: '层级关联', value: ToolbarEnum.CHECK_STRICTLY },
+        // { label: '层级独立', value: ToolbarEnum.CHECK_UN_STRICTLY },
       ]
     : defaultToolbarList;
 });
@@ -124,7 +142,10 @@ const getFieldNames = computed((): Required<FieldNames> => {
   };
 });
 
-const { getAllKeys } = useTree(treeDataRef, getFieldNames);
+const { getAllKeys, getLeafNodeIds, getEnabledKeys } = useTree(
+  treeDataRef,
+  getFieldNames,
+);
 const handleExpand = (keys: Key[]) => {
   state.expandedKeys = keys;
   state.autoExpandParent = false;
@@ -134,23 +155,20 @@ const handleSelect = (keys: Key[], e: any) => {
   emit('change', keys, e);
 };
 
-// const getParentKey = (
-//   key: number | string,
-//   tree: TreeProps['treeData'],
-// ): number | string | undefined => {
-//   let parentKey;
-//   tree &&
-//     tree.forEach((node) => {
-//       if (node.children) {
-//         if (node.children.some((item) => item.key === key)) {
-//           parentKey = node.key;
-//         } else if (getParentKey(key, node.children)) {
-//           parentKey = getParentKey(key, node.children);
-//         }
-//       }
-//     });
-//   return parentKey;
-// };
+const handleCheck = (keys: any) => {
+  let currentValue = [...keys];
+  if (searchState.startSearch) {
+    currentValue = currentValue.filter((item: number | string) => {
+      return state.lastLeafKeys.includes(item as never);
+    });
+  }
+
+  state.checkedKeys = currentValue;
+
+  const rawVal = toRaw(state.checkedKeys);
+  emit('update:value', rawVal);
+  emit('check', rawVal);
+};
 
 const contentStyle = computed<StyleValue>(() => {
   let height = 45;
@@ -170,6 +188,7 @@ const fetch = async () => {
     } else {
       treeDataRef.value = props.api.result ? get(res, props.api.result) : [];
     }
+    state.lastLeafKeys = getLeafNodeIds();
   } catch (error) {
     console.warn(error);
   } finally {
@@ -181,6 +200,14 @@ watchEffect(() => {
   props.immediate && fetch();
 });
 
+watchEffect(() => {
+  state.checkStrictly = props.checkStrictly;
+});
+
+watchEffect(() => {
+  state.checkedKeys = props.checkedKeys;
+});
+
 watch(
   () => props.api.params,
   () => {
@@ -254,15 +281,19 @@ const getNotFound = computed((): boolean => {
 const expandAll = (expandAll: boolean) => {
   state.expandedKeys = expandAll ? getAllKeys() : ([] as KeyType[]);
 };
+
+const checkAll = (checkAll: boolean) => {
+  state.checkedKeys = checkAll ? getEnabledKeys() : ([] as KeyType[]);
+};
 const handleMenuClick = async (e: any) => {
   const { key } = e;
   switch (key) {
     case ToolbarEnum.CHECK_STRICTLY: {
-      // emit('strictly-change', false);
+      state.checkStrictly = false;
       break;
     }
     case ToolbarEnum.CHECK_UN_STRICTLY: {
-      // emit('strictly-change', true);
+      state.checkStrictly = true;
       break;
     }
     case ToolbarEnum.EXPAND_ALL: {
@@ -274,7 +305,7 @@ const handleMenuClick = async (e: any) => {
       break;
     }
     case ToolbarEnum.SELECT_ALL: {
-      // props.checkAll?.(true);
+      checkAll(true);
       break;
     }
     case ToolbarEnum.UN_EXPAND_ALL: {
@@ -282,7 +313,7 @@ const handleMenuClick = async (e: any) => {
       break;
     }
     case ToolbarEnum.UN_SELECT_ALL: {
-      // props.checkAll?.(false);
+      checkAll(false);
       break;
     }
   }
@@ -322,6 +353,7 @@ const handleMenuClick = async (e: any) => {
       <DirectoryTree
         :expanded-keys="state.expandedKeys"
         :auto-expand-parent="state.autoExpandParent"
+        :checked-keys="state.checkedKeys"
         :tree-data="getTreeData"
         :field-names="{
           title: fieldNames.label,
@@ -331,28 +363,33 @@ const handleMenuClick = async (e: any) => {
         block-node
         :show-icon="false"
         class="px-2"
+        :checkable="checkable"
         @expand="handleExpand"
         @select="handleSelect"
+        @check="handleCheck"
+        :check-strictly="state.checkStrictly"
       >
         <template #title="treeDataItem">
-          <span
-            v-if="treeDataItem[fieldNames.label].indexOf(searchListData) > -1"
-          >
-            {{
-              treeDataItem[fieldNames.label].substring(
-                0,
-                treeDataItem[fieldNames.label].indexOf(searchListData),
-              )
-            }}
-            <span style="color: #f50">{{ searchListData }}</span>
-            {{
-              treeDataItem[fieldNames.label].substring(
-                treeDataItem[fieldNames.label].indexOf(searchListData) +
-                  searchListData.length,
-              )
-            }}
-          </span>
-          <span v-else>{{ treeDataItem[fieldNames.label] }}</span>
+          <slot name="title" v-bind="treeDataItem">
+            <span
+              v-if="treeDataItem[fieldNames.label].indexOf(searchListData) > -1"
+            >
+              {{
+                treeDataItem[fieldNames.label].substring(
+                  0,
+                  treeDataItem[fieldNames.label].indexOf(searchListData),
+                )
+              }}
+              <span style="color: #f50">{{ searchListData }}</span>
+              {{
+                treeDataItem[fieldNames.label].substring(
+                  treeDataItem[fieldNames.label].indexOf(searchListData) +
+                    searchListData.length,
+                )
+              }}
+            </span>
+            <span v-else>{{ treeDataItem[fieldNames.label] }}</span>
+          </slot>
         </template>
       </DirectoryTree>
       <div

+ 69 - 0
apps/web-baicai/src/components/bc-tree/src/useTree.ts

@@ -26,7 +26,76 @@ export function useTree(
     return keys as KeyType[];
   }
 
+  function getChildrenKeys(nodeKey: number | string, list?: TreeDataItem[]) {
+    const keys: (number | string)[] = [];
+    const treeData = list || unref(treeDataRef);
+    const { value: keyField, children: childrenField } = unref(getFieldNames);
+    if (!childrenField || !keyField) return keys;
+    for (const node of treeData) {
+      const children = node[childrenField];
+      if (nodeKey === node[keyField]) {
+        keys.push(node[keyField]);
+        if (children && children.length > 0) {
+          keys.push(...(getAllKeys(children) as string[]));
+        }
+      } else {
+        if (children && children.length > 0) {
+          keys.push(...getChildrenKeys(nodeKey, children));
+        }
+      }
+    }
+    return keys as (number | string)[];
+  }
+
+  /**
+   * 获取所有叶子节点
+   * @param treeData
+   */
+  function getLeafNodeIds(list?: TreeDataItem[]) {
+    const treeData = list || unref(treeDataRef);
+    const leafNodeIds: any = [];
+
+    function traverse(node: any) {
+      const { value: keyField, children: childrenField } = unref(getFieldNames);
+      if (
+        !node[childrenField || 'children'] ||
+        node[childrenField || 'children'].length === 0
+      ) {
+        leafNodeIds.push(node[keyField]);
+      } else {
+        for (const child of node[childrenField || 'children']) {
+          traverse(child);
+        }
+      }
+    }
+    for (const root of treeData) {
+      traverse(root);
+    }
+    return leafNodeIds;
+  }
+
+  function getEnabledKeys(list?: TreeDataItem[]) {
+    const keys: string[] = [];
+    const treeData = list || unref(treeDataRef);
+    const { value: keyField, children: childrenField } = unref(getFieldNames);
+    if (!childrenField || !keyField) return keys;
+
+    for (const node of treeData) {
+      node.disabled !== true &&
+        node.selectable !== false &&
+        keys.push(node[keyField]);
+      const children = node[childrenField];
+      if (children && children.length > 0) {
+        keys.push(...(getEnabledKeys(children) as string[]));
+      }
+    }
+    return keys as KeyType[];
+  }
+
   return {
     getAllKeys,
+    getChildrenKeys,
+    getLeafNodeIds,
+    getEnabledKeys,
   };
 }

+ 1 - 1
apps/web-baicai/src/components/form/components/bc-popup.vue

@@ -124,7 +124,7 @@ watch(
     <div
       :class="
         cn(
-          'bg-popover text-popover-foreground data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 border-border relative z-[1000] max-h-96 min-w-32 overflow-hidden rounded-md border',
+          'border-border relative max-h-96 min-w-32 overflow-hidden rounded-md border',
         )
       "
       class="bc-popup-selector"

+ 3 - 2
apps/web-baicai/src/components/form/helper.ts

@@ -18,9 +18,10 @@ export const createApiFunction = (apiConfig: ApiConfig) => {
               return EnumApi.getList(params);
             }
             default: {
-              return (requestClient as any)[apiConfig.method](
+              const method = apiConfig.method || 'get';
+              return (requestClient as any)[method](
                 apiConfig.url as any,
-                apiConfig.method === 'get' ? { params } : params,
+                method === 'get' ? { params } : params,
               );
             }
           }

+ 33 - 96
apps/web-baicai/src/components/select-card/src/select-card-item.vue

@@ -1,7 +1,9 @@
 <script setup lang="ts">
+import type { PropType } from 'vue';
+
 import type { Recordable } from '@vben/types';
 
-import { computed, type PropType, useSlots } from 'vue';
+import { computed, useSlots } from 'vue';
 
 import { Tooltip } from 'ant-design-vue';
 
@@ -50,6 +52,10 @@ const props = defineProps({
     type: Boolean as PropType<boolean>,
     default: false,
   },
+  checked: {
+    type: Boolean as PropType<boolean>,
+    default: false,
+  },
 });
 
 const hasCheckSlot = computed(() => {
@@ -94,26 +100,25 @@ const getBgcolor = computed(() => {
   }
 });
 
-const itemleftwidth = computed(() => {
-  return props.showTree ? '30%' : '25%';
-});
+// const itemleftwidth = computed(() => {
+//   return props.showTree ? '30%' : '25%';
+// });
 </script>
 
 <template>
-  <div class="select-card-item">
-    <div class="select-card-item-box">
-      <div class="select-card-item-left">
-        <img :src="getImage" />
-      </div>
-      <div class="z-10">
-        <div
-          v-for="(item, index) in fieldNames"
-          :key="index"
-          class="select-card-item-right flex items-center"
-        >
-          <div class="select-card-item-right-title">
+  <div
+    class="card-box text-card-foreground border-border bg-muted relative w-full border border-dotted p-[16px] shadow"
+    :style="{
+      'border-color': checked ? getFontColor : '',
+    }"
+  >
+    <div class="relative flex">
+      <img :src="getImage" />
+      <div class="flex flex-1 flex-col justify-between py-1">
+        <div v-for="(item, index) in fieldNames" :key="index" class="pl-4">
+          <h4>
             {{ item.title }}
-          </div>
+          </h4>
           <Tooltip
             v-if="
               model[item.value] &&
@@ -121,92 +126,35 @@ const itemleftwidth = computed(() => {
             "
             :title="model[item.value]"
           >
-            <div class="select-card-item-right-name">
+            <h4>
               {{
                 `${model[item.value].slice(0, model[item.value].maxLength)}...`
               }}
-            </div>
+            </h4>
           </Tooltip>
-          <div v-else class="select-card-item-right-name">
+          <h4 v-else>
             {{ model[item.value] || '-' }}
-          </div>
+          </h4>
         </div>
       </div>
-      <!-- <div v-if="props.disabled">
-        <div class="fixed-checked"> 禁用 </div>
-      </div> -->
-      <div v-if="hasCheckSlot" class="fixed-checked">
-        <slot name="check"></slot>
-      </div>
-      <div class="fixed-icon">
-        <Icon :color="getFillColor" class="size-16" icon="fa6-solid:user-tie" />
+      <div
+        class="absolute right-[-22px] top-[-22px] rotate-[48deg] opacity-70 dark:opacity-10"
+      >
+        <Icon :color="getFillColor" :size="64" icon="fa6-solid:user-tie" />
       </div>
     </div>
+    <div v-if="hasCheckSlot" class="absolute bottom-[4px] right-[8px]">
+      <slot name="check"></slot>
+    </div>
   </div>
 </template>
 
 <style lang="less" scoped>
 .select-card-item {
-  width: 30%;
-  margin-bottom: 20px;
-  margin-left: 20px;
-  overflow: hidden;
-  border-radius: 8px;
-  border-color: transparent;
   background: v-bind(getBgcolor);
-
   &:hover {
     border: 1px dotted v-bind(getFontColor);
   }
-
-  &-box {
-    display: flex;
-    position: relative;
-    margin: 14px;
-  }
-
-  &-left {
-    width: v-bind(itemleftwidth);
-    margin-right: 14px;
-
-    img {
-      width: 100%;
-      height: 100%;
-    }
-  }
-
-  &-right {
-    &-title {
-      margin: 10px 10px 4px 0;
-      opacity: 0.8;
-      color: #999;
-      font-size: 12px;
-      font-weight: bold;
-    }
-
-    &-name {
-      margin: 8px 0 4px;
-      opacity: 0.8;
-      color: #303133;
-      font-size: 14px;
-      font-weight: bold;
-    }
-  }
-
-  .fixed-checked {
-    position: absolute;
-    z-index: 1;
-    right: -6px;
-    bottom: -4px;
-  }
-
-  .fixed-icon {
-    position: absolute;
-    z-index: 0;
-    top: -24px;
-    right: -24px;
-    transform: rotate(48deg);
-  }
 }
 
 :deep(.ant-checkbox-inner) {
@@ -225,15 +173,4 @@ const itemleftwidth = computed(() => {
 :deep(.ant-checkbox-input:focus + .ant-checkbox-inner) {
   border-color: v-bind(getFontColor);
 }
-
-.picked {
-  border-width: 1px;
-  border-style: dotted;
-  border-color: v-bind(getFontColor);
-}
-
-.not-picked {
-  border-width: 1px;
-  border-style: dotted;
-}
 </style>

+ 50 - 90
apps/web-baicai/src/components/select-card/src/select-card.vue

@@ -1,9 +1,11 @@
 <script lang="ts" setup>
+import type { PropType } from 'vue';
+
 import type { Recordable } from '@vben/types';
 
 import type { ApiConfig } from '../../form/types';
 
-import { type PropType, reactive, ref } from 'vue';
+import { reactive, ref } from 'vue';
 
 import { useVbenModal } from '@vben/common-ui';
 import { isFunction } from '@vben/utils';
@@ -11,8 +13,7 @@ import { isFunction } from '@vben/utils';
 import { Checkbox, message, Pagination } from 'ant-design-vue';
 
 import { useVbenForm } from '#/adapter/form';
-import { EnumApi, QueryApi } from '#/api';
-import { requestClient } from '#/api/request';
+import { createApiFunction } from '#/components/form/helper';
 import { get } from '#/utils';
 
 import SelectCardItem from './select-card-item.vue';
@@ -88,26 +89,12 @@ const state = reactive<{
     color: '#b389ff',
   },
 });
-
 const fetch = async () => {
-  const api: any =
-    typeof state.api.url === 'string' || !state.api.url
-      ? (params: any) => {
-          Object.assign(params, state.page);
-          if (state.api.type === 'enum') {
-            return EnumApi.getList(params);
-          } else if (state.api.type === 'api') {
-            return QueryApi.postOptions(params);
-          } else
-            return (requestClient as any)[state.api.method](
-              state.api.url as any,
-              state.api.method === 'get' ? { params } : params,
-            );
-        }
-      : state.api.url;
+  const api = createApiFunction({ ...state.api });
   if (!api || !isFunction(api)) return;
   try {
-    const res = await api(state.api.params);
+    const params = { ...state.page, ...state.api.params };
+    const res = await api(params);
     if (Array.isArray(res)) {
       cardData.value = res;
     } else {
@@ -234,82 +221,55 @@ const handelPageChange = async (pageIndex: number) => {
 </script>
 
 <template>
-  <Modal :title="`选择${title}`" class="w-[1000px]">
-    <div class="select-card flex justify-between">
-      <div class="ml-4 flex-1">
-        <div class="select-card-header">
-          <div class="flex justify-between">
-            <div class="text-foreground text-lg font-semibold">
-              {{ title }}列表
-            </div>
-          </div>
+  <Modal :title="`选择${title}`" class="w-[1000px]" :fullscreen-button="false">
+    <div class="relative h-full p-4">
+      <div class="card-box text-card-foreground border-border w-full border">
+        <div class="flex flex-col gap-y-1.5 border-b p-2">
+          <h3 class="text-xl font-semibold tracking-tight">{{ title }}列表</h3>
+        </div>
+        <div class="p-2">
           <Form class="mt-4" />
         </div>
-        <div class="select-card-body">
-          <SelectCardItem
-            v-for="(item, index) in cardData"
-            :key="index"
-            :class="
-              state.selectedIds.includes(item[state.keyName])
-                ? 'picked'
-                : 'not-picked'
-            "
-            :config="state.config"
-            :disabled="
-              state.disabledIds &&
-              state.disabledIds.includes(item[state.keyName] as never)
-                ? true
-                : false
-            "
-            :field-names="state.fieldNames"
-            :model="item"
-            @click="hancelClick(item)"
-          >
-            <template #check>
-              <Checkbox
-                :checked="state.selectedIds.includes(item[state.keyName])"
-                size="small"
-              />
-            </template>
-          </SelectCardItem>
-          <div class="select-card-pagination">
-            <Pagination
-              v-model:current="state.page.pageIndex"
-              :page-size="state.page.pageSize"
-              :total="state.page.total"
-              show-less-items
-              show-quick-jumper
-              size="small"
-              @change="handelPageChange"
-            />
+        <div class="h-[336px]">
+          <div class="grid grid-cols-3 gap-4 p-2">
+            <SelectCardItem
+              v-for="(item, index) in cardData"
+              :key="index"
+              :checked="state.selectedIds.includes(item[state.keyName])"
+              :config="state.config"
+              :disabled="
+                state.disabledIds &&
+                state.disabledIds.includes(item[state.keyName] as never)
+                  ? true
+                  : false
+              "
+              :field-names="state.fieldNames"
+              :model="item"
+              @click="hancelClick(item)"
+            >
+              <template #check>
+                <Checkbox
+                  :checked="state.selectedIds.includes(item[state.keyName])"
+                  size="small"
+                />
+              </template>
+            </SelectCardItem>
           </div>
         </div>
+        <div class="my-[16px] flex w-full justify-end">
+          <Pagination
+            v-model:current="state.page.pageIndex"
+            :page-size="state.page.pageSize"
+            :total="state.page.total"
+            show-less-items
+            show-quick-jumper
+            size="small"
+            @change="handelPageChange"
+          />
+        </div>
       </div>
     </div>
   </Modal>
 </template>
 
-<style lang="less" scoped>
-.select-card {
-  display: flex;
-  height: 500px;
-  margin: 10px;
-  overflow: auto;
-  position: relative;
-
-  &-body {
-    display: flex;
-    flex-wrap: wrap;
-    padding: 10px 0;
-    overflow-y: auto;
-  }
-
-  &-pagination {
-    // width: 100%;
-    position: absolute;
-    text-align: right;
-    bottom: 10px;
-    right: 10px;
-  }
-}
-</style>
+<style lang="less" scoped></style>

+ 0 - 26
apps/web-baicai/src/utils/utils.ts

@@ -76,32 +76,6 @@ export const get = (object: any, path: string) => {
   return current;
 };
 
-/**
- * 获取所有叶子节点
- * @param treeData
- */
-export const getLeafNodeIds = (treeData: any) => {
-  const leafNodeIds: any = [];
-
-  function traverse(node: any) {
-    // 如果当前节点没有 children 或 children 是空数组,则它是叶子节点
-    if (!node.children || node.children.length === 0) {
-      leafNodeIds.push(node.id);
-    } else {
-      // 否则,递归遍历每个子节点
-      for (const child of node.children) {
-        traverse(child);
-      }
-    }
-  }
-
-  // 遍历树的根节点
-  for (const root of treeData) {
-    traverse(root);
-  }
-  return leafNodeIds;
-};
-
 export function getFileNameWithoutExtension(path: string) {
   // 使用正则表达式匹配最后一个 / 后面的所有字符,但不包括最后一个点(.)之后的内容
   const match = path.match(/[^/]+(?=\.[^./]+$|$)/);

+ 1 - 1
apps/web-baicai/src/views/system/design/table/components/step/baseConfig.vue

@@ -44,7 +44,7 @@ const [Form, { validate, setValues, getValues }] = useVbenForm({
       rules: 'required',
     },
     {
-      component: 'ApiPopup',
+      component: 'BcPopup',
       componentProps: {
         placeholder: '请选择接口',
         api: 'query-popup',

+ 22 - 73
apps/web-baicai/src/views/system/menu/components/grant.vue

@@ -1,6 +1,4 @@
 <script lang="ts" setup>
-import type { TreeProps } from 'ant-design-vue';
-
 import type { Recordable } from '@vben/types';
 
 import type { RelationRequest } from '#/api/model';
@@ -9,11 +7,11 @@ import { onMounted, reactive, ref, unref } from 'vue';
 
 import { useVbenDrawer } from '@vben/common-ui';
 
-import { Button, message, Tree } from 'ant-design-vue';
+import { message } from 'ant-design-vue';
 
 import { MenuApi, RoleApi, TenantApi } from '#/api';
+import { BcTree } from '#/components/bc-tree';
 import { Icon } from '#/components/icon';
-import { getLeafNodeIds } from '#/utils';
 
 defineOptions({
   name: 'MenuGrant',
@@ -24,34 +22,16 @@ const modelRef = ref<Recordable<any>>({});
 
 const state = reactive<{
   checkedKeys: number[] | string[];
-  expandedKeys: number[] | string[];
-  lastLeafKeys: number[] | string[];
-  selectedKeys: number[] | string[];
 }>({
-  selectedKeys: [],
-  expandedKeys: [],
   checkedKeys: [],
-  lastLeafKeys: [],
 });
 
-const treeData = ref<TreeProps['treeData']>([]);
-const isExpand = ref(false);
-
-// 所有节点key
-const allNodeIds = ref([]);
-// 当前展开的key
-const currentExpandedKeys = ref([]);
-
 const getRoleDetailData = async () => {
   const data =
     modelRef.value.type === 'tenant'
       ? await TenantApi.getMenuIds(unref(modelRef).id)
       : await RoleApi.getMenuIds(unref(modelRef).id);
   state.checkedKeys = data || [];
-  state.expandedKeys = data || [];
-  state.selectedKeys = data.filter((item: number | string) => {
-    return state.lastLeafKeys.includes(item as never);
-  });
 };
 
 const [Drawer, { close, setState, getData }] = useVbenDrawer({
@@ -85,60 +65,29 @@ const [Drawer, { close, setState, getData }] = useVbenDrawer({
   },
 });
 
-const fetch = async () => {
-  treeData.value = (await MenuApi.getList(
-    {},
-  )) as unknown as TreeProps['treeData'];
-
-  state.lastLeafKeys = getLeafNodeIds(unref(treeData));
-};
-
-onMounted(async () => {
-  await fetch();
-});
-/**
- * 点击复选框触发处理
- * @param mCheckedKeys
- */
-const handleCheck = (mCheckedKeys: any, e: any) => {
-  state.selectedKeys = mCheckedKeys;
-  state.checkedKeys = [...mCheckedKeys, ...e.halfCheckedKeys];
-};
-
-const handleExpand = (expandedKeys: any) => {
-  state.expandedKeys = expandedKeys;
-};
-// 展开折叠按钮事件
-const handleExpandAndCollapse = () => {
-  isExpand.value = !isExpand.value;
-  currentExpandedKeys.value = isExpand.value ? allNodeIds.value : [];
+onMounted(async () => {});
+const handleCheck = (value: any) => {
+  state.checkedKeys = [...value];
 };
 </script>
 <template>
   <Drawer class="w-[1000px]" title="授权菜单">
-    <Button type="primary" @click="handleExpandAndCollapse">
-      {{ isExpand ? '折叠' : '展开' }}
-    </Button>
-    <Tree
-      v-model:checked-keys="state.selectedKeys"
-      :block-node="true"
-      :checkable="true"
-      :default-expand-all="true"
-      :expanded-keys="state.expandedKeys"
-      :field-names="{
-        title: 'title',
-        key: 'id',
-      }"
-      :tree-data="treeData"
-      @check="handleCheck"
-      @expand="handleExpand"
-    >
-      <template #title="{ meta }">
-        <div class="flex items-center">
-          <Icon v-if="meta.icon" :icon="meta.icon" class="mr-1" />
-          <span>{{ meta.title }}</span>
-        </div>
-      </template>
-    </Tree>
+    <div class="h-full">
+      <BcTree
+        :api="{ url: MenuApi.getList }"
+        :field-names="{ label: 'name', value: 'id', children: 'children' }"
+        :checkable="true"
+        :checked-keys="state.checkedKeys"
+        @check="handleCheck"
+        title="菜单列表"
+      >
+        <template #title="{ meta }">
+          <div class="flex items-center">
+            <Icon v-if="meta.icon" :icon="meta.icon" class="mr-1" />
+            <span>{{ meta.title }}</span>
+          </div>
+        </template>
+      </BcTree>
+    </div>
   </Drawer>
 </template>