Commit e8ccdc7f34891ea31768aea9ebcfc33227d37eb7

Authored by vben
1 parent 73c8e0c1

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

CHANGELOG.zh_CN.md
... ... @@ -19,6 +19,10 @@
19 19 - 缓存可以配置是否加密,默认生产环境开启 Aes 加密
20 20 - 新增标签页拖拽排序
21 21  
  22 +### 🐛 Bug Fixes
  23 +
  24 +- 修复 tree 文本超出挡住操作按钮问题
  25 +
22 26 ### 🎫 Chores
23 27  
24 28 - 更新 antdv 到`2.0.0-rc.2`
... ...
src/components/ContextMenu/src/index.tsx
... ... @@ -8,7 +8,7 @@ import { defineComponent, nextTick, onMounted, computed, ref, unref, onUnmounted
8 8 import Icon from '/@/components/Icon';
9 9 import { Menu, Divider } from 'ant-design-vue';
10 10  
11   -import { props } from './props';
  11 +import { contextMenuProps } from './props';
12 12  
13 13 const prefixCls = 'context-menu';
14 14  
... ... @@ -24,7 +24,7 @@ const ItemContent: FunctionalComponent<ItemContentProps> = (props) => {
24 24  
25 25 export default defineComponent({
26 26 name: 'ContextMenu',
27   - props,
  27 + props: contextMenuProps,
28 28 setup(props) {
29 29 const wrapRef = ref<ElRef>(null);
30 30 const showRef = ref(false);
... ...
src/components/ContextMenu/src/props.ts
1 1 import type { PropType } from 'vue';
2 2 import type { Axis, ContextMenuItem } from './types';
3 3 import { propTypes } from '/@/utils/propTypes';
4   -export const props = {
  4 +export const contextMenuProps = {
5 5 width: propTypes.number.def(156),
6 6 customEvent: {
7 7 type: Object as PropType<Event>,
... ...
src/components/Menu/src/BasicMenu.tsx
... ... @@ -17,6 +17,7 @@ import {
17 17 import { Menu } from 'ant-design-vue';
18 18 import SearchInput from './SearchInput.vue';
19 19 import MenuContent from './MenuContent';
  20 +// import { ScrollContainer } from '/@/components/Container';
20 21  
21 22 import { MenuModeEnum, MenuTypeEnum } from '/@/enums/menuEnum';
22 23 import { ThemeEnum } from '/@/enums/appEnum';
... ... @@ -272,7 +273,10 @@ export default defineComponent({
272 273 onClick={handleInputClick}
273 274 collapsed={unref(getCollapsed)}
274 275 />
  276 +
  277 + {/* <section style={unref(getMenuWrapStyle)}> */}
275 278 <section style={unref(getMenuWrapStyle)} class="basic-menu__content">
  279 + {/* <ScrollContainer>{() => renderMenu()}</ScrollContainer> */}
276 280 {renderMenu()}
277 281 </section>
278 282 </section>
... ...
src/components/Menu/src/MenuContent.tsx
1 1 import type { Menu as MenuType } from '/@/router/types';
2   -import type { PropType } from 'vue';
  2 +import { computed, PropType, unref } from 'vue';
3 3  
4 4 import { defineComponent } from 'vue';
5 5 import Icon from '/@/components/Icon/index';
  6 +import { useI18n } from '/@/hooks/web/useI18n';
6 7  
7 8 export default defineComponent({
8 9 name: 'MenuContent',
... ... @@ -32,6 +33,13 @@ export default defineComponent({
32 33 },
33 34 },
34 35 setup(props) {
  36 + const { t } = useI18n();
  37 +
  38 + const getI18nName = computed(() => {
  39 + const { name } = props.item;
  40 +
  41 + return t(name);
  42 + });
35 43 /**
36 44 * @description: 渲染图标
37 45 */
... ... @@ -61,7 +69,8 @@ export default defineComponent({
61 69 return null;
62 70 }
63 71 const { showTitle } = props;
64   - const { name, icon } = props.item;
  72 + const { icon } = props.item;
  73 + const name = unref(getI18nName);
65 74 const searchValue = props.searchValue || '';
66 75 const index = name.indexOf(searchValue);
67 76  
... ...
src/components/Menu/src/index.less
... ... @@ -57,8 +57,8 @@
57 57 &__content {
58 58 /* 滚动槽 */
59 59 &::-webkit-scrollbar {
60   - width: 4px;
61   - height: 4px;
  60 + width: 5px;
  61 + height: 5px;
62 62 }
63 63  
64 64 &::-webkit-scrollbar-track {
... ...
src/components/Scrollbar/src/index.less
... ... @@ -38,12 +38,12 @@
38 38 z-index: 1;
39 39 border-radius: 4px;
40 40 opacity: 0;
41   - -webkit-transition: opacity 120ms ease-out;
42   - transition: opacity 120ms ease-out;
  41 + -webkit-transition: opacity 80ms ease;
  42 + transition: opacity 80ms ease;
43 43  
44 44 &.is-vertical {
45 45 top: 2px;
46   - width: 6px;
  46 + width: 5px;
47 47  
48 48 & > div {
49 49 width: 100%;
... ... @@ -52,7 +52,7 @@
52 52  
53 53 &.is-horizontal {
54 54 left: 2px;
55   - height: 6px;
  55 + height: 5px;
56 56  
57 57 & > div {
58 58 height: 100%;
... ... @@ -65,5 +65,5 @@
65 65 .scrollbar:focus > .scrollbar__bar,
66 66 .scrollbar:hover > .scrollbar__bar {
67 67 opacity: 1;
68   - transition: opacity 280ms ease-out;
  68 + transition: opacity 180ms ease;
69 69 }
... ...
src/components/Tree/src/BasicTree.tsx
1   -import type { ReplaceFields, TreeItem, Keys, CheckKeys, InsertNodeParams } from './types';
  1 +import './index.less';
  2 +
  3 +import type { ReplaceFields, TreeItem, Keys, CheckKeys } from './types';
2 4  
3   -import { defineComponent, reactive, computed, unref, ref, watchEffect } from 'vue';
  5 +import { defineComponent, reactive, computed, unref, ref, watchEffect, CSSProperties } from 'vue';
4 6 import { Tree } from 'ant-design-vue';
5 7 import { DownOutlined } from '@ant-design/icons-vue';
6 8  
7 9 import { useContextMenu, ContextMenuItem } from '/@/hooks/web/useContextMenu';
8 10  
9 11 import { isFunction } from '/@/utils/is';
10   -import { omit, cloneDeep } from 'lodash-es';
11   -import { forEach } from '/@/utils/helper/treeHelper';
  12 +import { omit } from 'lodash-es';
12 13 import { extendSlots } from '/@/utils/helper/tsxHelper';
13 14 import { tryTsxEmit } from '/@/utils/helper/vueHelper';
14 15  
15 16 import { basicProps } from './props';
16   -
17   -import './index.less';
  17 +import { useTree } from './useTree';
18 18  
19 19 interface State {
20 20 expandedKeys: Keys;
... ... @@ -49,17 +49,55 @@ export default defineComponent({
49 49 }
50 50 );
51 51  
52   - const getTreeData = computed(() => {
53   - return unref(treeDataRef);
  52 + const getContentStyle = computed(
  53 + (): CSSProperties => {
  54 + const { actionList } = props;
  55 + const width = actionList.length * 18;
  56 + return {
  57 + width: `calc(100% - ${width}px)`,
  58 + };
  59 + }
  60 + );
  61 +
  62 + const getBindValues = computed(() => {
  63 + let propsData = {
  64 + blockNode: true,
  65 + ...attrs,
  66 + ...props,
  67 + expandedKeys: state.expandedKeys,
  68 + selectedKeys: state.selectedKeys,
  69 + checkedKeys: state.checkedKeys,
  70 + replaceFields: unref(getReplaceFields),
  71 + 'onUpdate:expandedKeys': (v: Keys) => {
  72 + state.expandedKeys = v;
  73 + emit('update:expandedKeys', v);
  74 + },
  75 + 'onUpdate:selectedKeys': (v: Keys) => {
  76 + state.selectedKeys = v;
  77 + emit('update:selectedKeys', v);
  78 + },
  79 + onCheck: (v: CheckKeys) => {
  80 + state.checkedKeys = v;
  81 + emit('update:value', v);
  82 + },
  83 + onRightClick: handleRightClick,
  84 + };
  85 + propsData = omit(propsData, 'treeData');
  86 + return propsData;
54 87 });
55 88  
  89 + const getTreeData = computed((): TreeItem[] => unref(treeDataRef));
  90 +
  91 + const { deleteNodeByKey, insertNodeByKey, filterByLevel, updateNodeByKey } = useTree(
  92 + treeDataRef,
  93 + getReplaceFields
  94 + );
  95 +
56 96 // 渲染操作按钮
57 97 function renderAction(node: TreeItem) {
58 98 const { actionList } = props;
59 99  
60   - if (!actionList || actionList.length === 0) {
61   - return;
62   - }
  100 + if (!actionList || actionList.length === 0) return;
63 101  
64 102 return actionList.map((item, index) => {
65 103 return (
... ... @@ -81,12 +119,15 @@ export default defineComponent({
81 119 const propsData = omit(item, 'title');
82 120 const anyItem = item as any;
83 121 return (
84   - <Tree.TreeNode {...propsData} key={keyField && anyItem[keyField]}>
  122 + <Tree.TreeNode {...propsData} key={anyItem?.[keyField]}>
85 123 {{
86 124 title: () => (
87 125 <span class={`${prefixCls}-title`}>
88   - {titleField && anyItem[titleField]}
89   - {renderAction(item)}
  126 + <span class={`${prefixCls}__content`} style={unref(getContentStyle)}>
  127 + {' '}
  128 + {titleField && anyItem[titleField]}
  129 + </span>
  130 + <span class={`${prefixCls}__actions`}> {renderAction(item)}</span>
90 131 </span>
91 132 ),
92 133 default: () => renderTreeNode({ data: childrenField ? anyItem[childrenField] : [] }),
... ... @@ -135,86 +176,6 @@ export default defineComponent({
135 176 return state.checkedKeys;
136 177 }
137 178  
138   - // 展开指定级别
139   - function filterByLevel(level = 1, list?: TreeItem[], currentLevel = 1) {
140   - if (!level) {
141   - return [];
142   - }
143   - const res: (string | number)[] = [];
144   - const data = list || props.treeData || [];
145   - for (let index = 0; index < data.length; index++) {
146   - const item = data[index] as any;
147   -
148   - const { key: keyField, children: childrenField } = unref(getReplaceFields);
149   - const key = keyField ? item[keyField] : '';
150   - const children = childrenField ? item[childrenField] : [];
151   - res.push(key);
152   - if (children && children.length && currentLevel < level) {
153   - currentLevel += 1;
154   - res.push(...filterByLevel(level, children, currentLevel));
155   - }
156   - }
157   - return res as string[] | number[];
158   - }
159   -
160   - /**
161   - * 添加节点
162   - */
163   - function insertNodeByKey({ parentKey = null, node, push = 'push' }: InsertNodeParams) {
164   - const treeData: any = cloneDeep(unref(treeDataRef));
165   - if (!parentKey) {
166   - treeData[push](node);
167   - treeDataRef.value = treeData;
168   - return;
169   - }
170   - const { key: keyField, children: childrenField } = unref(getReplaceFields);
171   - forEach(treeData, (treeItem) => {
172   - if (treeItem[keyField] === parentKey) {
173   - treeItem[childrenField] = treeItem[childrenField] || [];
174   - treeItem[childrenField][push](node);
175   - }
176   - });
177   - treeDataRef.value = treeData;
178   - }
179   -
180   - // 删除节点
181   - function deleteNodeByKey(key: string, list: TreeItem[]) {
182   - if (!key) return;
183   - const treeData = list || unref(treeDataRef);
184   - const { key: keyField, children: childrenField } = unref(getReplaceFields);
185   -
186   - for (let index = 0; index < treeData.length; index++) {
187   - const element: any = treeData[index];
188   - const children = element[childrenField];
189   -
190   - if (element[keyField] === key) {
191   - treeData.splice(index, 1);
192   - break;
193   - } else if (children && children.length) {
194   - deleteNodeByKey(key, element[childrenField]);
195   - }
196   - }
197   - }
198   -
199   - // 更新节点
200   - function updateNodeByKey(key: string, node: TreeItem, list: TreeItem[]) {
201   - if (!key) return;
202   - const treeData = list || unref(treeDataRef);
203   - const { key: keyField, children: childrenField } = unref(getReplaceFields);
204   -
205   - for (let index = 0; index < treeData.length; index++) {
206   - const element: any = treeData[index];
207   - const children = element[childrenField];
208   -
209   - if (element[keyField] === key) {
210   - treeData[index] = { ...treeData[index], ...node };
211   - break;
212   - } else if (children && children.length) {
213   - updateNodeByKey(key, node, element[childrenField]);
214   - }
215   - }
216   - }
217   -
218 179 watchEffect(() => {
219 180 treeDataRef.value = props.treeData as TreeItem[];
220 181 state.expandedKeys = props.expandedKeys;
... ... @@ -237,31 +198,8 @@ export default defineComponent({
237 198 };
238 199 });
239 200 return () => {
240   - let propsData: any = {
241   - blockNode: true,
242   - ...attrs,
243   - ...props,
244   - expandedKeys: state.expandedKeys,
245   - selectedKeys: state.selectedKeys,
246   - checkedKeys: state.checkedKeys,
247   - replaceFields: unref(getReplaceFields),
248   - 'onUpdate:expandedKeys': (v: Keys) => {
249   - state.expandedKeys = v;
250   - emit('update:expandedKeys', v);
251   - },
252   - 'onUpdate:selectedKeys': (v: Keys) => {
253   - state.selectedKeys = v;
254   - emit('update:selectedKeys', v);
255   - },
256   - onCheck: (v: CheckKeys) => {
257   - state.checkedKeys = v;
258   - emit('update:value', v);
259   - },
260   - onRightClick: handleRightClick,
261   - };
262   - propsData = omit(propsData, 'treeData');
263 201 return (
264   - <Tree {...propsData} class={prefixCls}>
  202 + <Tree {...unref(getBindValues)} class={prefixCls}>
265 203 {{
266 204 switcherIcon: () => <DownOutlined />,
267 205 default: () => renderTreeNode({ data: unref(getTreeData) }),
... ...
src/components/Tree/src/index.less
... ... @@ -2,19 +2,34 @@
2 2 position: relative;
3 3  
4 4 &-title {
  5 + position: relative;
5 6 display: inline-block;
6 7 width: 100%;
7 8 padding-right: 10px;
8 9  
9   - .basic-tree__action {
10   - display: none;
11   - float: right;
12   - }
13   -
14 10 &:hover {
15 11 .basic-tree__action {
16   - display: inline-block;
  12 + visibility: visible;
17 13 }
18 14 }
19 15 }
  16 +
  17 + &__content {
  18 + display: inline-block;
  19 + overflow: hidden;
  20 + }
  21 +
  22 + &__actions {
  23 + position: absolute;
  24 + top: 0;
  25 + right: 0;
  26 + display: flex;
  27 + }
  28 +
  29 + &__action {
  30 + margin-left: 4px;
  31 + // float: right;
  32 + // display: none;
  33 + visibility: hidden;
  34 + }
20 35 }
... ...
src/components/Tree/src/useTree.ts 0 → 100644
  1 +import type { InsertNodeParams, ReplaceFields, TreeItem } from './types';
  2 +import type { Ref, ComputedRef } from 'vue';
  3 +
  4 +import { cloneDeep } from 'lodash-es';
  5 +import { unref } from 'vue';
  6 +import { forEach } from '/@/utils/helper/treeHelper';
  7 +
  8 +export function useTree(
  9 + treeDataRef: Ref<TreeItem[]>,
  10 + getReplaceFields: ComputedRef<ReplaceFields>
  11 +) {
  12 + // 更新节点
  13 + function updateNodeByKey(key: string, node: TreeItem, list: TreeItem[]) {
  14 + if (!key) return;
  15 + const treeData = list || unref(treeDataRef);
  16 + const { key: keyField, children: childrenField } = unref(getReplaceFields);
  17 +
  18 + if (!childrenField || !keyField) return;
  19 +
  20 + for (let index = 0; index < treeData.length; index++) {
  21 + const element: any = treeData[index];
  22 + const children = element[childrenField];
  23 +
  24 + if (element[keyField] === key) {
  25 + treeData[index] = { ...treeData[index], ...node };
  26 + break;
  27 + } else if (children && children.length) {
  28 + updateNodeByKey(key, node, element[childrenField]);
  29 + }
  30 + }
  31 + }
  32 +
  33 + // 展开指定级别
  34 + function filterByLevel(level = 1, list?: TreeItem[], currentLevel = 1) {
  35 + if (!level) {
  36 + return [];
  37 + }
  38 + const res: (string | number)[] = [];
  39 + const data = list || unref(treeDataRef) || [];
  40 + for (let index = 0; index < data.length; index++) {
  41 + const item = data[index] as any;
  42 +
  43 + const { key: keyField, children: childrenField } = unref(getReplaceFields);
  44 + const key = keyField ? item[keyField] : '';
  45 + const children = childrenField ? item[childrenField] : [];
  46 + res.push(key);
  47 + if (children && children.length && currentLevel < level) {
  48 + currentLevel += 1;
  49 + res.push(...filterByLevel(level, children, currentLevel));
  50 + }
  51 + }
  52 + return res as string[] | number[];
  53 + }
  54 +
  55 + /**
  56 + * 添加节点
  57 + */
  58 + function insertNodeByKey({ parentKey = null, node, push = 'push' }: InsertNodeParams) {
  59 + const treeData: any = cloneDeep(unref(treeDataRef));
  60 + if (!parentKey) {
  61 + treeData[push](node);
  62 + treeDataRef.value = treeData;
  63 + return;
  64 + }
  65 + const { key: keyField, children: childrenField } = unref(getReplaceFields);
  66 + if (!childrenField || !keyField) return;
  67 +
  68 + forEach(treeData, (treeItem) => {
  69 + if (treeItem[keyField] === parentKey) {
  70 + treeItem[childrenField] = treeItem[childrenField] || [];
  71 + treeItem[childrenField][push](node);
  72 + }
  73 + });
  74 + treeDataRef.value = treeData;
  75 + }
  76 +
  77 + // 删除节点
  78 + function deleteNodeByKey(key: string, list: TreeItem[]) {
  79 + if (!key) return;
  80 + const treeData = list || unref(treeDataRef);
  81 + const { key: keyField, children: childrenField } = unref(getReplaceFields);
  82 + if (!childrenField || !keyField) return;
  83 +
  84 + for (let index = 0; index < treeData.length; index++) {
  85 + const element: any = treeData[index];
  86 + const children = element[childrenField];
  87 +
  88 + if (element[keyField] === key) {
  89 + treeData.splice(index, 1);
  90 + break;
  91 + } else if (children && children.length) {
  92 + deleteNodeByKey(key, element[childrenField]);
  93 + }
  94 + }
  95 + }
  96 + return { deleteNodeByKey, insertNodeByKey, filterByLevel, updateNodeByKey };
  97 +}
... ...
src/layouts/default/menu/useLayoutMenu.ts
... ... @@ -17,10 +17,10 @@ import {
17 17 getShallowMenus,
18 18 } from '/@/router/menus';
19 19 import { permissionStore } from '/@/store/modules/permission';
20   -import { useI18n } from '/@/hooks/web/useI18n';
21   -import { cloneDeep } from 'lodash-es';
  20 +// import { useI18n } from '/@/hooks/web/useI18n';
  21 +// import { cloneDeep } from 'lodash-es';
22 22  
23   -const { t } = useI18n();
  23 +// const { t } = useI18n();
24 24 export function useSplitMenu(splitType: Ref<MenuSplitTyeEnum>) {
25 25 // Menu array
26 26 const menusRef = ref<Menu[]>([]);
... ... @@ -45,13 +45,13 @@ export function useSplitMenu(splitType: Ref&lt;MenuSplitTyeEnum&gt;) {
45 45 return unref(splitType) === MenuSplitTyeEnum.NONE || !unref(getSplit);
46 46 });
47 47  
48   - const getI18nFlatMenus = computed(() => {
49   - return setI18nName(flatMenusRef.value, true, false);
50   - });
  48 + // const getI18nFlatMenus = computed(() => {
  49 + // return setI18nName(flatMenusRef.value, true, false);
  50 + // });
51 51  
52   - const getI18nMenus = computed(() => {
53   - return setI18nName(menusRef.value, true, true);
54   - });
  52 + // const getI18nMenus = computed(() => {
  53 + // return setI18nName(menusRef.value, true, true);
  54 + // });
55 55  
56 56 watch(
57 57 [() => unref(currentRoute).path, () => unref(splitType)],
... ... @@ -83,17 +83,19 @@ export function useSplitMenu(splitType: Ref&lt;MenuSplitTyeEnum&gt;) {
83 83 genMenus();
84 84 });
85 85  
86   - function setI18nName(list: Menu[], clone = false, deep = true) {
87   - const menus = clone ? cloneDeep(list) : list;
88   - menus.forEach((item) => {
89   - if (!item.name.includes('.')) return;
90   - item.name = t(item.name);
91   - if (item.children && deep) {
92   - setI18nName(item.children, false, deep);
93   - }
94   - });
95   - return menus;
96   - }
  86 + // function setI18nName(list: Menu[], clone = false, deep = true) {
  87 + // const menus = clone ? cloneDeep(list) : list;
  88 + // const arr: Menu[] = [];
  89 + // menus.forEach((item) => {
  90 + // if (!item.name.includes('.')) return;
  91 + // item.name = t(item.name);
  92 +
  93 + // if (item.children && deep) {
  94 + // setI18nName(item.children, false, deep);
  95 + // }
  96 + // });
  97 + // return menus;
  98 + // }
97 99  
98 100 // Handle left menu split
99 101 async function handleSplitLeftMenu(parentPath: string) {
... ... @@ -133,5 +135,5 @@ export function useSplitMenu(splitType: Ref&lt;MenuSplitTyeEnum&gt;) {
133 135 }
134 136 }
135 137  
136   - return { flatMenusRef: getI18nFlatMenus, menusRef: getI18nMenus };
  138 + return { flatMenusRef, menusRef };
137 139 }
... ...
src/views/demo/tree/data.ts
... ... @@ -2,7 +2,7 @@ import { TreeItem } from &#39;/@/components/Tree/index&#39;;
2 2  
3 3 export const treeData: TreeItem[] = [
4 4 {
5   - title: 'parent 1',
  5 + title: 'parent 1parent ',
6 6 key: '0-0',
7 7 icon: 'home|svg',
8 8 children: [
... ...