Parcourir la source

perf(tree): strengthen BasicTree function

Vben il y a 4 ans
Parent
commit
cd8e924d46

+ 3 - 2
CHANGELOG.zh_CN.md

@@ -11,8 +11,9 @@
 - 新增修改密码界面
 - 新增部门管理示例界面
 - 新增 WebSocket 示例和服务脚本
-- BasicTree 组件新增 `renderIcon` 属性用于控制层级图标显示
-- BasicTree->actionItem 新增 show 属性,用于动态控制按钮显示
+- Tree 组件新增 `renderIcon` 属性用于控制层级图标显示
+- Tree->actionItem 新增 show 属性,用于动态控制按钮显示
+- Tree 新增工具栏/title/搜索功能
 
 ### ⚡ Performance Improvements
 

+ 1 - 1
package.json

@@ -29,7 +29,7 @@
   "dependencies": {
     "@iconify/iconify": "^2.0.0-rc.6",
     "@vueuse/core": "^4.3.1",
-    "@zxcvbn-ts/core": "^0.2.0",
+    "@zxcvbn-ts/core": "^0.3.0",
     "ant-design-vue": "2.0.1",
     "apexcharts": "^3.25.0",
     "axios": "^0.21.1",

+ 2 - 1
src/components/StrengthMeter/src/index.vue

@@ -23,7 +23,8 @@
 
   import { Input } from 'ant-design-vue';
 
-  import zxcvbn from '@zxcvbn-ts/core';
+  // @ts-ignore
+  import { zxcvbn } from '@zxcvbn-ts/core';
   import { useDesign } from '/@/hooks/web/useDesign';
   import { propTypes } from '/@/utils/propTypes';
 

+ 110 - 0
src/components/Tree/src/TreeHeader.vue

@@ -0,0 +1,110 @@
+<template>
+  <div class="flex px-2 py-1.5 items-center border-b-1">
+    <BasicTitle :helpMessage="helpMessage" v-if="title">{{ title }}</BasicTitle>
+
+    <div class="flex flex-1 justify-end items-center cursor-pointer" v-if="search || toolbar">
+      <div class="mr-1 w-2/3" v-if="search">
+        <InputSearch :placeholder="t('common.searchText')" size="small" @change="handleSearch" />
+      </div>
+      <Dropdown @click.prevent v-if="toolbar">
+        <Icon icon="ion:ellipsis-vertical" />
+        <template #overlay>
+          <Menu @click="handleMenuClick">
+            <MenuItem v-for="item in toolbarList" :key="item.value">
+              {{ item.label }}
+            </MenuItem>
+          </Menu>
+        </template>
+      </Dropdown>
+    </div>
+  </div>
+</template>
+<script lang="ts">
+  import type { PropType } from 'vue';
+  import { defineComponent, ref } from 'vue';
+
+  import { Dropdown, Menu, Checkbox, Input } from 'ant-design-vue';
+  import { Icon } from '/@/components/Icon';
+  import { BasicTitle } from '/@/components/Basic';
+
+  import { propTypes } from '/@/utils/propTypes';
+
+  import { useI18n } from '/@/hooks/web/useI18n';
+  import { useDebounce } from '/@/hooks/core/useDebounce';
+
+  import { ToolbarEnum } from './enum';
+
+  interface MenuInfo {
+    key: ToolbarEnum;
+  }
+  export default defineComponent({
+    name: 'BasicTreeHeader',
+    components: {
+      BasicTitle,
+      Icon,
+      Checkbox,
+      Dropdown,
+      Menu,
+      MenuItem: Menu.Item,
+      InputSearch: Input.Search,
+    },
+    props: {
+      helpMessage: {
+        type: [String, Array] as PropType<string | string[]>,
+        default: '',
+      },
+      title: propTypes.string,
+      toolbar: propTypes.bool,
+      search: propTypes.bool,
+      checkAll: propTypes.func,
+      expandAll: propTypes.func,
+    },
+    emits: ['strictly-change', 'search'],
+    setup(props, { emit }) {
+      const { t } = useI18n();
+      const toolbarList = ref([
+        { label: t('component.tree.selectAll'), value: ToolbarEnum.SELECT_ALL },
+        { label: t('component.tree.unSelectAll'), value: ToolbarEnum.UN_SELECT_ALL },
+        { label: t('component.tree.expandAll'), value: ToolbarEnum.EXPAND_ALL },
+        { label: t('component.tree.unExpandAll'), value: ToolbarEnum.UN_EXPAND_ALL },
+        { label: t('component.tree.checkStrictly'), value: ToolbarEnum.CHECK_STRICTLY },
+        { label: t('component.tree.checkUnStrictly'), value: ToolbarEnum.CHECK_UN_STRICTLY },
+      ]);
+
+      function handleMenuClick(e: MenuInfo) {
+        const { key } = e;
+        switch (key) {
+          case ToolbarEnum.SELECT_ALL:
+            props.checkAll?.(true);
+            break;
+          case ToolbarEnum.UN_SELECT_ALL:
+            props.checkAll?.(false);
+            break;
+          case ToolbarEnum.EXPAND_ALL:
+            props.expandAll?.(true);
+            break;
+          case ToolbarEnum.UN_EXPAND_ALL:
+            props.expandAll?.(false);
+            break;
+          case ToolbarEnum.CHECK_STRICTLY:
+            emit('strictly-change', false);
+            break;
+          case ToolbarEnum.CHECK_UN_STRICTLY:
+            emit('strictly-change', true);
+            break;
+        }
+      }
+
+      function emitChange(value?: string): void {
+        emit('search', value);
+      }
+      const [debounceEmitChange] = useDebounce(emitChange, 200);
+
+      function handleSearch(e: ChangeEvent): void {
+        debounceEmitChange(e.target.value);
+      }
+
+      return { t, toolbarList, handleMenuClick, handleSearch };
+    },
+  });
+</script>

+ 8 - 0
src/components/Tree/src/enum.ts

@@ -0,0 +1,8 @@
+export enum ToolbarEnum {
+  SELECT_ALL,
+  UN_SELECT_ALL,
+  EXPAND_ALL,
+  UN_EXPAND_ALL,
+  CHECK_STRICTLY,
+  CHECK_UN_STRICTLY,
+}

+ 138 - 85
src/components/Tree/src/index.vue

@@ -1,23 +1,16 @@
 <script lang="tsx">
   import type { ReplaceFields, Keys, CheckKeys, TreeActionType, TreeItem } from './types';
 
-  import {
-    defineComponent,
-    reactive,
-    computed,
-    unref,
-    ref,
-    watchEffect,
-    onMounted,
-    toRaw,
-  } from 'vue';
+  import { defineComponent, reactive, computed, unref, ref, watchEffect, toRaw } from 'vue';
   import { Tree } from 'ant-design-vue';
   import { TreeIcon } from './TreeIcon';
+  import TreeHeader from './TreeHeader.vue';
   // import { DownOutlined } from '@ant-design/icons-vue';
 
   import { omit, get } from 'lodash-es';
   import { isBoolean, isFunction } from '/@/utils/is';
   import { extendSlots } from '/@/utils/helper/tsxHelper';
+  import { filter } from '/@/utils/helper/treeHelper';
 
   import { useTree } from './useTree';
   import { useContextMenu, ContextMenuItem } from '/@/hooks/web/useContextMenu';
@@ -30,18 +23,25 @@
     expandedKeys: Keys;
     selectedKeys: Keys;
     checkedKeys: CheckKeys;
+    checkStrictly: boolean;
   }
   export default defineComponent({
     name: 'BasicTree',
     props: basicProps,
-    emits: ['update:expandedKeys', 'update:selectedKeys', 'update:value', 'get'],
+    emits: ['update:expandedKeys', 'update:selectedKeys', 'update:value', 'change'],
     setup(props, { attrs, slots, emit }) {
       const state = reactive<State>({
+        checkStrictly: props.checkStrictly,
         expandedKeys: props.expandedKeys || [],
         selectedKeys: props.selectedKeys || [],
         checkedKeys: props.checkedKeys || [],
       });
 
+      const searchState = reactive({
+        startSearch: false,
+        searchData: [] as TreeItem[],
+      });
+
       const treeDataRef = ref<TreeItem[]>([]);
 
       const [createContextMenu] = useContextMenu();
@@ -77,6 +77,7 @@
           expandedKeys: state.expandedKeys,
           selectedKeys: state.selectedKeys,
           checkedKeys: state.checkedKeys,
+          checkStrictly: state.checkStrictly,
           replaceFields: unref(getReplaceFields),
           'onUpdate:expandedKeys': (v: Keys) => {
             state.expandedKeys = v;
@@ -88,21 +89,27 @@
           },
           onCheck: (v: CheckKeys) => {
             state.checkedKeys = v;
+            emit('change', v);
             emit('update:value', v);
           },
           onRightClick: handleRightClick,
         };
-        propsData = omit(propsData, 'treeData');
+        propsData = omit(propsData, 'treeData', 'class');
         return propsData;
       });
 
