前言
前端二面试题,话不多说看题~
很基础的前端项目组织树,这边也是没有搭建环境,只是简单的引用了一个vue2库,保存到了本地直接引用加快访问。
功能
1.默认展示树内容
2.鼠标右击出现菜单
3.实现添加节点、删除节点、修改节点
代码 html
效果图先放最后
<!DOCTYPE html>
<html lang="">
<head>
<meta charset="utf-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width,initial-scale=1">
<title>menu</title>
<script src="./vue.min.js"></script>
</head>
<body>
<div id="app">
<p>选择节点ID:{{hoverId}} 下一个ID:{{lastId}}</p>
<div class="box" @click.right="fnRightClick($event)" @click.left="fnLeftClick($event)">
<div class="tree" v-html="treeHtml"></div>
</div>
<div class="menu" v-if="menuVisibleState" :style="{left: menuLeft,top: menuTop}">
<div class="btn" @click="fnOpenCreate()"><span>新增</span></div>
<div class="btn" @click="fnOpenModify()"><span>修改</span></div>
<div class="btn" @click="fnDelete()"><span>删除</span></div>
</div>
<div class="wrapper" v-if="formVisibleState">
<div class="dialog">
<div class="form">
<div>
<label for="name">组织名称:</label>
<input type="text" id="name" v-model="name">
</div>
<div>
<label for="desc">组织描述:</label>
<textarea rows="10" id="desc" v-model="desc"></textarea>
</div>
<div class="btn-group">
<button @click="fnCommit()">提交</button>
<button @click="fnCloseCreate()">取消</button>
</div>
</div>
</div>
</div>
</div>
</body>
<script>
const app = new Vue({
el: '#app',
component: {},
data: {
id: '',
name: '',
desc: '',
treeHtml: '',
hoverId: '',
lastId: 12,
detail: {},
menuLeft: 0,
menuTop: 0,
menuVisibleState: false,
formVisibleState: false,
orgDataList: [
{
id: 1, label: '1', desc: '',
children: [
{
id: 4, label: '1-1', desc: '',
children: [
{ id: 5, label: '1-1-1', desc: '', children: [] }
]
},
{
id: 10, label: '1-2', desc: '',
children: [
{ id: 11, label: '1-2-1', desc: '', children: [] }
]
}
]
},
{
id: 2, label: '2', desc: '',
children: [
{
id: 6, label: '2-1', desc: '',
children: [
{ id: 7, label: '2-1-1', desc: '', children: [] }
]
}
]
},
{
id: 3, label: '3', desc: '',
children: [
{
id: 8, label: '3-1', desc: '',
children: [
{ id: 9, label: '3-1-1', desc: '', children: [] }
]
}
]
}
],
},
methods: {
// 左击
fnLeftClick() {
this.menuVisibleState = false
},
// 右击
fnRightClick(event) {
event.preventDefault()
let e = event
this.menuLeft = e.clientX + "px"
this.menuTop = e.clientY + "px"
this.menuVisibleState = true
},
// 打开创建窗口
fnOpenCreate() {
this.menuVisibleState = false
this.formVisibleState = true
},
// 打开修改窗口
fnOpenModify() {
this.menuVisibleState = false
this.formVisibleState = true
fnOperateNode(this.hoverId, [], this.orgDataList, 'detail')
this.id = this.detail.id
this.name = this.detail.label
this.desc = this.detail.desc
this.children = this.detail.children
},
// 删除
fnDelete() {
fnOperateNode(this.hoverId, [], this.orgDataList, 'delete')
this.menuVisibleState = false
},
// 关闭创建窗口
fnCloseCreate() {
this.formVisibleState = false
},
// 提交
fnCommit() {
if (app.id) {
let val = {
id: app.id,
label: app.name,
desc: app.desc,
children: app.children
}
fnOperateNode(app.id, val, app.orgDataList, 'modify')
fnGeneratorTreeNode(app.orgDataList)
app.id = ''
} else {
let val = {
id: app.lastId,
label: app.name,
desc: app.desc,
children: []
}
if (app.hoverId) {
fnOperateNode(app.hoverId, val, app.orgDataList, 'create')
} else {
app.$set(app.orgDataList, app.orgDataList.length, val, 'create')
fnGeneratorTreeNode(app.orgDataList)
}
app.lastId = app.lastId + 1
}
app.formVisibleState = false
app.id = ''
app.name = ''
app.desc = ''
app.children = []
}
}
});
// 修改节点
function fnOperateNode(id, val, dataList, type) {
if (dataList && dataList.length) {
for (let i = 0; i < dataList.length; i++) {
if (dataList[i] && dataList[i].id == id) {
if (type == 'create') {
app.$set(dataList[i].children, dataList[i].children.length, val)
fnGeneratorTreeNode(app.orgDataList)
} else if (type == 'detail') {
app.detail = dataList[i]
} else if (type == 'modify') {
dataList[i] = val
fnGeneratorTreeNode(app.orgDataList)
} else if (type == 'delete') {
dataList.splice(i, 1)
console.log(app.orgDataList)
fnGeneratorTreeNode(app.orgDataList)
}
} else if (dataList[i].children && dataList[i].children.length) {
fnOperateNode(id, val, dataList[i].children, type)
}
}
}
}
// 悬浮选中ID
function fnSetHoverId(id) {
app.hoverId = id
}
// 移除悬浮选中ID
function fnUnsetHoverId() {
app.hoverId = ''
}
// 生成根节点 html
function fnGeneratorTreeNode(dataList) {
app.treeHtml = ''
dataList.forEach((item) => {
if (item) {
app.treeHtml += '<div class="node"><div class="content"><span οnmοuseenter="fnSetHoverId(' + item.id + ')">' + item.label + '</span>';
if (item.children && item.children.length) {
app.treeHtml += fnGeneratorLeafNode(item.children)
}
app.treeHtml += '</div></div>'
}
})
}
// 生成叶节点 html
function fnGeneratorLeafNode(dataList) {
let content = ''
for (let i = 0; i < dataList.length; i++) {
content += '<div class="node"><div class="content"><span οnmοuseenter="fnSetHoverId(' + dataList[i].id + ')">' + dataList[i].label + '</span>';
if (dataList[i].children && dataList[i].children.length) {
content += fnGeneratorLeafNode(dataList[i].children)
}
content += '</div></div>'
}
return content
}
fnGeneratorTreeNode(app.orgDataList)
</script>
<style>
* {
padding: 0;
margin: 0;
}
html,
body {
width: 100%;
height: 100%;
}
ul {
list-style: none;
}
#app {
width: 100%;
height: 100%;
}
.box {
width: 100%;
height: 100%;
background-color: #ffffff;
}
.menu {
width: 100px;
height: auto;
position: fixed;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
border: 1px solid #000000;
border-radius: 5px;
background-color: #ffffff;
}
.menu .btn {
width: 80px;
height: 20px;
padding: 10px;
text-align: center;
}
.menu .btn:hover {
background-color: black;
}
.menu .btn:hover span {
color: #ffffff;
}
.tree {
padding: 50px;
}
.node {
padding-left: 20px;
width: 100%;
height: 100%;
cursor: pointer;
}
.wrapper {
width: 100%;
height: 100%;
position: fixed;
top: 0;
right: 0;
bottom: 0;
left: 0;
z-index: 1000;
background-color: black;
opacity: 0.7;
text-align: center;
}
.dialog {
width: 320px;
height: 300px;
z-index: 1001;
background-color: #ffffff;
display: inline-block;
vertical-align: middle;
text-align: left;
}
.wrapper:after {
height: 100%;
width: 0px;
content: "";
display: inline-block;
text-align: center;
vertical-align: middle;
}
.form {
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
justify-items: start;
justify-content: left;
padding: 20px;
}
.form input {
width: 280px;
line-height: 25px;
margin-bottom: 10px;
}
.form textarea {
width: 280px;
}
button {
width: 50px;
height: 30px;
}
label {
font-size: 14px;
}
.btn-group {
margin-bottom: -1px;
}
</style>
</html>
效果图