diff --git a/backend/src/main/java/com/designhub/service/AnalyticsService.java b/backend/src/main/java/com/designhub/service/AnalyticsService.java index 1a04981..91cc507 100644 --- a/backend/src/main/java/com/designhub/service/AnalyticsService.java +++ b/backend/src/main/java/com/designhub/service/AnalyticsService.java @@ -630,12 +630,13 @@ public class AnalyticsService { int completedCount = mainCompletedCount + subCompletedCount; int inProgressCount = mainInProgressCount + subInProgressCount; - // 计算准时完成数 + // 计算准时完成数(主任务 + 子任务) int onTimeCount = 0; int overdueCount = 0; double totalTime = 0; int timeCount = 0; + // 主任务准时率计算 for (Task task : designerTasks) { if ("completed".equals(task.getStatus()) && task.getActualEndTime() != null) { LocalDateTime start = task.getActualStartTime() != null ? task.getActualStartTime() : task.getStartTime(); @@ -658,6 +659,22 @@ public class AnalyticsService { } } + // 子任务准时率计算 + for (TaskItem item : designerTaskItems) { + if ("completed".equals(item.getStatus()) && item.getCompleteTime() != null) { + // 判断是否准时 + if (item.getDeadline() != null) { + if (!item.getCompleteTime().isAfter(item.getDeadline())) { + onTimeCount++; + } else { + overdueCount++; + } + } else { + onTimeCount++; // 没有deadline的默认准时 + } + } + } + double completionRate = receivedCount > 0 ? (double) completedCount / receivedCount * 100 : 0; double onTimeRate = completedCount > 0 ? (double) onTimeCount / completedCount * 100 : 0; double avgProcessTime = timeCount > 0 ? totalTime / timeCount : 0; diff --git a/frontend/src/components/DailyProgressReminder.vue b/frontend/src/components/DailyProgressReminder.vue index 6fcb24e..7eaf9f8 100644 --- a/frontend/src/components/DailyProgressReminder.vue +++ b/frontend/src/components/DailyProgressReminder.vue @@ -2,18 +2,22 @@ @@ -239,14 +243,21 @@ const handleNoTasksConfirm = () => { message.success('确认成功') } +// 关闭弹窗 +const handleClose = () => { + visible.value = false +} + +// 稍后提醒(跳过本次) +const handleSkip = () => { + visible.value = false + message.info('已跳过,下次登录时将再次提醒') +} + // 提交所有任务进度 const handleSubmit = async () => { - // 验证:所有任务进度必须有更新 + // 验证:新进度不能小于当前进度 const hasError = tasks.value.some(task => { - if (task.newProgress === task.progress) { - task.error = '进度未更新,请调整进度' - return true - } if (task.newProgress < (task.progress || 0)) { task.error = '新进度不能小于当前进度' return true @@ -259,17 +270,29 @@ const handleSubmit = async () => { message.warning('请检查所有任务进度') return } + + // 过滤出有更新的任务 + const updatedTasks = tasks.value.filter(task => task.newProgress !== task.progress) submitting.value = true try { - // 批量更新进度 - const updates = tasks.value.map(task => ({ + // 批量更新进度(只更新有变化的任务) + const updates = updatedTasks.map(task => ({ taskId: task.id, progress: task.newProgress, remark: task.progressRemark })) + // 如果没有任何更新,直接关闭 + if (updates.length === 0) { + const today = dayjs().format('YYYY-MM-DD') + localStorage.setItem('lastProgressUpdateDate', today) + message.success('已确认今日进度') + visible.value = false + return + } + const response = await taskApi.batchUpdateProgress(updates) if (response.code === 200) { @@ -302,6 +325,7 @@ defineExpose({ display: flex; align-items: center; gap: 10px; + width: 100%; .title-icon { font-size: 20px; @@ -311,6 +335,19 @@ defineExpose({ font-size: 18px; font-weight: 600; color: $text-primary; + flex: 1; + } + + .skip-btn { + color: $text-tertiary; + font-size: 13px; + height: auto; + padding: 4px 12px; + + &:hover { + color: $text-secondary; + background: rgba(255, 255, 255, 0.05); + } } }