Files
reader-star/apps/admin/src/views/template/template-detail/index.vue
rjl ba38accdb9 fix(template): 修复区域生成编号和画布点击交互问题
修复批量生成区域时编号未考虑现有区域的问题,现在会基于现有最大编号递增。
修复画布点击交互:支持点击空白处取消选中,并优化多选切换为单选的逻辑。
2026-01-29 09:06:16 +08:00

194 lines
5.1 KiB
Vue

<!-- eslint-disable no-console -->
<script lang="ts" setup>
import type { GenSettings, Region, TemplateInfo } from './types'
import { Icon } from '@iconify/vue'
import { NButton, NUpload, useMessage } from 'naive-ui'
import { ref } from 'vue'
import { useRoute } from 'vue-router'
import { useRouterPush } from '@/hooks/common/router'
import { fetchAddTemplate } from '@/service/api/template'
import { useUpload } from './hooks/useUpload'
import TemplateCanvas from './modules/TemplateCanvas.vue'
import TemplateSettings from './modules/TemplateSettings.vue'
const route = useRoute()
const message = useMessage()
const { routerBack } = useRouterPush()
// 模版基础信息
const templateInfo = ref<TemplateInfo>({
name: '期末大考标准答题卡',
width: 2100,
height: 2994,
})
const imgSrc = ref('')
// 生成配置
const genSettings = ref<GenSettings>({
cols: 1,
rows: 1,
w: 296,
h: 309,
gapX: 5,
gapY: 0,
startX: 0,
startY: 0,
})
// 区域列表
const regions = ref<Region[]>([])
// 选中区域集合
const selectedRegionIds = ref<Set<string>>(new Set())
// Use Hook
const { loading, handleUpload, handleUploadCheck } = useUpload(imgSrc, templateInfo, regions, selectedRegionIds)
const uploadRef = ref<any>(null)
function handleUploadTrigger() {
handleUploadCheck(() => {
uploadRef.value?.openOpenFileDialog()
})
}
// --- Actions ---
/**
* 处理区域生成
*/
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,
x: currentX + c * (genSettings.value.w + genSettings.value.gapX),
y: currentY + r * (genSettings.value.h + genSettings.value.gapY),
w: genSettings.value.w,
h: genSettings.value.h,
label: `Q${index}`, // 题目编号递增
selected: false,
})
}
}
regions.value.push(...newRegions)
if (newRegions.length > 1) {
const newIds = new Set(newRegions.map(r => r.id))
selectedRegionIds.value = newIds
}
window?.$message?.success(`已生成 ${newRegions.length} 个区域`)
}
/**
* 删除区域
*/
function deleteRegion(id: string) {
regions.value = regions.value.filter(r => r.id !== id)
selectedRegionIds.value.delete(id)
}
/**
* 清空所有
*/
function clearAll() {
regions.value = []
selectedRegionIds.value.clear()
}
/**
* 保存模板
*/
async function handleSave() {
const id = Number(route.query.id) || 0
const params: Api.Template.AddTemplateParams = {
id,
pageNo: '1',
name: templateInfo.value.name,
backGroundUrl: templateInfo.value.backGroundUrl || '',
width: Math.round(templateInfo.value.width),
height: Math.round(templateInfo.value.height),
tempContent: JSON.stringify(regions.value),
}
console.log(params, 'params')
const { error } = await fetchAddTemplate(params)
if (!error) {
message.success('保存成功')
routerBack()
}
}
</script>
<template>
<div class="h-full flex flex-col bg-gray-100">
<!-- Header -->
<div class="h-14 flex shrink-0 items-center justify-between border-b bg-white px-4">
<div class="flex items-center gap-2">
<NButton quaternary circle @click="routerBack()">
<template #icon>
<Icon icon="carbon:arrow-left" />
</template>
</NButton>
<span class="text-lg font-medium">答题卡排版编辑器 V2.0</span>
</div>
<div class="flex items-center gap-3">
<NUpload
ref="uploadRef"
:show-file-list="false"
:custom-request="handleUpload"
accept=".pdf,image/png,image/jpeg,image/jpg"
style="display: none"
/>
<NButton @click="handleUploadTrigger">
<template #icon>
<Icon icon="carbon:upload" />
</template>
上传底稿
</NButton>
<NButton type="primary" :loading="loading" :disabled="!templateInfo.backGroundUrl" @click="handleSave">
完成设计并保存
</NButton>
</div>
</div>
<div class="flex flex-1 overflow-hidden">
<!-- Left: Canvas Area -->
<TemplateCanvas
v-model:template-info="templateInfo" v-model:regions="regions"
v-model:selected-region-ids="selectedRegionIds" :img-src="imgSrc" :loading="loading"
/>
<!-- Right: Settings Panel -->
<TemplateSettings
v-model:template-info="templateInfo" v-model:gen-settings="genSettings"
:regions="regions" @generate="handleGenerate" @delete-region="deleteRegion" @clear-all="clearAll"
/>
</div>
</div>
</template>