baaa6ca2f8
- student.html: 学员详情页,支持编辑/添加/删除问题 - plan_edit.html: 方案编辑页 - plans.html: 方案列表页 - home.html: 首页
170 lines
6.3 KiB
HTML
170 lines
6.3 KiB
HTML
{% extends "base.html" %}
|
|
|
|
{% block title %}编辑方案 - 钢琴练习方案系统{% endblock %}
|
|
|
|
{% block content %}
|
|
<div class="d-flex justify-content-between align-items-center mb-4">
|
|
<h4><i class="bi bi-edit"></i> 编辑方案</h4>
|
|
<div>
|
|
<a href="/plan/{{ plan_id }}" class="btn btn-outline-secondary">
|
|
<i class="bi bi-arrow-left"></i> 返回详情
|
|
</a>
|
|
</div>
|
|
</div>
|
|
|
|
<div class="card">
|
|
<div class="card-body">
|
|
<ul class="nav nav-tabs" id="editContentTabs" role="tablist">
|
|
<li class="nav-item" role="presentation">
|
|
<button class="nav-link active" id="aiReport-tab" data-bs-toggle="tab" data-bs-target="#aiReportPane" type="button">AI报告</button>
|
|
</li>
|
|
<li class="nav-item" role="presentation">
|
|
<button class="nav-link" id="dailySchedule-tab" data-bs-toggle="tab" data-bs-target="#dailySchedulePane" type="button">每日练习计划</button>
|
|
</li>
|
|
</ul>
|
|
<div class="tab-content mt-3" id="editContentTabContent">
|
|
<div class="tab-pane fade show active" id="aiReportPane" role="tabpanel">
|
|
<div class="alert alert-info">
|
|
<i class="bi bi-info-circle"></i> 使用 Markdown 格式编辑AI报告,支持标题、列表、加粗等格式
|
|
</div>
|
|
<textarea id="editAiReport" style="display:none;"></textarea>
|
|
<div id="editAiReportEditor"></div>
|
|
</div>
|
|
<div class="tab-pane fade" id="dailySchedulePane" role="tabpanel">
|
|
<div class="alert alert-info mb-2">
|
|
<i class="bi bi-info-circle"></i> 支持拖拽排序,双击单元格编辑、点击+添加行、点击×删除行
|
|
</div>
|
|
<div id="editDailyScheduleTable" style="height: 400px; max-height: 600px;"></div>
|
|
<button type="button" class="btn btn-outline-primary btn-sm mt-2" onclick="addScheduleRow()">
|
|
<i class="bi bi-plus"></i> 添加一行
|
|
</button>
|
|
</div>
|
|
</div>
|
|
</div>
|
|
<div class="card-footer">
|
|
<button type="button" class="btn btn-secondary" onclick="history.back()">取消</button>
|
|
<button type="button" class="btn btn-primary" onclick="savePlanContent()">
|
|
<i class="bi bi-save"></i> 保存
|
|
</button>
|
|
</div>
|
|
</div>
|
|
{% endblock %}
|
|
|
|
{% block extra_js %}
|
|
<script>
|
|
let planContentEditor = null;
|
|
let scheduleTable = null;
|
|
let editPlanOriginalState = { ai_report: '', scheduleData: [] };
|
|
|
|
window.pageInit = function() {
|
|
loadPlanForEdit();
|
|
};
|
|
|
|
async function loadPlanForEdit() {
|
|
const planId = {{ plan_id }};
|
|
try {
|
|
const resp = await fetch(`/api/plans/${planId}`);
|
|
const data = await resp.json();
|
|
const content = typeof data.content === 'string' ? JSON.parse(data.content) : data.content;
|
|
|
|
document.getElementById('editAiReport').value = content.ai_report || '';
|
|
|
|
if (planContentEditor) {
|
|
planContentEditor.toTextArea();
|
|
planContentEditor = null;
|
|
}
|
|
planContentEditor = new EasyMDE({
|
|
element: document.getElementById('editAiReport'),
|
|
spellChecker: false,
|
|
status: false,
|
|
toolbar: ['bold', 'italic', 'heading', '|', 'code', 'quote', 'unordered-list', '|', 'side-by-side', 'fullscreen'],
|
|
initialValue: content.ai_report || ''
|
|
});
|
|
|
|
const scheduleData = (content.daily_schedule || []).map(item => ({
|
|
phase: item.phase || '',
|
|
duration: item.duration || '',
|
|
content: item.content || '',
|
|
purpose: item.purpose || ''
|
|
}));
|
|
|
|
editPlanOriginalState.ai_report = content.ai_report || '';
|
|
editPlanOriginalState.scheduleData = JSON.parse(JSON.stringify(scheduleData));
|
|
|
|
if (scheduleTable) {
|
|
scheduleTable.destroy();
|
|
scheduleTable = null;
|
|
}
|
|
scheduleTable = new Tabulator("#editDailyScheduleTable", {
|
|
height: "100%",
|
|
data: scheduleData,
|
|
layout: "fitColumns",
|
|
movableRows: true,
|
|
editable: true,
|
|
addRowPos: "bottom",
|
|
columns: [
|
|
{ title: "环节", field: "phase", editor: "input", width: 120 },
|
|
{ title: "时长", field: "duration", editor: "input", width: 100 },
|
|
{ title: "内容", field: "content", editor: "input", minWidth: 200 },
|
|
{ title: "目的", field: "purpose", editor: "input", minWidth: 150 },
|
|
{
|
|
title: "操作",
|
|
width: 80,
|
|
formatter: function(cell) {
|
|
return "<button type='button' class='btn btn-sm btn-outline-danger'><i class='bi bi-trash'></i></button>";
|
|
},
|
|
cellClick: function(e, cell) {
|
|
cell.getRow().delete();
|
|
}
|
|
}
|
|
]
|
|
});
|
|
|
|
} catch (e) {
|
|
alert('加载方案失败: ' + e.message);
|
|
}
|
|
}
|
|
|
|
function addScheduleRow() {
|
|
if (scheduleTable) {
|
|
scheduleTable.addRow({ phase: '', duration: '', content: '', purpose: '' });
|
|
}
|
|
}
|
|
|
|
async function savePlanContent() {
|
|
const planId = {{ plan_id }};
|
|
const currentAiReport = planContentEditor ? planContentEditor.value() : document.getElementById('editAiReport').value;
|
|
const tableData = scheduleTable ? scheduleTable.getData() : [];
|
|
|
|
const hasChanges = currentAiReport !== editPlanOriginalState.ai_report ||
|
|
JSON.stringify(tableData) !== JSON.stringify(editPlanOriginalState.scheduleData);
|
|
|
|
if (!hasChanges) {
|
|
alert('没有修改,无需保存');
|
|
return;
|
|
}
|
|
|
|
if (!confirm('确定要保存修改吗?')) return;
|
|
|
|
try {
|
|
const resp = await fetch(`/api/plans/${planId}`, {
|
|
method: 'PUT',
|
|
headers: { 'Content-Type': 'application/json' },
|
|
body: JSON.stringify({
|
|
ai_report: currentAiReport,
|
|
daily_schedule: tableData
|
|
})
|
|
});
|
|
|
|
if (resp.ok) {
|
|
alert('保存成功');
|
|
window.location.href = `/plan/${planId}`;
|
|
} else {
|
|
alert('保存失败');
|
|
}
|
|
} catch (e) {
|
|
alert('保存失败: ' + e.message);
|
|
}
|
|
}
|
|
</script>
|
|
{% endblock %} |