Initial commit: Modular personal toolbox with high-fidelity Chinese stroke order tool and CI/CD
Build and Push Docker Image / build (push) Failing after 2m11s
Build and Push Docker Image / build (push) Failing after 2m11s
This commit is contained in:
@@ -0,0 +1,224 @@
|
||||
<!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>
|
||||
body {
|
||||
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
background-color: #f5f5f7;
|
||||
color: #1d1d1f;
|
||||
}
|
||||
.container {
|
||||
max-width: 1000px;
|
||||
margin: 0 auto;
|
||||
padding: 40px 20px;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 24px;
|
||||
}
|
||||
header {
|
||||
text-align: center;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
header h1 { font-size: 32px; margin-bottom: 8px; }
|
||||
header p { color: #86868b; margin: 0; }
|
||||
|
||||
.card {
|
||||
background: white;
|
||||
border-radius: 16px;
|
||||
padding: 28px;
|
||||
box-shadow: 0 8px 30px rgba(0,0,0,0.04);
|
||||
}
|
||||
|
||||
.form-layout {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
.input-row-top {
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.input-row-bottom {
|
||||
display: flex;
|
||||
gap: 16px;
|
||||
align-items: flex-end;
|
||||
}
|
||||
|
||||
.input-group {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 8px;
|
||||
}
|
||||
.input-group.flex-main { flex: 2; }
|
||||
.input-group.flex-side { flex: 1; }
|
||||
|
||||
label {
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
color: #1d1d1f;
|
||||
text-transform: uppercase;
|
||||
letter-spacing: 0.5px;
|
||||
}
|
||||
|
||||
input[type="text"] {
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
padding: 14px 16px;
|
||||
border: 1px solid #d2d2d7;
|
||||
border-radius: 10px;
|
||||
font-size: 18px;
|
||||
background-color: #fbfbfd;
|
||||
transition: all 0.2s;
|
||||
}
|
||||
input[type="text"]:focus {
|
||||
border-color: #0071e3;
|
||||
background-color: #fff;
|
||||
box-shadow: 0 0 0 4px rgba(0,113,227,0.1);
|
||||
outline: none;
|
||||
}
|
||||
|
||||
select {
|
||||
appearance: none;
|
||||
padding: 12px 16px;
|
||||
border: 1px solid #d2d2d7;
|
||||
border-radius: 10px;
|
||||
font-size: 15px;
|
||||
background-color: #fbfbfd;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
button {
|
||||
background-color: #0071e3;
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 14px 32px;
|
||||
border-radius: 10px;
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s;
|
||||
height: 48px;
|
||||
}
|
||||
button:hover { background-color: #0077ed; transform: translateY(-1px); }
|
||||
button:active { transform: translateY(0); }
|
||||
|
||||
#preview-container {
|
||||
width: 100%;
|
||||
height: 850px;
|
||||
border-radius: 16px;
|
||||
overflow: hidden;
|
||||
background: #fff;
|
||||
box-shadow: 0 4px 20px rgba(0,0,0,0.08);
|
||||
display: none;
|
||||
}
|
||||
iframe { width: 100%; height: 100%; border: none; }
|
||||
|
||||
.empty-state {
|
||||
text-align: center;
|
||||
padding: 80px 0;
|
||||
border: 2px dashed #d2d2d7;
|
||||
border-radius: 16px;
|
||||
color: #86868b;
|
||||
}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
<header>
|
||||
<h1>汉字字帖工具箱</h1>
|
||||
<p>生成标准笔顺教学字帖,支持 A4/Letter 打印</p>
|
||||
</header>
|
||||
|
||||
<div class="card">
|
||||
<div class="form-layout">
|
||||
<div class="input-row-top">
|
||||
<div class="input-group">
|
||||
<label for="chars">输入想要生成的汉字</label>
|
||||
<input type="text" id="chars" placeholder="例如:永和九年,岁在癸丑..." value="永">
|
||||
</div>
|
||||
</div>
|
||||
<div class="input-row-bottom">
|
||||
<div class="input-group flex-main">
|
||||
<label for="mode">布局模式</label>
|
||||
<select id="mode">
|
||||
<option value="teaching">2x3 教学方格 (带笔顺、箭头、序号)</option>
|
||||
<option value="step">步进式分解 (逐笔展示过程)</option>
|
||||
</select>
|
||||
</div>
|
||||
<div class="input-group flex-side">
|
||||
<label for="paper_size">纸张大小</label>
|
||||
<select id="paper_size">
|
||||
<option value="A4">A4 (210x297mm)</option>
|
||||
<option value="Letter">Letter (8.5x11in)</option>
|
||||
</select>
|
||||
</div>
|
||||
<button onclick="generatePDF()">生成预览</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div id="preview-container">
|
||||
<iframe id="pdf-frame"></iframe>
|
||||
</div>
|
||||
|
||||
<div id="empty-state" class="empty-state">
|
||||
<p>在上方输入汉字并点击按钮,即可生成高清矢量字帖预览</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<script>
|
||||
async function generatePDF() {
|
||||
const chars = document.getElementById('chars').value;
|
||||
const mode = document.getElementById('mode').value;
|
||||
const paper_size = document.getElementById('paper_size').value;
|
||||
|
||||
if (!chars.trim()) {
|
||||
alert('请输入汉字');
|
||||
return;
|
||||
}
|
||||
|
||||
const btn = document.querySelector('button');
|
||||
const originalText = btn.innerText;
|
||||
btn.innerText = '正在绘图中...';
|
||||
btn.disabled = true;
|
||||
|
||||
try {
|
||||
const response = await fetch('/api/jitie/generate', {
|
||||
method: 'POST',
|
||||
headers: { 'Content-Type': 'application/json' },
|
||||
body: JSON.stringify({
|
||||
chars: chars,
|
||||
mode: mode,
|
||||
flip_y: true,
|
||||
paper_size: paper_size
|
||||
})
|
||||
});
|
||||
|
||||
if (response.ok) {
|
||||
const blob = await response.blob();
|
||||
const url = URL.createObjectURL(blob);
|
||||
|
||||
document.getElementById('empty-state').style.display = 'none';
|
||||
const container = document.getElementById('preview-container');
|
||||
container.style.display = 'block';
|
||||
document.getElementById('pdf-frame').src = url;
|
||||
} else {
|
||||
const err = await response.json();
|
||||
alert('生成失败: ' + (err.error || '未知错误'));
|
||||
}
|
||||
} catch (e) {
|
||||
alert('网络请求失败,请检查后端服务是否运行');
|
||||
} finally {
|
||||
btn.innerText = originalText;
|
||||
btn.disabled = false;
|
||||
}
|
||||
}
|
||||
</script>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user