feat(project): 支持项目初始化任务的异步查询和状态展示
Some checks failed
Lint Code / Lint Code (push) Failing after 23m42s
Some checks failed
Lint Code / Lint Code (push) Failing after 23m42s
- 新增项目初始化任务相关类型定义及接口封装,包括任务列表、任务统计和单个任务状态查询 - 集成 Element Plus 通知组件,增加任务完成和失败时的用户通知提醒 - 在 SSE 状态管理版块添加任务列表状态管理,支持任务状态的动态获取和展示 - 在创建项目向导组件中集成任务列表展示,支持查看任务进度、完成结果及错误信息 - 增加“使用此结果”按钮,允许用户直接应用已完成任务的项目初始化结果 - 对任务列表样式进行设计,区分不同状态的任务视觉效果提升用户体验 - 打开项目创建对话框时自动刷新并加载最新的任务列表数据
This commit is contained in:
@@ -239,3 +239,53 @@ export const confirmProjectInit = (data: ProjectInitResult) => {
|
|||||||
{ data }
|
{ data }
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// ==================== 项目初始化任务(SSE) ====================
|
||||||
|
|
||||||
|
/** 项目初始化任务 VO - 根据 OpenAPI 定义 */
|
||||||
|
export type ProjectInitTaskVO = {
|
||||||
|
taskId: string;
|
||||||
|
userId?: number;
|
||||||
|
status: string; // pending, processing, completed, failed
|
||||||
|
statusDesc: string;
|
||||||
|
progress: number;
|
||||||
|
progressMessage: string;
|
||||||
|
originalFilename: string;
|
||||||
|
createTime: string;
|
||||||
|
startTime?: string;
|
||||||
|
completeTime?: string;
|
||||||
|
result?: ProjectInitResult;
|
||||||
|
errorMessage?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
/** 任务统计信息 */
|
||||||
|
export type TaskStats = {
|
||||||
|
total: number;
|
||||||
|
processing: number;
|
||||||
|
completed: number;
|
||||||
|
failed: number;
|
||||||
|
};
|
||||||
|
|
||||||
|
/** 查询我的任务列表 */
|
||||||
|
export const getMyTasks = () => {
|
||||||
|
return http.request<Result<ProjectInitTaskVO[]>>(
|
||||||
|
"get",
|
||||||
|
"/api/v1/project-init/my-tasks"
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
/** 查询我的任务统计信息 */
|
||||||
|
export const getMyTaskStats = () => {
|
||||||
|
return http.request<Result<TaskStats>>(
|
||||||
|
"get",
|
||||||
|
"/api/v1/project-init/my-tasks/stats"
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|
||||||
|
/** 查询单个任务状态 */
|
||||||
|
export const getTaskStatus = (taskId: string) => {
|
||||||
|
return http.request<Result<ProjectInitTaskVO>>(
|
||||||
|
"get",
|
||||||
|
`/api/v1/project-init/task/${taskId}`
|
||||||
|
);
|
||||||
|
};
|
||||||
|
|||||||
606
src/api/项目sse.openapi.json
Normal file
606
src/api/项目sse.openapi.json
Normal file
@@ -0,0 +1,606 @@
|
|||||||
|
{
|
||||||
|
"openapi": "3.0.1",
|
||||||
|
"info": {
|
||||||
|
"title": "默认模块",
|
||||||
|
"description": "",
|
||||||
|
"version": "1.0.0"
|
||||||
|
},
|
||||||
|
"tags": [],
|
||||||
|
"paths": {
|
||||||
|
"/api/v1/project-init/sse/submit-task": {
|
||||||
|
"post": {
|
||||||
|
"summary": "通过 SSE 提交项目初始化任务",
|
||||||
|
"deprecated": false,
|
||||||
|
"description": "使用通用 SSE 通道,通过 userId 推送进度",
|
||||||
|
"tags": [],
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"name": "userId",
|
||||||
|
"in": "query",
|
||||||
|
"description": "用户ID",
|
||||||
|
"required": true,
|
||||||
|
"schema": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Authorization",
|
||||||
|
"in": "header",
|
||||||
|
"description": "",
|
||||||
|
"example": "Bearer 6bf1de0f-9edf-413f-a98f-1103b8dc30fc",
|
||||||
|
"schema": {
|
||||||
|
"type": "string",
|
||||||
|
"default": "Bearer 6bf1de0f-9edf-413f-a98f-1103b8dc30fc"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"requestBody": {
|
||||||
|
"content": {
|
||||||
|
"multipart/form-data": {
|
||||||
|
"schema": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"file": {
|
||||||
|
"description": "项目资料文件",
|
||||||
|
"type": "string",
|
||||||
|
"format": "binary"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": ["file"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "",
|
||||||
|
"content": {
|
||||||
|
"application/json": {
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/components/schemas/BaseResponseMapObject",
|
||||||
|
"description": "提交结果"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"security": []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"/api/v1/project-init/my-tasks": {
|
||||||
|
"get": {
|
||||||
|
"summary": "查询我的任务列表",
|
||||||
|
"deprecated": false,
|
||||||
|
"description": "根据当前登录用户的token查询其所有任务",
|
||||||
|
"tags": [],
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"name": "Authorization",
|
||||||
|
"in": "header",
|
||||||
|
"description": "",
|
||||||
|
"example": "Bearer 6bf1de0f-9edf-413f-a98f-1103b8dc30fc",
|
||||||
|
"schema": {
|
||||||
|
"type": "string",
|
||||||
|
"default": "Bearer 6bf1de0f-9edf-413f-a98f-1103b8dc30fc"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "",
|
||||||
|
"content": {
|
||||||
|
"application/json": {
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/components/schemas/BaseResponseListProjectInitTaskVO",
|
||||||
|
"description": "任务列表"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"security": []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"/api/v1/project-init/my-tasks/stats": {
|
||||||
|
"get": {
|
||||||
|
"summary": "查询我的任务统计信息",
|
||||||
|
"deprecated": false,
|
||||||
|
"description": "",
|
||||||
|
"tags": [],
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"name": "Authorization",
|
||||||
|
"in": "header",
|
||||||
|
"description": "",
|
||||||
|
"example": "Bearer 6bf1de0f-9edf-413f-a98f-1103b8dc30fc",
|
||||||
|
"schema": {
|
||||||
|
"type": "string",
|
||||||
|
"default": "Bearer 6bf1de0f-9edf-413f-a98f-1103b8dc30fc"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "",
|
||||||
|
"content": {
|
||||||
|
"application/json": {
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/components/schemas/BaseResponseMapObject",
|
||||||
|
"description": "统计信息"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"security": []
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"/api/v1/project-init/task/{taskId}": {
|
||||||
|
"get": {
|
||||||
|
"summary": "查询单个任务状态",
|
||||||
|
"deprecated": false,
|
||||||
|
"description": "",
|
||||||
|
"tags": [],
|
||||||
|
"parameters": [
|
||||||
|
{
|
||||||
|
"name": "taskId",
|
||||||
|
"in": "path",
|
||||||
|
"description": "任务ID",
|
||||||
|
"required": true,
|
||||||
|
"schema": {
|
||||||
|
"type": "string"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Authorization",
|
||||||
|
"in": "header",
|
||||||
|
"description": "",
|
||||||
|
"example": "Bearer 6bf1de0f-9edf-413f-a98f-1103b8dc30fc",
|
||||||
|
"schema": {
|
||||||
|
"type": "string",
|
||||||
|
"default": "Bearer 6bf1de0f-9edf-413f-a98f-1103b8dc30fc"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"responses": {
|
||||||
|
"200": {
|
||||||
|
"description": "",
|
||||||
|
"content": {
|
||||||
|
"application/json": {
|
||||||
|
"schema": {
|
||||||
|
"$ref": "#/components/schemas/BaseResponseProjectInitTaskVO",
|
||||||
|
"description": "任务状态"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"security": []
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"components": {
|
||||||
|
"schemas": {
|
||||||
|
"ProjectInfo": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"project_name": {
|
||||||
|
"type": "string",
|
||||||
|
"description": ""
|
||||||
|
},
|
||||||
|
"project_type": {
|
||||||
|
"type": "string",
|
||||||
|
"description": ""
|
||||||
|
},
|
||||||
|
"description": {
|
||||||
|
"type": "string",
|
||||||
|
"description": ""
|
||||||
|
},
|
||||||
|
"objectives": {
|
||||||
|
"type": "string",
|
||||||
|
"description": ""
|
||||||
|
},
|
||||||
|
"plan_start_date": {
|
||||||
|
"type": "string",
|
||||||
|
"description": ""
|
||||||
|
},
|
||||||
|
"plan_end_date": {
|
||||||
|
"type": "string",
|
||||||
|
"description": ""
|
||||||
|
},
|
||||||
|
"budget": {
|
||||||
|
"type": "number",
|
||||||
|
"description": ""
|
||||||
|
},
|
||||||
|
"currency": {
|
||||||
|
"type": "string",
|
||||||
|
"description": ""
|
||||||
|
},
|
||||||
|
"priority": {
|
||||||
|
"type": "string",
|
||||||
|
"description": ""
|
||||||
|
},
|
||||||
|
"tags": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"description": ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"MilestoneInfo": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"milestone_name": {
|
||||||
|
"type": "string",
|
||||||
|
"description": ""
|
||||||
|
},
|
||||||
|
"description": {
|
||||||
|
"type": "string",
|
||||||
|
"description": ""
|
||||||
|
},
|
||||||
|
"plan_date": {
|
||||||
|
"type": "string",
|
||||||
|
"description": ""
|
||||||
|
},
|
||||||
|
"deliverables": {
|
||||||
|
"type": "string",
|
||||||
|
"description": ""
|
||||||
|
},
|
||||||
|
"owner_role": {
|
||||||
|
"type": "string",
|
||||||
|
"description": ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"TaskInfo": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"task_id": {
|
||||||
|
"type": "string",
|
||||||
|
"description": ""
|
||||||
|
},
|
||||||
|
"task_name": {
|
||||||
|
"type": "string",
|
||||||
|
"description": ""
|
||||||
|
},
|
||||||
|
"parent_task_id": {
|
||||||
|
"type": "string",
|
||||||
|
"description": ""
|
||||||
|
},
|
||||||
|
"description": {
|
||||||
|
"type": "string",
|
||||||
|
"description": ""
|
||||||
|
},
|
||||||
|
"plan_start_date": {
|
||||||
|
"type": "string",
|
||||||
|
"description": ""
|
||||||
|
},
|
||||||
|
"plan_end_date": {
|
||||||
|
"type": "string",
|
||||||
|
"description": ""
|
||||||
|
},
|
||||||
|
"estimated_hours": {
|
||||||
|
"type": "integer",
|
||||||
|
"description": ""
|
||||||
|
},
|
||||||
|
"priority": {
|
||||||
|
"type": "string",
|
||||||
|
"description": ""
|
||||||
|
},
|
||||||
|
"assignee_role": {
|
||||||
|
"type": "string",
|
||||||
|
"description": ""
|
||||||
|
},
|
||||||
|
"dependencies": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"description": ""
|
||||||
|
},
|
||||||
|
"deliverables": {
|
||||||
|
"type": "string",
|
||||||
|
"description": ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"BaseResponseMapObject": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"code": {
|
||||||
|
"type": "integer",
|
||||||
|
"description": ""
|
||||||
|
},
|
||||||
|
"data": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"total": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"processing": {
|
||||||
|
"type": "integer"
|
||||||
|
},
|
||||||
|
"completed": {
|
||||||
|
"type": "null"
|
||||||
|
},
|
||||||
|
"failed": {
|
||||||
|
"type": "null"
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"description": ""
|
||||||
|
},
|
||||||
|
"message": {
|
||||||
|
"type": "string",
|
||||||
|
"description": ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"MemberInfo": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"name": {
|
||||||
|
"type": "string",
|
||||||
|
"description": ""
|
||||||
|
},
|
||||||
|
"role_code": {
|
||||||
|
"type": "string",
|
||||||
|
"description": ""
|
||||||
|
},
|
||||||
|
"responsibility": {
|
||||||
|
"type": "string",
|
||||||
|
"description": ""
|
||||||
|
},
|
||||||
|
"department": {
|
||||||
|
"type": "string",
|
||||||
|
"description": ""
|
||||||
|
},
|
||||||
|
"weekly_hours": {
|
||||||
|
"type": "integer",
|
||||||
|
"description": ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ResourceInfo": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"resource_name": {
|
||||||
|
"type": "string",
|
||||||
|
"description": ""
|
||||||
|
},
|
||||||
|
"resource_type": {
|
||||||
|
"type": "string",
|
||||||
|
"description": ""
|
||||||
|
},
|
||||||
|
"quantity": {
|
||||||
|
"type": "number",
|
||||||
|
"description": ""
|
||||||
|
},
|
||||||
|
"unit": {
|
||||||
|
"type": "string",
|
||||||
|
"description": ""
|
||||||
|
},
|
||||||
|
"unit_price": {
|
||||||
|
"type": "number",
|
||||||
|
"description": ""
|
||||||
|
},
|
||||||
|
"supplier": {
|
||||||
|
"type": "string",
|
||||||
|
"description": ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"RiskInfo": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"risk_name": {
|
||||||
|
"type": "string",
|
||||||
|
"description": ""
|
||||||
|
},
|
||||||
|
"category": {
|
||||||
|
"type": "string",
|
||||||
|
"description": ""
|
||||||
|
},
|
||||||
|
"description": {
|
||||||
|
"type": "string",
|
||||||
|
"description": ""
|
||||||
|
},
|
||||||
|
"probability": {
|
||||||
|
"type": "integer",
|
||||||
|
"description": ""
|
||||||
|
},
|
||||||
|
"impact": {
|
||||||
|
"type": "integer",
|
||||||
|
"description": ""
|
||||||
|
},
|
||||||
|
"mitigation_plan": {
|
||||||
|
"type": "string",
|
||||||
|
"description": ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"TimelineNodeInfo": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"node_name": {
|
||||||
|
"type": "string",
|
||||||
|
"description": ""
|
||||||
|
},
|
||||||
|
"node_type": {
|
||||||
|
"type": "string",
|
||||||
|
"description": ""
|
||||||
|
},
|
||||||
|
"plan_date": {
|
||||||
|
"type": "string",
|
||||||
|
"description": ""
|
||||||
|
},
|
||||||
|
"description": {
|
||||||
|
"type": "string",
|
||||||
|
"description": ""
|
||||||
|
},
|
||||||
|
"kb_scope": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
|
"description": ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ProjectInitResult": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"project": {
|
||||||
|
"$ref": "#/components/schemas/ProjectInfo",
|
||||||
|
"description": "项目基本信息"
|
||||||
|
},
|
||||||
|
"milestones": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/components/schemas/MilestoneInfo",
|
||||||
|
"description": "cn.yinlihupo.domain.dto.ProjectInitResult.MilestoneInfo"
|
||||||
|
},
|
||||||
|
"description": "里程碑列表"
|
||||||
|
},
|
||||||
|
"tasks": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/components/schemas/TaskInfo",
|
||||||
|
"description": "cn.yinlihupo.domain.dto.ProjectInitResult.TaskInfo"
|
||||||
|
},
|
||||||
|
"description": "任务清单"
|
||||||
|
},
|
||||||
|
"members": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/components/schemas/MemberInfo",
|
||||||
|
"description": "cn.yinlihupo.domain.dto.ProjectInitResult.MemberInfo"
|
||||||
|
},
|
||||||
|
"description": "项目成员"
|
||||||
|
},
|
||||||
|
"resources": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/components/schemas/ResourceInfo",
|
||||||
|
"description": "cn.yinlihupo.domain.dto.ProjectInitResult.ResourceInfo"
|
||||||
|
},
|
||||||
|
"description": "资源需求"
|
||||||
|
},
|
||||||
|
"risks": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/components/schemas/RiskInfo",
|
||||||
|
"description": "cn.yinlihupo.domain.dto.ProjectInitResult.RiskInfo"
|
||||||
|
},
|
||||||
|
"description": "风险识别"
|
||||||
|
},
|
||||||
|
"timeline_nodes": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/components/schemas/TimelineNodeInfo",
|
||||||
|
"description": "cn.yinlihupo.domain.dto.ProjectInitResult.TimelineNodeInfo"
|
||||||
|
},
|
||||||
|
"description": "时间节点"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"ProjectInitTaskVO": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"taskId": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "任务ID"
|
||||||
|
},
|
||||||
|
"userId": {
|
||||||
|
"type": "integer",
|
||||||
|
"description": "用户ID(任务所属用户)",
|
||||||
|
"format": "int64"
|
||||||
|
},
|
||||||
|
"status": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "任务状态: pending-待处理, processing-处理中, completed-已完成, failed-失败"
|
||||||
|
},
|
||||||
|
"statusDesc": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "状态描述"
|
||||||
|
},
|
||||||
|
"progress": {
|
||||||
|
"type": "integer",
|
||||||
|
"description": "当前进度百分比 (0-100)"
|
||||||
|
},
|
||||||
|
"progressMessage": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "进度描述信息"
|
||||||
|
},
|
||||||
|
"originalFilename": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "原始文件名"
|
||||||
|
},
|
||||||
|
"createTime": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "任务创建时间"
|
||||||
|
},
|
||||||
|
"startTime": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "任务开始处理时间"
|
||||||
|
},
|
||||||
|
"completeTime": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "任务完成时间"
|
||||||
|
},
|
||||||
|
"result": {
|
||||||
|
"$ref": "#/components/schemas/ProjectInitResult",
|
||||||
|
"description": "处理结果(仅当status=completed时有值)"
|
||||||
|
},
|
||||||
|
"errorMessage": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "错误信息(仅当status=failed时有值)"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"BaseResponseListProjectInitTaskVO": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"code": {
|
||||||
|
"type": "integer",
|
||||||
|
"description": ""
|
||||||
|
},
|
||||||
|
"data": {
|
||||||
|
"type": "array",
|
||||||
|
"items": {
|
||||||
|
"$ref": "#/components/schemas/ProjectInitTaskVO",
|
||||||
|
"description": "项目初始化异步任务VO"
|
||||||
|
},
|
||||||
|
"description": ""
|
||||||
|
},
|
||||||
|
"message": {
|
||||||
|
"type": "string",
|
||||||
|
"description": ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"BaseResponseProjectInitTaskVO": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"code": {
|
||||||
|
"type": "integer",
|
||||||
|
"description": ""
|
||||||
|
},
|
||||||
|
"data": {
|
||||||
|
"$ref": "#/components/schemas/ProjectInitTaskVO",
|
||||||
|
"description": ""
|
||||||
|
},
|
||||||
|
"message": {
|
||||||
|
"type": "string",
|
||||||
|
"description": ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"responses": {},
|
||||||
|
"securitySchemes": {}
|
||||||
|
},
|
||||||
|
"servers": [],
|
||||||
|
"security": []
|
||||||
|
}
|
||||||
@@ -2,6 +2,11 @@ import { defineStore } from "pinia";
|
|||||||
import { ref, computed } from "vue";
|
import { ref, computed } from "vue";
|
||||||
import { SseClient, type ProjectInitTaskVO } from "@/utils/sse/SseClient";
|
import { SseClient, type ProjectInitTaskVO } from "@/utils/sse/SseClient";
|
||||||
import { store } from "../utils";
|
import { store } from "../utils";
|
||||||
|
import {
|
||||||
|
getMyTasks as fetchTasksApi,
|
||||||
|
type ProjectInitTaskVO as ApiTaskVO
|
||||||
|
} from "@/api/project";
|
||||||
|
import { ElNotification } from "element-plus";
|
||||||
|
|
||||||
export const useSseStore = defineStore("sse", () => {
|
export const useSseStore = defineStore("sse", () => {
|
||||||
// State
|
// State
|
||||||
@@ -13,12 +18,20 @@ export const useSseStore = defineStore("sse", () => {
|
|||||||
"idle" | "submitted" | "processing" | "completed" | "error"
|
"idle" | "submitted" | "processing" | "completed" | "error"
|
||||||
>("idle");
|
>("idle");
|
||||||
const errorMessage = ref("");
|
const errorMessage = ref("");
|
||||||
|
// 我的任务列表
|
||||||
|
const myTasks = ref<ApiTaskVO[]>([]);
|
||||||
|
// 是否有待处理的任务
|
||||||
|
const hasProcessingTask = computed(() =>
|
||||||
|
myTasks.value.some(t => t.status === "processing" || t.status === "pending")
|
||||||
|
);
|
||||||
|
|
||||||
// Getters
|
// Getters
|
||||||
const getIsConnected = computed(() => isConnected.value);
|
const getIsConnected = computed(() => isConnected.value);
|
||||||
const getCurrentTask = computed(() => currentTask.value);
|
const getCurrentTask = computed(() => currentTask.value);
|
||||||
const getTaskProgress = computed(() => taskProgress.value);
|
const getTaskProgress = computed(() => taskProgress.value);
|
||||||
const getTaskStatus = computed(() => taskStatus.value);
|
const getTaskStatus = computed(() => taskStatus.value);
|
||||||
|
const getMyTasks = computed(() => myTasks.value);
|
||||||
|
const getHasProcessingTask = computed(() => hasProcessingTask.value);
|
||||||
|
|
||||||
// Actions
|
// Actions
|
||||||
/**
|
/**
|
||||||
@@ -55,6 +68,18 @@ export const useSseStore = defineStore("sse", () => {
|
|||||||
taskProgress.value = 100;
|
taskProgress.value = 100;
|
||||||
taskStatus.value = "completed";
|
taskStatus.value = "completed";
|
||||||
console.log("SSE Store: 任务完成", data.result);
|
console.log("SSE Store: 任务完成", data.result);
|
||||||
|
|
||||||
|
// 发送通知
|
||||||
|
ElNotification({
|
||||||
|
title: "项目解析完成",
|
||||||
|
message: `文件 "${data.originalFilename}" 已解析完成,请前往新建项目查看预览数据。`,
|
||||||
|
type: "success",
|
||||||
|
duration: 5000,
|
||||||
|
position: "top-right"
|
||||||
|
});
|
||||||
|
|
||||||
|
// 刷新任务列表
|
||||||
|
fetchMyTasks();
|
||||||
});
|
});
|
||||||
|
|
||||||
// 监听任务提交
|
// 监听任务提交
|
||||||
@@ -71,6 +96,18 @@ export const useSseStore = defineStore("sse", () => {
|
|||||||
taskStatus.value = "error";
|
taskStatus.value = "error";
|
||||||
errorMessage.value = data.error;
|
errorMessage.value = data.error;
|
||||||
console.error("SSE Store: 任务错误", data.error);
|
console.error("SSE Store: 任务错误", data.error);
|
||||||
|
|
||||||
|
// 发送错误通知
|
||||||
|
ElNotification({
|
||||||
|
title: "项目解析失败",
|
||||||
|
message: data.error || "文件解析失败,请重试",
|
||||||
|
type: "error",
|
||||||
|
duration: 5000,
|
||||||
|
position: "top-right"
|
||||||
|
});
|
||||||
|
|
||||||
|
// 刷新任务列表
|
||||||
|
fetchMyTasks();
|
||||||
});
|
});
|
||||||
|
|
||||||
// 监听连接错误
|
// 监听连接错误
|
||||||
@@ -122,6 +159,20 @@ export const useSseStore = defineStore("sse", () => {
|
|||||||
errorMessage.value = "";
|
errorMessage.value = "";
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 查询我的任务列表
|
||||||
|
*/
|
||||||
|
async function fetchMyTasks() {
|
||||||
|
try {
|
||||||
|
const { code, data } = await fetchTasksApi();
|
||||||
|
if (code === 200 && data) {
|
||||||
|
myTasks.value = data;
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error("获取任务列表失败", error);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return {
|
return {
|
||||||
// State
|
// State
|
||||||
sseClient,
|
sseClient,
|
||||||
@@ -130,16 +181,20 @@ export const useSseStore = defineStore("sse", () => {
|
|||||||
taskProgress,
|
taskProgress,
|
||||||
taskStatus,
|
taskStatus,
|
||||||
errorMessage,
|
errorMessage,
|
||||||
|
myTasks,
|
||||||
// Getters
|
// Getters
|
||||||
getIsConnected,
|
getIsConnected,
|
||||||
getCurrentTask,
|
getCurrentTask,
|
||||||
getTaskProgress,
|
getTaskProgress,
|
||||||
getTaskStatus,
|
getTaskStatus,
|
||||||
|
getMyTasks,
|
||||||
|
getHasProcessingTask,
|
||||||
// Actions
|
// Actions
|
||||||
initSse,
|
initSse,
|
||||||
closeSse,
|
closeSse,
|
||||||
submitProjectInitTask,
|
submitProjectInitTask,
|
||||||
resetTaskStatus
|
resetTaskStatus,
|
||||||
|
fetchMyTasks
|
||||||
};
|
};
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { ref, reactive, computed, watch } from "vue";
|
import { ref, reactive, computed, watch, onMounted } from "vue";
|
||||||
import { message } from "@/utils/message";
|
import { message } from "@/utils/message";
|
||||||
import { confirmProjectInit } from "@/api/project";
|
import { confirmProjectInit, type ProjectInitTaskVO } from "@/api/project";
|
||||||
import type { ProjectInitResult } from "@/api/project";
|
import type { ProjectInitResult } from "@/api/project";
|
||||||
import {
|
import {
|
||||||
WizardStep,
|
WizardStep,
|
||||||
@@ -16,6 +16,7 @@ import CheckIcon from "~icons/ri/check-line";
|
|||||||
import DeleteIcon from "~icons/ri/delete-bin-line";
|
import DeleteIcon from "~icons/ri/delete-bin-line";
|
||||||
import AddIcon from "~icons/ri/add-line";
|
import AddIcon from "~icons/ri/add-line";
|
||||||
import LoadingIcon from "~icons/ri/loader-4-line";
|
import LoadingIcon from "~icons/ri/loader-4-line";
|
||||||
|
import TimeIcon from "~icons/ri/time-line";
|
||||||
|
|
||||||
const props = defineProps<{
|
const props = defineProps<{
|
||||||
visible: boolean;
|
visible: boolean;
|
||||||
@@ -42,6 +43,18 @@ const sseStore = useSseStoreHook();
|
|||||||
const taskProgress = computed(() => sseStore.taskProgress);
|
const taskProgress = computed(() => sseStore.taskProgress);
|
||||||
const taskStatus = computed(() => sseStore.taskStatus);
|
const taskStatus = computed(() => sseStore.taskStatus);
|
||||||
const currentTask = computed(() => sseStore.currentTask);
|
const currentTask = computed(() => sseStore.currentTask);
|
||||||
|
const myTasks = computed(() => sseStore.getMyTasks);
|
||||||
|
const hasProcessingTask = computed(() => sseStore.getHasProcessingTask);
|
||||||
|
|
||||||
|
// 对话框打开时查询任务列表
|
||||||
|
watch(
|
||||||
|
() => props.visible,
|
||||||
|
visible => {
|
||||||
|
if (visible) {
|
||||||
|
sseStore.fetchMyTasks();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
);
|
||||||
|
|
||||||
// 监听任务完成
|
// 监听任务完成
|
||||||
watch(
|
watch(
|
||||||
@@ -272,6 +285,15 @@ function removeRisk(index: number) {
|
|||||||
projectData.risks.splice(index, 1);
|
projectData.risks.splice(index, 1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 使用已完成任务的结果
|
||||||
|
function handleUseTaskResult(task: ProjectInitTaskVO) {
|
||||||
|
if (task.result) {
|
||||||
|
Object.assign(projectData, task.result);
|
||||||
|
currentStep.value = WizardStep.Preview;
|
||||||
|
message("已加载任务结果", { type: "success" });
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// 标签输入
|
// 标签输入
|
||||||
const tagInput = ref("");
|
const tagInput = ref("");
|
||||||
function handleTagInput() {
|
function handleTagInput() {
|
||||||
@@ -331,6 +353,69 @@ function removeTag(tag: string) {
|
|||||||
</template>
|
</template>
|
||||||
</el-upload>
|
</el-upload>
|
||||||
|
|
||||||
|
<!-- 待处理任务列表 -->
|
||||||
|
<div v-if="myTasks.length > 0" class="task-list-container mt-4">
|
||||||
|
<div class="flex items-center gap-2 mb-3">
|
||||||
|
<el-icon class="text-blue-500">
|
||||||
|
<component :is="useRenderIcon(TimeIcon)" />
|
||||||
|
</el-icon>
|
||||||
|
<span class="text-sm font-medium">我的任务</span>
|
||||||
|
</div>
|
||||||
|
<div class="task-list">
|
||||||
|
<div
|
||||||
|
v-for="task in myTasks"
|
||||||
|
:key="task.taskId"
|
||||||
|
class="task-item"
|
||||||
|
:class="{
|
||||||
|
'task-processing': task.status === 'processing',
|
||||||
|
'task-completed': task.status === 'completed',
|
||||||
|
'task-failed': task.status === 'failed'
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
<div class="task-info">
|
||||||
|
<span class="task-filename">{{ task.originalFilename }}</span>
|
||||||
|
<el-tag
|
||||||
|
:type="
|
||||||
|
task.status === 'completed'
|
||||||
|
? 'success'
|
||||||
|
: task.status === 'processing'
|
||||||
|
? 'warning'
|
||||||
|
: task.status === 'failed'
|
||||||
|
? 'danger'
|
||||||
|
: 'info'
|
||||||
|
"
|
||||||
|
size="small"
|
||||||
|
>
|
||||||
|
{{ task.statusDesc }}
|
||||||
|
</el-tag>
|
||||||
|
</div>
|
||||||
|
<div v-if="task.status === 'processing'" class="task-progress">
|
||||||
|
<el-progress
|
||||||
|
:percentage="task.progress"
|
||||||
|
:stroke-width="4"
|
||||||
|
:show-text="false"
|
||||||
|
/>
|
||||||
|
<span class="text-xs text-gray-500 ml-2"
|
||||||
|
>{{ task.progress }}%</span
|
||||||
|
>
|
||||||
|
</div>
|
||||||
|
<div v-if="task.status === 'completed'" class="task-actions">
|
||||||
|
<el-button
|
||||||
|
type="primary"
|
||||||
|
size="small"
|
||||||
|
link
|
||||||
|
@click="handleUseTaskResult(task)"
|
||||||
|
>
|
||||||
|
使用此结果
|
||||||
|
</el-button>
|
||||||
|
</div>
|
||||||
|
<div v-if="task.status === 'failed'" class="task-error">
|
||||||
|
<span class="text-xs text-red-500">{{ task.errorMessage }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
|
||||||
<!-- 进度显示 -->
|
<!-- 进度显示 -->
|
||||||
<div v-if="uploading" class="progress-container mt-6">
|
<div v-if="uploading" class="progress-container mt-6">
|
||||||
<div class="flex-c gap-2 mb-2">
|
<div class="flex-c gap-2 mb-2">
|
||||||
@@ -770,6 +855,74 @@ function removeTag(tag: string) {
|
|||||||
padding: 20px;
|
padding: 20px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.task-list-container {
|
||||||
|
padding: 12px 16px;
|
||||||
|
background: var(--el-fill-color-lighter);
|
||||||
|
border-radius: 8px;
|
||||||
|
|
||||||
|
.task-list {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.task-item {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
align-items: center;
|
||||||
|
padding: 10px 12px;
|
||||||
|
background: white;
|
||||||
|
border-left: 3px solid var(--el-color-info);
|
||||||
|
border-radius: 6px;
|
||||||
|
|
||||||
|
&.task-processing {
|
||||||
|
background: var(--el-color-warning-light-9);
|
||||||
|
border-left-color: var(--el-color-warning);
|
||||||
|
}
|
||||||
|
|
||||||
|
&.task-completed {
|
||||||
|
border-left-color: var(--el-color-success);
|
||||||
|
}
|
||||||
|
|
||||||
|
&.task-failed {
|
||||||
|
background: var(--el-color-danger-light-9);
|
||||||
|
border-left-color: var(--el-color-danger);
|
||||||
|
}
|
||||||
|
|
||||||
|
.task-info {
|
||||||
|
display: flex;
|
||||||
|
flex: 1;
|
||||||
|
gap: 10px;
|
||||||
|
align-items: center;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.task-filename {
|
||||||
|
flex: 1;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
font-size: 13px;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
|
||||||
|
.task-progress {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
width: 120px;
|
||||||
|
margin-left: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.task-actions {
|
||||||
|
margin-left: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.task-error {
|
||||||
|
width: 100%;
|
||||||
|
margin-top: 4px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.dialog-footer {
|
.dialog-footer {
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: 10px;
|
gap: 10px;
|
||||||
|
|||||||
Reference in New Issue
Block a user