提交
This commit is contained in:
BIN
src/assets/img/word.png
Normal file
BIN
src/assets/img/word.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 943 B |
215
src/common/js/commonJS.js
Normal file
215
src/common/js/commonJS.js
Normal file
@@ -0,0 +1,215 @@
|
|||||||
|
export default {
|
||||||
|
// 提取 Word 文件中的表格
|
||||||
|
extractTablesFromWord(arrayBuffer, callback) {
|
||||||
|
const zip = new JSZip();
|
||||||
|
zip.loadAsync(arrayBuffer)
|
||||||
|
.then((zip) => zip.file('word/document.xml').async('string'))
|
||||||
|
.then((docXml) => {
|
||||||
|
const parser = new DOMParser();
|
||||||
|
const xmlDoc = parser.parseFromString(docXml, 'text/xml');
|
||||||
|
const tables = xmlDoc.getElementsByTagName('w:tbl'); // 查找 Word 表格标签
|
||||||
|
let html = '';
|
||||||
|
const wordTables = []
|
||||||
|
for (let table of tables) {
|
||||||
|
var str = this.convertTableToHtml(table);
|
||||||
|
const container = document.createElement('div');
|
||||||
|
container.innerHTML = str;
|
||||||
|
wordTables.push({
|
||||||
|
html: this.updateTableStyles(container),
|
||||||
|
orientation: 'portrait' // 默认纵向
|
||||||
|
});
|
||||||
|
|
||||||
|
html += str;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!html)
|
||||||
|
html = '<p>未检测到表格内容。</p>';
|
||||||
|
|
||||||
|
|
||||||
|
callback( Array.from(wordTables).map((table) => table.html).join(''), wordTables);
|
||||||
|
})
|
||||||
|
.catch(function (err) {
|
||||||
|
console.error('解析 Word 文件出错:', err);
|
||||||
|
callback('<p>文件解析失败,请检查文件格式。</p>');
|
||||||
|
});
|
||||||
|
},
|
||||||
|
//更新传入所有表格样式
|
||||||
|
updateTableStyles(container,type,setTopBottomBorder) {
|
||||||
|
var typesettingType=type?type:1
|
||||||
|
const tables = container.querySelectorAll('table');
|
||||||
|
tables.forEach((table) => {
|
||||||
|
table.setAttribute(
|
||||||
|
'style',
|
||||||
|
`width: ${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);
|
||||||
|
}
|
||||||
|
// 检查当前 td 是否包含上下标
|
||||||
|
if (!this.containsSupOrSub(td)) {
|
||||||
|
// 递归处理单元格内的所有子节点
|
||||||
|
td.childNodes.forEach((node) => this.capitalizeFirstLetter(node));
|
||||||
|
// 替换 <a> 标签为其内部文本
|
||||||
|
td.querySelectorAll('a').forEach((a) => a.replaceWith(document.createTextNode(a.textContent)));
|
||||||
|
}
|
||||||
|
const childElements = td.querySelectorAll('*');
|
||||||
|
// 遍历每个子元素
|
||||||
|
childElements.forEach((element) => {
|
||||||
|
// 如果元素的文本内容匹配正则表达式
|
||||||
|
if (/\[\d+(?:,\d+)*\]/g.test(element.textContent)) {
|
||||||
|
element.classList.add('color-highlight');
|
||||||
|
element.style.color = 'rgb(0,130,170)';
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
if(setTopBottomBorder){
|
||||||
|
const firstRowTdElements = container.querySelectorAll('tr:first-child td'); // 获取第一个 <tr> 中的所有 <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');
|
||||||
|
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;'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
});
|
||||||
|
return Array.from(tables).map((table) => table.outerHTML).join('');
|
||||||
|
|
||||||
|
},
|
||||||
|
// 将 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) {
|
||||||
|
this.applyToChildNodes(node, (child) => this.replaceNegativeSign(child));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
capitalizeFirstLetter(node) {
|
||||||
|
if (node.nodeType === Node.TEXT_NODE) {
|
||||||
|
// 如果是文本节点,只处理第一个非空字符
|
||||||
|
node.nodeValue = node.nodeValue.replace(/^\s*([a-zA-Z])/, (match, firstLetter) => firstLetter.toUpperCase());
|
||||||
|
} else if (node.nodeType === Node.ELEMENT_NODE) {
|
||||||
|
this.applyToChildNodes(node, (child) => this.capitalizeFirstLetter(child));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
applyToChildNodes(node, fn) {
|
||||||
|
if (node.nodeType === Node.ELEMENT_NODE) {
|
||||||
|
node.childNodes.forEach(fn);
|
||||||
|
}
|
||||||
|
},
|
||||||
|
|
||||||
|
|
||||||
|
containsSupOrSub(element) {
|
||||||
|
// 如果当前节点是元素节点
|
||||||
|
if (element.nodeType === 1) {
|
||||||
|
// 如果是 <sup> 或 <sub> 标签,返回 true
|
||||||
|
if (element.tagName === 'SUP' || element.tagName === 'SUB') {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// 否则,递归检查子节点
|
||||||
|
return Array.from(element.childNodes).some((child) => this.containsSupOrSub(child));
|
||||||
|
}
|
||||||
|
// 如果不是元素节点(如文本节点),返回 false
|
||||||
|
return false;
|
||||||
|
},
|
||||||
|
|
||||||
|
// 通用递归方法
|
||||||
|
|
||||||
|
};
|
||||||
@@ -224,7 +224,14 @@
|
|||||||
</el-dialog>
|
</el-dialog>
|
||||||
|
|
||||||
<!-- 添加表格 -->
|
<!-- 添加表格 -->
|
||||||
<el-dialog title="Insert Table" :visible.sync="threeVisible" width="1200px" :close-on-click-modal="false" >
|
<el-dialog
|
||||||
|
destroy-on-close
|
||||||
|
v-if="threeVisible"
|
||||||
|
title="Insert Table"
|
||||||
|
:visible.sync="threeVisible"
|
||||||
|
width="1200px"
|
||||||
|
:close-on-click-modal="false"
|
||||||
|
>
|
||||||
<el-form ref="editMes" :model="lineStyle" label-width="115px">
|
<el-form ref="editMes" :model="lineStyle" label-width="115px">
|
||||||
<el-form-item label="Top Title :">
|
<el-form-item label="Top Title :">
|
||||||
<el-input placeholder="Please enter the table title..." v-model="lineStyle.titleTop"> </el-input>
|
<el-input placeholder="Please enter the table title..." v-model="lineStyle.titleTop"> </el-input>
|
||||||
@@ -235,9 +242,7 @@
|
|||||||
Content :
|
Content :
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
|
<common-table ref="commonTable" style="margin-left: -115px" :lineStyle="lineStyle"></common-table>
|
||||||
|
|
||||||
<common-table ref="commonTable" style="margin-left: -115px;" :lineStyle="lineStyle"></common-table>
|
|
||||||
</el-form-item>
|
</el-form-item>
|
||||||
<el-form-item label="Bottom Title :">
|
<el-form-item label="Bottom Title :">
|
||||||
<el-input placeholder="Please enter the table title..." v-model="lineStyle.titleBot"> </el-input>
|
<el-input placeholder="Please enter the table title..." v-model="lineStyle.titleBot"> </el-input>
|
||||||
@@ -548,13 +553,12 @@ export default {
|
|||||||
},
|
},
|
||||||
// 表格段落
|
// 表格段落
|
||||||
MTxtTable(val, num) {
|
MTxtTable(val, num) {
|
||||||
// this.lineStyle.p_main_id = val.p_main_id;
|
|
||||||
this.lineStyle.textarea = '';
|
this.lineStyle.textarea = '';
|
||||||
this.lineStyle.titleCon = '';
|
this.lineStyle.titleCon = '';
|
||||||
this.lineTable = [];
|
this.lineTable = [];
|
||||||
|
this.lineStyle = {};
|
||||||
this.threeVisible = true;
|
this.threeVisible = true;
|
||||||
this.typesettingType = '1';
|
|
||||||
this.$forceUpdate();
|
this.$forceUpdate();
|
||||||
},
|
},
|
||||||
|
|
||||||
|
|||||||
Binary file not shown.
|
Before Width: | Height: | Size: 38 KiB |
@@ -10,7 +10,7 @@ import { Document, Packer, PageOrientation, Paragraph, TextRun } from 'docx'; //
|
|||||||
import html2canvas from 'html2canvas';
|
import html2canvas from 'html2canvas';
|
||||||
const toolbar =
|
const toolbar =
|
||||||
'uploadWord|undo redo | formatselect | bold italic | forecolor |subscript superscript|table tabledelete |customButtonExportWord |customButtonExportImg |customDropdown | clearButton';
|
'uploadWord|undo redo | formatselect | bold italic | forecolor |subscript superscript|table tabledelete |customButtonExportWord |customButtonExportImg |customDropdown | clearButton';
|
||||||
const tableStyle = ` b span{
|
const tableStyle = ` b span{
|
||||||
font-weight: bold !important;
|
font-weight: bold !important;
|
||||||
}
|
}
|
||||||
i span{
|
i span{
|
||||||
@@ -87,6 +87,12 @@ const tableStyle = ` b span{
|
|||||||
line-height: 10pt !important;
|
line-height: 10pt !important;
|
||||||
mos-line-height: 10pt !important;
|
mos-line-height: 10pt !important;
|
||||||
}
|
}
|
||||||
|
table tr:first-child td {
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
table tr:last-of-type td {
|
||||||
|
border-bottom:1.0000pt solid #000 !important;mso-border-bottom-alt:0.5000pt solid #000 !important;;
|
||||||
|
}
|
||||||
|
|
||||||
`;
|
`;
|
||||||
export default {
|
export default {
|
||||||
@@ -276,7 +282,8 @@ export default {
|
|||||||
const reader = new FileReader();
|
const reader = new FileReader();
|
||||||
reader.onload = function (e) {
|
reader.onload = function (e) {
|
||||||
const arrayBuffer = e.target.result;
|
const arrayBuffer = e.target.result;
|
||||||
_this.extractTablesFromWord(arrayBuffer, function (tablesHtml) {
|
_this.$commonJS.extractTablesFromWord(arrayBuffer, function (tablesHtml) {
|
||||||
|
console.log('tablesHtml at line 279:', tablesHtml);
|
||||||
ed.setContent(tablesHtml);
|
ed.setContent(tablesHtml);
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
@@ -309,12 +316,14 @@ export default {
|
|||||||
const editorBody = ed.getBody();
|
const editorBody = ed.getBody();
|
||||||
// 创建 MutationObserver 监听内容变化
|
// 创建 MutationObserver 监听内容变化
|
||||||
const observer = new MutationObserver(() => {
|
const observer = new MutationObserver(() => {
|
||||||
const hasHorizontalScrollbar = editorBody.scrollWidth > editorBody.clientWidth;
|
console.log('editorBody at line 313:', editorBody);
|
||||||
if (hasHorizontalScrollbar) {
|
// _this.updateTableStyles(editorBody, _this.typesettingType);
|
||||||
console.log('TinyMCE 出现横向滚动条');
|
// const hasHorizontalScrollbar = editorBody.scrollWidth > editorBody.clientWidth;
|
||||||
} else {
|
// if (hasHorizontalScrollbar) {
|
||||||
console.log('没有横向滚动条');
|
// console.log('TinyMCE 出现横向滚动条');
|
||||||
}
|
// } else {
|
||||||
|
// console.log('没有横向滚动条');
|
||||||
|
// }
|
||||||
});
|
});
|
||||||
|
|
||||||
// 监听子节点和内容的变化
|
// 监听子节点和内容的变化
|
||||||
@@ -338,7 +347,10 @@ export default {
|
|||||||
let content = ed.getContent(); // 获取内容
|
let content = ed.getContent(); // 获取内容
|
||||||
content = content.replace(/<strong>/g, '<b>').replace(/<\/strong>/g, '</b>');
|
content = content.replace(/<strong>/g, '<b>').replace(/<\/strong>/g, '</b>');
|
||||||
content = content.replace(/<em>/g, '<i>').replace(/<\/strong>/g, '</i>');
|
content = content.replace(/<em>/g, '<i>').replace(/<\/strong>/g, '</i>');
|
||||||
_this.export('table', content);
|
const container = document.createElement('div');
|
||||||
|
container.innerHTML = content;
|
||||||
|
|
||||||
|
_this.export('table', _this.$commonJS.updateTableStyles(container, _this.typesettingType, 1));
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
// 定义自定义按钮
|
// 定义自定义按钮
|
||||||
@@ -377,222 +389,14 @@ export default {
|
|||||||
});
|
});
|
||||||
},
|
},
|
||||||
// 提取 Word 文件中的表格
|
// 提取 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) {
|
|
||||||
// 如果是文本节点,替换负号
|
|
||||||
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) {
|
updateTableStyles(container) {
|
||||||
var _this = this;
|
var html = this.$commonJS.updateTableStyles(container, this.typesettingType);
|
||||||
// 更新表格样式
|
var editor = window.tinymce.activeEditor; // 将外部 DOM 内容更新到编辑器
|
||||||
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');
|
const container1 = document.createElement('div');
|
||||||
container1.innerHTML = html; // html 是更新后的 HTML 内容
|
container1.innerHTML = html; // html 是更新后的 HTML 内容
|
||||||
|
editor.setContent(container1.innerHTML); // 更新编辑器内容
|
||||||
// 更新编辑器内容
|
editor.focus(); // 聚焦到编辑器// 触发编辑器内容变化后,如果需要,可能还要设置编辑器的样式
|
||||||
editor.setContent(container1.innerHTML);
|
|
||||||
|
|
||||||
// 触发编辑器内容变化后,如果需要,可能还要设置编辑器的样式
|
|
||||||
editor.focus(); // 聚焦到编辑器
|
|
||||||
},
|
},
|
||||||
//销毁富文本
|
//销毁富文本
|
||||||
destroyTinymce() {
|
destroyTinymce() {
|
||||||
@@ -682,19 +486,6 @@ export default {
|
|||||||
console.log(arr, '222');
|
console.log(arr, '222');
|
||||||
const _this = this;
|
const _this = this;
|
||||||
window.tinymce.get(_this.tinymceId).insertContent(arr);
|
window.tinymce.get(_this.tinymceId).insertContent(arr);
|
||||||
},
|
|
||||||
containsSupOrSub(element) {
|
|
||||||
// 如果当前节点是元素节点
|
|
||||||
if (element.nodeType === 1) {
|
|
||||||
// 如果是 <sup> 或 <sub> 标签,返回 true
|
|
||||||
if (element.tagName === 'SUP' || element.tagName === 'SUB') {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
// 否则,递归检查子节点
|
|
||||||
return Array.from(element.childNodes).some((child) => this.containsSupOrSub(child));
|
|
||||||
}
|
|
||||||
// 如果不是元素节点(如文本节点),返回 false
|
|
||||||
return false;
|
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
destroyed() {
|
destroyed() {
|
||||||
|
|||||||
@@ -4,12 +4,12 @@
|
|||||||
<div class="drag-drop-area" @dragover.prevent="onDragOver" @dragleave="onDragLeave" @drop.prevent="onDrop" @click="clickUpload">
|
<div class="drag-drop-area" @dragover.prevent="onDragOver" @dragleave="onDragLeave" @drop.prevent="onDrop" @click="clickUpload">
|
||||||
<p>将 Word 文件拖拽到此处</p>
|
<p>将 Word 文件拖拽到此处</p>
|
||||||
</div>
|
</div>
|
||||||
<el-dialog append-to-body
|
<el-dialog
|
||||||
|
append-to-body
|
||||||
title="Add Academic Integrity Committee"
|
title="Add Academic Integrity Committee"
|
||||||
:visible.sync="addVisible"
|
:visible.sync="addVisible"
|
||||||
width="660px"
|
width="660px"
|
||||||
:close-on-click-modal="false"
|
:close-on-click-modal="false"
|
||||||
|
|
||||||
>
|
>
|
||||||
<div v-if="tables.length > 0" class="preview-area">
|
<div v-if="tables.length > 0" class="preview-area">
|
||||||
<div v-for="(table, index) in tables" :key="index" class="table-wrapper">
|
<div v-for="(table, index) in tables" :key="index" class="table-wrapper">
|
||||||
@@ -43,28 +43,34 @@ export default {
|
|||||||
data() {
|
data() {
|
||||||
return {
|
return {
|
||||||
tables: [], // 保存解析后的表格数据
|
tables: [], // 保存解析后的表格数据
|
||||||
addVisible:false,
|
addVisible: false
|
||||||
};
|
};
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
clickUpload(){
|
addVisCancle(){
|
||||||
|
this.addVisible = false;
|
||||||
|
},
|
||||||
|
clickUpload() {
|
||||||
|
this.tables = [];
|
||||||
|
var that = this;
|
||||||
const input = document.createElement('input');
|
const input = document.createElement('input');
|
||||||
input.type = 'file';
|
input.type = 'file';
|
||||||
input.accept = '.docx'; // 限制为 Word 文件
|
input.accept = '.docx'; // 限制为 Word 文件
|
||||||
input.addEventListener('change', function () {
|
input.addEventListener('change', function () {
|
||||||
const file = input.files[0];
|
const file = input.files[0];
|
||||||
if (file) {
|
if (file) {
|
||||||
const reader = new FileReader();
|
const reader = new FileReader();
|
||||||
reader.onload = function (e) {
|
reader.onload = function (e) {
|
||||||
const arrayBuffer = e.target.result;
|
const arrayBuffer = e.target.result;
|
||||||
_this.extractTablesFromWord(arrayBuffer, function (tablesHtml) {
|
that.extractTablesFromWord(arrayBuffer, function (tablesHtml) {
|
||||||
ed.setContent(tablesHtml);
|
console.log('tablesHtml at line 61:', that.tables);
|
||||||
});
|
that.addVisible = true;
|
||||||
};
|
});
|
||||||
reader.readAsArrayBuffer(file);
|
};
|
||||||
}
|
reader.readAsArrayBuffer(file);
|
||||||
});
|
}
|
||||||
input.click();
|
});
|
||||||
|
input.click();
|
||||||
},
|
},
|
||||||
// 拖拽事件处理
|
// 拖拽事件处理
|
||||||
onDragOver(event) {
|
onDragOver(event) {
|
||||||
@@ -74,9 +80,8 @@ export default {
|
|||||||
event.currentTarget.style.borderColor = '#ccc';
|
event.currentTarget.style.borderColor = '#ccc';
|
||||||
},
|
},
|
||||||
|
|
||||||
|
// 提取 Word 文件中的表格
|
||||||
// 提取 Word 文件中的表格
|
extractTablesFromWord(arrayBuffer, callback) {
|
||||||
extractTablesFromWord(arrayBuffer, callback) {
|
|
||||||
const zip = new JSZip();
|
const zip = new JSZip();
|
||||||
var that = this;
|
var that = this;
|
||||||
zip.loadAsync(arrayBuffer)
|
zip.loadAsync(arrayBuffer)
|
||||||
@@ -91,18 +96,22 @@ export default {
|
|||||||
|
|
||||||
let html = '';
|
let html = '';
|
||||||
for (let table of tables) {
|
for (let table of tables) {
|
||||||
html += that.convertTableToHtml(table);
|
var str = that.convertTableToHtml(table);
|
||||||
|
const container = document.createElement('div');
|
||||||
|
container.innerHTML = str;
|
||||||
|
that.tables.push({
|
||||||
|
html: that.updateTableStyles(container),
|
||||||
|
orientation: 'portrait' // 默认纵向
|
||||||
|
});
|
||||||
|
|
||||||
|
html += str;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!html) {
|
if (!html) {
|
||||||
html = '<p>未检测到表格内容。</p>';
|
html = '<p>未检测到表格内容。</p>';
|
||||||
}
|
}
|
||||||
callback(html);
|
|
||||||
const container = document.createElement('div');
|
|
||||||
container.innerHTML = html;
|
|
||||||
that.updateTableStyles(container);
|
|
||||||
|
|
||||||
console.log('html at line 400:', html);
|
callback(html);
|
||||||
})
|
})
|
||||||
.catch(function (err) {
|
.catch(function (err) {
|
||||||
console.error('解析 Word 文件出错:', err);
|
console.error('解析 Word 文件出错:', err);
|
||||||
@@ -126,7 +135,7 @@ export default {
|
|||||||
orientation: 'portrait' // 默认纵向
|
orientation: 'portrait' // 默认纵向
|
||||||
}));
|
}));
|
||||||
|
|
||||||
this.addVisible=true
|
this.addVisible = true;
|
||||||
var html = '';
|
var html = '';
|
||||||
for (let table of this.tables) {
|
for (let table of this.tables) {
|
||||||
html += this.convertTableToHtml(table.html);
|
html += this.convertTableToHtml(table.html);
|
||||||
@@ -167,8 +176,8 @@ export default {
|
|||||||
reader.readAsArrayBuffer(file);
|
reader.readAsArrayBuffer(file);
|
||||||
});
|
});
|
||||||
},
|
},
|
||||||
// 将 XML 表格转换为 HTML
|
// 将 XML 表格转换为 HTML
|
||||||
convertTableToHtml(tableNode) {
|
convertTableToHtml(tableNode) {
|
||||||
const rows = tableNode.getElementsByTagName('w:tr');
|
const rows = tableNode.getElementsByTagName('w:tr');
|
||||||
let html = '<table border="1" style="border-collapse: collapse;">';
|
let html = '<table border="1" style="border-collapse: collapse;">';
|
||||||
for (let row of rows) {
|
for (let row of rows) {
|
||||||
@@ -257,7 +266,6 @@ export default {
|
|||||||
var _this = this;
|
var _this = this;
|
||||||
// 更新表格样式
|
// 更新表格样式
|
||||||
const tables = container.querySelectorAll('table');
|
const tables = container.querySelectorAll('table');
|
||||||
|
|
||||||
tables.forEach((table) => {
|
tables.forEach((table) => {
|
||||||
table.setAttribute(
|
table.setAttribute(
|
||||||
'style',
|
'style',
|
||||||
@@ -291,7 +299,7 @@ export default {
|
|||||||
console.log('匹配到带有数字的方括号内容');
|
console.log('匹配到带有数字的方括号内容');
|
||||||
// 为匹配的元素添加样式类
|
// 为匹配的元素添加样式类
|
||||||
element.classList.add('color-highlight');
|
element.classList.add('color-highlight');
|
||||||
element.style.color = 'rgb(0,130,170)';
|
element.style.color = 'rgb(0,130,170)';
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
@@ -332,26 +340,31 @@ export default {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
var editor = window.tinymce.activeEditor;
|
console.log('tables.forEach at line 270:', tables);
|
||||||
|
// var editor = window.tinymce.activeEditor;
|
||||||
var html = '';
|
var html = '';
|
||||||
tables.forEach((e) => {
|
tables.forEach((e) => {
|
||||||
console.log('e at line 579:', e);
|
|
||||||
|
|
||||||
html += e.outerHTML;
|
html += e.outerHTML;
|
||||||
console.log('html at line 582:', html);
|
console.log('html at line 582:', html);
|
||||||
});
|
});
|
||||||
// 将外部 DOM 内容更新到编辑器
|
this.$forceUpdate();
|
||||||
const container1 = document.createElement('div');
|
return html
|
||||||
container1.innerHTML = html; // html 是更新后的 HTML 内容
|
|
||||||
|
|
||||||
// 更新编辑器内容
|
|
||||||
editor.setContent(container1.innerHTML);
|
|
||||||
|
|
||||||
// 触发编辑器内容变化后,如果需要,可能还要设置编辑器的样式
|
|
||||||
editor.focus(); // 聚焦到编辑器
|
|
||||||
},
|
},
|
||||||
|
|
||||||
|
containsSupOrSub(element) {
|
||||||
|
// 如果当前节点是元素节点
|
||||||
|
if (element.nodeType === 1) {
|
||||||
|
// 如果是 <sup> 或 <sub> 标签,返回 true
|
||||||
|
if (element.tagName === 'SUP' || element.tagName === 'SUB') {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
// 否则,递归检查子节点
|
||||||
|
return Array.from(element.childNodes).some((child) => this.containsSupOrSub(child));
|
||||||
|
}
|
||||||
|
// 如果不是元素节点(如文本节点),返回 false
|
||||||
|
return false;
|
||||||
|
},
|
||||||
// 确认表格
|
// 确认表格
|
||||||
confirmTable(index) {
|
confirmTable(index) {
|
||||||
const table = this.tables[index];
|
const table = this.tables[index];
|
||||||
|
|||||||
@@ -1,432 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div>
|
|
||||||
<div>
|
|
||||||
<el-select v-model="typesettingType">
|
|
||||||
<el-option v-for="item in typesettingTypeOptions" :key="item.value" :label="item.label" :value="item.value"> </el-option>
|
|
||||||
</el-select>
|
|
||||||
<span>请在下面区域粘贴 Word 表格</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<div class="controls" style="overflow: hidden">
|
|
||||||
请在此区域粘贴 Word 表格(typesettingType 为页面方向 1为竖向 <span> </span>
|
|
||||||
<div
|
|
||||||
|
|
||||||
style="margin: 0 auto; box-sizing: border-box"
|
|
||||||
class="paste-area"
|
|
||||||
:style="`width: ${typesettingType == 1 ? 210 : 297}mm !important;
|
|
||||||
padding:25.4mm 19.1mm`"
|
|
||||||
contenteditable="true"
|
|
||||||
id="content"
|
|
||||||
ref="content"
|
|
||||||
@paste="handlePaste"
|
|
||||||
></div>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
|
|
||||||
<el-button @click="exportToWord">导出为 Word</el-button>
|
|
||||||
<el-button @click="downloadImage">导出为 图片</el-button>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import htmlDocx from 'html-docx-js/dist/html-docx.js';
|
|
||||||
import html2canvas from 'html2canvas';
|
|
||||||
export default {
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
updatedHtml: '',
|
|
||||||
typesettingType: 1
|
|
||||||
};
|
|
||||||
},
|
|
||||||
mounted() {
|
|
||||||
this.typesettingType = 1;
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
downloadImage() {
|
|
||||||
|
|
||||||
},
|
|
||||||
|
|
||||||
handlePaste(event) {
|
|
||||||
setTimeout(() => {
|
|
||||||
content= this.$refs.content
|
|
||||||
|
|
||||||
|
|
||||||
if (content) {
|
|
||||||
const updatedHtml = this.setHtmlWord(content.innerHTML);
|
|
||||||
|
|
||||||
this.updatedHtml = updatedHtml.match(/<table[\s\S]*?<\/table>/i)[0];
|
|
||||||
console.log('updatedHtml at line 114:', this.updatedHtml);
|
|
||||||
}
|
|
||||||
|
|
||||||
}, 50);
|
|
||||||
},
|
|
||||||
//判断内容是否有上下标
|
|
||||||
containsSupOrSub(element) {
|
|
||||||
// 如果当前节点是元素节点
|
|
||||||
if (element.nodeType === 1) {
|
|
||||||
// 如果是 <sup> 或 <sub> 标签,返回 true
|
|
||||||
if (element.tagName === 'SUP' || element.tagName === 'SUB') {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
// 否则,递归检查子节点
|
|
||||||
return Array.from(element.childNodes).some((child) => this.containsSupOrSub(child));
|
|
||||||
}
|
|
||||||
// 如果不是元素节点(如文本节点),返回 false
|
|
||||||
return false;
|
|
||||||
},
|
|
||||||
setHtmlWord(setHtmlWord) {
|
|
||||||
if (setHtmlWord.includes('<table')) {
|
|
||||||
} else {
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
const html = setHtmlWord
|
|
||||||
.replace(
|
|
||||||
/<(table)([^>]*style="([^"]*)")/g, // 匹配有 style 属性的 <table>
|
|
||||||
(match, p1, p2, p3) => {
|
|
||||||
const existingStyle = p3;
|
|
||||||
|
|
||||||
// 如果现有样式不为空,则在原样式后追加新的样式
|
|
||||||
const updatedStyle = existingStyle
|
|
||||||
? existingStyle +
|
|
||||||
`; width: ${
|
|
||||||
this.typesettingType == 1 ? '488' : '736'
|
|
||||||
}pt !important; mso-padding-alt: 0.7500pt 0.7500pt 0.7500pt 0.7500pt;`
|
|
||||||
: `width: ${
|
|
||||||
this.typesettingType == 1 ? '488' : '736'
|
|
||||||
}pt !important; mso-padding-alt: 0.7500pt 0.7500pt 0.7500pt 0.7500pt;`;
|
|
||||||
var str = `<table${p2.replace(`style="${existingStyle}"`, `style="${updatedStyle}"`)}`;
|
|
||||||
return str;
|
|
||||||
}
|
|
||||||
)
|
|
||||||
.replace(/style="style=;/g, 'style="')
|
|
||||||
.replace(/;;/g, ';')
|
|
||||||
.replace(/\n/g, '<br>');
|
|
||||||
|
|
||||||
const container = document.createElement('div');
|
|
||||||
container.innerHTML = html;
|
|
||||||
const AllTd = container.querySelectorAll('table td'); // 获取 <tr> 中的所有 <td> 元素
|
|
||||||
const firstRowTdElementsAll = container.querySelectorAll('*'); // 获取第一个 <tr> 中的所有 <td> 元素
|
|
||||||
|
|
||||||
// 遍历所有 <td> 元素,添加上下边框样式
|
|
||||||
firstRowTdElementsAll.forEach((td) => {
|
|
||||||
let htmlContent = td.innerHTML;
|
|
||||||
const currentStyle = td.getAttribute('style');
|
|
||||||
if (currentStyle) {
|
|
||||||
td.setAttribute(
|
|
||||||
'style',
|
|
||||||
currentStyle +
|
|
||||||
";text-align:left;font-family:'Charis SIL' !important; font-size: 7.5000pt !important; mso-font-kerning: 1.0000pt !important; line-height: 10pt !important;mos-line-height: 10pt !important;"
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
td.setAttribute(
|
|
||||||
'style',
|
|
||||||
"text-align:left;font-family:'Charis SIL' !important; font-size: 7.5000pt !important; mso-font-kerning: 1.0000pt !important; line-height: 10pt !important;mos-line-height: 10pt !important;mso-line-height-rule: exactly !important;"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
// 遍历所有 <td> 元素,添加上下边框样式
|
|
||||||
AllTd.forEach((td) => {
|
|
||||||
if (/^-?\d+(\.\d+)?$/.test(td.textContent)) {
|
|
||||||
// 如果是纯数字(包括负数),替换负号为双负号
|
|
||||||
td.textContent = td.textContent.replace(/^-(?=\d)/, '−'); // 替换负号为 "--"
|
|
||||||
}
|
|
||||||
|
|
||||||
const hasSupOrSub = this.containsSupOrSub(td); // 检查当前 td 是否包含上下标
|
|
||||||
if (!hasSupOrSub) {
|
|
||||||
td.textContent = td.textContent.replace(/(^|\s)(\w)/g, (match, space, firstLetter) => {
|
|
||||||
// 将首字母大写,并保持原始空格或其他字符
|
|
||||||
return space + firstLetter.toUpperCase();
|
|
||||||
});
|
|
||||||
td.textContent = td.textContent.replace(/<a\b[^>]*>(.*?)<\/a>/gi, (match, innerText) => {
|
|
||||||
return innerText; // 保留链接内部的内容
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const currentStyle = td.getAttribute('style');
|
|
||||||
if (currentStyle) {
|
|
||||||
const regex = /\[\d+(?:,\d+)*\]/g;
|
|
||||||
if (regex.test(td.innerHTML)) {
|
|
||||||
// 如果匹配到,则对该 <td> 应用样式,比如改变文本颜色
|
|
||||||
td.setAttribute(
|
|
||||||
'style',
|
|
||||||
currentStyle +
|
|
||||||
';color:rgb(0,130,170) !important;border-left:none !important;mso-border-left-alt:none !important;border-right:none !important;mso-border-right-alt:none !important;border-top:none;mso-border-top-alt:none !important;border-bottom:none !important;mso-border-bottom-alt:none !important;'
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
td.setAttribute(
|
|
||||||
'style',
|
|
||||||
currentStyle +
|
|
||||||
';color:#000000 !important;border-left:none !important;mso-border-left-alt:none !important;border-right:none !important;mso-border-right-alt:none !important;border-top:none;mso-border-top-alt:none !important;border-bottom:none !important;mso-border-bottom-alt:none !important;'
|
|
||||||
);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (regex.test(td.innerHTML)) {
|
|
||||||
// 如果匹配到,则对该 <td> 应用样式,比如改变文本颜色
|
|
||||||
td.setAttribute(
|
|
||||||
'style',
|
|
||||||
'color:rgb(0,130,170) !important;border-left:none !important;mso-border-left-alt:none !important;border-right:none !important;mso-border-right-alt:none !important;border-top:none;mso-border-top-alt:none !important;border-bottom:none !important;mso-border-bottom-alt:none !important;'
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
td.setAttribute(
|
|
||||||
'style',
|
|
||||||
'color:#000000 !important;border-left:none !important;mso-border-left-alt:none !important;border-right:none !important;mso-border-right-alt:none !important;border-top:none;mso-border-top-alt:none !important;border-bottom:none !important;mso-border-bottom-alt:none !important;'
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
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 +
|
|
||||||
';color:#000000 !important; 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',
|
|
||||||
'color:#000000 !important;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;'
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// 获取修改后的 HTML 内容
|
|
||||||
const updatedHtml = container.innerHTML;
|
|
||||||
|
|
||||||
this.$forceUpdate();
|
|
||||||
// 输出修改后的 HTML
|
|
||||||
|
|
||||||
return updatedHtml;
|
|
||||||
},
|
|
||||||
getContent(type, data) {
|
|
||||||
if (type == 'table') {
|
|
||||||
var tableHtml = `<html xmlns:w="urn:schemas-microsoft-com:office:word">
|
|
||||||
<head>
|
|
||||||
<style>
|
|
||||||
@page {
|
|
||||||
size: A4 !important;
|
|
||||||
margin: 1in;
|
|
||||||
size: landscape; /* 强制页面方向为横向 */
|
|
||||||
}
|
|
||||||
table {
|
|
||||||
border-collapse: collapse;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
th, td {
|
|
||||||
border: 1px solid #000;
|
|
||||||
padding: 8px;
|
|
||||||
text-align: left;
|
|
||||||
}
|
|
||||||
p,th, td,tr,table {
|
|
||||||
font-size: 7.5000pt; /* 字体大小 */
|
|
||||||
|
|
||||||
margin-top: 0pt; /* 段前间距 */
|
|
||||||
margin-bottom: 0pt; /* 段后间距 */
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
${data}
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
|
|
||||||
`;
|
|
||||||
console.log('tableHtml at line 150:', tableHtml);
|
|
||||||
const converted = htmlDocx.asBlob(tableHtml); // 将 HTML 转换为 Word Blob
|
|
||||||
|
|
||||||
// 触发文件下载
|
|
||||||
const link = document.createElement('a');
|
|
||||||
link.href = URL.createObjectURL(converted);
|
|
||||||
link.download = 'table.docx';
|
|
||||||
link.click();
|
|
||||||
} else if (type == 'image') {
|
|
||||||
// tinymce
|
|
||||||
|
|
||||||
// 显示内容的容器
|
|
||||||
const container = this.$refs.output;
|
|
||||||
console.log('outputDiv at line 358:', container);
|
|
||||||
const outputDiv = container.querySelector('table');
|
|
||||||
console.log('outputDiv at line 360:', outputDiv);
|
|
||||||
|
|
||||||
// 使用 html2canvas 将内容转为图片
|
|
||||||
html2canvas(outputDiv, {
|
|
||||||
scale: 3, // 提高图片的分辨率,默认为1,设置为2可以使图片更清晰
|
|
||||||
logging: false, // 禁用日志输出
|
|
||||||
useCORS: true, // 允许跨域图像
|
|
||||||
allowTaint: true // 允许污染 canvas,解决图片链接不可用问题
|
|
||||||
})
|
|
||||||
// 清空现有内容,显示图片
|
|
||||||
.then((canvas) => {
|
|
||||||
// 创建一个图片对象
|
|
||||||
const imgData = canvas.toDataURL('image/png');
|
|
||||||
|
|
||||||
// 创建一个链接并下载图片
|
|
||||||
const link = document.createElement('a');
|
|
||||||
link.href = imgData;
|
|
||||||
link.download = 'image.png';
|
|
||||||
link.click();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
exportToWord() {
|
|
||||||
|
|
||||||
// 获取 HTML 内容
|
|
||||||
|
|
||||||
// console.log('html at line 118:', updatedHtml);
|
|
||||||
|
|
||||||
const tableHtml = `
|
|
||||||
<html xmlns:w="urn:schemas-microsoft-com:office:word">
|
|
||||||
<head>
|
|
||||||
<style>
|
|
||||||
@page {
|
|
||||||
size: A4 !important;
|
|
||||||
margin: 1in;
|
|
||||||
size: landscape; /* 强制页面方向为横向 */
|
|
||||||
}
|
|
||||||
table {
|
|
||||||
border-collapse: collapse;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
th, td {
|
|
||||||
border: 1px solid #000;
|
|
||||||
padding: 8px;
|
|
||||||
text-align: left;
|
|
||||||
}
|
|
||||||
p,th, td,tr,table {
|
|
||||||
font-size: 7.5000pt; /* 字体大小 */
|
|
||||||
margin-top: 0pt; /* 段前间距 */
|
|
||||||
margin-bottom: 0pt; /* 段后间距 */
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<div align="center">
|
|
||||||
${this.$refs.content.innerHTML}
|
|
||||||
</div>
|
|
||||||
</body>
|
|
||||||
</html>`;
|
|
||||||
console.log('tableHtml at line 150:', tableHtml);
|
|
||||||
const converted = htmlDocx.asBlob(tableHtml); // 将 HTML 转换为 Word Blob
|
|
||||||
// 触发文件下载
|
|
||||||
const link = document.createElement('a');
|
|
||||||
link.href = URL.createObjectURL(converted);
|
|
||||||
link.download = 'table.docx';
|
|
||||||
link.click();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style scoped>
|
|
||||||
table {
|
|
||||||
border-collapse: collapse;
|
|
||||||
width: 100%;
|
|
||||||
margin: 20px 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
th,
|
|
||||||
td {
|
|
||||||
border: 1px dashed #dcdfe6;
|
|
||||||
padding: 8px;
|
|
||||||
text-align: center;
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
table {
|
|
||||||
border-top: 2px solid #000;
|
|
||||||
border-bottom: 1px solid #000;
|
|
||||||
}
|
|
||||||
th {
|
|
||||||
border-bottom: 1px solid #000;
|
|
||||||
}
|
|
||||||
th input,
|
|
||||||
td input {
|
|
||||||
width: 100%;
|
|
||||||
border: none;
|
|
||||||
outline: none;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
th input,
|
|
||||||
td input ::placeholder {
|
|
||||||
color: #aaa !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.controls {
|
|
||||||
margin: 0 0 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.drag-handle {
|
|
||||||
cursor: move;
|
|
||||||
}
|
|
||||||
::v-deep .paste-area {
|
|
||||||
height: auto; /* A4纸高度 */
|
|
||||||
background: white; /* 纸张背景 */
|
|
||||||
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); /* 添加阴影模拟纸张浮起 */
|
|
||||||
border: 1px solid #ddd; /* 模拟纸张边框 */
|
|
||||||
/* padding: 25.4mm 19.1mm; //内边距 */
|
|
||||||
box-sizing: border-box; /* 确保内边距不会影响整体尺寸 */
|
|
||||||
transform-origin: top left;
|
|
||||||
}
|
|
||||||
::v-deep .paste-area table {
|
|
||||||
/* border-top: 2px solid #000 !important; */
|
|
||||||
/* border-bottom: 1px solid #000 !important; */
|
|
||||||
margin-left: 0 !important;
|
|
||||||
margin-right: 0 !important;
|
|
||||||
margin: 0 auto !important;
|
|
||||||
}
|
|
||||||
::v-deep .paste-area table td {
|
|
||||||
border-top: none !important;
|
|
||||||
border-bottom: none !important;
|
|
||||||
border: 1px dashed #dcdfe6 !important;
|
|
||||||
/* display: flex;
|
|
||||||
align-items: center; */
|
|
||||||
}
|
|
||||||
::v-deep .paste-area table td p {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
::v-deep .paste-area table .MsoNormal {
|
|
||||||
max-width: 200px !important; /* 限制容器宽度 */
|
|
||||||
|
|
||||||
word-wrap: break-word !important;
|
|
||||||
overflow-wrap: break-word !important;
|
|
||||||
}
|
|
||||||
.text-container {
|
|
||||||
position: relative;
|
|
||||||
padding: 20px;
|
|
||||||
border: 1px solid #ccc;
|
|
||||||
margin: 20px;
|
|
||||||
font-size: 16px;
|
|
||||||
line-height: 1.5;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
</style>
|
|
||||||
@@ -1,657 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div>
|
|
||||||
<div>
|
|
||||||
<el-select v-model="typesettingType">
|
|
||||||
<el-option v-for="item in typesettingTypeOptions" :key="item.value" :label="item.label" :value="item.value"> </el-option>
|
|
||||||
</el-select>
|
|
||||||
<span>请在下面区域粘贴 Word 表格</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
<tinymce
|
|
||||||
ref="tinymceChild"
|
|
||||||
:value="updatedHtml"
|
|
||||||
:typesettingType="typesettingType"
|
|
||||||
class="paste-area text-container"
|
|
||||||
@handlePaste="handlePaste"
|
|
||||||
@getContent="getContent"
|
|
||||||
:style="`width: ${typesettingType == 1 ? 210 : 297}mm !important;
|
|
||||||
padding:2mm`"
|
|
||||||
style="
|
|
||||||
white-space: pre-line;
|
|
||||||
line-height: 12px;
|
|
||||||
max-height: 50vh;
|
|
||||||
overflow: auto;
|
|
||||||
font-size: 12px;
|
|
||||||
font-size: 7.5pt; /* 字体大小 */
|
|
||||||
|
|
||||||
margin-top: 0pt; /* 段前间距 */
|
|
||||||
margin-bottom: 0pt; /* 段后间距 */
|
|
||||||
"
|
|
||||||
></tinymce>
|
|
||||||
<div class="controls" style="overflow: hidden">
|
|
||||||
请在此区域粘贴 Word 表格<span> </span>
|
|
||||||
<div
|
|
||||||
v-show="!updatedHtml"
|
|
||||||
style="margin: 0 auto; box-sizing: border-box"
|
|
||||||
class="paste-area"
|
|
||||||
:style="`width: ${typesettingType == 1 ? 210 : 297}mm !important;
|
|
||||||
padding:25.4mm 19.1mm`"
|
|
||||||
contenteditable="true"
|
|
||||||
id="content"
|
|
||||||
ref="content"
|
|
||||||
@paste="handlePaste"
|
|
||||||
></div>
|
|
||||||
</div>
|
|
||||||
<!-- <div id="output" ref="output" v-html="updatedHtml"></div> -->
|
|
||||||
<!-- 要导出的内容 -->
|
|
||||||
|
|
||||||
<el-button @click="exportToWord">导出为 Word</el-button>
|
|
||||||
<el-button @click="downloadImage">导出为 图片</el-button>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script>
|
|
||||||
import htmlDocx from 'html-docx-js/dist/html-docx.js';
|
|
||||||
|
|
||||||
import html2canvas from 'html2canvas';
|
|
||||||
import Tinymce from '@/components/page/components/Tinymce';
|
|
||||||
export default {
|
|
||||||
props: ['lineStyle'],
|
|
||||||
components: {
|
|
||||||
Tinymce
|
|
||||||
},
|
|
||||||
data() {
|
|
||||||
return {
|
|
||||||
showToolbar: false, // 是否显示工具栏
|
|
||||||
toolbarStyle: {
|
|
||||||
top: '0px',
|
|
||||||
left: '0px'
|
|
||||||
}, // 工具栏的样式
|
|
||||||
selectionRange: null, // 保存选区范围
|
|
||||||
updatedHtml: '',
|
|
||||||
imgHtml: '',
|
|
||||||
orientation: this.typesettingType == 1 ? 'portrait' : 'landscape',
|
|
||||||
pageSize: {
|
|
||||||
width: 11906,
|
|
||||||
height: 16976
|
|
||||||
},
|
|
||||||
|
|
||||||
transform: null,
|
|
||||||
typesettingType: 1
|
|
||||||
};
|
|
||||||
},
|
|
||||||
mounted() {
|
|
||||||
this.typesettingType = 1;
|
|
||||||
},
|
|
||||||
methods: {
|
|
||||||
handleTextSelection() {
|
|
||||||
// 获取用户的文本选区
|
|
||||||
const selection = window.getSelection();
|
|
||||||
if (selection && selection.rangeCount > 0) {
|
|
||||||
const range = selection.getRangeAt(0);
|
|
||||||
const selectedText = selection.toString();
|
|
||||||
|
|
||||||
if (selectedText.trim()) {
|
|
||||||
// 保存选区
|
|
||||||
this.selectionRange = range;
|
|
||||||
|
|
||||||
// 获取选区的边界矩形
|
|
||||||
const rect = range.getBoundingClientRect();
|
|
||||||
|
|
||||||
this.toolbarStyle = {
|
|
||||||
top: `${rect.top}px`,
|
|
||||||
left: `${rect.right}px`
|
|
||||||
};
|
|
||||||
|
|
||||||
// 显示工具栏
|
|
||||||
this.showToolbar = true;
|
|
||||||
} else {
|
|
||||||
// 如果没有选中文字,隐藏工具栏
|
|
||||||
this.showToolbar = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
applyFormat(format, value) {
|
|
||||||
if (!this.selectionRange) return;
|
|
||||||
|
|
||||||
const wrapper = document.createElement('span');
|
|
||||||
switch (format) {
|
|
||||||
case 'bold':
|
|
||||||
wrapper.style.fontWeight = 'bold';
|
|
||||||
break;
|
|
||||||
case 'italic':
|
|
||||||
wrapper.style.fontStyle = 'italic';
|
|
||||||
break;
|
|
||||||
case 'color':
|
|
||||||
wrapper.style.color = value;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 用选区的内容替换为格式化后的内容
|
|
||||||
wrapper.appendChild(this.selectionRange.extractContents());
|
|
||||||
this.selectionRange.insertNode(wrapper);
|
|
||||||
|
|
||||||
// 清除选区
|
|
||||||
window.getSelection().removeAllRanges();
|
|
||||||
this.showToolbar = false;
|
|
||||||
},
|
|
||||||
|
|
||||||
downloadImage() {
|
|
||||||
this.$nextTick(() => {
|
|
||||||
this.$refs.tinymceChild.getContent('image');
|
|
||||||
return;
|
|
||||||
});
|
|
||||||
},
|
|
||||||
|
|
||||||
handlePaste(event) {
|
|
||||||
setTimeout(() => {
|
|
||||||
// 解析内容并替换表格
|
|
||||||
console.log('event.currentTarget.innerHTML at line 146:', event);
|
|
||||||
let replacedContent = this.setHtmlWord(event);
|
|
||||||
|
|
||||||
this.$nextTick(() => {
|
|
||||||
this.$refs.tinymceChild.tableSuccessCBK(replacedContent);
|
|
||||||
});
|
|
||||||
|
|
||||||
// 将新的内容插入到可编辑区域
|
|
||||||
|
|
||||||
// if (container && content) {
|
|
||||||
// const containerWidth = container.offsetWidth;
|
|
||||||
// const containerHeight = container.offsetHeight;
|
|
||||||
|
|
||||||
// const contentWidth = content.offsetWidth;
|
|
||||||
|
|
||||||
// const contentHeight = content.offsetHeight;
|
|
||||||
|
|
||||||
// // 计算缩放比例,保持等比例缩放
|
|
||||||
// const scale = Math.min(containerWidth / contentWidth, containerHeight / contentHeight);
|
|
||||||
// console.log('scale at line 60:', scale);
|
|
||||||
|
|
||||||
// // 应用缩放比例
|
|
||||||
// // content.style.transform = `scale(${scale})`;
|
|
||||||
// // content.style.transformOrigin = 'top left'; // 缩放基点
|
|
||||||
// if (content) {
|
|
||||||
// const updatedHtml = this.setHtmlWord();
|
|
||||||
|
|
||||||
// this.updatedHtml = updatedHtml.match(/<table[\s\S]*?<\/table>/i)[0];
|
|
||||||
// console.log('updatedHtml at line 114:', this.updatedHtml);
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
}, 50);
|
|
||||||
},
|
|
||||||
// 处理 td 点击事件
|
|
||||||
handleTdClick(index, content) {
|
|
||||||
alert(`Clicked TD ${index + 1}: ${content}`);
|
|
||||||
},
|
|
||||||
containsSupOrSub(element) {
|
|
||||||
// 如果当前节点是元素节点
|
|
||||||
if (element.nodeType === 1) {
|
|
||||||
// 如果是 <sup> 或 <sub> 标签,返回 true
|
|
||||||
if (element.tagName === 'SUP' || element.tagName === 'SUB') {
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
// 否则,递归检查子节点
|
|
||||||
return Array.from(element.childNodes).some((child) => this.containsSupOrSub(child));
|
|
||||||
}
|
|
||||||
// 如果不是元素节点(如文本节点),返回 false
|
|
||||||
return false;
|
|
||||||
},
|
|
||||||
setHtmlWord(setHtmlWord) {
|
|
||||||
// if (setHtmlWord.includes('<table')) {
|
|
||||||
// } else {
|
|
||||||
// return false;
|
|
||||||
// }
|
|
||||||
const html = setHtmlWord
|
|
||||||
.replace(
|
|
||||||
/<(table)([^>]*style="([^"]*)")/g, // 匹配有 style 属性的 <table>
|
|
||||||
(match, p1, p2, p3) => {
|
|
||||||
const existingStyle = p3;
|
|
||||||
|
|
||||||
// 如果现有样式不为空,则在原样式后追加新的样式
|
|
||||||
const updatedStyle = existingStyle
|
|
||||||
? existingStyle +
|
|
||||||
`; width: ${
|
|
||||||
this.typesettingType == 1 ? '488' : '736'
|
|
||||||
}pt !important; mso-padding-alt: 0.7500pt 0.7500pt 0.7500pt 0.7500pt;`
|
|
||||||
: `width: ${
|
|
||||||
this.typesettingType == 1 ? '488' : '736'
|
|
||||||
}pt !important; mso-padding-alt: 0.7500pt 0.7500pt 0.7500pt 0.7500pt;`;
|
|
||||||
var str = `<table${p2.replace(`style="${existingStyle}"`, `style="${updatedStyle}"`)}`;
|
|
||||||
return str;
|
|
||||||
}
|
|
||||||
)
|
|
||||||
.replace(/style="style=;/g, 'style="')
|
|
||||||
.replace(/;;/g, ';')
|
|
||||||
.replace(/\n/g, '<br>');
|
|
||||||
|
|
||||||
const container = document.createElement('div');
|
|
||||||
container.innerHTML = html;
|
|
||||||
|
|
||||||
const AllTd = container.querySelectorAll('table td'); // 获取 <tr> 中的所有 <td> 元素
|
|
||||||
const firstRowTdElementsAll = container.querySelectorAll('*'); // 获取第一个 <tr> 中的所有 <td> 元素
|
|
||||||
|
|
||||||
// 遍历所有 <td> 元素,添加上下边框样式
|
|
||||||
firstRowTdElementsAll.forEach((td) => {
|
|
||||||
let htmlContent = td.innerHTML;
|
|
||||||
|
|
||||||
const currentStyle = td.getAttribute('style');
|
|
||||||
if (currentStyle) {
|
|
||||||
td.setAttribute(
|
|
||||||
'style',
|
|
||||||
currentStyle +
|
|
||||||
";text-align:left;font-family:'Charis SIL' !important; font-size: 7.5000pt !important; mso-font-kerning: 1.0000pt !important; line-height: 10pt !important;mos-line-height: 10pt !important;"
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
td.setAttribute(
|
|
||||||
'style',
|
|
||||||
"text-align:left;font-family:'Charis SIL' !important; font-size: 7.5000pt !important; mso-font-kerning: 1.0000pt !important; line-height: 10pt !important;mos-line-height: 10pt !important;mso-line-height-rule: exactly !important;"
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
// 遍历所有 <td> 元素,添加上下边框样式
|
|
||||||
AllTd.forEach((td) => {
|
|
||||||
// td.textContent = td.textContent.replace(/<a[^>]*>(.*?)<\/a>/g, '$1');
|
|
||||||
// td.textContent = td.textContent.trimEnd();
|
|
||||||
|
|
||||||
if (/^-?\d+(\.\d+)?$/.test(td.textContent)) {
|
|
||||||
// 如果是纯数字(包括负数),替换负号为双负号
|
|
||||||
td.textContent = td.textContent.replace(/^-(?=\d)/, '−'); // 替换负号为 "--"
|
|
||||||
}
|
|
||||||
|
|
||||||
const hasSupOrSub = this.containsSupOrSub(td); // 检查当前 td 是否包含上下标
|
|
||||||
if (!hasSupOrSub) {
|
|
||||||
td.textContent = td.textContent.replace(/(^|\s)(\w)/g, (match, space, firstLetter) => {
|
|
||||||
// 将首字母大写,并保持原始空格或其他字符
|
|
||||||
return space + firstLetter.toUpperCase();
|
|
||||||
});
|
|
||||||
td.textContent = td.textContent.replace(/<a\b[^>]*>(.*?)<\/a>/gi, (match, innerText) => {
|
|
||||||
return innerText; // 保留链接内部的内容
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const currentStyle = td.getAttribute('style');
|
|
||||||
if (currentStyle) {
|
|
||||||
const regex = /\[\d+(?:,\d+)*\]/g;
|
|
||||||
if (regex.test(td.innerHTML)) {
|
|
||||||
// 如果匹配到,则对该 <td> 应用样式,比如改变文本颜色
|
|
||||||
td.setAttribute(
|
|
||||||
'style',
|
|
||||||
currentStyle +
|
|
||||||
';color:rgb(0,130,170) !important;border-left:none !important;mso-border-left-alt:none !important;border-right:none !important;mso-border-right-alt:none !important;border-top:none;mso-border-top-alt:none !important;border-bottom:none !important;mso-border-bottom-alt:none !important;'
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
td.setAttribute(
|
|
||||||
'style',
|
|
||||||
currentStyle +
|
|
||||||
';color:#000000 !important;border-left:none !important;mso-border-left-alt:none !important;border-right:none !important;mso-border-right-alt:none !important;border-top:none;mso-border-top-alt:none !important;border-bottom:none !important;mso-border-bottom-alt:none !important;'
|
|
||||||
);
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (regex.test(td.innerHTML)) {
|
|
||||||
// 如果匹配到,则对该 <td> 应用样式,比如改变文本颜色
|
|
||||||
td.setAttribute(
|
|
||||||
'style',
|
|
||||||
'color:rgb(0,130,170) !important;border-left:none !important;mso-border-left-alt:none !important;border-right:none !important;mso-border-right-alt:none !important;border-top:none;mso-border-top-alt:none !important;border-bottom:none !important;mso-border-bottom-alt:none !important;'
|
|
||||||
);
|
|
||||||
} else {
|
|
||||||
td.setAttribute(
|
|
||||||
'style',
|
|
||||||
'color:#000000 !important;border-left:none !important;mso-border-left-alt:none !important;border-right:none !important;mso-border-right-alt:none !important;border-top:none;mso-border-top-alt:none !important;border-bottom:none !important;mso-border-bottom-alt:none !important;'
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
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 +
|
|
||||||
';color:#000000 !important; 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',
|
|
||||||
'color:#000000 !important;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;'
|
|
||||||
);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// 获取修改后的 HTML 内容
|
|
||||||
const updatedHtml = container.innerHTML;
|
|
||||||
|
|
||||||
this.$forceUpdate();
|
|
||||||
// 输出修改后的 HTML
|
|
||||||
|
|
||||||
return updatedHtml;
|
|
||||||
},
|
|
||||||
getContent(type, data) {
|
|
||||||
if (type == 'table') {
|
|
||||||
var tableHtml = `<html xmlns:w="urn:schemas-microsoft-com:office:word">
|
|
||||||
<head>
|
|
||||||
<style>
|
|
||||||
@page {
|
|
||||||
size: A4 !important;
|
|
||||||
margin: 1in;
|
|
||||||
size: landscape; /* 强制页面方向为横向 */
|
|
||||||
}
|
|
||||||
table {
|
|
||||||
border-collapse: collapse;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
th, td {
|
|
||||||
border: 1px solid #000;
|
|
||||||
padding: 8px;
|
|
||||||
text-align: left;
|
|
||||||
}
|
|
||||||
p,th, td,tr,table {
|
|
||||||
font-size: 7.5000pt; /* 字体大小 */
|
|
||||||
|
|
||||||
margin-top: 0pt; /* 段前间距 */
|
|
||||||
margin-bottom: 0pt; /* 段后间距 */
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
${data}
|
|
||||||
</body>
|
|
||||||
</html>
|
|
||||||
|
|
||||||
`;
|
|
||||||
console.log('tableHtml at line 150:', tableHtml);
|
|
||||||
const converted = htmlDocx.asBlob(tableHtml); // 将 HTML 转换为 Word Blob
|
|
||||||
|
|
||||||
// 触发文件下载
|
|
||||||
const link = document.createElement('a');
|
|
||||||
link.href = URL.createObjectURL(converted);
|
|
||||||
link.download = 'table.docx';
|
|
||||||
link.click();
|
|
||||||
} else if (type == 'image') {
|
|
||||||
// tinymce
|
|
||||||
|
|
||||||
// 显示内容的容器
|
|
||||||
const container = this.$refs.output;
|
|
||||||
console.log('outputDiv at line 358:', container);
|
|
||||||
const outputDiv = container.querySelector('table');
|
|
||||||
console.log('outputDiv at line 360:', outputDiv);
|
|
||||||
|
|
||||||
// 使用 html2canvas 将内容转为图片
|
|
||||||
html2canvas(outputDiv, {
|
|
||||||
scale: 3, // 提高图片的分辨率,默认为1,设置为2可以使图片更清晰
|
|
||||||
logging: false, // 禁用日志输出
|
|
||||||
useCORS: true, // 允许跨域图像
|
|
||||||
allowTaint: true // 允许污染 canvas,解决图片链接不可用问题
|
|
||||||
})
|
|
||||||
// 清空现有内容,显示图片
|
|
||||||
.then((canvas) => {
|
|
||||||
// 创建一个图片对象
|
|
||||||
const imgData = canvas.toDataURL('image/png');
|
|
||||||
|
|
||||||
// 创建一个链接并下载图片
|
|
||||||
const link = document.createElement('a');
|
|
||||||
link.href = imgData;
|
|
||||||
link.download = 'image.png';
|
|
||||||
link.click();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
},
|
|
||||||
exportToWord() {
|
|
||||||
this.$nextTick(() => {
|
|
||||||
this.$refs.tinymceChild.getContent('table');
|
|
||||||
return;
|
|
||||||
});
|
|
||||||
|
|
||||||
// 获取 HTML 内容
|
|
||||||
|
|
||||||
// console.log('html at line 118:', updatedHtml);
|
|
||||||
|
|
||||||
const tableHtml = `
|
|
||||||
<html xmlns:w="urn:schemas-microsoft-com:office:word">
|
|
||||||
<head>
|
|
||||||
<style>
|
|
||||||
@page {
|
|
||||||
size: A4 !important;
|
|
||||||
margin: 1in;
|
|
||||||
size: landscape; /* 强制页面方向为横向 */
|
|
||||||
}
|
|
||||||
table {
|
|
||||||
border-collapse: collapse;
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
th, td {
|
|
||||||
border: 1px solid #000;
|
|
||||||
padding: 8px;
|
|
||||||
text-align: left;
|
|
||||||
}
|
|
||||||
p,th, td,tr,table {
|
|
||||||
font-size: 7.5000pt; /* 字体大小 */
|
|
||||||
|
|
||||||
margin-top: 0pt; /* 段前间距 */
|
|
||||||
margin-bottom: 0pt; /* 段后间距 */
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
</style>
|
|
||||||
</head>
|
|
||||||
<body>
|
|
||||||
<div align="center">
|
|
||||||
${
|
|
||||||
this.lineStyle.titleTop
|
|
||||||
? `<p
|
|
||||||
class="MsoNormal"
|
|
||||||
align="center"
|
|
||||||
style="
|
|
||||||
margin-bottom: 0pt;
|
|
||||||
mso-line-break-override: restrictions;
|
|
||||||
punctuation-wrap: simple;
|
|
||||||
text-align: center;
|
|
||||||
mso-outline-level: 1;
|
|
||||||
line-height: 10pt !important;mos-line-height: 10pt !important;
|
|
||||||
mso-line-height-rule: exactly;
|
|
||||||
|
|
||||||
"
|
|
||||||
>
|
|
||||||
<b
|
|
||||||
><span style="font-family: Charis SIL; font-size: 7.5pt"
|
|
||||||
>${this.lineStyle.titleTop}</span
|
|
||||||
></b
|
|
||||||
>
|
|
||||||
</p>`
|
|
||||||
: ``
|
|
||||||
}
|
|
||||||
|
|
||||||
${updatedHtml}
|
|
||||||
|
|
||||||
${
|
|
||||||
this.lineStyle.titleBot
|
|
||||||
? ` <p
|
|
||||||
class="MsoNormal"
|
|
||||||
style="
|
|
||||||
margin-bottom: 0pt;
|
|
||||||
mso-line-break-override: restrictions;
|
|
||||||
punctuation-wrap: simple;
|
|
||||||
mso-outline-level: 1;
|
|
||||||
line-height: 10pt !important;mos-line-height: 10pt !important;
|
|
||||||
mso-line-height-rule: exactly;
|
|
||||||
text-align: justify; /* 使用 CSS 标准的两端对齐 */
|
|
||||||
mso-text-align: justify; /* 确保 Word 中应用两端对齐 */
|
|
||||||
"
|
|
||||||
>
|
|
||||||
<span
|
|
||||||
style="
|
|
||||||
mso-spacerun: 'yes';
|
|
||||||
font-family: 宋体;
|
|
||||||
mso-ascii-font-family: 'Charis SIL';
|
|
||||||
mso-hansi-font-family: 'Charis SIL';
|
|
||||||
mso-bidi-font-family: 'Charis SIL';
|
|
||||||
font-size: 7.5pt;
|
|
||||||
mso-font-kerning: 1pt;
|
|
||||||
"
|
|
||||||
>${this.lineStyle.titleBot}</span
|
|
||||||
>
|
|
||||||
</p>`
|
|
||||||
: ``
|
|
||||||
}
|
|
||||||
|
|
||||||
</div>
|
|
||||||
</body>
|
|
||||||
</html>`;
|
|
||||||
console.log('tableHtml at line 150:', tableHtml);
|
|
||||||
const converted = htmlDocx.asBlob(tableHtml); // 将 HTML 转换为 Word Blob
|
|
||||||
|
|
||||||
// 触发文件下载
|
|
||||||
const link = document.createElement('a');
|
|
||||||
link.href = URL.createObjectURL(converted);
|
|
||||||
link.download = 'table.docx';
|
|
||||||
link.click();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style scoped>
|
|
||||||
table {
|
|
||||||
border-collapse: collapse;
|
|
||||||
width: 100%;
|
|
||||||
margin: 20px 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
th,
|
|
||||||
td {
|
|
||||||
border: 1px dashed #dcdfe6;
|
|
||||||
padding: 8px;
|
|
||||||
text-align: center;
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
table {
|
|
||||||
border-top: 2px solid #000;
|
|
||||||
border-bottom: 1px solid #000;
|
|
||||||
}
|
|
||||||
th {
|
|
||||||
border-bottom: 1px solid #000;
|
|
||||||
}
|
|
||||||
th input,
|
|
||||||
td input {
|
|
||||||
width: 100%;
|
|
||||||
border: none;
|
|
||||||
outline: none;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
th input,
|
|
||||||
td input ::placeholder {
|
|
||||||
color: #aaa !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
.controls {
|
|
||||||
margin: 0 0 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.drag-handle {
|
|
||||||
cursor: move;
|
|
||||||
}
|
|
||||||
::v-deep .paste-area {
|
|
||||||
height: auto; /* A4纸高度 */
|
|
||||||
background: white; /* 纸张背景 */
|
|
||||||
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1); /* 添加阴影模拟纸张浮起 */
|
|
||||||
border: 1px solid #ddd; /* 模拟纸张边框 */
|
|
||||||
/* padding: 25.4mm 19.1mm; //内边距 */
|
|
||||||
box-sizing: border-box; /* 确保内边距不会影响整体尺寸 */
|
|
||||||
transform-origin: top left;
|
|
||||||
}
|
|
||||||
::v-deep .paste-area table {
|
|
||||||
/* border-top: 2px solid #000 !important; */
|
|
||||||
/* border-bottom: 1px solid #000 !important; */
|
|
||||||
margin-left: 0 !important;
|
|
||||||
margin-right: 0 !important;
|
|
||||||
margin: 0 auto !important;
|
|
||||||
}
|
|
||||||
::v-deep .paste-area table td {
|
|
||||||
border-top: none !important;
|
|
||||||
border-bottom: none !important;
|
|
||||||
border: 1px dashed #dcdfe6 !important;
|
|
||||||
/* display: flex;
|
|
||||||
align-items: center; */
|
|
||||||
}
|
|
||||||
::v-deep .paste-area table td p {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
::v-deep .paste-area table .MsoNormal {
|
|
||||||
max-width: 200px !important; /* 限制容器宽度 */
|
|
||||||
|
|
||||||
word-wrap: break-word !important;
|
|
||||||
overflow-wrap: break-word !important;
|
|
||||||
}
|
|
||||||
.text-container {
|
|
||||||
position: relative;
|
|
||||||
padding: 20px;
|
|
||||||
border: 1px solid #ccc;
|
|
||||||
margin: 20px;
|
|
||||||
font-size: 16px;
|
|
||||||
line-height: 1.5;
|
|
||||||
}
|
|
||||||
|
|
||||||
.toolbar {
|
|
||||||
position: absolute;
|
|
||||||
display: flex;
|
|
||||||
gap: 5px;
|
|
||||||
padding: 5px;
|
|
||||||
background-color: white;
|
|
||||||
border: 1px solid #ccc;
|
|
||||||
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2);
|
|
||||||
z-index: 10;
|
|
||||||
}
|
|
||||||
|
|
||||||
.toolbar button {
|
|
||||||
padding: 5px 10px;
|
|
||||||
font-size: 16px;
|
|
||||||
cursor: pointer;
|
|
||||||
border: none;
|
|
||||||
background: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.toolbar button:hover {
|
|
||||||
background-color: #f0f0f0;
|
|
||||||
}
|
|
||||||
::v-deep #output table tr td {
|
|
||||||
border: 1px dashed #dcdfe6 !important;
|
|
||||||
border-left: 1px dashed #dcdfe6 !important;
|
|
||||||
border-right: 1px dashed #dcdfe6 !important;
|
|
||||||
}
|
|
||||||
::v-deep #output table tr td p {
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
}
|
|
||||||
.modal {
|
|
||||||
z-index: 1000; /* 保证模态框 z-index 低于颜色选择框 */
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
File diff suppressed because it is too large
Load Diff
@@ -54,7 +54,8 @@ Vue.filter('formatDate', function(originVal) {
|
|||||||
})
|
})
|
||||||
// 引入wps文档编辑
|
// 引入wps文档编辑
|
||||||
import mammoth from "mammoth";
|
import mammoth from "mammoth";
|
||||||
|
import commonJS from '@/common/js/commonJS.js'
|
||||||
|
Vue.prototype.$commonJS = commonJS
|
||||||
|
|
||||||
Vue.prototype.Common = Common;
|
Vue.prototype.Common = Common;
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user