1.节点数据显示上一个的数据
2.节点数据出现死循环
主要问题是,为了优化搜索性能,在请求接口的方法使用了节流函数,导致树形结构加载数据不能同步
处理方式是,将节流函数放到搜索方法
错误代码
<template>
<div class="department-container">
<div class="search">
<a-input-search
v-model="departName"
placeholder="请输入搜索关键字"
allowClear
@search="haneleSearch"
@change="haneleSearch"
/>
</div>
<div class="title flex row-middle row-between cursor" @click="haneleOther">
<div :class="`other ${other ? 'active' : ''}`">
其他成员
<!-- <a-icon class="icon" type="question-circle" /> -->
</div>
<!-- <a-button type="primary" @click="handleSync">立即同步</a-button> -->
</div>
<div class="title flex row-middle row-between">
<div>
部门
<!-- <a-icon class="icon" type="question-circle" /> -->
</div>
<!-- <a-button type="primary" @click="handleSync">立即同步</a-button> -->
</div>
<a-progress :percent="0" v-if="percent > 0"></a-progress>
<div class="search-list" v-if="isSearch">
<ul>
<li
v-for="(item, index) in searchData"
:key="index"
:class="searchKey == item.id ? 'active' : ''"
@click="handleDepartClick(null, null, item, 'depart')"
>
<svg-icon icon-class="department"></svg-icon>{{ item.name }}
</li>
</ul>
</div>
<div v-show="!isSearch">
<a-tree
:load-data="onLoadData"
:tree-data="treeData"
:props="defaultProps"
:show-icon="true"
:check-strictly="true"
:check-on-click-node="true"
:default-expand-all="true"
:expand-on-click-node="false"
:selectedKeys="selectedKeys"
@select="handleDepartClick"
>
<template slot="icon" slot-scope="item">
<svg-icon
v-if="item.depth == 0"
icon-class="organization1"
></svg-icon>
<svg-icon v-else icon-class="department"></svg-icon>
</template>
<template slot="custom" slot-scope="item">
{{ item.name }}
</template>
</a-tree>
</div>
</div>
</template>
<script>
import { getUserPick, syncData } from "@/api/common";
import throttle from "lodash/throttle";
export default {
name: "department",
data() {
return {
treeData: [],
searchData: [],
defaultProps: {
children: "children",
label: "name",
},
percent: 0,
departName: "",
isSearch: false,
selectedKeys: [],
searchKey: "",
other: false,
};
},
created() {
this.getDepartData();
},
methods: {
async onLoadData(treeNode) {
const that = this;
return new Promise((resolve) => {
// console.log(treeNode.dataRef)
if (treeNode.dataRef.children) {
resolve();
return;
}
that.getDepartData(treeNode).then((res) => {
treeNode.dataRef.children = res;
that.treeData = [...that.treeData];
resolve();
});
});
},
//错误示例
getDepartData: throttle(async function () {
let id = 0;
const params = {};
if (this.isSearch) {
params.depart_name = this.departName;
params.load = "search-department";
} else {
if (treeNode) {
id = treeNode.dataRef.id;
}
params.department_id = id;
params.load = "department";
}
const { data } = await getUserPick(params);
if (treeNode) {
return this.converData(data);
}
if (this.isSearch) {
this.searchData = data;
this.handleDepartClick(null, null, this.searchData[0]);
} else {
this.treeData = this.converData(data);
if (!treeNode)
this.handleDepartClick(null, null, this.treeData[0], "init");
}
}, 1000),
converData(data) {
data.forEach((item) => {
item.isLeaf = !(parseInt(item.right) - parseInt(item.left) > 1);
item.key = item.id;
item.scopedSlots = {
title: "custom",
icon: "icon",
switcherIcon: "switcherIcon",
};
if (item.children) {
delete item.children;
}
});
return data;
},
// 点击节点
handleDepartClick(data, e, item, depart) {
if (e || depart) {
this.other = false;
this.selectedKeys = [item ? item.id : e.node.dataRef.id];
this.searchKey = item ? item.id : e.node.dataRef.id;
this.$emit("change", item ? item : e.node.dataRef);
}
},
haneleOther() {
// -999 区分正常部门id, 表示为其他用户
this.other = true;
this.selectedKeys = [];
this.$emit("change", { id: -999, name: "其他员工" });
},
haneleSearch(){
this.isSearch = Boolean(this.departName);
if (this.isSearch) {
this.getDepartData();
}
},
async handleSync() {
let interval = setInterval(() => {
this.percent = this.percent + 2;
if (this.percent >= 80) clearInterval(interval);
}, 300);
this.hide = this.$message.loading("同步中...", 0);
try {
await syncData();
setTimeout(() => {
this.$message.success("操作成功");
this.percent = 0;
}, 2000);
clearInterval(interval);
setTimeout(this.hide, 0);
} catch {
this.percent = 0;
clearInterval(interval);
this.hide();
}
setTimeout(() => {
clearInterval(interval);
setTimeout(this.hide, 0);
}, 600);
},
},
};
</script>
<style lang="less">
.department-container .ant-tree {
font-size: 14px;
.ant-tree-switcher i {
font-size: @fontsize16 !important;
color: @8B8B8B-color;
}
}
</style>
<style lang="less" scoped>
.search {
padding: 0 8px;
/deep/.ant-input {
.borderRadius(2px,2px,2px,2px);
}
}
.search-list {
li {
cursor: pointer;
line-height: 24px;
margin: 8px 0;
&.active {
background-color: #bae7ff;
}
.svg-icon {
margin-left: 10px;
margin-right: 5px;
}
}
}
.other {
&:hover {
background-color: #e6f7ff;
}
&.active {
background-color: #bae7ff;
}
}
.title {
margin-top: 15px;
font-size: 14px;
color: @text-color66;
> div {
padding: 2px 8px;
}
.icon {
margin-left: 6px;
}
/deep/.ant-btn {
background: @66B3FF-color;
border: none;
.borderRadius(2px,2px,2px,2px);
padding: 0 8px;
height: 30px;
}
}
.department-container {
max-height: 100%;
height: calc(100vh - 128px);
padding: 0 7px;
box-sizing: border-box;
overflow: hidden;
overflow-y: auto;
.is-horizontal {
display: none;
}
/deep/.el-scrollbar__wrap {
overflow-x: hidden;
.el-scrollbar__view {
padding: 0;
}
}
}
</style>
正确代码:
<template>
<div class="department-container">
<div class="search">
<a-input-search
v-model="departName"
placeholder="请输入搜索关键字"
allowClear
@search="haneleSearch"
@change="haneleSearch"
/>
</div>
<div class="title flex row-middle row-between cursor" @click="haneleOther">
<div :class="`other ${other ? 'active' : ''}`">
其他成员
<!-- <a-icon class="icon" type="question-circle" /> -->
</div>
<!-- <a-button type="primary" @click="handleSync">立即同步</a-button> -->
</div>
<div class="title flex row-middle row-between">
<div>
部门
<!-- <a-icon class="icon" type="question-circle" /> -->
</div>
<!-- <a-button type="primary" @click="handleSync">立即同步</a-button> -->
</div>
<a-progress :percent="0" v-if="percent > 0"></a-progress>
<div class="search-list" v-if="isSearch">
<ul>
<li
v-for="(item, index) in searchData"
:key="index"
:class="searchKey == item.id ? 'active' : ''"
@click="handleDepartClick(null, null, item, 'depart')"
>
<svg-icon icon-class="department"></svg-icon>{{ item.name }}
</li>
</ul>
</div>
<div v-show="!isSearch">
<a-tree
:load-data="onLoadData"
:tree-data="treeData"
:props="defaultProps"
:show-icon="true"
:check-strictly="true"
:check-on-click-node="true"
:default-expand-all="true"
:expand-on-click-node="false"
:selectedKeys="selectedKeys"
@select="handleDepartClick"
>
<template slot="icon" slot-scope="item">
<svg-icon
v-if="item.depth == 0"
icon-class="organization1"
></svg-icon>
<svg-icon v-else icon-class="department"></svg-icon>
</template>
<template slot="custom" slot-scope="item">
{{ item.name }}
</template>
</a-tree>
</div>
</div>
</template>
<script>
import { getUserPick, syncData } from "@/api/common";
import throttle from "lodash/throttle";
export default {
name: "department",
data() {
return {
treeData: [],
searchData: [],
defaultProps: {
children: "children",
label: "name",
},
percent: 0,
departName: "",
isSearch: false,
selectedKeys: [],
searchKey: "",
other: false,
};
},
created() {
this.getDepartData();
},
methods: {
async onLoadData(treeNode) {
const that = this;
return new Promise((resolve) => {
// console.log(treeNode.dataRef)
if (treeNode.dataRef.children) {
resolve();
return;
}
that.getDepartData(treeNode).then((res) => {
treeNode.dataRef.children = res;
that.treeData = [...that.treeData];
resolve();
});
});
},
//正确示例
async getDepartData(treeNode) {
let id = 0;
const params = {};
if (this.isSearch) {
params.depart_name = this.departName;
params.load = "search-department";
} else {
if (treeNode) {
id = treeNode.dataRef.id;
}
params.department_id = id;
params.load = "department";
}
const { data } = await getUserPick(params);
if (treeNode) {
return this.converData(data);
}
if (this.isSearch) {
this.searchData = data;
this.handleDepartClick(null, null, this.searchData[0]);
} else {
this.treeData = this.converData(data);
if (!treeNode)
this.handleDepartClick(null, null, this.treeData[0], "init");
}
},
converData(data) {
data.forEach((item) => {
item.isLeaf = !(parseInt(item.right) - parseInt(item.left) > 1);
item.key = item.id;
item.scopedSlots = {
title: "custom",
icon: "icon",
switcherIcon: "switcherIcon",
};
if (item.children) {
delete item.children;
}
});
return data;
},
// 点击节点
handleDepartClick(data, e, item, depart) {
if (e || depart) {
this.other = false;
this.selectedKeys = [item ? item.id : e.node.dataRef.id];
this.searchKey = item ? item.id : e.node.dataRef.id;
this.$emit("change", item ? item : e.node.dataRef);
}
},
haneleOther() {
// -999 区分正常部门id, 表示为其他用户
this.other = true;
this.selectedKeys = [];
this.$emit("change", { id: -999, name: "其他员工" });
},
haneleSearch: throttle(function () {
this.isSearch = Boolean(this.departName);
if (this.isSearch) {
this.getDepartData();
}
}, 1000),
async handleSync() {
let interval = setInterval(() => {
this.percent = this.percent + 2;
if (this.percent >= 80) clearInterval(interval);
}, 300);
this.hide = this.$message.loading("同步中...", 0);
try {
await syncData();
setTimeout(() => {
this.$message.success("操作成功");
this.percent = 0;
}, 2000);
clearInterval(interval);
setTimeout(this.hide, 0);
} catch {
this.percent = 0;
clearInterval(interval);
this.hide();
}
setTimeout(() => {
clearInterval(interval);
setTimeout(this.hide, 0);
}, 600);
},
},
};
</script>
<style lang="less">
.department-container .ant-tree {
font-size: 14px;
.ant-tree-switcher i {
font-size: @fontsize16 !important;
color: @8B8B8B-color;
}
}
</style>
<style lang="less" scoped>
.search {
padding: 0 8px;
/deep/.ant-input {
.borderRadius(2px,2px,2px,2px);
}
}
.search-list {
li {
cursor: pointer;
line-height: 24px;
margin: 8px 0;
&.active {
background-color: #bae7ff;
}
.svg-icon {
margin-left: 10px;
margin-right: 5px;
}
}
}
.other {
&:hover {
background-color: #e6f7ff;
}
&.active {
background-color: #bae7ff;
}
}
.title {
margin-top: 15px;
font-size: 14px;
color: @text-color66;
> div {
padding: 2px 8px;
}
.icon {
margin-left: 6px;
}
/deep/.ant-btn {
background: @66B3FF-color;
border: none;
.borderRadius(2px,2px,2px,2px);
padding: 0 8px;
height: 30px;
}
}
.department-container {
max-height: 100%;
height: calc(100vh - 128px);
padding: 0 7px;
box-sizing: border-box;
overflow: hidden;
overflow-y: auto;
.is-horizontal {
display: none;
}
/deep/.el-scrollbar__wrap {
overflow-x: hidden;
.el-scrollbar__view {
padding: 0;
}
}
}
</style>