Files
short-drama-agent-front/src/features/subject-images/components/GenerateImageDialog.vue
T
GouJ 669f4d9df0 feat: 同步视觉风格与主体身份母版工作区
对齐后端 dev 059e597,新增项目风格编辑锁定、身份文本生成、身份参考图与母版切换。
整理六个工作区入口,接通人物及场景母版继承说明、形态图片来源追溯。
补充草稿保护、正式 ID 校验、费用确认及部分失败回执,67 项测试和静态检查、构建通过。
未修改后端,未调用真实模型;本环境未完成浏览器视觉验收。
2026-08-28 19:28:27 +08:00

129 lines
6.1 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<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>
<p class="text-xs leading-6 text-muted">
{{
form?.subject.module === 'character' || form?.subject.module === 'scene'
? '后端会自动引用此人物/场景当前的身份母版,保持身份或空间结构;没有母版时仍可按文本生成。更换母版不会自动更新已有形态图。'
: '当前道具形态生图暂不自动引用身份母版。'
}}
身份母版与此处的形态主参考图是两种不同用途的图片
</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>