对齐后端 dev 059e597,新增项目风格编辑锁定、身份文本生成、身份参考图与母版切换。 整理六个工作区入口,接通人物及场景母版继承说明、形态图片来源追溯。 补充草稿保护、正式 ID 校验、费用确认及部分失败回执,67 项测试和静态检查、构建通过。 未修改后端,未调用真实模型;本环境未完成浏览器视觉验收。
129 lines
6.1 KiB
Vue
129 lines
6.1 KiB
Vue
<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>
|