forked from 77media/video-flow
根据接口 再次调整相关交互逻辑
This commit is contained in:
parent
6c32afbe73
commit
d7fba252d0
@ -23,6 +23,8 @@ interface UseImageStoryService {
|
|||||||
hasAnalyzed: boolean;
|
hasAnalyzed: boolean;
|
||||||
/** 计算后的角色头像数据 */
|
/** 计算后的角色头像数据 */
|
||||||
avatarComputed: Array<{ name: string; url: string }>;
|
avatarComputed: Array<{ name: string; url: string }>;
|
||||||
|
/** 原始用户描述 */
|
||||||
|
originalUserDescription: string;
|
||||||
/** 上传图片并分析 */
|
/** 上传图片并分析 */
|
||||||
uploadAndAnalyzeImage: () => Promise<void>;
|
uploadAndAnalyzeImage: () => Promise<void>;
|
||||||
/** 触发文件选择 */
|
/** 触发文件选择 */
|
||||||
@ -40,13 +42,14 @@ interface UseImageStoryService {
|
|||||||
/** 重置图片故事数据 */
|
/** 重置图片故事数据 */
|
||||||
resetImageStory: (showAnalysisState?: boolean) => void;
|
resetImageStory: (showAnalysisState?: boolean) => void;
|
||||||
setCharactersAnalysis: Dispatch<SetStateAction<CharacterAnalysis[]>>
|
setCharactersAnalysis: Dispatch<SetStateAction<CharacterAnalysis[]>>
|
||||||
|
setOriginalUserDescription: Dispatch<SetStateAction<string>>
|
||||||
}
|
}
|
||||||
|
|
||||||
export const useImageStoryServiceHook = (): UseImageStoryService => {
|
export const useImageStoryServiceHook = (): UseImageStoryService => {
|
||||||
// 基础状态
|
// 基础状态
|
||||||
const [imageStory, setImageStory] = useState<Partial<ImageStoryEntity>>({
|
const [imageStory, setImageStory] = useState<Partial<ImageStoryEntity>>({
|
||||||
imageUrl: "",
|
imageUrl: "",
|
||||||
storyType: "auto",
|
storyType: "",
|
||||||
});
|
});
|
||||||
|
|
||||||
// 图片相关状态
|
// 图片相关状态
|
||||||
@ -54,6 +57,8 @@ export const useImageStoryServiceHook = (): UseImageStoryService => {
|
|||||||
|
|
||||||
// 故事内容状态(统一管理用户输入和AI分析结果)
|
// 故事内容状态(统一管理用户输入和AI分析结果)
|
||||||
const [storyContent, setStoryContent] = useState<string>("");
|
const [storyContent, setStoryContent] = useState<string>("");
|
||||||
|
// 原始用户描述
|
||||||
|
const [originalUserDescription, setOriginalUserDescription] = useState<string>("");
|
||||||
|
|
||||||
// 分析结果状态
|
// 分析结果状态
|
||||||
/** 角色头像及名称 */
|
/** 角色头像及名称 */
|
||||||
@ -64,7 +69,7 @@ export const useImageStoryServiceHook = (): UseImageStoryService => {
|
|||||||
const [potentialGenres, setPotentialGenres] = useState<string[]>([]);
|
const [potentialGenres, setPotentialGenres] = useState<string[]>([]);
|
||||||
|
|
||||||
// 分类状态
|
// 分类状态
|
||||||
const [selectedCategory, setSelectedCategory] = useState<string>("Auto");
|
const [selectedCategory, setSelectedCategory] = useState<string>("");
|
||||||
|
|
||||||
// 流程状态
|
// 流程状态
|
||||||
const [isLoading, setIsLoading] = useState(false);
|
const [isLoading, setIsLoading] = useState(false);
|
||||||
@ -216,18 +221,17 @@ export const useImageStoryServiceHook = (): UseImageStoryService => {
|
|||||||
const uploadAndAnalyzeImage = useCallback(
|
const uploadAndAnalyzeImage = useCallback(
|
||||||
async (): Promise<void> => {
|
async (): Promise<void> => {
|
||||||
try {
|
try {
|
||||||
console.log('123123123', 123123123)
|
|
||||||
setIsLoading(true);
|
setIsLoading(true);
|
||||||
|
|
||||||
// 调用用例处理图片上传和分析
|
// 调用用例处理图片上传和分析
|
||||||
await imageStoryUseCase.handleImageUpload(activeImageUrl);
|
const newImageStory = await imageStoryUseCase.handleImageUpload(activeImageUrl);
|
||||||
|
setOriginalUserDescription(storyContent)
|
||||||
// 获取更新后的数据
|
// 获取更新后的数据
|
||||||
const updatedStory = imageStoryUseCase.storyLogline;
|
const updatedStory = imageStoryUseCase.storyLogline;
|
||||||
const updatedCharacters = imageStoryUseCase.charactersAnalysis;
|
const updatedCharacters = imageStoryUseCase.charactersAnalysis;
|
||||||
const updatedGenres = imageStoryUseCase.potentialGenres;
|
const updatedGenres = imageStoryUseCase.potentialGenres;
|
||||||
const updatedImageStory = imageStoryUseCase.imageStory;
|
const updatedImageStory = imageStoryUseCase.imageStory;
|
||||||
|
setSelectedCategory(imageStoryUseCase.potentialGenres[0]);
|
||||||
// 更新所有响应式状态
|
// 更新所有响应式状态
|
||||||
setCharactersAnalysis(updatedCharacters);
|
setCharactersAnalysis(updatedCharacters);
|
||||||
setPotentialGenres(updatedGenres);
|
setPotentialGenres(updatedGenres);
|
||||||
@ -235,18 +239,20 @@ export const useImageStoryServiceHook = (): UseImageStoryService => {
|
|||||||
|
|
||||||
// 将AI分析的故事内容直接更新到统一的故事内容字段
|
// 将AI分析的故事内容直接更新到统一的故事内容字段
|
||||||
updateStoryContent(updatedStory || "");
|
updateStoryContent(updatedStory || "");
|
||||||
setSelectedCategory("Auto");
|
|
||||||
|
|
||||||
// 标记已分析
|
// 标记已分析
|
||||||
setHasAnalyzed(true);
|
setHasAnalyzed(true);
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("图片上传分析失败:", error);
|
console.error("图片上传分析失败:", error);
|
||||||
|
setHasAnalyzed(false);
|
||||||
|
|
||||||
throw error;
|
throw error;
|
||||||
} finally {
|
} finally {
|
||||||
setIsLoading(false);
|
setIsLoading(false);
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
[activeImageUrl, imageStoryUseCase]
|
[activeImageUrl, imageStoryUseCase,storyContent,setOriginalUserDescription]
|
||||||
);
|
);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -314,8 +320,9 @@ export const useImageStoryServiceHook = (): UseImageStoryService => {
|
|||||||
(oldName: string, newName: string) => {
|
(oldName: string, newName: string) => {
|
||||||
// 更新故事内容中的角色标签
|
// 更新故事内容中的角色标签
|
||||||
setStoryContent((prev) => {
|
setStoryContent((prev) => {
|
||||||
const regex = new RegExp(`<role_name>${oldName}<\/role_name>`, "g");
|
// 匹配新的角色标签格式 <role id="C1">Dezhong Huang</role>
|
||||||
const content = prev.replace(regex, `<role_name>${newName}</role_name>`);
|
const regex = new RegExp(`<role[^>]*>${oldName}<\/role>`, "g");
|
||||||
|
const content = prev.replace(regex, `<role >${newName}</role>`);
|
||||||
imageStoryUseCase.updateStoryContent(content);
|
imageStoryUseCase.updateStoryContent(content);
|
||||||
return content;
|
return content;
|
||||||
});
|
});
|
||||||
@ -363,14 +370,15 @@ export const useImageStoryServiceHook = (): UseImageStoryService => {
|
|||||||
// 重置所有状态
|
// 重置所有状态
|
||||||
setImageStory({
|
setImageStory({
|
||||||
imageUrl: "",
|
imageUrl: "",
|
||||||
storyType: "auto",
|
storyType: "",
|
||||||
});
|
});
|
||||||
setActiveImageUrl("");
|
setActiveImageUrl("");
|
||||||
updateStoryContent("");
|
updateStoryContent("");
|
||||||
setPotentialGenres([]);
|
setPotentialGenres([]);
|
||||||
setSelectedCategory("auto");
|
setSelectedCategory("");
|
||||||
setHasAnalyzed(false);
|
setHasAnalyzed(false);
|
||||||
setIsLoading(false);
|
setIsLoading(false);
|
||||||
|
setOriginalUserDescription("");
|
||||||
}, [imageStoryUseCase]);
|
}, [imageStoryUseCase]);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -437,6 +445,7 @@ export const useImageStoryServiceHook = (): UseImageStoryService => {
|
|||||||
isLoading,
|
isLoading,
|
||||||
hasAnalyzed,
|
hasAnalyzed,
|
||||||
avatarComputed,
|
avatarComputed,
|
||||||
|
originalUserDescription,
|
||||||
setCharactersAnalysis,
|
setCharactersAnalysis,
|
||||||
uploadAndAnalyzeImage,
|
uploadAndAnalyzeImage,
|
||||||
triggerFileSelection,
|
triggerFileSelection,
|
||||||
@ -446,5 +455,6 @@ export const useImageStoryServiceHook = (): UseImageStoryService => {
|
|||||||
updateCharacterName,
|
updateCharacterName,
|
||||||
syncRoleNameToContent,
|
syncRoleNameToContent,
|
||||||
resetImageStory,
|
resetImageStory,
|
||||||
|
setOriginalUserDescription
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@ -1,6 +1,5 @@
|
|||||||
|
|
||||||
import { getUploadToken, uploadToQiniu } from "@/api/common";
|
import { getUploadToken, uploadToQiniu } from "@/api/common";
|
||||||
import { useState, useCallback } from "react";
|
import { useState, useCallback, useEffect } from "react";
|
||||||
import { ScriptEditKey } from "../usecase/ScriptEditUseCase";
|
import { ScriptEditKey } from "../usecase/ScriptEditUseCase";
|
||||||
/**
|
/**
|
||||||
* 渲染数据转换器
|
* 渲染数据转换器
|
||||||
@ -13,7 +12,7 @@ export function parseScriptBlock(
|
|||||||
key: ScriptEditKey,
|
key: ScriptEditKey,
|
||||||
headerName: string,
|
headerName: string,
|
||||||
scriptText: string,
|
scriptText: string,
|
||||||
contentType?: 'paragraph' | 'bold' | 'italic' | 'heading' | 'tag',
|
contentType?: "paragraph" | "bold" | "italic" | "heading" | "tag"
|
||||||
) {
|
) {
|
||||||
return {
|
return {
|
||||||
id: key,
|
id: key,
|
||||||
@ -27,8 +26,6 @@ export function parseScriptBlock(
|
|||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 用于上传文件到七牛云的自定义 Hook
|
* 用于上传文件到七牛云的自定义 Hook
|
||||||
* @returns {object} - 包含上传函数和加载状态
|
* @returns {object} - 包含上传函数和加载状态
|
||||||
@ -45,14 +42,17 @@ export function useUploadFile() {
|
|||||||
* @throws {Error} - 上传失败时抛出异常
|
* @throws {Error} - 上传失败时抛出异常
|
||||||
*/
|
*/
|
||||||
const uploadFile = useCallback(
|
const uploadFile = useCallback(
|
||||||
async (file: File, onProgress?: (progress: number) => void): Promise<string> => {
|
async (
|
||||||
|
file: File,
|
||||||
|
onProgress?: (progress: number) => void
|
||||||
|
): Promise<string> => {
|
||||||
try {
|
try {
|
||||||
setIsUploading(true);
|
setIsUploading(true);
|
||||||
const { token } = await getUploadToken();
|
const { token } = await getUploadToken();
|
||||||
const fileUrl = await uploadToQiniu(file, token, onProgress);
|
const fileUrl = await uploadToQiniu(file, token, onProgress);
|
||||||
return fileUrl;
|
return fileUrl;
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
console.error('文件上传失败:', err);
|
console.error("文件上传失败:", err);
|
||||||
throw err;
|
throw err;
|
||||||
} finally {
|
} finally {
|
||||||
setIsUploading(false);
|
setIsUploading(false);
|
||||||
@ -63,3 +63,65 @@ export function useUploadFile() {
|
|||||||
|
|
||||||
return { uploadFile, isUploading };
|
return { uploadFile, isUploading };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**加载文案定时变 */
|
||||||
|
export function useLoadScriptText(loading: boolean): { loadingText: string } {
|
||||||
|
// 如果loading 为true 则每五秒切换一次文本,如果变false 则停止切换,且重置文本位置
|
||||||
|
const tests = [
|
||||||
|
"loading...",
|
||||||
|
"Brainstorming initial story concepts and themes.",
|
||||||
|
"Drafting the screenplay's first outline.",
|
||||||
|
"Refining character arcs and plot points.",
|
||||||
|
"Finalizing the script with dialogue polish.",
|
||||||
|
"Creating detailed storyboards for key scenes.",
|
||||||
|
"Scouting potential filming locations.",
|
||||||
|
"Designing mood boards for visual aesthetics.",
|
||||||
|
"Casting actors to bring characters to life.",
|
||||||
|
"Scheduling production timelines and shoots.",
|
||||||
|
"Securing permits for on-location filming.",
|
||||||
|
"Building sets to match the story’s vision.",
|
||||||
|
"Designing costumes for character authenticity.",
|
||||||
|
"Planning lighting setups for each scene.",
|
||||||
|
"Renting equipment for high-quality production.",
|
||||||
|
"Rehearsing actors for seamless performances.",
|
||||||
|
"Setting up cameras for the first shot.",
|
||||||
|
"Filming establishing shots for scene context.",
|
||||||
|
"Capturing key dialogue scenes with precision.",
|
||||||
|
"Recording action sequences with dynamic angles.",
|
||||||
|
"Filming close-ups to capture emotions.",
|
||||||
|
"Wrapping principal photography on set.",
|
||||||
|
"Reviewing dailies for quality assurance.",
|
||||||
|
"Organizing raw footage for editing.",
|
||||||
|
"Editing scenes for narrative flow.",
|
||||||
|
"Adding sound effects to enhance immersion.",
|
||||||
|
"Composing the film’s musical score.",
|
||||||
|
"Mixing audio for balanced sound design.",
|
||||||
|
"Applying color grading for visual consistency.",
|
||||||
|
"Rendering visual effects for final polish.",
|
||||||
|
"Exporting the final cut for distribution.",
|
||||||
|
];
|
||||||
|
const [currentIndex, setCurrentIndex] = useState(0);
|
||||||
|
const [intervalId, setIntervalId] = useState<NodeJS.Timeout | null>(null);
|
||||||
|
useEffect(() => {
|
||||||
|
if (loading) {
|
||||||
|
const interval = setInterval(() => {
|
||||||
|
setCurrentIndex((prev) => (prev + 1) % tests.length);
|
||||||
|
}, 5000);
|
||||||
|
setIntervalId(interval);
|
||||||
|
} else {
|
||||||
|
if (intervalId) {
|
||||||
|
clearInterval(intervalId);
|
||||||
|
setIntervalId(null);
|
||||||
|
setCurrentIndex(0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return () => {
|
||||||
|
if (intervalId) {
|
||||||
|
clearInterval(intervalId);
|
||||||
|
setIntervalId(null);
|
||||||
|
setCurrentIndex(0);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
}, [loading, tests.length]);
|
||||||
|
return { loadingText: tests[currentIndex] };
|
||||||
|
}
|
||||||
|
|||||||
@ -8,10 +8,13 @@ import { MovieStartDTO, CharacterAnalysis } from "@/api/DTO/movie_start_dto";
|
|||||||
*/
|
*/
|
||||||
export class ImageStoryUseCase {
|
export class ImageStoryUseCase {
|
||||||
/** 当前图片故事数据 */
|
/** 当前图片故事数据 */
|
||||||
imageStory: Partial<ImageStoryEntity> = {
|
imageStory: ImageStoryEntity = {
|
||||||
|
id: "",
|
||||||
|
imageAnalysis: "",
|
||||||
|
roleImage: [],
|
||||||
imageUrl: "",
|
imageUrl: "",
|
||||||
imageStory: "",
|
imageStory: "",
|
||||||
storyType: "Auto",
|
storyType: "",
|
||||||
};
|
};
|
||||||
|
|
||||||
/** 故事梗概 */
|
/** 故事梗概 */
|
||||||
@ -28,7 +31,6 @@ export class ImageStoryUseCase {
|
|||||||
|
|
||||||
/** 是否正在上传 */
|
/** 是否正在上传 */
|
||||||
isUploading: boolean = false;
|
isUploading: boolean = false;
|
||||||
|
|
||||||
constructor() {}
|
constructor() {}
|
||||||
|
|
||||||
|
|
||||||
@ -46,9 +48,12 @@ export class ImageStoryUseCase {
|
|||||||
*/
|
*/
|
||||||
resetImageStory(): void {
|
resetImageStory(): void {
|
||||||
this.imageStory = {
|
this.imageStory = {
|
||||||
|
id: "",
|
||||||
|
imageAnalysis: "",
|
||||||
|
roleImage: [],
|
||||||
imageUrl: "",
|
imageUrl: "",
|
||||||
imageStory: "",
|
imageStory: "",
|
||||||
storyType: "Auto",
|
storyType: "",
|
||||||
};
|
};
|
||||||
this.storyLogline = "";
|
this.storyLogline = "";
|
||||||
this.charactersAnalysis = [];
|
this.charactersAnalysis = [];
|
||||||
@ -62,7 +67,7 @@ export class ImageStoryUseCase {
|
|||||||
* @param {string} imageUrl - 已上传的图片URL
|
* @param {string} imageUrl - 已上传的图片URL
|
||||||
* @returns {Promise<void>}
|
* @returns {Promise<void>}
|
||||||
*/
|
*/
|
||||||
async handleImageUpload(imageUrl: string): Promise<void> {
|
async handleImageUpload(imageUrl: string) {
|
||||||
try {
|
try {
|
||||||
this.isUploading = false; // 图片已上传,设置上传状态为false
|
this.isUploading = false; // 图片已上传,设置上传状态为false
|
||||||
this.isAnalyzing = true;
|
this.isAnalyzing = true;
|
||||||
@ -71,7 +76,7 @@ export class ImageStoryUseCase {
|
|||||||
this.setImageStory({ imageUrl });
|
this.setImageStory({ imageUrl });
|
||||||
|
|
||||||
// 调用AI分析接口
|
// 调用AI分析接口
|
||||||
await this.analyzeImageWithAI();
|
return await this.analyzeImageWithAI();
|
||||||
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error("图片分析失败:", error);
|
console.error("图片分析失败:", error);
|
||||||
@ -87,7 +92,7 @@ export class ImageStoryUseCase {
|
|||||||
* 使用AI分析图片
|
* 使用AI分析图片
|
||||||
* @returns {Promise<void>}
|
* @returns {Promise<void>}
|
||||||
*/
|
*/
|
||||||
async analyzeImageWithAI(): Promise<void> {
|
async analyzeImageWithAI() {
|
||||||
console.log('this.imageStory.imageUrl', this.imageStory.imageUrl)
|
console.log('this.imageStory.imageUrl', this.imageStory.imageUrl)
|
||||||
try {
|
try {
|
||||||
// 调用AI分析接口
|
// 调用AI分析接口
|
||||||
@ -102,6 +107,7 @@ export class ImageStoryUseCase {
|
|||||||
|
|
||||||
// 组合成ImageStoryEntity
|
// 组合成ImageStoryEntity
|
||||||
this.composeImageStoryEntity(response.data);
|
this.composeImageStoryEntity(response.data);
|
||||||
|
return this.imageStory;
|
||||||
} else {
|
} else {
|
||||||
throw new Error("AI分析失败");
|
throw new Error("AI分析失败");
|
||||||
}
|
}
|
||||||
@ -140,13 +146,15 @@ export class ImageStoryUseCase {
|
|||||||
y: character.region?.y || 0,
|
y: character.region?.y || 0,
|
||||||
width: character.region?.width || 0,
|
width: character.region?.width || 0,
|
||||||
height: character.region?.height || 0,
|
height: character.region?.height || 0,
|
||||||
}
|
},
|
||||||
|
|
||||||
})) || [];
|
})) || [];
|
||||||
|
|
||||||
// 更新ImageStoryEntity
|
// 更新ImageStoryEntity
|
||||||
this.setImageStory({
|
this.setImageStory({
|
||||||
|
...this.imageStory,
|
||||||
imageAnalysis: data.story_logline || "",
|
imageAnalysis: data.story_logline || "",
|
||||||
storyType: "Auto", // 使用第一个分类作为故事类型
|
storyType: data.potential_genres[0] || "", // 使用第一个分类作为故事类型
|
||||||
roleImage,
|
roleImage,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@ -35,6 +35,7 @@ import StarterKit from "@tiptap/starter-kit";
|
|||||||
import { HighlightTextExtension } from "@/components/ui/main-editor/HighlightText";
|
import { HighlightTextExtension } from "@/components/ui/main-editor/HighlightText";
|
||||||
import Placeholder from "@tiptap/extension-placeholder";
|
import Placeholder from "@tiptap/extension-placeholder";
|
||||||
import { createMovieProjectV1 } from "@/api/video_flow";
|
import { createMovieProjectV1 } from "@/api/video_flow";
|
||||||
|
import { useLoadScriptText } from "@/app/service/domain/service";
|
||||||
|
|
||||||
// 自定义音频播放器样式
|
// 自定义音频播放器样式
|
||||||
const customAudioPlayerStyles = `
|
const customAudioPlayerStyles = `
|
||||||
@ -914,10 +915,11 @@ const RoleHighlightEditor = ({
|
|||||||
to < doc.content.size
|
to < doc.content.size
|
||||||
? doc.textBetween(to, Math.min(doc.content.size, to + 50))
|
? doc.textBetween(to, Math.min(doc.content.size, to + 50))
|
||||||
: "";
|
: "";
|
||||||
// TODO role id 的结构
|
// 匹配新的角色标签格式 <role id="C1">Dezhong Huang</role>
|
||||||
const beforeMatch = textBefore.match(/<role_name>[^<]*$/);
|
const beforeMatch = textBefore.match(/<role[^>]*>[^<]*$/);
|
||||||
const afterMatch = textAfter.match(/^[^>]*<\/role_name>/);
|
const afterMatch = textAfter.match(/^[^>]*<\/role>/);
|
||||||
|
|
||||||
|
// 如果光标在角色标签内,阻止输入(只允许删除操作)
|
||||||
if (beforeMatch || afterMatch) {
|
if (beforeMatch || afterMatch) {
|
||||||
if (event.key !== "Backspace" && event.key !== "Delete") {
|
if (event.key !== "Backspace" && event.key !== "Delete") {
|
||||||
event.preventDefault();
|
event.preventDefault();
|
||||||
@ -938,9 +940,9 @@ const RoleHighlightEditor = ({
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 将带标签的内容转换为高亮显示
|
// 将带标签的内容转换为高亮显示(支持新的角色标签格式)
|
||||||
const htmlContent = content.replace(
|
const htmlContent = content.replace(
|
||||||
/<role_name>([^<]+)<\/role_name>/g,
|
/<role[^>]*>([^<]+)<\/role>/g,
|
||||||
'<highlight-text type="role" text="$1" color="blue">$1</highlight-text>'
|
'<highlight-text type="role" text="$1" color="blue">$1</highlight-text>'
|
||||||
);
|
);
|
||||||
editor.commands.setContent(htmlContent, { emitUpdate: false });
|
editor.commands.setContent(htmlContent, { emitUpdate: false });
|
||||||
@ -1028,8 +1030,9 @@ const PhotoStoryModal = ({
|
|||||||
avatarComputed,
|
avatarComputed,
|
||||||
uploadAndAnalyzeImage,
|
uploadAndAnalyzeImage,
|
||||||
setCharactersAnalysis,
|
setCharactersAnalysis,
|
||||||
|
originalUserDescription
|
||||||
} = useImageStoryServiceHook();
|
} = useImageStoryServiceHook();
|
||||||
|
const { loadingText } = useLoadScriptText(isLoading);
|
||||||
// 重置状态
|
// 重置状态
|
||||||
const handleClose = () => {
|
const handleClose = () => {
|
||||||
resetImageStory();
|
resetImageStory();
|
||||||
@ -1068,7 +1071,7 @@ const PhotoStoryModal = ({
|
|||||||
</div>
|
</div>
|
||||||
}
|
}
|
||||||
>
|
>
|
||||||
<Spin spinning={isLoading} tip="Processing...">
|
<Spin spinning={isLoading} tip={loadingText}>
|
||||||
<div className="rounded-2xl">
|
<div className="rounded-2xl">
|
||||||
{/* 弹窗头部 */}
|
{/* 弹窗头部 */}
|
||||||
<div className="flex items-center gap-3 p-2 border-b border-white/[0.1]">
|
<div className="flex items-center gap-3 p-2 border-b border-white/[0.1]">
|
||||||
@ -1083,7 +1086,7 @@ const PhotoStoryModal = ({
|
|||||||
<div className="flex-shrink-0">
|
<div className="flex-shrink-0">
|
||||||
<div
|
<div
|
||||||
data-alt="image-upload-area"
|
data-alt="image-upload-area"
|
||||||
className={`w-20 h-20 rounded-lg flex flex-col items-center justify-center transition-all duration-300 cursor-pointer ${
|
className={`w-24 h-24 rounded-lg flex flex-col items-center justify-center transition-all duration-300 cursor-pointer ${
|
||||||
activeImageUrl
|
activeImageUrl
|
||||||
? "border-2 border-white/20 bg-white/[0.05]"
|
? "border-2 border-white/20 bg-white/[0.05]"
|
||||||
: "border-2 border-dashed border-white/20 bg-white/[0.02] hover:border-white/40 hover:bg-white/[0.05] hover:scale-105"
|
: "border-2 border-dashed border-white/20 bg-white/[0.02] hover:border-white/40 hover:bg-white/[0.05] hover:scale-105"
|
||||||
@ -1154,8 +1157,12 @@ const PhotoStoryModal = ({
|
|||||||
);
|
);
|
||||||
return updatedCharacters;
|
return updatedCharacters;
|
||||||
});
|
});
|
||||||
// 从故事内容中删除该角色名称
|
// 从故事内容中删除该角色的所有标签和引用
|
||||||
const updatedStory = storyContent
|
const updatedStory = storyContent
|
||||||
|
.replace(
|
||||||
|
new RegExp(`<role[^>]*>${avatar.name}<\/role>`, "g"),
|
||||||
|
""
|
||||||
|
)
|
||||||
.replace(
|
.replace(
|
||||||
new RegExp(`\\b${avatar.name}\\b`, "g"),
|
new RegExp(`\\b${avatar.name}\\b`, "g"),
|
||||||
""
|
""
|
||||||
@ -1164,7 +1171,6 @@ const PhotoStoryModal = ({
|
|||||||
.trim();
|
.trim();
|
||||||
// 更新状态
|
// 更新状态
|
||||||
updateStoryContent(updatedStory);
|
updateStoryContent(updatedStory);
|
||||||
// 注意:这里需要直接更新 charactersAnalysis,但 hook 中没有提供 setter
|
|
||||||
}}
|
}}
|
||||||
className="absolute top-1 right-1 w-4 h-4 bg-black/[0.05] border border-black/[0.1] text-white rounded-full flex items-center justify-center transition-colors opacity-0 group-hover:opacity-100 z-10"
|
className="absolute top-1 right-1 w-4 h-4 bg-black/[0.05] border border-black/[0.1] text-white rounded-full flex items-center justify-center transition-colors opacity-0 group-hover:opacity-100 z-10"
|
||||||
>
|
>
|
||||||
@ -1196,7 +1202,7 @@ const PhotoStoryModal = ({
|
|||||||
{hasAnalyzed && potentialGenres.length > 0 && (
|
{hasAnalyzed && potentialGenres.length > 0 && (
|
||||||
<div className="flex-shrink-0 animate-in fade-in-0 slide-in-from-right-4 duration-300">
|
<div className="flex-shrink-0 animate-in fade-in-0 slide-in-from-right-4 duration-300">
|
||||||
<div className="flex gap-2">
|
<div className="flex gap-2">
|
||||||
{["Auto", ...potentialGenres].map((genre) => (
|
{[ ...potentialGenres].map((genre) => (
|
||||||
<button
|
<button
|
||||||
key={genre}
|
key={genre}
|
||||||
onClick={() => updateStoryType(genre)}
|
onClick={() => updateStoryType(genre)}
|
||||||
@ -1213,6 +1219,11 @@ const PhotoStoryModal = ({
|
|||||||
</div>
|
</div>
|
||||||
)}
|
)}
|
||||||
</div>
|
</div>
|
||||||
|
{/* 原始用户描述的展示 */}
|
||||||
|
{originalUserDescription && (
|
||||||
|
<div className="mt-2 text-sm text-white/30 italic">Your Provided Text:{originalUserDescription}</div>
|
||||||
|
)}
|
||||||
|
|
||||||
<div className="flex items-start gap-4 mt-2 relative">
|
<div className="flex items-start gap-4 mt-2 relative">
|
||||||
{/* 文本输入框 */}
|
{/* 文本输入框 */}
|
||||||
<RoleHighlightEditor
|
<RoleHighlightEditor
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user