提交 e8ccdc7f 编写于 作者: V vben

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

上级 73c8e0c1
......@@ -19,6 +19,10 @@
- 缓存可以配置是否加密,默认生产环境开启 Aes 加密
- 新增标签页拖拽排序
### 🐛 Bug Fixes
- 修复 tree 文本超出挡住操作按钮问题
### 🎫 Chores
- 更新 antdv 到`2.0.0-rc.2`
......
......@@ -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);
......
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>,
......
......@@ -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>
......
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);
......
......@@ -57,8 +57,8 @@
&__content {
/* 滚动槽 */
&::-webkit-scrollbar {
width: 4px;
height: 4px;
width: 5px;
height: 5px;
}
&::-webkit-scrollbar-track {
......
......@@ -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;
}
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) }),
......
......@@ -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;
}
}
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 };
}
......@@ -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 };
}
......@@ -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: [
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册