From 854b1f0769b824bb1cff1eafa014def1b6984955 Mon Sep 17 00:00:00 2001 From: yuetsh <517252939@qq.com> Date: Mon, 13 Oct 2025 14:58:21 +0800 Subject: [PATCH] update --- .../components/FlowchartEvaluationDisplay.vue | 179 +++++++++ src/oj/problem/components/SubmitFlowchart.vue | 356 ++---------------- .../composables/useFlowchartSubmission.ts | 106 ++++++ .../problem/composables/useFlowchartSubmit.ts | 106 ++++++ .../composables/useMermaidConverter.ts | 108 ++++++ .../FlowchartEditor/NodeHandles.vue | 5 + .../components/FlowchartEditor/index.vue | 25 +- 7 files changed, 555 insertions(+), 330 deletions(-) create mode 100644 src/oj/problem/components/FlowchartEvaluationDisplay.vue create mode 100644 src/oj/problem/composables/useFlowchartSubmission.ts create mode 100644 src/oj/problem/composables/useFlowchartSubmit.ts create mode 100644 src/oj/problem/composables/useMermaidConverter.ts diff --git a/src/oj/problem/components/FlowchartEvaluationDisplay.vue b/src/oj/problem/components/FlowchartEvaluationDisplay.vue new file mode 100644 index 0000000..0f70c61 --- /dev/null +++ b/src/oj/problem/components/FlowchartEvaluationDisplay.vue @@ -0,0 +1,179 @@ + + + + + + + {{ evaluationResult.score }}分 {{ evaluationResult.grade }}级 + + + + + + + + + + + + + + 加载到流程图编辑器 + + + + + + {{ evaluationResult.feedback }} + + + + + {{ evaluationResult.suggestions }} + + + + + + + {{ key }} + + {{ detail.score || 0 }}分 / {{ detail.max }}分 + + + + {{ detail.comment }} + + + + + + + + + + diff --git a/src/oj/problem/components/SubmitFlowchart.vue b/src/oj/problem/components/SubmitFlowchart.vue index 106a205..4d6b7dc 100644 --- a/src/oj/problem/components/SubmitFlowchart.vue +++ b/src/oj/problem/components/SubmitFlowchart.vue @@ -1,96 +1,24 @@ @@ -301,74 +55,18 @@ const formatTime = (timeString: string) => { > {{ loading ? "评分中..." : "提交流程图" }} + {{ submissionCount }} 次 - - - {{ evaluationResult.score }}分 {{ evaluationResult.grade }}级 - - - - - - - {{ evaluationResult.feedback }} - - - - - {{ evaluationResult.suggestions }} - - - - - - - {{ key }} - - {{ detail.score || 0 }}分 / {{ detail.max }}分 - - - - {{ detail.comment }} - - - - + + {}" + @load-to-editor="handleLoadToEditor" + /> diff --git a/src/oj/problem/composables/useFlowchartSubmission.ts b/src/oj/problem/composables/useFlowchartSubmission.ts new file mode 100644 index 0000000..40437cf --- /dev/null +++ b/src/oj/problem/composables/useFlowchartSubmission.ts @@ -0,0 +1,106 @@ +import { ref, watch } from 'vue' +import { useMessage } from 'naive-ui' +import { + useFlowchartWebSocket, + type FlowchartEvaluationUpdate, +} from 'shared/composables/websocket' + +export interface EvaluationResult { + score?: number + grade?: string + feedback?: string + suggestions?: string + criteriaDetails?: any +} + +export interface SubmissionStatus { + status: string + submission_id: string + created_time?: string +} + +export function useFlowchartSubmission() { + const message = useMessage() + + const evaluationResult = ref(null) + const submissionStatus = ref(null) + const loading = ref(false) + + // 处理 WebSocket 消息 + const handleWebSocketMessage = (data: FlowchartEvaluationUpdate) => { + console.log("收到流程图评分更新:", data) + + if (data.type === "flowchart_evaluation_completed") { + loading.value = false + evaluationResult.value = { + score: data.score, + grade: data.grade, + feedback: data.feedback, + } + submissionStatus.value = null // 清除状态 + message.success(`流程图评分完成!得分: ${data.score}分 (${data.grade}级)`) + } else if (data.type === "flowchart_evaluation_failed") { + console.log("处理评分失败消息") + loading.value = false + submissionStatus.value = null // 清除状态 + message.error(`流程图评分失败: ${data.error}`) + } else { + console.log("未知的消息类型:", data.type) + } + } + + // 创建 WebSocket 连接 + const { connect, disconnect, subscribe, status } = useFlowchartWebSocket( + handleWebSocketMessage, + ) + + // 监听WebSocket状态变化 + watch( + status, + (newStatus) => { + console.log("WebSocket状态变化:", newStatus) + }, + { immediate: true }, + ) + + // 订阅提交更新 + const subscribeToSubmission = (submissionId: string) => { + console.log("开始订阅WebSocket更新") + subscribe(submissionId) + + // 设置评分状态显示 + submissionStatus.value = { + status: "processing", + submission_id: submissionId, + created_time: new Date().toISOString(), + } + } + + // 清除结果 + const clearResult = () => { + evaluationResult.value = null + submissionStatus.value = null + } + + // 设置加载状态 + const setLoading = (value: boolean) => { + loading.value = value + } + + // 设置评估结果 + const setEvaluationResult = (result: EvaluationResult) => { + evaluationResult.value = result + } + + return { + evaluationResult, + submissionStatus, + loading, + connect, + disconnect, + subscribeToSubmission, + clearResult, + setLoading, + setEvaluationResult, + } +} diff --git a/src/oj/problem/composables/useFlowchartSubmit.ts b/src/oj/problem/composables/useFlowchartSubmit.ts new file mode 100644 index 0000000..a30f1aa --- /dev/null +++ b/src/oj/problem/composables/useFlowchartSubmit.ts @@ -0,0 +1,106 @@ +import { ref } from 'vue' +import { useMessage } from 'naive-ui' +import { submitFlowchart, getCurrentProblemFlowchartSubmission } from 'oj/api' +import { useProblemStore } from 'oj/store/problem' +import { atou, utoa } from 'utils/functions' +import { useMermaidConverter } from './useMermaidConverter' +import { useFlowchartSubmission } from './useFlowchartSubmission' + +export function useFlowchartSubmit() { + const message = useMessage() + const problemStore = useProblemStore() + const { problem } = toRefs(problemStore) + + const { convertToMermaid } = useMermaidConverter() + const { + evaluationResult, + submissionStatus, + loading, + connect, + disconnect, + subscribeToSubmission, + clearResult, + setLoading, + setEvaluationResult, + } = useFlowchartSubmission() + + // 提交次数 + const submissionCount = ref(0) + + // 存储流程图数据 + const myFlowchartZippedStr = ref("") + + // 检查当前问题的流程图提交状态 + const checkCurrentSubmissionStatus = async () => { + if (!problem.value?.id) return + + const { data } = await getCurrentProblemFlowchartSubmission(problem.value.id) + const submission = data.submission + submissionCount.value = data.count + if (submission && submission.status === 2) { + myFlowchartZippedStr.value = data.submission.flowchart_data.data + setEvaluationResult({ + score: submission.ai_score, + grade: submission.ai_grade, + feedback: submission.ai_feedback, + suggestions: submission.ai_suggestions, + criteriaDetails: submission.ai_criteria_details, + }) + } + } + + // 提交流程图 + const submitFlowchartData = async (flowchartEditorRef: any) => { + if (!flowchartEditorRef?.value) return + + // 获取流程图的JSON数据 + const flowchartData = flowchartEditorRef.value.getFlowchartData() + + if (flowchartData.nodes.length === 0 || flowchartData.edges.length === 0) { + message.error("流程图节点或边不能为空") + return + } + + const mermaidCode = convertToMermaid(flowchartData) + const compressed = utoa(JSON.stringify(flowchartData)) + + setLoading(true) + clearResult() // 清除之前的结果 + + try { + const response = await submitFlowchart({ + problem_id: problem.value!.id, + mermaid_code: mermaidCode, + flowchart_data: { + compressed: true, + data: compressed, + }, + }) + + // 获取提交ID并订阅更新 + const submissionId = response.data.submission_id + + if (submissionId) { + subscribeToSubmission(submissionId) + } + + message.success("流程图已提交,请耐心等待评分") + } catch (error) { + setLoading(false) + message.error("流程图提交失败") + console.error("提交流程图失败:", error) + } + } + + return { + evaluationResult, + submissionStatus, + loading, + submissionCount, + myFlowchartZippedStr, + connect, + disconnect, + checkCurrentSubmissionStatus, + submitFlowchartData, + } +} diff --git a/src/oj/problem/composables/useMermaidConverter.ts b/src/oj/problem/composables/useMermaidConverter.ts new file mode 100644 index 0000000..d51a7d1 --- /dev/null +++ b/src/oj/problem/composables/useMermaidConverter.ts @@ -0,0 +1,108 @@ +/** + * 将流程图JSON数据转换为Mermaid格式 + */ +export function useMermaidConverter() { + const convertToMermaid = (flowchartData: any) => { + const { nodes, edges } = flowchartData + + if (!nodes || nodes.length === 0) { + return "graph TD\n A[空流程图]" + } + + let mermaid = "graph TD\n" + + // 处理节点 - 根据原始类型和自定义标签 + nodes.forEach((node: any) => { + const nodeId = node.id + const label = node.data?.customLabel || node.data?.label || "节点" + const originalType = node.data?.originalType || node.type + + // 根据节点原始类型确定Mermaid语法 + switch (originalType) { + case "start": + mermaid += ` ${nodeId}((${label}))\n` + break + case "end": + mermaid += ` ${nodeId}((${label}))\n` + break + case "input": + // 输入框使用平行四边形 + mermaid += ` ${nodeId}[/${label}/]\n` + break + case "output": + // 输出框使用平行四边形 + mermaid += ` ${nodeId}[/${label}/]\n` + break + case "default": + mermaid += ` ${nodeId}[${label}]\n` + break + case "decision": + mermaid += ` ${nodeId}{${label}}\n` + break + case "loop": + // 循环使用菱形 + mermaid += ` ${nodeId}{${label}}\n` + break + default: + mermaid += ` ${nodeId}[${label}]\n` + } + }) + + // 处理边 + edges.forEach((edge: any) => { + const source = edge.source + const target = edge.target + const label = edge.label ?? "" + + if (label && label.trim() !== "") { + mermaid += ` ${source} -->|${label}| ${target}\n` + } else { + mermaid += ` ${source} --> ${target}\n` + } + }) + + // 添加样式定义来区分不同类型的节点 + mermaid += "\n" + mermaid += + " classDef startEnd fill:#e1f5fe,stroke:#01579b,stroke-width:2px\n" + mermaid += " classDef input fill:#e3f2fd,stroke:#1976d2,stroke-width:2px\n" + mermaid += + " classDef output fill:#f3e5f5,stroke:#7b1fa2,stroke-width:2px\n" + mermaid += + " classDef process fill:#e8f5e8,stroke:#1b5e20,stroke-width:2px\n" + mermaid += + " classDef decision fill:#fff3e0,stroke:#e65100,stroke-width:2px\n" + mermaid += "\n" + + // 为节点应用样式 + nodes.forEach((node: any) => { + const nodeId = node.id + const originalType = node.data?.originalType || node.type + + switch (originalType) { + case "start": + case "end": + mermaid += ` class ${nodeId} startEnd\n` + break + case "input": + mermaid += ` class ${nodeId} input\n` + break + case "output": + mermaid += ` class ${nodeId} output\n` + break + case "decision": + case "loop": + mermaid += ` class ${nodeId} decision\n` + break + default: + mermaid += ` class ${nodeId} process\n` + } + }) + + return mermaid + } + + return { + convertToMermaid, + } +} diff --git a/src/shared/components/FlowchartEditor/NodeHandles.vue b/src/shared/components/FlowchartEditor/NodeHandles.vue index d1dc33c..5164e82 100644 --- a/src/shared/components/FlowchartEditor/NodeHandles.vue +++ b/src/shared/components/FlowchartEditor/NodeHandles.vue @@ -3,6 +3,7 @@ @@ -12,6 +13,7 @@ @@ -21,6 +23,7 @@ @@ -122,11 +125,13 @@ diff --git a/src/shared/components/FlowchartEditor/index.vue b/src/shared/components/FlowchartEditor/index.vue index 164ae84..2908b7b 100644 --- a/src/shared/components/FlowchartEditor/index.vue +++ b/src/shared/components/FlowchartEditor/index.vue @@ -122,6 +122,28 @@ onUnmounted(() => { document.removeEventListener('keydown', handleKeyDown) }) +// 加载外部数据到编辑器 +const setFlowchartData = (data: { nodes: Node[], edges: Edge[] }) => { + if (data && data.nodes && data.edges) { + // 确保节点数据包含必要的位置信息 + const processedNodes = data.nodes.map(node => ({ + ...node, + position: node.position || { x: 0, y: 0 } + })) + + // 确保边数据包含必要的 handle 信息 + const processedEdges = data.edges.map(edge => ({ + ...edge, + sourceHandle: edge.sourceHandle || null, + targetHandle: edge.targetHandle || null + })) + + nodes.value = processedNodes + edges.value = processedEdges + saveState(nodes.value, edges.value) + } +} + // 暴露节点和边数据给父组件 defineExpose({ nodes, @@ -129,7 +151,8 @@ defineExpose({ getFlowchartData: () => ({ nodes: nodes.value, edges: edges.value - }) + }), + setFlowchartData })