<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Markdown 转 Word/Excel (极速版+一键复制)</title>
<!-- 1. Markdown 解析 -->
<script src="https://cdn.jsdelivr.net/npm/marked/marked.min.js"></script>
<!-- 2. Excel 导出库 -->
<script src="https://cdn.sheetjs.com/xlsx-latest/package/dist/xlsx.full.min.js"></script>
<!-- 3. 文件保存库 -->
<script src="https://cdnjs.cloudflare.com/ajax/libs/FileSaver.js/2.0.5/FileSaver.min.js"></script>
<!-- 4. Word 导出库 -->
<script src="https://unpkg.com/html-docx-js/dist/html-docx.js"></script>
<!-- 5. KaTeX 公式渲染引擎 -->
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/[email protected]/dist/katex.min.css">
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/katex.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/[email protected]/dist/contrib/auto-render.min.js"></script>
<style>
:root { --primary: #2563eb; --word: #2b579a; --excel: #217346; --copy: #f59e0b; }
body { font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif; margin: 0; padding: 0; background-color: #f3f4f6; height: 100vh; display: flex; flex-direction: column; }
header { background: white; padding: 0 20px; height: 60px; display: flex; align-items: center; justify-content: space-between; box-shadow: 0 1px 3px rgba(0,0,0,0.1); flex-shrink: 0; }
h1 { font-size: 18px; color: #1f2937; margin: 0; display: flex; align-items: center; gap: 10px; }
.badge { font-size: 12px; background: #e0f2fe; color: #0369a1; padding: 2px 8px; border-radius: 4px; font-weight: normal; }
.actions { display: flex; gap: 12px; }
button { padding: 8px 16px; border: none; border-radius: 6px; cursor: pointer; font-size: 14px; font-weight: 500; color: white; display: flex; align-items: center; gap: 6px; transition: opacity 0.2s; }
button:hover { opacity: 0.9; }
button:active { transform: translateY(1px); }
.btn-word { background-color: var(--word); }
.btn-excel { background-color: var(--excel); }
.btn-copy { background-color: white; color: #333; border: 1px solid #ddd; }
.btn-copy:hover { background-color: #f9fafb; }
.main-area { display: flex; flex: 1; padding: 20px; gap: 20px; overflow: hidden; }
.editor-box, .preview-box { flex: 1; display: flex; flex-direction: column; background: white; border-radius: 8px; border: 1px solid #e5e7eb; overflow: hidden; }
/* 头部样式调整,以容纳复制按钮 */
.box-header { padding: 10px 15px; background: #f9fafb; border-bottom: 1px solid #e5e7eb; font-size: 14px; font-weight: 600; color: #374151; display: flex; justify-content: space-between; align-items: center; height: 40px;}
textarea { flex: 1; border: none; padding: 20px; resize: none; outline: none; font-family: 'Consolas', 'Monaco', monospace; font-size: 14px; line-height: 1.6; color: #333; }
#preview { flex: 1; padding: 20px; overflow-y: auto; color: #333; }
/* 预览区样式 */
#preview table { border-collapse: collapse; width: 100%; margin: 15px 0; border: 1px solid #ddd; }
#preview th, #preview td { border: 1px solid #000; padding: 8px 12px; text-align: left; }
#preview th { background-color: #f3f4f6; font-weight: bold; }
#preview p { margin: 10px 0; line-height: 1.6; }
/* 隐藏 KaTeX 的辅助 MathML,防止 Excel 导出重复 */
.katex-mathml { display: none; }
</style>
</head>
<body>
<header>
<h1>Markdown 转换工具 <span class="badge">V3.5 极速版</span></h1>
<div class="actions">
<button class="btn-word" onclick="toWord()"><span>📄</span> 导出 Word</button>
<button class="btn-excel" onclick="toExcel()"><span>📊</span> 导出 Excel</button>
</div>
</header>
<div class="main-area">
<div class="editor-box">
<div class="box-header">Markdown 输入</div>
<textarea id="input" placeholder="输入内容...
# 财务指标分析
$$\text{可售比} = \frac{\text{地上可售面积} + \text{地下可售面积}}{\text{总建筑面积}}$$
**【案例实证:面积折损分析】**
| 楼栋 | 地上 | 地下 |
| --- | --- | --- |
| 1号楼 | 5000 | 200 |
"></textarea>
</div>
<div class="preview-box">
<div class="box-header">
<span>实时预览</span>
<!-- 新增复制按钮 -->
<button class="btn-copy" onclick="copyPreviewContent()">📋 一键复制</button>
</div>
<div id="preview"></div>
</div>
</div>
<script>
const input = document.getElementById('input');
const preview = document.getElementById('preview');
// 1. 核心渲染逻辑 (V3 逻辑)
function render() {
preview.innerHTML = marked.parse(input.value);
renderMathInElement(preview, {
delimiters: [
{left: '$$', right: '$$', display: true}, // 块级公式
{left: '$', right: '$', display: false} // 行内公式
],
throwOnError: false
});
}
input.addEventListener('input', render);
render();
// ---------------------------------------------------------
// 新功能:一键复制预览内容
// ---------------------------------------------------------
function copyPreviewContent() {
// 创建一个选区,选中 preview 里的所有内容
const range = document.createRange();
range.selectNodeContents(preview);
const selection = window.getSelection();
selection.removeAllRanges();
selection.addRange(range);
try {
// 执行浏览器原生的复制命令
const successful = document.execCommand('copy');
if(successful) {
// 简单的视觉反馈:更改按钮文字
const btn = document.querySelector('.btn-copy');
const originalText = btn.innerText;
btn.innerText = "✅ 已复制!";
btn.style.borderColor = "#217346";
btn.style.color = "#217346";
setTimeout(() => {
btn.innerText = originalText;
btn.style.borderColor = "#ddd";
btn.style.color = "#333";
}, 1500);
} else {
alert('复制失败,请手动全选复制。');
}
} catch (err) {
alert('浏览器不支持自动复制');
}
// 取消选中,以免干扰视线
selection.removeAllRanges();
}
// ---------------------------------------------------------
// 导出 Word (V3 逻辑,无截图引擎)
// ---------------------------------------------------------
function toWord() {
const content = `
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<style>
body { font-family: 'SimSun', serif; }
table { border-collapse: collapse; width: 100%; margin-bottom: 20px; }
td, th { border: 1px solid #000; padding: 8px; }
.katex { font-size: 1.1em; }
</style>
</head>
<body>
${preview.innerHTML}
</body>
</html>
`;
const converted = htmlDocx.asBlob(content);
saveAs(converted, '文档导出.docx');
}
// ---------------------------------------------------------
// 导出 Excel (V3 逻辑,智能分行)
// ---------------------------------------------------------
function toExcel() {
const wb = XLSX.utils.book_new();
let rows = [];
const nodes = preview.children;
for (let node of nodes) {
const tagName = node.tagName.toLowerCase();
if (tagName === 'table') {
if(rows.length > 0) rows.push([""]);
const tableSheet = XLSX.utils.table_to_sheet(node);
const tableData = XLSX.utils.sheet_to_json(tableSheet, {header: 1});
tableData.forEach(r => rows.push(r));
rows.push([""]);
} else {
let text = node.innerText.trim();
if (text) {
rows.push([text]);
rows.push([""]);
}
}
}
const ws = XLSX.utils.aoa_to_sheet(rows);
ws['!cols'] = [{ wch: 80 }, { wch: 15 }, { wch: 15 }];
XLSX.utils.book_append_sheet(wb, ws, "导出数据");
XLSX.writeFile(wb, '数据导出.xlsx');
}
</script>
</body>
</html>
index.html
index.html