✨(component): tree组件 加入半选状态,优化data改变是视图更新,优化checkedKeys支持下级节点,默认开启过渡动画,优化关闭连线后支持行内点击
This commit is contained in:
@@ -7,7 +7,7 @@ export default {
|
||||
<script setup lang="ts">
|
||||
import { LayIcon } from "@layui/icons-vue";
|
||||
import LayCheckbox from "../checkbox/index.vue";
|
||||
import { Ref, useSlots } from "vue";
|
||||
import { computed, Ref, useSlots } from "vue";
|
||||
import { Tree } from "./tree";
|
||||
import { Nullable } from "../../types";
|
||||
import LayTransition from "../transition/index.vue";
|
||||
@@ -19,9 +19,9 @@ export interface TreeData {
|
||||
children: TreeData[];
|
||||
parentKey: Nullable<StringOrNumber>;
|
||||
isRoot: boolean;
|
||||
isChecked: Ref<boolean>;
|
||||
isDisabled: Ref<boolean>;
|
||||
isLeaf: Ref<boolean>;
|
||||
isChecked: boolean;
|
||||
isDisabled: boolean;
|
||||
isLeaf: boolean;
|
||||
hasNextSibling: boolean;
|
||||
parentNode: Nullable<TreeData>;
|
||||
}
|
||||
@@ -65,7 +65,7 @@ const nodeIconType = (node: TreeData): string => {
|
||||
return "";
|
||||
}
|
||||
if (node.children.length !== 0) {
|
||||
return !node.isLeaf.value
|
||||
return !node.isLeaf
|
||||
? "layui-icon-addition"
|
||||
: "layui-icon-subtraction";
|
||||
}
|
||||
@@ -81,7 +81,7 @@ function handleChange(checked: boolean, node: TreeData) {
|
||||
}
|
||||
|
||||
function handleIconClick(node: TreeData) {
|
||||
node.isLeaf.value = !node.isLeaf.value;
|
||||
node.isLeaf = !node.isLeaf;
|
||||
}
|
||||
|
||||
function handleTitleClick(node: TreeData) {
|
||||
@@ -90,6 +90,39 @@ function handleTitleClick(node: TreeData) {
|
||||
}
|
||||
emit("node-click", node);
|
||||
}
|
||||
function handleRowClick(node:TreeData){
|
||||
if(!props.showLine){
|
||||
handleTitleClick(node);
|
||||
}
|
||||
}
|
||||
|
||||
//判断是否半选
|
||||
const isChildAllSelected=computed(()=>{
|
||||
function _isChildAllSelected(node:TreeData):boolean{
|
||||
if(!props.showCheckbox){
|
||||
return false;
|
||||
}
|
||||
let childSelectNum=0;
|
||||
let res=false;// true为半选 false为全选
|
||||
for (const item of node.children) {
|
||||
if(item.isChecked) childSelectNum++;
|
||||
}
|
||||
if(childSelectNum>0) node.isChecked=true;//此处的处理与 checkedKeys 有关联
|
||||
if(childSelectNum==node.children.length){//继续递归向下判断
|
||||
for (const item of node.children) {
|
||||
res=_isChildAllSelected(item)
|
||||
if(res) break;
|
||||
}
|
||||
}else{
|
||||
res=true;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
return function(node:TreeData):boolean{
|
||||
let res=_isChildAllSelected(node)
|
||||
return res;
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
<template>
|
||||
@@ -102,7 +135,7 @@ function handleTitleClick(node: TreeData) {
|
||||
'layui-tree-setHide': node.isRoot,
|
||||
}"
|
||||
>
|
||||
<div class="layui-tree-entry">
|
||||
<div class="layui-tree-entry" @click="handleRowClick(node)">
|
||||
<div class="layui-tree-main">
|
||||
<span
|
||||
:class="[
|
||||
@@ -110,12 +143,12 @@ function handleTitleClick(node: TreeData) {
|
||||
{ 'layui-tree-iconClick': true },
|
||||
]"
|
||||
>
|
||||
<lay-icon :type="nodeIconType(node)" @click="handleIconClick(node)" />
|
||||
<lay-icon :type="nodeIconType(node)" @click.stop="handleIconClick(node)" />
|
||||
</span>
|
||||
<lay-checkbox
|
||||
v-if="showCheckbox"
|
||||
:modelValue="node.isChecked.value"
|
||||
:disabled="node.isDisabled.value"
|
||||
:modelValue="node.isChecked"
|
||||
:disabled="node.isDisabled"
|
||||
skin="primary"
|
||||
label=""
|
||||
@change="
|
||||
@@ -123,13 +156,14 @@ function handleTitleClick(node: TreeData) {
|
||||
handleChange(checked, node);
|
||||
}
|
||||
"
|
||||
:isIndeterminate='isChildAllSelected(node)'
|
||||
/>
|
||||
<span
|
||||
:class="{
|
||||
'layui-tree-txt': true,
|
||||
'layui-disabled': node.isDisabled.value,
|
||||
'layui-disabled': node.isDisabled,
|
||||
}"
|
||||
@click="handleTitleClick(node)"
|
||||
@click.stop="handleTitleClick(node)"
|
||||
>
|
||||
<template v-if="slots.title">
|
||||
<slot name="title" :data="node"></slot>
|
||||
@@ -142,7 +176,7 @@ function handleTitleClick(node: TreeData) {
|
||||
</div>
|
||||
<lay-transition :enable="collapseTransition">
|
||||
<div
|
||||
v-if="node.isLeaf.value"
|
||||
v-if="node.isLeaf"
|
||||
class="layui-tree-pack layui-tree-showLine"
|
||||
style="display: block"
|
||||
>
|
||||
|
||||
@@ -6,7 +6,7 @@ export default {
|
||||
|
||||
<script lang="ts" setup>
|
||||
import TreeNode from "./TreeNode.vue";
|
||||
import { computed, useSlots } from "vue";
|
||||
import { computed, useSlots, watch,ref } from "vue";
|
||||
import { useTree } from "./useTree";
|
||||
import { TreeData } from "./tree";
|
||||
import { StringFn, StringOrNumber, KeysType, EditType } from "./tree.type";
|
||||
@@ -43,9 +43,10 @@ interface TreeEmits {
|
||||
}
|
||||
|
||||
const props = withDefaults(defineProps<TreeProps>(), {
|
||||
checkedKeys:()=>{ return [] },
|
||||
showCheckbox: false,
|
||||
edit: false,
|
||||
collapseTransition: false,
|
||||
collapseTransition: true,
|
||||
onlyIconControl: false,
|
||||
disabled: false,
|
||||
showLine: true,
|
||||
@@ -70,10 +71,29 @@ const className = computed(() => {
|
||||
};
|
||||
});
|
||||
|
||||
const { tree, nodeList } = useTree(props, emit);
|
||||
let tree=ref();
|
||||
let nodeList=ref();
|
||||
const loadNodeList=()=>{
|
||||
let { tree:_tree, nodeList:_nodeList }=useTree(props, emit);
|
||||
tree.value=_tree
|
||||
nodeList.value=_nodeList.value
|
||||
}
|
||||
watch(
|
||||
() => props.data,
|
||||
() => {
|
||||
loadNodeList();
|
||||
},
|
||||
{ deep: true,immediate:true}
|
||||
)
|
||||
watch(
|
||||
() => props.checkedKeys,
|
||||
() => {
|
||||
loadNodeList()
|
||||
},
|
||||
)
|
||||
|
||||
function handleClick(node: TreeData) {
|
||||
const originNode = tree.getOriginData(node.id);
|
||||
const originNode = tree.value.getOriginData(node.id);
|
||||
emit("node-click", originNode);
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -11,9 +11,9 @@ export interface TreeData {
|
||||
children: TreeData[];
|
||||
parentKey: Nullable<StringOrNumber>;
|
||||
isRoot: boolean;
|
||||
isChecked: Ref<boolean>;
|
||||
isDisabled: Ref<boolean>;
|
||||
isLeaf: Ref<boolean>;
|
||||
isChecked: boolean;
|
||||
isDisabled: boolean;
|
||||
isLeaf: boolean;
|
||||
hasNextSibling: boolean;
|
||||
parentNode: Nullable<TreeData>;
|
||||
}
|
||||
@@ -106,21 +106,23 @@ class Tree {
|
||||
children: nodeChildren ? nodeChildren : [],
|
||||
parentKey: parentKey,
|
||||
isRoot: parentKey === "",
|
||||
isDisabled: ref(false),
|
||||
isChecked: ref(false),
|
||||
isLeaf: ref(false),
|
||||
isDisabled:false,
|
||||
isChecked: false,
|
||||
isLeaf: false,
|
||||
hasNextSibling: hasNextSibling,
|
||||
parentNode: parentNode || null,
|
||||
});
|
||||
|
||||
node.isDisabled.value = nodeDisabled;
|
||||
node.isChecked.value = parentNode
|
||||
? parentNode.isChecked.value
|
||||
: checkedKeys.includes(nodeKey);
|
||||
node.isLeaf.value = parentNode
|
||||
? parentNode.isLeaf.value
|
||||
node.isDisabled = nodeDisabled;
|
||||
if(parentNode && parentNode.isChecked){
|
||||
node.isChecked=true;
|
||||
}else{
|
||||
node.isChecked=checkedKeys.includes(nodeKey);
|
||||
}
|
||||
node.isLeaf = parentNode
|
||||
? parentNode.isLeaf
|
||||
: expandKeys.includes(nodeKey);
|
||||
node.isLeaf.value = nodeIsLeaf;
|
||||
node.isLeaf = nodeIsLeaf;
|
||||
|
||||
if (!nodeMap.has(nodeKey)) {
|
||||
nodeMap.set(nodeKey, node);
|
||||
@@ -134,7 +136,7 @@ class Tree {
|
||||
setChildrenChecked(checked: boolean, nodes: TreeData[]) {
|
||||
const len = nodes.length;
|
||||
for (let i = 0; i < len; i++) {
|
||||
nodes[i].isChecked.value = checked;
|
||||
nodes[i].isChecked = checked;
|
||||
nodes[i].children &&
|
||||
nodes[i].children.length > 0 &&
|
||||
this.setChildrenChecked(checked, nodes[i].children);
|
||||
@@ -145,11 +147,11 @@ class Tree {
|
||||
if (!parent) {
|
||||
return;
|
||||
}
|
||||
parent.isChecked.value = checked;
|
||||
parent.isChecked = checked;
|
||||
const pChild = parent.children;
|
||||
const pChildChecked = pChild.some((c) => c.isChecked.value);
|
||||
const pChildChecked = pChild.some((c) => c.isChecked);
|
||||
if (pChildChecked) {
|
||||
parent.isChecked.value = true;
|
||||
parent.isChecked = true;
|
||||
}
|
||||
if (parent.parentNode) {
|
||||
this.setParentChecked(checked, parent.parentNode);
|
||||
@@ -157,7 +159,7 @@ class Tree {
|
||||
}
|
||||
|
||||
setCheckedKeys(checked: boolean, node: TreeData) {
|
||||
node.isChecked.value = checked;
|
||||
node.isChecked = checked;
|
||||
// 处理上级
|
||||
if (node.parentNode) {
|
||||
this.setParentChecked(checked, node.parentNode);
|
||||
@@ -180,10 +182,10 @@ class Tree {
|
||||
while (!next.done) {
|
||||
const [, node] = next.value;
|
||||
const id = Reflect.get(node, this.config.replaceFields.id);
|
||||
if (node.isChecked.value) {
|
||||
if (node.isChecked) {
|
||||
checkedKeys.push(id);
|
||||
}
|
||||
if (node.isLeaf.value) {
|
||||
if (node.isLeaf) {
|
||||
expandKeys.push(id);
|
||||
}
|
||||
next = iterator.next();
|
||||
|
||||
Reference in New Issue
Block a user