forked from 77media/video-flow
重构角色编辑相关接口,更新角色数据结构以支持新字段,优化角色列表和用户角色库的获取逻辑,增强角色选择功能。
This commit is contained in:
parent
03aa092a08
commit
3f1f2e34c8
@ -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 {}
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -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]);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 替换角色
|
* 替换角色
|
||||||
|
|||||||
@ -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, // 允许编辑
|
||||||
|
|||||||
@ -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>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
@ -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>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user