Skip to content

Commit 1c5cdfd

Browse files
committed
feat: 禁止折叠,高亮,禁止选择
1 parent 74489d8 commit 1c5cdfd

File tree

6 files changed

+178
-33
lines changed

6 files changed

+178
-33
lines changed
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
import { ref, Ref } from 'vue'
2+
3+
interface TypeHighlightClass {
4+
[key: string]: 'active' | '' | 'devui-tree_isDisabledNode'
5+
}
6+
type TypeUseHighlightNode = () => {
7+
nodeClassNameReflect: Ref<TypeHighlightClass>
8+
handleClickOnNode: (index: string) => void
9+
handleInitNodeClassNameReflect: (isDisabled: boolean, ...keys: Array<string>) => string
10+
}
11+
12+
const HIGHLIGHT_CLASS = 'active'
13+
const IS_DISABLED_FLAG = 'devui-tree_isDisabledNode'
14+
const useHighlightNode: TypeUseHighlightNode = () => {
15+
const nodeClassNameReflectRef = ref<TypeHighlightClass>({})
16+
const handleInit = (isDisabled = false, ...keys) => {
17+
const key = keys.join('-')
18+
nodeClassNameReflectRef.value[key] = isDisabled ? IS_DISABLED_FLAG : (nodeClassNameReflectRef.value[key] || '')
19+
return key
20+
}
21+
const handleClick = (key) => {
22+
if (nodeClassNameReflectRef.value[key] === IS_DISABLED_FLAG) {
23+
return
24+
}
25+
nodeClassNameReflectRef.value =
26+
Object.fromEntries(
27+
Object
28+
.entries(nodeClassNameReflectRef.value)
29+
.map(([k]) => [k, k === key ? HIGHLIGHT_CLASS : ''])
30+
)
31+
}
32+
return {
33+
nodeClassNameReflect: nodeClassNameReflectRef,
34+
handleClickOnNode: handleClick,
35+
handleInitNodeClassNameReflect: handleInit,
36+
}
37+
}
38+
export default useHighlightNode
Original file line numberDiff line numberDiff line change
@@ -1,25 +1,31 @@
1-
import { ref } from 'vue'
2-
import { TreeData, TreeItem } from '../tree-types'
1+
import { ref } from "vue";
2+
import { TreeData, TreeItem } from "../tree-types";
33

44
export default function useToggle(data: TreeData): any {
55
const openedTree = (tree: any) => {
6-
return tree.reduce((acc: TreeItem, item: TreeItem) => (
7-
item.open
8-
? acc.concat(item, openedTree(item.children))
9-
: acc.concat(item)
10-
), [])
11-
}
6+
return tree.reduce(
7+
(acc: TreeItem, item: TreeItem) =>
8+
item.open
9+
? acc.concat(item, openedTree(item.children))
10+
: acc.concat(item),
11+
[]
12+
);
13+
};
1214

13-
const openedData = ref(openedTree(data)) // 响应式对象
15+
const openedData = ref(openedTree(data)); // 响应式对象
1416

17+
// 触发打开状态
1518
const toggle = (item: TreeItem) => {
16-
if (!item.children) return
17-
item.open = !item.open
18-
openedData.value = openedTree(data)
19-
}
19+
if (!item.children) return;
20+
// 如果设置禁止打开则跳过
21+
if (item.disableToggle) return;
22+
item.open = !item.open;
23+
24+
openedData.value = openedTree(data);
25+
};
2026

2127
return {
2228
openedData,
2329
toggle,
24-
}
30+
};
2531
}
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
1-
import type { PropType, ExtractPropTypes } from 'vue'
1+
import type { PropType, ExtractPropTypes } from "vue";
22

33
export interface TreeItem {
4-
label: string
5-
children: TreeData
6-
[key: string]: any
4+
label: string;
5+
children?: TreeData;
6+
disableToggle?: boolean;
7+
disabled?: boolean;
8+
[key: string]: any;
79
}
810

911
export type TreeData = Array<TreeItem>;
@@ -12,8 +14,7 @@ export const treeProps = {
1214
data: {
1315
type: Array as PropType<TreeData>,
1416
default: () => [],
15-
}
16-
} as const
17-
18-
export type TreeProps = ExtractPropTypes<typeof treeProps>
17+
},
18+
} as const;
1919

