对齐后端 dev 059e597,新增项目风格编辑锁定、身份文本生成、身份参考图与母版切换。 整理六个工作区入口,接通人物及场景母版继承说明、形态图片来源追溯。 补充草稿保护、正式 ID 校验、费用确认及部分失败回执,67 项测试和静态检查、构建通过。 未修改后端,未调用真实模型;本环境未完成浏览器视觉验收。
80 lines
3.8 KiB
Vue
80 lines
3.8 KiB
Vue
<script setup lang="ts">
|
||
import { computed, ref } from 'vue'
|
||
import { Search, ChevronDown } from '@lucide/vue'
|
||
import { EmptyState } from '../../../components/ui'
|
||
import type { SubjectCandidate, SubjectForm } from '../types'
|
||
|
||
/** 主体列表按稳定 ref 展示;形态以 profileId 关联。 */
|
||
const props = defineProps<{ subjects: SubjectCandidate[]; forms: SubjectForm[]; projectId?: string }>()
|
||
const search = ref('')
|
||
const filtered = computed(() =>
|
||
props.subjects.filter(item =>
|
||
`${item.name} ${item.ref} ${item.aliases?.join(' ') ?? ''}`
|
||
.toLowerCase()
|
||
.includes(search.value.trim().toLowerCase())
|
||
)
|
||
)
|
||
</script>
|
||
|
||
<template>
|
||
<div class="p-5 lg:p-7">
|
||
<div class="mb-5 flex items-center justify-between gap-3">
|
||
<p class="text-sm text-muted">{{ subjects.length }} 个主体</p>
|
||
<div class="search-field">
|
||
<Search :size="14" /><input v-model="search" placeholder="搜索名称或引用" aria-label="搜索主体" />
|
||
</div>
|
||
</div>
|
||
<div v-if="filtered.length" class="divide-y divide-line">
|
||
<article v-for="subject in filtered" :key="subject.profileId" class="py-5 first:pt-0">
|
||
<div class="mb-3 flex flex-wrap items-center gap-3">
|
||
<h3 class="font-semibold">{{ subject.name }}</h3>
|
||
<code class="subject-ref">{{ subject.ref }}</code
|
||
><span v-if="subject.aliases?.length" class="text-xs text-muted"
|
||
>别名:{{ subject.aliases.join('、') }}</span
|
||
>
|
||
</div>
|
||
<p class="text-sm leading-7">{{ subject.description }}</p>
|
||
<RouterLink
|
||
v-if="projectId"
|
||
:to="{ path: `/projects/${projectId}/subject-identity`, query: { subjectRef: subject.ref } }"
|
||
class="text-button mt-3 mr-4"
|
||
>管理稳定身份/母版</RouterLink
|
||
>
|
||
<RouterLink
|
||
v-if="projectId"
|
||
:to="{ path: `/projects/${projectId}/subject-images`, query: { subjectRef: subject.ref } }"
|
||
class="text-button mt-3"
|
||
>查看形态图片/生图</RouterLink
|
||
>
|
||
<details class="subject-details mt-4">
|
||
<summary>
|
||
<ChevronDown :size="14" />外观描述与形态
|
||
<span class="ml-1 text-muted">{{
|
||
forms.filter(form => form.profileId === subject.profileId).length
|
||
}}</span>
|
||
</summary>
|
||
<p class="mt-3 whitespace-pre-wrap text-sm leading-7 text-muted">
|
||
{{ subject.appearance_prompt || '暂无外观描述' }}
|
||
</p>
|
||
<div
|
||
v-for="form in forms.filter(item => item.profileId === subject.profileId)"
|
||
:key="form.formId"
|
||
class="mt-3 border-l-2 border-line pl-4"
|
||
>
|
||
<p class="text-sm font-medium">
|
||
{{ form.formName || form.name }}<span v-if="form.isDefault" class="tag ml-2">默认</span>
|
||
</p>
|
||
<p class="mt-2 text-sm leading-6 text-muted">{{ form.description }}</p>
|
||
<p class="mt-2 text-xs leading-6 text-muted">{{ form.appearancePrompt }}</p>
|
||
</div>
|
||
</details>
|
||
</article>
|
||
</div>
|
||
<EmptyState
|
||
v-else
|
||
:title="subjects.length ? '没有匹配的主体' : '尚无主体结果'"
|
||
description="主体会在抽取、合并与档案整理完成后出现在这里。若未启用该模块,则不会生成此类主体。"
|
||
/>
|
||
</div>
|
||
</template>
|