clipboard.js 7.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280
  1. import { insertTagToEditor } from "./utils";
  2. // import { randomCode } from "../../utils/utils";
  3. let _this = null;
  4. export function dataUrlToBlob(base64Buf) {
  5. console.log(base64Buf);
  6. const bufs = base64Buf.split(".");
  7. const mime = bufs[0].match(/:(.*?);/)[1];
  8. const bstr = window.atob(bufs[1]);
  9. const len = bstr.length;
  10. const u8arr = new Uint8Array(len);
  11. for (let i = 0; i < len; i++) {
  12. u8arr[i] = bstr.charCodeAt(i);
  13. }
  14. return new Blob([u8arr], { type: mime });
  15. }
  16. function fileToBase64(file) {
  17. return new Promise((resolve, reject) => {
  18. const reader = new FileReader();
  19. reader.readAsDataURL(file);
  20. reader.onload = () => resolve(reader.result);
  21. reader.onerror = (error) => reject(error);
  22. });
  23. }
  24. function getDataUrlFromImageUrl(url) {
  25. const img = new Image();
  26. img.setAttribute("crossorigin", "anonymous");
  27. img.src = url;
  28. return new Promise((resolve, reject) => {
  29. const canvas = document.createElement("canvas");
  30. const ctx = canvas.getContext("2d");
  31. canvas.width = img.width;
  32. canvas.height = img.height;
  33. // console.dir(img);
  34. img.onload = function () {
  35. ctx.drawImage(img, 0, 0);
  36. resolve(canvas.toDataURL("image/png"));
  37. };
  38. img.onerror = function (err) {
  39. reject(err);
  40. };
  41. });
  42. }
  43. function dataItemGetAsString(dataTransferItem) {
  44. return new Promise((resolve, reject) => {
  45. try {
  46. dataTransferItem.getAsString((s) => {
  47. resolve(s);
  48. });
  49. } catch (error) {
  50. reject(error);
  51. }
  52. });
  53. }
  54. // function checkHtmlIsFromOffice(htmlDom) {
  55. // const meta = htmlDom.querySelector("meta[name='Originator']");
  56. // return meta && meta.getAttribute("content").includes("Microsoft Word");
  57. // }
  58. function checkDomIsBlock(dom) {
  59. if (dom.nodeType === Node.ELEMENT_NODE) {
  60. const disPlay = window.getComputedStyle(dom).display;
  61. if (disPlay) {
  62. return disPlay === "block";
  63. } else {
  64. const blockDomNames = [
  65. "div",
  66. "p",
  67. "h1",
  68. "h2",
  69. "h3",
  70. "h4",
  71. "h5",
  72. "h6",
  73. "ul",
  74. "ol",
  75. "table",
  76. "tr",
  77. ];
  78. return blockDomNames.includes(dom.tagName.toLowerCase());
  79. }
  80. }
  81. }
  82. function checkDomHasTextContent(content) {
  83. let cont = content.replace(/\n/g, "");
  84. return !!cont;
  85. }
  86. function checkDomIsUnvalid(dom) {
  87. const unvalidFunc = [
  88. (node) =>
  89. node.tagName &&
  90. ["meta", "link", "script", "style", "title"].includes(
  91. node.tagName.toLowerCase()
  92. ),
  93. (node) => node.nodeType === Node.COMMENT_NODE,
  94. ];
  95. return unvalidFunc.some((unfunc) => unfunc(dom));
  96. }
  97. function getHtmlValidDom(htmlDom, checkBlock) {
  98. let doms = [];
  99. let curBlockDoms = [];
  100. const findDom = (nodeList) => {
  101. nodeList.forEach((node) => {
  102. if (checkDomIsUnvalid(node)) return;
  103. if (checkBlock && checkDomIsBlock(node) && curBlockDoms.length) {
  104. doms.push(curBlockDoms);
  105. curBlockDoms = [];
  106. }
  107. if (node.childNodes.length) {
  108. findDom(node.childNodes);
  109. return;
  110. }
  111. if (
  112. (node.tagName && node.tagName.toLowerCase() === "img") ||
  113. (node.nodeType === Node.TEXT_NODE &&
  114. checkDomHasTextContent(node.textContent))
  115. ) {
  116. curBlockDoms.push(node);
  117. }
  118. });
  119. };
  120. findDom(htmlDom.childNodes);
  121. if (curBlockDoms.length) {
  122. doms.push(curBlockDoms);
  123. curBlockDoms = [];
  124. }
  125. console.log(doms);
  126. return doms;
  127. }
  128. // function getOfficeHtmlValidDom(htmlDom) {
  129. // let doms = [];
  130. // htmlDom.querySelectorAll("p").forEach((pDom) => {
  131. // const elems = getHtmlValidDom(pDom, false);
  132. // if (elems.length) doms.push(...elems);
  133. // });
  134. // return doms;
  135. // }
  136. async function pasteHtmlItem(htmlItem) {
  137. const cont = await dataItemGetAsString(htmlItem).catch((e) => {
  138. console.log(e);
  139. });
  140. if (!cont) return;
  141. const divDom = document.createElement("div");
  142. divDom.innerHTML = cont;
  143. console.log(divDom);
  144. const groups = getHtmlValidDom(divDom, true);
  145. for (let i = 0; i < groups.length; i++) {
  146. for (let j = 0; j < groups[i].length; j++) {
  147. const element = groups[i][j];
  148. if (element.tagName && element.tagName.toLowerCase() === "img") {
  149. await pasteImageDom(element);
  150. } else {
  151. document.execCommand("insertText", false, element.textContent);
  152. }
  153. }
  154. document.execCommand("insertParagraph");
  155. }
  156. }
  157. async function pasteImageDom(imgDom) {
  158. console.log(imgDom);
  159. const src = imgDom.getAttribute("src");
  160. if (!src) return;
  161. let attributes = {};
  162. if (imgDom.getAttribute("width"))
  163. attributes.width = imgDom.getAttribute("width");
  164. if (imgDom.getAttribute("height"))
  165. attributes.height = imgDom.getAttribute("height");
  166. if (src.includes("base64")) {
  167. insertTagToEditor(null, "IMG", src, attributes);
  168. } else if (
  169. src.includes("http://") ||
  170. src.includes("https://") ||
  171. src.includes("file:///")
  172. ) {
  173. const dataUrl = await getDataUrlFromImageUrl(src).catch(() => {});
  174. // console.log(dataUrl);
  175. if (!dataUrl) return;
  176. insertTagToEditor(null, "IMG", dataUrl, attributes);
  177. } else {
  178. console.log("unknown image url");
  179. }
  180. }
  181. async function pasteImage(file, attributes) {
  182. if (file.size > _this.maxImageSize) {
  183. _this.$message(`单张图片超过限制,最大为 ${_this.maxImageSize / 1024} KB.`);
  184. return;
  185. }
  186. // 默认转base64;
  187. const srcBase64 = await fileToBase64(file);
  188. insertTagToEditor(null, "IMG", srcBase64, attributes);
  189. }
  190. async function pasteText(textItem) {
  191. const cont = await dataItemGetAsString(textItem).catch((e) => {
  192. console.log(e);
  193. });
  194. if (!cont) return;
  195. document.execCommand("insertText", false, cont);
  196. }
  197. function filterItem(dataItems, filterFunc) {
  198. let data = [];
  199. for (let index = 0; index < dataItems.length; index++) {
  200. if (filterFunc(dataItems[index])) data.push(dataItems[index]);
  201. }
  202. return data;
  203. }
  204. export async function pasteHandle(event) {
  205. _this = this;
  206. // 禁止默认粘贴
  207. event.preventDefault();
  208. const clipboard = event.clipboardData;
  209. const htmlItems = filterItem(
  210. clipboard.items,
  211. (item) => item.kind == "string" && item.type.match("^text/html")
  212. );
  213. if (htmlItems.length) {
  214. console.log("... paste: html ");
  215. for (let index = 0; index < htmlItems.length; index++) {
  216. await pasteHtmlItem(htmlItems[index]);
  217. }
  218. _this.$refs.editor.dispatchEvent(new Event("input"));
  219. return;
  220. }
  221. const fileItems = filterItem(
  222. clipboard.items,
  223. (item) => item.kind == "file" && item.type.match("^image/")
  224. );
  225. if (fileItems.length) {
  226. console.log("... paste: file ");
  227. for (let index = 0; index < fileItems.length; index++) {
  228. const file = fileItems[index].getAsFile();
  229. await pasteImage(file);
  230. }
  231. _this.$refs.editor.dispatchEvent(new Event("input"));
  232. return;
  233. }
  234. for (var i = 0; i < clipboard.items.length; i++) {
  235. const clipboardItem = clipboard.items[i];
  236. if (
  237. clipboardItem.kind == "string" &&
  238. clipboardItem.type.match("^text/plain")
  239. ) {
  240. console.log("... paste: text ");
  241. await pasteText(clipboardItem);
  242. } else {
  243. console.log("... paste: other ");
  244. }
  245. }
  246. }