20+
export type TreeProps = ExtractPropTypes<typeof treeProps>;
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
1+
$devui-text-weak: var(--devui-text-weak, #575d6c);
2+
$devui-font-size: var(--devui-font-size, 12px);
3+
$devui-list-item-selected-bg: var(--devui-list-item-selected-bg, #e9edfa);
4+
$devui-list-item-hover-bg: var(--devui-list-item-hover-bg, #f2f5fc);
5+
$devui-border-radius: var(--devui-border-radius, 2px);
6+
$devui-animation-duration-fast: var(--devui-animation-duration-fast, 100ms);
7+
$devui-animation-ease-in-smooth: var(
8+
--devui-animation-ease-in-smooth,
9+
cubic-bezier(0.645, 0.045, 0.355, 1)
10+
);
11+
$devui-disabled-text: var(--devui-disabled-text, #adb0b8);
12+
13+
.devui-text-ellipsis {
14+
text-overflow: ellipsis;
15+
overflow: hidden;
16+
white-space: nowrap;
17+
}
18+
19+
.devui-tree-node {
20+
color: $devui-text-weak;
21+
line-height: 1.5;
22+
white-space: nowrap;
23+
position: relative;
24+
25+
.devui-tree-node__content {
26+
display: inline-flex;
27+
align-items: center;
28+
font-size: $devui-font-size;
29+
padding-right: 10px;
30+
width: 100%;
31+
border-radius: $devui-border-radius;
32+
padding-left: 6px;
33+
transition: color $devui-animation-duration-fast
34+
$devui-animation-ease-in-smooth,
35+
background-color $devui-animation-duration-fast
36+
$devui-animation-ease-in-smooth;
37+
38+
&.active {
39+
background-color: $devui-list-item-selected-bg;
40+
text-decoration: none;
41+
border-color: transparent;
42+
}
43+
44+
&:not(.active):hover {
45+
background-color: $devui-list-item-hover-bg;
46+
}
47+
}
48+
49+
.devui-tree-node__content--value-wrapper {
50+
display: inline-flex;
51+
align-items: center;
52+
height: 30px;
53+
width: 100%;
54+
55+
.toggle-disabled {
56+
cursor: not-allowed;
57+
58+
svg.svg-icon rect {
59+
stroke: $devui-disabled-text;
60+
}
61+
62+
svg.svg-icon path {
63+
fill: $devui-disabled-text;
64+
}
65+
}
66+
}
67+
68+
.devui-tree-node__title {
69+
@extend .devui-text-ellipsis;
70+
71+
margin-left: 5px;
72+
display: inline-block;
73+
border: 1px dashed transparent;
74+
border-radius: $devui-border-radius;
75+
max-width: 100%;
76+
77+
&:not(.disabled) {
78+
cursor: pointer;
79+
}
80+
}
81+
}

packages/devui-vue/devui/tree/src/tree.tsx

+28-11
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import IconOpen from './components/icon-open'
44
import IconClose from './components/icon-close'
55
import useToggle from './composables/use-toggle'
66
import './tree.scss'
7+
import useHighlightNode from './composables/use-highlight'
78

89
export default defineComponent({
910
name: 'DTree',
@@ -12,28 +13,44 @@ export default defineComponent({
1213
setup(props: TreeProps, ctx) {
1314
const { data } = toRefs(props)
1415
const { openedData, toggle } = useToggle(data.value)
16+
const { nodeClassNameReflect, handleInitNodeClassNameReflect, handleClickOnNode } = useHighlightNode()
1517

1618
// 增加缩进的展位元素
1719
const Indent = () => {
1820
return <span style="display: inline-block; width: 16px; height: 16px;"></span>
1921
}
2022

23+
const renderIcon = (item: TreeItem) => {
24+
return item.children
25+
? <span class={item.disableToggle && 'toggle-disabled'}>
26+
{
27+
item.open
28+
? <IconOpen class='mr-xs' onClick={() => toggle(item)} />
29+
: <IconClose class='mr-xs' onClick={() => toggle(item)} />
30+
}
31+
</span>
32+
: <Indent />
33+
}
34+
2135
const renderNode = (item: TreeItem) => {
36+
const { key = '', label, disabled, open, level } = item
37+
const nodeId = handleInitNodeClassNameReflect(disabled, key, label) // 获取nodeId
38+
2239
return (
2340
<div
24-
class={['devui-tree-node', item.open && 'devui-tree-node__open']}
25-
style={{ paddingLeft: `${24 * (item.level - 1)}px` }}
41+
class={['devui-tree-node', open && 'devui-tree-node__open']}
42+
style={{ paddingLeft: `${24 * (level - 1)}px` }}
2643
>
27-
<div class="devui-tree-node__content">
44+
<div class={['devui-tree-node__content', nodeClassNameReflect.value[nodeId]]}
45+
onClick={() => handleClickOnNode(nodeId)}>
2846
<div class="devui-tree-node__content--value-wrapper">
29-
{
30-
item.children
31-
? item.open
32-
? <IconOpen class="mr-xs" onClick={() => toggle(item)} /> // 给节点绑定点击事件
33-
: <IconClose class="mr-xs" onClick={() => toggle(item)} /> // 给节点绑定点击事件
34-
: <Indent />
35-
}
36-
<span class="devui-tree-node__title">{item.label}</span>
47+
{/* 折叠图标 */}
48+
{renderIcon(item)}
49+
<span class={[
50+
'devui-tree-node__title',
51+
item.disabled && 'select-disabled']}>
52+
{item.label}
53+
</span>
3754
</div>
3855
</div>
3956
</div>

packages/devui-vue/docs/components/tree/index.md

+2
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,8 @@ export default defineComponent({
2323
}, {
2424
label: '一级 2', level: 1,
2525
open: true, // 新增
26+
disableToggle: true, // 新增
27+
disabled: true, // 新增
2628
children: [{
2729
label: '二级 2-1', level: 2,
2830
children: [{

0 commit comments

Comments
 (0)