From 887851419d98568a815de95a6a96fbdfb3dfcf38 Mon Sep 17 00:00:00 2001
From: qx <1084500556@qq.com>
Date: 星期四, 26 六月 2025 15:21:22 +0800
Subject: [PATCH] qx

---
 src/views/ceshi.vue |  198 +++++++++++++++++++++++++++++++++++++++++++++++++
 1 files changed, 198 insertions(+), 0 deletions(-)

diff --git a/src/views/ceshi.vue b/src/views/ceshi.vue
new file mode 100644
index 0000000..4a8f64d
--- /dev/null
+++ b/src/views/ceshi.vue
@@ -0,0 +1,198 @@
+<template>
+  <div>
+    <h3>鍓嶇 Word 缂栬緫鍣紙TinyMCE锛�</h3>
+    <editor v-model="content" :init="editorInit" :api-key="apiKey"></editor>
+    <div class="actions">
+      <button @click="exportToWord">瀵煎嚭 Word</button>
+      <button @click="exportToPDF">瀵煎嚭 PDF</button>
+    </div>
+  </div>
+</template>
+
+<script>
+import Editor from '@tinymce/tinymce-vue';
+import { Document, Packer, Paragraph, TextRun, ImageRun, Table as DocxTable, TableRow as DocxTableRow, TableCell as DocxTableCell } from 'docx';
+import { saveAs } from 'file-saver';
+import jsPDF from 'jspdf';
+import html2canvas from 'html2canvas';
+
+export default {
+  components: { Editor },
+  data() {
+    return {
+      apiKey: '3ceaxd5ckw4te35xj38vj3p5rmmeyv0x8pq2yrr92rwdiqzp', // 鎮ㄧ殑 TinyMCE API 瀵嗛挜
+      content: `
+
+      `,
+      editorInit: {
+        height: 500,
+        menubar: true,
+        plugins: [
+          'advlist autolink lists link image charmap print preview anchor',
+          'searchreplace visualblocks code fullscreen',
+          'insertdatetime media table paste code help wordcount'
+        ],
+        toolbar:
+          'undo redo | formatselect | bold italic | alignleft aligncenter alignright alignjustify | bullist numlist outdent indent | image table | removeformat | help',
+        images_upload_handler: (blobInfo, success, failure) => {
+          const reader = new FileReader();
+          reader.onload = () => success(reader.result);
+          reader.onerror = () => failure('鍥剧墖涓婁紶澶辫触');
+          reader.readAsDataURL(blobInfo.blob());
+        },
+        content_style: 'body { font-family: SimSun, Arial; font-size: 14px; }',
+        table_default_attributes: { border: 1 },
+        readonly: false,
+        language: 'zh_CN', // 灏濊瘯 zh-CN
+      },
+    };
+  },
+  methods: {
+    exportToWord() {
+      this.parseHtmlToDocx(this.content).then((elements) => {
+        const doc = new Document({
+          sections: [{ properties: {}, children: elements }],
+        });
+
+        Packer.toBlob(doc).then((blob) => {
+          saveAs(blob, '鏂囨。.docx');
+        }).catch((err) => {
+          console.error('鐢熸垚 Word 澶辫触锛�', err);
+        });
+      }).catch((err) => {
+        console.error('瑙f瀽 HTML 澶辫触锛�', err);
+      });
+    },
+    parseHtmlToDocx(html) {
+      return new Promise((resolve, reject) => {
+        const parser = new DOMParser();
+        const doc = parser.parseFromString(html, 'text/html');
+        const elements = [];
+
+        const processNode = (node, callback) => {
+          if (node.nodeType === Node.ELEMENT_NODE) {
+            if (node.tagName === 'P') {
+              const textRuns = Array.from(node.childNodes).map((child) => {
+                if (child.nodeType === Node.TEXT_NODE) {
+                  return new TextRun({ text: child.textContent || '' });
+                } else if (child.tagName === 'B') {
+                  return new TextRun({ text: child.textContent || '', bold: true });
+                } else if (child.tagName === 'I') {
+                  return new TextRun({ text: child.textContent || '', italics: true });
+                } else if (child.tagName === 'IMG') {
+                  this.getImageBase64FromUrl(child.src).then((base64) => {
+                    callback(null, new ImageRun({
+                      data: base64,
+                      transformation: { width: 200, height: 200 },
+                    }));
+                  }).catch((err) => {
+                    console.error('鍥剧墖鍔犺浇澶辫触锛�', err);
+                    callback(null, new TextRun(`[鍥剧墖: ${child.alt || '鍥惧儚'}]`));
+                  });
+                  return null;
+                }
+                return new TextRun({ text: child.textContent || '' });
+              }).filter(Boolean);
+              if (textRuns.length) {
+                elements.push(new Paragraph({ children: textRuns }));
+              }
+            } else if (node.tagName === 'H1') {
+              elements.push(new Paragraph({ text: node.textContent || '', heading: 'Heading1' }));
+            } else if (node.tagName === 'UL' || node.tagName === 'OL') {
+              Array.from(node.children).forEach((li) => {
+                elements.push(
+                  new Paragraph({
+                    text: li.textContent || '',
+                    bullet: node.tagName === 'UL' ? { level: 0 } : { level: 0, number: true },
+                  })
+                );
+              });
+            } else if (node.tagName === 'TABLE') {
+              const rows = Array.from(node.querySelectorAll('tr')).map((tr) => {
+                const cells = Array.from(tr.querySelectorAll('td, th')).map((cell) => {
+                  return new DocxTableCell({
+                    children: [new Paragraph(cell.textContent || '')],
+                  });
+                });
+                return new DocxTableRow({ children: cells });
+              });
+              elements.push(new DocxTable({ rows }));
+            }
+            Array.from(node.children).forEach((child) => processNode(child, callback));
+          }
+          callback(null);
+        };
+
+        const nodes = Array.from(doc.body.childNodes);
+        let completed = 0;
+        const checkCompletion = () => {
+          completed++;
+          if (completed === nodes.length) {
+            resolve(elements);
+          }
+        };
+
+        if (nodes.length === 0) {
+          resolve(elements);
+        } else {
+          nodes.forEach((node) => {
+            processNode(node, checkCompletion);
+          });
+        }
+      });
+    },
+    getImageBase64FromUrl(url) {
+      return new Promise((resolve, reject) => {
+        fetch(url, { mode: 'cors' })
+          .then((response) => {
+            if (!response.ok) throw new Error('缃戠粶鍥剧墖鍔犺浇澶辫触');
+            return response.blob();
+          })
+          .then((blob) => {
+            const reader = new FileReader();
+            reader.onloadend = () => resolve(reader.result.split(',')[1]);
+            reader.onerror = () => reject(new Error('鍥剧墖璇诲彇澶辫触'));
+            reader.readAsDataURL(blob);
+          })
+          .catch((err) => reject(err));
+      });
+    },
+    exportToPDF() {
+      const contentElement = document.createElement('div');
+      contentElement.innerHTML = this.content;
+      contentElement.style.padding = '10px';
+      document.body.appendChild(contentElement);
+
+      html2canvas(contentElement, { scale: 2 })
+        .then((canvas) => {
+          const doc = new jsPDF();
+          const imgData = canvas.toDataURL('image/png');
+          doc.addImage(imgData, 'PNG', 10, 10, 190, 0);
+          doc.save('鏂囨。.pdf');
+        })
+        .catch((err) => {
+          console.error('鐢熸垚 PDF 澶辫触锛�', err);
+          document.body.removeChild(contentElement);
+        });
+    },
+  },
+};
+</script>
+
+<style>
+.actions {
+  margin-top: 10px;
+}
+.actions button {
+  padding: 8px 16px;
+  margin-right: 10px;
+  background-color: #409eff;
+  color: white;
+  border: none;
+  border-radius: 4px;
+  cursor: pointer;
+}
+.actions button:hover {
+  background-color: #66b1ff;
+}
+</style>
\ No newline at end of file

--
Gitblit v1.8.0