人生轨迹功能完善
This commit is contained in:
@@ -98,7 +98,9 @@ const transformToBackendFormat = (frontendData) => {
|
||||
style,
|
||||
length,
|
||||
content,
|
||||
isSelected
|
||||
isSelected,
|
||||
character,
|
||||
events
|
||||
} = frontendData;
|
||||
|
||||
// 解析内容生成标题和各部分
|
||||
@@ -127,6 +129,55 @@ const transformToBackendFormat = (frontendData) => {
|
||||
});
|
||||
}
|
||||
|
||||
// 格式化角色信息
|
||||
let characterInfo = '';
|
||||
if (character) {
|
||||
const parts = [
|
||||
`姓名:${character.nickname || '未设置'}`,
|
||||
`性别:${character.gender || '未设置'}`,
|
||||
`MBTI:${character.mbti || '未设置'}`,
|
||||
`星座:${character.zodiac || '未设置'}`,
|
||||
`职业:${character.profession || '未设置'}`,
|
||||
`兴趣爱好:${character.hobbies?.join(',') || '无'}`
|
||||
];
|
||||
|
||||
if (character.future) {
|
||||
if (character.future.vision) parts.push(`未来愿景:${character.future.vision}`);
|
||||
if (character.future.ideal) parts.push(`理想生活:${character.future.ideal}`);
|
||||
}
|
||||
|
||||
characterInfo = parts.join('\n');
|
||||
}
|
||||
|
||||
// 格式化过往经历
|
||||
let lifeEventsSummary = '';
|
||||
const eventParts = [];
|
||||
|
||||
// 1. 核心记忆 (Childhood, Joy, Low from character data)
|
||||
if (character) {
|
||||
if (character.childhood?.text) {
|
||||
eventParts.push(`【童年记忆】(${character.childhood.date || '未知时间'}): ${character.childhood.text}`);
|
||||
}
|
||||
if (character.joy?.text) {
|
||||
eventParts.push(`【高光时刻】(${character.joy.date || '未知时间'}): ${character.joy.text}`);
|
||||
}
|
||||
if (character.low?.text) {
|
||||
eventParts.push(`【至暗时刻】(${character.low.date || '未知时间'}): ${character.low.text}`);
|
||||
}
|
||||
}
|
||||
|
||||
// 2. 详细人生事件
|
||||
if (events && Array.isArray(events)) {
|
||||
events.forEach(e => {
|
||||
const dateStr = e.time || e.eventDate || '未知时间';
|
||||
const titleStr = e.title || '无标题';
|
||||
const contentStr = e.content || '';
|
||||
eventParts.push(`【人生事件】(${dateStr}) ${titleStr}${contentStr ? ': ' + contentStr : ''}`);
|
||||
});
|
||||
}
|
||||
|
||||
lifeEventsSummary = eventParts.join('\n');
|
||||
|
||||
return {
|
||||
id,
|
||||
title,
|
||||
@@ -138,7 +189,9 @@ const transformToBackendFormat = (frontendData) => {
|
||||
plotClimax,
|
||||
plotEnding,
|
||||
plotJson: content ? { fullContent: content } : null,
|
||||
isSelected
|
||||
isSelected,
|
||||
characterInfo,
|
||||
lifeEventsSummary
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
@@ -2,7 +2,6 @@ import { useState, useEffect } from 'react';
|
||||
import { UserCog, PenTool, Sparkles, BookOpen, Loader2 } from 'lucide-react';
|
||||
import { GlassCard, GlassButton, GlassInput, GlassSelect } from '../components/ui';
|
||||
import useStore from '../store/useStore';
|
||||
import { generateEpicScript } from '../services/ai';
|
||||
import { scriptStyles, scriptLengths } from '../utils/constants';
|
||||
|
||||
/**
|
||||
@@ -48,12 +47,15 @@ const ScriptView = ({ onOpenProfile }) => {
|
||||
setIsLoading(true);
|
||||
|
||||
try {
|
||||
const content = await generateEpicScript(
|
||||
{ theme, style, length, character: registrationData },
|
||||
lifeEvents
|
||||
);
|
||||
// 直接调用后端创建接口,由后端调用AI生成
|
||||
await addScript({
|
||||
theme,
|
||||
style,
|
||||
length,
|
||||
character: registrationData,
|
||||
events: lifeEvents
|
||||
});
|
||||
|
||||
addScript({ theme, style, length, content });
|
||||
setTheme('');
|
||||
} catch (error) {
|
||||
console.error('Failed to generate script:', error);
|
||||
|
||||
@@ -3,7 +3,76 @@ import { Plus, Wind, Sparkles } from 'lucide-react';
|
||||
import { GlassCard, GlassButton, GlassInput, GlassTextarea } from '../components/ui';
|
||||
import Modal from '../components/Modal';
|
||||
import useStore from '../store/useStore';
|
||||
import { analyzeLifeEvent } from '../services/ai';
|
||||
|
||||
/**
|
||||
* 格式化 AI 反馈内容的组件
|
||||
*/
|
||||
const FeedbackContent = ({ content }) => {
|
||||
if (!content) return null;
|
||||
|
||||
// 检查是否为结构化格式 (包含分隔符 --- 和标题标识 ####)
|
||||
const isStructured = content.includes('---') && content.includes('####');
|
||||
|
||||
if (!isStructured) {
|
||||
return (
|
||||
<p className="text-xs italic text-white/50 leading-loose whitespace-pre-wrap">
|
||||
{content}
|
||||
</p>
|
||||
);
|
||||
}
|
||||
|
||||
// 解析结构化内容
|
||||
const sections = content.split('---')
|
||||
.map(s => s.trim())
|
||||
.filter(s => s && s.length > 0);
|
||||
|
||||
return (
|
||||
<div className="space-y-5 mt-2">
|
||||
{sections.map((section, index) => {
|
||||
// 移除 #### 前缀
|
||||
const cleanSection = section.replace(/^####\s*/, '');
|
||||
|
||||
// 提取标题 (通常在 【】 中)
|
||||
const titleMatch = cleanSection.match(/【(.*?)】/);
|
||||
const title = titleMatch ? titleMatch[1] : '';
|
||||
|
||||
// 提取正文
|
||||
let body = cleanSection;
|
||||
if (titleMatch) {
|
||||
body = cleanSection.replace(titleMatch[0], '').trim();
|
||||
}
|
||||
|
||||
if (!title && !body) return null;
|
||||
|
||||
return (
|
||||
<div key={index} className="text-xs leading-relaxed">
|
||||
{title && (
|
||||
<h5 className="text-orange-100 font-bold mb-2 flex items-center gap-2 text-[11px] tracking-wide">
|
||||
{title}
|
||||
</h5>
|
||||
)}
|
||||
<div className="text-white/60 pl-3 border-l-2 border-orange-200/10 space-y-1">
|
||||
{body.split('\n').map((line, i) => {
|
||||
const trimmedLine = line.trim();
|
||||
if (!trimmedLine) return null;
|
||||
// 简单的列表项处理
|
||||
if (trimmedLine.startsWith('*') || trimmedLine.startsWith('-')) {
|
||||
return (
|
||||
<div key={i} className="flex gap-2 pl-1">
|
||||
<span className="text-orange-200/40">•</span>
|
||||
<span>{trimmedLine.substring(1).trim()}</span>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
return <p key={i}>{trimmedLine}</p>;
|
||||
})}
|
||||
</div>
|
||||
</div>
|
||||
);
|
||||
})}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
||||
/**
|
||||
* TimelineView 组件
|
||||
@@ -42,20 +111,16 @@ const TimelineView = () => {
|
||||
setIsLoading(true);
|
||||
|
||||
try {
|
||||
// 调用 AI 分析
|
||||
const aiFeedback = await analyzeLifeEvent(eventForm);
|
||||
|
||||
// 添加事件
|
||||
addLifeEvent({
|
||||
...eventForm,
|
||||
aiFeedback
|
||||
// 直接调用后端添加事件,由后端调用AI进行疗愈分析
|
||||
await addLifeEvent({
|
||||
...eventForm
|
||||
});
|
||||
|
||||
// 重置表单并关闭模态框
|
||||
setEventForm({ title: '', time: '', content: '' });
|
||||
setIsModalOpen(false);
|
||||
} catch (error) {
|
||||
console.error('Failed to analyze event:', error);
|
||||
console.error('Failed to add event:', error);
|
||||
} finally {
|
||||
setIsLoading(false);
|
||||
}
|
||||
@@ -124,15 +189,13 @@ const TimelineView = () => {
|
||||
{/* AI 反馈区域 - 仅在有反馈时显示 */}
|
||||
{event.aiFeedback && (
|
||||
<div className="ai-glow-card p-5 rounded-2xl bg-orange-200/[0.02] border border-orange-200/5">
|
||||
<div className="flex items-center gap-2 mb-2">
|
||||
<div className="flex items-center gap-2 mb-4">
|
||||
<Sparkles className="w-3 h-3 text-orange-200" />
|
||||
<span className="text-[9px] uppercase tracking-[0.2em] text-orange-200/60 font-bold">
|
||||
引路人洞察
|
||||
</span>
|
||||
</div>
|
||||
<p className="text-xs italic text-white/50 leading-loose">
|
||||
{event.aiFeedback}
|
||||
</p>
|
||||
<FeedbackContent content={event.aiFeedback} />
|
||||
</div>
|
||||
)}
|
||||
</GlassCard>
|
||||
|
||||
Reference in New Issue
Block a user