update
Deploy / deploy (push) Canceled after 0s

This commit is contained in:
xuyue committed 2026-09-27 19:36:28 +08:00
1 parent 23844ae301
commit d25b5dbe6d
268 files changed
+3684 -8861

No files matched your search

@@ -87,9 +87,7 @@ let globalClickHandler: ((event: MouseEvent) => void) | null = null
// 计算属性
const nodeType = computed(() => props.data.originalType || props.type)
const nodeConfig = computed(() => getNodeTypeConfig(nodeType.value))
const displayLabel = computed(
() => props.data.customLabel || nodeConfig.value.label,
)
const displayLabel = computed(() => props.data.customLabel || nodeConfig.value.label)
// 事件处理器
const handleDelete = () => emit("delete", props.id)
@@ -164,10 +162,7 @@ const addGlobalClickHandler = () => {
if (globalClickHandler) return
globalClickHandler = (event: MouseEvent) => {
if (
isEditing.value &&
!(event.target as Element)?.closest(".custom-node")
) {
if (isEditing.value && !(event.target as Element)?.closest(".custom-node")) {
handleSaveEdit()
}
}
@@ -1,14 +1,6 @@
<template>
<div
class="node-actions"
@mouseenter="$emit('mouseenter')"
@mouseleave="$emit('mouseleave')"
>
<button
class="action-btn delete-btn"
@click.stop="$emit('delete')"
title="删除节点"
>
<div class="node-actions" @mouseenter="$emit('mouseenter')" @mouseleave="$emit('mouseleave')">
<button class="action-btn delete-btn" @click.stop="$emit('delete')" title="删除节点">
<svg width="20" height="20" viewBox="0 0 24 24" fill="currentColor">
<path
d="M19 6.41L17.59 5 12 10.59 6.41 5 5 6.41 10.59 12 5 17.59 6.41 19 12 13.41 17.59 19 19 17.59 13.41 12z"
@@ -14,8 +14,7 @@ const onDragStart = (event: DragEvent, type: string) => {
// 隐藏浏览器默认拖影,改用 canvas 跟随预览
const emptyImg = new Image(1, 1)
emptyImg.src =
"data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7"
emptyImg.src = "data:image/gif;base64,R0lGODlhAQABAIAAAAAAAP///yH5BAEAAAAALAAAAAABAAEAAAIBRAA7"
event.dataTransfer.setDragImage(emptyImg, 0, 0)
}
@@ -44,15 +43,13 @@ const emit = defineEmits<{
// 节点类型定义 - 优化性能
const nodeTypes = computed(() =>
["start", "input", "default", "decision", "loop", "output", "end"].map(
(type) => {
const config = getNodeTypeConfig(type)
return {
type,
...config,
}
},
),
["start", "input", "default", "decision", "loop", "output", "end"].map((type) => {
const config = getNodeTypeConfig(type)
return {
type,
...config,
}
}),
)
const saveStatusTitle = computed(() => {
@@ -135,11 +132,7 @@ const saveStatusTitle = computed(() => {
<span class="btn-text">重做</span>
</button>
</div>
<button
class="action-btn clear-btn"
@click="$emit('clear')"
title="清空画布"
>
<button class="action-btn clear-btn" @click="$emit('clear')" title="清空画布">
<span class="btn-icon">🗑️</span>
<span class="btn-text">清空画布</span>
</button>
@@ -4,13 +4,7 @@ import Toolbar from "./Toolbar.vue"
import "@vue-flow/core/dist/style.css"
import "@vue-flow/core/dist/theme-default.css"
import "@vue-flow/controls/dist/style.css"
import {
useVueFlow,
VueFlow,
type Node,
type Edge,
MarkerType,
} from "@vue-flow/core"
import { useVueFlow, VueFlow, type Node, type Edge, MarkerType } from "@vue-flow/core"
import { Controls } from "@vue-flow/controls"
import { Background } from "@vue-flow/background"
@@ -49,18 +43,16 @@ const cacheKey = computed(() =>
? `flowchart-editor-data-problem-${problem.value!._id}`
: "flowchart-editor-data",
)
const {
isSaving,
lastSaved,
hasUnsavedChanges,
saveToCache,
loadFromCache,
clearCache,
} = useCache(nodes, edges, cacheKey, () => {
// 换题后画布已经被换成新题的草稿,历史必须跟着重建,
// 否则一次撤销就会把上一题的图还原到这一题里
resetHistory(nodes.value, edges.value)
})
const { isSaving, lastSaved, hasUnsavedChanges, saveToCache, loadFromCache, clearCache } = useCache(
nodes,
edges,
cacheKey,
() => {
// 换题后画布已经被换成新题的草稿,历史必须跟着重建,
// 否则一次撤销就会把上一题的图还原到这一题里
resetHistory(nodes.value, edges.value)
},
)
// 拖拽处理
const { onDragOver, onDragLeave, onDrop, isDragOver, screenDragPos } = useDnD()
@@ -85,14 +77,7 @@ const {
handleNodeUpdate,
clearCanvas,
deleteSelected,
} = useFlowOperations(
nodes,
edges,
addEdges,
removeNodes,
removeEdges,
saveState,
)
} = useFlowOperations(nodes, edges, addEdges, removeNodes, removeEdges, saveState)
const handleDrop = async (event: DragEvent) => {
const newNode = onDrop(event)
@@ -140,10 +125,7 @@ const handleClear = () => {
// 键盘事件
const handleKeyDown = (event: KeyboardEvent) => {
if (
event.target instanceof HTMLInputElement ||
event.target instanceof HTMLTextAreaElement
)
if (event.target instanceof HTMLInputElement || event.target instanceof HTMLTextAreaElement)
return
if (event.key === "Delete" || event.key === "Backspace") {
@@ -220,9 +202,7 @@ defineExpose({
class="drag-node-preview"
:style="dragPreviewStyle"
>
<span class="preview-icon">{{
getNodeTypeConfig(currentDragNodeType).icon
}}</span>
<span class="preview-icon">{{ getNodeTypeConfig(currentDragNodeType).icon }}</span>
<span>{{ getNodeTypeConfig(currentDragNodeType).label }}</span>
</div>
</Transition>
@@ -272,12 +252,7 @@ defineExpose({
orient="auto"
markerUnits="strokeWidth"
>
<path
d="M0,0 L0,6 L10,3 z"
fill="#6366f1"
stroke="#6366f1"
strokeWidth="0.5"
/>
<path d="M0,0 L0,6 L10,3 z" fill="#6366f1" stroke="#6366f1" strokeWidth="0.5" />
</marker>
</defs>
<template #node-custom="{ data, id, type }">
@@ -53,9 +53,7 @@ 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
}
@@ -1,10 +1,6 @@
import { ref } from "vue"
import { useVueFlow } from "@vue-flow/core"
import {
getNodeTypeConfig,
createNodeStyle,
getNodeDimensions,
} from "./useNodeStyles"
import { getNodeTypeConfig, createNodeStyle, getNodeDimensions } from "./useNodeStyles"
import { getRandomId } from "utils/functions"
// 模块级共享:当前拖拽的节点类型(Toolbar 写入,canvas 读取)
@@ -57,12 +57,7 @@ export function useFlowOperations(
const targetNode = nodes.value.find((node) => node.id === params.target)
// 自动推断标签
const autoLabel = getAutoLabel(
sourceNode,
targetNode,
params.sourceHandle,
params.targetHandle,
)
const autoLabel = getAutoLabel(sourceNode, targetNode, params.sourceHandle, params.targetHandle)
const newEdge: Edge = {
id: `edge-${getRandomId()}`,
@@ -15,10 +15,7 @@ export function useHistory() {
// 是否可以重做
const canRedo = computed(() => historyIndex.value < history.value.length - 1)
const deepCopyState = (
nodes: Node[],
edges: Edge[],
): { nodes: Node[]; edges: Edge[] } =>
const deepCopyState = (nodes: Node[], edges: Edge[]): { nodes: Node[]; edges: Edge[] } =>
// 先裁掉 vue-flow 的运行时内部字段再深拷贝:20 份快照 × 每个节点几百字节的
// handleBounds/dimensions,纯属白拷
JSON.parse(