前言
前端二面试题,话不多说看题~
很基础的前端项目组织树,这边也是没有搭建环境,只是简单的引用了一个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>
效果图