Browse Source

富文本编辑器可以嵌套元素

Michael Wang 4 years ago
parent
commit
3f6341072b

+ 1 - 1
src/components/VEditor/VEditor.vue

@@ -26,7 +26,7 @@ export default {
     placeholder: { type: String, default: "请输入..." },
     placeholder: { type: String, default: "请输入..." },
     value: {
     value: {
       type: String,
       type: String,
-      default: () => "{}",
+      default: () => `{}`,
     },
     },
   },
   },
   watch: {
   watch: {

+ 1 - 0
src/components/VEditor/changeMode.js

@@ -54,6 +54,7 @@ export function setDocMode(type, edt) {
     je.addEventListener("dblclick", () => {
     je.addEventListener("dblclick", () => {
       document.body.removeChild(je);
       document.body.removeChild(je);
       bToJSON = false;
       bToJSON = false;
+      edt.contentEditable = true;
     });
     });
   } else {
   } else {
     oContent = document.createRange();
     oContent = document.createRange();

+ 3 - 2
src/components/VEditor/components/VMenu.vue

@@ -1,11 +1,12 @@
 <template>
 <template>
   <div class="edit-menus" style="display: flex; gap: 10px;">
   <div class="edit-menus" style="display: flex; gap: 10px;">
-    <img
+    <!-- 由于v-model会re-render,这里无法redo了 -->
+    <!-- <img
       class="intLink"
       class="intLink"
       title="Undo"
       title="Undo"
       @click="execCommand('undo')"
       @click="execCommand('undo')"
       src="data:image/gif;base64,R0lGODlhFgAWAOMKADljwliE33mOrpGjuYKl8aezxqPD+7/I19DV3NHa7P///////////////////////yH5BAEKAA8ALAAAAAAWABYAAARR8MlJq7046807TkaYeJJBnES4EeUJvIGapWYAC0CsocQ7SDlWJkAkCA6ToMYWIARGQF3mRQVIEjkkSVLIbSfEwhdRIH4fh/DZMICe3/C4nBQBADs="
       src="data:image/gif;base64,R0lGODlhFgAWAOMKADljwliE33mOrpGjuYKl8aezxqPD+7/I19DV3NHa7P///////////////////////yH5BAEKAA8ALAAAAAAWABYAAARR8MlJq7046807TkaYeJJBnES4EeUJvIGapWYAC0CsocQ7SDlWJkAkCA6ToMYWIARGQF3mRQVIEjkkSVLIbSfEwhdRIH4fh/DZMICe3/C4nBQBADs="
-    />
+    /> -->
     <img
     <img
       class="intLink"
       class="intLink"
       title="Bold"
       title="Bold"

+ 25 - 6
src/components/VEditor/renderJSON.js

@@ -43,14 +43,33 @@ function renderBlock(block, inline) {
     //     })
     //     })
     // }
     // }
     if (block.param) {
     if (block.param) {
+      let uNode, bNode, iNode;
       if (block.param.underline) {
       if (block.param.underline) {
-        node = document.createElement("u");
-      } else if (block.param.bold) {
-        node = document.createElement("b");
-      } else if (block.param.italic) {
-        node = document.createElement("i");
+        uNode = document.createElement("u");
       }
       }
-      node.textContent = block.value;
+      if (block.param.bold) {
+        bNode = document.createElement("b");
+      }
+      if (block.param.italic) {
+        iNode = document.createElement("i");
+      }
+      // 将不为空的元素依次append
+      node = [uNode, bNode, iNode]
+        .filter((v) => v)
+        .reduceRight((p, c) => {
+          c.appendChild(p);
+          return c;
+        });
+
+      let childNode = node;
+
+      for (let i = 0; i < 3; i++) {
+        if (childNode && childNode.hasChildNodes()) {
+          childNode = childNode.childNodes[0];
+        }
+      }
+
+      childNode.textContent = block.value;
     } else {
     } else {
       node = document.createTextNode(block.value);
       node = document.createTextNode(block.value);
     }
     }

+ 73 - 8
src/components/VEditor/toJSON.js

@@ -1,3 +1,5 @@
+import { flattenDeep } from "lodash-es";
+
 let res = [];
 let res = [];
 
 
 /**
 /**
@@ -11,17 +13,19 @@ export function toJSON(editor) {
     toSection(e);
     toSection(e);
   }
   }
   const newRes = [];
   const newRes = [];
-  for (let s of res) {
-    let newS = [];
-    for (const b of s) {
+  for (let section of res) {
+    section = flattenElement(section);
+
+    let newSection = [];
+    for (const b of section) {
       const toB = toBlock(b);
       const toB = toBlock(b);
-      if (toB) newS.push(toB);
+      if (toB) newSection.push(toB);
     }
     }
-    if (newS.length === 0) {
+    if (newSection.length === 0) {
       // 空行特殊处理
       // 空行特殊处理
-      newS = [{ type: "text", value: " ", param: null }];
+      newSection = [{ type: "text", value: " ", param: null }];
     }
     }
-    newRes.push({ blocks: newS });
+    newRes.push({ blocks: newSection });
   }
   }
   res = { sections: newRes };
   res = { sections: newRes };
   // console.log(res);
   // console.log(res);
@@ -44,6 +48,49 @@ function toSection(e) {
   }
   }
 }
 }
 
 
+/**
+ *
+ * @param {Node[]} section
+ */
+function flattenElement(section) {
+  section = [...section];
+  // console.log(section);
+  for (let i = 0; i < section.length; i++) {
+    if (section[i].hasChildNodes()) {
+      section[i] = [...section[i].childNodes];
+
+      for (let j = 0; j < section[i].length; j++) {
+        if (section[i][j].hasChildNodes()) {
+          section[i][j] = [...section[i][j].childNodes];
+          for (let k = 0; k < section[i][j].length; k++) {
+            if (section[i][j][k].hasChildNodes()) {
+              section[i][j][k] = [...section[i][j][k].childNodes];
+            }
+          }
+        }
+      }
+    }
+  }
+  section = flattenDeep(section);
+  return section;
+}
+
+/**
+ *
+ * @param {Node} e
+ * @param {String} tag
+ */
+function checkAncestorElementTag(e, tag) {
+  for (let i = 0; i < 3; i++) {
+    if (e.parentElement.nodeName === tag) {
+      return true;
+    } else {
+      e = e.parentElement;
+    }
+  }
+  return false;
+}
+
 /**
 /**
  *
  *
  * @param {Node} e
  * @param {Node} e
@@ -53,7 +100,25 @@ function toBlock(e) {
   if (e.nodeType === Node.TEXT_NODE) {
   if (e.nodeType === Node.TEXT_NODE) {
     block.type = "text";
     block.type = "text";
     block.value = e.textContent;
     block.value = e.textContent;
-    block.param = null;
+    block.param = {};
+    // block.param.italic =
+    //   window.getComputedStyle(e.parentElement).fontStyle === "italic";
+    // block.param.bold =
+    //   window.getComputedStyle(e.parentElement).fontWeight > 400;
+    // block.param.underline =
+    //   window.getComputedStyle(e.parentElement).textDecorationLine ===
+    //   "underline";
+
+    block.param.italic = checkAncestorElementTag(e, "I");
+    block.param.bold = checkAncestorElementTag(e, "B");
+    block.param.underline = checkAncestorElementTag(e, "U");
+
+    // console.log(block.param);
+    const allFalse = Object.values(block.param).every((v) => v === false);
+    // console.log(allFalse);
+    if (allFalse) {
+      block.param = null;
+    }
     // } else if (e.nodeType == Node.ELEMENT_NODE && e.nodeName === "SPAN") {
     // } else if (e.nodeType == Node.ELEMENT_NODE && e.nodeName === "SPAN") {
     //   block.type = "text";
     //   block.type = "text";
     //   block.value = e.textContent;
     //   block.value = e.textContent;