Как фильтровать поиском вложенную струкутуру компонента дерева?
Поиск по группам только работает по пользователям не работает Пример кода можно еще здесь посмотреть
### UsersTree.vue ###
<template>
<div class="d-flex flex-column">
<div class="treeview-title">
<div class="inner-treeview-title">Группы пользователей</div>
</div>
<loading-animation v-if="statusLoading"/>
<tree-view-item-users v-else
:nodes="treeGroupsUsers"
class="tree"
/>
</div>
</template>
<script>
import TreeViewItemUsers from "./Items/TreeViewItemUsers";
export default {
name: 'UserTree',
components:{
TreeViewItemUsers,
},
props: {
group: String,
statusLoading: Boolean
},
data() {
return {
treeGroupsUsers = [
{
"uuid": "86585e18",
"name": "Группа 1",
"users": [
{
"uuid": "6d69158b",
"name": "Наумов Виктор",
}
]
},
{
"uuid": "99033184",
"name": "Группа 2",
"users": [
{
"uuid": "14cf75e9",
"name": "Иванов Виталий",
},
{
"uuid": "36a2b701",
"name": "Петров Петр",
},
{
"uuid": "e86bda49",
"name": "Сидоров Иван",
}
]
},
]
}
},
}
</script>
<style lang="scss" scoped>
</style>
TreeViewItemUsers.vue
<template>
<div>
<div :class="{'tree-view__root': isRoot, 'tree-view__node': isNode}">
<template v-if="isRoot">
<div class="pl-4 pr-4 mb-3">
<b-input class="search-control"
v-model="search"
placeholder="Найти"
/>
</div>
<div class="scroll-content">
<vuescroll :ops="ops">
<div is="tree-view-item-users" class="l1"
v-for="node in searchResultGroup"
:key="node.uuid"
:node="node"
></div>
</vuescroll>
</div>
</template>
<template v-else-if="isNode">
<div class="node-name">
<span class="toggle">
<i v-if="childrenExists"
class="fas"
:class="{
'fa-minus': isOpen && !isLoading,
'fa-plus': !isOpen || isLoading
}"
@click="isOpen=!isOpen"
:title="(isOpen ? 'Скрыть' : 'Показать') + ' вложенный список'"
></i>
</span>
<div v-if="isUsers"
class="tree-users-item user d-flex flex-grow-1"
@click="selectNode">
<router-link class="tree-view-link"
:to="'/users/user/' + node.uuid">
<span class="label-name"
:group-uuid="node.uuid"
:class="{'tree-view__bold': isSelected}"
>
{{ node.name }}
</span>
</router-link>
</div>
<div v-else class="tree-users-item has-child d-flex flex-grow-1">
<router-link class="tree-view-link"
:to="'/users/group/' + node.uuid">
<span class="label-name">
{{ node.name }}
</span>
</router-link>
</div>
</div>
<div v-show="isOpen" v-if="hasChildren" class="nodes-child">
<tree-view-item-users v-for="child in searchResultUser"
:key="child.uuid"
:node="child"
:group-uuid="node.uuid"
ref="users"
class="node-child-tree"
@selected-node="$emit('select-node', $event)"
/>
</div>
</template>
</div>
</div>
</template>
<script>
import EventBus from "@/components/system/event-bus";
import AE from 'vue-active-element';
import vuescroll from 'vuescroll'
export default {
name: 'tree-view-item-users',
components: {
vuescroll
},
props: {
node: {
type: Object,
},
nodes: {
type: Array,
default: null
},
groupUuid: String,
filterterm: String
},
directives: {isclick: AE.directive},
data() {
return {
showChildren: true,
isOpen: false,
isLoading: false,
selectedNodeItem: null,
isSelectedItem: false,
selectItemTree: undefined,
search: '',
operation: {
rail: {
size: '20px'
},
bar: {
size: '15px'
}
},
ops: {
rail: {
opacity: '0',
background: '#585d68',
border: 'transparent',
size: '3px'
},
bar: {
background: 'rgba(88,96,101,0.8)',
keepShow: false,
size: '10px',
minSize: 0.3
},
}
}
},
computed: {
isRoot() {
return typeof this.nodes == 'object' && this.nodes !== null;
},
isNode() {
return Boolean(!this.isRoot && typeof this.node == 'object' && this.node);
},
isUsers() {
return Boolean(!Array.isArray(this.node.users))
},
isSelected() {
return this.isNode && this.value === this.node.uuid;
},
childrenExists() {
if (!this.node) {
return false;
}
if (this.canLoadChildren) {
return true;
}
return this.hasChildren;
},
hasChildren() {
return Boolean(
this.node
&& Array.isArray(this.node.users)
&& this.node.users.length > 0
);
},
canLoadChildren() {
return Boolean(
this.node
&& Number.isInteger(this.node.users) && this.node.users > 0
);
},
searchResultGroup () {
// по группам ищет
return this.nodes.filter(item => item.name.indexOf(this.search) !== -1);
},
searchResultUser () {
return this.node.users.filter(item => item.name.indexOf(this.search) === 0)
},
},
methods: {
itemNode(id) {
this.selectedNodeItem = id;
},
selectNode() {
if (!this.node.users) {
EventBus.$emit('select-node', this.node.uuid);
} else {
EventBus.$emit('select-group', {
uuid: this.node.uuid,
name: this.node.name
});
}
if (this.isSelected && this.isOpen) {
return true;
}
this.updateValue();
},
toggleChildren() {
this.isOpen = !this.isOpen;
},
updateValue(value = null) {
this.value = arguments.length || !this.node ? value : this.node.uuid;
if (this.$parent && (this.$parent.isNode ||
this.$parent.isRoot) &&
typeof this.$parent.updateValue === 'function') {
this.$parent.updateValue(this.value);
}
},
}
}
</script>
<style scoped>
.toggle {
cursor: pointer;
width: 10px;
position: absolute;
display: block;
left: 20px;
}
.node-name {
user-select: none;
}
.tree-view__node > .node-name {
/* padding: 0 10px 0 20px;*/
font-size: 14px;
font-weight: 600;
text-transform: capitalize;
display: flex;
align-items: center;
position: relative;
}
.tree-view__node > .node-name > .label-name {
display: block;
width: 100%;
padding-top: 10px;
padding-bottom: 10px;
padding-left: 10px;
}
.tree-view__root > .tree-view__node > .nodes-child > .tree-view__node .node-name {
/*padding: 0 20px 0 48px;*/
cursor: pointer;
font-weight: 400;
font-size: 14px;
}
.node-name {
cursor: pointer
}
.node-name > svg {
cursor: pointer;
margin-right: 5px;
}
.node-name > .label-name {
cursor: pointer;
}
li.node-tree {
list-style: none;
}
.node-name.active-node {
background-color: #292c32 !important;
}
.selected {
background-color: #292c32 !important;
}
.active {
background-color: #292c32 !important;
}
.label-name {
text-transform: none;
}
.scroll-content {
height: 662px;
}
.search-control {
border: 1px solid #202225;
}
</style>
Ответы (1 шт):
Как я и предполагал работает только фильтрация групп. Пользователи не ищутся в том смысле, что на них фильтр не действует, они появляются всем списком (внутри своей группы)
Во-первых очень странно пытаться одновременно фильтровать и группы и пользователей. Ведь группа будет "вычеркнута" вместе со всеми своими пользователями, даже если они удовлетворяют условию фильтра. Нужен более хитрый алгоритм.
Во-вторых для фильтрации групп используется поле search корневого элемента (для которого предусмотрен ввод).
А пользователи-то фильтруются полем search, компонента, который в "глубине" дерева, которое никогда не меняется, всегда пустая строка. Поэтому все пользователи всегда удовлетворяют условию фильтра.
Можно как-то (через props или другим способом или использовать store) передавать корневой search вверх/вниз по дереву.
П.С. canLoadChildren действительно неправильная. Но по счастливому стечению обстоятельств её отрицательный результат не используется:)
П.С.2 Так как же всё таки фильтровать? Для начала надо чётко определиться, что же должно получится в итоге.
Фильтровать только пользователей, показывая все группы. Для этого и данный образчик можно приспособить, решив проблему с search.
Или более сложный вариант, когда из дерева удаляются любые узлы, но такие, что сами и их дочерние узлы до любой глубины не удовлетворяют условию фильтра. Здесь наверное понадобится обход дерева туда-обратно, либо делать как бы "подстриженную" копию, чтобы не возвращаться обратно.