feat: 接入形态图库与 Seedream 生图操作

展示已有主图、候选图和失败记录,支持单图及批量生成、主图选择。
补充正式 ID 校验、费用确认、图片加载占位和回归测试。
依赖后端 eac8dc3 的项目形态图库只读接口。
This commit is contained in:
GouJ
2026-08-28 15:16:25 +08:00
parent bae69c3c62
commit 47443c0efe
26 changed files with 1338 additions and 25 deletions
@@ -0,0 +1,120 @@
<script setup lang="ts">
import { computed, ref, watch } from 'vue'
import { AppDialog } from '../../../components/ui'
import { primaryImage, validImageSize } from '../model'
import type { GenerateFormImageInput, SubjectFormAsset } from '../types'
/** 单形态生成确认:尺寸与自定义 Prompt 只用于本次请求。 */
const props = defineProps<{ form: SubjectFormAsset | null; disabled: boolean }>()
const open = defineModel<boolean>('open', { default: false })
const emit = defineEmits<{ generate: [formId: string, input: GenerateFormImageInput] }>()
const prompt = ref('')
const width = ref<number | ''>('')
const height = ref<number | ''>('')
const setPrimary = ref(false)
const acknowledged = ref(false)
const dimensionsValid = computed(() => validImageSize(width.value, height.value))
const hasPrompt = computed(
() => !!(prompt.value.trim() || (props.form?.generationPrompt ?? props.form?.appearancePrompt ?? '').trim())
)
/** 每次打开重新建立表单,避免把另一形态的 Prompt 或尺寸带入请求。 */
watch(
() => [open.value, props.form?.id],
() => {
prompt.value = ''
width.value = ''
height.value = ''
acknowledged.value = false
setPrimary.value = props.form ? !primaryImage(props.form.images) : false
},
{ immediate: true }
)
/** 完成前置确认后只发出一次明确的生图请求,不触发整条生产流程。 */
function submit() {
if (!props.form || props.disabled || !dimensionsValid.value || !hasPrompt.value || !acknowledged.value) return
emit('generate', props.form.id, {
provider: 'seedream',
setPrimary: setPrimary.value,
...(prompt.value.trim() ? { prompt: prompt.value.trim() } : {}),
...(width.value !== '' && height.value !== '' ? { width: width.value, height: height.value } : {})
})
open.value = false
}
</script>
<template>
<AppDialog
v-model:open="open"
title="生成形态图片"
:description="`${form?.subject.name || ''} · ${form?.name || ''}`"
>
<form class="mt-6 space-y-5" @submit.prevent="submit">
<p class="alert text-xs">使用后端配置的 Seedream 模型可能产生费用每次新增一张图片不删除历史结果</p>
<label class="block"
><span class="field-label">自定义提示词可选仅用于本次</span
><textarea
id="image-prompt"
v-model="prompt"
class="input min-h-28"
placeholder="留空使用已保存的 generationPrompt,其次使用 appearancePrompt"
/>
</label>
<details v-if="form?.generationPrompt || form?.appearancePrompt" class="text-xs">
<summary class="cursor-pointer text-muted">查看后端默认提示词</summary>
<p class="mt-3 whitespace-pre-wrap leading-6">{{ form.generationPrompt ?? form.appearancePrompt }}</p>
</details>
<div class="grid grid-cols-2 gap-4">
<label
><span class="field-label">宽度px</span
><input
id="image-width"
v-model.number="width"
class="input"
type="number"
min="1"
step="1"
placeholder="后端默认" /></label
><label
><span class="field-label">高度px</span
><input
id="image-height"
v-model.number="height"
class="input"
type="number"
min="1"
step="1"
placeholder="后端默认"
/></label>
</div>
<p class="text-xs text-muted">宽高同时留空时使用后端默认 2K自定义尺寸须满足模型限制</p>
<p v-if="!dimensionsValid" class="text-xs text-danger" role="alert">宽高需要同时填写正整数或同时留空</p>
<p v-if="!hasPrompt" class="text-xs text-danger" role="alert">当前形态没有可用提示词请填写本次提示词</p>
<label class="flex gap-2 text-sm"
><input v-model="setPrimary" type="checkbox" class="accent-accent" />生成成功后设为此形态主参考图</label
>
<p v-if="setPrimary && form && primaryImage(form.images)" class="text-xs text-muted">
会替换当前主图标记旧图片保留已有视频提示词不会自动更新
</p>
<label class="flex items-start gap-2 text-xs leading-6"
><input
id="confirm-image-cost"
v-model="acknowledged"
type="checkbox"
class="mt-1.5 accent-accent"
/>我已确认后台没有同项目的生成任务了解本次会调用模型并产生费用</label
>
<div class="dialog-footer">
<button type="button" class="button button-secondary" @click="open = false">取消</button
><button
type="submit"
class="button button-primary"
:disabled="disabled || !acknowledged || !dimensionsValid || !hasPrompt"
>
确认生成图片
</button>
</div>
</form>
</AppDialog>
</template>