From da149760cb74b6511cd0d1592eaee8c893a5997d Mon Sep 17 00:00:00 2001 From: yangchunlong <292345300@qq.com> Date: Wed, 23 Jul 2025 15:42:28 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=F0=9F=9A=80=20tabs=E5=8A=9F=E8=83=BD?= =?UTF-8?q?=E5=9F=BA=E6=9C=AC=E5=AE=8C=E6=88=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/components.d.ts | 3 + src/components/Editor/index.vue | 388 ++++++++++++++------ src/components/Editor/index2.vue | 393 ++++++++++++++------ src/components/Editor/index3333.vue | 370 +++++++++++++++++++ src/components/Editor/index444.vue | 446 +++++++++++++++++++++++ src/components/Editor/index5555.vue | 517 +++++++++++++++++++++++++++ src/components/Editor/quill-tabs.js | 84 +++++ src/components/Editor/quill-tabs1.js | 84 +++++ 8 files changed, 2058 insertions(+), 227 deletions(-) create mode 100644 src/components/Editor/index3333.vue create mode 100644 src/components/Editor/index444.vue create mode 100644 src/components/Editor/index5555.vue create mode 100644 src/components/Editor/quill-tabs.js create mode 100644 src/components/Editor/quill-tabs1.js diff --git a/src/components.d.ts b/src/components.d.ts index 85f38fb..0b3820a 100644 --- a/src/components.d.ts +++ b/src/components.d.ts @@ -62,6 +62,9 @@ declare module "vue" { IEpSearch: typeof import("~icons/ep/search")["default"]; IEpSwitchButton: typeof import("~icons/ep/switch-button")["default"]; Index2: typeof import("./components/Editor/index2.vue")["default"]; + Index3333: typeof import("./components/Editor/index3333.vue")["default"]; + Index444: typeof import("./components/Editor/index444.vue")["default"]; + Index5555: typeof import("./components/Editor/index5555.vue")["default"]; RouterLink: typeof import("vue-router")["RouterLink"]; RouterView: typeof import("vue-router")["RouterView"]; } diff --git a/src/components/Editor/index.vue b/src/components/Editor/index.vue index a0d3bf9..740bcbd 100644 --- a/src/components/Editor/index.vue +++ b/src/components/Editor/index.vue @@ -1,5 +1,5 @@ - diff --git a/src/components/Editor/index2.vue b/src/components/Editor/index2.vue index 218bd94..506b25d 100644 --- a/src/components/Editor/index2.vue +++ b/src/components/Editor/index2.vue @@ -1,5 +1,5 @@ - diff --git a/src/components/Editor/index3333.vue b/src/components/Editor/index3333.vue new file mode 100644 index 0000000..ff28764 --- /dev/null +++ b/src/components/Editor/index3333.vue @@ -0,0 +1,370 @@ + + + + + diff --git a/src/components/Editor/index444.vue b/src/components/Editor/index444.vue new file mode 100644 index 0000000..6640f73 --- /dev/null +++ b/src/components/Editor/index444.vue @@ -0,0 +1,446 @@ + + + + + diff --git a/src/components/Editor/index5555.vue b/src/components/Editor/index5555.vue new file mode 100644 index 0000000..5ad6288 --- /dev/null +++ b/src/components/Editor/index5555.vue @@ -0,0 +1,517 @@ + + + + + diff --git a/src/components/Editor/quill-tabs.js b/src/components/Editor/quill-tabs.js new file mode 100644 index 0000000..da6ec9c --- /dev/null +++ b/src/components/Editor/quill-tabs.js @@ -0,0 +1,84 @@ +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 = "quill-tabs"; + + constructor(domNode) { + super(domNode); + this.bindEvents(); + } + + static create(value) { + const node = super.create(value); + const tabs = value; + // 标签栏 + const tabList = document.createElement("div"); + tabList.className = "quill-tab-list"; + // 内容区 + const contentList = document.createElement("div"); + contentList.className = "quill-tab-content-list"; + // 生成标签和内容 + tabs.forEach((tab, index) => { + // 标签按钮 + const btn = document.createElement("button"); + btn.className = `quill-tab-button ${index === 0 ? "active" : ""}`; + btn.setAttribute("data-index", index); + btn.textContent = tab.title; + tabList.appendChild(btn); + + // 内容面板 + const panel = document.createElement("div"); + panel.className = `quill-tab-content ${index === 0 ? "active" : ""}`; + panel.setAttribute("data-index", index); + panel.innerHTML = tab.content; + contentList.appendChild(panel); + }); + + node.appendChild(tabList); + node.appendChild(contentList); + node.setAttribute("contenteditable", "false"); // 禁止直接编辑容器 + return node; + } + + bindEvents() { + // 事件委托,确保动态生成的元素也能触发 + this.domNode.addEventListener("click", e => { + const btn = e.target.closest(".quill-tab-button"); + if (btn) { + e.stopPropagation(); + const index = parseInt(btn.dataset.index, 10); + this.selectTab(index); + } + }); + } + + selectTab(index) { + const buttons = this.domNode.querySelectorAll(".quill-tab-button"); + const panels = this.domNode.querySelectorAll(".quill-tab-content"); + + buttons.forEach((btn, i) => btn.classList.toggle("active", i === index)); + panels.forEach((panel, i) => panel.classList.toggle("active", i === index)); + } + + static value(node) { + const tabs = []; + node.querySelectorAll(".quill-tab-button").forEach((btn, i) => { + tabs.push({ + title: btn.textContent, + content: node.querySelectorAll(".quill-tab-content")[i].innerHTML + }); + }); + return { tabs }; + } + + update(mutations, context) { + super.update(mutations, context); + this.bindEvents(); // 重新绑定事件 + } +} + +export default TabsBlot; diff --git a/src/components/Editor/quill-tabs1.js b/src/components/Editor/quill-tabs1.js new file mode 100644 index 0000000..da6ec9c --- /dev/null +++ b/src/components/Editor/quill-tabs1.js @@ -0,0 +1,84 @@ +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 = "quill-tabs"; + + constructor(domNode) { + super(domNode); + this.bindEvents(); + } + + static create(value) { + const node = super.create(value); + const tabs = value; + // 标签栏 + const tabList = document.createElement("div"); + tabList.className = "quill-tab-list"; + // 内容区 + const contentList = document.createElement("div"); + contentList.className = "quill-tab-content-list"; + // 生成标签和内容 + tabs.forEach((tab, index) => { + // 标签按钮 + const btn = document.createElement("button"); + btn.className = `quill-tab-button ${index === 0 ? "active" : ""}`; + btn.setAttribute("data-index", index); + btn.textContent = tab.title; + tabList.appendChild(btn); + + // 内容面板 + const panel = document.createElement("div"); + panel.className = `quill-tab-content ${index === 0 ? "active" : ""}`; + panel.setAttribute("data-index", index); + panel.innerHTML = tab.content; + contentList.appendChild(panel); + }); + + node.appendChild(tabList); + node.appendChild(contentList); + node.setAttribute("contenteditable", "false"); // 禁止直接编辑容器 + return node; + } + + bindEvents() { + // 事件委托,确保动态生成的元素也能触发 + this.domNode.addEventListener("click", e => { + const btn = e.target.closest(".quill-tab-button"); + if (btn) { + e.stopPropagation(); + const index = parseInt(btn.dataset.index, 10); + this.selectTab(index); + } + }); + } + + selectTab(index) { + const buttons = this.domNode.querySelectorAll(".quill-tab-button"); + const panels = this.domNode.querySelectorAll(".quill-tab-content"); + + buttons.forEach((btn, i) => btn.classList.toggle("active", i === index)); + panels.forEach((panel, i) => panel.classList.toggle("active", i === index)); + } + + static value(node) { + const tabs = []; + node.querySelectorAll(".quill-tab-button").forEach((btn, i) => { + tabs.push({ + title: btn.textContent, + content: node.querySelectorAll(".quill-tab-content")[i].innerHTML + }); + }); + return { tabs }; + } + + update(mutations, context) { + super.update(mutations, context); + this.bindEvents(); // 重新绑定事件 + } +} + +export default TabsBlot;