# RFC 137: 角色-对白关联表单交互设计 > **状态**:Draft > **创建日期**:2026-01-30 > **参考**:ui-ux-pro-max UX 准则、现有 StoryboardDialogue 数据模型 --- ## 1. 背景与目标 ### 1.1 当前问题 - 分镜表单的「角色」栏仅为 Badge 列表 + ResourcePicker,与对白无关联 - 对白目前存在「拍摄描述」字段(原 dialogue),与角色无一一对应 - 后端 `StoryboardDialogue` 已支持 `characterName`,一个分镜可有多个角色各自的对白 ### 1.2 设计目标 1. **关联展示**:选择角色后自动展示该角色在此分镜下的对白 2. **可编辑**:对白以输入框形式,支持手动编辑 3. **初始化**:AI 解析剧本时自动提取对白并关联角色 4. **数据一致**:与 `StoryboardDialogue` 模型对齐(characterName + text) --- ## 2. 数据结构扩展 ### 2.1 DraftShot 扩展 ```ts // client/src/types/storyboard.ts export interface DraftShot { // ... 现有字段 characters: string[]; /** 角色-对白映射。key=角色名, value=该角色在此分镜的对白文本 */ characterDialogues: Record; } ``` - 保留 `characters`:角色列表(用于 ResourcePicker、智能提取) - 新增 `characterDialogues`:按角色存储对白 - 原 `dialogue` 已改为拍摄描述,不再用于对白 ### 2.2 AI 解析输出 剧本 AI 解析时需输出分镜维度的角色对白,例如: ```json { "storyboards": [ { "characters": ["唐僧", "孙悟空"], "characterDialogues": { "唐僧": "如何才能救你?", "孙悟空": "山顶有张符咒,撕下来就行!" } } ] } ``` --- ## 3. 交互设计(基于 ui-ux-pro-max) ### 3.1 推荐方案:角色-对白行式布局 **布局**:左侧角色名,右侧对白输入框,一行一个角色。类似两列表格,结构清晰、一目了然。 ``` ┌────────────┬──────────────────────────────────────┐ │ 角色 │ 对白 │ ├────────────┼──────────────────────────────────────┤ │ 孙悟空 │ [师父!救救我!______________] │ │ 唐僧 │ [如何才能救你?______________] │ │ 土地公公 │ [(无对白)________________] │ ← 此分镜该角色无对白 └────────────┴──────────────────────────────────────┘ ``` **无对白角色**:该角色在此分镜无台词时,输入框为空,placeholder 显示「(此分镜无对白)」;用户仍可手动输入,以补充漏提取的对白。 | UX 准则 | 应用 | |---------|------| | **Form Labels** | 表头「角色」「对白」;每行角色名作为该行输入框的语义标签 | | **Input Affordance** | 输入框有 border/background,明显可编辑 | | **Empty States** | 无对白时 placeholder:「(此分镜无对白)」;无角色时显示「添加角色后可编辑对白」 | | **Input Affordance** | 输入框保持可编辑,便于用户补充 | **优点**: - 结构简单,无展开/收起,学习成本低 - 所有角色及对白同屏可见,便于对照修改 - 天然支持「有/无对白」:无对白即为空输入框 --- ## 4. 视觉与交互规格 ### 4.1 角色-对白行式结构 ``` ┌─────────────────────────────────────────────────────────────┐ │ [User图标] 角色 [+ 添加角色] │ ├──────────────────┬──────────────────────────────────────────┤ │ 孙悟空 [×] │ ┌────────────────────────────────────┐ │ │ │ │ 师父!救救我! │ │ │ │ └────────────────────────────────────┘ │ ├──────────────────┼──────────────────────────────────────────┤ │ 唐僧 [×] │ ┌────────────────────────────────────┐ │ │ │ │ 如何才能救你? │ │ │ │ └────────────────────────────────────┘ │ ├──────────────────┼──────────────────────────────────────────┤ │ 土地公公 [×] │ ┌────────────────────────────────────┐ │ │ │ │ (此分镜无对白) │ │ ← placeholder │ │ └────────────────────────────────────┘ │ └──────────────────┴──────────────────────────────────────────┘ ``` - **左列**:角色名 + 删除按钮 `[×]`,固定宽度(如 100–120px) - **右列**:单行 Input 或可折叠的 Textarea(短对白用 Input,长对白可展开) - **添加角色**:顶部 ResourcePicker,与现有一致 ### 4.2 状态与反馈 | 状态 | 表现 | |------|------| | 有对白 | 输入框显示对白内容 | | 无对白 | 输入框为空,placeholder:「(此分镜无对白)」 | | 无角色 | 空状态:「添加角色后可编辑对白」 | | 焦点 | 输入框 focus:ring-2 focus:ring-brand | ### 4.3 输入框类型建议 - **默认**:单行 `Input`(`type="text"`),适合大多对白 - **可选**:对白较长时使用 `Textarea`,或支持点击展开为多行 ### 4.4 无障碍 - 每行用 `role="row"` 或 `aria-labelledby` 关联角色名与输入框 - 输入框有 `aria-label` 或通过 `