fix(template): 修复区域生成编号和画布点击交互问题
修复批量生成区域时编号未考虑现有区域的问题,现在会基于现有最大编号递增。 修复画布点击交互:支持点击空白处取消选中,并优化多选切换为单选的逻辑。
This commit is contained in:
@ -62,11 +62,23 @@ function handleGenerate() {
|
|||||||
const groupId = crypto.randomUUID()
|
const groupId = crypto.randomUUID()
|
||||||
const newRegions: Region[] = []
|
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 currentX = genSettings.value.startX
|
||||||
const currentY = genSettings.value.startY
|
const currentY = genSettings.value.startY
|
||||||
|
|
||||||
for (let r = 0; r < genSettings.value.rows; r++) {
|
for (let r = 0; r < genSettings.value.rows; r++) {
|
||||||
for (let c = 0; c < genSettings.value.cols; c++) {
|
for (let c = 0; c < genSettings.value.cols; c++) {
|
||||||
|
const index = maxNum + r * genSettings.value.cols + c + 1 // 计算当前区域的编号
|
||||||
newRegions.push({
|
newRegions.push({
|
||||||
id: crypto.randomUUID(),
|
id: crypto.randomUUID(),
|
||||||
groupId,
|
groupId,
|
||||||
@ -74,7 +86,7 @@ function handleGenerate() {
|
|||||||
y: currentY + r * (genSettings.value.h + genSettings.value.gapY),
|
y: currentY + r * (genSettings.value.h + genSettings.value.gapY),
|
||||||
w: genSettings.value.w,
|
w: genSettings.value.w,
|
||||||
h: genSettings.value.h,
|
h: genSettings.value.h,
|
||||||
label: `Q${newRegions.length + 1}`, // Placeholder label
|
label: `Q${index}`, // 题目编号递增
|
||||||
selected: false,
|
selected: false,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -121,6 +133,7 @@ async function handleSave() {
|
|||||||
height: Math.round(templateInfo.value.height),
|
height: Math.round(templateInfo.value.height),
|
||||||
tempContent: JSON.stringify(regions.value),
|
tempContent: JSON.stringify(regions.value),
|
||||||
}
|
}
|
||||||
|
console.log(params, 'params')
|
||||||
|
|
||||||
const { error } = await fetchAddTemplate(params)
|
const { error } = await fetchAddTemplate(params)
|
||||||
|
|
||||||
|
|||||||
@ -44,6 +44,7 @@ watch(() => props.imgSrc, () => {
|
|||||||
|
|
||||||
// 拖拽初始状态记录
|
// 拖拽初始状态记录
|
||||||
const dragStartPositions = ref<Map<string, { x: number, y: number }>>(new Map())
|
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 键
|
* 支持多选:按住 Shift/Ctrl/Meta 键
|
||||||
*/
|
*/
|
||||||
function onRegionMouseDown(region: Region, e: MouseEvent) {
|
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 isModifierDown = e.shiftKey || e.ctrlKey || e.metaKey
|
||||||
const isSelected = props.selectedRegionIds.has(region.id)
|
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 接管
|
// 移除 onActivated 的选中逻辑,完全由 onRegionMouseDown 接管
|
||||||
function onActivated(_id: string) {
|
function onActivated(_id: string) {
|
||||||
// 空实现,避免覆盖多选逻辑
|
// 空实现,避免覆盖多选逻辑
|
||||||
@ -272,6 +316,7 @@ onUnmounted(() => {
|
|||||||
width: `${templateInfo.width}px`,
|
width: `${templateInfo.width}px`,
|
||||||
height: `${templateInfo.height}px`,
|
height: `${templateInfo.height}px`,
|
||||||
}"
|
}"
|
||||||
|
@mousedown="onCanvasMouseDown"
|
||||||
>
|
>
|
||||||
<!-- 背景图片 -->
|
<!-- 背景图片 -->
|
||||||
<img
|
<img
|
||||||
@ -307,6 +352,7 @@ onUnmounted(() => {
|
|||||||
class="region-content group relative h-full w-full flex items-center justify-center transition-transform"
|
class="region-content group relative h-full w-full flex items-center justify-center transition-transform"
|
||||||
:style="{ transform: `rotate(${region.rotation || 0}deg)` }"
|
:style="{ transform: `rotate(${region.rotation || 0}deg)` }"
|
||||||
@mousedown="onRegionMouseDown(region, $event)"
|
@mousedown="onRegionMouseDown(region, $event)"
|
||||||
|
@mouseup="onRegionMouseUp(region, $event)"
|
||||||
>
|
>
|
||||||
<!-- 视觉边框 (旋转) -->
|
<!-- 视觉边框 (旋转) -->
|
||||||
<div
|
<div
|
||||||
|
|||||||
Reference in New Issue
Block a user