Selaa lähdekoodia

fix(tree): fix tree style (#99)

vben 4 vuotta sitten
vanhempi
commit
e8ccdc7f34

+ 4 - 0
CHANGELOG.zh_CN.md

@@ -19,6 +19,10 @@
 - 缓存可以配置是否加密,默认生产环境开启 Aes 加密
 - 新增标签页拖拽排序
 
+### 🐛 Bug Fixes
+
+- 修复 tree 文本超出挡住操作按钮问题
+
 ### 🎫 Chores
 
 - 更新 antdv 到`2.0.0-rc.2`

+ 2 - 2
src/components/ContextMenu/src/index.tsx

@@ -8,7 +8,7 @@ import { defineComponent, nextTick, onMounted, computed, ref, unref, onUnmounted
 import Icon from '/@/components/Icon';
 import { Menu, Divider } from 'ant-design-vue';
 
-import { props } from './props';
+import { contextMenuProps } from './props';
 
 const prefixCls = 'context-menu';
 
@@ -24,7 +24,7 @@ const ItemContent: FunctionalComponent<ItemContentProps> = (props) => {
 
 export default defineComponent({
   name: 'ContextMenu',
-  props,
+  props: contextMenuProps,
   setup(props) {
     const wrapRef = ref<ElRef>(null);
     const showRef = ref(false);

+ 1 - 1
src/components/ContextMenu/src/props.ts

@@ -1,7 +1,7 @@
 import type { PropType } from 'vue';
 import type { Axis, ContextMenuItem } from './types';
 import { propTypes } from '/@/utils/propTypes';
-export const props = {
+export const contextMenuProps = {
   width: propTypes.number.def(156),
   customEvent: {
     type: Object as PropType<Event>,

+ 4 - 0
src/components/Menu/src/BasicMenu.tsx

@@ -17,6 +17,7 @@ import {
 import { Menu } from 'ant-design-vue';
 import SearchInput from './SearchInput.vue';
 import MenuContent from './MenuContent';
+// import { ScrollContainer } from '/@/components/Container';
 
 import { MenuModeEnum, MenuTypeEnum } from '/@/enums/menuEnum';
 import { ThemeEnum } from '/@/enums/appEnum';
@@ -272,7 +273,10 @@ export default defineComponent({
             onClick={handleInputClick}
             collapsed={unref(getCollapsed)}
           />
+
+          {/* <section style={unref(getMenuWrapStyle)}> */}
           <section style={unref(getMenuWrapStyle)} class="basic-menu__content">
+            {/* <ScrollContainer>{() => renderMenu()}</ScrollContainer> */}
             {renderMenu()}
           </section>
         </section>

+ 11 - 2
src/components/Menu/src/MenuContent.tsx

@@ -1,8 +1,9 @@
 import type { Menu as MenuType } from '/@/router/types';
-import type { PropType } from 'vue';
+import { computed, PropType, unref } from 'vue';
 
 import { defineComponent } from 'vue';
 import Icon from '/@/components/Icon/index';
+import { useI18n } from '/@/hooks/web/useI18n';
 
 export default defineComponent({
   name: 'MenuContent',
@@ -32,6 +33,13 @@ export default defineComponent({
     },
   },
   setup(props) {
+    const { t } = useI18n();
+
+    const getI18nName = computed(() => {
+      const { name } = props.item;
+
+      return t(name);
+    });
     /**
      * @description: 渲染图标
      */
@@ -61,7 +69,8 @@ export default defineComponent({
         return null;
       }
       const { showTitle } = props;
-      const { name, icon } = props.item;
+      const { icon } = props.item;
+      const name = unref(getI18nName);
       const searchValue = props.searchValue || '';
       const index = name.indexOf(searchValue);
 

+ 2 - 2
src/components/Menu/src/index.less

@@ -57,8 +57,8 @@
   &__content {
     /* 滚动槽 */
     &::-webkit-scrollbar {
-      width: 4px;
-      height: 4px;
+      width: 5px;
+      height: 5px;
     }
 
     &::-webkit-scrollbar-track {

+ 5 - 5
src/components/Scrollbar/src/index.less

@@ -38,12 +38,12 @@
     z-index: 1;
     border-radius: 4px;
     opacity: 0;
-    -webkit-transition: opacity 120ms ease-out;
-    transition: opacity 120ms ease-out;
+    -webkit-transition: opacity 80ms ease;
+    transition: opacity 80ms ease;
 
     &.is-vertical {
       top: 2px;
-      width: 6px;
+      width: 5px;
 
       & > div {
         width: 100%;
@@ -52,7 +52,7 @@
 
     &.is-horizontal {
       left: 2px;
-      height: 6px;
+      height: 5px;
 
       & > div {
         height: 100%;
@@ -65,5 +65,5 @@
 .scrollbar:focus > .scrollbar__bar,
 .scrollbar:hover > .scrollbar__bar {
   opacity: 1;
-  transition: opacity 280ms ease-out;
+  transition: opacity 180ms ease;
 }

+ 56 - 118
src/components/Tree/src/BasicTree.tsx

@@ -1,20 +1,20 @@
-import type { ReplaceFields, TreeItem, Keys, CheckKeys, InsertNodeParams } from './types';
+import './index.less';
+
+import type { ReplaceFields, TreeItem, Keys, CheckKeys } from './types';
 
-import { defineComponent, reactive, computed, unref, ref, watchEffect } from 'vue';
+import { defineComponent, reactive, computed, unref, ref, watchEffect, CSSProperties } from 'vue';
 import { Tree } from 'ant-design-vue';
 import { DownOutlined } from '@ant-design/icons-vue';
 
 import { useContextMenu, ContextMenuItem } from '/@/hooks/web/useContextMenu';
 
 import { isFunction } from '/@/utils/is';
-import { omit, cloneDeep } from 'lodash-es';
-import { forEach } from '/@/utils/helper/treeHelper';
+import { omit } from 'lodash-es';
 import { extendSlots } from '/@/utils/helper/tsxHelper';
 import { tryTsxEmit } from '/@/utils/helper/vueHelper';
 
 import { basicProps } from './props';
-
-import './index.less';
+import { useTree } from './useTree';
 
 interface State {
   expandedKeys: Keys;
@@ -49,17 +49,55 @@ export default defineComponent({
       }
     );
 
-    const getTreeData = computed(() => {
-      return unref(treeDataRef);
+    const getContentStyle = computed(
+      (): CSSProperties => {
+        const { actionList } = props;
+        const width = actionList.length * 18;
+        return {
+          width: `calc(100% - ${width}px)`,
+        };
+      }
+    );
+
+    const getBindValues = computed(() => {
+      let propsData = {
+        blockNode: true,
+        ...attrs,
+        ...props,
+        expandedKeys: state.expandedKeys,
+        selectedKeys: state.selectedKeys,
+        checkedKeys: state.checkedKeys,
+        replaceFields: unref(getReplaceFields),
+        'onUpdate:expandedKeys': (v: Keys) => {
+          state.expandedKeys = v;
+          emit('update:expandedKeys', v);
+        },
+        'onUpdate:selectedKeys': (v: Keys) => {
+          state.selectedKeys = v;
+          emit('update:selectedKeys', v);
+        },
+        onCheck: (v: CheckKeys) => {
+          state.checkedKeys = v;
+          emit('update:value', v);
+        },
+        onRightClick: handleRightClick,
+      };
+      propsData = omit(propsData, 'treeData');
+      return propsData;
     });
 
+    const getTreeData = computed((): TreeItem[] => unref(treeDataRef));
+
+    const { deleteNodeByKey, insertNodeByKey, filterByLevel, updateNodeByKey } = useTree(
+      treeDataRef,
+      getReplaceFields
+    );
+
     //  渲染操作按钮
     function renderAction(node: TreeItem) {
       const { actionList } = props;
 
-      if (!actionList || actionList.length === 0) {
-        return;
-      }
+      if (!actionList || actionList.length === 0) return;
 
       return actionList.map((item, index) => {
         return (
@@ -81,12 +119,15 @@ export default defineComponent({
         const propsData = omit(item, 'title');
         const anyItem = item as any;
         return (
-          <Tree.TreeNode {...propsData} key={keyField && anyItem[keyField]}>
+          <Tree.TreeNode {...propsData} key={anyItem?.[keyField]}>
             {{
               title: () => (
                 <span class={`${prefixCls}-title`}>
-                  {titleField && anyItem[titleField]}
-                  {renderAction(item)}
+                  <span class={`${prefixCls}__content`} style={unref(getContentStyle)}>
+                    {' '}
+                    {titleField && anyItem[titleField]}
+                  </span>
+                  <span class={`${prefixCls}__actions`}> {renderAction(item)}</span>
                 </span>
               ),
               default: () => renderTreeNode({ data: childrenField ? anyItem[childrenField] : [] }),
@@ -135,86 +176,6 @@ export default defineComponent({
       return state.checkedKeys;
     }
 
-    // 展开指定级别
-    function filterByLevel(level = 1, list?: TreeItem[], currentLevel = 1) {
-      if (!level) {
-        return [];
-      }
-      const res: (string | number)[] = [];
-      const data = list || props.treeData || [];
-      for (let index = 0; index < data.length; index++) {
-        const item = data[index] as any;
-
-        const { key: keyField, children: childrenField } = unref(getReplaceFields);
-        const key = keyField ? item[keyField] : '';
-        const children = childrenField ? item[childrenField] : [];
-        res.push(key);
-        if (children && children.length && currentLevel < level) {
-          currentLevel += 1;
-          res.push(...filterByLevel(level, children, currentLevel));
-        }
-      }
-      return res as string[] | number[];
-    }
-
-    /**
-     * 添加节点
-     */
-    function insertNodeByKey({ parentKey = null, node, push = 'push' }: InsertNodeParams) {
-      const treeData: any = cloneDeep(unref(treeDataRef));
-      if (!parentKey) {
-        treeData[push](node);
-        treeDataRef.value = treeData;
-        return;
-      }
-      const { key: keyField, children: childrenField } = unref(getReplaceFields);
-      forEach(treeData, (treeItem) => {
-        if (treeItem[keyField] === parentKey) {
-          treeItem[childrenField] = treeItem[childrenField] || [];
-          treeItem[childrenField][push](node);
-        }
-      });
-      treeDataRef.value = treeData;
-    }
-
-    // 删除节点
-    function deleteNodeByKey(key: string, list: TreeItem[]) {
-      if (!key) return;
-      const treeData = list || unref(treeDataRef);
-      const { key: keyField, children: childrenField } = unref(getReplaceFields);
-
-      for (let index = 0; index < treeData.length; index++) {
-        const element: any = treeData[index];
-        const children = element[childrenField];
-
-        if (element[keyField] === key) {
-          treeData.splice(index, 1);
-          break;
-        } else if (children && children.length) {
-          deleteNodeByKey(key, element[childrenField]);
-        }
-      }
-    }
-
-    // 更新节点
-    function updateNodeByKey(key: string, node: TreeItem, list: TreeItem[]) {
-      if (!key) return;
-      const treeData = list || unref(treeDataRef);
-      const { key: keyField, children: childrenField } = unref(getReplaceFields);
-
-      for (let index = 0; index < treeData.length; index++) {
-        const element: any = treeData[index];
-        const children = element[childrenField];
-
-        if (element[keyField] === key) {
-          treeData[index] = { ...treeData[index], ...node };
-          break;
-        } else if (children && children.length) {
-          updateNodeByKey(key, node, element[childrenField]);
-        }
-      }
-    }
-
     watchEffect(() => {
       treeDataRef.value = props.treeData as TreeItem[];
       state.expandedKeys = props.expandedKeys;
@@ -237,31 +198,8 @@ export default defineComponent({
       };
     });
     return () => {
-      let propsData: any = {
-        blockNode: true,
-        ...attrs,
-        ...props,
-        expandedKeys: state.expandedKeys,
-        selectedKeys: state.selectedKeys,
-        checkedKeys: state.checkedKeys,
-        replaceFields: unref(getReplaceFields),
-        'onUpdate:expandedKeys': (v: Keys) => {
-          state.expandedKeys = v;
-          emit('update:expandedKeys', v);
-        },
-        'onUpdate:selectedKeys': (v: Keys) => {
-          state.selectedKeys = v;
-          emit('update:selectedKeys', v);
-        },
-        onCheck: (v: CheckKeys) => {
-          state.checkedKeys = v;
-          emit('update:value', v);
-        },
-        onRightClick: handleRightClick,
-      };
-      propsData = omit(propsData, 'treeData');
       return (
-        <Tree {...propsData} class={prefixCls}>
+        <Tree {...unref(getBindValues)} class={prefixCls}>
           {{
             switcherIcon: () => <DownOutlined />,
             default: () => renderTreeNode({ data: unref(getTreeData) }),

+ 21 - 6
src/components/Tree/src/index.less

@@ -2,19 +2,34 @@
   position: relative;
 
   &-title {
+    position: relative;
     display: inline-block;
     width: 100%;
     padding-right: 10px;
 
-    .basic-tree__action {
-      display: none;
-      float: right;
-    }
-
     &:hover {
       .basic-tree__action {
-        display: inline-block;
+        visibility: visible;
       }
     }
   }
+
+  &__content {
+    display: inline-block;
+    overflow: hidden;
+  }
+
+  &__actions {
+    position: absolute;
+    top: 0;
+    right: 0;
+    display: flex;
+  }
+
+  &__action {
+    margin-left: 4px;
+    // float: right;
+    // display: none;
+    visibility: hidden;
+  }
 }

+ 97 - 0
src/components/Tree/src/useTree.ts

@@ -0,0 +1,97 @@
+import type { InsertNodeParams, ReplaceFields, TreeItem } from './types';
+import type { Ref, ComputedRef } from 'vue';
+
+import { cloneDeep } from 'lodash-es';
+import { unref } from 'vue';
+import { forEach } from '/@/utils/helper/treeHelper';
+
+export function useTree(
+  treeDataRef: Ref<TreeItem[]>,
+  getReplaceFields: ComputedRef<ReplaceFields>
+) {
+  // 更新节点
+  function updateNodeByKey(key: string, node: TreeItem, list: TreeItem[]) {
+    if (!key) return;
+    const treeData = list || unref(treeDataRef);
+    const { key: keyField, children: childrenField } = unref(getReplaceFields);
+
+    if (!childrenField || !keyField) return;
+
+    for (let index = 0; index < treeData.length; index++) {
+      const element: any = treeData[index];
+      const children = element[childrenField];
+
+      if (element[keyField] === key) {
+        treeData[index] = { ...treeData[index], ...node };
+        break;
+      } else if (children && children.length) {
+        updateNodeByKey(key, node, element[childrenField]);
+      }
+    }
+  }
+
+  // 展开指定级别
+  function filterByLevel(level = 1, list?: TreeItem[], currentLevel = 1) {
+    if (!level) {
+      return [];
+    }
+    const res: (string | number)[] = [];
+    const data = list || unref(treeDataRef) || [];
+    for (let index = 0; index < data.length; index++) {
+      const item = data[index] as any;
+
+      const { key: keyField, children: childrenField } = unref(getReplaceFields);
+      const key = keyField ? item[keyField] : '';
+      const children = childrenField ? item[childrenField] : [];
+      res.push(key);
+      if (children && children.length && currentLevel < level) {
+        currentLevel += 1;
+        res.push(...filterByLevel(level, children, currentLevel));
+      }
+    }
+    return res as string[] | number[];
+  }
+
+  /**
+   * 添加节点
+   */
+  function insertNodeByKey({ parentKey = null, node, push = 'push' }: InsertNodeParams) {
+    const treeData: any = cloneDeep(unref(treeDataRef));
+    if (!parentKey) {
+      treeData[push](node);
+      treeDataRef.value = treeData;
+      return;
+    }
+    const { key: keyField, children: childrenField } = unref(getReplaceFields);
+    if (!childrenField || !keyField) return;
+
+    forEach(treeData, (treeItem) => {
+      if (treeItem[keyField] === parentKey) {
+        treeItem[childrenField] = treeItem[childrenField] || [];
+        treeItem[childrenField][push](node);
+      }
+    });
+    treeDataRef.value = treeData;
+  }
+
+  // 删除节点
+  function deleteNodeByKey(key: string, list: TreeItem[]) {
+    if (!key) return;
+    const treeData = list || unref(treeDataRef);
+    const { key: keyField, children: childrenField } = unref(getReplaceFields);
+    if (!childrenField || !keyField) return;
+
+    for (let index = 0; index < treeData.length; index++) {
+      const element: any = treeData[index];
+      const children = element[childrenField];
+
+      if (element[keyField] === key) {
+        treeData.splice(index, 1);
+        break;
+      } else if (children && children.length) {
+        deleteNodeByKey(key, element[childrenField]);
+      }
+    }
+  }
+  return { deleteNodeByKey, insertNodeByKey, filterByLevel, updateNodeByKey };
+}

+ 23 - 21
src/layouts/default/menu/useLayoutMenu.ts

@@ -17,10 +17,10 @@ import {
   getShallowMenus,
 } from '/@/router/menus';
 import { permissionStore } from '/@/store/modules/permission';
-import { useI18n } from '/@/hooks/web/useI18n';
-import { cloneDeep } from 'lodash-es';
+// import { useI18n } from '/@/hooks/web/useI18n';
+// import { cloneDeep } from 'lodash-es';
 
-const { t } = useI18n();
+// const { t } = useI18n();
 export function useSplitMenu(splitType: Ref<MenuSplitTyeEnum>) {
   // Menu array
   const menusRef = ref<Menu[]>([]);
@@ -45,13 +45,13 @@ export function useSplitMenu(splitType: Ref<MenuSplitTyeEnum>) {
     return unref(splitType) === MenuSplitTyeEnum.NONE || !unref(getSplit);
   });
 
-  const getI18nFlatMenus = computed(() => {
-    return setI18nName(flatMenusRef.value, true, false);
-  });
+  // const getI18nFlatMenus = computed(() => {
+  //   return setI18nName(flatMenusRef.value, true, false);
+  // });
 
-  const getI18nMenus = computed(() => {
-    return setI18nName(menusRef.value, true, true);
-  });
+  // const getI18nMenus = computed(() => {
+  //   return setI18nName(menusRef.value, true, true);
+  // });
 
   watch(
     [() => unref(currentRoute).path, () => unref(splitType)],
@@ -83,17 +83,19 @@ export function useSplitMenu(splitType: Ref<MenuSplitTyeEnum>) {
     genMenus();
   });
 
-  function setI18nName(list: Menu[], clone = false, deep = true) {
-    const menus = clone ? cloneDeep(list) : list;
-    menus.forEach((item) => {
-      if (!item.name.includes('.')) return;
-      item.name = t(item.name);
-      if (item.children && deep) {
-        setI18nName(item.children, false, deep);
-      }
-    });
-    return menus;
-  }
+  // function setI18nName(list: Menu[], clone = false, deep = true) {
+  //   const menus = clone ? cloneDeep(list) : list;
+  //   const arr: Menu[] = [];
+  //   menus.forEach((item) => {
+  //     if (!item.name.includes('.')) return;
+  //     item.name = t(item.name);
+
+  //     if (item.children && deep) {
+  //       setI18nName(item.children, false, deep);
+  //     }
+  //   });
+  //   return menus;
+  // }
 
   // Handle left menu split
   async function handleSplitLeftMenu(parentPath: string) {
@@ -133,5 +135,5 @@ export function useSplitMenu(splitType: Ref<MenuSplitTyeEnum>) {
     }
   }
 
-  return { flatMenusRef: getI18nFlatMenus, menusRef: getI18nMenus };
+  return { flatMenusRef, menusRef };
 }

+ 1 - 1
src/views/demo/tree/data.ts

@@ -2,7 +2,7 @@ import { TreeItem } from '/@/components/Tree/index';
 
 export const treeData: TreeItem[] = [
   {
-    title: 'parent 1',
+    title: 'parent 1parent ',
     key: '0-0',
     icon: 'home|svg',
     children: [