From a3bd20c31ed62e6ad6f32b767b1622ae767c7013 Mon Sep 17 00:00:00 2001 From: patdelphi Date: Sat, 23 Aug 2025 12:40:31 +0800 Subject: [PATCH] Fix AI interpretation button issues and animation conflicts - Fix AI interpretation recordId parameter passing for all analysis types - Resolve double border issue in AI interpretation results display - Fix animation conflicts causing visual duplication - Update component interfaces to support recordId parameter - Add AI interpretation status indicators in history page - Disable conflicting animations in AI result containers --- src/components/AnalysisResultDisplay.tsx | 16 +- src/components/CompleteBaziAnalysis.tsx | 4 +- src/components/CompleteYijingAnalysis.tsx | 5 +- src/components/CompleteZiweiAnalysis.tsx | 4 +- src/components/ui/AIInterpretationButton.tsx | 21 +- src/components/ui/YijingQuestionSelector.tsx | 278 +++++++++++++++++++ src/pages/AnalysisPage.tsx | 51 +++- src/pages/HistoryPage.tsx | 30 +- 8 files changed, 374 insertions(+), 35 deletions(-) create mode 100644 src/components/ui/YijingQuestionSelector.tsx diff --git a/src/components/AnalysisResultDisplay.tsx b/src/components/AnalysisResultDisplay.tsx index d0ee29c..36ddb54 100644 --- a/src/components/AnalysisResultDisplay.tsx +++ b/src/components/AnalysisResultDisplay.tsx @@ -17,6 +17,7 @@ interface AnalysisResultDisplayProps { userId?: string; divinationMethod?: string; preAnalysisData?: any; // 预先分析的数据,用于历史记录 + recordId?: number; // 历史记录ID,用于AI解读 } const AnalysisResultDisplay: React.FC = ({ @@ -26,7 +27,8 @@ const AnalysisResultDisplay: React.FC = ({ question, userId, divinationMethod, - preAnalysisData + preAnalysisData, + recordId }) => { // 安全地获取数据的辅助函数 const safeGet = (obj: any, path: string, defaultValue: any = '暂无数据') => { @@ -62,7 +64,7 @@ const AnalysisResultDisplay: React.FC = ({ const renderBaziAnalysis = () => { // 如果有 birthDate,使用新的 CompleteBaziAnalysis 组件 if (birthDate) { - return ; + return ; } // 如果有分析结果但没有 birthDate,尝试从结果中提取出生信息 if (analysisResult && analysisResult.data) { @@ -74,7 +76,7 @@ const AnalysisResultDisplay: React.FC = ({ name: basicInfo.personal_data.name || '', gender: basicInfo.personal_data.gender === '男性' ? 'male' : 'female' }; - return ; + return ; } } // 如果没有足够的数据,返回错误提示 @@ -89,7 +91,7 @@ const AnalysisResultDisplay: React.FC = ({ const renderZiweiAnalysis = () => { // 如果有 birthDate,使用新的 CompleteZiweiAnalysis 组件 if (birthDate) { - return ; + return ; } // 如果有分析结果但没有 birthDate,尝试从结果中提取出生信息 if (analysisResult && analysisResult.data) { @@ -101,7 +103,7 @@ const AnalysisResultDisplay: React.FC = ({ name: basicInfo.personal_data.name || '', gender: basicInfo.personal_data.gender === '男性' ? 'male' : 'female' }; - return ; + return ; } } @@ -261,6 +263,8 @@ const AnalysisResultDisplay: React.FC = ({ question={question} userId={userId} divinationMethod={divinationMethod} + analysisData={preAnalysisData} + recordId={recordId} /> ); } @@ -274,6 +278,8 @@ const AnalysisResultDisplay: React.FC = ({ question={basicInfo.divination_data.question || '综合运势如何?'} userId={userId || 'user123'} divinationMethod={divinationMethod || 'time'} + analysisData={preAnalysisData} + recordId={recordId} /> ); } diff --git a/src/components/CompleteBaziAnalysis.tsx b/src/components/CompleteBaziAnalysis.tsx index 06264be..4198a87 100644 --- a/src/components/CompleteBaziAnalysis.tsx +++ b/src/components/CompleteBaziAnalysis.tsx @@ -16,9 +16,10 @@ interface CompleteBaziAnalysisProps { gender?: string; }; analysisData?: any; // 可选的预先分析的数据 + recordId?: number; // 历史记录ID,用于AI解读 } -const CompleteBaziAnalysis: React.FC = ({ birthDate, analysisData: propAnalysisData }) => { +const CompleteBaziAnalysis: React.FC = ({ birthDate, analysisData: propAnalysisData, recordId }) => { const [isLoading, setIsLoading] = useState(!propAnalysisData); const [error, setError] = useState(null); const [analysisData, setAnalysisData] = useState(propAnalysisData || null); @@ -286,6 +287,7 @@ const CompleteBaziAnalysis: React.FC = ({ birthDate, setShowAIConfig(true)} /> diff --git a/src/components/CompleteYijingAnalysis.tsx b/src/components/CompleteYijingAnalysis.tsx index 3a60f9b..612735e 100644 --- a/src/components/CompleteYijingAnalysis.tsx +++ b/src/components/CompleteYijingAnalysis.tsx @@ -12,13 +12,15 @@ interface CompleteYijingAnalysisProps { userId?: string; divinationMethod?: string; analysisData?: any; // 可选的预先分析的数据 + recordId?: number; // 历史记录ID,用于AI解读 } const CompleteYijingAnalysis: React.FC = ({ question, userId = 'user123', divinationMethod = 'time', - analysisData: propAnalysisData + analysisData: propAnalysisData, + recordId }) => { const [isLoading, setIsLoading] = useState(!propAnalysisData); const [error, setError] = useState(null); @@ -274,6 +276,7 @@ const CompleteYijingAnalysis: React.FC = ({ setShowAIConfig(true)} /> diff --git a/src/components/CompleteZiweiAnalysis.tsx b/src/components/CompleteZiweiAnalysis.tsx index 304ce2d..d5a0d56 100644 --- a/src/components/CompleteZiweiAnalysis.tsx +++ b/src/components/CompleteZiweiAnalysis.tsx @@ -19,9 +19,10 @@ interface CompleteZiweiAnalysisProps { gender?: string; }; analysisData?: any; // 可选的预先分析的数据 + recordId?: number; // 历史记录ID,用于AI解读 } -const CompleteZiweiAnalysis: React.FC = ({ birthDate, analysisData: propAnalysisData }) => { +const CompleteZiweiAnalysis: React.FC = ({ birthDate, analysisData: propAnalysisData, recordId }) => { const [isLoading, setIsLoading] = useState(!propAnalysisData); const [error, setError] = useState(null); const [analysisData, setAnalysisData] = useState(propAnalysisData || null); @@ -589,6 +590,7 @@ const CompleteZiweiAnalysis: React.FC = ({ birthDate setShowAIConfig(true)} /> diff --git a/src/components/ui/AIInterpretationButton.tsx b/src/components/ui/AIInterpretationButton.tsx index 855bbd0..81969de 100644 --- a/src/components/ui/AIInterpretationButton.tsx +++ b/src/components/ui/AIInterpretationButton.tsx @@ -277,19 +277,19 @@ const AIInterpretationButton: React.FC = ({ {/* AI解读结果显示 */} {(interpretation || streamingContent) && showResult && ( - - - +
+
+
{isLoading ? ( ) : ( )} - AI智能解读 - {getAnalysisTypeName(analysisType)} + AI智能解读 - {getAnalysisTypeName(analysisType)} {isLoading && streamingContent && ( 正在生成中... )} - +
{interpretation && (
解读时间: {new Date(interpretation.timestamp).toLocaleString('zh-CN')} @@ -297,8 +297,8 @@ const AIInterpretationButton: React.FC = ({ {interpretation.tokensUsed && 消耗Token: {interpretation.tokensUsed}}
)} - - +
+
{interpretation && !interpretation.success ? (
@@ -371,11 +371,10 @@ const AIInterpretationButton: React.FC = ({ {isLoading && streamingContent && ( - )} -
+ )}
)} - - +
+ )} ); diff --git a/src/components/ui/YijingQuestionSelector.tsx b/src/components/ui/YijingQuestionSelector.tsx new file mode 100644 index 0000000..54f2e39 --- /dev/null +++ b/src/components/ui/YijingQuestionSelector.tsx @@ -0,0 +1,278 @@ +import React, { useState, useEffect } from 'react'; +import { ChineseInput } from './ChineseInput'; +import { ChineseSelect } from './ChineseSelect'; +import { ChineseButton } from './ChineseButton'; +import { Lightbulb, RefreshCw } from 'lucide-react'; + +interface YijingQuestionSelectorProps { + value: string; + onChange: (value: string) => void; + className?: string; +} + +// 问题分类和预设问题数据 +const questionCategories = { + career: { + name: '事业发展', + icon: '💼', + questions: [ + '我的事业发展前景如何?', + '现在是否适合换工作?', + '我应该选择哪个职业方向?', + '创业的时机是否成熟?', + '如何提升我的职场竞争力?', + '我的工作能力能否得到认可?', + '是否应该接受这个工作机会?', + '我的事业何时能有突破?' + ] + }, + love: { + name: '感情婚姻', + icon: '💕', + questions: [ + '我的感情运势如何?', + '现在的恋情能否修成正果?', + '我何时能遇到真爱?', + '这段感情是否值得继续?', + '如何改善我们的关系?', + '我应该主动表白吗?', + '婚姻生活会幸福吗?', + '如何处理感情中的矛盾?' + ] + }, + wealth: { + name: '财运投资', + icon: '💰', + questions: [ + '我的财运发展如何?', + '这项投资是否明智?', + '如何改善我的财务状况?', + '现在适合创业吗?', + '我的理财方向是否正确?', + '何时能实现财务自由?', + '这个商业机会值得把握吗?', + '如何增加我的收入来源?' + ] + }, + health: { + name: '健康养生', + icon: '🏥', + questions: [ + '我的健康状况如何?', + '如何改善我的身体状况?', + '这个治疗方案是否有效?', + '我需要注意哪些健康问题?', + '如何调理我的身心状态?', + '什么运动最适合我?', + '我的饮食习惯需要调整吗?', + '如何预防疾病的发生?' + ] + }, + study: { + name: '学业考试', + icon: '📚', + questions: [ + '我的学习成绩能否提升?', + '这次考试能否顺利通过?', + '应该选择哪个专业方向?', + '如何提高学习效率?', + '是否应该继续深造?', + '我的学习方法是否正确?', + '何时是最佳的考试时机?', + '如何克服学习中的困难?' + ] + }, + family: { + name: '家庭生活', + icon: '🏠', + questions: [ + '我的家庭关系如何?', + '如何处理家庭矛盾?', + '子女教育应该注意什么?', + '如何改善与父母的关系?', + '家庭财务规划是否合理?', + '搬家的时机是否合适?', + '如何营造和谐的家庭氛围?', + '家人的健康状况如何?' + ] + }, + general: { + name: '综合运势', + icon: '🔮', + questions: [ + '我的整体运势如何?', + '近期需要注意什么?', + '如何把握人生机遇?', + '我的人生方向是否正确?', + '如何化解当前的困境?', + '什么时候运势会好转?', + '我应该如何规划未来?', + '如何提升我的整体运势?' + ] + } +}; + +export const YijingQuestionSelector: React.FC = ({ + value, + onChange, + className +}) => { + const [selectedCategory, setSelectedCategory] = useState(''); + const [selectedQuestion, setSelectedQuestion] = useState(''); + const [showPresets, setShowPresets] = useState(true); + + // 分类选项 + const categoryOptions = Object.entries(questionCategories).map(([key, category]) => ({ + value: key, + label: `${category.icon} ${category.name}` + })); + + // 当选择分类时,更新问题选项 + const questionOptions = selectedCategory && questionCategories[selectedCategory as keyof typeof questionCategories] + ? questionCategories[selectedCategory as keyof typeof questionCategories].questions.map((question, index) => ({ + value: question, + label: question + })) + : []; + + // 处理分类选择 + const handleCategoryChange = (category: string) => { + setSelectedCategory(category); + setSelectedQuestion(''); + // 选择分类后自动显示预设问题 + if (category) { + setShowPresets(true); + } + }; + + // 处理预设问题选择 + const handleQuestionSelect = (question: string) => { + setSelectedQuestion(question); + onChange(question); + }; + + // 随机选择问题 + const handleRandomQuestion = () => { + const allQuestions = Object.values(questionCategories).flatMap(category => category.questions); + const randomQuestion = allQuestions[Math.floor(Math.random() * allQuestions.length)]; + onChange(randomQuestion); + setSelectedQuestion(randomQuestion); + + // 找到对应的分类 + const categoryKey = Object.entries(questionCategories).find(([_, category]) => + category.questions.includes(randomQuestion) + )?.[0]; + if (categoryKey) { + setSelectedCategory(categoryKey); + } + }; + + // 切换预设问题显示 + const togglePresets = () => { + setShowPresets(!showPresets); + }; + + return ( +
+ {/* 预设问题选择区域 */} +
+ {/* 控制按钮 */} +
+ + + {showPresets ? '隐藏预设问题' : '选择预设问题'} + + + + + 随机问题 + +
+ + {/* 预设问题选择界面 */} + {showPresets && ( +
+

+ 🎯 + 选择问题类别和预设问题 +

+ + {/* 分类选择 */} +
+ handleCategoryChange(e.target.value)} + options={[ + { value: '', label: '请选择问题类别' }, + ...categoryOptions + ]} +variant="default" + className="mb-3 [&_select]:!bg-blue-50 [&_select]:!border-blue-200 [&_select:hover]:!bg-blue-100 [&_select:focus]:!bg-white [&_select:focus]:!border-blue-500 [&_select:focus]:!ring-blue-500/20" + /> +
+ + {/* 预设问题选择 */} + {selectedCategory && questionOptions.length > 0 && ( +
+ handleQuestionSelect(e.target.value)} + options={[ + { value: '', label: '请选择预设问题' }, + ...questionOptions + ]} + variant="filled" + /> +
+ )} + + {/* 快速选择按钮 */} + {selectedCategory && questionOptions.length > 0 && ( +
+

或点击快速选择:

+
+ {questionOptions.slice(0, 6).map((option, index) => ( + + ))} +
+
+ )} +
+ )} +
+ + {/* 主要问题输入框 */} + onChange(e.target.value)} + placeholder="请输入您希望占卜的具体问题,或选择上方预设问题" + required + variant="filled" + helperText="💡 提示:问题越具体,占卜结果越准确。您可以使用预设问题或自行输入。" + /> + + +
+ ); +}; + +export default YijingQuestionSelector; \ No newline at end of file diff --git a/src/pages/AnalysisPage.tsx b/src/pages/AnalysisPage.tsx index 2447c18..96c1902 100644 --- a/src/pages/AnalysisPage.tsx +++ b/src/pages/AnalysisPage.tsx @@ -5,6 +5,7 @@ import { ChineseButton } from '../components/ui/ChineseButton'; import { ChineseInput } from '../components/ui/ChineseInput'; import { ChineseSelect } from '../components/ui/ChineseSelect'; import { ChineseCard, ChineseCardContent, ChineseCardHeader, ChineseCardTitle } from '../components/ui/ChineseCard'; +import YijingQuestionSelector from '../components/ui/YijingQuestionSelector'; import AnalysisResultDisplay from '../components/AnalysisResultDisplay'; import { toast } from 'sonner'; import { Sparkles, Star, Compass, Calendar, MapPin, User, Loader2 } from 'lucide-react'; @@ -24,7 +25,7 @@ const AnalysisPage: React.FC = () => { birth_time: '', gender: 'male' as 'male' | 'female', birth_place: '', - question: '财运' + question: '' }); const [loading, setLoading] = useState(false); const [analysisResult, setAnalysisResult] = useState(null); @@ -56,7 +57,7 @@ const AnalysisPage: React.FC = () => { birth_time: data.birth_time || '', gender: data.gender || 'male', birth_place: data.birth_location || '', - question: '财运' + question: '' }); } } catch (error) { @@ -139,10 +140,38 @@ const AnalysisPage: React.FC = () => { // 后端返回格式: { data: { analysis } } const analysisData = data.analysis; - setAnalysisResult({ - type: analysisType, - data: analysisData - }); + // 保存历史记录 + try { + const saveResponse = await localApi.request('/analysis/save-history', { + method: 'POST', + body: JSON.stringify({ + analysis_type: analysisType, + analysis_data: analysisData, + input_data: analysisType === 'yijing' ? { question: formData.question } : birthData + }) + }); + + if (saveResponse.data?.record_id) { + // 将record_id添加到分析结果中,用于AI解读 + setAnalysisResult({ + type: analysisType, + data: analysisData, + recordId: saveResponse.data.record_id + }); + } else { + setAnalysisResult({ + type: analysisType, + data: analysisData + }); + } + } catch (saveError) { + console.error('保存历史记录失败:', saveError); + // 即使保存失败,也显示分析结果 + setAnalysisResult({ + type: analysisType, + data: analysisData + }); + } // 分析完成后,滚动到结果区域 setTimeout(() => { @@ -278,14 +307,9 @@ const AnalysisPage: React.FC = () => { {analysisType === 'yijing' ? ( // 易经占卜表单
- setFormData(prev => ({ ...prev, question: e.target.value }))} - placeholder="请输入您希望占卜的具体问题,如:我的事业发展如何?" - required - variant="filled" - helperText="💡 提示:问题越具体,占卜结果越准确。可以询问事业、感情、财运、健康等方面的问题。" + onChange={(value) => setFormData(prev => ({ ...prev, question: value }))} />
) : ( @@ -402,6 +426,7 @@ const AnalysisPage: React.FC = () => { question={analysisType === 'yijing' ? formData.question : undefined} userId={user?.id?.toString()} divinationMethod="time" + recordId={analysisResult.recordId} /> )} diff --git a/src/pages/HistoryPage.tsx b/src/pages/HistoryPage.tsx index c399d25..8cf51cb 100644 --- a/src/pages/HistoryPage.tsx +++ b/src/pages/HistoryPage.tsx @@ -21,6 +21,7 @@ const HistoryPage: React.FC = () => { // 分页相关状态 const [currentPage, setCurrentPage] = useState(1); + const [aiInterpretations, setAiInterpretations] = useState<{[key: number]: boolean}>({}); const itemsPerPage = 10; // 安全地从input_data中获取值的辅助函数 @@ -88,6 +89,20 @@ const HistoryPage: React.FC = () => { }); setReadings(processedData); + + // 检查每个记录的AI解读状态 + const aiStatus: {[key: number]: boolean} = {}; + for (const reading of processedData) { + try { + const aiResponse = await localApi.request(`/ai-interpretation/get/${reading.id}`, { + method: 'GET' + }); + aiStatus[reading.id] = aiResponse.success && aiResponse.data; + } catch { + aiStatus[reading.id] = false; + } + } + setAiInterpretations(aiStatus); } catch (error: any) { toast.error('加载历史记录失败:' + (error.message || '未知错误')); } finally { @@ -214,6 +229,7 @@ const HistoryPage: React.FC = () => { divinationMethod={selectedReading.reading_type === 'yijing' ? getInputDataValue(selectedReading.input_data, 'divination_method', 'time') : undefined} preAnalysisData={selectedReading.analysis} + recordId={selectedReading.id} /> @@ -279,9 +295,17 @@ const HistoryPage: React.FC = () => {
-

- {reading.name || '未知姓名'} - {getAnalysisTypeName(reading.reading_type)} -

+
+

+ {reading.name || '未知姓名'} - {getAnalysisTypeName(reading.reading_type)} +

+ {aiInterpretations[reading.id] && ( +
+ + 已有AI解读 +
+ )} +