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

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 {
/** 角色唯一标识符 */
id: string;
/** 角色名称 */
name: 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[];
export interface SketchPromptJson {
/** 草图名称 */
sketch_name: string;
/** 草图描述 */
sketch_description: string;
/** 核心氛围 */
core_atmosphere: string;
}
/**
*
*
*/
export interface SketchItem {
export interface SketchData {
/** 草图名称 */
sketch_name: string;
/** 图片ID */
@ -178,51 +106,97 @@ export enum Gender {
/** 提示词 */
prompt: string;
/** 提示词JSON */
prompt_json: PromptJson;
/** 草图名称前缀 */
sketch_name_prefix: string;
/** 图片URL列表 */
urls: string[];
prompt_json: SketchPromptJson;
/** 图片路径 */
image_path: string;
}
/**
*
*
*/
export interface ShotSketchItem {
/** 镜头草图名称 */
shot_sketch_name: string;
export interface SketchResponse {
/** 总数 */
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 */
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;
/** 提示词JSON */
prompt_json: PromptJson;
/** 视频名称前缀 */
video_name_prefix: string;
/** 视频URL列表 */
urls: string[];
prompt_json: ShotSketchPromptJson;
/** 图片URL */
url: string;
}
/**
*
*
*/
export interface MasterCharacter {
export interface ShotSketchResponse {
/** 镜头草图数据列表 */
data: ShotSketchData[];
/** 总数 */
total_count: number;
}
/**
*
*/
export interface MasterCharacterListItem {
/** 角色ID */
id: string;
/** 角色名称 */
@ -232,9 +206,9 @@ export enum Gender {
}
/**
*
*
*/
export interface MasterWardrobe {
export interface MasterWardrobeListItem {
/** 所属角色 */
belongs_to: string;
/** 服装描述 */
@ -242,9 +216,9 @@ export enum Gender {
}
/**
*
*
*/
export interface MasterScene {
export interface MasterSceneListItem {
/** 场景名称 */
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 {
/** 视频列表 */
data: VideoItem[];
/** 视频ID */
video_id: string;
/** 描述 */
description: string;
/** 提示词JSON */
prompt_json: VideoPromptJson;
/** 视频名称前缀 */
video_name_prefix: string;
/** 视频URL列表 */
urls: string[];
}
/**
*
*/
export interface VideoResponse {
/** 视频数据列表 */
data: VideoData[];
/** 总数 */
total_count: number;
/** 全局ID */
@ -266,37 +278,14 @@ export enum Gender {
}
/**
*
*
*/
export interface MusicData {
[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 {
/** 最终视频URL */
@ -304,126 +293,24 @@ export enum Gender {
}
/**
*
*
*/
export interface MultilingualVideo {
/** 多语言视频相关数据,当前为空对象 */
[key: string]: any;
}
/**
*
*
*/
export interface ProjectSettings {
/** 视频质量设置 */
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 {
export interface ProjectContentData {
/** 草图数据 */
sketch: SketchItem[];
sketch: SketchResponse;
/** 角色数据 */
character: Character[];
character: CharacterData;
/** 镜头草图数据 */
shot_sketch: ShotSketchItem[];
shot_sketch: ShotSketchResponse;
/** 视频数据 */
video: VideoData;
video: VideoResponse;
/** 音乐数据 */
music: MusicData;
/** 最终视频 */
@ -432,40 +319,6 @@ export enum Gender {
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 */
project_id: string;
/** 项目模式 */
mode: ProjectMode;
mode: string;
/** 分辨率 */
resolution: Resolution;
resolution: string;
/** 语言 */
language: Language;
language: string;
/** 原始文本 */
original_text: string;
/** 生成的脚本 */
generated_script: string;
/** 项目状态 */
status: ProjectStatus;
status: string;
/** 项目标题 */
title: string;
/** 项目类型 */
@ -493,11 +346,21 @@ export enum Gender {
/** 项目标签 */
tags: string[];
/** 项目步骤 */
step: ProjectStep;
step: 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 { RoleEntity, AITextEntity } from '../domain/Entities';
import { RoleEditUseCase } from '../usecase/RoleEditUseCase';
import { getRoleData, getRoleList, getUserRoleLibrary, replaceRole } from '@/api/video_flow';
import { getUploadToken, uploadToQiniu } from '@/api/common';
/**
@ -70,12 +69,12 @@ export const useRoleServiceHook = (): UseRoleService => {
*/
const fetchRoleList = useCallback(async (projectId: string) => {
try {
const response = await getRoleList({ projectId });
if (response.successful) {
setRoleList(response.data);
} else {
throw new Error(`获取角色列表失败: ${response.message}`);
}
// 初始化角色编辑UseCase实例
const newRoleEditUseCase = new RoleEditUseCase();
const roleList = await newRoleEditUseCase.getRoleList(projectId);
setRoleList(roleList);
setRoleEditUseCase(newRoleEditUseCase);
} catch (error) {
console.error('获取角色列表失败:', error);
throw error;
@ -90,18 +89,7 @@ export const useRoleServiceHook = (): UseRoleService => {
* @returns {Promise<void>} Promise
*/
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) {
setSelectedRole(role);
// 初始化角色编辑UseCase实例
const newRoleEditUseCase = new RoleEditUseCase();
newRoleEditUseCase.roleList = roleList;
await newRoleEditUseCase.selectRole(roleId);
setRoleEditUseCase(newRoleEditUseCase);
// 如果RoleEditUseCase已经初始化直接使用否则创建新的
if (!roleEditUseCase) {
const newRoleEditUseCase = new RoleEditUseCase();
newRoleEditUseCase.roleList = roleList;
setRoleEditUseCase(newRoleEditUseCase);
}
// 调用selectRole方法
await roleEditUseCase!.selectRole(roleId);
// 初始化角色数据
await initializeRoleData(roleId);
} else {
throw new Error('未找到对应的角色');
}
}, [initializeRoleData, roleList]);
}, [roleList, roleEditUseCase, initializeRoleData]);
/**
* AI文本
@ -236,17 +228,19 @@ export const useRoleServiceHook = (): UseRoleService => {
*/
const fetchUserRoleLibrary = useCallback(async () => {
try {
const response = await getUserRoleLibrary();
if (response.successful) {
setUserRoleLibrary(response.data);
} else {
throw new Error(`获取用户角色库失败: ${response.message}`);
// 如果没有初始化RoleEditUseCase创建一个新的实例
if (!roleEditUseCase) {
const newRoleEditUseCase = new RoleEditUseCase();
setRoleEditUseCase(newRoleEditUseCase);
}
const roleLibraryList = await roleEditUseCase!.getRoleLibraryList();
setUserRoleLibrary(roleLibraryList);
} catch (error) {
console.error('获取用户角色库失败:', error);
throw error;
}
}, []);
}, [roleEditUseCase]);
/**
*

View File

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

View File

@ -1,36 +1,40 @@
import FloatingGlassPanel from './FloatingGlassPanel';
import { ImageWave } from '@/components/ui/ImageWave';
import { RoleEntity } from '@/app/service/domain/Entities';
const imageUrls = [
'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',
'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',
];
interface CharacterLibrarySelectorProps {
isReplaceLibraryOpen: boolean;
setIsReplaceLibraryOpen: (open: boolean) => void;
onSelect: (index: number) => void;
/** 用户角色库数据 */
userRoleLibrary: RoleEntity[];
}
export function CharacterLibrarySelector({
isReplaceLibraryOpen,
setIsReplaceLibraryOpen,
onSelect,
}: {
isReplaceLibraryOpen: boolean;
setIsReplaceLibraryOpen: (open: boolean) => void;
onSelect: (index: number) => void;
}) {
userRoleLibrary = []
}: CharacterLibrarySelectorProps) {
// 将 RoleEntity[] 转换为图片URL数组
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 (
<FloatingGlassPanel
open={isReplaceLibraryOpen}
@ -54,4 +58,4 @@ export function CharacterLibrarySelector({
/>
</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 { ImageUp, Library, Play, Pause, RefreshCw, Wand2, Users, Check, ReplaceAll, X, TriangleAlert } from 'lucide-react';
import { cn } from '@/public/lib/utils';
@ -8,6 +8,7 @@ import FloatingGlassPanel from './FloatingGlassPanel';
import { ReplaceCharacterPanel, mockShots, mockCharacter } from './replace-character-panel';
import { CharacterLibrarySelector } from './character-library-selector';
import HorizontalScroller from './HorizontalScroller';
import { useRoleServiceHook } from '@/app/service/Interaction/RoleService';
interface Appearance {
hairStyle: string;
@ -56,7 +57,7 @@ interface CharacterTabContentProps {
roles: Role[];
}
export function CharacterTabContent({
export function CharacterTabContent({
taskSketch,
currentRoleIndex,
onSketchSelect,
@ -73,7 +74,14 @@ export function CharacterTabContent({
const fileInputRef = useRef<HTMLInputElement>(null);
const [enableAnimation, setEnableAnimation] = 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 = () => {
setIsRemindReplacePanelOpen(false);
setIsReplacePanelOpen(true);
@ -122,15 +130,23 @@ export function CharacterTabContent({
// 从角色库中选择角色
const handleSelectCharacter = (index: number) => {
console.log('index', index);
console.log('选择的角色索引:', index);
console.log('选择的角色数据:', userRoleLibrary[index]);
setIsReplaceLibraryOpen(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 = () => {
setIsReplaceLibraryOpen(true);
setShowAddToLibrary(true);
fetchUserRoleLibrary();
};
const handleRegenerate = () => {
@ -182,7 +198,7 @@ export function CharacterTabContent({
onChange={handleFileChange}
/>
{/* 上部分:角色缩略图 */}
<motion.div
<motion.div
className="space-y-6"
initial={{ opacity: 0, y: 20 }}
animate={{ opacity: 1, y: 0 }}
@ -194,7 +210,7 @@ export function CharacterTabContent({
selectedIndex={selectRoleIndex}
onItemClick={(i: number) => handleChangeRole(i)}
>
{roles.map((role, index) => (
{roleList.map((role, index) => (
<motion.div
key={`role-${index}`}
className={cn(
@ -205,8 +221,8 @@ export function CharacterTabContent({
whileHover={{ scale: 1.05 }}
whileTap={{ scale: 0.95 }}
>
<img
src={role.url}
<img
src={role.imageUrl}
alt={role.name}
className="w-full h-full object-cover"
/>
@ -241,7 +257,7 @@ export function CharacterTabContent({
{/* 应用角色按钮 */}
<div className='absolute top-3 right-3 flex gap-2'>
<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"
whileHover={{ scale: 1.05 }}
whileTap={{ scale: 0.95 }}
@ -250,7 +266,7 @@ export function CharacterTabContent({
<ImageUp className="w-4 h-4" />
</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"
whileHover={{ scale: 1.05 }}
whileTap={{ scale: 0.95 }}
@ -271,7 +287,7 @@ export function CharacterTabContent({
<div className="grid grid-cols-2 gap-2">
<motion.button
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"
whileHover={{ scale: 1.02 }}
whileTap={{ scale: 0.98 }}
@ -281,7 +297,7 @@ export function CharacterTabContent({
</motion.button>
<motion.button
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"
whileHover={{ scale: 1.02 }}
whileTap={{ scale: 0.98 }}
@ -292,7 +308,7 @@ export function CharacterTabContent({
</div>
</div>
</motion.div>
@ -317,6 +333,7 @@ export function CharacterTabContent({
isReplaceLibraryOpen={isReplaceLibraryOpen}
setIsReplaceLibraryOpen={setIsReplaceLibraryOpen}
onSelect={handleSelectCharacter}
userRoleLibrary={userRoleLibrary}
/>
{/* 提醒用户角色已修改 是否需要替换 */}
@ -330,9 +347,9 @@ export function CharacterTabContent({
<TriangleAlert className="w-6 h-6 text-yellow-400" />
<p className="text-lg font-medium"></p>
</div>
<div className="flex gap-3 mt-2">
<button
<button
onClick={() => handleConfirmGotoReplace()}
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"
@ -340,8 +357,8 @@ export function CharacterTabContent({
<ReplaceAll className="w-4 h-4" />
</button>
<button
<button
onClick={() => handleCloseRemindReplacePanel()}
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"
@ -354,4 +371,4 @@ export function CharacterTabContent({
</FloatingGlassPanel>
</div>
);
}
}