提交
This commit is contained in:
@@ -363,6 +363,7 @@ const en = {
|
||||
typesettingType1: 'Vertical A4',
|
||||
exportWord: 'Export Word',
|
||||
exportImg: 'Export PNG',
|
||||
PaperRotation: 'Paper Rotation',
|
||||
},
|
||||
|
||||
}
|
||||
|
||||
@@ -356,6 +356,7 @@ const zh = {
|
||||
typesettingType1: '竖向 A4',
|
||||
exportWord: '导出 Word',
|
||||
exportImg: '导出 图片',
|
||||
PaperRotation: '纸张方向',
|
||||
},
|
||||
}
|
||||
|
||||
|
||||
@@ -107,6 +107,9 @@
|
||||
<i class="el-icon-document-add"></i>
|
||||
</b>
|
||||
</p>
|
||||
<p>
|
||||
<common-drag-word></common-drag-word>
|
||||
</p>
|
||||
<div class="unfetteredBox">
|
||||
<div
|
||||
:style="item.isHidden ? 'opacity:0.2' : 'opacity:1'"
|
||||
|
||||
BIN
src/components/page/components/Tinymce/img/bit_bug.png
Normal file
BIN
src/components/page/components/Tinymce/img/bit_bug.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 38 KiB |
@@ -6,10 +6,10 @@
|
||||
<script>
|
||||
import { string } from 'html-docx-js/dist/html-docx';
|
||||
import htmlDocx from 'html-docx-js/dist/html-docx.js';
|
||||
import { Document, Packer, PageOrientation, Paragraph, TextRun } from "docx"; // 引入 docx.js
|
||||
import { Document, Packer, PageOrientation, Paragraph, TextRun } from 'docx'; // 引入 docx.js
|
||||
import html2canvas from 'html2canvas';
|
||||
const toolbar =
|
||||
'undo redo | formatselect | bold italic | forecolor |subscript superscript|table tabledelete | |spacer customButton |customButtonExportWord |customButtonExportImg | pageOrientation';
|
||||
'uploadWord|undo redo | formatselect | bold italic | forecolor |subscript superscript|table tabledelete |customButtonExportWord |customButtonExportImg |customDropdown | clearButton';
|
||||
const tableStyle = ` b span{
|
||||
font-weight: bold !important;
|
||||
}
|
||||
@@ -128,6 +128,39 @@ export default {
|
||||
data() {
|
||||
return {
|
||||
typesettingType: 1,
|
||||
typesettingTypeOptions: [
|
||||
{
|
||||
label: this.$t('commonTable.typesettingType1'),
|
||||
value: 1,
|
||||
orientation: 'portrait',
|
||||
pageSize: {
|
||||
width: 11906,
|
||||
height: 16976
|
||||
},
|
||||
pageMargins: {
|
||||
top: 1440,
|
||||
bottom: 1440,
|
||||
left: 1084,
|
||||
right: 1084
|
||||
}
|
||||
},
|
||||
// 1 cm = 144 Twip 上边距(2.54 cm) = 2.54 × 144 = 365.76 Twip
|
||||
{
|
||||
label: this.$t('commonTable.typesettingType2'),
|
||||
value: 2,
|
||||
orientation: 'landscape',
|
||||
pageSize: {
|
||||
width: 16976,
|
||||
height: 11906
|
||||
},
|
||||
pageMargins: {
|
||||
top: 1440,
|
||||
bottom: 1440,
|
||||
left: 1084,
|
||||
right: 1084
|
||||
}
|
||||
}
|
||||
],
|
||||
uploadReset: false,
|
||||
dialogVisible: false,
|
||||
form: {
|
||||
@@ -191,7 +224,7 @@ export default {
|
||||
tables.forEach((table) => {
|
||||
const editor = window.tinymce.get(this.tinymceId);
|
||||
editor.dom.setStyles(table, {
|
||||
width: this.typesettingType == 1 ? '171.5mm' : '258.6mm'
|
||||
width: this.typesettingType == 1 ? '17.18cm' : '25.88cm'
|
||||
});
|
||||
});
|
||||
|
||||
@@ -224,25 +257,54 @@ export default {
|
||||
statusbar: false, // 关闭底部状态栏
|
||||
custom_colors: false,
|
||||
color_map: ['000000', 'Black', '0082AA', 'TMR Blue'],
|
||||
plugins: 'forecolor code paste table', // 启用 forecolor 和 code 插件
|
||||
plugins: 'forecolor code paste table image', // 启用 forecolor 和 code 插件
|
||||
end_container_on_empty_block: true,
|
||||
content_css: 'default', // 加载 TinyMCE 默认样式表
|
||||
//设置自定义按钮 myCustomToolbarButton
|
||||
|
||||
setup(ed) {
|
||||
ed.ui.registry.addButton('pageOrientation', {
|
||||
text: '切换方向',
|
||||
ed.ui.registry.addButton('uploadWord', {
|
||||
text: 'Word',
|
||||
icon: 'import-word', // 使用自定义图标
|
||||
onAction: function () {
|
||||
_this.typesettingType = _this.typesettingType == 1 ? 2 : 1;
|
||||
_this.changeTable();
|
||||
// if (ed.dom.hasClass(ed.getBody(), 'a4-portrait')) {
|
||||
// ed.dom.removeClass(ed.getBody(), 'a4-portrait');
|
||||
// ed.dom.addClass(ed.getBody(), 'a4-landscape');
|
||||
// } else {
|
||||
// ed.dom.removeClass(ed.getBody(), 'a4-landscape');
|
||||
// ed.dom.addClass(ed.getBody(), 'a4-portrait');
|
||||
// }
|
||||
const input = document.createElement('input');
|
||||
input.type = 'file';
|
||||
input.accept = '.docx'; // 限制为 Word 文件
|
||||
input.addEventListener('change', function () {
|
||||
const file = input.files[0];
|
||||
if (file) {
|
||||
const reader = new FileReader();
|
||||
reader.onload = function (e) {
|
||||
const arrayBuffer = e.target.result;
|
||||
_this.extractTablesFromWord(arrayBuffer, function (tablesHtml) {
|
||||
ed.setContent(tablesHtml);
|
||||
});
|
||||
};
|
||||
reader.readAsArrayBuffer(file);
|
||||
}
|
||||
});
|
||||
input.click();
|
||||
}
|
||||
});
|
||||
|
||||
ed.ui.registry.addMenuButton('customDropdown', {
|
||||
text: _this.$t('commonTable.PaperRotation'), // 下拉框标题
|
||||
fetch: function (callback) {
|
||||
// 定义下拉框的内容
|
||||
const items = [..._this.typesettingTypeOptions];
|
||||
const menuItems = items.map((item) => ({
|
||||
type: 'menuitem',
|
||||
text: item.label,
|
||||
onAction: function () {
|
||||
_this.typesettingType = item.value;
|
||||
_this.changeTable();
|
||||
// ed.execCommand(item.value); // 执行命令
|
||||
}
|
||||
}));
|
||||
callback(menuItems);
|
||||
}
|
||||
});
|
||||
|
||||
ed.on('init', function () {
|
||||
const editorBody = ed.getBody();
|
||||
// 创建 MutationObserver 监听内容变化
|
||||
@@ -259,13 +321,15 @@ export default {
|
||||
observer.observe(editorBody, { childList: true, subtree: true, characterData: true });
|
||||
});
|
||||
// 定义自定义按钮
|
||||
ed.ui.registry.addButton('customButton', {
|
||||
ed.ui.registry.addButton('clearButton', {
|
||||
text: 'Empty',
|
||||
|
||||
onAction: () => {
|
||||
// 插入自定义表格到编辑器中
|
||||
ed.setContent('');
|
||||
}
|
||||
});
|
||||
|
||||
// 定义自定义按钮
|
||||
ed.ui.registry.addButton('customButtonExportWord', {
|
||||
text: _this.$t('commonTable.exportWord'),
|
||||
@@ -312,6 +376,107 @@ export default {
|
||||
}
|
||||
});
|
||||
},
|
||||
// 提取 Word 文件中的表格
|
||||
extractTablesFromWord(arrayBuffer, callback) {
|
||||
const zip = new JSZip();
|
||||
var that = this;
|
||||
zip.loadAsync(arrayBuffer)
|
||||
.then(function (zip) {
|
||||
const docXmlPath = 'word/document.xml'; // Word 主文档的 XML 路径
|
||||
return zip.file(docXmlPath).async('string');
|
||||
})
|
||||
.then(function (docXml) {
|
||||
const parser = new DOMParser();
|
||||
const xmlDoc = parser.parseFromString(docXml, 'text/xml');
|
||||
const tables = xmlDoc.getElementsByTagName('w:tbl'); // 查找 Word 表格标签
|
||||
|
||||
let html = '';
|
||||
for (let table of tables) {
|
||||
html += that.convertTableToHtml(table);
|
||||
}
|
||||
|
||||
if (!html) {
|
||||
html = '<p>未检测到表格内容。</p>';
|
||||
}
|
||||
callback(html);
|
||||
const container = document.createElement('div');
|
||||
container.innerHTML = html;
|
||||
that.updateTableStyles(container);
|
||||
|
||||
console.log('html at line 400:', html);
|
||||
})
|
||||
.catch(function (err) {
|
||||
console.error('解析 Word 文件出错:', err);
|
||||
callback('<p>文件解析失败,请检查文件格式。</p>');
|
||||
});
|
||||
},
|
||||
|
||||
// 将 XML 表格转换为 HTML
|
||||
convertTableToHtml(tableNode) {
|
||||
const rows = tableNode.getElementsByTagName('w:tr');
|
||||
let html = '<table border="1" style="border-collapse: collapse;">';
|
||||
for (let row of rows) {
|
||||
html += '<tr>';
|
||||
const cells = row.getElementsByTagName('w:tc');
|
||||
for (let cell of cells) {
|
||||
let cellHtml = '';
|
||||
const paragraphs = cell.getElementsByTagName('w:p'); // 获取单元格内段落
|
||||
|
||||
for (let paragraph of paragraphs) {
|
||||
const texts = paragraph.getElementsByTagName('w:r'); // 获取段落内的文本和样式
|
||||
for (let run of texts) {
|
||||
const textNode = run.getElementsByTagName('w:t')[0];
|
||||
if (textNode) {
|
||||
const style = this.getStyleFromRun(run); // 提取样式
|
||||
cellHtml += `<span style="${style}">${textNode.textContent}</span>`;
|
||||
}
|
||||
}
|
||||
cellHtml += '<br>'; // 段落换行
|
||||
}
|
||||
|
||||
html += `<td>${cellHtml}</td>`;
|
||||
}
|
||||
html += '</tr>';
|
||||
}
|
||||
html += '</table>';
|
||||
|
||||
return html;
|
||||
},
|
||||
|
||||
// 提取 w:r 节点中的样式并转换为 CSS
|
||||
getStyleFromRun(run) {
|
||||
const styleNode = run.getElementsByTagName('w:rPr')[0];
|
||||
let style = '';
|
||||
|
||||
if (styleNode) {
|
||||
// 加粗
|
||||
if (styleNode.getElementsByTagName('w:b').length > 0) {
|
||||
style += 'font-weight: bold;';
|
||||
}
|
||||
// 斜体
|
||||
if (styleNode.getElementsByTagName('w:i').length > 0) {
|
||||
style += 'font-style: italic;';
|
||||
}
|
||||
// 上标或下标
|
||||
const vertAlign = styleNode.getElementsByTagName('w:vertAlign')[0];
|
||||
if (vertAlign) {
|
||||
const alignVal = vertAlign.getAttribute('w:val');
|
||||
if (alignVal === 'superscript') {
|
||||
style += 'vertical-align: super; font-size: smaller;';
|
||||
} else if (alignVal === 'subscript') {
|
||||
style += 'vertical-align: sub; font-size: smaller;';
|
||||
}
|
||||
}
|
||||
// 字体颜色
|
||||
const colorNode = styleNode.getElementsByTagName('w:color')[0];
|
||||
if (colorNode) {
|
||||
const colorVal = colorNode.getAttribute('w:val');
|
||||
style += `color: #${colorVal};`;
|
||||
}
|
||||
}
|
||||
|
||||
return style;
|
||||
},
|
||||
replaceNegativeSign(node) {
|
||||
if (node.nodeType === Node.TEXT_NODE) {
|
||||
// 如果是文本节点,替换负号
|
||||
@@ -336,11 +501,12 @@ export default {
|
||||
var _this = this;
|
||||
// 更新表格样式
|
||||
const tables = container.querySelectorAll('table');
|
||||
|
||||
tables.forEach((table) => {
|
||||
table.setAttribute(
|
||||
'style',
|
||||
`width: ${
|
||||
this.typesettingType == 1 ? '171.5mm' : '258.6mm'
|
||||
this.typesettingType == 1 ? '17.18cm' : '25.88cm'
|
||||
};border: none; margin: 0 auto !important;border-collapse: collapse; `
|
||||
);
|
||||
const cells = table.querySelectorAll('td');
|
||||
@@ -369,6 +535,7 @@ export default {
|
||||
console.log('匹配到带有数字的方括号内容');
|
||||
// 为匹配的元素添加样式类
|
||||
element.classList.add('color-highlight');
|
||||
element.style.color = 'rgb(0,130,170)';
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -409,6 +576,23 @@ export default {
|
||||
}
|
||||
});
|
||||
});
|
||||
var editor = window.tinymce.activeEditor;
|
||||
var html = '';
|
||||
tables.forEach((e) => {
|
||||
console.log('e at line 579:', e);
|
||||
|
||||
html += e.outerHTML;
|
||||
console.log('html at line 582:', html);
|
||||
});
|
||||
// 将外部 DOM 内容更新到编辑器
|
||||
const container1 = document.createElement('div');
|
||||
container1.innerHTML = html; // html 是更新后的 HTML 内容
|
||||
|
||||
// 更新编辑器内容
|
||||
editor.setContent(container1.innerHTML);
|
||||
|
||||
// 触发编辑器内容变化后,如果需要,可能还要设置编辑器的样式
|
||||
editor.focus(); // 聚焦到编辑器
|
||||
},
|
||||
//销毁富文本
|
||||
destroyTinymce() {
|
||||
@@ -424,16 +608,13 @@ export default {
|
||||
getContent(type) {
|
||||
this.$emit('getContent', type, window.tinymce.get(this.tinymceId).getContent());
|
||||
},
|
||||
|
||||
export(type, data) {
|
||||
|
||||
async export(type, data) {
|
||||
if (type == 'table') {
|
||||
var tableHtml = `<html xmlns:w="urn:schemas-microsoft-com:office:word">
|
||||
<head>
|
||||
<style>
|
||||
@page {
|
||||
size: A4; /* 或者 A3, Letter 等纸张大小 */
|
||||
margin: 20mm;
|
||||
}
|
||||
|
||||
${tableStyle}
|
||||
</style>
|
||||
</head>
|
||||
@@ -441,8 +622,17 @@ export default {
|
||||
${data}
|
||||
</body>
|
||||
</html>`;
|
||||
console.log('tableHtml at line 438:', tableHtml);
|
||||
const converted = htmlDocx.asBlob(tableHtml); // 将 HTML 转换为 Word Blob
|
||||
|
||||
const converted = htmlDocx.asBlob(tableHtml, {
|
||||
orientation: this.typesettingTypeOptions[this.typesettingType - 1].orientation,
|
||||
pageSize: {
|
||||
...this.typesettingTypeOptions[this.typesettingType - 1].pageSize
|
||||
},
|
||||
pageMargins: {
|
||||
...this.typesettingTypeOptions[this.typesettingType - 1].pageMargins
|
||||
}
|
||||
});
|
||||
// const converted = htmlDocx.asBlob(tableHtml); // 将 HTML 转换为 Word Blob
|
||||
// 触发文件下载
|
||||
const link = document.createElement('a');
|
||||
link.href = URL.createObjectURL(converted);
|
||||
@@ -519,4 +709,15 @@ export default {
|
||||
::v-deep .tox .tox-menu {
|
||||
z-index: 9999 !important;
|
||||
}
|
||||
/* 自定义按钮样式 */
|
||||
.custom-btn {
|
||||
background-color: #28a745 !important;
|
||||
color: #fff !important;
|
||||
border-radius: 4px;
|
||||
padding: 5px 10px;
|
||||
font-weight: bold;
|
||||
}
|
||||
.custom-btn:hover {
|
||||
background-color: #218838 !important;
|
||||
}
|
||||
</style>
|
||||
|
||||
File diff suppressed because one or more lines are too long
393
src/components/page/components/table/dragWord.vue
Normal file
393
src/components/page/components/table/dragWord.vue
Normal file
@@ -0,0 +1,393 @@
|
||||
<template>
|
||||
<div>
|
||||
<!-- 拖拽区域 -->
|
||||
<div class="drag-drop-area" @dragover.prevent="onDragOver" @dragleave="onDragLeave" @drop.prevent="onDrop" @click="clickUpload">
|
||||
<p>将 Word 文件拖拽到此处</p>
|
||||
</div>
|
||||
<el-dialog append-to-body
|
||||
title="Add Academic Integrity Committee"
|
||||
:visible.sync="addVisible"
|
||||
width="660px"
|
||||
:close-on-click-modal="false"
|
||||
|
||||
>
|
||||
<div v-if="tables.length > 0" class="preview-area">
|
||||
<div v-for="(table, index) in tables" :key="index" class="table-wrapper">
|
||||
<h3>Table {{ index + 1 }}</h3>
|
||||
<div v-html="table.html" class="table-preview"></div>
|
||||
|
||||
<div class="table-options">
|
||||
<label>
|
||||
页面方向:
|
||||
<select v-model="table.orientation">
|
||||
<option value="portrait">纵向</option>
|
||||
<option value="landscape">横向</option>
|
||||
</select>
|
||||
</label>
|
||||
<button @click="confirmTable(index)">确认表格</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<span slot="footer" class="dialog-footer">
|
||||
<el-button @click="addVisCancle">Cancel</el-button>
|
||||
<el-button type="primary" @click="saveAdd()" v-if="dis_able">OK</el-button>
|
||||
</span>
|
||||
</el-dialog>
|
||||
<!-- 表格预览区 -->
|
||||
</div>
|
||||
</template>
|
||||
<script>
|
||||
import JSZip from 'jszip';
|
||||
|
||||
export default {
|
||||
data() {
|
||||
return {
|
||||
tables: [], // 保存解析后的表格数据
|
||||
addVisible:false,
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
clickUpload(){
|
||||
const input = document.createElement('input');
|
||||
input.type = 'file';
|
||||
input.accept = '.docx'; // 限制为 Word 文件
|
||||
input.addEventListener('change', function () {
|
||||
const file = input.files[0];
|
||||
if (file) {
|
||||
const reader = new FileReader();
|
||||
reader.onload = function (e) {
|
||||
const arrayBuffer = e.target.result;
|
||||
_this.extractTablesFromWord(arrayBuffer, function (tablesHtml) {
|
||||
ed.setContent(tablesHtml);
|
||||
});
|
||||
};
|
||||
reader.readAsArrayBuffer(file);
|
||||
}
|
||||
});
|
||||
input.click();
|
||||
},
|
||||
// 拖拽事件处理
|
||||
onDragOver(event) {
|
||||
event.currentTarget.style.borderColor = '#007bff';
|
||||
},
|
||||
onDragLeave(event) {
|
||||
event.currentTarget.style.borderColor = '#ccc';
|
||||
},
|
||||
|
||||
|
||||
// 提取 Word 文件中的表格
|
||||
extractTablesFromWord(arrayBuffer, callback) {
|
||||
const zip = new JSZip();
|
||||
var that = this;
|
||||
zip.loadAsync(arrayBuffer)
|
||||
.then(function (zip) {
|
||||
const docXmlPath = 'word/document.xml'; // Word 主文档的 XML 路径
|
||||
return zip.file(docXmlPath).async('string');
|
||||
})
|
||||
.then(function (docXml) {
|
||||
const parser = new DOMParser();
|
||||
const xmlDoc = parser.parseFromString(docXml, 'text/xml');
|
||||
const tables = xmlDoc.getElementsByTagName('w:tbl'); // 查找 Word 表格标签
|
||||
|
||||
let html = '';
|
||||
for (let table of tables) {
|
||||
html += that.convertTableToHtml(table);
|
||||
}
|
||||
|
||||
if (!html) {
|
||||
html = '<p>未检测到表格内容。</p>';
|
||||
}
|
||||
callback(html);
|
||||
const container = document.createElement('div');
|
||||
container.innerHTML = html;
|
||||
that.updateTableStyles(container);
|
||||
|
||||
console.log('html at line 400:', html);
|
||||
})
|
||||
.catch(function (err) {
|
||||
console.error('解析 Word 文件出错:', err);
|
||||
callback('<p>文件解析失败,请检查文件格式。</p>');
|
||||
});
|
||||
},
|
||||
async onDrop(event) {
|
||||
const file = event.dataTransfer.files[0];
|
||||
if (!file || !file.name.endsWith('.docx')) {
|
||||
alert('请拖拽一个 Word 文件 (.docx)');
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const tables = await this.parseWordFile(file);
|
||||
if (tables.length === 0) {
|
||||
alert('未检测到表格内容');
|
||||
} else {
|
||||
this.tables = tables.map((table) => ({
|
||||
html: this.convertTableToHtml(table),
|
||||
orientation: 'portrait' // 默认纵向
|
||||
}));
|
||||
|
||||
this.addVisible=true
|
||||
var html = '';
|
||||
for (let table of this.tables) {
|
||||
html += this.convertTableToHtml(table.html);
|
||||
}
|
||||
|
||||
if (!html) {
|
||||
html = '<p>未检测到表格内容。</p>';
|
||||
}
|
||||
const container = document.createElement('div');
|
||||
container.innerHTML = html;
|
||||
this.updateTableStyles(container);
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('文件解析失败:', err);
|
||||
alert('文件解析失败,请检查文件格式。');
|
||||
}
|
||||
},
|
||||
|
||||
// 解析 Word 文件
|
||||
async parseWordFile(file) {
|
||||
const reader = new FileReader();
|
||||
return new Promise((resolve, reject) => {
|
||||
reader.onload = async (e) => {
|
||||
try {
|
||||
const arrayBuffer = e.target.result;
|
||||
|
||||
const zip = new JSZip();
|
||||
const zipContent = await zip.loadAsync(arrayBuffer);
|
||||
const docXmlPath = 'word/document.xml';
|
||||
const docXml = await zipContent.file(docXmlPath).async('string');
|
||||
const parser = new DOMParser();
|
||||
const xmlDoc = parser.parseFromString(docXml, 'text/xml');
|
||||
resolve(Array.from(xmlDoc.getElementsByTagName('w:tbl')));
|
||||
} catch (err) {
|
||||
reject(err);
|
||||
}
|
||||
};
|
||||
reader.readAsArrayBuffer(file);
|
||||
});
|
||||
},
|
||||
// 将 XML 表格转换为 HTML
|
||||
convertTableToHtml(tableNode) {
|
||||
const rows = tableNode.getElementsByTagName('w:tr');
|
||||
let html = '<table border="1" style="border-collapse: collapse;">';
|
||||
for (let row of rows) {
|
||||
html += '<tr>';
|
||||
const cells = row.getElementsByTagName('w:tc');
|
||||
for (let cell of cells) {
|
||||
let cellHtml = '';
|
||||
const paragraphs = cell.getElementsByTagName('w:p'); // 获取单元格内段落
|
||||
|
||||
for (let paragraph of paragraphs) {
|
||||
const texts = paragraph.getElementsByTagName('w:r'); // 获取段落内的文本和样式
|
||||
for (let run of texts) {
|
||||
const textNode = run.getElementsByTagName('w:t')[0];
|
||||
if (textNode) {
|
||||
const style = this.getStyleFromRun(run); // 提取样式
|
||||
cellHtml += `<span style="${style}">${textNode.textContent}</span>`;
|
||||
}
|
||||
}
|
||||
cellHtml += '<br>'; // 段落换行
|
||||
}
|
||||
|
||||
html += `<td>${cellHtml}</td>`;
|
||||
}
|
||||
html += '</tr>';
|
||||
}
|
||||
html += '</table>';
|
||||
|
||||
return html;
|
||||
},
|
||||
|
||||
// 提取 w:r 节点中的样式并转换为 CSS
|
||||
getStyleFromRun(run) {
|
||||
const styleNode = run.getElementsByTagName('w:rPr')[0];
|
||||
let style = '';
|
||||
|
||||
if (styleNode) {
|
||||
// 加粗
|
||||
if (styleNode.getElementsByTagName('w:b').length > 0) {
|
||||
style += 'font-weight: bold;';
|
||||
}
|
||||
// 斜体
|
||||
if (styleNode.getElementsByTagName('w:i').length > 0) {
|
||||
style += 'font-style: italic;';
|
||||
}
|
||||
// 上标或下标
|
||||
const vertAlign = styleNode.getElementsByTagName('w:vertAlign')[0];
|
||||
if (vertAlign) {
|
||||
const alignVal = vertAlign.getAttribute('w:val');
|
||||
if (alignVal === 'superscript') {
|
||||
style += 'vertical-align: super; font-size: smaller;';
|
||||
} else if (alignVal === 'subscript') {
|
||||
style += 'vertical-align: sub; font-size: smaller;';
|
||||
}
|
||||
}
|
||||
// 字体颜色
|
||||
const colorNode = styleNode.getElementsByTagName('w:color')[0];
|
||||
if (colorNode) {
|
||||
const colorVal = colorNode.getAttribute('w:val');
|
||||
style += `color: #${colorVal};`;
|
||||
}
|
||||
}
|
||||
|
||||
return style;
|
||||
},
|
||||
replaceNegativeSign(node) {
|
||||
if (node.nodeType === Node.TEXT_NODE) {
|
||||
// 如果是文本节点,替换负号
|
||||
node.nodeValue = node.nodeValue.replace(/^-(?=\d)/, '−');
|
||||
} else if (node.nodeType === Node.ELEMENT_NODE) {
|
||||
// 如果是元素节点,递归处理子节点
|
||||
node.childNodes.forEach(this.replaceNegativeSign);
|
||||
}
|
||||
},
|
||||
capitalizeFirstLetter(node) {
|
||||
if (node.nodeType === Node.TEXT_NODE) {
|
||||
// 如果是文本节点,只处理第一个非空字符
|
||||
node.nodeValue = node.nodeValue.replace(/^\s*([a-zA-Z])/, (match, firstLetter) => {
|
||||
return firstLetter.toUpperCase();
|
||||
});
|
||||
} else if (node.nodeType === Node.ELEMENT_NODE) {
|
||||
// 递归处理子节点
|
||||
node.childNodes.forEach(this.capitalizeFirstLetter);
|
||||
}
|
||||
},
|
||||
updateTableStyles(container) {
|
||||
var _this = this;
|
||||
// 更新表格样式
|
||||
const tables = container.querySelectorAll('table');
|
||||
|
||||
tables.forEach((table) => {
|
||||
table.setAttribute(
|
||||
'style',
|
||||
`width: ${
|
||||
this.typesettingType == 1 ? '17.18cm' : '25.88cm'
|
||||
};border: none; margin: 0 auto !important;border-collapse: collapse; `
|
||||
);
|
||||
const cells = table.querySelectorAll('td');
|
||||
cells.forEach((td) => {
|
||||
if (/^-?\d+(\.\d+)?$/.test(td.textContent.trim())) {
|
||||
_this.replaceNegativeSign(td);
|
||||
}
|
||||
const hasSupOrSub = _this.containsSupOrSub(td); // 检查当前 td 是否包含上下标
|
||||
if (!hasSupOrSub) {
|
||||
// 递归处理单元格内的所有子节点
|
||||
td.childNodes.forEach(_this.capitalizeFirstLetter);
|
||||
// 替换 <a> 标签为其内部文本
|
||||
td.querySelectorAll('a').forEach((a) => {
|
||||
const textNode = document.createTextNode(a.textContent); // 创建文本节点
|
||||
a.replaceWith(textNode); // 用文本节点替换 <a> 标签
|
||||
});
|
||||
}
|
||||
|
||||
// 获取 td 元素中的所有子元素
|
||||
const childElements = td.querySelectorAll('*');
|
||||
|
||||
// 遍历每个子元素
|
||||
childElements.forEach((element) => {
|
||||
// 如果元素的文本内容匹配正则表达式
|
||||
if (/\[\d+(?:,\d+)*\]/g.test(element.textContent)) {
|
||||
console.log('匹配到带有数字的方括号内容');
|
||||
// 为匹配的元素添加样式类
|
||||
element.classList.add('color-highlight');
|
||||
element.style.color = 'rgb(0,130,170)';
|
||||
}
|
||||
});
|
||||
});
|
||||
const firstRowTdElements = container.querySelectorAll('tr:first-child td'); // 获取第一个 <tr> 中的所有 <td> 元素
|
||||
// 遍历所有 <td> 元素,添加上下边框样式
|
||||
firstRowTdElements.forEach((td) => {
|
||||
const currentStyle = td.getAttribute('style');
|
||||
if (currentStyle) {
|
||||
td.setAttribute(
|
||||
'style',
|
||||
currentStyle +
|
||||
';border-top:1.0000pt solid #000 !important;mso-border-top-alt:0.5000pt solid #000 !important;border-bottom:1.0000pt solid #000 !important;mso-border-bottom-alt:0.5000pt solid #000 !important;'
|
||||
);
|
||||
} else {
|
||||
td.setAttribute(
|
||||
'style',
|
||||
'border-top:1.0000pt solid #000 !important;mso-border-top-alt:0.5000pt solid #000 !important;border-bottom:1.0000pt solid #000 !important;mso-border-bottom-alt:0.5000pt solid #000 !important;'
|
||||
);
|
||||
}
|
||||
});
|
||||
const firstRowTdElementsLast = container.querySelectorAll('tr:last-of-type td');
|
||||
// 遍历所有 <td> 元素,添加上下边框样式
|
||||
firstRowTdElementsLast.forEach((td) => {
|
||||
// 获取当前的 style 属性(如果有)
|
||||
const currentStyle = td.getAttribute('style');
|
||||
// 如果已有 style 属性,则追加边框样式;如果没有 style 属性,则设置新的 style
|
||||
if (currentStyle) {
|
||||
td.setAttribute(
|
||||
'style',
|
||||
currentStyle +
|
||||
';border-bottom:1.0000pt solid #000 !important;mso-border-bottom-alt:0.5000pt solid #000 !important;'
|
||||
);
|
||||
} else {
|
||||
td.setAttribute(
|
||||
'style',
|
||||
'border-bottom:1.0000pt solid #000 !important;mso-border-bottom-alt:0.5000pt solid #000 !important;'
|
||||
);
|
||||
}
|
||||
});
|
||||
});
|
||||
var editor = window.tinymce.activeEditor;
|
||||
var html = '';
|
||||
tables.forEach((e) => {
|
||||
console.log('e at line 579:', e);
|
||||
|
||||
html += e.outerHTML;
|
||||
console.log('html at line 582:', html);
|
||||
});
|
||||
// 将外部 DOM 内容更新到编辑器
|
||||
const container1 = document.createElement('div');
|
||||
container1.innerHTML = html; // html 是更新后的 HTML 内容
|
||||
|
||||
// 更新编辑器内容
|
||||
editor.setContent(container1.innerHTML);
|
||||
|
||||
// 触发编辑器内容变化后,如果需要,可能还要设置编辑器的样式
|
||||
editor.focus(); // 聚焦到编辑器
|
||||
},
|
||||
|
||||
|
||||
// 确认表格
|
||||
confirmTable(index) {
|
||||
const table = this.tables[index];
|
||||
alert(`表格 ${index + 1} 已确认,页面方向为 ${table.orientation === 'portrait' ? '纵向' : '横向'}`);
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.drag-drop-area {
|
||||
border: 2px dashed #ccc;
|
||||
padding: 20px;
|
||||
text-align: center;
|
||||
width: 300px;
|
||||
margin: 0 auto;
|
||||
cursor: pointer;
|
||||
}
|
||||
.drag-drop-area:hover {
|
||||
border-color: #007bff;
|
||||
}
|
||||
|
||||
.preview-area {
|
||||
margin-top: 20px;
|
||||
}
|
||||
.table-wrapper {
|
||||
border: 1px solid #ccc;
|
||||
padding: 10px;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
.table-preview {
|
||||
border: 1px solid #000;
|
||||
padding: 10px;
|
||||
overflow-x: auto;
|
||||
}
|
||||
.table-options {
|
||||
margin-top: 10px;
|
||||
}
|
||||
</style>
|
||||
@@ -8,7 +8,6 @@
|
||||
</div>
|
||||
|
||||
<tinymce
|
||||
|
||||
ref="tinymceChild"
|
||||
:value="updatedHtml"
|
||||
:typesettingType="typesettingType"
|
||||
@@ -71,10 +70,12 @@ export default {
|
||||
selectionRange: null, // 保存选区范围
|
||||
updatedHtml: '',
|
||||
imgHtml: '',
|
||||
typesettingTypeOptions: [
|
||||
{ label: this.$t('commonTable.typesettingType1'), value: 1, pageWidth: 210 },
|
||||
{ label: this.$t('commonTable.typesettingType2'), value: 2, pageWidth: 297 }
|
||||
],
|
||||
orientation: this.typesettingType == 1 ? 'portrait' : 'landscape',
|
||||
pageSize: {
|
||||
width: 11906,
|
||||
height: 16976
|
||||
},
|
||||
|
||||
transform: null,
|
||||
typesettingType: 1
|
||||
};
|
||||
@@ -145,10 +146,9 @@ export default {
|
||||
handlePaste(event) {
|
||||
setTimeout(() => {
|
||||
// 解析内容并替换表格
|
||||
console.log('event.currentTarget.innerHTML at line 146:', event)
|
||||
console.log('event.currentTarget.innerHTML at line 146:', event);
|
||||
let replacedContent = this.setHtmlWord(event);
|
||||
|
||||
|
||||
|
||||
this.$nextTick(() => {
|
||||
this.$refs.tinymceChild.tableSuccessCBK(replacedContent);
|
||||
});
|
||||
@@ -652,6 +652,6 @@ td input ::placeholder {
|
||||
align-items: center;
|
||||
}
|
||||
.modal {
|
||||
z-index: 1000; /* 保证模态框 z-index 低于颜色选择框 */
|
||||
z-index: 1000; /* 保证模态框 z-index 低于颜色选择框 */
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -72,6 +72,8 @@ Vue.component("Editor", Editor);
|
||||
|
||||
import commonTable from '@/components/page/components/table/table.vue'
|
||||
Vue.component('common-table', commonTable);
|
||||
import commonDragWord from '@/components/page/components/table/dragWord.vue'
|
||||
Vue.component('common-drag-word', commonDragWord);
|
||||
Vue.use(VueI18n);
|
||||
Vue.use(ElementUI, {
|
||||
size: 'small',
|
||||
|
||||
Reference in New Issue
Block a user