fix flowchart
Some checks failed
Deploy / deploy (build, debian, 22, /root/OJDeploy/data/clientnext) (push) Has been cancelled
Deploy / deploy (build:staging, school, 8822, /root/OJ/data/dist) (push) Has been cancelled

This commit is contained in:
2026-05-08 19:13:04 -06:00
parent 769e1a52c0
commit d13a5d6465
4 changed files with 35 additions and 62 deletions

View File

@@ -28,7 +28,6 @@
@keydown.enter="handleSaveEdit"
@keydown.escape="handleCancelEdit"
@click.stop
@focusout="handleSaveEdit"
/>
<!-- 隐藏的文字用于保持尺寸 -->
@@ -96,25 +95,19 @@ const displayLabel = computed(
const handleDelete = () => emit("delete", props.id)
const handleMouseDown = (event: MouseEvent) => {
// 检查是否点击在连线点区域
const target = event.target as HTMLElement
if (target.closest(".vue-flow__handle")) {
// 如果在连线点区域,禁用节点拖拽
event.preventDefault()
return false
}
}
const handleDragStart = (event: DragEvent) => {
if (isEditing.value) {
return
}
if (isEditing.value) return
// 检查是否在连线点区域开始拖拽
const target = event.target as HTMLElement
if (target.closest(".vue-flow__handle")) {
event.preventDefault()
return false
return
}
if (event.dataTransfer) {
@@ -137,10 +130,7 @@ const handleDoubleClick = (event: MouseEvent) => {
const handleSaveEdit = () => {
if (isEditing.value) {
// 保存编辑的文本
if (editText.value.trim()) {
emit("update", props.id, editText.value.trim())
}
isEditing.value = false
removeGlobalClickHandler()
}

View File

@@ -31,7 +31,7 @@ withDefaults(defineProps<Props>(), {
})
// Vue Flow 实例
const { addNodes, addEdges, removeNodes, removeEdges } = useVueFlow()
const { addEdges, removeNodes, removeEdges } = useVueFlow()
// 节点和边的响应式数据
const nodes = ref<Node[]>([])
@@ -42,7 +42,12 @@ const { canUndo, canRedo, saveState, undo, redo } = useHistory()
const problemStore = useProblemStore()
const { problem } = storeToRefs(problemStore)
// 缓存管理
// 缓存管理:用 computed key 支持题目 ID 异步加载后自动切换到正确的 storage
const cacheKey = computed(() =>
problem.value?._id
? `flowchart-editor-data-problem-${problem.value!._id}`
: "flowchart-editor-data",
)
const {
isSaving,
lastSaved,
@@ -50,13 +55,7 @@ const {
saveToCache,
loadFromCache,
clearCache,
} = useCache(
nodes,
edges,
problem.value?._id
? `flowchart-editor-data-problem-${problem.value!._id}`
: "flowchart-editor-data",
)
} = useCache(nodes, edges, cacheKey)
// 拖拽处理
const { onDragOver, onDragLeave, onDrop, isDragOver, screenDragPos } = useDnD()
@@ -84,26 +83,16 @@ const {
} = useFlowOperations(
nodes,
edges,
addNodes,
addEdges,
removeNodes,
removeEdges,
saveState,
)
// 拖拽处理包装
const handleDragOver = (event: DragEvent) => {
onDragOver(event)
}
const handleDragLeave = () => {
onDragLeave()
}
const handleDrop = (event: DragEvent) => {
// 处理正常的节点创建拖拽
const handleDrop = async (event: DragEvent) => {
const newNode = onDrop(event)
if (newNode) {
await nextTick()
saveState(nodes.value, edges.value)
}
}
@@ -219,8 +208,8 @@ defineExpose({
<VueFlow
v-model:nodes="nodes"
v-model:edges="edges"
@dragover="handleDragOver"
@dragleave="handleDragLeave"
@dragover="onDragOver"
@dragleave="onDragLeave"
@drop="handleDrop"
@connect="handleConnect"
@edge-click="handleEdgeClick"

View File

@@ -1,4 +1,4 @@
import { ref, watch } from "vue"
import { ref, watch, type Ref, type MaybeRefOrGetter } from "vue"
import { useStorage, useDebounceFn } from "@vueuse/core"
import type { Node, Edge } from "@vue-flow/core"
@@ -6,15 +6,15 @@ import type { Node, Edge } from "@vue-flow/core"
* 缓存管理 - 使用 @vueuse 的 useStorage
*/
export function useCache(
nodes: any,
edges: any,
storageKey: string = "flowchart-editor-data",
nodes: Ref<Node[]>,
edges: Ref<Edge[]>,
storageKey: MaybeRefOrGetter<string> = "flowchart-editor-data",
) {
const isSaving = ref(false)
const lastSaved = ref<Date | null>(null)
const hasUnsavedChanges = ref(false)
// 使用 useStorage 管理数据存储
// 使用 useStorage 管理数据存储,支持响应式 key题目 ID 异步加载时自动切换)
const storedData = useStorage<{
nodes: Node[]
edges: Edge[]
@@ -25,9 +25,8 @@ export function useCache(
timestamp: "",
})
// 防抖保存
// 防抖保存isSaving 在 watch 中置 true保存完成后置 false使 UI 能感知保存中状态
const debouncedSave = useDebounceFn(() => {
isSaving.value = true
storedData.value.nodes = nodes.value
storedData.value.edges = edges.value
storedData.value.timestamp = new Date().toISOString()
@@ -68,11 +67,12 @@ export function useCache(
hasUnsavedChanges.value = false
}
// 监听节点和边的变化
// 监听节点和边的变化isSaving 在此置 true 以覆盖防抖等待窗口
watch(
[nodes, edges],
() => {
hasUnsavedChanges.value = true
isSaving.value = true
debouncedSave()
},
{ deep: true },

View File

@@ -1,16 +1,17 @@
import type { Ref } from "vue"
import type { Node, Edge, Connection } from "@vue-flow/core"
import { useVueFlow } from "@vue-flow/core"
import { getRandomId } from "utils/functions"
export function useFlowOperations(
nodes: Ref<Node[]>,
edges: Ref<Edge[]>,
addNodes: (nodes: Node[]) => void,
addEdges: (edges: Edge[]) => void,
removeNodes: (nodeIds: string[]) => void,
removeEdges: (edgeIds: string[]) => void,
saveState: (nodes: Node[], edges: Edge[]) => void,
) {
const { findNode, getSelectedNodes, getSelectedEdges } = useVueFlow()
const getAutoLabel = (
sourceNode: Node | undefined,
targetNode: Node | undefined,
@@ -95,23 +96,16 @@ export function useFlowOperations(
saveState(nodes.value, edges.value)
}
// 节点更新
// 节点更新,空标签时清除自定义标签(恢复默认类型名称)
const handleNodeUpdate = (nodeId: string, newLabel: string) => {
const nodeIndex = nodes.value.findIndex((node) => node.id === nodeId)
if (nodeIndex !== -1) {
const oldNode = nodes.value[nodeIndex]
// 创建新的节点对象以确保响应式更新
const updatedNode = {
...oldNode,
data: {
...oldNode.data,
customLabel: newLabel,
},
const node = findNode(nodeId)
if (node) {
if (newLabel) {
node.data = { ...node.data, customLabel: newLabel }
} else {
const { customLabel: _, ...rest } = node.data
node.data = rest
}
nodes.value[nodeIndex] = updatedNode
saveState(nodes.value, edges.value)
}
}
@@ -125,8 +119,8 @@ export function useFlowOperations(
// 删除选中的节点和边
const deleteSelected = () => {
const selectedNodes = nodes.value.filter((node) => (node as any).selected)
const selectedEdges = edges.value.filter((edge) => (edge as any).selected)
const selectedNodes = getSelectedNodes.value
const selectedEdges = getSelectedEdges.value
if (selectedNodes.length > 0) {
removeNodes(selectedNodes.map((node) => node.id))