项目完成后,产品又提新需求,
通过拖动能够改变下面的组织顺序,又给我增加了好大的工作量!
先吐槽产品一波,怎么早先不想好呢!
首先要想实现拖动改变顺序,那我从后端查询得来的数据
treeList
首先就必须有顺序,
后端为了实现节点有顺序—在实体类中又增加了一个字段
原先类实体
public class OrgNode {
private String id;
private String name;
private String parentId;
}
增加了一个brotherId
当前类实体
public class OrgNode {
private String id;
private String name;
private String parentId;
private String brotherId;
}
后端
1.新增节点
每当我新增一个节点时,就把他的brotherId设为 “-1”,
如果新增节点所在的层级之前没有节点,那不需要处理;
如果新增节点所在的层级之前有节点,那么把之前brotherId为-1的节点的brotherId改为新增节点的id
2.删除节点
当删除节点时,为保证顺序,需要把 被删节点 后一个节点的brotherId 改为 被删节点的 brotherId,
这样这一层级的节点顺序才不会出错
以上写了一点后端的做法,下面来详细讲解前端实现
属性结构的代码
<el-tree :draggable="true" @node-drop="testTrop" :allow-drop="dropPosition" :data="organization" node-key="id" :current-node-key="currentNode" :default-expanded-keys="keys" :expand-on-click-node="false"
@node-click="nodeClick" :props="defaultProps" ref='treeOrg' :filter-node-method='filterNode' highlight-current>
<span class="custom-tree-node" slot-scope="{ node, data }">
<el-tooltip v-if="node.label.length>10" :content='node.label' placement="top-start" effect="dark"
popper-class="atooltip">
<span>{{node.label | showTreeName}}</span>
</el-tooltip>
<span v-else>{{ node.label }}</span>
<span v-if="node.data.id==selectId">
<el-button type="text" icon="el-icon-edit" size="mini" @click.stop=" () => updateOrganization(data)"></el-button>
<el-button type="text" icon="el-icon-delete" size="mini" @click.stop="() => remove(node, data)"></el-button>
</span>
</span>
</el-tree>
该组织树的使用
1. 属性 draggable 是否开启 拖拽功能
2. 属性 allow-drop 拖拽时判定目标节点能否被放置。
3. 事件 node-drop 拖拽成功触发的事件 主要是我们 把数据发给 后端
4. data 我们要展示的数据 从后端获取的 treeList
5. node-key 每个树节点用来作为唯一标识的属性,整棵树应该是唯一的
6. current-node-key 当前选中的节点
7. default-expanded-keys 默认展开的节点的 key 的数组 就是我们 想展开节点的 id 数组
8. expand-on-click-node 是否在点击节点的时候展开或者收缩节点, 默认值为 true,如果为 false,则只有点箭头图标的时候才会展开或者收缩节点。
9. node-click 节点被点击时的回调 节点被点击时 我们想做什么事 一般是查询
10. props 主要用来 指定 我们想展示节点的 的那两个属性 属性名 必须和 从后端得到数据的 属性名 相对应
11. ref ref被用来给元素或子组件 注册 引用信息。 引用信息将会注册在父组件的 $refs对象上。
12. filter-node-method 对树节点进行筛选时执行的方法,返回 true 表示这个节点可以显示,返回 false 则表示这个节点会被隐藏
13. highlight-current 是否高亮当前选中节点,默认值是 false。
14. el-tooltip 文字提示 用法 <el-tooltip> 要展示的内容 </el-tooltip> 给这个 内容 做一些提示
当前我们前端要达到的效果是 如果文字过长 很后面以省略号结束 但是有必须 让用户 看到 该条数据的全部内容 所以用 文字提示的 方法去达到目的
当前这个文字提示 已经添加 v-if 判断字数大于10 才会生效
v-else 就显示原本的字数
这里主要讲解拖拽的两个属性,一个事件
- 属性 draggable 是否开启 拖拽功能
- 属性 allow-drop 拖拽时判定目标节点能否被放置。
- 事件 node-drop 拖拽成功触发的事件 主要是我们 把数据发给 后端
第一个draggable没什么好讲的
第三个node-drop用法也简单
本人主要是
我被该属性卡住了
因为我的是有一个限制,同级中不能有同名组织,
所以需要判定 被拖组织 能否置于 目标组织 前面,后面,或者内部
dropPosition(draggingNode, dropNode, type) {
console.log(draggingNode);
console.log(dropNode);
console.log(type);
//这三个参数 是我们在页面拖动时 我们停止拖动时 会自动生成的参数三个参数
// 转移节点draggingNode 目标节点dropNode 置于目标节点的位置 type='prev'、'inner' 和 'next' 三个中其中一个
if (type === "inner") {
checkOrganizationName(draggingNode.data.id, draggingNode.data.name, dropNode.data.id).then(data => {
if (data.data.data === true) {
//说明目标节点内部存在 与转移节点 相同名称的 节点,不能转入
//那我们不设返回值 就不会触发移动事件
} else {
return "inner";
}
});
} else if (type === "prev" || type === "next") {
//判断转移节点 能否放置于 目标节点的 前面或者后面
checkOrganizationName(draggingNode.data.id, draggingNode.data.name, dropNode.data.parentId).then(data => {
if (data.data.data === true) {
//说明 前后不能放
//那我们也不设返回值 就不会触发移动事件
} else {
return type;
}
});
}
},
所以不能通过向后端发请求的方式,判断能否放于 目标位置
应该通过比较前端已经有的数据,判断能否放于目标位置
processDataTree(data) {
for (let index in data) {
let obj = data[index];
this.organizationTemp.push({id: obj.id, name: obj.name, parentId: obj.parentId});
if (obj.children !== null && obj.children.length > 0) {
this.processDataTree(obj.children);
}
}
},
getOrganizationTemp() {
getOrganization().then(data => {
this.organizationAfterMove = data.data.data;
this.organizationTemp = [];
this.processDataTree(this.organizationAfterMove);
});
},
dropPosition(draggingNode, dropNode, type) {
//这三个参数 是我们在页面拖动时 我们停止拖动时 会自动生成的参数三个参数
// 转移节点draggingNode 目标节点dropNode 置于目标节点的位置 type='prev'、'inner' 和 'next' 三个中其中一个
if (type === "inner") {
if (dropNode.data.children===null){
//目标节点没有子节点 可以直接放入
return "inner"
}else {
let childArray=dropNode.data.children;
for (let i = 0; i < childArray.length; i++) {
if (childArray[i].name===draggingNode.data.name){
return
}
}
return "inner"
}
} else if (type === "prev" || type === "next") {
let existObj;
for (let i = 0; i <this.organizationTemp.length; i++) {
let obj = this.organizationTemp[i];
if(obj.parentId === dropNode.data.parentId && obj.name === draggingNode.data.name && obj.id!==draggingNode.data.id){
existObj=obj;
}
}
if (existObj) {
} else {
return type;
}
}
},
testTrop(before,after,inner,event){
changeParentOrg(before.data.id,after.data.id,inner,this.$getCookie().getUserName());
setTimeout(()=>{this.getOrganizationTemp()},500);
},