feat: 实现剧本创作与拆解前端工作台

This commit is contained in:
GouJ
2026-08-27 19:33:51 +08:00
parent edc003443f
commit 2ee6f049f6
56 changed files with 7597 additions and 2 deletions
@@ -0,0 +1,67 @@
<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[] }>()
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>
<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>