// 全域變數
let currentPage = 1;
let currentView = 'list';
let issues = [];
let projects = [];
// 頁面載入完成後初始化
document.addEventListener('DOMContentLoaded', function() {
loadProjects();
loadIssues();
loadStats();
});
// 載入專案列表
async function loadProjects() {
try {
const response = await fetch('/api/projects');
const result = await response.json();
if (result.success) {
projects = result.data;
updateProjectSelects();
} else {
showAlert('載入專案失敗: ' + result.message, 'error');
}
} catch (error) {
console.error('載入專案錯誤:', error);
showAlert('載入專案時發生錯誤', 'error');
}
}
// 更新專案選擇器
function updateProjectSelects() {
const projectFilter = document.getElementById('projectFilter');
const issueProject = document.getElementById('issueProject');
// 清空現有選項
projectFilter.innerHTML = '';
issueProject.innerHTML = '';
// 添加專案選項
projects.forEach(project => {
const option1 = new Option(project.name, project.id);
const option2 = new Option(project.name, project.id);
projectFilter.appendChild(option1);
issueProject.appendChild(option2);
});
}
// 載入問題列表
async function loadIssues(page = 1) {
currentPage = page;
showLoading(true);
try {
const projectId = document.getElementById('projectFilter').value;
const status = document.getElementById('statusFilter').value;
const priority = document.getElementById('priorityFilter').value;
let url = `/api/issues?page=${page}&limit=10`;
if (projectId) url += `&project_id=${projectId}`;
if (status) url += `&status=${status}`;
if (priority) url += `&priority=${priority}`;
const response = await fetch(url);
const result = await response.json();
if (result.success) {
issues = result.data;
renderIssues();
renderPagination(result.pagination);
updateStats();
} else {
showAlert('載入問題失敗: ' + result.message, 'error');
}
} catch (error) {
console.error('載入問題錯誤:', error);
showAlert('載入問題時發生錯誤', 'error');
} finally {
showLoading(false);
}
}
// 渲染問題列表
function renderIssues() {
const issuesList = document.getElementById('issuesList');
if (issues.length === 0) {
issuesList.innerHTML = `
`;
return;
}
const issuesHtml = issues.map(issue => `
${issue.description ? `
${escapeHtml(issue.description)}
` : ''}
`).join('');
issuesList.innerHTML = issuesHtml;
}
// 渲染分頁
function renderPagination(pagination) {
const paginationDiv = document.getElementById('pagination');
if (pagination.pages <= 1) {
paginationDiv.innerHTML = '';
return;
}
let paginationHtml = '';
// 上一頁按鈕
paginationHtml += `
`;
// 頁碼按鈕
const startPage = Math.max(1, pagination.page - 2);
const endPage = Math.min(pagination.pages, pagination.page + 2);
for (let i = startPage; i <= endPage; i++) {
paginationHtml += `
`;
}
// 下一頁按鈕
paginationHtml += `
`;
paginationDiv.innerHTML = paginationHtml;
}
// 載入統計資料
async function loadStats() {
try {
const response = await fetch('/api/issues');
const result = await response.json();
if (result.success) {
const stats = {
open: 0,
in_progress: 0,
closed: 0,
total: result.data.length
};
result.data.forEach(issue => {
if (issue.status === 'open') stats.open++;
else if (issue.status === 'in_progress') stats.in_progress++;
else if (issue.status === 'closed') stats.closed++;
});
updateStatsDisplay(stats);
}
} catch (error) {
console.error('載入統計錯誤:', error);
}
}
// 更新統計顯示
function updateStatsDisplay(stats) {
document.getElementById('openCount').textContent = stats.open;
document.getElementById('progressCount').textContent = stats.in_progress;
document.getElementById('closedCount').textContent = stats.closed;
document.getElementById('totalCount').textContent = stats.total;
}
// 更新統計(從當前問題列表)
function updateStats() {
const stats = {
open: 0,
in_progress: 0,
closed: 0,
total: issues.length
};
issues.forEach(issue => {
if (issue.status === 'open') stats.open++;
else if (issue.status === 'in_progress') stats.in_progress++;
else if (issue.status === 'closed') stats.closed++;
});
updateStatsDisplay(stats);
}
// 顯示問題詳情
async function showIssueDetail(issueId) {
try {
const response = await fetch(`/api/issues/${issueId}`);
const result = await response.json();
if (result.success) {
const issue = result.data;
const modal = document.getElementById('issueDetailModal');
const title = document.getElementById('issueDetailTitle');
const content = document.getElementById('issueDetailContent');
title.textContent = `#${issue.id} ${issue.title}`;
content.innerHTML = `
描述
${issue.description || '無描述'}
${issue.comments && issue.comments.length > 0 ? `
` : ''}
`;
showModal('issueDetailModal');
} else {
showAlert('載入問題詳情失敗: ' + result.message, 'error');
}
} catch (error) {
console.error('載入問題詳情錯誤:', error);
showAlert('載入問題詳情時發生錯誤', 'error');
}
}
// 顯示新增問題模態框
function showCreateIssueModal() {
showModal('createIssueModal');
}
// 顯示新增專案模態框
function showCreateProjectModal() {
showModal('createProjectModal');
}
// 建立問題
async function createIssue() {
const form = document.getElementById('createIssueForm');
const formData = new FormData(form);
const issueData = {
title: formData.get('title'),
description: formData.get('description'),
project_id: formData.get('project_id') || null,
priority: formData.get('priority'),
assignee: formData.get('assignee'),
reporter: formData.get('reporter')
};
if (!issueData.title) {
showAlert('請填寫問題標題', 'error');
return;
}
try {
const response = await fetch('/api/issues', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(issueData)
});
const result = await response.json();
if (result.success) {
showAlert('問題建立成功!', 'success');
closeModal('createIssueModal');
form.reset();
loadIssues();
loadStats();
} else {
showAlert('建立問題失敗: ' + result.message, 'error');
}
} catch (error) {
console.error('建立問題錯誤:', error);
showAlert('建立問題時發生錯誤', 'error');
}
}
// 建立專案
async function createProject() {
const form = document.getElementById('createProjectForm');
const formData = new FormData(form);
const projectData = {
name: formData.get('name'),
description: formData.get('description'),
status: formData.get('status')
};
if (!projectData.name) {
showAlert('請填寫專案名稱', 'error');
return;
}
try {
const response = await fetch('/api/projects', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(projectData)
});
const result = await response.json();
if (result.success) {
showAlert('專案建立成功!', 'success');
closeModal('createProjectModal');
form.reset();
loadProjects();
} else {
showAlert('建立專案失敗: ' + result.message, 'error');
}
} catch (error) {
console.error('建立專案錯誤:', error);
showAlert('建立專案時發生錯誤', 'error');
}
}
// 切換檢視模式
function toggleView(view, el) {
currentView = view;
// 更新按鈕 active 樣式
const buttons = document.querySelectorAll('.view-toggle .btn');
buttons.forEach(btn => btn.classList.remove('active'));
el.classList.add('active');
// 切換容器樣式
const issuesList = document.getElementById('issuesList');
if (view === 'grid') {
issuesList.classList.remove('issues-list');
issuesList.classList.add('issues-grid');
} else {
issuesList.classList.remove('issues-grid');
issuesList.classList.add('issues-list');
}
renderIssues();
}
// 顯示模態框
function showModal(modalId) {
const modal = document.getElementById(modalId);
modal.classList.add('show');
modal.style.display = 'flex';
}
// 關閉模態框
function closeModal(modalId) {
const modal = document.getElementById(modalId);
modal.classList.remove('show');
modal.style.display = 'none';
}
// 顯示載入指示器
function showLoading(show) {
const loading = document.getElementById('loadingIndicator');
loading.style.display = show ? 'block' : 'none';
}
// 顯示警告訊息
function showAlert(message, type = 'info') {
// 移除現有的警告
const existingAlert = document.querySelector('.alert');
if (existingAlert) {
existingAlert.remove();
}
const alert = document.createElement('div');
alert.className = `alert alert-${type}`;
alert.textContent = message;
document.querySelector('.container').insertBefore(alert, document.querySelector('.stats-grid'));
// 3秒後自動移除
setTimeout(() => {
if (alert.parentNode) {
alert.remove();
}
}, 3000);
}
// 工具函數
function escapeHtml(text) {
const div = document.createElement('div');
div.textContent = text;
return div.innerHTML;
}
function formatDate(dateString) {
const date = new Date(dateString);
return date.toLocaleDateString('zh-TW', {
year: 'numeric',
month: '2-digit',
day: '2-digit',
hour: '2-digit',
minute: '2-digit'
});
}
function getStatusText(status) {
const statusMap = {
'open': '待處理',
'in_progress': '處理中',
'closed': '已完成'
};
return statusMap[status] || status;
}
function getPriorityText(priority) {
const priorityMap = {
'high': '高',
'medium': '中',
'low': '低'
};
return priorityMap[priority] || priority;
}
// 點擊模態框外部關閉
window.onclick = function(event) {
const modals = document.querySelectorAll('.modal');
modals.forEach(modal => {
if (event.target === modal) {
modal.classList.remove('show');
modal.style.display = 'none';
}
});
}
評論 (${issue.comments.length})
${issue.comments.map(comment => `