文章目录
一、前言
很久没有做前端了,最近写了一个组织机构管理,链接如下(随时可能删除):http://jianxun.xyz:8080/dist/index.html#/
项目截图如下:
首页
编辑页:
添加页:
当是最后叶子节点时显示卡片
节点下还有子部门时显示列表,并且分页
有子节点时不能删除
二、使用到的技术及方法
没有使用到后台,接口文档使用的是yapi,前台使用的vue+elementui。
三、界面展示详解
1. 左侧树
使用的是el-tree
代码如下:
<el-tree
:data="treeList"
:props="defaultProps"
:expand-on-click-node="false"
highlight-current
node-key="organizationId"
ref="tree"
default-expand-all
@node-click="handleNodeClick"
@node-drop="handleDrop"
:allow-drop="allowDrop"
:draggable="false"
>
<span class="custom-tree-node" slot-scope="{ node, data }">
<span>{{ node.label }}</span>
<div>
<el-button type="text" size="mini" @click="() => edit(data)"
v-if="data.organizationId !=='1000' "
>编辑</el-button
>
<el-button type="text" size="mini" @click.stop="del(node,data)"
v-if="data.organizationId !=='1000' "
>删除</el-button
>
<el-button type="text" size="mini" @click="() => add(data)"
>添加</el-button
>
</div>
</span>
</el-tree>
- 以前的时候,能够跨级拖拽,之后因为业务需求,不要实现跨级拖拽,因此把所有的拖拽功能都去掉了,增加了序号排序。(跨级拖拽排序代码注释掉了,有这个需求的小伙伴可以参考)
- 这里的v-if="data.organizationId !==‘1000’,因为根节点的id是1000,在根节点上不能编辑和删除,所以加上这段话。
2.右侧卡片或列表
使用了el-card
或el-table
实现,同时结合v-if
是否显示或隐藏
<div class="org-right-container">
<el-card class="box-card" v-if="isShow">
<div slot="header" class="clearfix">
<span>机构详情</span>
</div>
<div>
<div class="org-detail-item">
<div class="org-detail-item-question" style="padding-top:0px">机构编码:</div>
<div class="org-detail-item-answer" style="margin-top: 0px">{{treeNodeClickData.organizationId}}</div>
</div>
<div class="org-detail-item">
<div class="org-detail-item-question">机构名称:</div>
<div class="org-detail-item-answer">{{treeNodeClickData.organizationName}}</div>
</div>
<div class="org-detail-item">
<div class="org-detail-item-question">上级机构名称:</div>
<div class="org-detail-item-answer">{{treeNodeClickData.parentOrganizationName}}</div>
</div>
<div class="org-detail-item">
<div class="org-detail-item-question">机构简称:</div>
<div class="org-detail-item-answer">{{treeNodeClickData.organizationShortName}}</div>
</div>
<div class="org-detail-item">
<div class="org-detail-item-question">排序序号:</div>
<div class="org-detail-item-answer">{{treeNodeClickData.hotKey}}</div>
</div>
<div class="org-detail-item">
<div class="org-detail-item-question">行政区划:</div>
<div class="org-detail-item-answer">{{treeNodeClickData.code}}</div>
</div>
<div class="org-detail-item">
<div class="org-detail-item-question">机构地址:</div>
<div class="org-detail-item-answer">{{treeNodeClickData.address}}</div>
</div>
<div class="org-detail-item">
<div class="org-detail-item-question">邮政编码:</div>
<div class="org-detail-item-answer">{{treeNodeClickData.postCode}}</div>
</div>
<div class="org-detail-item">
<div class="org-detail-item-question">联系电话:</div>
<div class="org-detail-item-answer">{{treeNodeClickData.phone}}</div>
</div>
</div>
</el-card>
<!-- 表格 -->
<el-table
class="org-table"
v-if="!isShow"
:data="treeClickData"
style="width: 100%">
<el-table-column
label="机构编码"
:width="table.itemWidth">
<template slot-scope="scope">
<i class="el-icon-time"></i>
<span style="margin-left: 10px">{{ scope.row.organizationId}}</span>
</template>
</el-table-column>
<el-table-column
label="机构名称"
:width="table.itemWidth">
<template slot-scope="scope">
<el-popover trigger="hover" placement="top">
<p>机构简称: {{ scope.row.organizationShortName }}</p>
<p>具体描述: {{ scope.row.description }}</p>
<div slot="reference" class="name-wrapper">
<el-tag size="medium">{{ scope.row.organizationName}}</el-tag>
</div>
</el-popover>
</template>
</el-table-column>
<el-table-column
label="上级机构名称"
:width="table.itemWidth">
<template slot-scope="scope">
<el-tag size="medium" type="info">{{ scope.row.parentOrganizationName}}</el-tag>
</template>
</el-table-column>
<el-table-column
label="行政区划"
:width="table.itemWidth">
<template slot-scope="scope">
<span style="margin-left: 10px">{{ scope.row.code}}</span>
</template>
</el-table-column>
<el-table-column
label="地址"
:width="table.itemWidth">
<template slot-scope="scope">
<span style="margin-left: 10px">{{ scope.row.address}}</span>
</template>
</el-table-column>
<!-- <el-table-column label="操作" width="150px">-->
<!-- <template slot-scope="scope">-->
<!-- <div class="handle-button">-->
<!-- <el-button-->
<!-- size="mini"-->
<!-- @click="edit(scope.$index, scope.row)">编辑-->
<!-- </el-button>-->
<!-- <el-button-->
<!-- size="mini"-->
<!-- type="danger"-->
<!-- @click="delete(scope.$index, scope.row)">删除-->
<!-- </el-button>-->
<!-- </div>-->
<!-- </template>-->
<!-- </el-table-column>-->
</el-table>
<div class="page">
<el-pagination @size-change="handleSizeChange" @current-change="handleCurrentChange"
:current-page="table.currentPage"
:page-sizes="[1, 2,5, 10]" :page-size="table.pageSize"
layout="total, sizes, prev, pager, next, jumper"
v-if="!isShow"
:total="table.total">
</el-pagination>
</div>
</div>
上面代码还包含了分页,具体细节下面会讲解。
四、具体功能及实现
1. 数据展示
- 在页面载入的时候,完成对左边树的初始化
mounted() {
// 获取部门树数据
this.getTreeDept('')
}
/** 查询部门下拉树结构 */
getTreeDept(keyword) {
if (keyword) {
// 把关键字存起来
this.keyword = ''
this.keyword = keyword
// debugger;
organizationService.getOrganizationsListByKeyword(keyword).then(res => {
// 初始化树
this.treeList = res.data.content
// 清空表格数据
this.treeClickData = null
// 清空右侧数据
this.treeNodeClickData = {}
}),
err => {
this.$message.error(err.message)
}
} else {
// 获取部门树数据
organizationService.getOrganizationsList().then(res => {
// 初始化树
this.treeList = res.data.content
// 清空表格数据
this.treeClickData = null
// 清空右侧数据
this.treeNodeClickData = {}
}),
err => {
this.$message.error(err.message)
}
}
},
2. 添加功能
/** 添加部门 */
add(data) {
// this.$refs['ruleForm'].clearValidate();
console.log(data)
// 判断是个点击了某个部门
this.editOrAddDialog.form = {}
this.editOrAddDialog.form.parentOrganizationName = data.organizationName
this.editOrAddDialog.form.parentOrganizationId = data.organizationId
this.editOrAddDialog.isVisible = true
console.log('data', data)
this.editOrAddDialog.title = '添加'
}
3. 编辑功能
edit(data) {
console.log(data)
// 如果不加这个,那么树将会跟着编辑框一起动
let editData = {...data}
console.log('edit', editData)
this.editOrAddDialog.title = '编辑'
this.editOrAddDialog.isVisible = true
this.editOrAddDialog.form = editData
},
4. 删除功能
/** 删除部门 */
del(node, data) {
console.log(node, data)
const len = data.children.length
if (len == 0) {
let confirmText = `确定删除${data.organizationName}及其子部门吗`
this.$confirm(confirmText, '提示', {
type: 'warning'
}).then(() => {
organizationService.delOrganizations(data.organizationId).then(res => {
this.$message.success(`删除${data.organizationName}及其子部门成功`)
//刷新数据
this.getTreeDept(this.keyword)
})
err => {
this.$message.error(err.message)
}
})
} else {
this.$message.error(`该部门下有${len}个直接子部门,您不能删除!`)
}
}
5. 关于分页的处理
使用了elementui的分页,很好用
// 每页数量改变时
handleSizeChange(val) {
console.log(`每页 ${val} 条`);
this.table.pageSize = val
this.getList()
},
// 当当前页改变
handleCurrentChange(val) {
console.log(`当前页: ${val}`);
this.table.currentPage = val
this.getList()
},
getList() {
//过滤分页
this.treeClickData = this.table.data.filter((item, index) =>
index < this.table.currentPage * this.table.pageSize && index >= this.table.pageSize * (this.table.currentPage - 1)
)
console.log('this.treeClickData', this.treeClickData)
//总页数
this.table.total = this.table.data.length
}
6. 关于树的拖拽排序
树的拖拽排序分为3种,inner,after,before.
当为inner的时候:被拖拽的parentId=放置位置的Id。
当为before或inner的时候,被拖拽的parentId放置位置的parentId。
这里详细代码就不贴出来了,相信你们能写好的。
7.树结构转化成数组
// 递归
// 1. 结束条件 treeData为空,返回[]
// 2. 结束条件 treeData不为空 返回[当前元素].concat(transfer(子元素))
// 需要实现transfer函数, 输入树形结构, 输出结果为一个一维数组:arrayData
transfer(treeData) {
if (!(!treeData.hasOwnProperty('organizationId') || !treeData)) {
let arr = []
let obj = {}
obj.organizationId = treeData.organizationId
obj.organizationName = treeData.organizationName
obj.organizationShortName = treeData.organizationShortName
obj.description = treeData.description
obj.parentOrganizationName = treeData.parentOrganizationName
obj.code = treeData.code
obj.address = treeData.address
obj.children = treeData.children.map(value => {
// [1] arr = arr.concat(transfer(value))
return value.organizationId
})
arr.push(obj)
// 这段代码可由代码 [1] 替代,替代后父元素在子元素后
treeData.children.forEach(value => {
arr = arr.concat(this.transfer(value))
})
//
return arr
} else { // 初始treeData是否为空树
return []
}
}
8.数组结构转化成树
gre:function (array, ckey) {
let menuData = [];
let indexKeys = Array.isArray(array) ? array.map((e) => {return e.oraId}) : [];
ckey = ckey || 'parentOraId';
array.forEach(function (e, i) {
//alert(e[ckey])
//一级菜单
if (!e[ckey] || (e[ckey]===e.oraId)) {
delete e[ckey];
menuData.push(deepcopy(e)); //深拷贝
}else if(Array.isArray(indexKeys)){
//检测ckey有效性
let parentIndex = indexKeys.findIndex(function(id){
return id == e[ckey];
});
if(parentIndex===-1){
menuData.push(e);
}
}
});
// 查找子节点,并把子节点当成孩子节点赋值给他的上一级
let findChildren = function (parentArr) {
if (Array.isArray(parentArr) && parentArr.length) {
parentArr.forEach(function (parentNode) {
array.forEach(function (node) {
if (parentNode.oraId === node[ckey]) {
if (parentNode.children) {
parentNode.children.push(node);
} else {
parentNode.children = [node];
}
}
});
if (parentNode.children) {
findChildren(parentNode.children);
}
});
}
};
findChildren(menuData);
return menuData;
}
五、总结
- 界面不太美观。
- 接口设计的时候不把组织机构编码设为id好了
- 返回数据的时候不应该全部返回,只返回编码和机构名称就好了,点击的时候再从后台返回数据。
最后感谢您看完,有什么问题评论告诉我
希望你是披荆斩棘的女英雄,也是令人疼爱的小朋友