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 @@ + + + + + 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 @@ 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 @@