diff --git a/.gitignore b/.gitignore index 37fe845..3849f8c 100644 --- a/.gitignore +++ b/.gitignore @@ -23,4 +23,8 @@ tsconfig.tsbuildinfo #qoder -**.qoder** \ No newline at end of file +**.qoder** + +#trae + +**.trae** \ No newline at end of file diff --git a/src/api/project.ts b/src/api/project.ts index 6bc61e0..df9cc62 100644 --- a/src/api/project.ts +++ b/src/api/project.ts @@ -333,6 +333,64 @@ export const getProjectDetail = (projectId: string) => { ); }; +export type OverallProgressAssessment = { + status?: string; + deviationPercentage?: number; + description?: string; + keyIssues?: string[]; +}; + +export type DailyReportUpdateSuggestionVO = { + suggestionId?: string; + targetType?: string; + targetId?: string; + targetName?: string; + currentStatus?: string; + currentProgress?: number; + suggestedStatus?: string; + suggestedProgress?: number; + reason?: string; + confidence?: number; + status?: string; +}; + +export type DailyReportAnalysisSuggestionsVO = { + analysisId?: string; + reportId?: string; + projectId?: string; + reportDate?: string; + overallProgressAssessment?: OverallProgressAssessment; + suggestions?: DailyReportUpdateSuggestionVO[]; +}; + +export const getDailyReportAnalysisSuggestions = (params: { + projectId: string; + reportId?: string; + reportDate?: string; + submitterUsername?: string; +}) => { + return http.request>( + "get", + "/api/v1/daily-report/analysis/suggestions", + { params } + ); +}; + +export type ApplyDailyReportSuggestionsRequest = { + projectId: string; + suggestionIds: string[]; +}; + +export const applyDailyReportAnalysisSuggestions = ( + data: ApplyDailyReportSuggestionsRequest +) => { + return http.request>( + "post", + "/api/v1/daily-report/analysis/suggestions/apply", + { data } + ); +}; + // ==================== 项目初始化(复用 system.ts 中的定义) ==================== /** 项目信息 */ diff --git a/src/api/日报分析建议.openapi.json b/src/api/日报分析建议.openapi.json new file mode 100644 index 0000000..a2a719c --- /dev/null +++ b/src/api/日报分析建议.openapi.json @@ -0,0 +1,295 @@ +{ + "openapi": "3.0.1", + "info": { + "title": "默认模块", + "description": "", + "version": "1.0.0" + }, + "tags": [], + "paths": { + "/api/v1/daily-report/analysis/suggestions": { + "get": { + "summary": "获取日报进度更新建议", + "deprecated": false, + "description": "", + "tags": [], + "parameters": [ + { + "name": "projectId", + "in": "query", + "description": "项目ID", + "required": true, + "schema": { + "type": "integer" + } + }, + { + "name": "reportId", + "in": "query", + "description": "日报ID", + "required": false, + "schema": { + "type": "integer" + } + }, + { + "name": "reportDate", + "in": "query", + "description": "日报日期", + "required": false, + "schema": { + "type": "string" + } + }, + { + "name": "submitterUsername", + "in": "query", + "description": "日报提交人用户名", + "required": false, + "schema": { + "type": "string" + } + }, + { + "name": "Authorization", + "in": "header", + "description": "", + "example": "Bearer b35c6f5b-bc0b-4652-bef2-eca04a5cdd95", + "schema": { + "type": "string", + "default": "Bearer b35c6f5b-bc0b-4652-bef2-eca04a5cdd95" + } + } + ], + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/BaseResponseDailyReportAnalysisSuggestionsVO" + } + } + } + } + }, + "security": [] + } + }, + "/api/v1/daily-report/analysis/suggestions/apply": { + "post": { + "summary": "应用日报进度回写建议", + "deprecated": false, + "description": "", + "tags": [], + "parameters": [ + { + "name": "Authorization", + "in": "header", + "description": "", + "example": "Bearer b35c6f5b-bc0b-4652-bef2-eca04a5cdd95", + "schema": { + "type": "string", + "default": "Bearer b35c6f5b-bc0b-4652-bef2-eca04a5cdd95" + } + } + ], + "requestBody": { + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/ApplyDailyReportSuggestionsRequest", + "description": "建议ID列表" + } + } + } + }, + "responses": { + "200": { + "description": "", + "content": { + "application/json": { + "schema": { + "$ref": "#/components/schemas/BaseResponseInteger", + "description": "应用结果" + } + } + } + } + }, + "security": [] + } + } + }, + "components": { + "schemas": { + "OverallProgressAssessment": { + "type": "object", + "properties": { + "status": { + "type": "string", + "description": "进度状态:ahead-提前,on_track-正常,delayed-滞后" + }, + "deviationPercentage": { + "type": "number", + "description": "进度偏差百分比 (正数表示提前,负数表示滞后)" + }, + "description": { + "type": "string", + "description": "评估说明" + }, + "keyIssues": { + "type": "array", + "items": { + "type": "string" + }, + "description": "关键问题" + } + } + }, + "DailyReportUpdateSuggestionVO": { + "type": "object", + "properties": { + "suggestionId": { + "type": "integer", + "description": "", + "format": "int64" + }, + "targetType": { + "type": "string", + "description": "" + }, + "targetId": { + "type": "integer", + "description": "", + "format": "int64" + }, + "targetName": { + "type": "string", + "description": "" + }, + "currentStatus": { + "type": "string", + "description": "" + }, + "currentProgress": { + "type": "integer", + "description": "" + }, + "suggestedStatus": { + "type": "string", + "description": "" + }, + "suggestedProgress": { + "type": "integer", + "description": "" + }, + "reason": { + "type": "string", + "description": "" + }, + "confidence": { + "type": "number", + "description": "" + }, + "status": { + "type": "string", + "description": "" + } + } + }, + "DailyReportAnalysisSuggestionsVO": { + "type": "object", + "properties": { + "analysisId": { + "type": "integer", + "description": "", + "format": "int64" + }, + "reportId": { + "type": "integer", + "description": "", + "format": "int64" + }, + "projectId": { + "type": "integer", + "description": "", + "format": "int64" + }, + "reportDate": { + "type": "string", + "description": "" + }, + "overallProgressAssessment": { + "$ref": "#/components/schemas/OverallProgressAssessment", + "description": "" + }, + "suggestions": { + "type": "array", + "items": { + "$ref": "#/components/schemas/DailyReportUpdateSuggestionVO", + "description": "cn.yinlihupo.domain.vo.DailyReportUpdateSuggestionVO" + }, + "description": "" + } + } + }, + "BaseResponseDailyReportAnalysisSuggestionsVO": { + "type": "object", + "properties": { + "code": { + "type": "integer", + "description": "" + }, + "data": { + "$ref": "#/components/schemas/DailyReportAnalysisSuggestionsVO", + "description": "" + }, + "message": { + "type": "string", + "description": "" + } + } + }, + "BaseResponseInteger": { + "type": "object", + "properties": { + "code": { + "type": "integer", + "description": "" + }, + "data": { + "type": "integer", + "description": "" + }, + "message": { + "type": "string", + "description": "" + } + } + }, + "ApplyDailyReportSuggestionsRequest": { + "type": "object", + "properties": { + "projectId": { + "type": "integer", + "description": "", + "format": "int64" + }, + "suggestionIds": { + "type": "array", + "items": { + "type": "integer" + }, + "description": "" + } + }, + "required": ["projectId", "suggestionIds"] + } + }, + "responses": {}, + "securitySchemes": {} + }, + "servers": [], + "security": [] +} diff --git a/src/views/project/detail.vue b/src/views/project/detail.vue index 97e98f2..950fd56 100644 --- a/src/views/project/detail.vue +++ b/src/views/project/detail.vue @@ -4,6 +4,8 @@ import { useRoute, useRouter } from "vue-router"; import { useRenderIcon } from "@/components/ReIcon/src/hooks"; import { getProjectDetail, + getDailyReportAnalysisSuggestions, + applyDailyReportAnalysisSuggestions, createTask, updateTask, deleteTask, @@ -19,6 +21,8 @@ import { type ProjectTask, type ProjectResource, type ProjectRisk, + type DailyReportAnalysisSuggestionsVO, + type DailyReportUpdateSuggestionVO, type Resource, type ResourceUpdateRequest } from "@/api/project"; @@ -88,6 +92,13 @@ const marginStyle = computed(() => ({ // 项目详情数据 const projectDetail = ref(null); +const suggestionsLoading = ref(false); +const suggestionsApplying = ref(false); +const dailyReportSuggestions = ref( + null +); +const selectedSuggestionIds = ref([]); + // 成员详情模态框 const memberDetailModal = ref(false); const selectedMember = ref(null); @@ -548,6 +559,140 @@ async function fetchProjectDetail() { } } +async function fetchDailyReportSuggestions() { + if (!projectId.value) return; + suggestionsLoading.value = true; + try { + const res = await getDailyReportAnalysisSuggestions({ + projectId: projectId.value + }); + const result = res as any; + if (result.code === 200) { + dailyReportSuggestions.value = result.data || null; + selectedSuggestionIds.value = []; + } + } catch (error) { + console.error("获取进度更新建议失败:", error); + message("获取进度更新建议失败", { type: "error" }); + } finally { + suggestionsLoading.value = false; + } +} + +function handleSuggestionSelectionChange( + rows: DailyReportUpdateSuggestionVO[] +) { + selectedSuggestionIds.value = rows + .map(r => String(r.suggestionId || "")) + .filter(id => id.length > 0); +} + +function getOverallAssessmentType( + status?: string +): "success" | "warning" | "info" | "primary" | "danger" { + switch (status) { + case "ahead": + return "success"; + case "on_track": + return "primary"; + case "delayed": + return "danger"; + default: + return "info"; + } +} + +function getOverallAssessmentText(status?: string) { + switch (status) { + case "ahead": + return "提前"; + case "on_track": + return "正常"; + case "delayed": + return "滞后"; + default: + return status || "未知"; + } +} + +function formatDeviation(deviationPercentage?: number) { + if (deviationPercentage === undefined || deviationPercentage === null) + return "--"; + const value = Number(deviationPercentage); + if (!Number.isFinite(value)) return "--"; + const sign = value > 0 ? "+" : ""; + return `${sign}${value}%`; +} + +function isSuggestionApplied(status?: string) { + const normalized = (status || "").toLowerCase(); + return ( + normalized === "applied" || + normalized === "accepted" || + normalized === "done" || + normalized === "completed" + ); +} + +function getSuggestionStatusType( + status?: string +): "success" | "warning" | "info" | "primary" | "danger" { + if (isSuggestionApplied(status)) return "success"; + if (!status) return "info"; + const normalized = status.toLowerCase(); + if (normalized.includes("reject") || normalized.includes("fail")) + return "danger"; + if (normalized.includes("pending") || normalized.includes("new")) + return "warning"; + return "info"; +} + +function getSuggestionStatusText(status?: string) { + if (isSuggestionApplied(status)) return "已应用"; + if (!status) return "待处理"; + const normalized = status.toLowerCase(); + if (normalized.includes("reject")) return "已拒绝"; + if (normalized.includes("fail")) return "失败"; + if (normalized.includes("pending") || normalized.includes("new")) + return "待处理"; + return status; +} + +async function handleApplySuggestions( + ids: Array +) { + if (!projectId.value) return; + const suggestionIds = Array.from( + new Set(ids.map(i => String(i || "")).filter(Boolean)) + ); + if (suggestionIds.length === 0) return; + if (suggestionsApplying.value) return; + suggestionsApplying.value = true; + try { + const res = await applyDailyReportAnalysisSuggestions({ + projectId: projectId.value, + suggestionIds + }); + const result = res as any; + if (result.code === 200) { + message("已应用进度更新建议", { type: "success" }); + await fetchProjectDetail(); + await fetchDailyReportSuggestions(); + } else { + message(result.message || "应用建议失败", { type: "error" }); + } + } catch (error) { + console.error("应用进度更新建议失败:", error); + message("应用建议失败", { type: "error" }); + } finally { + suggestionsApplying.value = false; + } +} + +async function handleApplySelectedSuggestions() { + await handleApplySuggestions(selectedSuggestionIds.value); +} + // 获取状态文本 function getStatusText(status?: string): string { const statusMap: Record = { @@ -914,6 +1059,7 @@ async function handleDeleteResource(resourceId: string) { onMounted(() => { fetchProjectDetail(); + fetchDailyReportSuggestions(); fetchGanttData(); }); @@ -1076,9 +1222,8 @@ onMounted(() => { - - + @@ -1244,141 +1389,354 @@ onMounted(() => { + + + + - -
-
-
- - - - 项目里程碑 - {{ milestoneList.length }} 个 -
- + + + + + +
+
+
-
-
- + + + + + +
+
+
+
+
+ {{ + milestone.milestoneName + }} + + {{ getMilestoneStatusText(milestone.status) }} + + + 关键 + +
+
+ + + + 计划日期: {{ milestone.planDate }} + + (实际: {{ milestone.actualDate }}) + +
+
+ {{ milestone.description }} +
+
+ + 编辑 + + + +
+ + + + +
+ +
+
+
+ 总体评估 + + {{ + getOverallAssessmentText( + dailyReportSuggestions.overallProgressAssessment + .status + ) + }} + + + 偏差 + {{ + formatDeviation( + dailyReportSuggestions.overallProgressAssessment + .deviationPercentage + ) + }} + +
+
+ {{ + dailyReportSuggestions.overallProgressAssessment + .description + }} +
+
+ + {{ issue }} + +
+
+ + + + + + + + + + + + + + + + + + + + + + + +
+
+
+
+ + + + + +