fix(template): 修复区域生成编号和画布点击交互问题

修复批量生成区域时编号未考虑现有区域的问题,现在会基于现有最大编号递增。
修复画布点击交互:支持点击空白处取消选中,并优化多选切换为单选的逻辑。
This commit is contained in:
2026-01-29 09:06:16 +08:00
parent 16d7ecd172
commit ba38accdb9
2 changed files with 61 additions and 2 deletions

View File

@ -62,11 +62,23 @@ function handleGenerate() {
const groupId = crypto.randomUUID()
const newRegions: Region[] = []
// 计算起始编号:查找现有的最大 Qx 编号
let maxNum = 0
regions.value.forEach((r) => {
const match = r.label.match(/^Q(\d+)$/)
if (match) {
const n = Number.parseInt(match[1], 10)
if (!Number.isNaN(n) && n > maxNum) {
maxNum = n
}
}
})
const currentX = genSettings.value.startX
const currentY = genSettings.value.startY
for (let r = 0; r < genSettings.value.rows; r++) {
for (let c = 0; c < genSettings.value.cols; c++) {
const index = maxNum + r * genSettings.value.cols + c + 1 // 计算当前区域的编号
newRegions.push({
id: crypto.randomUUID(),
groupId,
@ -74,7 +86,7 @@ function handleGenerate() {
y: currentY + r * (genSettings.value.h + genSettings.value.gapY),
w: genSettings.value.w,
h: genSettings.value.h,
label: `Q${newRegions.length + 1}`, // Placeholder label
label: `Q${index}`, // 题目编号递增
selected: false,
})
}
@ -121,6 +133,7 @@ async function handleSave() {
height: Math.round(templateInfo.value.height),
tempContent: JSON.stringify(regions.value),
}
console.log(params, 'params')
const { error } = await fetchAddTemplate(params)

View File

@ -44,6 +44,7 @@ watch(() => props.imgSrc, () => {
// 拖拽初始状态记录
const dragStartPositions = ref<Map<string, { x: number, y: number }>>(new Map())
const mouseDownPos = ref<{ x: number, y: number } | null>(null)
// --- 拖拽与调整大小逻辑 ---
@ -52,6 +53,9 @@ const dragStartPositions = ref<Map<string, { x: number, y: number }>>(new Map())
* 支持多选:按住 Shift/Ctrl/Meta 键
*/
function onRegionMouseDown(region: Region, e: MouseEvent) {
// e.stopPropagation() // 移除阻止冒泡,以便 vue-draggable-resizable 能接收到事件并启动拖拽
mouseDownPos.value = { x: e.clientX, y: e.clientY }
const isModifierDown = e.shiftKey || e.ctrlKey || e.metaKey
const isSelected = props.selectedRegionIds.has(region.id)
@ -77,6 +81,46 @@ function onRegionMouseDown(region: Region, e: MouseEvent) {
}
}
/**
* 处理区域鼠标松开
* 用于在未拖拽的情况下,从多选切换为单选
*/
function onRegionMouseUp(region: Region, e: MouseEvent) {
if (!mouseDownPos.value)
return
const dx = Math.abs(e.clientX - mouseDownPos.value.x)
const dy = Math.abs(e.clientY - mouseDownPos.value.y)
const isClick = dx < 3 && dy < 3
const isModifierDown = e.shiftKey || e.ctrlKey || e.metaKey
// 如果是单纯点击,且没有按修饰键,且已经选中(在 onRegionMouseDown 中处理了未选中的情况)
// 那么将多选变为单选
if (isClick && !isModifierDown && props.selectedRegionIds.has(region.id)) {
// 如果当前选中的数量大于 1则切换为单选当前这个
if (props.selectedRegionIds.size > 1) {
emit('update:selectedRegionIds', new Set([region.id]))
}
}
mouseDownPos.value = null
}
/**
* 点击画布空白处取消选中
*/
function onCanvasMouseDown(e: MouseEvent) {
// 检查点击目标是否是区域相关元素
const target = e.target as HTMLElement
// 如果点击的是区域(.region-box, .region-content, .vdr-container 等),则忽略
if (target.closest('.region-box') || target.closest('.region-content') || target.closest('.vdr-container') || target.closest('.vdr-handle')) {
return
}
// 只有点击真正的空白处才取消选中
emit('update:selectedRegionIds', new Set())
}
// 移除 onActivated 的选中逻辑,完全由 onRegionMouseDown 接管
function onActivated(_id: string) {
// 空实现,避免覆盖多选逻辑
@ -272,6 +316,7 @@ onUnmounted(() => {
width: `${templateInfo.width}px`,
height: `${templateInfo.height}px`,
}"
@mousedown="onCanvasMouseDown"
>
<!-- 背景图片 -->
<img
@ -307,6 +352,7 @@ onUnmounted(() => {
class="region-content group relative h-full w-full flex items-center justify-center transition-transform"
:style="{ transform: `rotate(${region.rotation || 0}deg)` }"
@mousedown="onRegionMouseDown(region, $event)"
@mouseup="onRegionMouseUp(region, $event)"
>
<!-- 视觉边框 (旋转) -->
<div