This commit is contained in:
2026-08-29 01:11:01 +08:00
parent 474d561173
commit 5c43370fc9
37 changed files with 1309 additions and 409 deletions
+61 -33
View File
@@ -52,6 +52,14 @@ async function loadAll() {
const tasks = await request.get('/label-tasks', { params: { page: 1, size: 100 } }).catch(() => null)
const running = (tasks?.list || []).find((t) => t.datasetId === datasetId.value && t.status === 'running')
if (running) pollLabelTask(running.id)
// 刷新时恢复进行中的 AI 生成任务(或展示最近一次失败的提示)
const gt = await request.get('/datasets/gen-task', { params: { datasetId: datasetId.value } }).catch(() => null)
if (gt && gt.status === 'running') {
genTask.value = { id: gt.id, status: gt.status, total: gt.total, done: gt.done, error: gt.error }
pollGenTask()
} else if (gt && gt.error) {
genTask.value = gt
}
} finally {
loading.value = false
}
@@ -59,41 +67,63 @@ async function loadAll() {
// ---------- 添加图片(AI 生成) ----------
const addVisible = ref(false)
const genForm = reactive({ species: '', count: 1, size: '704x1248', distance: 25 })
const speciesOptions = ref([])
// 单物种规则:物种固定取数据集名,表单无物种输入
const genForm = reactive({ count: 1, size: '704x1248', distance: 25 })
const generating = ref(false)
// 生成表单选项(物种池/场景池来自 config,随配置热更新)
function loadGenOptions() {
request.get('/datasets/gen-options', { params: { datasetId: datasetId.value } }).then((d) => {
speciesOptions.value = d.species || []
}).catch(() => {})
}
function openAdd() {
genForm.species = ''
loadGenOptions()
genForm.count = 1
genForm.size = '704x1248'
genForm.distance = 25
addVisible.value = true
}
// AI 生成为异步任务:提交即返回 taskId,轮询 /datasets/gen-task 看进度
const genTask = ref(null)
const genPoll = ref(null)
const genRunning = computed(() => genTask.value && genTask.value.status === 'running')
function pollGenTask() {
stopGenPoll()
genPoll.value = setInterval(async () => {
const d = await request.get('/datasets/gen-task', { params: { datasetId: datasetId.value } }).catch(() => null)
if (!d) return
genTask.value = { id: d.id, status: d.status, total: d.total, done: d.done, error: d.error }
if (d.status !== 'running') {
stopGenPoll()
if (d.error) {
ElMessage.error(`AI 生成未完成:${d.error}`)
} else if (d.total > 0) {
ElMessage.success(`AI 生成完成(${d.done}/${d.total} 张),自动标注处理中…`)
}
loadAll().then(() => {
if (editorVisible.value) nextTick(resetEditor)
})
}
}, 3000)
}
function stopGenPoll() {
if (genPoll.value) {
clearInterval(genPoll.value)
genPoll.value = null
}
}
function submitGen() {
generating.value = true
request
.post('/datasets/generate', {
datasetId: datasetId.value,
species: genForm.species.trim(),
count: genForm.count,
size: genForm.size,
distance: genForm.distance,
}, { timeout: 2400000 })
})
.then((data) => {
const rj = data.rejected ? `${data.rejected} 张不合格已跳过` : ''
ElMessage.success(`已生成 ${data.generated || 0}${rj}(生成图片为付费资产,删除前请确认),自动标注处理中…`)
addVisible.value = false
loadAll()
genTask.value = { id: data.taskId, status: 'running', total: data.total, done: 0, error: '' }
ElMessage.success(`已提交生成任务(共 ${data.total} 张,约 40 秒/张),进度见页顶进度条`)
pollGenTask()
})
.catch(() => {})
.finally(() => {
@@ -491,6 +521,7 @@ onMounted(() => {
onBeforeUnmount(() => {
stopLabelPoll()
stopGenPoll()
stopHighlight()
window.removeEventListener('resize', onWinResize)
})
@@ -512,7 +543,7 @@ onBeforeUnmount(() => {
<!-- 图片工具 -->
<div class="img-toolbar">
<el-button type="primary" :icon="Plus" @click="openAdd">添加</el-button>
<el-button type="primary" :icon="Plus" :disabled="genRunning" @click="openAdd">添加</el-button>
<el-button :icon="MagicStick" :disabled="labelRunning" @click="startFullLabelTask">全量标注</el-button>
</div>
@@ -526,6 +557,16 @@ onBeforeUnmount(() => {
<div v-if="labelTask && labelTask.status === 'done' && labelTask.error" class="label-error">
自动标注未完成{{ labelTask.error }}
</div>
<div v-if="genRunning" class="label-progress">
<el-progress
:percentage="genTask.total ? Math.min(100, Math.round((genTask.done / genTask.total) * 100)) : 0"
:stroke-width="10"
/>
<span class="label-progress-tip">AI 生成中 {{ genTask.done }}/{{ genTask.total }} 40 /</span>
</div>
<div v-if="genTask && genTask.status !== 'running' && genTask.error" class="label-error">
AI 生成未完成{{ genTask.error }}
</div>
<!-- 图片卡片点击缩略图直接进标注弹窗有标注框直接显示在图上可继续画框 -->
<div v-loading="loading" class="rows-area">
@@ -564,21 +605,8 @@ onBeforeUnmount(() => {
<!-- 添加图片AI 生成 -->
<el-dialog v-model="addVisible" title="AI 生成图片" width="min(520px, 92vw)" append-to-body>
<el-form label-width="90px">
<el-form-item label="动物名称">
<el-select
v-model="genForm.species"
filterable
allow-create
default-first-option
clearable
placeholder="留空按数据集物种池随机"
style="width: 100%"
>
<el-option v-for="s in speciesOptions" :key="s" :label="s" :value="s" />
</el-select>
</el-form-item>
<el-form-item label="生成张数">
<el-input-number v-model="genForm.count" :min="1" :max="8" />
<el-input-number v-model="genForm.count" :min="1" :max="1000" />
</el-form-item>
<el-form-item label="目标距离">
<div class="gen-inline">
@@ -589,11 +617,11 @@ onBeforeUnmount(() => {
<el-form-item label="图片尺寸">
<span class="gen-size">竖版 704x1248</span>
</el-form-item>
<div class="gen-tip">生成图片为付费资产请勿随意删除手填提示词时无需写目标位置系统统一约束目标只出现在画面上部三分之一区域</div>
<div class="gen-tip">按本数据集物种{{ dataset?.name || '' }}生成每张固定 1 生成图片为付费资产请勿随意删除批量生成为后台任务 40 /进度见页顶进度条手填提示词时无需写拍摄角度/位置系统自动约束画面中有且只有这一只动物</div>
</el-form>
<template #footer>
<el-button type="primary" :loading="generating" @click="submitGen">开始生成可能需数分钟</el-button>
<el-button type="primary" :loading="generating" @click="submitGen">开始生成</el-button>
</template>
</el-dialog>