重构角色编辑相关接口,更新角色数据结构以支持新字段,优化角色列表和用户角色库的获取逻辑,增强角色选择功能。

This commit is contained in:
海龙 2025-08-10 16:02:38 +08:00
parent 03aa092a08
commit 3f1f2e34c8
5 changed files with 252 additions and 374 deletions

View File

@ -84,93 +84,21 @@ export enum Gender {
} }
/** /**
* * JSON结构接
*/ */
export interface Character { export interface SketchPromptJson {
/** 角色唯一标识符 */ /** 草图名称 */
id: string; sketch_name: string;
/** 角色名称 */ /** 草图描述 */
name: string; sketch_description: string;
/** 角色描述,包含外貌、性格等详细信息 */
description: string;
/** 角色类型 */
role: RoleType;
/** 角色年龄 */
age: number;
/** 角色性别 */
gender: Gender;
/** 角色种族 */
race: Race;
/** 角色简介 */
brief?: string;
/** 角色体型描述 */
physique?: string;
/** 角色发型描述 */
hairstyle?: string;
/** 角色默认举止描述 */
default_demeanor?: string;
}
/**
*
*/
export interface Wardrobe {
/** 服装所属角色ID */
belongs_to: string;
/** 服装详细描述 */
description: string;
}
/**
*
*/
export interface Scene {
/** 场景名称 */
name: string;
/** 场景详细描述 */
description: string;
}
/**
*
*/
export interface ProjectStyle {
/** 地理设置和种族信息 */
geographic_setting_ethnicity: string;
/** 格式要求 */
format: string;
/** 美学风格描述 */
aesthetics: string;
/** 技术蓝图 */
technical_blueprint: string;
}
/**
* JSON结构接口
*/
export interface PromptJson {
/** 导演指令 */
directors_directive: string;
/** 叙事提示 */
narrative_prompt: string;
/** 对话语言 */
dialogue_language: string;
/** 整体项目风格 */
overall_project_style: string;
/** 主要角色列表 */
master_character_list: MasterCharacter[];
/** 主要服装列表 */
master_wardrobe_list: MasterWardrobe[];
/** 主要场景列表 */
master_scene_list: MasterScene[];
/** 核心氛围 */ /** 核心氛围 */
core_atmosphere: string; core_atmosphere: string;
} }
/** /**
* *
*/ */
export interface SketchItem { export interface SketchData {
/** 草图名称 */ /** 草图名称 */
sketch_name: string; sketch_name: string;
/** 图片ID */ /** 图片ID */
@ -178,51 +106,97 @@ export enum Gender {
/** 提示词 */ /** 提示词 */
prompt: string; prompt: string;
/** 提示词JSON */ /** 提示词JSON */
prompt_json: PromptJson; prompt_json: SketchPromptJson;
/** 草图名称前缀 */ /** 图片路径 */
sketch_name_prefix: string; image_path: string;
/** 图片URL列表 */
urls: string[];
} }
/** /**
* *
*/ */
export interface ShotSketchItem { export interface SketchResponse {
/** 镜头草图名称 */ /** 总数 */
shot_sketch_name: string; total_count: number;
/** 草图数据列表 */
data: SketchData[];
}
/**
*
*/
export interface CharacterResponse {
/** 角色图片路径 */
image_path: string;
/** 角色名称 */
character_name: string;
/** 角色描述 */
character_description: string;
}
/**
*
*/
export interface CharacterData {
/** 角色数据列表 */
data: CharacterResponse[];
/** 总数 */
total_count: number;
}
/**
* JSON结构接口
*/
export interface ShotSketchPromptJson {
/** 镜头类型 */
shot_type: string;
/** 帧描述 */
frame_description: string;
/** 关键动作 */
key_action: string;
/** 氛围 */
atmosphere: string;
/** 摄影蓝图构图 */
cinematography_blueprint_composition: string;
/** 摄影蓝图镜头运动 */
cinematography_blueprint_camera_motion: string;
/** 对话表演台词 */
dialogue_performance_line?: any;
/** 对话表演语言 */
dialogue_performance_language?: any;
/** 对话表演表达 */
dialogue_performance_delivery?: any;
/** 对话表演说话者 */
dialogue_performance_speaker?: any;
}
/**
*
*/
export interface ShotSketchData {
/** 图片ID */ /** 图片ID */
image_id: string; image_id: string;
/** 提示词 */
prompt: string;
/** 提示词JSON */
prompt_json: PromptJson;
/** 镜头草图名称前缀 */
shot_sketch_name_prefix: string;
/** 图片URL列表 */
urls: string[];
}
/**
*
*/
export interface VideoItem {
/** 视频ID */
video_id: string;
/** 描述 */ /** 描述 */
description: string; description: string;
/** 提示词JSON */ /** 提示词JSON */
prompt_json: PromptJson; prompt_json: ShotSketchPromptJson;
/** 视频名称前缀 */ /** 图片URL */
video_name_prefix: string; url: string;
/** 视频URL列表 */
urls: string[];
} }
/** /**
* *
*/ */
export interface MasterCharacter { export interface ShotSketchResponse {
/** 镜头草图数据列表 */
data: ShotSketchData[];
/** 总数 */
total_count: number;
}
/**
*
*/
export interface MasterCharacterListItem {
/** 角色ID */ /** 角色ID */
id: string; id: string;
/** 角色名称 */ /** 角色名称 */
@ -232,9 +206,9 @@ export enum Gender {
} }
/** /**
* *
*/ */
export interface MasterWardrobe { export interface MasterWardrobeListItem {
/** 所属角色 */ /** 所属角色 */
belongs_to: string; belongs_to: string;
/** 服装描述 */ /** 服装描述 */
@ -242,9 +216,9 @@ export enum Gender {
} }
/** /**
* *
*/ */
export interface MasterScene { export interface MasterSceneListItem {
/** 场景名称 */ /** 场景名称 */
name: string; name: string;
/** 场景描述 */ /** 场景描述 */
@ -252,11 +226,49 @@ export enum Gender {
} }
/** /**
* * JSON结构接口
*/
export interface VideoPromptJson {
/** 导演指令 */
directors_directive: string;
/** 叙事提示 */
narrative_prompt: string;
/** 对话语言 */
dialogue_language: string;
/** 整体项目风格 */
overall_project_style: string;
/** 主要角色列表 */
master_character_list: MasterCharacterListItem[];
/** 主要服装列表 */
master_wardrobe_list: MasterWardrobeListItem[];
/** 主要场景列表 */
master_scene_list: MasterSceneListItem[];
/** 核心氛围 */
core_atmosphere: string;
}
/**
*
*/ */
export interface VideoData { export interface VideoData {
/** 视频列表 */ /** 视频ID */
data: VideoItem[]; video_id: string;
/** 描述 */
description: string;
/** 提示词JSON */
prompt_json: VideoPromptJson;
/** 视频名称前缀 */
video_name_prefix: string;
/** 视频URL列表 */
urls: string[];
}
/**
*
*/
export interface VideoResponse {
/** 视频数据列表 */
data: VideoData[];
/** 总数 */ /** 总数 */
total_count: number; total_count: number;
/** 全局ID */ /** 全局ID */
@ -266,37 +278,14 @@ export enum Gender {
} }
/** /**
* *
*/ */
export interface MusicData { export interface MusicData {
[key: string]: any; [key: string]: any;
} }
/**
*
*/
export interface Video {
/** 视频唯一标识符 */
video_id: string;
/** 视频描述 */
description: string;
/** 提示JSON数据 */
prompt_json: PromptJson;
/** 视频名称前缀 */
video_name_prefix: string;
/** 视频URL列表 */
urls: string[];
}
/** /**
* *
*/
export interface Music {
/** 音乐相关数据,当前为空对象 */
[key: string]: any;
}
/**
*
*/ */
export interface FinalVideo { export interface FinalVideo {
/** 最终视频URL */ /** 最终视频URL */
@ -304,126 +293,24 @@ export enum Gender {
} }
/** /**
* *
*/ */
export interface MultilingualVideo { export interface MultilingualVideo {
/** 多语言视频相关数据,当前为空对象 */
[key: string]: any; [key: string]: any;
} }
/** /**
* *
*/ */
export interface ProjectSettings { export interface ProjectContentData {
/** 视频质量设置 */
quality: string;
/** 帧率设置 */
frame_rate: string;
/** 分辨率设置 */
resolution: string;
/** 音频设置 */
audio: {
/** 音频质量 */
quality: string;
/** 音频格式 */
format: string;
};
}
/**
*
*/
export interface ProjectStatistics {
/** 总视频数 */
total_videos: number;
/** 总角色数 */
total_characters: number;
/** 总场景数 */
total_scenes: number;
/** 总服装数 */
total_wardrobes: number;
/** 总音乐数 */
total_music: number;
/** 总时长 */
total_duration: number;
/** 总大小 */
total_size: number;
}
/**
*
*/
export interface ProjectMetadata {
/** 项目版本 */
version: string;
/** 项目语言 */
language: string;
/** 项目分类 */
category: string;
/** 项目难度 */
difficulty: string;
/** 项目时长 */
duration: string;
/** 项目预算 */
budget: string;
/** 项目团队 */
team: string[];
/** 项目设备 */
equipment: string[];
/** 项目软件 */
software: string[];
/** 项目许可证 */
license: string;
/** 项目版权 */
copyright: string;
/** 项目评分 */
rating: number;
/** 项目评论数 */
review_count: number;
/** 项目下载数 */
download_count: number;
/** 项目分享数 */
share_count: number;
/** 项目收藏数 */
favorite_count: number;
/** 项目观看数 */
view_count: number;
/** 项目完成度 */
completion_rate: number;
/** 项目进度 */
progress: number;
/** 项目阶段 */
stage: string;
/** 项目里程碑 */
milestones: string[];
/** 项目任务 */
tasks: string[];
/** 项目文件 */
files: string[];
/** 项目链接 */
links: string[];
/** 项目备注 */
notes: string;
/** 项目历史 */
history: string[];
/** 项目日志 */
logs: string[];
/** 项目统计 */
statistics: ProjectStatistics;
}
/**
*
*/
export interface ProjectContent {
/** 草图数据 */ /** 草图数据 */
sketch: SketchItem[]; sketch: SketchResponse;
/** 角色数据 */ /** 角色数据 */
character: Character[]; character: CharacterData;
/** 镜头草图数据 */ /** 镜头草图数据 */
shot_sketch: ShotSketchItem[]; shot_sketch: ShotSketchResponse;
/** 视频数据 */ /** 视频数据 */
video: VideoData; video: VideoResponse;
/** 音乐数据 */ /** 音乐数据 */
music: MusicData; music: MusicData;
/** 最终视频 */ /** 最终视频 */
@ -432,40 +319,6 @@ export enum Gender {
multilingual_video: MultilingualVideo; multilingual_video: MultilingualVideo;
} }
/**
*
*/
export interface VideoFlowProject {
/** 项目名称 */
name: string;
/** 项目描述 */
description: string;
/** 项目类型 */
type: string;
/** 项目状态 */
status: string;
/** 项目创建时间 */
created_at: string;
/** 项目更新时间 */
updated_at: string;
/** 项目所有者ID */
owner_id: string;
/** 项目标签列表 */
tags: string[];
/** 项目设置 */
settings: ProjectSettings;
/** 项目元数据 */
metadata: ProjectMetadata;
/** 项目内容 */
content: ProjectContent;
/** 音乐信息 */
music: Music;
/** 最终视频 */
final_video: FinalVideo;
/** 多语言视频 */
multilingual_video: MultilingualVideo;
}
/** /**
* - * -
*/ */
@ -475,17 +328,17 @@ export enum Gender {
/** 项目ID */ /** 项目ID */
project_id: string; project_id: string;
/** 项目模式 */ /** 项目模式 */
mode: ProjectMode; mode: string;
/** 分辨率 */ /** 分辨率 */
resolution: Resolution; resolution: string;
/** 语言 */ /** 语言 */
language: Language; language: string;
/** 原始文本 */ /** 原始文本 */
original_text: string; original_text: string;
/** 生成的脚本 */ /** 生成的脚本 */
generated_script: string; generated_script: string;
/** 项目状态 */ /** 项目状态 */
status: ProjectStatus; status: string;
/** 项目标题 */ /** 项目标题 */
title: string; title: string;
/** 项目类型 */ /** 项目类型 */
@ -493,11 +346,21 @@ export enum Gender {
/** 项目标签 */ /** 项目标签 */
tags: string[]; tags: string[];
/** 项目步骤 */ /** 项目步骤 */
step: ProjectStep; step: string;
/** 最后消息 */ /** 最后消息 */
last_message: string; last_message: string;
/** 项目内容 */ /** 项目内容 */
data: ProjectContent; data: ProjectContentData;
} }
// 为了向后兼容,保留原有的接口名称
export interface Character extends CharacterResponse {}
export interface Sketch extends SketchResponse {}
export interface Shot_sketch extends ShotSketchResponse {}
export interface Video extends VideoResponse {}
export interface Music extends MusicData {}
export interface Final_video extends FinalVideo {}
export interface Multilingual_video extends MultilingualVideo {}
export interface RootObject extends VideoFlowProjectResponse {}

View File

@ -1,7 +1,6 @@
import { useState, useCallback, useMemo } from 'react'; import { useState, useCallback, useMemo } from 'react';
import { RoleEntity, AITextEntity } from '../domain/Entities'; import { RoleEntity, AITextEntity } from '../domain/Entities';
import { RoleEditUseCase } from '../usecase/RoleEditUseCase'; import { RoleEditUseCase } from '../usecase/RoleEditUseCase';
import { getRoleData, getRoleList, getUserRoleLibrary, replaceRole } from '@/api/video_flow';
import { getUploadToken, uploadToQiniu } from '@/api/common'; import { getUploadToken, uploadToQiniu } from '@/api/common';
/** /**
@ -70,12 +69,12 @@ export const useRoleServiceHook = (): UseRoleService => {
*/ */
const fetchRoleList = useCallback(async (projectId: string) => { const fetchRoleList = useCallback(async (projectId: string) => {
try { try {
const response = await getRoleList({ projectId }); // 初始化角色编辑UseCase实例
if (response.successful) { const newRoleEditUseCase = new RoleEditUseCase();
setRoleList(response.data); const roleList = await newRoleEditUseCase.getRoleList(projectId);
} else {
throw new Error(`获取角色列表失败: ${response.message}`); setRoleList(roleList);
} setRoleEditUseCase(newRoleEditUseCase);
} catch (error) { } catch (error) {
console.error('获取角色列表失败:', error); console.error('获取角色列表失败:', error);
throw error; throw error;
@ -90,18 +89,7 @@ export const useRoleServiceHook = (): UseRoleService => {
* @returns {Promise<void>} Promise * @returns {Promise<void>} Promise
*/ */
const initializeRoleData = useCallback(async (roleId: string) => { const initializeRoleData = useCallback(async (roleId: string) => {
try {
const response = await getRoleData({ roleId });
if (response.successful) {
const { text } = response.data;
setCurrentRoleText(text.content);
} else {
throw new Error(`获取角色数据失败: ${response.message}`);
}
} catch (error) {
console.error('初始化角色数据失败:', error);
throw error;
}
}, []); }, []);
/** /**
@ -114,18 +102,22 @@ export const useRoleServiceHook = (): UseRoleService => {
if (role) { if (role) {
setSelectedRole(role); setSelectedRole(role);
// 初始化角色编辑UseCase实例 // 如果RoleEditUseCase已经初始化直接使用否则创建新的
const newRoleEditUseCase = new RoleEditUseCase(); if (!roleEditUseCase) {
newRoleEditUseCase.roleList = roleList; const newRoleEditUseCase = new RoleEditUseCase();
await newRoleEditUseCase.selectRole(roleId); newRoleEditUseCase.roleList = roleList;
setRoleEditUseCase(newRoleEditUseCase); setRoleEditUseCase(newRoleEditUseCase);
}
// 调用selectRole方法
await roleEditUseCase!.selectRole(roleId);
// 初始化角色数据 // 初始化角色数据
await initializeRoleData(roleId); await initializeRoleData(roleId);
} else { } else {
throw new Error('未找到对应的角色'); throw new Error('未找到对应的角色');
} }
}, [initializeRoleData, roleList]); }, [roleList, roleEditUseCase, initializeRoleData]);
/** /**
* AI文本 * AI文本
@ -236,17 +228,19 @@ export const useRoleServiceHook = (): UseRoleService => {
*/ */
const fetchUserRoleLibrary = useCallback(async () => { const fetchUserRoleLibrary = useCallback(async () => {
try { try {
const response = await getUserRoleLibrary(); // 如果没有初始化RoleEditUseCase创建一个新的实例
if (response.successful) { if (!roleEditUseCase) {
setUserRoleLibrary(response.data); const newRoleEditUseCase = new RoleEditUseCase();
} else { setRoleEditUseCase(newRoleEditUseCase);
throw new Error(`获取用户角色库失败: ${response.message}`);
} }
const roleLibraryList = await roleEditUseCase!.getRoleLibraryList();
setUserRoleLibrary(roleLibraryList);
} catch (error) { } catch (error) {
console.error('获取用户角色库失败:', error); console.error('获取用户角色库失败:', error);
throw error; throw error;
} }
}, []); }, [roleEditUseCase]);
/** /**
* *

View File

@ -47,15 +47,15 @@ export class RoleEditUseCase {
} }
parseRoleList(detail: VideoFlowProjectResponse): RoleEntity[] { parseRoleList(detail: VideoFlowProjectResponse): RoleEntity[] {
const characters = detail.data?.character || []; const characters = detail.data?.character.data || [];
return characters.map((char, index) => { return characters.map((char, index) => {
const roleEntity: RoleEntity = { const roleEntity: RoleEntity = {
id: `role_${index + 1}`, id: `role_${index + 1}`,
name: char.name || '', name: char.character_name || '',
generateText: char.description || '', generateText: char.character_description || '',
tags: [], // 默认为空标签数组 tags: [], // 默认为空标签数组
imageUrl: '', // 默认为空图片URL imageUrl: char.image_path || '', // 使用API返回的图片路径
loadingProgress: 100, // 默认加载完成 loadingProgress: 100, // 默认加载完成
disableEdit: false, // 默认允许编辑 disableEdit: false, // 默认允许编辑
updatedAt: Date.now() updatedAt: Date.now()
@ -265,7 +265,7 @@ export class RoleEditUseCase {
id: `role_${Date.now()}`, id: `role_${Date.now()}`,
name: '从图片识别的角色', name: '从图片识别的角色',
generateText: '通过图片识别生成的角色描述', generateText: '通过图片识别生成的角色描述',
tagIds: [], // 空标签数组 tags: [], // 空标签数组
imageUrl: imageUrl, // 使用传入的图片地址 imageUrl: imageUrl, // 使用传入的图片地址
loadingProgress: 100, // 加载完成 loadingProgress: 100, // 加载完成
disableEdit: false, // 允许编辑 disableEdit: false, // 允许编辑

View File

@ -1,36 +1,40 @@
import FloatingGlassPanel from './FloatingGlassPanel'; import FloatingGlassPanel from './FloatingGlassPanel';
import { ImageWave } from '@/components/ui/ImageWave'; import { ImageWave } from '@/components/ui/ImageWave';
import { RoleEntity } from '@/app/service/domain/Entities';
const imageUrls = [ interface CharacterLibrarySelectorProps {
'https://d3phaj0sisr2ct.cloudfront.net/app/gen4/object-reference/welcome-ref-1.jpg', isReplaceLibraryOpen: boolean;
'https://d3phaj0sisr2ct.cloudfront.net/app/gen4/object-reference/welcome-ref-2.jpg', setIsReplaceLibraryOpen: (open: boolean) => void;
'https://d3phaj0sisr2ct.cloudfront.net/app/gen4/object-reference/welcome-ref-3.jpg', onSelect: (index: number) => void;
'https://d3phaj0sisr2ct.cloudfront.net/app/gen4/object-reference/welcome-ref-4.jpg', /** 用户角色库数据 */
'https://d3phaj0sisr2ct.cloudfront.net/app/gen4/object-reference/welcome-ref-1.jpg', userRoleLibrary: RoleEntity[];
'https://d3phaj0sisr2ct.cloudfront.net/app/gen4/object-reference/welcome-ref-2.jpg', }
'https://d3phaj0sisr2ct.cloudfront.net/app/gen4/object-reference/welcome-ref-3.jpg',
'https://d3phaj0sisr2ct.cloudfront.net/app/gen4/object-reference/welcome-ref-4.jpg',
'https://d3phaj0sisr2ct.cloudfront.net/app/gen4/object-reference/welcome-ref-1.jpg',
'https://d3phaj0sisr2ct.cloudfront.net/app/gen4/object-reference/welcome-ref-2.jpg',
'https://d3phaj0sisr2ct.cloudfront.net/app/gen4/object-reference/welcome-ref-3.jpg',
'https://d3phaj0sisr2ct.cloudfront.net/app/gen4/object-reference/welcome-ref-4.jpg',
'https://d3phaj0sisr2ct.cloudfront.net/app/gen4/object-reference/welcome-ref-1.jpg',
'https://d3phaj0sisr2ct.cloudfront.net/app/gen4/object-reference/welcome-ref-2.jpg',
'https://d3phaj0sisr2ct.cloudfront.net/app/gen4/object-reference/welcome-ref-3.jpg',
'https://d3phaj0sisr2ct.cloudfront.net/app/gen4/object-reference/welcome-ref-4.jpg',
'https://d3phaj0sisr2ct.cloudfront.net/app/gen4/object-reference/welcome-ref-1.jpg',
'https://d3phaj0sisr2ct.cloudfront.net/app/gen4/object-reference/welcome-ref-2.jpg',
];
export function CharacterLibrarySelector({ export function CharacterLibrarySelector({
isReplaceLibraryOpen, isReplaceLibraryOpen,
setIsReplaceLibraryOpen, setIsReplaceLibraryOpen,
onSelect, onSelect,
}: { userRoleLibrary = []
isReplaceLibraryOpen: boolean; }: CharacterLibrarySelectorProps) {
setIsReplaceLibraryOpen: (open: boolean) => void; // 将 RoleEntity[] 转换为图片URL数组
onSelect: (index: number) => void; const imageUrls = userRoleLibrary.map(role => role.imageUrl);
}) {
// 如果没有数据,显示空状态
if (userRoleLibrary.length === 0) {
return (
<FloatingGlassPanel
open={isReplaceLibraryOpen}
width='90vw'
panel_style={{ background: 'unset', border: 'unset', backdropFilter: 'unset', boxShadow: 'none' }}
onClose={() => setIsReplaceLibraryOpen(false)}
>
<div className="flex items-center justify-center h-64 text-white/50">
<p></p>
</div>
</FloatingGlassPanel>
);
}
return ( return (
<FloatingGlassPanel <FloatingGlassPanel
open={isReplaceLibraryOpen} open={isReplaceLibraryOpen}
@ -54,4 +58,4 @@ export function CharacterLibrarySelector({
/> />
</FloatingGlassPanel> </FloatingGlassPanel>
); );
} }

View File

@ -1,4 +1,4 @@
import React, { useState, useRef } from 'react'; import React, { useState, useRef, useEffect } from 'react';
import { motion, AnimatePresence } from 'framer-motion'; import { motion, AnimatePresence } from 'framer-motion';
import { ImageUp, Library, Play, Pause, RefreshCw, Wand2, Users, Check, ReplaceAll, X, TriangleAlert } from 'lucide-react'; import { ImageUp, Library, Play, Pause, RefreshCw, Wand2, Users, Check, ReplaceAll, X, TriangleAlert } from 'lucide-react';
import { cn } from '@/public/lib/utils'; import { cn } from '@/public/lib/utils';
@ -8,6 +8,7 @@ import FloatingGlassPanel from './FloatingGlassPanel';
import { ReplaceCharacterPanel, mockShots, mockCharacter } from './replace-character-panel'; import { ReplaceCharacterPanel, mockShots, mockCharacter } from './replace-character-panel';
import { CharacterLibrarySelector } from './character-library-selector'; import { CharacterLibrarySelector } from './character-library-selector';
import HorizontalScroller from './HorizontalScroller'; import HorizontalScroller from './HorizontalScroller';
import { useRoleServiceHook } from '@/app/service/Interaction/RoleService';
interface Appearance { interface Appearance {
hairStyle: string; hairStyle: string;
@ -56,7 +57,7 @@ interface CharacterTabContentProps {
roles: Role[]; roles: Role[];
} }
export function CharacterTabContent({ export function CharacterTabContent({
taskSketch, taskSketch,
currentRoleIndex, currentRoleIndex,
onSketchSelect, onSketchSelect,
@ -73,7 +74,14 @@ export function CharacterTabContent({
const fileInputRef = useRef<HTMLInputElement>(null); const fileInputRef = useRef<HTMLInputElement>(null);
const [enableAnimation, setEnableAnimation] = useState(true); const [enableAnimation, setEnableAnimation] = useState(true);
const [showAddToLibrary, setShowAddToLibrary] = useState(true); const [showAddToLibrary, setShowAddToLibrary] = useState(true);
const {fetchRoleList,roleList,fetchUserRoleLibrary,userRoleLibrary} = useRoleServiceHook()
useEffect(() => {
// 从url 获取 episodeId 作为projctId
const projectId = new URLSearchParams(window.location.search).get('episodeId');
if (projectId) {
fetchRoleList(projectId);
}
}, [fetchRoleList]);
const handleConfirmGotoReplace = () => { const handleConfirmGotoReplace = () => {
setIsRemindReplacePanelOpen(false); setIsRemindReplacePanelOpen(false);
setIsReplacePanelOpen(true); setIsReplacePanelOpen(true);
@ -122,15 +130,23 @@ export function CharacterTabContent({
// 从角色库中选择角色 // 从角色库中选择角色
const handleSelectCharacter = (index: number) => { const handleSelectCharacter = (index: number) => {
console.log('index', index); console.log('选择的角色索引:', index);
console.log('选择的角色数据:', userRoleLibrary[index]);
setIsReplaceLibraryOpen(false); setIsReplaceLibraryOpen(false);
setShowAddToLibrary(false); setShowAddToLibrary(false);
handleReplaceCharacter('https://c.huiying.video/images/5740cb7c-6e08-478f-9e7c-bca7f78a2bf6.jpg');
// 使用真实的角色数据
const selectedRole = userRoleLibrary[index];
if (selectedRole) {
handleReplaceCharacter(selectedRole.imageUrl);
}
}; };
const handleOpenReplaceLibrary = () => { const handleOpenReplaceLibrary = () => {
setIsReplaceLibraryOpen(true); setIsReplaceLibraryOpen(true);
setShowAddToLibrary(true); setShowAddToLibrary(true);
fetchUserRoleLibrary();
}; };
const handleRegenerate = () => { const handleRegenerate = () => {
@ -182,7 +198,7 @@ export function CharacterTabContent({
onChange={handleFileChange} onChange={handleFileChange}
/> />
{/* 上部分:角色缩略图 */} {/* 上部分:角色缩略图 */}
<motion.div <motion.div
className="space-y-6" className="space-y-6"
initial={{ opacity: 0, y: 20 }} initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }} animate={{ opacity: 1, y: 0 }}
@ -194,7 +210,7 @@ export function CharacterTabContent({
selectedIndex={selectRoleIndex} selectedIndex={selectRoleIndex}
onItemClick={(i: number) => handleChangeRole(i)} onItemClick={(i: number) => handleChangeRole(i)}
> >
{roles.map((role, index) => ( {roleList.map((role, index) => (
<motion.div <motion.div
key={`role-${index}`} key={`role-${index}`}
className={cn( className={cn(
@ -205,8 +221,8 @@ export function CharacterTabContent({
whileHover={{ scale: 1.05 }} whileHover={{ scale: 1.05 }}
whileTap={{ scale: 0.95 }} whileTap={{ scale: 0.95 }}
> >
<img <img
src={role.url} src={role.imageUrl}
alt={role.name} alt={role.name}
className="w-full h-full object-cover" className="w-full h-full object-cover"
/> />
@ -241,7 +257,7 @@ export function CharacterTabContent({
{/* 应用角色按钮 */} {/* 应用角色按钮 */}
<div className='absolute top-3 right-3 flex gap-2'> <div className='absolute top-3 right-3 flex gap-2'>
<motion.button <motion.button
className="p-2 bg-black/50 hover:bg-black/70 className="p-2 bg-black/50 hover:bg-black/70
text-white rounded-full backdrop-blur-sm transition-colors z-10" text-white rounded-full backdrop-blur-sm transition-colors z-10"
whileHover={{ scale: 1.05 }} whileHover={{ scale: 1.05 }}
whileTap={{ scale: 0.95 }} whileTap={{ scale: 0.95 }}
@ -250,7 +266,7 @@ export function CharacterTabContent({
<ImageUp className="w-4 h-4" /> <ImageUp className="w-4 h-4" />
</motion.button> </motion.button>
<motion.button <motion.button
className="p-2 bg-black/50 hover:bg-black/70 className="p-2 bg-black/50 hover:bg-black/70
text-white rounded-full backdrop-blur-sm transition-colors z-10" text-white rounded-full backdrop-blur-sm transition-colors z-10"
whileHover={{ scale: 1.05 }} whileHover={{ scale: 1.05 }}
whileTap={{ scale: 0.95 }} whileTap={{ scale: 0.95 }}
@ -271,7 +287,7 @@ export function CharacterTabContent({
<div className="grid grid-cols-2 gap-2"> <div className="grid grid-cols-2 gap-2">
<motion.button <motion.button
onClick={() => handleReplaceCharacter('https://c.huiying.video/images/5740cb7c-6e08-478f-9e7c-bca7f78a2bf6.jpg')} onClick={() => handleReplaceCharacter('https://c.huiying.video/images/5740cb7c-6e08-478f-9e7c-bca7f78a2bf6.jpg')}
className="flex items-center justify-center gap-2 px-4 py-3 bg-pink-500/10 hover:bg-pink-500/20 className="flex items-center justify-center gap-2 px-4 py-3 bg-pink-500/10 hover:bg-pink-500/20
text-pink-500 rounded-lg transition-colors" text-pink-500 rounded-lg transition-colors"
whileHover={{ scale: 1.02 }} whileHover={{ scale: 1.02 }}
whileTap={{ scale: 0.98 }} whileTap={{ scale: 0.98 }}
@ -281,7 +297,7 @@ export function CharacterTabContent({
</motion.button> </motion.button>
<motion.button <motion.button
onClick={() => handleRegenerate()} onClick={() => handleRegenerate()}
className="flex items-center justify-center gap-2 px-4 py-3 bg-blue-500/10 hover:bg-blue-500/20 className="flex items-center justify-center gap-2 px-4 py-3 bg-blue-500/10 hover:bg-blue-500/20
text-blue-500 rounded-lg transition-colors" text-blue-500 rounded-lg transition-colors"
whileHover={{ scale: 1.02 }} whileHover={{ scale: 1.02 }}
whileTap={{ scale: 0.98 }} whileTap={{ scale: 0.98 }}
@ -292,7 +308,7 @@ export function CharacterTabContent({
</div> </div>
</div> </div>
</motion.div> </motion.div>
@ -317,6 +333,7 @@ export function CharacterTabContent({
isReplaceLibraryOpen={isReplaceLibraryOpen} isReplaceLibraryOpen={isReplaceLibraryOpen}
setIsReplaceLibraryOpen={setIsReplaceLibraryOpen} setIsReplaceLibraryOpen={setIsReplaceLibraryOpen}
onSelect={handleSelectCharacter} onSelect={handleSelectCharacter}
userRoleLibrary={userRoleLibrary}
/> />
{/* 提醒用户角色已修改 是否需要替换 */} {/* 提醒用户角色已修改 是否需要替换 */}
@ -330,9 +347,9 @@ export function CharacterTabContent({
<TriangleAlert className="w-6 h-6 text-yellow-400" /> <TriangleAlert className="w-6 h-6 text-yellow-400" />
<p className="text-lg font-medium"></p> <p className="text-lg font-medium"></p>
</div> </div>
<div className="flex gap-3 mt-2"> <div className="flex gap-3 mt-2">
<button <button
onClick={() => handleConfirmGotoReplace()} onClick={() => handleConfirmGotoReplace()}
data-alt="confirm-replace-button" data-alt="confirm-replace-button"
className="px-4 py-2 bg-blue-600 hover:bg-blue-700 rounded-md transition-colors duration-200 flex items-center gap-2" className="px-4 py-2 bg-blue-600 hover:bg-blue-700 rounded-md transition-colors duration-200 flex items-center gap-2"
@ -340,8 +357,8 @@ export function CharacterTabContent({
<ReplaceAll className="w-4 h-4" /> <ReplaceAll className="w-4 h-4" />
</button> </button>
<button <button
onClick={() => handleCloseRemindReplacePanel()} onClick={() => handleCloseRemindReplacePanel()}
data-alt="ignore-button" data-alt="ignore-button"
className="px-4 py-2 bg-gray-600 hover:bg-gray-700 rounded-md transition-colors duration-200 flex items-center gap-2" className="px-4 py-2 bg-gray-600 hover:bg-gray-700 rounded-md transition-colors duration-200 flex items-center gap-2"
@ -354,4 +371,4 @@ export function CharacterTabContent({
</FloatingGlassPanel> </FloatingGlassPanel>
</div> </div>
); );
} }