import { Quill } from "@vueup/vue-quill"; const BlockEmbed = Quill.import("blots/block/embed"); class TabsBlot extends BlockEmbed { static blotName = "tabs"; static tagName = "div"; static className = "m-quill-tabs"; constructor(domNode) { super(domNode); this.bindEvents(); this.bindDeleteKeyEvent(); // 绑定删除键事件 } static create(value) { const node = super.create(value); const tabs = value || []; // 主容器样式 node.setAttribute( "style", ` margin: 15px 0; overflow: hidden; border-radius: 4px; position: relative; ` ); // 标签栏滚动容器 - 优化高度计算 const tabScrollContainer = document.createElement("div"); tabScrollContainer.className = "m-quill-tab-scroll-container"; tabScrollContainer.setAttribute( "style", ` -webkit-overflow-scrolling: touch; scrollbar-width: none; -ms-overflow-style: none; overflow-y: hidden; overflow-x: auto; height: auto; ` ); // height: auto; /* 自动高度 */ // Chrome, Safari 隐藏滚动条 tabScrollContainer.style.overflow = "auto"; tabScrollContainer.style.webkitOverflowScrolling = "touch"; tabScrollContainer.style.scrollbarWidth = "none"; tabScrollContainer.style.msOverflowStyle = "none"; // 标签栏 - 保持原有样式不变 const tabList = document.createElement("div"); tabList.className = "m-quill-tab-list"; tabList.setAttribute( "style", ` display: flex; border-bottom: 2px solid #dddddd; min-width: max-content; /* 确保内容撑开容器 */ ` ); // 生成标签按钮 - 优化内边距计算 tabs.forEach((tab, index) => { const btn = document.createElement("button"); btn.className = `m-quill-tab-button`; btn.setAttribute("data-index", index); btn.textContent = tab.title; btn.setAttribute( "style", ` font-weight: 900; color: #8f9099; cursor: pointer; background: transparent; border: none; padding-bottom:14px; margin-right:3%; /* 增大间距 */ cursor: pointer; white-space:nowrap; font-size:16px; ${index === 0 ? "color: #1f2635; border-bottom: 3px solid #537CD8;font-size:16px;" : ""} ` ); tabList.appendChild(btn); }); // 编辑按钮 - 保持原有样式不变 const editBtn = document.createElement("button"); editBtn.className = "m-quill-tab-edit-btn"; editBtn.innerHTML = "编辑"; editBtn.setAttribute("data-action", "edit"); editBtn.setAttribute( "style", ` padding: 10px; margin-left: auto; color: #606266; cursor: pointer; width:60px; padding-left:20px; background: transparent; border: none; display:block; ` ); tabList.appendChild(editBtn); // 内容区 - 保持原有样式不变 const contentList = document.createElement("div"); contentList.className = "m-quill-tab-content-list"; contentList.setAttribute( "style", ` padding: 15px; ` ); // 生成内容面板 - 保持原有样式不变 tabs.forEach((tab, index) => { const panel = document.createElement("div"); panel.className = `m-quill-tab-content`; panel.setAttribute("data-index", index); panel.innerHTML = tab.content; panel.setAttribute( "style", ` display: ${index === 0 ? "block" : "none"}; min-height: 50px; ` ); panel.contentEditable = "false"; contentList.appendChild(panel); }); // 组装结构 tabScrollContainer.appendChild(tabList); node.appendChild(tabScrollContainer); // 滚动容器添加到主节点 node.appendChild(contentList); // 内容区 // 标签页切换逻辑 - 保持原有逻辑不变 const scriptTag = document.createElement("script"); scriptTag.textContent = ` (function() { const container = document.currentScript.parentElement; const isAdmin = window.location.pathname.includes('/admin'); const editBtn1 = container.querySelector('.m-quill-tab-edit-btn'); // 仅在非管理系统(文章网站)隐藏编辑按钮,管理系统保持显示 if (!isAdmin && editBtn1) { editBtn1.style.display = 'none'; // 文章网站隐藏按钮 } else if (isAdmin && editBtn1) { editBtn1.style.display = 'block'; // 管理系统强制显示按钮 editBtn1.style.width='60px'; editBtn1.style.minWidth='60px'; } // 非管理系统才执行标签切换逻辑(管理系统不执行) if (!isAdmin) { const tabButtons = container.querySelectorAll('.m-quill-tab-button:not([data-action])'); const contentPanels = container.querySelectorAll('.m-quill-tab-content'); tabButtons.forEach(btn => { btn.addEventListener('click', function() { const index = parseInt(this.dataset.index); tabButtons.forEach((b, i) => { b.setAttribute('style', \` font-weight: 900; cursor: pointer; background: transparent; font-size:16px; white-space:nowrap; padding-bottom:14px; margin-right:3%; color: #8f9099; border: none; \${i === index ? 'color: #1f2635;border-bottom: 3px solid #537CD8;font-size:16px;' : '' } \`); }); contentPanels.forEach((panel, i) => { panel.style.display = i === index ? 'block' : 'none'; }); // 添加滚动逻辑到自执行函数中 const scrollContainer = container.querySelector(".m-quill-tab-scroll-container"); const activeBtn = tabButtons[index]; if (scrollContainer && activeBtn) { activeBtn.scrollIntoView({ behavior: "smooth", block: "nearest", inline: "center" }); } }); }); } })(); `; node.appendChild(scriptTag); node.setAttribute("contenteditable", "false"); return node; } bindEvents() { if (!this.eventBoundElements) { this.eventBoundElements = new WeakMap(); } // 编辑按钮事件 - 保持原有逻辑不变 const editBtn = this.domNode.querySelector(".m-quill-tab-edit-btn"); if (editBtn) { editBtn.removeEventListener("click", this.handleEditClick); this.handleEditClick = e => { e.stopPropagation(); this.domNode.dispatchEvent( new CustomEvent("edit-tabs", { bubbles: true, detail: { blot: this } }) ); }; editBtn.addEventListener("click", this.handleEditClick); } // 标签切换事件 - 保持原有逻辑不变 const tabButtons = this.domNode.querySelectorAll(".m-quill-tab-button:not([data-action])"); tabButtons.forEach(btn => { if (!this.eventBoundElements.has(btn)) { btn.addEventListener("click", () => { const index = parseInt(btn.dataset.index, 10); this.selectTab(index); }); this.eventBoundElements.set(btn, true); } }); } // 增强版删除键处理 - 保持原有逻辑不变 bindDeleteKeyEvent() { this.domNode.addEventListener( "keydown", e => { if (e.key === "Backspace" || e.key === "Delete") { const selection = window.getSelection(); if (!selection.rangeCount) return; const range = selection.getRangeAt(0); const parentBlock = this.domNode; const isInside = parentBlock.contains(range.commonAncestorContainer); if (!isInside) { e.preventDefault(); return; } if ( range.startContainer === parentBlock && range.endContainer === parentBlock && range.startOffset === 0 && range.endOffset >= parentBlock.childNodes.length ) { e.preventDefault(); } } }, true ); const tabList = this.domNode.querySelector(".m-quill-tab-list"); if (tabList) { tabList.querySelectorAll("*").forEach(el => { el.contentEditable = "false"; }); } } selectTab(index) { const buttons = this.domNode.querySelectorAll(".m-quill-tab-button:not([data-action])"); const panels = this.domNode.querySelectorAll(".m-quill-tab-content"); // 保持原有样式逻辑不变 buttons.forEach((btn, i) => { btn.setAttribute( "style", ` font-weight: 900; cursor: pointer; background: transparent; border: none; font-size:16px; padding-bottom:14px; margin-right:3%; white-space:nowrap; color: #8f9099; border-bottom: 3px solid transparent; ${i === index ? "color: #1f2635;border-bottom: 3px solid #537CD8;font-size:16px;" : ""} ` ); }); panels.forEach((panel, i) => { panel.style.display = i === index ? "block" : "none"; }); // 滚动到当前选中的标签 const scrollContainer = this.domNode.querySelector(".m-quill-tab-scroll-container"); const activeBtn = buttons[index]; if (scrollContainer && activeBtn) { activeBtn.scrollIntoView({ behavior: "smooth", block: "nearest", inline: "center" }); } } static value(node) { const tabs = []; const buttons = node.querySelectorAll(".m-quill-tab-button:not([data-action])"); const panels = node.querySelectorAll(".m-quill-tab-content"); buttons.forEach((btn, i) => { tabs.push({ title: btn.textContent, content: panels[i]?.innerHTML || "" }); }); return tabs; } update(mutations, context) { super.update(mutations, context); const scriptTag = this.domNode.querySelector("script"); if (scriptTag) { const newScript = document.createElement("script"); newScript.textContent = scriptTag.textContent; scriptTag.parentNode.replaceChild(newScript, scriptTag); } this.bindEvents(); this.bindDeleteKeyEvent(); } getValue() { return TabsBlot.value(this.domNode); } // 更新标签页数据 - 保持原有逻辑不变 updateContents(tabs) { const contentList = this.domNode.querySelector(".m-quill-tab-content-list"); const tabList = this.domNode.querySelector(".m-quill-tab-list"); const editBtn = this.domNode.querySelector(".m-quill-tab-edit-btn"); Array.from(tabList.children).forEach(child => { if (!child.classList.contains("m-quill-tab-edit-btn")) { child.remove(); } }); contentList.innerHTML = ""; tabs.forEach((tab, index) => { const btn = document.createElement("button"); btn.className = "m-quill-tab-button"; btn.setAttribute("data-index", index); btn.textContent = tab.title; btn.setAttribute( "style", ` font-weight: 900; color: #8f9099; cursor: pointer; background: transparent; border: none; padding-bottom:14px; margin-right:3%; white-space:nowrap; font-size:16px; ${index === 0 ? "color: #1f2635; border-bottom: 3px solid #537CD8;font-size:16px;" : ""} ` ); tabList.insertBefore(btn, editBtn); const panel = document.createElement("div"); panel.className = "m-quill-tab-content"; panel.setAttribute("data-index", index); panel.innerHTML = tab.content; panel.setAttribute( "style", ` display: ${index === 0 ? "block" : "none"}; min-height: 50px; ` ); panel.contentEditable = "false"; contentList.appendChild(panel); }); this.bindEvents(); } } export default TabsBlot;