feat: 新增崗位描述與清單整合功能 v2.1
主要功能更新: - 崗位描述保存功能:保存後資料寫入資料庫 - 崗位清單自動刷新:切換模組時自動載入最新資料 - 崗位清單檢視功能:點擊「檢視」按鈕載入對應描述 - 管理者頁面擴充:新增崗位資料管理與匯出功能 - CSV 批次匯入:支援崗位與職務資料批次匯入 後端 API 新增: - Position Description CRUD APIs - Position List Query & Export APIs - CSV Template Download & Import APIs 文件更新: - SDD.md 更新至版本 2.1 - README.md 更新功能說明與版本歷史 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
252
improve_error_display.py
Normal file
252
improve_error_display.py
Normal file
@@ -0,0 +1,252 @@
|
||||
"""
|
||||
改進錯誤訊息顯示 - 使錯誤訊息可完整顯示和複製
|
||||
"""
|
||||
|
||||
with open('index.html', 'r', encoding='utf-8') as f:
|
||||
content = f.read()
|
||||
|
||||
# 備份
|
||||
with open('index.html.backup2', 'w', encoding='utf-8') as f:
|
||||
f.write(content)
|
||||
|
||||
# 找到錯誤處理的 alert 並替換為更好的顯示方式
|
||||
old_error_handling = ''' } catch (error) {
|
||||
console.error("Error calling LLM API:", error);
|
||||
alert(`AI 生成錯誤: ${error.message}\\n\\n請確保:\\n1. Flask 後端已啟動 (python app_updated.py)\\n2. 已在 .env 文件中配置 LLM API Key\\n3. 網路連線正常`);
|
||||
throw error;
|
||||
}'''
|
||||
|
||||
new_error_handling = ''' } catch (error) {
|
||||
console.error("Error calling LLM API:", error);
|
||||
|
||||
// 嘗試解析更詳細的錯誤訊息
|
||||
let errorDetails = error.message;
|
||||
try {
|
||||
// 如果錯誤訊息是 JSON 格式,嘗試美化顯示
|
||||
const errorJson = JSON.parse(error.message);
|
||||
errorDetails = JSON.stringify(errorJson, null, 2);
|
||||
} catch (e) {
|
||||
// 不是 JSON,使用原始訊息
|
||||
}
|
||||
|
||||
// 創建可複製的錯誤對話框
|
||||
showCopyableError({
|
||||
title: 'AI 生成錯誤',
|
||||
message: error.message,
|
||||
details: errorDetails,
|
||||
suggestions: [
|
||||
'Flask 後端已啟動 (python start_server.py)',
|
||||
'已在 .env 文件中配置有效的 LLM API Key',
|
||||
'網路連線正常',
|
||||
'嘗試使用不同的 LLM API (DeepSeek 或 OpenAI)'
|
||||
]
|
||||
});
|
||||
|
||||
throw error;
|
||||
}'''
|
||||
|
||||
# 替換
|
||||
new_content = content.replace(old_error_handling, new_error_handling)
|
||||
|
||||
if new_content == content:
|
||||
print("WARNING: Pattern not found, content not changed")
|
||||
else:
|
||||
print("SUCCESS: Error handling improved")
|
||||
|
||||
# 添加 showCopyableError 函數(如果還沒有)
|
||||
if 'function showCopyableError' not in new_content:
|
||||
# 在 </script> 前添加新函數
|
||||
error_display_function = '''
|
||||
// 顯示可複製的錯誤訊息
|
||||
function showCopyableError(options) {
|
||||
const { title, message, details, suggestions } = options;
|
||||
|
||||
// 創建對話框
|
||||
const modal = document.createElement('div');
|
||||
modal.style.cssText = `
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
background: rgba(0,0,0,0.7);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
z-index: 10000;
|
||||
animation: fadeIn 0.3s;
|
||||
`;
|
||||
|
||||
modal.innerHTML = `
|
||||
<div style="
|
||||
background: white;
|
||||
border-radius: 12px;
|
||||
max-width: 600px;
|
||||
width: 90%;
|
||||
max-height: 80vh;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 20px 60px rgba(0,0,0,0.3);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
">
|
||||
<!-- Header -->
|
||||
<div style="
|
||||
background: linear-gradient(135deg, #e74c3c 0%, #c0392b 100%);
|
||||
color: white;
|
||||
padding: 20px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 15px;
|
||||
">
|
||||
<span style="font-size: 2rem;">❌</span>
|
||||
<h3 style="margin: 0; font-size: 1.3rem; flex: 1;">${title}</h3>
|
||||
<button onclick="this.closest('[style*=\\'position: fixed\\']').remove()" style="
|
||||
background: rgba(255,255,255,0.2);
|
||||
border: none;
|
||||
color: white;
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
border-radius: 50%;
|
||||
cursor: pointer;
|
||||
font-size: 1.2rem;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
">×</button>
|
||||
</div>
|
||||
|
||||
<!-- Body -->
|
||||
<div style="
|
||||
padding: 25px;
|
||||
overflow-y: auto;
|
||||
flex: 1;
|
||||
">
|
||||
<div style="
|
||||
color: #333;
|
||||
line-height: 1.6;
|
||||
margin-bottom: 20px;
|
||||
font-size: 1rem;
|
||||
">${message}</div>
|
||||
|
||||
${suggestions && suggestions.length > 0 ? `
|
||||
<div style="
|
||||
background: #fff3cd;
|
||||
border: 1px solid #ffc107;
|
||||
border-radius: 6px;
|
||||
padding: 15px;
|
||||
margin-bottom: 20px;
|
||||
">
|
||||
<strong style="color: #856404; display: block; margin-bottom: 10px;">💡 請確保:</strong>
|
||||
<ul style="margin: 0; padding-left: 20px; color: #856404;">
|
||||
${suggestions.map(s => `<li style="margin: 5px 0;">${s}</li>`).join('')}
|
||||
</ul>
|
||||
</div>
|
||||
` : ''}
|
||||
|
||||
${details ? `
|
||||
<details style="
|
||||
background: #f8f9fa;
|
||||
border: 1px solid #dee2e6;
|
||||
border-radius: 6px;
|
||||
padding: 15px;
|
||||
">
|
||||
<summary style="
|
||||
cursor: pointer;
|
||||
font-weight: 600;
|
||||
color: #495057;
|
||||
user-select: none;
|
||||
margin-bottom: 10px;
|
||||
">🔍 詳細錯誤訊息(點擊展開)</summary>
|
||||
<pre id="errorDetailsText" style="
|
||||
background: white;
|
||||
padding: 15px;
|
||||
border-radius: 4px;
|
||||
overflow-x: auto;
|
||||
font-size: 0.85rem;
|
||||
color: #666;
|
||||
margin: 10px 0 0 0;
|
||||
white-space: pre-wrap;
|
||||
word-break: break-word;
|
||||
max-height: 300px;
|
||||
overflow-y: auto;
|
||||
">${details}</pre>
|
||||
<button onclick="copyErrorDetails()" style="
|
||||
background: #007bff;
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 8px 16px;
|
||||
border-radius: 4px;
|
||||
cursor: pointer;
|
||||
margin-top: 10px;
|
||||
font-size: 0.9rem;
|
||||
">📋 複製錯誤訊息</button>
|
||||
</details>
|
||||
` : ''}
|
||||
</div>
|
||||
|
||||
<!-- Footer -->
|
||||
<div style="
|
||||
padding: 15px 25px;
|
||||
border-top: 1px solid #f0f0f0;
|
||||
display: flex;
|
||||
justify-content: flex-end;
|
||||
gap: 10px;
|
||||
">
|
||||
<button onclick="this.closest('[style*=\\'position: fixed\\']').remove()" style="
|
||||
background: #007bff;
|
||||
color: white;
|
||||
border: none;
|
||||
padding: 10px 25px;
|
||||
border-radius: 6px;
|
||||
cursor: pointer;
|
||||
font-size: 0.95rem;
|
||||
font-weight: 500;
|
||||
">確定</button>
|
||||
</div>
|
||||
</div>
|
||||
`;
|
||||
|
||||
document.body.appendChild(modal);
|
||||
|
||||
// 點擊背景關閉
|
||||
modal.addEventListener('click', (e) => {
|
||||
if (e.target === modal) {
|
||||
modal.remove();
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
// 複製錯誤訊息到剪貼板
|
||||
function copyErrorDetails() {
|
||||
const text = document.getElementById('errorDetailsText').textContent;
|
||||
navigator.clipboard.writeText(text).then(() => {
|
||||
alert('錯誤訊息已複製到剪貼板!');
|
||||
}).catch(err => {
|
||||
// Fallback: 選取文字
|
||||
const range = document.createRange();
|
||||
range.selectNode(document.getElementById('errorDetailsText'));
|
||||
window.getSelection().removeAllRanges();
|
||||
window.getSelection().addRange(range);
|
||||
try {
|
||||
document.execCommand('copy');
|
||||
alert('錯誤訊息已複製到剪貼板!');
|
||||
} catch (e) {
|
||||
alert('複製失敗,請手動選取並複製');
|
||||
}
|
||||
});
|
||||
}
|
||||
'''
|
||||
|
||||
new_content = new_content.replace(' </script>', error_display_function + '\n </script>')
|
||||
print("Added showCopyableError function")
|
||||
|
||||
# 寫回
|
||||
with open('index.html', 'w', encoding='utf-8') as f:
|
||||
f.write(new_content)
|
||||
|
||||
print("\nDone! Improvements:")
|
||||
print("1. Error messages now show in a modal dialog")
|
||||
print("2. Full error details are expandable")
|
||||
print("3. Error details can be copied to clipboard")
|
||||
print("4. Better formatting and readability")
|
||||
print("\nPlease reload the page (Ctrl+F5) to see the changes")
|
||||
Reference in New Issue
Block a user