-      const getTreeData = computed((): TreeItem[] => unref(treeDataRef));
-
-      const { deleteNodeByKey, insertNodeByKey, filterByLevel, updateNodeByKey } = useTree(
-        treeDataRef,
-        getReplaceFields
+      const getTreeData = computed((): TreeItem[] =>
+        searchState.startSearch ? searchState.searchData : unref(treeDataRef)
       );
 
+      const {
+        deleteNodeByKey,
+        insertNodeByKey,
+        filterByLevel,
+        updateNodeByKey,
+        getAllKeys,
+      } = useTree(treeDataRef, getReplaceFields);
+
       function getIcon(params: Recordable, icon?: string) {
         if (!icon) {
           if (props.renderIcon && isFunction(props.renderIcon)) {
@@ -112,60 +119,6 @@
         return icon;
       }
 
-      function renderAction(node: TreeItem) {
-        const { actionList } = props;
-        if (!actionList || actionList.length === 0) return;
-        return actionList.map((item, index) => {
-          if (isFunction(item.show)) {
-            return item.show?.(node);
-          }
-
-          if (isBoolean(item.show)) {
-            return item.show;
-          }
-
-          return (
-            <span key={index} class={`${prefixCls}__action`}>
-              {item.render(node)}
-            </span>
-          );
-        });
-      }
-
-      function renderTreeNode({ data, level }: { data: TreeItem[] | undefined; level: number }) {
-        if (!data) {
-          return null;
-        }
-        return data.map((item) => {
-          const { title: titleField, key: keyField, children: childrenField } = unref(
-            getReplaceFields
-          );
-
-          const propsData = omit(item, 'title');
-          const icon = getIcon({ ...item, level }, item.icon);
-          return (
-            <Tree.TreeNode {...propsData} node={toRaw(item)} key={get(item, keyField)}>
-              {{
-                title: () => (
-                  <span class={`${prefixCls}-title`}>
-                    {icon && <TreeIcon icon={icon} />}
-                    <span
-                      class={`${prefixCls}__content`}
-                      //  style={unref(getContentStyle)}
-                    >
-                      {get(item, titleField)}
-                    </span>
-                    <span class={`${prefixCls}__actions`}> {renderAction({ ...item, level })}</span>
-                  </span>
-                ),
-                default: () =>
-                  renderTreeNode({ data: get(item, childrenField) || [], level: level + 1 }),
-              }}
-            </Tree.TreeNode>
-          );
-        });
-      }
-
       async function handleRightClick({ event, node }: any) {
         const { rightMenuList: menuList = [], beforeRightClick } = props;
         let rightMenuList: ContextMenuItem[] = [];
@@ -205,6 +158,32 @@
         return state.checkedKeys;
       }
 
+      function checkAll(checkAll: boolean) {
+        state.checkedKeys = checkAll ? getAllKeys() : ([] as Keys);
+      }
+
+      function expandAll(expandAll: boolean) {
+        state.expandedKeys = expandAll ? getAllKeys() : ([] as Keys);
+      }
+
+      function onStrictlyChange(strictly: boolean) {
+        state.checkStrictly = strictly;
+      }
+
+      function handleSearch(searchValue: string) {
+        if (!searchValue) {
+          searchState.startSearch = false;
+          return;
+        }
+        searchState.startSearch = true;
+
+        searchState.searchData = filter(unref(treeDataRef), (node) => {
+          const { title } = node;
+          return title?.includes(searchValue) ?? false;
+          // || key?.includes(searchValue);
+        });
+      }
+
       watchEffect(() => {
         treeDataRef.value = props.treeData as TreeItem[];
         state.expandedKeys = props.expandedKeys;
@@ -212,6 +191,16 @@
         state.checkedKeys = props.checkedKeys;
       });
 
+      watchEffect(() => {
+        if (props.value) {
+          state.checkedKeys = props.value;
+        }
+      });
+
+      watchEffect(() => {
+        state.checkStrictly = props.checkStrictly;
+      });
+
       const instance: TreeActionType = {
         setExpandedKeys,
         getExpandedKeys,
@@ -222,6 +211,8 @@
         insertNodeByKey,
         deleteNodeByKey,
         updateNodeByKey,
+        checkAll,
+        expandAll,
         filterByLevel: (level: number) => {
           state.expandedKeys = filterByLevel(level);
         },
@@ -229,19 +220,83 @@
 
       useExpose<TreeActionType>(instance);
 
-      onMounted(() => {
-        emit('get', instance);
-      });
+      function renderAction(node: TreeItem) {
+        const { actionList } = props;
+        if (!actionList || actionList.length === 0) return;
+        return actionList.map((item, index) => {
+          if (isFunction(item.show)) {
+            return item.show?.(node);
+          }
+
+          if (isBoolean(item.show)) {
+            return item.show;
+          }
+
+          return (
+            <span key={index} class={`${prefixCls}__action`}>
+              {item.render(node)}
+            </span>
+          );
+        });
+      }
+
+      function renderTreeNode({ data, level }: { data: TreeItem[] | undefined; level: number }) {
+        if (!data) {
+          return null;
+        }
+        return data.map((item) => {
+          const { title: titleField, key: keyField, children: childrenField } = unref(
+            getReplaceFields
+          );
 
+          const propsData = omit(item, 'title');
+          const icon = getIcon({ ...item, level }, item.icon);
+          return (
+            <Tree.TreeNode {...propsData} node={toRaw(item)} key={get(item, keyField)}>
+              {{
+                title: () => (
+                  <span class={`${prefixCls}-title pl-2`}>
+                    {icon && <TreeIcon icon={icon} />}
+                    <span
+                      class={`${prefixCls}__content`}
+                      //  style={unref(getContentStyle)}
+                    >
+                      {get(item, titleField)}
+                    </span>
+                    <span class={`${prefixCls}__actions`}> {renderAction({ ...item, level })}</span>
+                  </span>
+                ),
+                default: () =>
+                  renderTreeNode({ data: get(item, childrenField) || [], level: level + 1 }),
+              }}
+            </Tree.TreeNode>
+          );
+        });
+      }
       return () => {
+        const { title, helpMessage, toolbar, search } = props;
         return (
-          <Tree {...unref(getBindValues)} showIcon={false} class={[prefixCls]}>
-            {{
-              // switcherIcon: () => <DownOutlined />,
-              default: () => renderTreeNode({ data: unref(getTreeData), level: 1 }),
-              ...extendSlots(slots),
-            }}
-          </Tree>
+          <div class={[prefixCls, 'h-full bg-white']}>
+            {(title || toolbar || search) && (
+              <TreeHeader
+                checkAll={checkAll}
+                expandAll={expandAll}
+                title={title}
+                search={search}
+                toolbar={toolbar}
+                helpMessage={helpMessage}
+                onStrictlyChange={onStrictlyChange}
+                onSearch={handleSearch}
+              />
+            )}
+            <Tree {...unref(getBindValues)} showIcon={false}>
+              {{
+                // switcherIcon: () => <DownOutlined />,
+                default: () => renderTreeNode({ data: unref(getTreeData), level: 1 }),
+                ...extendSlots(slots),
+              }}
+            </Tree>
+          </div>
         );
       };
     },
@@ -251,8 +306,6 @@
   @prefix-cls: ~'@{namespace}-basic-tree';
 
   .@{prefix-cls} {
-    position: relative;
-
     .ant-tree-node-content-wrapper {
       position: relative;
 
@@ -278,14 +331,14 @@
     }
 
     &__content {
-      display: inline-block;
+      // display: inline-block;
       overflow: hidden;
     }
 
     &__actions {
       position: absolute;
       top: 2px;
-      right: 2px;
+      right: 3px;
       display: flex;
     }
 

+ 15 - 0
src/components/Tree/src/props.ts

@@ -2,11 +2,26 @@ import type { PropType } from 'vue';
 import type { ReplaceFields, ActionItem, Keys, CheckKeys } from './types';
 import type { ContextMenuItem } from '/@/hooks/web/useContextMenu';
 import type { TreeDataItem } from 'ant-design-vue/es/tree/Tree';
+import { propTypes } from '/@/utils/propTypes';
 
 export const basicProps = {
+  value: {
+    type: Array as PropType<Keys>,
+  },
   renderIcon: {
     type: Function as PropType<(params: Recordable) => string>,
   },
+
+  helpMessage: {
+    type: [String, Array] as PropType<string | string[]>,
+    default: '',
+  },
+
+  title: propTypes.string,
+  toolbar: propTypes.bool,
+  search: propTypes.bool,
+  checkStrictly: propTypes.bool,
+
   replaceFields: {
     type: Object as PropType<ReplaceFields>,
   },

+ 2 - 0
src/components/Tree/src/types.ts

@@ -21,6 +21,8 @@ export type CheckKeys =
   | { checked: string[] | number[]; halfChecked: string[] | number[] };
 
 export interface TreeActionType {
+  checkAll: (checkAll: boolean) => void;
+  expandAll: (expandAll: boolean) => void;
   setExpandedKeys: (keys: Keys) => void;
   getExpandedKeys: () => Keys;
   setSelectedKeys: (keys: Keys) => void;

+ 19 - 2
src/components/Tree/src/useTree.ts

@@ -1,4 +1,4 @@
-import type { InsertNodeParams, ReplaceFields } from './types';
+import type { InsertNodeParams, Keys, ReplaceFields } from './types';
 import type { Ref, ComputedRef } from 'vue';
 import type { TreeDataItem } from 'ant-design-vue/es/tree/Tree';
 
@@ -10,6 +10,23 @@ export function useTree(
   treeDataRef: Ref<TreeDataItem[]>,
   getReplaceFields: ComputedRef<ReplaceFields>
 ) {
+  function getAllKeys(list?: TreeDataItem[]) {
+    const keys: string[] = [];
+    const treeData = list || unref(treeDataRef);
+    const { key: keyField, children: childrenField } = unref(getReplaceFields);
+    if (!childrenField || !keyField) return keys;
+
+    for (let index = 0; index < treeData.length; index++) {
+      const node = treeData[index];
+      keys.push(node[keyField]!);
+      const children = node[childrenField];
+      if (children && children.length) {
+        keys.push(...(getAllKeys(children) as string[]));
+      }
+    }
+    return keys as Keys;
+  }
+
   // Update node
   function updateNodeByKey(key: string, node: TreeDataItem, list?: TreeDataItem[]) {
     if (!key) return;
@@ -94,5 +111,5 @@ export function useTree(
       }
     }
   }
-  return { deleteNodeByKey, insertNodeByKey, filterByLevel, updateNodeByKey };
+  return { deleteNodeByKey, insertNodeByKey, filterByLevel, updateNodeByKey, getAllKeys };
 }

+ 9 - 0
src/locales/lang/en/component/tree.ts

@@ -0,0 +1,9 @@
+export default {
+  selectAll: 'Select All',
+  unSelectAll: 'Cancel Select',
+  expandAll: 'Expand All',
+  unExpandAll: 'Collapse all',
+
+  checkStrictly: 'Hierarchical association',
+  checkUnStrictly: 'Hierarchical independence',
+};

+ 1 - 1
src/locales/lang/en/routes/demo/comp.ts

@@ -12,7 +12,7 @@ export default {
   tree: 'Tree',
 
   treeBasic: 'Basic',
-  editTree: 'Right-click',
+  editTree: 'Searchable/toolbar',
   actionTree: 'Function operation',
 
   modal: 'Modal',

+ 8 - 0
src/locales/lang/zh_CN/component/tree.ts

@@ -0,0 +1,8 @@
+export default {
+  selectAll: '选择全部',
+  unSelectAll: '取消选择',
+  expandAll: '展开全部',
+  unExpandAll: '折叠全部',
+  checkStrictly: '层级关联',
+  checkUnStrictly: '层级独立',
+};

+ 1 - 1
src/locales/lang/zh_CN/routes/demo/comp.ts

@@ -11,7 +11,7 @@ export default {
 
   tree: 'Tree',
   treeBasic: '基础树',
-  editTree: '右键示例',
+  editTree: '可搜索/工具栏',
   actionTree: '函数操作示例',
 
   modal: '弹窗扩展',

+ 12 - 4
src/router/menus/modules/demo/comp.ts

@@ -6,7 +6,10 @@ const menu: MenuModule = {
   menu: {
     name: t('routes.demo.comp.comp'),
     path: '/comp',
-
+    tag: {
+      dot: true,
+      type: 'warn',
+    },
     children: [
       {
         path: 'basic',
@@ -154,6 +157,10 @@ const menu: MenuModule = {
       {
         path: 'tree',
         name: t('routes.demo.comp.tree'),
+        tag: {
+          dot: true,
+          type: 'warn',
+        },
         children: [
           {
             path: 'basic',
@@ -162,6 +169,10 @@ const menu: MenuModule = {
           {
             path: 'editTree',
             name: t('routes.demo.comp.editTree'),
+            tag: {
+              dot: true,
+              type: 'warn',
+            },
           },
           {
             path: 'actionTree',
@@ -172,9 +183,6 @@ const menu: MenuModule = {
       {
         name: t('routes.demo.editor.editor'),
         path: 'editor',
-        tag: {
-          content: 'new',
-        },
         children: [
           {
             path: 'markdown',

+ 4 - 4
src/router/menus/modules/demo/system.ts

@@ -22,21 +22,21 @@ const menu: MenuModule = {
         path: 'role',
         name: t('routes.demo.system.role'),
         tag: {
-          content: 'new',
+          dot: true,
         },
       },
       {
         path: 'menu',
         name: t('routes.demo.system.menu'),
         tag: {
-          content: 'new',
+          dot: true,
         },
       },
       {
         path: 'dept',
         name: t('routes.demo.system.dept'),
         tag: {
-          content: 'new',
+          dot: true,
         },
       },
 
@@ -44,7 +44,7 @@ const menu: MenuModule = {
         path: 'changePassword',
         name: t('routes.demo.system.password'),
         tag: {
-          content: 'new',
+          dot: true,
         },
       },
     ],

+ 4 - 3
src/store/modules/permission.ts

@@ -18,7 +18,7 @@ import { transformObjToRoute } from '/@/router/helper/routeHelper';
 import { transformRouteToMenu } from '/@/router/helper/menuHelper';
 
 import { useMessage } from '/@/hooks/web/useMessage';
-import { useI18n } from '/@/hooks/web/useI18n';
+// import { useI18n } from '/@/hooks/web/useI18n';
 import { ERROR_LOG_ROUTE, PAGE_NOT_FOUND_ROUTE } from '/@/router/constant';
 
 const { createMessage } = useMessage();
@@ -84,7 +84,7 @@ class Permission extends VuexModule {
 
   @Action
   async buildRoutesAction(id?: number | string): Promise<AppRouteRecordRaw[]> {
-    const { t } = useI18n();
+    // const { t } = useI18n();
     let routes: AppRouteRecordRaw[] = [];
     const roleList = toRaw(userStore.getRoleListState);
 
@@ -101,7 +101,8 @@ class Permission extends VuexModule {
       //  If you are sure that you do not need to do background dynamic permissions, please comment the entire judgment below
     } else if (permissionMode === PermissionModeEnum.BACK) {
       createMessage.loading({
-        content: t('sys.app.menuLoading'),
+        content: 'Loading menu...',
+        // content: 't('sys.app.menuLoading')',
         duration: 1,
       });
       // Here to get the background routing menu logic to modify by yourself

+ 18 - 5
src/views/demo/tree/ActionTree.vue

@@ -1,8 +1,14 @@
 <template>
   <PageWrapper title="Tree函数操作示例" contentBackground contentClass="p-4">
     <div class="mb-4">
+      <a-button @click="expandAll(true)" class="mr-2"> 展开全部 </a-button>
+      <a-button @click="expandAll(false)" class="mr-2"> 折叠全部 </a-button>
+      <a-button @click="checkAll(true)" class="mr-2"> 全选 </a-button>
+      <a-button @click="checkAll(false)" class="mr-2"> 全不选 </a-button>
       <a-button @click="handleLevel(2)" class="mr-2"> 显示到第2级 </a-button>
       <a-button @click="handleLevel(1)" class="mr-2"> 显示到第1级 </a-button>
+    </div>
+    <div class="mb-4">
       <a-button @click="handleSetCheckData" class="mr-2"> 设置勾选数据 </a-button>
       <a-button @click="handleGetCheckData" class="mr-2"> 获取勾选数据 </a-button>
       <a-button @click="handleSetSelectData" class="mr-2"> 设置选中数据 </a-button>
@@ -17,21 +23,18 @@
       <a-button @click="deleteNodeByKey('2-2')" class="mr-2"> 删除parent3节点 </a-button>
       <a-button @click="updateNodeByKey('1-1')" class="mr-2"> 更新parent2节点 </a-button>
     </div>
-    <CollapseContainer title="函数操作" class="mr-4" :canExpan="false" :style="{ width: '33%' }">
-      <BasicTree :treeData="treeData" ref="treeRef" :checkable="true" />
-    </CollapseContainer>
+    <BasicTree :treeData="treeData" title="函数操作" ref="treeRef" :checkable="true" />
   </PageWrapper>
 </template>
 <script lang="ts">
   import { defineComponent, ref, unref } from 'vue';
   import { BasicTree, TreeActionType } from '/@/components/Tree/index';
   import { treeData } from './data';
-  import { CollapseContainer } from '/@/components/Container/index';
   import { useMessage } from '/@/hooks/web/useMessage';
   import { PageWrapper } from '/@/components/Page';
 
   export default defineComponent({
-    components: { BasicTree, CollapseContainer, PageWrapper },
+    components: { BasicTree, PageWrapper },
     setup() {
       const treeRef = ref<Nullable<TreeActionType>>(null);
       const { createMessage } = useMessage();
@@ -75,6 +78,14 @@
         createMessage.success(JSON.stringify(keys));
       }
 
+      function checkAll(checkAll: boolean) {
+        getTree().checkAll(checkAll);
+      }
+
+      function expandAll(checkAll: boolean) {
+        getTree().expandAll(checkAll);
+      }
+
       function appendNodeByKey(parentKey: string | null = null) {
         getTree().insertNodeByKey({
           parentKey: parentKey,
@@ -112,6 +123,8 @@
         appendNodeByKey,
         deleteNodeByKey,
         updateNodeByKey,
+        checkAll,
+        expandAll,
       };
     },
   });

+ 24 - 9
src/views/demo/tree/EditTree.vue

@@ -1,13 +1,29 @@
 <template>
   <PageWrapper title="Tree函数操作示例">
     <div class="flex">
-      <CollapseContainer title="右侧操作按钮/自定义图标" class="mr-4" :style="{ width: '33%' }">
-        <BasicTree :treeData="treeData" :actionList="actionList" :renderIcon="createIcon" />
-      </CollapseContainer>
-
-      <CollapseContainer title="右键菜单" class="mr-4" :style="{ width: '33%' }">
-        <BasicTree :treeData="treeData" :beforeRightClick="getRightMenuList" />
-      </CollapseContainer>
+      <BasicTree
+        class="w-1/3"
+        title="右侧操作按钮/自定义图标"
+        helpMessage="帮助信息"
+        :treeData="treeData"
+        :actionList="actionList"
+        :renderIcon="createIcon"
+      />
+      <BasicTree
+        class="w-1/3 mx-4"
+        title="右键菜单"
+        :treeData="treeData"
+        :beforeRightClick="getRightMenuList"
+      />
+      <BasicTree
+        class="w-1/3"
+        title="工具栏使用"
+        toolbar
+        checkable
+        search
+        :treeData="treeData"
+        :beforeRightClick="getRightMenuList"
+      />
     </div>
   </PageWrapper>
 </template>
@@ -15,12 +31,11 @@
   import { defineComponent, h } from 'vue';
   import { BasicTree, ActionItem, ContextMenuItem } from '/@/components/Tree/index';
   import { treeData } from './data';
-  import { CollapseContainer } from '/@/components/Container/index';
   import { PlusOutlined, DeleteOutlined } from '@ant-design/icons-vue';
   import { PageWrapper } from '/@/components/Page';
 
   export default defineComponent({
-    components: { BasicTree, CollapseContainer, PageWrapper },
+    components: { BasicTree, PageWrapper },
     setup() {
       function handlePlus(node: any) {
         console.log(node);

+ 11 - 16
src/views/demo/tree/index.vue

@@ -1,22 +1,18 @@
 <template>
   <PageWrapper title="Tree基础示例">
     <div class="flex">
-      <CollapseContainer title="基础示例" :style="{ width: '33%' }" class="mr-4">
-        <BasicTree :treeData="treeData" />
-      </CollapseContainer>
+      <BasicTree :treeData="treeData" title="基础示例" class="w-1/3" />
 
-      <CollapseContainer title="可勾选" class="mr-4" :style="{ width: '33%' }">
-        <BasicTree :treeData="treeData" :checkable="true" />
-      </CollapseContainer>
+      <BasicTree :treeData="treeData" title="可勾选" :checkable="true" class="w-1/3 mx-4" />
 
-      <CollapseContainer title="默认展开/勾选示例" :style="{ width: '33%' }">
-        <BasicTree
-          :treeData="treeData"
-          :checkable="true"
-          :expandedKeys="['0-0']"
-          :checkedKeys="['0-0']"
-        />
-      </CollapseContainer>
+      <BasicTree
+        title="默认展开/勾选示例"
+        :treeData="treeData"
+        :checkable="true"
+        :expandedKeys="['0-0']"
+        :checkedKeys="['0-0']"
+        class="w-1/3"
+      />
     </div>
   </PageWrapper>
 </template>
@@ -24,11 +20,10 @@
   import { defineComponent } from 'vue';
   import { BasicTree } from '/@/components/Tree/index';
   import { treeData } from './data';
-  import { CollapseContainer } from '/@/components/Container/index';
   import { PageWrapper } from '/@/components/Page';
 
   export default defineComponent({
-    components: { BasicTree, CollapseContainer, PageWrapper },
+    components: { BasicTree, PageWrapper },
     setup() {
       return { treeData };
     },

+ 4 - 4
yarn.lock

@@ -1791,10 +1791,10 @@
     micromatch "^4.0.2"
     windicss "^2.2.3"
 
-"@zxcvbn-ts/core@^0.2.0":
-  version "0.2.0"
-  resolved "https://registry.npmjs.org/@zxcvbn-ts/core/-/core-0.2.0.tgz#ba3af1fed2213464ae12c0ab565798590afe8ef7"
-  integrity sha512-1NVKw2Tz3Iv3NE4RFTTcF2EQlmHfkNi48U0H80ZR/KLt3ANOFsCDp/mxGawdzCnBrf64E121xI49mpZDAACYZw==
+"@zxcvbn-ts/core@^0.3.0":
+  version "0.3.0"
+  resolved "https://registry.npmjs.org/@zxcvbn-ts/core/-/core-0.3.0.tgz#1a021afef29b97a5f8f72458de005fa149628e32"
+  integrity sha512-H1SOAoC7MbccN/CU9ENZHXwvwTwh6aRt88SOkGROAN9nT88o/qDPJ5B5bElRSbjKLfmmO1LqK2K4u2lUxjbQKQ==
 
 JSONStream@^1.0.4:
   version "1.3.5"