▍程式碼
<!-- history_analysis.html -->
<!-- 題目詳情 -->
<div id="questionDetailModal">
<div>
<div>
<div>
<h5 id="questionDetailModalLabel">題目詳情與筆記</h5>
<button type="button" data-bs-dismiss="modal" aria-label="Close"></button>
</div>
<div>
<h6 id="modal-question-id"></h6>
<p id="modal-question-text">載入中...</p>
<div id="modal-options">
<!-- 選項 -->
</div>
<div class="modal-question-info bg-light p-3 rounded mb-4">
<p><strong>正確答案:</strong> <span id="modal-answer">N/A</span></p>
<p><strong>來源書籍:</strong> <span id="modal-book-source">N/A</span></p>
<p><strong>頁數:</strong> <span id="modal-page-number">N/A</span></p>
</div>
<h6>我的筆記</h6>
<textarea id="modal-user-notes" placeholder="寫下重點筆記..."></textarea>
</div>
<div>
<button type="button" data-bs-dismiss="modal">關閉</button>
<button type="button" id="save-notes-btn" data-question-key="">儲存筆記</button>
<div id="notes-status"></div>
</div>
</div>
</div>
</div>
<script>
$(document).on('click', '.clickable-question', function() {
const questionKey = $(this).data('question-key');
if (!questionKey || questionKey === '題目內容缺失') {
console.error("DANGER: 無法解析題目 Key (內容),屬性為:", $(this).attr('data-question-key'));
showStatus('錯誤:無法解析題目內容。', 'danger');
return;
}
currentQuestionKey = questionKey;
const encodedKey = encodeURIComponent(questionKey);
const apiUrl = `/api/question/${encodedKey}`;
$('#modal-question-id').text(`Key: ${questionKey}`); // 顯示題目 key
$('#modal-question-text').text('載入中...');
$('#modal-options').empty();
$('#modal-answer').text('N/A');
$('#modal-book-source').text('N/A');
$('#modal-page-number').text('N/A');
$('#modal-user-notes').val('');
$('#notes-status').text('');
$('#save-notes-btn').prop('disabled', true).text('儲存筆記').removeAttr('data-question-id');
if (questionDetailModal) {
questionDetailModal.show();
} else {
console.error("Bootstrap Modal 物件未初始化!請檢查 Bootstrap JS 是否正確載入。");
return;
}
console.log(`[DEBUG] 正在發送 GET 請求到: ${apiUrl}`);
$.ajax({
url: apiUrl,
type: 'GET',
success: function(data) {
console.log(`[DEBUG] API 請求成功,收到數據:`, data);
$('#modal-question-id').text(`ID(索引): ${data.id} | Key: ${data.question_key}`); // 顯示後端返回的 key
$('#modal-question-text').text(data.question_text);
$('#modal-answer').text(data.answer);
$('#modal-book-source').text(data.book_source);
$('#modal-page-number').text(data.page_number);
$('#modal-user-notes').val(data.user_notes);
const labels = ['A', 'B', 'C', 'D', 'E'];
let optionsHtml = '';
(data.options || []).forEach((opt, index) => {
optionsHtml += `<p class="mb-1">(${labels[index]}) ${opt}</p>`;
});
$('#modal-options').html(optionsHtml);
// 啟用儲存按鈕
$('#save-notes-btn').prop('disabled', false).attr('data-question-key', data.question_key);
},
error: function(jqXHR) {
const errorMsg = jqXHR.responseJSON ? (jqXHR.responseJSON.error || JSON.stringify(jqXHR.responseJSON)) : jqXHR.responseText;
console.error(`[ERROR] 獲取題目詳情失敗 (${jqXHR.status}):`, errorMsg);
$('#modal-question-text').html(`<strong>載入失敗!</strong> 錯誤碼: ${jqXHR.status}。請檢查後端日誌。`);
showStatus(`錯誤: 載入詳情失敗。${jqXHR.status}`, 'danger');
}
});
});
// 儲存筆記處理
$('#save-notes-btn').on('click', function() {
const qKey = $(this).data('question-key');
const notes = $('#modal-user-notes').val();
if (!qKey) {
showStatus('錯誤:找不到題目 Key。', 'danger');
return;
}
$('#save-notes-btn').prop('disabled', true).text('儲存中...');
$.ajax({
url: '/api/save_notes',
type: 'POST',
contentType: 'application/json',
// *** 關鍵變更:提交 question_key 而不是 id ***
data: JSON.stringify({ question_key: qKey, user_notes: notes }),
success: function(response) {
showStatus(response.message || '儲存成功!', 'success');
$('#save-notes-btn').prop('disabled', false).text('儲存筆記');
},
error: function(jqXHR) {
const errorMsg = jqXHR.responseJSON ? jqXHR.responseJSON.error : '未知錯誤';
showStatus(`儲存失敗: ${errorMsg}`, 'danger');
console.error("儲存筆記失敗:", errorMsg);
$('#save-notes-btn').prop('disabled', false).text('儲存筆記');
}
});
});
</script>
{% endblock %}
# views.py
@app.route('/api/question/<path:question_key>', methods=['GET'])
def get_question_details(question_key):
"""根據題目內容 (question_key) 獲取單個題目的詳細資訊和用戶筆記。"""
try:
# 遍歷知識庫以找到匹配的題目
found_question = None
found_index = -1
for index, question in enumerate(KNOWLEDGE_BASE):
# 兼容 '題目' 和 'question_text' 兩種 key
q_text = question.get('題目', question.get('question_text', ''))
# 使用完全匹配
if q_text == question_key:
found_question = question
found_index = index
break
if found_question:
# 確保返回前端需要的欄位名稱
response_data = {
'id': found_index, # 傳回索引給前端備用,雖然查找用 key
'question_key': question_key, # 傳回 key 供前端儲存筆記時使用
'question_text': found_question.get('題目', found_question.get('question_text', '內容缺失')),
'options': found_question.get('選項', []),
'answer': found_question.get('答案', 'N/A'),
'book_source': found_question.get('來源書籍', 'N/A'),
'page_number': found_question.get('頁數', 'N/A'),
'user_notes': found_question.get('user_notes', '')
}
return jsonify(response_data)
else:
return jsonify({'error': f'找不到題目詳情: {question_key}'}, 404)
except Exception as e:
print(f"獲取題目詳情失敗: {e}")
return jsonify({'error': f'伺服器內部錯誤: {e}'}), 500
@app.route('/api/save_notes', methods=['POST'])
def save_user_notes():
"""接收 POST 請求,儲存用戶筆記到 KNOWLEDGE_BASE 並持久化到文件。使用題目內容作為查找鍵。"""
try:
data = request.get_json()
question_key = data.get('question_key') # 現在用 question_key 而不是 id
user_notes = data.get('user_notes', '')
if question_key is None:
return jsonify({'error': '缺少題目內容鍵 (question_key)'}), 400
# 查找題目
found_index = -1
for index, question in enumerate(KNOWLEDGE_BASE):
q_text = question.get('題目', question.get('question_text', ''))
if q_text == question_key:
found_index = index
break
if found_index != -1:
# 更新 KNOWLEDGE_BASE
KNOWLEDGE_BASE[found_index]['user_notes'] = user_notes
# 持久化到文件
if save_knowledge_base():
return jsonify({'success': True, 'message': '筆記儲存成功!'})
else:
return jsonify({'error': '筆記儲存成功,但寫入文件失敗。'}, 500)
else:
return jsonify({'error': f'找不到題目內容: {question_key}'}, 404)
except Exception as e:
print(f"儲存筆記失敗: {e}")
return jsonify({'error': f'伺服器內部錯誤: {e}'}), 500