先看下效果图
直接上代码
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta http-equiv="X-UA-Compatible" content="ie=edge"> <title>Document</title> </head> <body> <div id="container"> </div> </body> <style> .containerParent { display: flex; flex-direction: column; auto; } .containerChildren { margin-left: 15px; 300px; border-left-style: dotted; border-color: #D8D8D8; } .parentChild { 300px; } .operaion_row-button { display: inline-block; min- 50px; } .editContainer { display: inline-block; padding-left: 10px; padding-right: 10px; 100px; height: 20px; max- 100px; overflow: hidden; text-overflow: ellipsis; white-space: nowrap } .editContainer:focus { color: goldenrod; } .Icon_class { 15px; height: 15px; } .parentIcon { margin-left: 10px; } .parent_span { margin-left: 10px; } .descriter { display: inline-block; margin-top: -3px; color: #D8D8D8; vertical-align: top; } .focusClass { color: blue; } </style> <script> // 源数据 const data = [ { id: 1, name: "高配室", spread: true, tempChildren: [], children: [ { id: 2, name: "第一个子节点", edit: false, checked: false, children: [ { id: 8, edit: false, checked: false, name: "孙子节点", children: [] }, ] }, { id: 5, name: "第二个子节点", checked: false, edit: false, children: [] }, ] } ]; // 收集数据 const arrId = []; // 切换子树中的显示状态 function toggleFnc(val) { data.map(currentData => { if (currentData.id == val) { let temCuttentchildren = currentData.children; let temCuttenttempChildren = currentData.tempChildren; currentData.children = temCuttenttempChildren currentData.tempChildren = temCuttentchildren; currentData.spread = !currentData.spread } }) container.innerHTML = getParent(data); }; // 切换选中状态 function toggleStatus(data, status) { if (data.children.length > 0) { data.children.map(currentData => { currentData.checked = status; if (currentData.children.length > 0) { toggleStatus(currentData, status); } }) } }; // 获取选中数据,切换选中状态 function toogleChecked(data, val, isInit) { data.map(currentData => { if (currentData.id == val) { currentData.checked = !currentData.checked; if (currentData.checked) { arrId.push(currentData.name); toggleStatus(currentData, true); } else { let index = arrId.indexOf(currentData.name); arrId.splice(index, 1); toggleStatus(currentData, false); } } else if (currentData.children.length > 0) { toogleChecked(currentData.children, val, "NoInit"); } }) }; // 获取选中数据,切换选中状态,刷新数据 function selectionFunc(id,checked) { toogleChecked(data, id); container.innerHTML = getParent(data); let EleColor = document.getElementById(id).classList; if (!checked) { EleColor.add("focusClass"); } else { EleColor.remove("focusClass"); } } // 确认数据,规范化参数, function ascertainVaL(data, val, inpuId) { console.log("val", val, "inpuId", inpuId) data.map((currentData) => { if (currentData.id == val) { currentData.edit = false; if (inpuId.textContent == "") { inpuId.textContent = "默认值" } currentData.name = inpuId.textContent; } else if (currentData.children.length > 0) { ascertainVaL(currentData.children, val, inpuId); } }) }; // 确定数据,刷新页面 function ascertain(val) { let inpuId = document.getElementById(val); console.log("val", val); ascertainVaL(data, val, inpuId); container.innerHTML = getParent(data); }; //生成默认数据,把数据加入源数据 function addVal(data, val, randomId) { data.map((currentData) => { if (currentData.id == val) { let temObj = { id: randomId, name: "默认值", edit: true, children: [] }; currentData.children.push(temObj); } else if (currentData.children.length > 0) { addVal(currentData.children, val, randomId); } }) console.warn("randomId", randomId) return randomId; }; // 新建数据,把数据加入源数据,刷新页面 function add(val) { let randomId = val + Math.ceil(Math.random(9999) * 10000) + 1000; let targetId = addVal(data, val, randomId); container.innerHTML = getParent(data); document.getElementById(targetId).focus(); } // 删除数据 function remove(val) { let removeVal = function (data) { data.map(currentData => { if (currentData.id == val) { currentData.id = 9999; } else if (currentData.children.length > 0) { removeVal(currentData.children); } }) } removeVal(data); container.innerHTML = getParent(data); } // 获取父级数据 function getParent(currentData) { if (currentData.id == "") return; let html = ""; currentData.map(data => { let htmlValue = ""; if (data.id != 9999) { htmlValue = ` <div class= "containerParent"> <div class="parentChild"> <img src = ${data.spread == true ? "./imgs/free.svg" : "./imgs/shrink.svg"} alt="切换是否显示图标" class="Icon_class" onclick = ${`"toggleFnc(${data.id})"`} /> <span data-appid=${data.id} class="parent_span">${data.name}</span> <img src = "./imgs/add.svg" alt="新增按钮" class="Icon_class parentIcon" onclick = ${`"add(${data.id})"`} /> </div> ${data.children && data.children.length > 0 ? getChildren(data.children) : ""} </div> ` } html += htmlValue; }) return html; } // 获取子树数据 function getChildren(data) { let currentData; let html = ""; if (data.id == "" || data.id == 9999) return html; data.map((cuttentDate) => { if (cuttentDate.id == 9999) return html; let htmlValue if (cuttentDate.id) { let spanVal = `<img src = "./imgs/define.svg" alt="确定按钮" class="Icon_class" onclick = ascertain(${cuttentDate.id})>`; let inputClick = cuttentDate.id + "checkbox" htmlValue = ` <div class="containerChildren"> <span class="descriter">...</span> ${cuttentDate.checked == true ? `<img src = "./imgs/checked.svg" onclick = ${`"selectionFunc(${cuttentDate.id},${cuttentDate.checked})"`} id=${inputClick} alt="选中按钮" class="Icon_class" >` : `<img src = "./imgs/unChecked.svg" onclick = ${`"selectionFunc(${cuttentDate.id},${cuttentDate.checked})"`} id=${inputClick} alt="不选中按钮" class="Icon_class">` } <span id=${cuttentDate.id} contenteditable = ${cuttentDate["edit"]} class="editContainer" title=${cuttentDate.name}>${cuttentDate.name}</span> ${cuttentDate.edit == false ? "" : spanVal} <img src = "./imgs/add.svg" alt="新增按钮" class="Icon_class" onclick = ${`"add(${cuttentDate.id})"`}> <img src = "./imgs/remove.svg" alt="删除按钮" class="Icon_class" onclick = ${`"remove(${cuttentDate.id})"`}> ${cuttentDate.children && cuttentDate.children.length > 0 ? getChildren(cuttentDate.children) : ""} </div > ` } html += htmlValue }) return html } //双击触发事件 function dbClickFnc(e) { let targetValue = (e.targetValue || e.srcElement).id; if (targetValue) { let getData = function (data) { data.map(currentData => { if (currentData.id == targetValue) { if (e.type == 'focusout') { currentData.edit = false; } else { currentData.edit = true; } } else if (currentData.children.length > 0) { getData(currentData.children); } } ) } getData(data); container.innerHTML = getParent(data); let getEle = document.getElementById(targetValue); getEle.focus(); } } const container = document.getElementById("container"); container.addEventListener("dblclick", dbClickFnc, false) container.innerHTML = getParent(data); </script> </html>