提交
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;
|
||||
},
|
||||
|
||||
// 通用递归方法
|
||||
|
||||
};
|
||||
@@ -70,7 +70,7 @@
|
||||
<el-button icon="el-icon-finished" type="primary" style="width: 350px" @click="pushOnline()"> Push Online </el-button>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<div
|
||||
class="right-side"
|
||||
style="
|
||||
@@ -224,7 +224,14 @@
|
||||
</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-item label="Top Title :">
|
||||
<el-input placeholder="Please enter the table title..." v-model="lineStyle.titleTop"> </el-input>
|
||||
@@ -234,10 +241,8 @@
|
||||
<font style="color: #f56c6c; margin-right: 5px">*</font>
|
||||
Content :
|
||||
</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 label="Bottom Title :">
|
||||
<el-input placeholder="Please enter the table title..." v-model="lineStyle.titleBot"> </el-input>
|
||||
@@ -548,13 +553,12 @@ export default {
|
||||
},
|
||||
// 表格段落
|
||||
MTxtTable(val, num) {
|
||||
// this.lineStyle.p_main_id = val.p_main_id;
|
||||
this.lineStyle.textarea = '';
|
||||
this.lineStyle.titleCon = '';
|
||||
this.lineTable = [];
|
||||
|
||||
this.lineStyle = {};
|
||||
this.threeVisible = true;
|
||||
this.typesettingType = '1';
|
||||
|
||||
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';
|
||||
const toolbar =
|
||||
'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;
|
||||
}
|
||||
i span{
|
||||
@@ -87,6 +87,12 @@ const tableStyle = ` b span{
|
||||
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 {
|
||||
@@ -276,7 +282,8 @@ export default {
|
||||
const reader = new FileReader();
|
||||
reader.onload = function (e) {
|
||||
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);
|
||||
});
|
||||
};
|
||||
@@ -309,12 +316,14 @@ export default {
|
||||
const editorBody = ed.getBody();
|
||||
// 创建 MutationObserver 监听内容变化
|
||||
const observer = new MutationObserver(() => {
|
||||
const hasHorizontalScrollbar = editorBody.scrollWidth > editorBody.clientWidth;
|
||||
if (hasHorizontalScrollbar) {
|
||||
console.log('TinyMCE 出现横向滚动条');
|
||||
} else {
|
||||
console.log('没有横向滚动条');
|
||||
}
|
||||
console.log('editorBody at line 313:', editorBody);
|
||||
// _this.updateTableStyles(editorBody, _this.typesettingType);
|
||||
// const hasHorizontalScrollbar = editorBody.scrollWidth > editorBody.clientWidth;
|
||||
// if (hasHorizontalScrollbar) {
|
||||
// console.log('TinyMCE 出现横向滚动条');
|
||||
// } else {
|
||||
// console.log('没有横向滚动条');
|
||||
// }
|
||||
});
|
||||
|
||||
// 监听子节点和内容的变化
|
||||
@@ -338,7 +347,10 @@ export default {
|
||||
let content = ed.getContent(); // 获取内容
|
||||
content = content.replace(/<strong>/g, '<b>').replace(/<\/strong>/g, '</b>');
|
||||
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 文件中的表格
|
||||
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) {
|
||||
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 内容更新到编辑器
|
||||
var html = this.$commonJS.updateTableStyles(container, this.typesettingType);
|
||||
var editor = window.tinymce.activeEditor; // 将外部 DOM 内容更新到编辑器
|
||||
const container1 = document.createElement('div');
|
||||
container1.innerHTML = html; // html 是更新后的 HTML 内容
|
||||
|
||||
// 更新编辑器内容
|
||||
editor.setContent(container1.innerHTML);
|
||||
|
||||
// 触发编辑器内容变化后,如果需要,可能还要设置编辑器的样式
|
||||
editor.focus(); // 聚焦到编辑器
|
||||
editor.setContent(container1.innerHTML); // 更新编辑器内容
|
||||
editor.focus(); // 聚焦到编辑器// 触发编辑器内容变化后,如果需要,可能还要设置编辑器的样式
|
||||
},
|
||||
//销毁富文本
|
||||
destroyTinymce() {
|
||||
@@ -682,19 +486,6 @@ export default {
|
||||
console.log(arr, '222');
|
||||
const _this = this;
|
||||
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() {
|
||||
|
||||
@@ -4,12 +4,12 @@
|
||||
<div class="drag-drop-area" @dragover.prevent="onDragOver" @dragleave="onDragLeave" @drop.prevent="onDrop" @click="clickUpload">
|
||||
<p>将 Word 文件拖拽到此处</p>
|
||||
</div>
|
||||
<el-dialog append-to-body
|
||||
<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">
|
||||
@@ -43,28 +43,34 @@ export default {
|
||||
data() {
|
||||
return {
|
||||
tables: [], // 保存解析后的表格数据
|
||||
addVisible:false,
|
||||
addVisible: false
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
clickUpload(){
|
||||
addVisCancle(){
|
||||
this.addVisible = false;
|
||||
},
|
||||
clickUpload() {
|
||||
this.tables = [];
|
||||
var that = this;
|
||||
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();
|
||||
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;
|
||||
that.extractTablesFromWord(arrayBuffer, function (tablesHtml) {
|
||||
console.log('tablesHtml at line 61:', that.tables);
|
||||
that.addVisible = true;
|
||||
});
|
||||
};
|
||||
reader.readAsArrayBuffer(file);
|
||||
}
|
||||
});
|
||||
input.click();
|
||||
},
|
||||
// 拖拽事件处理
|
||||
onDragOver(event) {
|
||||
@@ -74,9 +80,8 @@ export default {
|
||||
event.currentTarget.style.borderColor = '#ccc';
|
||||
},
|
||||
|
||||
|
||||
// 提取 Word 文件中的表格
|
||||
extractTablesFromWord(arrayBuffer, callback) {
|
||||
// 提取 Word 文件中的表格
|
||||
extractTablesFromWord(arrayBuffer, callback) {
|
||||
const zip = new JSZip();
|
||||
var that = this;
|
||||
zip.loadAsync(arrayBuffer)
|
||||
@@ -91,18 +96,22 @@ export default {
|
||||
|
||||
let html = '';
|
||||
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) {
|
||||
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) {
|
||||
console.error('解析 Word 文件出错:', err);
|
||||
@@ -126,7 +135,7 @@ export default {
|
||||
orientation: 'portrait' // 默认纵向
|
||||
}));
|
||||
|
||||
this.addVisible=true
|
||||
this.addVisible = true;
|
||||
var html = '';
|
||||
for (let table of this.tables) {
|
||||
html += this.convertTableToHtml(table.html);
|
||||
@@ -152,7 +161,7 @@ export default {
|
||||
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';
|
||||
@@ -167,8 +176,8 @@ export default {
|
||||
reader.readAsArrayBuffer(file);
|
||||
});
|
||||
},
|
||||
// 将 XML 表格转换为 HTML
|
||||
convertTableToHtml(tableNode) {
|
||||
// 将 XML 表格转换为 HTML
|
||||
convertTableToHtml(tableNode) {
|
||||
const rows = tableNode.getElementsByTagName('w:tr');
|
||||
let html = '<table border="1" style="border-collapse: collapse;">';
|
||||
for (let row of rows) {
|
||||
@@ -257,7 +266,6 @@ export default {
|
||||
var _this = this;
|
||||
// 更新表格样式
|
||||
const tables = container.querySelectorAll('table');
|
||||
|
||||
tables.forEach((table) => {
|
||||
table.setAttribute(
|
||||
'style',
|
||||
@@ -291,7 +299,7 @@ export default {
|
||||
console.log('匹配到带有数字的方括号内容');
|
||||
// 为匹配的元素添加样式类
|
||||
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 = '';
|
||||
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(); // 聚焦到编辑器
|
||||
this.$forceUpdate();
|
||||
return html
|
||||
|
||||
},
|
||||
|
||||
|
||||
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) {
|
||||
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文档编辑
|
||||
import mammoth from "mammoth";
|
||||
|
||||
import commonJS from '@/common/js/commonJS.js'
|
||||
Vue.prototype.$commonJS = commonJS
|
||||
|
||||
Vue.prototype.Common = Common;
|
||||
|
||||
|
||||
Reference in New Issue
Block a user