diff --git a/apps/admin/src/views/template/template-detail/index.vue b/apps/admin/src/views/template/template-detail/index.vue index ad00293..9f10602 100644 --- a/apps/admin/src/views/template/template-detail/index.vue +++ b/apps/admin/src/views/template/template-detail/index.vue @@ -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) diff --git a/apps/admin/src/views/template/template-detail/modules/TemplateCanvas.vue b/apps/admin/src/views/template/template-detail/modules/TemplateCanvas.vue index 42b78d4..eac1bcc 100644 --- a/apps/admin/src/views/template/template-detail/modules/TemplateCanvas.vue +++ b/apps/admin/src/views/template/template-detail/modules/TemplateCanvas.vue @@ -44,6 +44,7 @@ watch(() => props.imgSrc, () => { // 拖拽初始状态记录 const dragStartPositions = ref>(new Map()) +const mouseDownPos = ref<{ x: number, y: number } | null>(null) // --- 拖拽与调整大小逻辑 --- @@ -52,6 +53,9 @@ const dragStartPositions = ref>(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" > { 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)" >