style: 优化选角面板间距与角色卡片布局

This commit is contained in:
GouJ
2026-09-01 18:12:54 +08:00
parent badea1b798
commit 7773721399
5 changed files with 139 additions and 34 deletions
@@ -166,7 +166,7 @@ watch(
title="角色选角总览与批量操作"
:has-receipt="!!session.receipt"
>
<div class="flex flex-wrap items-end justify-between gap-4">
<div class="identity-tools-intro flex flex-wrap items-end justify-between gap-4">
<div>
<h2 class="text-lg font-semibold">主体身份</h2>
<p class="mt-2 text-sm text-muted">
@@ -294,21 +294,21 @@ watch(
>
并发数与本批上限都必须是正整数
</p>
<div class="casting-list mt-5">
<div class="casting-list" role="group" aria-label="角色选角进度">
<NButton
v-for="item in casting.items"
:key="item.subjectId"
@click="select(item.subjectId)"
class="casting-item"
:class="{ selected: selectedId === item.subjectId }"
><span class="min-w-0">
<span class="block truncate text-xs font-medium">{{
item.subjectName
}}</span>
<span class="mt-1 block font-mono text-[10px] text-muted">{{
:aria-pressed="selectedId === item.subjectId"
><span class="casting-item-identity">
<span class="casting-item-name">{{ item.subjectName }}</span>
<span class="casting-item-ref">{{
item.subjectRef
}}</span> </span
><StatusBadge
class="casting-item-status"
:status="item.status"
:label="castingStatusLabel(item.status)"
/></NButton>
+63
View File
@@ -527,6 +527,69 @@ describe('视觉风格与主体身份工作区', () => {
expect(wrapper!.text()).toContain('当前身份母版')
})
it('选角进度卡片分层显示长姓名、编号和状态,点击仍按正式 ID 切换主体', async () => {
const longName = '来自记忆当铺的另一条时间线中尚未丢失记忆的林默'
const items = [
{
subjectId: 'subject-db-1',
subjectRef: '@CH0001',
subjectName: longName,
status: 'ready',
isLocked: true
},
{
subjectId: 'subject-db-2',
subjectRef: '@CH0002',
subjectName: '老周',
status: 'candidate_pending',
isLocked: false
}
]
const fetcher = vi.fn<typeof fetch>().mockImplementation(async url => {
const path = String(url)
if (path.endsWith('/subject-forms')) return jsonResponse([formFixture()])
if (path.endsWith('/visual-style')) return jsonResponse(styleFixture())
if (path.endsWith('/character-casting/readiness'))
return jsonResponse({
total: 2,
ready: 1,
missingIdentity: 0,
missingAnchor: 0,
candidatePending: 1,
unlocked: 0,
locked: 1,
items
})
if (path.endsWith('/identity/images')) return jsonResponse([])
const second = path.includes('/subject-db-2/')
return jsonResponse(
identityFixture({
subjectId: second ? 'subject-db-2' : 'subject-db-1',
description: second ? '老周的稳定身份' : '林默的稳定身份'
})
)
})
vi.stubGlobal('fetch', fetcher)
await mountAssets('identity')
expect(wrapper!.get('.identity-tools-intro').text()).toContain('为同一个人物、场景或道具固定稳定特征')
const cards = wrapper!.get('[aria-label="角色选角进度"]').findAll('.casting-item')
expect(cards).toHaveLength(2)
expect(cards[0]!.get('.casting-item-name').text()).toBe(longName)
expect(cards[0]!.get('.casting-item-ref').text()).toBe('@CH0001')
expect(cards[0]!.get('.casting-item-status').text()).toBe('选角已完成')
expect(cards[0]!.find('.truncate').exists()).toBe(false)
expect(cards[0]!.attributes('aria-pressed')).toBe('true')
expect(cards[1]!.get('.casting-item-status').text()).toBe('等待确认演员')
await cards[1]!.trigger('click')
await flushPromises()
expect(cards[0]!.attributes('aria-pressed')).toBe('false')
expect(cards[1]!.attributes('aria-pressed')).toBe('true')
expect(cards[1]!.classes()).toContain('selected')
expect(wrapper!.get<HTMLTextAreaElement>('#identity-description').element.value).toBe('老周的稳定身份')
expect(fetcher.mock.calls.some(([url]) => String(url).endsWith('/subjects/subject-db-2/identity'))).toBe(true)
expect(fetcher.mock.calls.every(([, init]) => init?.method === 'GET')).toBe(true)
})
it('Character 确认选角原子切换 Anchor 并锁定 Identity,不调用普通母版接口', async () => {
let locked = false
let selected = false