Files
worklist/frontend/js/timer.js
2025-12-30 09:03:29 +00:00

269 lines
8.8 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

// 计时器模块
class TaskTimer {
constructor() {
this.activeTimers = new Map(); // 存储活跃的计时器
this.timerIntervals = new Map(); // 存储定时器ID
}
// 开始计时
async startTimer(taskId) {
try {
// 检查是否已有活跃计时器
if (this.activeTimers.has(taskId)) {
throw new Error('该任务已在计时中');
}
// 调用API开始计时
const timeRecord = await api.startTimer(taskId);
// 创建本地计时器
const timer = {
taskId: taskId,
startTime: new Date(),
currentSessionDuration: 0, // 当前会话时长从0开始
isRunning: true
};
this.activeTimers.set(taskId, timer);
// 启动定时器更新
const intervalId = setInterval(() => {
this.updateTimer(taskId);
}, 1000);
this.timerIntervals.set(taskId, intervalId);
// 更新UI
this.updateTimerDisplay(taskId);
this.updateTaskStatus(taskId, 'in_progress');
return timer;
} catch (error) {
console.error('开始计时失败:', error);
throw error;
}
}
// 停止计时
async stopTimer(taskId) {
try {
const timer = this.activeTimers.get(taskId);
if (!timer) {
throw new Error('没有找到活跃的计时器');
}
// 调用API停止计时
await api.stopTimer(taskId);
// 清除本地计时器
this.clearTimer(taskId);
// 更新UI
this.updateTaskStatus(taskId, 'pending');
this.updateTimerDisplay(taskId);
// 刷新任务列表以显示总时长
if (window.app) {
window.app.loadTasks();
}
} catch (error) {
console.error('停止计时失败:', error);
throw error;
}
}
// 清除计时器
clearTimer(taskId) {
const intervalId = this.timerIntervals.get(taskId);
if (intervalId) {
clearInterval(intervalId);
this.timerIntervals.delete(taskId);
}
this.activeTimers.delete(taskId);
}
// 更新计时器
updateTimer(taskId) {
const timer = this.activeTimers.get(taskId);
if (!timer) return;
const now = new Date();
// 计算当前会话的时长(从开始计时到现在)
timer.currentSessionDuration = Math.floor((now - timer.startTime) / 1000);
this.updateTimerDisplay(taskId);
}
// 更新计时器显示
updateTimerDisplay(taskId) {
const timer = this.activeTimers.get(taskId);
const displayElement = document.querySelector(`[data-task-id="${taskId}"] .timer-display`);
if (!displayElement) return;
if (timer && timer.isRunning) {
// 显示当前会话的时长从0开始
const duration = timer.currentSessionDuration || 0;
const hours = Math.floor(duration / 3600);
const minutes = Math.floor((duration % 3600) / 60);
const seconds = duration % 60;
displayElement.textContent = `${hours.toString().padStart(2, '0')}:${minutes.toString().padStart(2, '0')}:${seconds.toString().padStart(2, '0')}`;
displayElement.style.color = '#48bb78';
} else {
// 显示总时长
this.updateTotalTimeDisplay(taskId);
}
}
// 更新总时长显示
async updateTotalTimeDisplay(taskId) {
try {
const tasks = await api.getTasks();
const task = tasks.find(t => t.id === taskId);
if (task) {
const displayElement = document.querySelector(`[data-task-id="${taskId}"] .timer-display`);
if (displayElement) {
const totalSeconds = task.total_time || 0;
const hours = Math.floor(totalSeconds / 3600);
const minutes = Math.floor((totalSeconds % 3600) / 60);
const seconds = totalSeconds % 60;
displayElement.textContent = `${hours.toString().padStart(2, '0')}:${minutes.toString().padStart(2, '0')}:${seconds.toString().padStart(2, '0')}`;
displayElement.style.color = '#4a5568';
}
}
} catch (error) {
console.error('更新总时长显示失败:', error);
}
}
// 更新任务状态
updateTaskStatus(taskId, status) {
const taskElement = document.querySelector(`[data-task-id="${taskId}"]`);
if (!taskElement) return;
// 更新状态类
taskElement.className = taskElement.className.replace(/in-progress|completed|pending/g, '');
taskElement.classList.add(status);
// 更新状态显示
const statusElement = taskElement.querySelector('.task-status');
if (statusElement) {
statusElement.textContent = this.getStatusText(status);
statusElement.className = `task-status ${status}`;
}
// 更新按钮状态
this.updateTimerButtons(taskId, status);
}
// 更新计时器按钮
updateTimerButtons(taskId, status) {
const startBtn = document.querySelector(`[data-task-id="${taskId}"] .start-timer-btn`);
const stopBtn = document.querySelector(`[data-task-id="${taskId}"] .stop-timer-btn`);
if (startBtn && stopBtn) {
if (status === 'in_progress') {
startBtn.style.display = 'none';
stopBtn.style.display = 'inline-flex';
} else {
startBtn.style.display = 'inline-flex';
stopBtn.style.display = 'none';
}
}
}
// 获取状态文本
getStatusText(status) {
const statusMap = {
'pending': '待开始',
'in_progress': '进行中',
'completed': '已完成'
};
return statusMap[status] || status;
}
// 格式化时间
formatTime(seconds) {
const hours = Math.floor(seconds / 3600);
const minutes = Math.floor((seconds % 3600) / 60);
const secs = seconds % 60;
return `${hours.toString().padStart(2, '0')}:${minutes.toString().padStart(2, '0')}:${secs.toString().padStart(2, '0')}`;
}
// 检查是否有活跃计时器
hasActiveTimer(taskId) {
return this.activeTimers.has(taskId);
}
// 获取所有活跃计时器
getActiveTimers() {
return Array.from(this.activeTimers.values());
}
// 停止所有计时器
async stopAllTimers() {
const promises = Array.from(this.activeTimers.keys()).map(taskId =>
this.stopTimer(taskId).catch(error => {
console.error(`停止任务${taskId}计时失败:`, error);
})
);
await Promise.all(promises);
}
// 恢复未结束的计时器(用于页面重新加载后)
async restoreTimers(tasks) {
try {
const taskIds = tasks.map(task => task.id);
if (!taskIds.length) {
return;
}
const statuses = await api.getTimerStatuses(taskIds);
for (const task of tasks) {
const timerStatus = statuses?.[task.id];
if (timerStatus && timerStatus.is_running) {
// 使用服务器返回的开始时间从ISO格式字符串创建Date对象
const startTime = new Date(timerStatus.start_time);
const now = new Date();
// 恢复计时器
const timer = {
taskId: task.id,
startTime: startTime,
currentSessionDuration: Math.floor((now - startTime) / 1000),
isRunning: true
};
this.activeTimers.set(task.id, timer);
// 启动定时器更新
const intervalId = setInterval(() => {
this.updateTimer(task.id);
}, 1000);
this.timerIntervals.set(task.id, intervalId);
// 更新UI
this.updateTimerDisplay(task.id);
this.updateTaskStatus(task.id, 'in_progress');
console.log(`恢复了任务 ${task.id} 的计时器`);
}
}
} catch (error) {
console.error('恢复计时器失败:', error);
}
}
}
// 创建全局计时器实例
const taskTimer = new TaskTimer();