From 655531eacab5aa66c811718b9f710b22286549e9 Mon Sep 17 00:00:00 2001 From: chiyi Date: Mon, 12 Oct 2026 14:20:25 +0800 Subject: [PATCH] =?UTF-8?q?feat(canvas):=20=E7=BC=A9=E6=94=BE=E7=99=BE?= =?UTF-8?q?=E5=88=86=E6=AF=94=E6=94=B9=E4=B8=BA=E4=B8=8B=E6=8B=89=E9=80=89?= =?UTF-8?q?=E6=8B=A9,=E6=94=AF=E6=8C=81=E7=9B=B4=E6=8E=A5=E8=B7=B3?= =?UTF-8?q?=E5=88=B060%=E7=AD=89=E6=A1=A3=E4=BD=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../src/components/canvas/CanvasStage.vue | 12 +++++++++- frontend/src/views/CanvasView.vue | 22 +++++++++++++------ 2 files changed, 26 insertions(+), 8 deletions(-) diff --git a/frontend/src/components/canvas/CanvasStage.vue b/frontend/src/components/canvas/CanvasStage.vue index 599b1f0..75b2533 100644 --- a/frontend/src/components/canvas/CanvasStage.vue +++ b/frontend/src/components/canvas/CanvasStage.vue @@ -633,6 +633,16 @@ function zoomStep(direction) { Object.assign(vp, zoomAt(vp, cx, cy, scale)) notifyViewport() } +// 直接缩放到指定比例(围绕视口中心) +function zoomTo(scale) { + const r = stageEl.value?.getBoundingClientRect() + const cx = r ? r.width / 2 : 0 + const cy = r ? r.height / 2 : 0 + scale = Math.min(MAX_SCALE, Math.max(MIN_SCALE, scale)) + if (scale === vp.scale) return + Object.assign(vp, zoomAt(vp, cx, cy, scale)) + notifyViewport() +} function centerOn(x, y) { const r = stageEl.value?.getBoundingClientRect() vp.x = (r ? r.width / 2 : 400) - x * vp.scale @@ -690,7 +700,7 @@ function screenRectToWorld(r) { } } defineExpose({ - setViewport, zoomStep, centerOn, clearSelection, selectedIds, selectAll, syncZTop, vp, + setViewport, zoomStep, zoomTo, centerOn, clearSelection, selectedIds, selectAll, syncZTop, vp, cancelLink, consumeSelectedEdges, hasSelectedEdges, fitToContent, selectIds, bumpNode, }) diff --git a/frontend/src/views/CanvasView.vue b/frontend/src/views/CanvasView.vue index 98f3c8e..a4f6719 100644 --- a/frontend/src/views/CanvasView.vue +++ b/frontend/src/views/CanvasView.vue @@ -55,6 +55,8 @@ const countOptions = [1, 2, 3, 4].map((n) => ({ value: n, label: `${n} 张` })) const canvasOptions = computed(() => canvases.value.map((c) => ({ value: c.id, label: c.title || '未命名画布' })), ) +const zoomOptions = [20, 30, 40, 50, 60, 70, 80, 90, 100, 125, 150, 200] + .map((p) => ({ value: p, label: `${p}%` })) const maxRefs = computed(() => Math.min(8, Math.max( Number(model.value?.max_reference_images || 0), model.value?.image_to_image ? 1 : 0, ))) @@ -827,7 +829,12 @@ const saveLabel = computed(() => ({ - {{ scalePct }}% + @@ -1079,6 +1086,13 @@ const saveLabel = computed(() => ({ .cv-dd--canvas :deep(.field) { height: 30px; } +.cv-dd--zoom { + min-width: 74px; +} +.cv-dd--zoom :deep(.field) { + height: 30px; + justify-content: center; +} .cv-sep { width: 2.5px; height: 20px; @@ -1091,12 +1105,6 @@ const saveLabel = computed(() => ({ font-size: 12px; font-weight: 700; color: var(--fg); - min-width: 46px; - text-align: center; - background: var(--hover); - border: 1px solid rgb(124 92 252 / 0.35); - border-radius: 999px; - padding: 1px 8px; } .cv-save { font-size: 11px;