Files
gangyan/scripts/latex-editor/index.html
liuguancen 570c0f3d61 fix: 工具广场统一18000端口 + 去除电量限制 + bot头像更新
nginx反代(tools-nginx.conf):
- 10个工具统一通过18000端口路径分发
- 解决/static/冲突(Lama精确匹配,其余给LibreTranslate)
- 解决/api/冲突(PDF用sub_filter改为/pdf-api/,/api/给imgcompress)
- Overleaf兜底处理所有未匹配的绝对路径
- 前端工具卡片统一走18000端口+路径

后端:
- 去除对话电量限制(validateUser中的num扣减逻辑)

前端:
- bot头像更新为战知logo(蓝底白字)
- LaTeX公式编辑器"复制为图片"改为"下载为图片"(解决跨域问题)

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 19:28:23 +08:00

154 lines
9.0 KiB
HTML
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>LaTeX 公式编辑器</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/katex@0.16.9/dist/katex.min.css">
<script src="https://cdn.jsdelivr.net/npm/katex@0.16.9/dist/katex.min.js"></script>
<style>
* { margin: 0; padding: 0; box-sizing: border-box; }
body { font-family: -apple-system, "Microsoft YaHei", sans-serif; background: #f0f2f5; color: #333; }
.header { background: #004EA0; color: #fff; padding: 16px 32px; font-size: 18px; font-weight: bold; }
.container { max-width: 1200px; margin: 24px auto; padding: 0 24px; display: flex; gap: 20px; }
.panel { background: #fff; border-radius: 10px; padding: 20px; box-shadow: 0 2px 12px rgba(0,0,0,0.08); flex: 1; }
.panel h3 { font-size: 15px; color: #333; margin-bottom: 12px; display: flex; justify-content: space-between; align-items: center; }
textarea { width: 100%; height: 200px; border: 1px solid #ddd; border-radius: 8px; padding: 14px; font-family: 'Courier New', monospace; font-size: 15px; resize: vertical; outline: none; }
textarea:focus { border-color: #004EA0; }
.preview { min-height: 200px; border: 1px solid #eee; border-radius: 8px; padding: 20px; display: flex; align-items: center; justify-content: center; background: #fafbfc; overflow: auto; }
.preview .katex { font-size: 24px; }
.error { color: #dc2626; font-size: 13px; text-align: center; }
.toolbar { display: flex; flex-wrap: wrap; gap: 6px; margin-bottom: 14px; }
.toolbar button { padding: 4px 10px; border: 1px solid #ddd; border-radius: 6px; background: #f5f7fa; cursor: pointer; font-size: 13px; transition: all 0.15s; }
.toolbar button:hover { border-color: #004EA0; color: #004EA0; background: #e8f0fe; }
.symbols { display: flex; flex-wrap: wrap; gap: 4px; margin-top: 16px; }
.symbols button { width: 42px; height: 36px; border: 1px solid #eee; border-radius: 6px; background: #fafbfc; cursor: pointer; font-size: 16px; display: flex; align-items: center; justify-content: center; }
.symbols button:hover { background: #e8f0fe; border-color: #004EA0; }
.actions { margin-top: 14px; display: flex; gap: 10px; }
.actions button { padding: 8px 20px; border: none; border-radius: 8px; cursor: pointer; font-size: 14px; }
.btn-primary { background: #004EA0; color: #fff; }
.btn-primary:hover { background: #003a7a; }
.btn-secondary { background: #f0f2f5; color: #333; border: 1px solid #ddd !important; }
.btn-secondary:hover { background: #e5e7eb; }
.templates { margin-top: 20px; }
.templates h4 { font-size: 14px; color: #666; margin-bottom: 8px; }
.tpl-list { display: flex; flex-wrap: wrap; gap: 6px; }
.tpl-list button { padding: 4px 12px; border: 1px solid #e0e0e0; border-radius: 6px; background: #fff; cursor: pointer; font-size: 12px; color: #555; }
.tpl-list button:hover { border-color: #004EA0; color: #004EA0; }
.copied { position: fixed; top: 50%; left: 50%; transform: translate(-50%,-50%); background: rgba(0,0,0,0.7); color: #fff; padding: 10px 24px; border-radius: 8px; font-size: 14px; display: none; z-index: 999; }
</style>
</head>
<body>
<div class="header">LaTeX 公式编辑器</div>
<div class="container">
<div class="panel" style="flex:1.2">
<h3>输入公式</h3>
<div class="toolbar">
<button onclick="ins('\\frac{a}{b}')">分数</button>
<button onclick="ins('\\sqrt{x}')">根号</button>
<button onclick="ins('\\sum_{i=1}^{n}')">求和</button>
<button onclick="ins('\\int_{a}^{b}')">积分</button>
<button onclick="ins('\\lim_{x \\to \\infty}')">极限</button>
<button onclick="ins('\\prod_{i=1}^{n}')">连乘</button>
<button onclick="ins('x^{2}')">上标</button>
<button onclick="ins('x_{i}')">下标</button>
<button onclick="ins('\\binom{n}{k}')">组合</button>
<button onclick="ins('\\vec{a}')">向量</button>
<button onclick="ins('\\hat{x}')"></button>
<button onclick="ins('\\bar{x}')">横线</button>
<button onclick="ins('\\dot{x}')"></button>
<button onclick="ins('\\overline{AB}')">上划线</button>
<button onclick="ins('\\begin{matrix} a & b \\\\ c & d \\end{matrix}')">矩阵</button>
<button onclick="ins('\\begin{cases} x & y \\\\ a & b \\end{cases}')">分段</button>
<button onclick="ins('\\log')">log</button>
<button onclick="ins('\\ln')">ln</button>
</div>
<textarea id="input" placeholder="在此输入 LaTeX 公式E = mc^{2}" oninput="render()">E = mc^{2}</textarea>
<div class="symbols" id="symbolBar"></div>
<div class="actions">
<button class="btn-primary" onclick="copyLatex()">复制公式</button>
<button class="btn-secondary" onclick="downloadImg()">下载为图片</button>
<button class="btn-secondary" onclick="document.getElementById('input').value='';render()">清空</button>
</div>
<div class="templates">
<h4>常用公式模板</h4>
<div class="tpl-list">
<button onclick="setTpl('E = mc^{2}')">质能方程</button>
<button onclick="setTpl('e^{i\\pi} + 1 = 0')">欧拉公式</button>
<button onclick="setTpl('a^2 + b^2 = c^2')">勾股定理</button>
<button onclick="setTpl('x = \\frac{-b \\pm \\sqrt{b^2 - 4ac}}{2a}')">求根公式</button>
<button onclick="setTpl('f\'(x) = \\lim_{h \\to 0} \\frac{f(x+h) - f(x)}{h}')">导数定义</button>
<button onclick="setTpl('\\int_{-\\infty}^{\\infty} e^{-x^2} dx = \\sqrt{\\pi}')">高斯积分</button>
<button onclick="setTpl('\\nabla \\times \\vec{E} = -\\frac{\\partial \\vec{B}}{\\partial t}')">麦克斯韦</button>
<button onclick="setTpl('\\sigma = \\sqrt{\\frac{1}{N}\\sum_{i=1}^{N}(x_i - \\mu)^2}')">标准差</button>
<button onclick="setTpl('P(A|B) = \\frac{P(B|A) \\cdot P(A)}{P(B)}')">贝叶斯</button>
<button onclick="setTpl('\\mathbf{A} = \\begin{pmatrix} a_{11} & a_{12} \\\\ a_{21} & a_{22} \\end{pmatrix}')">矩阵</button>
</div>
</div>
</div>
<div class="panel">
<h3>实时预览 <span style="font-weight:normal;font-size:12px;color:#999">公式渲染由 KaTeX 提供</span></h3>
<div class="preview" id="preview"></div>
</div>
</div>
<div class="copied" id="toast">已复制!</div>
<script>
const symbols = ['\\alpha','\\beta','\\gamma','\\delta','\\epsilon','\\zeta','\\eta','\\theta','\\lambda','\\mu','\\nu','\\xi','\\pi','\\rho','\\sigma','\\tau','\\phi','\\psi','\\omega','\\Gamma','\\Delta','\\Theta','\\Lambda','\\Pi','\\Sigma','\\Phi','\\Psi','\\Omega','\\infty','\\partial','\\nabla','\\forall','\\exists','\\in','\\notin','\\subset','\\cup','\\cap','\\times','\\cdot','\\div','\\pm','\\mp','\\leq','\\geq','\\neq','\\approx','\\equiv','\\sim','\\rightarrow','\\leftarrow','\\Rightarrow','\\Leftarrow'];
const bar = document.getElementById('symbolBar');
symbols.forEach(s => {
const b = document.createElement('button');
try { katex.render(s, b, {throwOnError:false}); } catch(e) { b.textContent = s; }
b.onclick = () => ins(s);
bar.appendChild(b);
});
function render() {
const input = document.getElementById('input').value.trim();
const el = document.getElementById('preview');
if (!input) { el.innerHTML = '<span style="color:#bbb">在左侧输入公式</span>'; return; }
try { katex.render(input, el, { displayMode: true, throwOnError: true }); }
catch(e) { el.innerHTML = '<span class="error">' + e.message + '</span>'; }
}
function ins(s) {
const t = document.getElementById('input');
const start = t.selectionStart, end = t.selectionEnd;
t.value = t.value.slice(0, start) + s + t.value.slice(end);
t.focus();
t.selectionStart = t.selectionEnd = start + s.length;
render();
}
function setTpl(s) { document.getElementById('input').value = s; render(); }
function copyLatex() {
navigator.clipboard.writeText(document.getElementById('input').value);
showToast('公式已复制!');
}
function downloadImg() {
const el = document.getElementById('preview');
if (!el.querySelector('.katex')) { showToast('请先输入公式'); return; }
// 直接用 KaTeX 的 mathml/svg 渲染结果导出为 SVG 文件下载
const w = el.scrollWidth + 40, h = el.scrollHeight + 40;
const svgStr = `<svg xmlns="http://www.w3.org/2000/svg" width="${w}" height="${h}">
<rect width="100%" height="100%" fill="white"/>
<foreignObject width="100%" height="100%">
<div xmlns="http://www.w3.org/1999/xhtml" style="display:flex;align-items:center;justify-content:center;width:${w}px;height:${h}px;font-size:24px">
${el.innerHTML}
</div>
</foreignObject>
</svg>`;
const blob = new Blob([svgStr], {type: 'image/svg+xml'});
const a = document.createElement('a');
a.href = URL.createObjectURL(blob);
a.download = 'formula.svg';
a.click();
URL.revokeObjectURL(a.href);
showToast('图片已下载!');
}
function showToast(msg) {
const t = document.getElementById('toast');
t.textContent = msg; t.style.display = 'block';
setTimeout(() => t.style.display = 'none', 1500);
}
render();
</script>
</body>
</html>