From 6f1720acd5b00b7a14f9c4f1e8be4572a367533b Mon Sep 17 00:00:00 2001 From: yuetsh <517252939@qq.com> Date: Mon, 13 Oct 2025 15:18:14 +0800 Subject: [PATCH] update --- src/oj/flowchart/index.vue | 7 ++ src/oj/problem/components/Form.vue | 24 +++-- src/oj/problem/components/ProblemEditor.vue | 7 +- src/oj/problem/components/SubmitFlowchart.vue | 2 +- .../composables/useFlowchartSubmission.ts | 10 +- .../problem/composables/useFlowchartSubmit.ts | 26 +++--- .../composables/useMermaidConverter.ts | 3 +- src/routes.ts | 4 + .../components/FlowchartEditor/CustomNode.vue | 66 ++++++------- .../FlowchartEditor/NodeActions.vue | 8 +- .../FlowchartEditor/NodeHandles.vue | 8 +- .../components/FlowchartEditor/Toolbar.vue | 93 ++++++++++--------- .../components/FlowchartEditor/index.vue | 89 +++++++++++------- .../components/FlowchartEditor/useCache.ts | 30 +++--- .../components/FlowchartEditor/useDnD.ts | 30 +++--- .../components/FlowchartEditor/useHistory.ts | 14 +-- src/shared/composables/websocket.ts | 11 ++- 17 files changed, 248 insertions(+), 184 deletions(-) create mode 100644 src/oj/flowchart/index.vue diff --git a/src/oj/flowchart/index.vue b/src/oj/flowchart/index.vue new file mode 100644 index 0000000..cd8329d --- /dev/null +++ b/src/oj/flowchart/index.vue @@ -0,0 +1,7 @@ + + + + + diff --git a/src/oj/problem/components/Form.vue b/src/oj/problem/components/Form.vue index 4c135f2..f654977 100644 --- a/src/oj/problem/components/Form.vue +++ b/src/oj/problem/components/Form.vue @@ -72,19 +72,17 @@ const menu = computed(() => [ { label: "重置代码", key: "reset" }, ]) -const languageOptions: DropdownOption[] = languages.value.map( - (it) => ({ - label: () => - h(NFlex, { align: "center" }, () => [ - h(Icon, { - icon: ICON_SET[it], - width: 16, - }), - LANGUAGE_SHOW_VALUE[it], - ]), - value: it, - }), -) +const languageOptions: DropdownOption[] = languages.value.map((it) => ({ + label: () => + h(NFlex, { align: "center" }, () => [ + h(Icon, { + icon: ICON_SET[it], + width: 16, + }), + LANGUAGE_SHOW_VALUE[it], + ]), + value: it, +})) const copy = async () => { const success = await copyToClipboard(codeStore.code.value) diff --git a/src/oj/problem/components/ProblemEditor.vue b/src/oj/problem/components/ProblemEditor.vue index 58ccc47..e69c03d 100644 --- a/src/oj/problem/components/ProblemEditor.vue +++ b/src/oj/problem/components/ProblemEditor.vue @@ -81,7 +81,7 @@ const handleSyncStatusChange = (status: { } // 提供FlowchartEditor的ref给子组件 -provide('flowchartEditorRef', flowchartEditorRef) +provide("flowchartEditorRef", flowchartEditorRef) @@ -93,7 +93,10 @@ provide('flowchartEditorRef', flowchartEditorRef) @change-language="changeLanguage" @toggle-sync="toggleSync" /> - + {{ loading ? "评分中..." : "提交流程图" }} - + {{ submissionCount }} 次 diff --git a/src/oj/problem/composables/useFlowchartSubmission.ts b/src/oj/problem/composables/useFlowchartSubmission.ts index 40437cf..92e374b 100644 --- a/src/oj/problem/composables/useFlowchartSubmission.ts +++ b/src/oj/problem/composables/useFlowchartSubmission.ts @@ -1,9 +1,9 @@ -import { ref, watch } from 'vue' -import { useMessage } from 'naive-ui' +import { ref, watch } from "vue" +import { useMessage } from "naive-ui" import { useFlowchartWebSocket, type FlowchartEvaluationUpdate, -} from 'shared/composables/websocket' +} from "shared/composables/websocket" export interface EvaluationResult { score?: number @@ -21,7 +21,7 @@ export interface SubmissionStatus { export function useFlowchartSubmission() { const message = useMessage() - + const evaluationResult = ref(null) const submissionStatus = ref(null) const loading = ref(false) @@ -67,7 +67,7 @@ export function useFlowchartSubmission() { const subscribeToSubmission = (submissionId: string) => { console.log("开始订阅WebSocket更新") subscribe(submissionId) - + // 设置评分状态显示 submissionStatus.value = { status: "processing", diff --git a/src/oj/problem/composables/useFlowchartSubmit.ts b/src/oj/problem/composables/useFlowchartSubmit.ts index a30f1aa..60ec958 100644 --- a/src/oj/problem/composables/useFlowchartSubmit.ts +++ b/src/oj/problem/composables/useFlowchartSubmit.ts @@ -1,16 +1,16 @@ -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' +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, @@ -26,7 +26,7 @@ export function useFlowchartSubmit() { // 提交次数 const submissionCount = ref(0) - + // 存储流程图数据 const myFlowchartZippedStr = ref("") @@ -34,7 +34,9 @@ export function useFlowchartSubmit() { const checkCurrentSubmissionStatus = async () => { if (!problem.value?.id) return - const { data } = await getCurrentProblemFlowchartSubmission(problem.value.id) + const { data } = await getCurrentProblemFlowchartSubmission( + problem.value.id, + ) const submission = data.submission submissionCount.value = data.count if (submission && submission.status === 2) { @@ -52,7 +54,7 @@ export function useFlowchartSubmit() { // 提交流程图 const submitFlowchartData = async (flowchartEditorRef: any) => { if (!flowchartEditorRef?.value) return - + // 获取流程图的JSON数据 const flowchartData = flowchartEditorRef.value.getFlowchartData() @@ -60,7 +62,7 @@ export function useFlowchartSubmit() { message.error("流程图节点或边不能为空") return } - + const mermaidCode = convertToMermaid(flowchartData) const compressed = utoa(JSON.stringify(flowchartData)) diff --git a/src/oj/problem/composables/useMermaidConverter.ts b/src/oj/problem/composables/useMermaidConverter.ts index d51a7d1..4333add 100644 --- a/src/oj/problem/composables/useMermaidConverter.ts +++ b/src/oj/problem/composables/useMermaidConverter.ts @@ -65,7 +65,8 @@ export function useMermaidConverter() { 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 input fill:#e3f2fd,stroke:#1976d2,stroke-width:2px\n" mermaid += " classDef output fill:#f3e5f5,stroke:#7b1fa2,stroke-width:2px\n" mermaid += diff --git a/src/routes.ts b/src/routes.ts index 89fb3a9..d235961 100644 --- a/src/routes.ts +++ b/src/routes.ts @@ -96,6 +96,10 @@ export const ojs: RouteRecordRaw = { component: () => import("oj/ai/analysis.vue"), meta: { requiresAuth: true }, }, + { + path: "flowchart", + component: () => import("oj/flowchart/index.vue"), + }, ], } diff --git a/src/shared/components/FlowchartEditor/CustomNode.vue b/src/shared/components/FlowchartEditor/CustomNode.vue index 57efc5b..d6a0259 100644 --- a/src/shared/components/FlowchartEditor/CustomNode.vue +++ b/src/shared/components/FlowchartEditor/CustomNode.vue @@ -1,5 +1,5 @@ - - - + + {{ displayLabel }} - + - - + - {{ displayLabel }} + {{ + displayLabel + }} - + - @@ -67,11 +61,15 @@ const getSaveStatusTitle = () => { 节点库 - + ⏳ ● ✔ @@ -80,7 +78,6 @@ const getSaveStatusTitle = () => { 拖拽节点到画布中 - { - { ↶ 撤销 - { 重做 - + 🗑️ 清空画布 - @@ -150,7 +150,6 @@ const getSaveStatusTitle = () => { transition: all 0.3s ease; } - .toolbar-header { margin-bottom: 16px; border-bottom: 1px solid #e5e7eb; @@ -207,23 +206,30 @@ const getSaveStatusTitle = () => { } @keyframes spin { - from { transform: rotate(0deg); } - to { transform: rotate(360deg); } + from { + transform: rotate(0deg); + } + to { + transform: rotate(360deg); + } } @keyframes pulse { - 0%, 100% { opacity: 1; } - 50% { opacity: 0.5; } + 0%, + 100% { + opacity: 1; + } + 50% { + opacity: 0.5; + } } - .description { margin: 0; font-size: 12px; color: #6b7280; } - /* 节点列表样式 */ .nodes { display: flex; @@ -285,7 +291,6 @@ const getSaveStatusTitle = () => { line-height: 1.3; } - /* 工具栏操作按钮样式 */ .toolbar-actions { display: flex; @@ -366,7 +371,6 @@ const getSaveStatusTitle = () => { font-size: 12px; } - /* 滚动条样式 */ .toolbar::-webkit-scrollbar { width: 6px; @@ -386,7 +390,6 @@ const getSaveStatusTitle = () => { background: #94a3b8; } - /* 响应式设计 */ @media (max-width: 768px) { .toolbar { diff --git a/src/shared/components/FlowchartEditor/index.vue b/src/shared/components/FlowchartEditor/index.vue index 2908b7b..977aeed 100644 --- a/src/shared/components/FlowchartEditor/index.vue +++ b/src/shared/components/FlowchartEditor/index.vue @@ -1,10 +1,16 @@ @@ -166,13 +186,14 @@ defineExpose({ @drop="handleDrop" @connect="handleConnect" @edge-click="handleEdgeClick" + :readonly="readonly" :default-edge-options="{ type: 'step', - style: { - stroke: '#6366f1', - strokeWidth: 2.5, + style: { + stroke: '#6366f1', + strokeWidth: 2.5, cursor: 'pointer', - filter: 'drop-shadow(0 2px 4px rgba(0,0,0,0.1))' + filter: 'drop-shadow(0 2px 4px rgba(0,0,0,0.1))', }, markerEnd: { type: MarkerType.ArrowClosed, @@ -204,7 +225,12 @@ defineExpose({ orient="auto" markerUnits="strokeWidth" > - + @@ -220,6 +246,8 @@ defineExpose({ - diff --git a/src/shared/components/FlowchartEditor/useCache.ts b/src/shared/components/FlowchartEditor/useCache.ts index 4a2c4e6..c711def 100644 --- a/src/shared/components/FlowchartEditor/useCache.ts +++ b/src/shared/components/FlowchartEditor/useCache.ts @@ -1,6 +1,6 @@ -import { ref, watch } from 'vue' -import { useStorage, useDebounceFn } from '@vueuse/core' -import type { Node, Edge } from '@vue-flow/core' +import { ref, watch } from "vue" +import { useStorage, useDebounceFn } from "@vueuse/core" +import type { Node, Edge } from "@vue-flow/core" /** * 缓存管理 - 使用 @vueuse 的 useStorage @@ -8,7 +8,7 @@ import type { Node, Edge } from '@vue-flow/core' export function useCache( nodes: any, edges: any, - storageKey: string = 'flowchart-editor-data' + storageKey: string = "flowchart-editor-data", ) { const isSaving = ref(false) const lastSaved = ref(null) @@ -22,7 +22,7 @@ export function useCache( }>(storageKey, { nodes: [], edges: [], - timestamp: '' + timestamp: "", }) // 防抖保存 @@ -52,7 +52,9 @@ export function useCache( if (storedData.value.nodes?.length || storedData.value.edges?.length) { nodes.value = storedData.value.nodes edges.value = storedData.value.edges - lastSaved.value = storedData.value.timestamp ? new Date(storedData.value.timestamp) : null + lastSaved.value = storedData.value.timestamp + ? new Date(storedData.value.timestamp) + : null hasUnsavedChanges.value = false return true } @@ -61,16 +63,20 @@ export function useCache( // 清除缓存数据 const clearCache = () => { - storedData.value = { nodes: [], edges: [], timestamp: '' } + storedData.value = { nodes: [], edges: [], timestamp: "" } lastSaved.value = null hasUnsavedChanges.value = false } // 监听节点和边的变化 - watch([nodes, edges], () => { - hasUnsavedChanges.value = true - debouncedSave() - }, { deep: true }) + watch( + [nodes, edges], + () => { + hasUnsavedChanges.value = true + debouncedSave() + }, + { deep: true }, + ) return { isSaving, @@ -78,6 +84,6 @@ export function useCache( hasUnsavedChanges, saveToCache, loadFromCache, - clearCache + clearCache, } } diff --git a/src/shared/components/FlowchartEditor/useDnD.ts b/src/shared/components/FlowchartEditor/useDnD.ts index cb650af..7fda135 100644 --- a/src/shared/components/FlowchartEditor/useDnD.ts +++ b/src/shared/components/FlowchartEditor/useDnD.ts @@ -1,7 +1,11 @@ -import { ref } from 'vue' +import { ref } from "vue" import { useVueFlow } from "@vue-flow/core" import { nanoid } from "nanoid" -import { getNodeTypeConfig, createNodeStyle, getNodeDimensions } from "./useNodeStyles" +import { + getNodeTypeConfig, + createNodeStyle, + getNodeDimensions, +} from "./useNodeStyles" /** * 简化的拖拽处理 @@ -25,39 +29,39 @@ export function useDnD() { const onDrop = (event: DragEvent) => { event.preventDefault() isDragOver.value = false - + const type = event.dataTransfer?.getData("application/vueflow") if (!type) return - + // 获取鼠标在画布中的坐标 const position = screenToFlowCoordinate({ x: event.clientX, y: event.clientY, }) - + // 根据节点类型获取实际尺寸 const dimensions = getNodeDimensions(type) - + // 调整位置,使节点中心点对齐到鼠标位置 const adjustedPosition = { x: position.x - dimensions.width / 2, - y: position.y - dimensions.height / 2 + y: position.y - dimensions.height / 2, } - + const nodeId = `node-${nanoid()}` const config = getNodeTypeConfig(type) const newNode = { id: nodeId, - type: 'custom', + type: "custom", position: adjustedPosition, data: { label: config.label, color: config.color, - originalType: type + originalType: type, }, - style: createNodeStyle(type) + style: createNodeStyle(type), } - + addNodes([newNode]) return newNode } @@ -66,6 +70,6 @@ export function useDnD() { isDragOver, onDragOver, onDragLeave, - onDrop + onDrop, } } diff --git a/src/shared/components/FlowchartEditor/useHistory.ts b/src/shared/components/FlowchartEditor/useHistory.ts index bb52a02..c166234 100644 --- a/src/shared/components/FlowchartEditor/useHistory.ts +++ b/src/shared/components/FlowchartEditor/useHistory.ts @@ -1,11 +1,11 @@ -import { ref, computed } from 'vue' -import type { Node, Edge } from '@vue-flow/core' +import { ref, computed } from "vue" +import type { Node, Edge } from "@vue-flow/core" /** * 简化的历史记录管理 */ export function useHistory() { - const history = ref<{ nodes: Node[], edges: Edge[] }[]>([]) + const history = ref<{ nodes: Node[]; edges: Edge[] }[]>([]) const historyIndex = ref(-1) // 是否可以撤销 @@ -17,15 +17,15 @@ export function useHistory() { // 保存状态到历史记录 const saveState = (nodes: Node[], edges: Edge[]) => { const currentState = { nodes: [...nodes], edges: [...edges] } - + // 如果当前不在历史记录的末尾,删除后面的记录 if (historyIndex.value < history.value.length - 1) { history.value = history.value.slice(0, historyIndex.value + 1) } - + history.value.push(currentState) historyIndex.value = history.value.length - 1 - + // 限制历史记录数量 if (history.value.length > 20) { history.value.shift() @@ -58,6 +58,6 @@ export function useHistory() { canRedo, saveState, undo, - redo + redo, } } diff --git a/src/shared/composables/websocket.ts b/src/shared/composables/websocket.ts index ae7444b..e995fa1 100644 --- a/src/shared/composables/websocket.ts +++ b/src/shared/composables/websocket.ts @@ -414,7 +414,10 @@ export function createWebSocketComposable( * 流程图评分更新消息类型 */ export interface FlowchartEvaluationUpdate extends WebSocketMessage { - type: "flowchart_evaluation_completed" | "flowchart_evaluation_failed" | "flowchart_evaluation_update" + type: + | "flowchart_evaluation_completed" + | "flowchart_evaluation_failed" + | "flowchart_evaluation_update" submission_id: string score?: number grade?: string @@ -474,8 +477,10 @@ export function useFlowchartWebSocket( scheduleDisconnect: (delay?: number) => ws.scheduleDisconnect(delay), cancelScheduledDisconnect: () => ws.cancelScheduledDisconnect(), status: ws.status, - addHandler: (h: MessageHandler) => ws.addHandler(h), - removeHandler: (h: MessageHandler) => ws.removeHandler(h), + addHandler: (h: MessageHandler) => + ws.addHandler(h), + removeHandler: (h: MessageHandler) => + ws.removeHandler(h), } }
拖拽节点到画布中