feat: add export preview with template rendering and watermark support
This commit is contained in:
@@ -2,6 +2,124 @@
|
||||
|
||||
{% block title %}方案详情 - 钢琴练习方案系统{% endblock %}
|
||||
|
||||
{% block extra_css %}
|
||||
<style>
|
||||
/* 导出预览样式 - 镜像PDF效果 */
|
||||
.preview-content {
|
||||
font-family: "Microsoft YaHei", "微软雅黑", sans-serif;
|
||||
font-size: 12px;
|
||||
line-height: 1.6;
|
||||
color: #333;
|
||||
padding: 20px;
|
||||
background: white;
|
||||
}
|
||||
|
||||
.preview-content h1 {
|
||||
font-size: 24px;
|
||||
font-weight: bold;
|
||||
margin: 0 0 16px 0;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.preview-content h2 {
|
||||
font-size: 20px;
|
||||
font-weight: bold;
|
||||
margin: 16px 0 12px 0;
|
||||
border-bottom: 1px solid #ddd;
|
||||
padding-bottom: 4px;
|
||||
}
|
||||
|
||||
.preview-content h3 {
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
margin: 12px 0 8px 0;
|
||||
}
|
||||
|
||||
.preview-content p {
|
||||
margin: 8px 0;
|
||||
}
|
||||
|
||||
.preview-content ul, .preview-content ol {
|
||||
margin: 8px 0;
|
||||
padding-left: 24px;
|
||||
}
|
||||
|
||||
.preview-content li {
|
||||
margin: 4px 0;
|
||||
}
|
||||
|
||||
.preview-content table {
|
||||
border-collapse: collapse;
|
||||
width: 100%;
|
||||
margin: 12px 0;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.preview-content table th,
|
||||
.preview-content table td {
|
||||
border: 1px solid #ddd;
|
||||
padding: 4px 8px;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.preview-content table th {
|
||||
background: #f5f5f5;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.preview-content strong {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.preview-content em {
|
||||
font-style: italic;
|
||||
}
|
||||
|
||||
.preview-content code {
|
||||
font-family: Consolas, monospace;
|
||||
background: #f5f5f5;
|
||||
padding: 1px 4px;
|
||||
border-radius: 3px;
|
||||
}
|
||||
|
||||
.preview-content blockquote {
|
||||
border-left: 3px solid #ddd;
|
||||
margin: 8px 0;
|
||||
padding-left: 12px;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.preview-watermark-overlay {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
pointer-events: none;
|
||||
z-index: 10;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.preview-watermark-overlay::before {
|
||||
content: attr(data-watermark);
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%) rotate(-45deg);
|
||||
font-size: 48px;
|
||||
font-weight: bold;
|
||||
color: rgba(200, 200, 200, 0.3);
|
||||
white-space: nowrap;
|
||||
z-index: 11;
|
||||
display: none;
|
||||
}
|
||||
|
||||
.preview-watermark-overlay.watermark-active::before {
|
||||
display: block;
|
||||
}
|
||||
</style>
|
||||
{% endblock %}
|
||||
|
||||
{% block content %}
|
||||
<div class="d-flex justify-content-between align-items-center mb-4">
|
||||
<h4><i class="bi bi-file-text"></i> 方案详情</h4>
|
||||
@@ -81,10 +199,10 @@ async function loadPlan() {
|
||||
<button onclick="downloadPDFWithTemplate()" class="btn btn-sm btn-primary">
|
||||
<i class="bi bi-download"></i> 下载PDF
|
||||
</button>
|
||||
<button onclick="downloadDOCXWithTemplate()" class="btn btn-sm btn-outline-primary">
|
||||
<i class="bi bi-file-word"></i> 下载DOCX
|
||||
<button onclick="showPreview()" class="btn btn-sm btn-outline-success">
|
||||
<i class="bi bi-eye"></i> 预览
|
||||
</button>
|
||||
<button onclick="downloadMDWithTemplate()" class="btn btn-sm btn-outline-secondary">
|
||||
<button onclick="downloadMDWithTemplate()" class="btn btn-sm btn-outline-primary" style="display:none">
|
||||
<i class="bi bi-file-markdown"></i> 下载MD
|
||||
</button>
|
||||
</div>
|
||||
@@ -141,10 +259,45 @@ function downloadMDWithTemplate() {
|
||||
window.open(`/api/plans/${currentPlanId}/md${suffix}`, '_blank');
|
||||
}
|
||||
|
||||
function downloadDOCXWithTemplate() {
|
||||
async function showPreview() {
|
||||
const templateId = document.getElementById('reportTemplateSelect')?.value;
|
||||
const planId = currentPlanId;
|
||||
const suffix = templateId ? `?template_id=${templateId}` : '';
|
||||
window.open(`/api/plans/${currentPlanId}/docx${suffix}`, '_blank');
|
||||
|
||||
try {
|
||||
const resp = await fetch(`/api/plans/${planId}/preview${suffix}`);
|
||||
if (!resp.ok) {
|
||||
alert('预览加载失败');
|
||||
return;
|
||||
}
|
||||
const html = await resp.text();
|
||||
document.getElementById('previewContent').innerHTML = html;
|
||||
|
||||
// 加载水印配置
|
||||
try {
|
||||
const cfgResp = await fetch('/api/config');
|
||||
if (cfgResp.ok) {
|
||||
const cfg = await cfgResp.json();
|
||||
const wmText = cfg.watermark_text || '';
|
||||
const wmEl = document.getElementById('previewWatermark');
|
||||
if (wmText) {
|
||||
wmEl.setAttribute('data-watermark', wmText);
|
||||
wmEl.classList.add('watermark-active');
|
||||
} else {
|
||||
wmEl.classList.remove('watermark-active');
|
||||
}
|
||||
}
|
||||
} catch (e) {
|
||||
console.log('水印配置加载失败', e);
|
||||
}
|
||||
|
||||
// 显示模态框
|
||||
const modal = new bootstrap.Modal(document.getElementById('previewModal'));
|
||||
modal.show();
|
||||
} catch (err) {
|
||||
console.error('预览错误:', err);
|
||||
alert('预览加载失败');
|
||||
}
|
||||
}
|
||||
|
||||
async function loadTemplates() {
|
||||
@@ -195,4 +348,20 @@ window.currentStudentId = null;
|
||||
|
||||
loadPlan();
|
||||
</script>
|
||||
|
||||
<!-- 导出预览模态框 -->
|
||||
<div class="modal fade" id="previewModal" tabindex="-1" aria-labelledby="previewModalLabel" aria-hidden="true">
|
||||
<div class="modal-dialog modal-xl modal-dialog-scrollable">
|
||||
<div class="modal-content">
|
||||
<div class="modal-header">
|
||||
<h5 class="modal-title" id="previewModalLabel">导出预览</h5>
|
||||
<button type="button" class="btn-close" data-bs-dismiss="modal" aria-label="关闭"></button>
|
||||
</div>
|
||||
<div class="modal-body" style="position: relative; overflow: hidden;">
|
||||
<div id="previewWatermark" class="preview-watermark-overlay"></div>
|
||||
<div id="previewContent" class="preview-content"></div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
{% endblock %}
|
||||
Reference in New Issue
Block a user