@[豆包ai 生成动态tree 增、删、改以及上移下移 html+jquery)
人工Ai 编程
推荐一Kimi https://kimi.moonshot.cn/
推荐二 豆包https://www.doubao.com/
实现效果图
html 代码
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale = 1.0"> <title>Chapter Tree</title> <style> /* 整体树容器样式 */ #chapter-tree-container { padding-left: 20px; } /* 章节li样式 */ li { position: relative; padding-left: 20px; margin: 5px 0; line-height: 24px; } /* 利用伪元素创建线条 */ li::before { content: ''; position: absolute; left: 0; top: 12px; width: 10px; border-top: 1px solid #ccc; } /* 顶级li去除顶部线条 */ #chapter-tree-container ul li:first-child::before { border-top: none; } /* 有子章节的li添加垂直线条 */ li:has(ul)::before { height: 100%; border-left: 1px solid #ccc; } /* 子章节ul样式 */ ul { list-style-type: none; padding-left: 10px; } /* 美化 input */ input.edit-input { width: 150px; padding: 8px; border: 1px solid #ccc; border-radius: 4px; font-size: 14px; color: #555; outline: none; } input.edit-input::placeholder { color: #999; } /* 美化操作按钮 */ button { padding: 6px 12px; background-color: #007BFF; color: white; border: none; border-radius: 4px; font-size: 14px; cursor: pointer; transition: background-color 0.3s ease; } button:hover { background-color: #0056b3; } button.add-button { background-color: #28a745; } button.add-button:hover { background-color: #218838; } button.modify-button { background-color: #ffc107; } button.modify-button:hover { background-color: #e0a800; } button.delete-button { background-color: #dc3545; } button.delete-button:hover { background-color: #c82333; } /* 折叠按钮样式 */ button[text="+"], button[text="-"] { width: 24px; height: 24px; border-radius: 50%; padding: 0; font-size: 14px; line-height: 24px; text-align: center; } </style> </head> <body> <div id="chapter-tree-container"></div> <script src="https://code.jquery.com/jquery-3.6.0.min.js"></script> <script> // 初始数据结构,为每个章节添加唯一 id var initialData = [ { id: 1, name: "第一单元 成长的节拍", children: [ { id: 2, name: "第一课 中学时代", children: [ { id: 3, name: "中学序曲" }, { id: 4, name: "珍惜青春" }, { id: 5, name: "步入中学生活" } ] }, { id: 6, name: "少年有梦", children: [ { id: 7, name: "梦想的含义" }, { id: 8, name: "努力的意义" }, { id: 9, name: "实现理想的途径" }, { id: 10, name: "正确对待理想与现实" } ] } ] } ]; // 渲染树形章节结构 function renderTree(data) { // 清空旧的渲染内容 $('#chapter-tree-container').empty(); var ul = $("<ul>"); function renderChildren(children, parentUl) { if (!children) return; children.forEach(function (child) { let li; li = $("<li>").data('chapter-id', child.id); var expandButton = $("<button>").text("+"); expandButton.click(function () { try { var subUl = li.find("ul"); if (subUl.length) { subUl.toggle(); expandButton.text(subUl.is(":visible")? "-" : "+"); } else { var newSubUl = $("<ul>").hide(); renderChildren(child.children, newSubUl); li.append(newSubUl); newSubUl.show(); expandButton.text("-"); } } catch (error) { console.error('Error in expand button click:', error); } }); var chapterNameSpan = $("<span>").text(child.name).addClass('chapter-name-span'); // 添加增删改按钮 var addButton = $("<button>").text("添加").addClass('add-button'); var modifyButton = $("<button>").text("修改").addClass('modify-button'); var deleteButton = $("<button>").text("删除").addClass('delete-button'); var moveUpButton = $("<button>").text("上移"); var moveDownButton = $("<button>").text("下移"); var addSubChapterButton = $("<button>").text("添加子章节"); addButton.click(function () { try { addChapter(li); } catch (error) { console.error('Error in add button click:', error); } }); modifyButton.click(function () { try { var currentLi = $(this).closest('li'); var chapterId = currentLi.data('chapter-id'); var chapter = findChapterById(chapterId, initialData); if (!chapter) { console.error('Chapter not found for modification.'); return; } var chapterNameSpan = currentLi.find('.chapter-name-span:eq(0)'); var input = $("<input>").val(chapter.name).addClass('edit-input').focus(); chapterNameSpan.replaceWith(input); input.on('blur', function () { try { var newName = $(this).val(); var currentLi = $(this).closest('li'); var chapter = getChapterFromLi(currentLi); if (chapter) { chapter.name = newName; var newChapterNameSpan = $("<span>").text(newName).addClass('chapter-name-span'); $(this).replaceWith(newChapterNameSpan); if (chapter.children && chapter.children.length > 0) { var subUl = currentLi.find('ul'); if (!subUl.length) { subUl = $("<ul>"); renderChildren(chapter.children, subUl); currentLi.append(subUl); } } } } catch (error) { console.error('Error in blur event of edit input:', error); } }); } catch (error) { console.error('Error in modify button click:', error); } }); deleteButton.click(function () { try { var chapterId = li.data('chapter-id'); var chapter = findChapterById(chapterId, initialData); if (chapter) { deleteChapter(chapter); } } catch (error) { console.error('Error in delete button click:', error); } }); moveUpButton.click(function () { try { moveChapterUp(li); } catch (error) { console.error('Error in move up button click:', error); } }); moveDownButton.click(function () { try { moveChapterDown(li); } catch (error) { console.error('Error in move down button click:', error); } }); addSubChapterButton.click(function () { try { var chapterId = li.data('chapter-id'); var chapter = findChapterById(chapterId, initialData); if (chapter) { addSubChapter(chapter); } } catch (error) { console.error('Error in add sub-chapter button click:', error); } }); li.append(expandButton, chapterNameSpan, addButton, modifyButton, deleteButton, moveUpButton, moveDownButton, addSubChapterButton); if (child.children && child.children.length > 0) { var subUl = $("<ul>"); renderChildren(child.children, subUl); li.append(subUl); } parentUl.append(li); }); } renderChildren(data, ul); $("#chapter-tree-container").append(ul); } // 添加章节 function addChapter(clickedLi) { try { var newChapter = { id: Date.now(), name: "默认章节", children: [] }; var parentUl = clickedLi.parent('ul'); var parentChapter; if (parentUl.length) { var parentLi = parentUl.parent('li'); if (parentLi.length) { parentChapter = getChapterFromLi(parentLi); } else { // 顶级 ul 的情况 parentChapter = { children: initialData }; } } else { // 顶级 li 的情况 parentChapter = { children: initialData }; } if (!parentChapter.children) { parentChapter.children = []; } parentChapter.children.push(newChapter); renderTree(initialData); } catch (error) { console.error('Error in addChapter function:', error); } } // 添加子章节 function addSubChapter(parentNode) { try { var newChapter = { id: Date.now(), name: "默认子章节", children: [] }; if (!parentNode.children) { parentNode.children = []; } parentNode.children.push(newChapter); renderTree(initialData); } catch (error) { console.error('Error in addSubChapter function:', error); } } // 删除章节 function deleteChapter(node) { try { if (node.parent) { var index = node.parent.children.indexOf(node); if (index >-1) { node.parent.children.splice(index, 1); } } else { var index = initialData.indexOf(node); if (index >-1) { initialData.splice(index, 1); } } renderTree(initialData); } catch (error) { console.error('Error in deleteChapter function:', error); } } // 章节上移 function moveChapterUp(li) { try { var prevLi = li.prev("li"); if (prevLi.length) { var prevIndex = li.parent().children().index(prevLi); var currentIndex = li.parent().children().index(li); var parent = getParentOfLi(li); if (parent && Array.isArray(parent.children)) { var chapter = getChapterFromLi(li); if (chapter) { parent.children.splice(currentIndex, 1); parent.children.splice(prevIndex, 0, chapter); } } else if (!parent && Array.isArray(initialData)) { var chapter = getChapterFromLi(li); if (chapter) { initialData.splice(currentIndex, 1); initialData.splice(prevIndex, 0, chapter); } } li.insertBefore(prevLi); } } catch (error) { console.error('Error in moveChapterUp function:', error); } } // 章节下移 function moveChapterDown(li) { try { var nextLi = li.next("li"); if (nextLi.length) { var nextIndex = li.parent().children().index(nextLi); var currentIndex = li.parent().children().index(li); var parent = getParentOfLi(li); if (parent && Array.isArray(parent.children)) { var chapter = getChapterFromLi(li); if (chapter) { parent.children.splice(currentIndex, 1); parent.children.splice(nextIndex + 1, 0, chapter); } } else if (!parent && Array.isArray(initialData)) { var chapter = getChapterFromLi(li); if (chapter) { initialData.splice(currentIndex, 1); initialData.splice(nextIndex + 1, 0, chapter); } } li.insertAfter(nextLi); } } catch (error) { console.error('Error in moveChapterDown function:', error); } } function getParentOfLi(li) { try { var parentLi = li.parent('li'); if (parentLi.length) { var parentChapterId = parentLi.data('chapter-id'); return findChapterById(parentChapterId, initialData); } return null; } catch (error) { console.error('Error in getParentOfLi function:', error); } } function getChapterFromLi(li) { try { var chapterId = li.data('chapter-id'); return findChapterById(chapterId, initialData); } catch (error) { console.error('Error in getChapterFromLi function:', error); } } function findChapterById(id, data) { try { for (var i = 0; i < data.length; i++) { if (data[i].id === id) { return data[i]; } if (data[i].children && data[i].children.length > 0) { var found = findChapterById(id, data[i].children); if (found) { return found; } } } return null; } catch (error) { console.error('Error in findChapterById function:', error); } } // 初始化渲染 $(document).ready(function () { renderTree(initialData); }); </script> </body> </html>
复制
上一版本有问题,下面是最新版本的。
<!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8"> <meta name="viewport" content="width=device-width, initial-scale = 1.0"> <title>Chapter Tree</title> <style> /* 整体树容器样式 */ #chapter-tree-container { padding-left: 20px; } /* 章节li样式 */ li { position: relative; padding-left: 20px; margin: 5px 0; line-height: 24px; } /* 利用伪元素创建线条 */ li::before { content: ''; position: absolute; left: 0; top: 12px; width: 10px; border-top: 1px solid #ccc; } /* 顶级li去除顶部线条 */ #chapter-tree-container ul li:first-child::before { border-top: none; } /* 有子章节的li添加垂直线条 */ li:has(ul)::before { height: 100%; border-left: 1px solid #ccc; } /* 子章节ul样式 */ ul { list-style-type: none; padding-left: 10px; } /* 美化 input */ input.edit-input { width: 150px; padding: 8px; border: 1px solid #ccc; border-radius: 4px; font-size: 14px; color: #555; outline: none; } input.edit-input::placeholder { color: #999; } /* 美化操作按钮 */ button { padding: 6px 12px; background-color: #007BFF; color: white; border: none; border-radius: 4px; font-size: 14px; cursor: pointer; transition: background-color 0.3s ease; } button:hover { background-color: #0056b3; } button.add-button { background-color: #28a745; } button.add-button:hover { background-color: #218838; } button.modify-button { background-color: #ffc107; } button.modify-button:hover { background-color: #e0a800; } button.delete-button { background-color: #dc3545; } button.delete-button:hover { background-color: #c82333; } /* 折叠按钮样式 */ button[text="+"], button[text="-"] { width: 24px; height: 24px; border-radius: 50%; padding: 0; font-size: 14px; line-height: 24px; text-align: center; } </style> </head> <body> <div id="chapter-tree-container"></div> <script src="https://code.jquery.com/jquery-3.6.0.min.js"></script> <script> // 初始数据结构,为每个章节添加唯一 id var initialData = [ { id: 1, name: "第一单元 成长的节拍", children: [ { id: 2, name: "第一课 中学时代", children: [ { id: 3, name: "中学序曲" }, { id: 4, name: "珍惜青春" }, { id: 5, name: "步入中学生活" } ] }, { id: 6, name: "少年有梦", children: [ { id: 7, name: "梦想的含义" }, { id: 8, name: "努力的意义" }, { id: 9, name: "实现理想的途径" }, { id: 10, name: "正确对待理想与现实" } ] } ] } ]; // 记录展开状态的对象 var expandedStates = {}; // 渲染树形章节结构 function renderTree(data) { // 清空旧的渲染内容 $('#chapter-tree-container').empty(); var ul = $("<ul>"); function renderChildren(children, parentUl) { if (!children) return; children.forEach(function (child) { let li; li = $("<li>").data('chapter-id', child.id); var expandButton = $("<button>").text(expandedStates[child.id]? "-" : "+"); expandButton.click(function () { try { var subUl = li.find("ul"); if (subUl.length) { subUl.toggle(); expandButton.text(subUl.is(":visible")? "-" : "+"); expandedStates[child.id] = subUl.is(":visible"); } else { var newSubUl = $("<ul>").hide(); renderChildren(child.children, newSubUl); li.append(newSubUl); newSubUl.show(); expandButton.text("-"); expandedStates[child.id] = true; } } catch (error) { console.error('Error in expand button click:', error); } }); var chapterNameSpan = $("<span>").text(child.name).addClass('chapter-name-span'); // 添加增删改按钮 var addButton = $("<button>").text("添加").addClass('add-button'); var modifyButton = $("<button>").text("修改").addClass('modify-button'); var deleteButton = $("<button>").text("删除").addClass('delete-button'); var moveUpButton = $("<button>").text("上移"); var moveDownButton = $("<button>").text("下移"); var addSubChapterButton = $("<button>").text("添加子章节"); addButton.click(function () { try { addChapter(li); } catch (error) { console.error('Error in add button click:', error); } }); modifyButton.click(function () { try { var currentLi = $(this).closest('li'); var chapterId = currentLi.data('chapter-id'); var chapter = findChapterById(chapterId, initialData); if (!chapter) { console.error('Chapter not found for modification.'); return; } var chapterNameSpan = currentLi.find('.chapter-name-span:eq(0)'); var input = $("<input>").val(chapter.name).addClass('edit-input').focus(); chapterNameSpan.replaceWith(input); input.on('blur', function () { try { var newName = $(this).val(); var currentLi = $(this).closest('li'); var chapter = getChapterFromLi(currentLi); if (chapter) { chapter.name = newName; var newChapterNameSpan = $("<span>").text(newName).addClass('chapter-name-span'); $(this).replaceWith(newChapterNameSpan); if (chapter.children && chapter.children.length > 0) { var subUl = currentLi.find('ul'); if (!subUl.length) { subUl = $("<ul>"); renderChildren(chapter.children, subUl); currentLi.append(subUl); } } } } catch (error) { console.error('Error in blur event of edit input:', error); } }); } catch (error) { console.error('Error in modify button click:', error); } }); deleteButton.click(function () { try { var chapterId = li.data('chapter-id'); var chapter = findChapterById(chapterId, initialData); if (chapter && (!chapter.children || (chapter.children && chapter.children.length === 0))) { deleteChapter(chapter); } } catch (error) { console.error('Error in delete button click:', error); } }); moveUpButton.click(function () { try { moveChapterUp(li); } catch (error) { console.error('Error in move up button click:', error); } }); moveDownButton.click(function () { try { moveChapterDown(li); } catch (error) { console.error('Error in move down button click:', error); } }); addSubChapterButton.click(function () { try { var chapterId = li.data('chapter-id'); var chapter = findChapterById(chapterId, initialData); if (chapter) { addSubChapter(chapter); } } catch (error) { console.error('Error in add sub - chapter button click:', error); } }); li.append(expandButton, chapterNameSpan, addButton, modifyButton, deleteButton, moveUpButton, moveDownButton, addSubChapterButton); if (child.children && child.children.length > 0) { var subUl = $("<ul>"); renderChildren(child.children, subUl); li.append(subUl); if (expandedStates[child.id]) { subUl.show(); expandButton.text("-"); } else { subUl.hide(); expandButton.text("+"); } } parentUl.append(li); }); } renderChildren(data, ul); $("#chapter-tree-container").append(ul); } // 添加章节 function addChapter(clickedLi) { try { var newChapter = { id: Date.now(), name: "默认章节", children: [] }; var parentUl = clickedLi.parent('ul'); var parentChapter; if (parentUl.length) { var parentLi = parentUl.parent('li'); if (parentLi.length) { parentChapter = getChapterFromLi(parentLi); } else { // 顶级 ul 的情况 parentChapter = { children: initialData }; } } else { // 顶级 li 的情况 parentChapter = { children: initialData }; } if (!parentChapter.children) { parentChapter.children = []; } parentChapter.children.push(newChapter); renderTree(initialData); } catch (error) { console.error('Error in addChapter function:', error); } } // 添加子章节 function addSubChapter(parentNode) { try { var newChapter = { id: Date.now(), name: "默认子章节", children: [] }; if (!parentNode.children) { parentNode.children = []; } parentNode.children.push(newChapter); renderTree(initialData); } catch (error) { console.error('Error in addSubChapter function:', error); } } // 删除章节 function deleteChapter(node) { try { let parent; let parentArray; if (node.id === initialData[0].id) { initialData = []; } else { const findParent = (data) => { for (let i = 0; i < data.length; i++) { if (data[i].children) { for (let j = 0; j < data[i].children.length; j++) { if (data[i].children[j].id === node.id) { return { parent: data[i], index: j }; } } const result = findParent(data[i].children); if (result) { return result; } } } return null; }; const parentInfo = findParent(initialData); if (parentInfo) { parent = parentInfo.parent; parentArray = parent.children; parentArray.splice(parentInfo.index, 1); } } renderTree(initialData); } catch (error) { console.error('Error in deleteChapter function:', error); } } // 章节上移 function moveChapterUp(li) { try { var chapter = getChapterFromLi(li); if (!chapter) { return; } var parentArray = findContainingArray(chapter, initialData); if (!parentArray) { return; } var currentIndex = parentArray.indexOf(chapter); if (currentIndex > 0) { var temp = parentArray[currentIndex - 1]; parentArray[currentIndex - 1] = chapter; parentArray[currentIndex] = temp; renderTree(initialData); } } catch (error) { console.error('Error in moveChapterUp function:', error); } } // 章节下移 function moveChapterDown(li) { try { var chapter = getChapterFromLi(li); if (!chapter) { return; } var parentArray = findContainingArray(chapter, initialData); if (!parentArray) { return; } var currentIndex = parentArray.indexOf(chapter); if (currentIndex < parentArray.length - 1) { var temp = parentArray[currentIndex + 1]; parentArray[currentIndex + 1] = chapter; parentArray[currentIndex] = temp; renderTree(initialData); } } catch (error) { console.error('Error in moveChapterDown function:', error); } } function findContainingArray(target, data) { for (let i = 0; i < data.length; i++) { if (data[i].id === target.id) { return data; } if (data[i].children) { const subArray = findContainingArray(target, data[i].children); if (subArray) { return subArray; } } } return null; } function getChapterFromLi(li) { const chapterId = li.data('chapter-id'); return findChapterById(chapterId, initialData); } function findChapterById(id, data) { for (let i = 0; i < data.length; i++) { if (data[i].id === id) { return data[i]; } if (data[i].children) { const found = findChapterById(id, data[i].children); if (found) { return found; } } } return null; } // 初始化渲染 $(document).ready(function () { renderTree(initialData); }); </script> </body> </html>
复制
再次实现,两个tree 左右对比,左边的章节,选中关联的在右边tree2 默认出来
增加搜索按钮,动态折叠展示搜索的内容
增加一个layui 弹框展示无限极联动select 通过数组动态渲染,省市区联动或者章节联动
效果图:
<!-- 按钮,点击弹出多级联动select的弹框 --> <button id="openSelectDialog" class="layui-btn">打开多级联动选择弹框</button> <script> layui.use(['layer', 'jquery', 'form'], function(){ var layer = layui.layer; var $ = layui.jquery; var form = layui.form; var initialData = [ { id: 1, name: "1", sort: 1, children: [ { id: 2, name: "1-1", sort: 2, children: [ { id: 3, name: "1-1-1", sort: 3, checkbox: true, color: true }, { id: 4, name: "1-1-2", sort: 1 }, { id: 5, name: "1-1-3", sort: 2 } ] }, { id: 6, name: "少年有梦", sort: 1, children: [ { id: 7, name: "1-2-1", sort: 1, color: true }, { id: 8, name: "1-2-2", sort: 2, color: true }, { id: 9, name: "1-2-3", sort: 3 }, { id: 10, name: "1-2-4", sort: 4 } ] } ] }, { id: 11, name: "2", sort: 1, children: [ { id: 12, name: "2-1", sort: 2, children: [ { id: 13, name: "2-1-1", sort: 3, checkbox: true, color: true }, { id: 14, name: "2-1-2", sort: 1 }, { id: 15, name: "2-1-3", sort: 2 } ] }, { id: 16, name: "2-2", sort: 1, children: [ { id: 17, name: "2-2-1", sort: 1, color: true }, { id: 18, name: "2-2-2", sort: 2, color: true }, { id: 19, name: "2-2-3", sort: 3 }, { id: 20, name: "2-2-4", sort: 4 } ] } ] } ]; // 打开弹框的按钮点击事件 $('#openSelectDialog').on('click', function(){ // 弹出弹框 layer.open({ type: 1, area: ['400px', '500px'], title: '多级联动选择', content: '<div id="selectContainer"></div>', // 弹框内容容器 success: function(layero, index){ // 初始化多级联动select,根据数据结构中最深的层级来确定select的数量 var maxDepth = getMaxDepth(initialData); initSelect(initialData, '#selectContainer', maxDepth); form.render(); // 重新渲染表单 }, btn: ['确定', '取消'], yes: function (index, layero) { var result = []; var level1 = $("#select1").val()??0; var level2 = $("#select2").val()??0; var level3 = $("#select3").val()??0; var level4 = $("#select4").val()??0; var level5 = $("#select5").val()??0; var data = { id:chapterId, types:'upmove', level1:level1, level2:level2, level3:level3, level4:level4, level5:level5, type:2 }; $.ajax({ url: "../Chapters/chapters", dataType: "json", data: data, type: 'post', success: function(res) { if (res.code == 200) { layer.msg('移动完成'); layer.close(index); onLoadZTreewb(); } } }) }, btn2: function (index) { layer.close(index); return false; } }); }); // 获取数据结构中最深的层级 function getMaxDepth(data, currentDepth = 1){ var maxDepth = currentDepth; data.forEach(function(item){ if(item.children && item.children.length > 0){ var childDepth = getMaxDepth(item.children, currentDepth + 1); if(childDepth > maxDepth){ maxDepth = childDepth; } } }); return maxDepth; } // 初始化多级联动select的函数 function initSelect(data, containerSelector, maxDepth){ var selectContainer = $(containerSelector); // 根据最深的层级创建所有select for(var i = 1; i <= maxDepth; i++){ var selectHtml = '<select name="level'+i+'" class="layui-select level-select">'; selectHtml += '<option value="">请选择第'+i+'级分类</option>'; selectHtml += '</select>'; selectContainer.append(selectHtml); } // 为第一个select填充数据 var firstSelect = selectContainer.find('select[name="level1"]'); data.forEach(function(item){ firstSelect.append('<option value="'+item.id+'">'+item.name+'</option>'); }); // 绑定change事件,动态填充后续select selectContainer.on('change', '.level-select', function(){ var currentSelect = $(this); var currentIndex = currentSelect.attr('name').replace('level', ''); var nextSelect = selectContainer.find('select[name="level'+(parseInt(currentIndex) + 1)+'"]'); nextSelect.empty().append('<option value="">请选择第'+(parseInt(currentIndex) + 1)+'级分类</option>'); var selectedId = currentSelect.val(); if(selectedId){ var childrenData = findChildrenById(data, selectedId); if(childrenData && childrenData.length > 0){ childrenData.forEach(function(childItem){ nextSelect.append('<option value="'+childItem.id+'">'+childItem.name+'</option>'); }); } } // 清空当前select之后的所有select的选项 for(var i = parseInt(currentIndex) + 2; i <= maxDepth; i++){ selectContainer.find('select[name="level'+i+'"]').empty().append('<option value="">请选择第'+i+'级分类</option>'); } form.render(); // 重新渲染表单 }); // 根据id查找子数据的函数 function findChildrenById(data, id){ for(var i = 0; i < data.length; i++){ if(data[i].id == id){ return data[i].children; }else if(data[i].children && data[i].children.length > 0){ var result = findChildrenById(data[i].children, id); if(result){ return result; } } } return null; } } }); </script>
复制