516 lines
107 KiB
HTML
516 lines
107 KiB
HTML
<!DOCTYPE html>
|
||
<html lang="zh-CN">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
<title>多功能Markdown编辑器</title>
|
||
<link rel="stylesheet" href="/static/css/markdown.css">
|
||
</head>
|
||
<body>
|
||
<div class="header">
|
||
<h1>多功能Markdown编辑器</h1>
|
||
<p>支持表格合并和完整Markdown语法</p>
|
||
</div>
|
||
|
||
<div class="main-content">
|
||
<div class="syntax-guide">
|
||
<div class="guide-card">
|
||
<h3>📊 表格合并语法</h3>
|
||
<ul>
|
||
<li><span class="syntax">^2#</span> 垂直合并2个单元格(共3行)</li>
|
||
<li><span class="syntax">>3#</span> 水平合并3个单元格(共4列)</li>
|
||
<li><span class="syntax">^2>3#</span> 同时垂直和水平合并</li>
|
||
</ul>
|
||
</div>
|
||
|
||
<div class="guide-card">
|
||
<h3>📝 基础Markdown语法</h3>
|
||
<ul>
|
||
<li><span class="syntax"># 标题</span> 一级标题</li>
|
||
<li><span class="syntax">**粗体**</span> 粗体文本</li>
|
||
<li><span class="syntax">*斜体*</span> 斜体文本</li>
|
||
<li><span class="syntax">- 列表项</span> 无序列表</li>
|
||
<li><span class="syntax">1. 列表项</span> 有序列表</li>
|
||
</ul>
|
||
</div>
|
||
|
||
<div class="guide-card">
|
||
<h3>🔗 高级语法</h3>
|
||
<ul>
|
||
<li><span class="syntax">[链接](URL)</span> 超链接</li>
|
||
<li><span class="syntax"></span> 图片</li>
|
||
<li><span class="syntax">`代码`</span> 行内代码</li>
|
||
<li><span class="syntax">```代码块```</span> 代码块</li>
|
||
<li><span class="syntax">> 引用</span> 引用块</li>
|
||
</ul>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="container">
|
||
<div class="editor">
|
||
<h3>📝 Markdown编辑器</h3>
|
||
<textarea id="markdownInput" placeholder="在此输入Markdown内容...
|
||
|
||
💡 提示:支持表格合并和完整Markdown语法
|
||
|
||
示例:
|
||
# 标题
|
||
**粗体文本**
|
||
- 列表项
|
||
| 表格 | 示例 |
|
||
|---|---|
|
||
| 单元格 | ^2# |"></textarea>
|
||
<div class="toolbar">
|
||
<button onclick="convertToHTML()">🔄 转换为HTML</button>
|
||
<button onclick="loadExample()">📋 加载示例</button>
|
||
<button onclick="clearEditor()">🗑️ 清空编辑器</button>
|
||
<button onclick="copyToClipboard()">📄 复制HTML</button>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="preview">
|
||
<h3>👁️ HTML预览</h3>
|
||
<div id="htmlPreview"></div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="guide-card">
|
||
<h3>📚 使用示例</h3>
|
||
<div>
|
||
<h4>复杂表格合并示例:</h4>
|
||
<pre>| 部门 | 产品A | 产品B | 产品C | 总计 |
|
||
|---|---|---|---|---|
|
||
| 销售部 | 100 | 150 | 200 | 450 |
|
||
| 技术部 | ^2# | 80 | 120 | 200 |
|
||
| 市场部 | 60 | 90 | 110 | 260 |
|
||
| 年度汇总>3# | | | | 910 |</pre>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<div class="footer">
|
||
<p>© 2024 多功能Markdown编辑器 | 支持表格合并和完整Markdown语法</p>
|
||
</div>
|
||
|
||
<script src="/static/js/markdown-plugin-table.js"></script>
|
||
<script src="/static/js/markdown-parser.js"></script>
|
||
<script>
|
||
const markdownParser = new MarkdownParser();
|
||
|
||
function convertToHTML() {
|
||
const markdown = document.getElementById('markdownInput').value;
|
||
try {
|
||
document.getElementById('htmlPreview').innerHTML = markdownParser.parseFull(markdown);
|
||
showStatus('转换成功!', 'success');
|
||
} catch (error) {
|
||
document.getElementById('htmlPreview').innerHTML = '<p style="color: red;">转换失败:' + error.message + '</p>';
|
||
showStatus('转换失败:' + error.message, 'error');
|
||
}
|
||
}
|
||
|
||
function loadExample() {
|
||
document.getElementById('markdownInput').value = `### 标题分级
|
||
|
||
# 一级标题
|
||
## 二级标题
|
||
### 三级标题
|
||
#### 四级标题
|
||
##### 五级标题
|
||
###### 六级标题
|
||
|
||
---
|
||
|
||
### 代码示例
|
||
|
||
\`\`\`javascript
|
||
// 计算年度总销售额
|
||
const totalSales = 100 + 120 + 150 + 180;
|
||
console.log("年度总销售额:", totalSales + "万");
|
||
\`\`\`
|
||
|
||
### 无序列表
|
||
|
||
- **粗体**: 550万
|
||
- **粗体**: 15%
|
||
- *斜体*: 约 9%
|
||
|
||
### 有序列表
|
||
|
||
1.**粗体**: 550万
|
||
|
||
2.**粗体**: 15%
|
||
|
||
3.*斜体*: 约 9%
|
||
|
||
---
|
||
|
||
### 销售表格
|
||
|
||
这是一个包含**合并单元格**的销售报表示例。\`这行内代码\`
|
||
|
||
| 项目>1# | |第一季度 | 第二季度 | 第三季度 | 第四季度 |
|
||
|---|---|---|---|---|---|
|
||
| 销售额>1# | |100万 | 120万 | 150万 | 180万 |
|
||
| 利润>1# | |30万| 30万 | 40万 | 50万 |
|
||
| 成本>1# | |70万 | 90万 | 110万 | 130万 |
|
||
| 区域汇总^1#| 北京地区 |30万 | 30万 | 30万 | 30万 |
|
||
| | 上海地区 |70万 | 80万 | 90万 | 30万 |
|
||
| 总计>1# | |400万 | 450万 | 500万 | 550万 |
|
||
|
||
> 注意:这里是注释。
|
||
|
||
### 相关链接
|
||
|
||
- [查看详细报告](https://example.com/report)
|
||
- [下载数据表格](https://example.com/download)
|
||
|
||

|
||
|
||

|
||
---
|
||
|
||
*最后更新: 2024年1月*`;
|
||
convertToHTML();
|
||
}
|
||
|
||
function clearEditor() {
|
||
document.getElementById('markdownInput').value = '';
|
||
document.getElementById('htmlPreview').innerHTML = '';
|
||
showStatus('编辑器已清空', 'success');
|
||
}
|
||
|
||
function copyToClipboard() {
|
||
const html = document.getElementById('htmlPreview').innerHTML;
|
||
if (!html) {
|
||
showStatus('没有内容可复制', 'error');
|
||
return;
|
||
}
|
||
|
||
const tempTextArea = document.createElement('textarea');
|
||
tempTextArea.value = `<!DOCTYPE html>
|
||
<html lang="zh-CN">
|
||
<head>
|
||
<meta charset="UTF-8">
|
||
<meta name="viewport" content="width=device-width, initial-scale=1.0">
|
||
<title></title>
|
||
<style>
|
||
:root {
|
||
--primary-color: #2c3e50;
|
||
--secondary-color: #3498db;
|
||
--accent-color: #e74c3c;
|
||
--light-bg: #f8f9fa;
|
||
--dark-bg: #2c3e50;
|
||
--text-color: #333;
|
||
--border-color: #e0e0e0;
|
||
--shadow: 0 2px 10px rgba(0, 0, 0, 0.1);
|
||
}
|
||
|
||
* {
|
||
margin: 5px;
|
||
padding: 0;
|
||
box-sizing: border-box;
|
||
}
|
||
|
||
body {
|
||
font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;
|
||
max-width: 1400px;
|
||
margin: 0 auto;
|
||
padding: 20px;
|
||
line-height: 1.6;
|
||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||
min-height: 100vh;
|
||
}
|
||
|
||
.header {
|
||
text-align: center;
|
||
margin-bottom: 30px;
|
||
color: white;
|
||
}
|
||
|
||
.header h1 {
|
||
font-size: 2.5rem;
|
||
margin-bottom: 10px;
|
||
text-shadow: 2px 2px 4px rgba(0, 0, 0, 0.3);
|
||
}
|
||
|
||
.header p {
|
||
font-size: 1.1rem;
|
||
opacity: 0.9;
|
||
}
|
||
|
||
.main-content {
|
||
background: white;
|
||
border-radius: 15px;
|
||
padding: 30px;
|
||
box-shadow: var(--shadow);
|
||
margin-bottom: 30px;
|
||
}
|
||
|
||
.container {
|
||
display: flex;
|
||
gap: 30px;
|
||
margin-bottom: 40px;
|
||
}
|
||
|
||
.editor, .preview {
|
||
flex: 1;
|
||
border: 2px solid var(--border-color);
|
||
padding: 25px;
|
||
border-radius: 10px;
|
||
background: white;
|
||
transition: all 0.3s ease;
|
||
}
|
||
|
||
.editor:hover, .preview:hover {
|
||
border-color: var(--secondary-color);
|
||
box-shadow: 0 4px 15px rgba(52, 152, 219, 0.2);
|
||
}
|
||
|
||
.editor h3, .preview h3 {
|
||
color: var(--primary-color);
|
||
margin-bottom: 15px;
|
||
font-size: 1.3rem;
|
||
border-bottom: 2px solid var(--secondary-color);
|
||
padding-bottom: 10px;
|
||
}
|
||
|
||
textarea {
|
||
width: 100%;
|
||
height: 400px;
|
||
font-family: 'Fira Code', 'Courier New', monospace;
|
||
font-size: 14px;
|
||
border: 1px solid var(--border-color);
|
||
border-radius: 8px;
|
||
padding: 15px;
|
||
resize: vertical;
|
||
transition: all 0.3s ease;
|
||
background: #fafafa;
|
||
}
|
||
|
||
textarea:focus {
|
||
outline: none;
|
||
border-color: var(--secondary-color);
|
||
box-shadow: 0 0 0 3px rgba(52, 152, 219, 0.1);
|
||
background: white;
|
||
}
|
||
|
||
.toolbar {
|
||
margin-top: 20px;
|
||
display: flex;
|
||
gap: 10px;
|
||
flex-wrap: wrap;
|
||
}
|
||
|
||
button {
|
||
padding: 12px 24px;
|
||
background: linear-gradient(135deg, var(--secondary-color), #2980b9);
|
||
color: white;
|
||
border: none;
|
||
border-radius: 8px;
|
||
cursor: pointer;
|
||
font-size: 14px;
|
||
font-weight: 600;
|
||
transition: all 0.3s ease;
|
||
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.1);
|
||
}
|
||
|
||
button:hover {
|
||
transform: translateY(-2px);
|
||
box-shadow: 0 4px 15px rgba(52, 152, 219, 0.3);
|
||
}
|
||
|
||
button:active {
|
||
transform: translateY(0);
|
||
}
|
||
|
||
#htmlPreview {
|
||
min-height: 400px;
|
||
padding: 15px;
|
||
border: 1px solid var(--border-color);
|
||
border-radius: 8px;
|
||
background: #fafafa;
|
||
overflow-y: auto;
|
||
}
|
||
|
||
#htmlPreview::-webkit-scrollbar {
|
||
display: none;
|
||
}
|
||
|
||
.syntax-guide {
|
||
display: grid;
|
||
grid-template-columns: repeat(auto-fit, minmax(300px, 1fr));
|
||
gap: 20px;
|
||
margin-bottom: 30px;
|
||
}
|
||
|
||
.guide-card {
|
||
background: var(--light-bg);
|
||
padding: 20px;
|
||
border-radius: 10px;
|
||
border-left: 4px solid var(--secondary-color);
|
||
}
|
||
|
||
.guide-card h3 {
|
||
color: var(--primary-color);
|
||
margin-bottom: 15px;
|
||
font-size: 1.2rem;
|
||
}
|
||
|
||
.syntax {
|
||
background: #2c3e50;
|
||
color: white;
|
||
padding: 8px 12px;
|
||
border-radius: 5px;
|
||
font-family: 'Fira Code', monospace;
|
||
font-size: 13px;
|
||
margin: 5px 0;
|
||
display: inline-block;
|
||
}
|
||
|
||
|
||
@keyframes slideIn {
|
||
from {
|
||
transform: translateY(-10px);
|
||
opacity: 0;
|
||
}
|
||
to {
|
||
transform: translateY(0);
|
||
opacity: 1;
|
||
}
|
||
}
|
||
|
||
/* Blockquote 样式 - 注释样式,字体变小,颜色变灰 */
|
||
blockquote {
|
||
background: linear-gradient(135deg, #f8f9fa, #e9ecef);
|
||
border-left: 4px solid #6c757d;
|
||
margin: 20px 0;
|
||
padding: 15px 20px;
|
||
font-size: 0.9em;
|
||
color: #6c757d;
|
||
font-style: italic;
|
||
border-radius: 0 8px 8px 0;
|
||
position: relative;
|
||
}
|
||
|
||
blockquote::before {
|
||
content: "❝";
|
||
font-size: 2em;
|
||
color: #adb5bd;
|
||
position: absolute;
|
||
left: 10px;
|
||
top: 5px;
|
||
opacity: 0.5;
|
||
}
|
||
|
||
blockquote p {
|
||
margin: 0;
|
||
line-height: 1.5;
|
||
}
|
||
|
||
/* CodeBlock 样式 - 更鲜艳的样式 */
|
||
pre {
|
||
background: linear-gradient(135deg, #2d3748, #4a5568);
|
||
border: 1px solid #4a5568;
|
||
border-radius: 8px;
|
||
padding: 20px;
|
||
margin: 20px 0;
|
||
overflow-x: auto;
|
||
box-shadow: 0 4px 15px rgba(0, 0, 0, 0.2);
|
||
position: relative;
|
||
white-space: pre;
|
||
font-family: 'Fira Code', monospace;
|
||
font-size: 13px;
|
||
line-height: 1.4;
|
||
color: #e2e8f0;
|
||
}
|
||
|
||
pre::before {
|
||
content: "</>";
|
||
position: absolute;
|
||
top: 10px;
|
||
right: 15px;
|
||
color: #81e6d9;
|
||
font-size: 0.8em;
|
||
font-weight: bold;
|
||
opacity: 0.7;
|
||
|
||
}
|
||
|
||
|
||
/* 行内代码样式 */
|
||
:not(pre) > code {
|
||
background: linear-gradient(135deg, #fed7e2, #fbb6ce);
|
||
color: #702459;
|
||
padding: 2px 6px;
|
||
border-radius: 4px;
|
||
line-height: 1.4;
|
||
border: 1px solid #fbb6ce;
|
||
}
|
||
|
||
.footer {
|
||
text-align: center;
|
||
color: white;
|
||
margin-top: 30px;
|
||
opacity: 0.8;
|
||
}
|
||
|
||
@media (max-width: 768px) {
|
||
.container {
|
||
flex-direction: column;
|
||
}
|
||
|
||
.syntax-guide {
|
||
grid-template-columns: 1fr;
|
||
}
|
||
|
||
.header h1 {
|
||
font-size: 2rem;
|
||
}
|
||
}
|
||
|
||
</style>
|
||
</head>
|
||
<body><div id="htmlPreview">` + html + `</div></body></html>`;
|
||
document.body.appendChild(tempTextArea);
|
||
tempTextArea.select();
|
||
document.execCommand('copy');
|
||
document.body.removeChild(tempTextArea);
|
||
|
||
showStatus('HTML代码已复制到剪贴板', 'success');
|
||
}
|
||
|
||
function showStatus(message, type) {
|
||
// 移除现有的状态消息
|
||
const existingStatus = document.querySelector('.status');
|
||
if (existingStatus) {
|
||
existingStatus.remove();
|
||
}
|
||
|
||
// 创建新的状态消息
|
||
const statusDiv = document.createElement('div');
|
||
statusDiv.className = 'status ' + type;
|
||
statusDiv.textContent = message;
|
||
|
||
// 添加到编辑器区域
|
||
const editorDiv = document.querySelector('.editor');
|
||
editorDiv.appendChild(statusDiv);
|
||
|
||
// 3秒后自动移除
|
||
setTimeout(() => {
|
||
if (statusDiv.parentNode) {
|
||
statusDiv.remove();
|
||
}
|
||
}, 3000);
|
||
}
|
||
|
||
// 实时预览功能
|
||
document.getElementById('markdownInput').addEventListener('input', function () {
|
||
clearTimeout(this.debounce);
|
||
this.debounce = setTimeout(convertToHTML, 500);
|
||
});
|
||
|
||
// 延迟加载示例,确保所有组件已初始化
|
||
setTimeout(loadExample, 100);
|
||
</script>
|
||
</body>
|
||
</html> |