feat: Complete overhaul of Bazi analysis system with professional features

- Implement dynamic Bazi analysis based on traditional Four Pillars theory
- Add comprehensive pillar interpretations (year, month, day, hour)
- Implement precise Dayun (Great Luck) and yearly fortune calculations
- Add detailed 6-year fortune analysis with monthly highlights
- Replace fixed templates with dynamic pattern analysis
- Implement Wuxing (Five Elements) strength analysis system
- Add professional life guidance and modern application suggestions
- Create new CompleteBaziAnalysis component with modern UI
- Update BaziDetailsPage with improved user experience
- Integrate all analysis modules into unified display system
- Add missing getElementRelation method to BaziAnalyzer
- Ensure all analysis content is dynamically generated from real Bazi data
This commit is contained in:
patdelphi
2025-08-18 23:45:30 +08:00
parent d9c57dedb7
commit 2a70320730
10 changed files with 3061 additions and 512 deletions

View File

@@ -1,5 +1,5 @@
import React from 'react';
import ComprehensiveBaziAnalysis from './ComprehensiveBaziAnalysis';
import CompleteBaziAnalysis from './CompleteBaziAnalysis';
import BaziAnalysisDisplay from './BaziAnalysisDisplay';
interface AnalysisResultDisplayProps {
@@ -44,16 +44,28 @@ const AnalysisResultDisplay: React.FC<AnalysisResultDisplayProps> = ({ analysisR
// 渲染八字命理分析
const renderBaziAnalysis = () => {
// 如果有分析结果数据,优先使用 ComprehensiveBaziAnalysis 组件
if (analysisResult && analysisResult.data) {
return <ComprehensiveBaziAnalysis analysisResult={analysisResult} />;
// 如果有 birthDate使用新的 CompleteBaziAnalysis 组件
if (birthDate) {
return <CompleteBaziAnalysis birthDate={birthDate} />;
}
// 如果有 birthDate 但没有分析结果,使用 BaziAnalysisDisplay 组件
// 如果有分析结果但没有 birthDate,尝试从结果中提取出生信息
if (analysisResult && analysisResult.data) {
const basicInfo = analysisResult.data.basic_info;
if (basicInfo && basicInfo.personal_data) {
const extractedBirthDate = {
date: basicInfo.personal_data.birth_date || '',
time: basicInfo.personal_data.birth_time || '12:00',
name: basicInfo.personal_data.name || '',
gender: basicInfo.personal_data.gender === '男性' ? 'male' : 'female'
};
return <CompleteBaziAnalysis birthDate={extractedBirthDate} />;
}
}
// 回退到旧的组件(向后兼容)
if (birthDate) {
return <BaziAnalysisDisplay birthDate={birthDate} />;
}
// 默认使用 ComprehensiveBaziAnalysis 组件(向后兼容)
return <ComprehensiveBaziAnalysis analysisResult={analysisResult} />;
return <div className="text-center text-red-600 p-8"></div>;
};
// 渲染紫微斗数分析

View File

@@ -37,6 +37,25 @@ const BaziAnalysisDisplay: React.FC<BaziAnalysisDisplayProps> = ({ birthDate })
const [baziDetailsData, setBaziDetailsData] = useState<BaziDetailsData | null>(null);
const [wuxingAnalysisData, setWuxingAnalysisData] = useState<WuxingAnalysisData | null>(null);
const [fullBaziAnalysisData, setFullBaziAnalysisData] = useState<any>(null);
// 辅助方法
const getBranchElement = (branch: string): string => {
const branchElements: { [key: string]: string } = {
'子': '水', '丑': '土', '寅': '木', '卯': '木', '辰': '土', '巳': '火',
'午': '火', '未': '土', '申': '金', '酉': '金', '戌': '土', '亥': '水'
};
return branchElements[branch] || '土';
};
const getStemYinYang = (stem: string): string => {
const yangStems = ['甲', '丙', '戊', '庚', '壬'];
return yangStems.includes(stem) ? '阳' : '阴';
};
const getBranchYinYang = (branch: string): string => {
const yangBranches = ['子', '寅', '辰', '午', '申', '戌'];
return yangBranches.includes(branch) ? '阳' : '阴';
};
// 五行颜色配置
const elementColors: { [key: string]: string } = {
@@ -71,69 +90,143 @@ const BaziAnalysisDisplay: React.FC<BaziAnalysisDisplayProps> = ({ birthDate })
'阴': 'text-purple-600 bg-purple-50 border-purple-300'
};
// 调用 Supabase Edge Functions
// 调用本地API
useEffect(() => {
const fetchAnalysisData = async () => {
try {
setIsLoading(true);
setError(null);
const requestBody = {
birthDate: birthDate.date,
birthTime: birthDate.time
const birthData = {
name: '用户', // 默认名称
birth_date: birthDate.date,
birth_time: birthDate.time,
gender: 'male' // 默认性别,后续可以从用户输入获取
};
// 并行调用两个函数
const [baziDetailsResponse, wuxingAnalysisResponse] = await Promise.all([
localApi.functions.invoke('bazi-details', {
body: requestBody
}),
localApi.functions.invoke('bazi-wuxing-analysis', {
body: requestBody
})
]);
// 调用八字分析API
const baziResponse = await localApi.analysis.bazi(birthData);
if (baziDetailsResponse.error || wuxingAnalysisResponse.error) {
throw new Error('获取分析数据失败');
if (baziResponse.error) {
throw new Error(baziResponse.error.message || '八字分析失败');
}
const baziDetailsResult = baziDetailsResponse.data;
const wuxingAnalysisResult = wuxingAnalysisResponse.data;
if (baziDetailsResult.error) {
throw new Error(baziDetailsResult.error.message || '八字详情分析失败');
const analysisResult = baziResponse.data?.analysis;
if (!analysisResult) {
throw new Error('分析结果为空');
}
if (wuxingAnalysisResult.error) {
throw new Error(wuxingAnalysisResult.error.message || '五行分析失败');
}
setBaziDetailsData(baziDetailsResult.data);
setWuxingAnalysisData(wuxingAnalysisResult.data);
// 转换数据格式以适配现有的显示组件
const baziChart = analysisResult.basic_info?.bazi_chart;
const wuxingAnalysis = analysisResult.wuxing_analysis;
// 为了展示更多推理内容,在这里添加模拟的完整分析数据
const mockFullAnalysis = {
geju_analysis: {
pattern_type: '正印格',
pattern_strength: '中等',
characteristics: '您的八字呈现正印格特征,表明您天生具有学习能力强、善于思考、重视名誉的特质。这种格局的人通常具有文雅的气质,对知识和智慧有着深度的追求。',
career_path: '适合从事教育、文化、研究、咨询等需要专业知识和智慧的行业。也适合公务员、律师、医生等职业。',
life_meaning: '您的人生使命是通过学习和知识的积累,不断提升自己的智慧和品德,并且将这些智慧传递给他人。'
},
dayun_analysis: {
current_period: '青年时期运势稳定,适合打基础和积累经验',
life_periods: '早年学业有成,中年事业发展,晚年享受成果',
future_outlook: '未来十年整体运势向好,特别是在学业和事业方面将有明显的提升。'
},
life_guidance: {
career_development: '建议您专注于专业技能的提升,在自己的领域内深耕细作。可以考虑进修或者参加专业培训,不断学习新知识。',
marriage_relationships: '在情感方面,您比较重视精神交流和心灵沟通。建议寻找一个有共同话题和相似价值观的伴侣。',
health_wellness: '注意用脑过度,定期休息。建议多进行户外运动,平衡脑力和体力的消耗。',
wealth_guidance: '财运方面,您的财富主要来源于工作收入和专业技能。建议进行稳健的投资,避免高风险投机。'
}
};
if (baziChart) {
// 构造八字详情数据
const baziDetailsData = {
baziDetails: {
year: {
tiangan: baziChart.year_pillar.stem,
dizhi: baziChart.year_pillar.branch,
tianganWuxing: baziChart.year_pillar.element,
dizhiWuxing: getBranchElement(baziChart.year_pillar.branch),
tianganYinYang: getStemYinYang(baziChart.year_pillar.stem),
dizhiYinYang: getBranchYinYang(baziChart.year_pillar.branch),
combination: `${baziChart.year_pillar.stem}${baziChart.year_pillar.branch}`,
pillarName: '年柱'
},
month: {
tiangan: baziChart.month_pillar.stem,
dizhi: baziChart.month_pillar.branch,
tianganWuxing: baziChart.month_pillar.element,
dizhiWuxing: getBranchElement(baziChart.month_pillar.branch),
tianganYinYang: getStemYinYang(baziChart.month_pillar.stem),
dizhiYinYang: getBranchYinYang(baziChart.month_pillar.branch),
combination: `${baziChart.month_pillar.stem}${baziChart.month_pillar.branch}`,
pillarName: '月柱'
},
day: {
tiangan: baziChart.day_pillar.stem,
dizhi: baziChart.day_pillar.branch,
tianganWuxing: baziChart.day_pillar.element,
dizhiWuxing: getBranchElement(baziChart.day_pillar.branch),
tianganYinYang: getStemYinYang(baziChart.day_pillar.stem),
dizhiYinYang: getBranchYinYang(baziChart.day_pillar.branch),
combination: `${baziChart.day_pillar.stem}${baziChart.day_pillar.branch}`,
pillarName: '日柱'
},
hour: {
tiangan: baziChart.hour_pillar.stem,
dizhi: baziChart.hour_pillar.branch,
tianganWuxing: baziChart.hour_pillar.element,
dizhiWuxing: getBranchElement(baziChart.hour_pillar.branch),
tianganYinYang: getStemYinYang(baziChart.hour_pillar.stem),
dizhiYinYang: getBranchYinYang(baziChart.hour_pillar.branch),
combination: `${baziChart.hour_pillar.stem}${baziChart.hour_pillar.branch}`,
pillarName: '时柱'
}
},
rizhu: {
tiangan: baziChart.day_master,
wuxing: baziChart.day_master_element,
yinyang: getStemYinYang(baziChart.day_master),
description: `日主${baziChart.day_master}${baziChart.day_master_element}`
},
summary: {
fullBazi: baziChart.complete_chart,
birthInfo: {
solarDate: birthDate.date,
birthTime: birthDate.time
}
},
interpretation: {
overall: wuxingAnalysis?.detailed_analysis || '八字分析结果'
}
};
setBaziDetailsData(baziDetailsData);
}
setFullBaziAnalysisData(mockFullAnalysis);
if (wuxingAnalysis) {
// 构造五行分析数据
const elements = wuxingAnalysis.distribution || {};
const total = Object.values(elements).reduce((sum: number, count: any) => sum + (typeof count === 'number' ? count : 0), 0);
const wuxingData = {
bazi: baziChart,
wuxingCount: elements,
wuxingPercentage: Object.fromEntries(
Object.entries(elements).map(([key, value]) => [
key,
total > 0 ? Math.round(((value as number) / total) * 100) : 0
])
),
wuxingWithStrength: Object.entries(elements).map(([element, count]) => ({
element,
count: count as number,
percentage: total > 0 ? Math.round(((count as number) / total) * 100) : 0,
strength: (count as number) >= 3 ? '旺' : (count as number) >= 2 ? '中' : '弱'
})),
radarData: Object.entries(elements).map(([element, count]) => ({
element,
value: count as number,
fullMark: 5
})),
balanceAnalysis: wuxingAnalysis.detailed_analysis || '五行分析',
suggestions: [wuxingAnalysis.improvement_suggestions || '建议保持平衡'],
dominantElement: Object.entries(elements).reduce((a, b) => (elements[a[0]] as number) > (elements[b[0]] as number) ? a : b)[0],
weakestElement: Object.entries(elements).reduce((a, b) => (elements[a[0]] as number) < (elements[b[0]] as number) ? a : b)[0],
isBalanced: Math.max(...Object.values(elements) as number[]) - Math.min(...Object.values(elements) as number[]) <= 2
};
setWuxingAnalysisData(wuxingData);
}
// 设置完整分析数据 - 使用真实的后端分析结果
setFullBaziAnalysisData({
basic_info: analysisResult.basic_info || {},
geju_analysis: analysisResult.geju_analysis || {},
dayun_analysis: analysisResult.dayun_analysis || {},
life_guidance: analysisResult.life_guidance || {},
modern_applications: analysisResult.modern_applications || {}
});
} catch (err) {
console.error('获取分析数据出错:', err);
setError(err instanceof Error ? err.message : '分析数据获取失败,请稍后重试');
@@ -533,8 +626,101 @@ const BaziAnalysisDisplay: React.FC<BaziAnalysisDisplayProps> = ({ birthDate })
</Card>
)}
{/* 格局分析 */}
{fullBaziAnalysisData?.geju_analysis && (
<Card className="chinese-card-decoration dragon-corner border-2 border-yellow-400">
<CardHeader>
<CardTitle className="text-red-800 text-2xl font-bold chinese-text-shadow flex items-center">
<Star className="mr-2 h-6 w-6 text-yellow-600" />
</CardTitle>
</CardHeader>
<CardContent>
<div className="bg-gradient-to-br from-red-50 to-yellow-50 rounded-lg p-6">
<div className="space-y-4">
<div className="bg-white p-4 rounded-lg border-l-4 border-purple-500">
<h4 className="font-bold text-red-800 mb-2"></h4>
<p className="text-red-700 leading-relaxed">
<span className="font-semibold">{fullBaziAnalysisData.geju_analysis.pattern_type}</span>
{fullBaziAnalysisData.geju_analysis.pattern_strength}
</p>
</div>
<div className="bg-white p-4 rounded-lg border-l-4 border-blue-500">
<h4 className="font-bold text-red-800 mb-2"></h4>
<p className="text-red-700 leading-relaxed">
{fullBaziAnalysisData.geju_analysis.characteristics}
</p>
</div>
<div className="bg-white p-4 rounded-lg border-l-4 border-green-500">
<h4 className="font-bold text-red-800 mb-2"></h4>
<p className="text-red-700 leading-relaxed">
{fullBaziAnalysisData.geju_analysis.career_path}
</p>
</div>
<div className="bg-white p-4 rounded-lg border-l-4 border-orange-500">
<h4 className="font-bold text-red-800 mb-2"></h4>
<p className="text-red-700 leading-relaxed">
{fullBaziAnalysisData.geju_analysis.life_meaning}
</p>
</div>
{fullBaziAnalysisData.geju_analysis.development_strategy && (
<div className="bg-white p-4 rounded-lg border-l-4 border-yellow-500">
<h4 className="font-bold text-red-800 mb-2"></h4>
<p className="text-red-700 leading-relaxed">
{fullBaziAnalysisData.geju_analysis.development_strategy}
</p>
</div>
)}
</div>
</div>
</CardContent>
</Card>
)}
{/* 四柱详细解释 */}
{fullBaziAnalysisData?.basic_info?.pillar_interpretations && (
<Card className="chinese-card-decoration dragon-corner border-2 border-yellow-400">
<CardHeader>
<CardTitle className="text-red-800 text-2xl font-bold chinese-text-shadow flex items-center">
<BookOpen className="mr-2 h-6 w-6 text-yellow-600" />
</CardTitle>
</CardHeader>
<CardContent>
<div className="bg-gradient-to-br from-red-50 to-yellow-50 rounded-lg p-6">
<div className="space-y-6">
<div className="bg-white p-4 rounded-lg border-l-4 border-purple-500">
<h4 className="font-bold text-red-800 mb-2"></h4>
<p className="text-red-700 leading-relaxed text-sm">
{fullBaziAnalysisData.basic_info.pillar_interpretations.year_pillar}
</p>
</div>
<div className="bg-white p-4 rounded-lg border-l-4 border-blue-500">
<h4 className="font-bold text-red-800 mb-2"></h4>
<p className="text-red-700 leading-relaxed text-sm">
{fullBaziAnalysisData.basic_info.pillar_interpretations.month_pillar}
</p>
</div>
<div className="bg-white p-4 rounded-lg border-l-4 border-green-500">
<h4 className="font-bold text-red-800 mb-2"></h4>
<p className="text-red-700 leading-relaxed text-sm">
{fullBaziAnalysisData.basic_info.pillar_interpretations.day_pillar}
</p>
</div>
<div className="bg-white p-4 rounded-lg border-l-4 border-orange-500">
<h4 className="font-bold text-red-800 mb-2"></h4>
<p className="text-red-700 leading-relaxed text-sm">
{fullBaziAnalysisData.basic_info.pillar_interpretations.hour_pillar}
</p>
</div>
</div>
</div>
</CardContent>
</Card>
)}
{/* 大运流年分析 */}
{fullBaziAnalysisData && (
{fullBaziAnalysisData?.dayun_analysis && (
<Card className="chinese-card-decoration dragon-corner border-2 border-yellow-400">
<CardHeader>
<CardTitle className="text-red-800 text-2xl font-bold chinese-text-shadow flex items-center">
@@ -546,15 +732,27 @@ const BaziAnalysisDisplay: React.FC<BaziAnalysisDisplayProps> = ({ birthDate })
<div className="bg-gradient-to-br from-red-50 to-yellow-50 rounded-lg p-6">
<div className="space-y-4">
<div className="bg-white p-4 rounded-lg border-l-4 border-red-500">
<h4 className="font-bold text-red-800 mb-2"></h4>
<h4 className="font-bold text-red-800 mb-2"></h4>
<p className="text-red-700 leading-relaxed">
{fullBaziAnalysisData.dayun_analysis?.current_period}
{fullBaziAnalysisData.dayun_analysis?.start_luck_age}
</p>
<p className="text-red-700 leading-relaxed">
{fullBaziAnalysisData.dayun_analysis?.current_dayun?.ganzhi || '未起运'}
{fullBaziAnalysisData.dayun_analysis?.current_dayun &&
`${fullBaziAnalysisData.dayun_analysis.current_dayun.start_age}-${fullBaziAnalysisData.dayun_analysis.current_dayun.end_age}岁)`
}
</p>
</div>
<div className="bg-white p-4 rounded-lg border-l-4 border-blue-500">
<h4 className="font-bold text-red-800 mb-2"></h4>
<h4 className="font-bold text-red-800 mb-2"></h4>
<p className="text-red-700 leading-relaxed">
{fullBaziAnalysisData.dayun_analysis?.life_periods}
{fullBaziAnalysisData.dayun_analysis?.dayun_influence}
</p>
</div>
<div className="bg-white p-4 rounded-lg border-l-4 border-green-500">
<h4 className="font-bold text-red-800 mb-2"></h4>
<p className="text-red-700 leading-relaxed">
{fullBaziAnalysisData.dayun_analysis?.yearly_fortune}
</p>
</div>
<div className="bg-white p-4 rounded-lg border-l-4 border-orange-500">
@@ -569,6 +767,76 @@ const BaziAnalysisDisplay: React.FC<BaziAnalysisDisplayProps> = ({ birthDate })
</Card>
)}
{/* 详细流年分析 */}
{fullBaziAnalysisData?.dayun_analysis?.detailed_yearly_analysis && (
<Card className="chinese-card-decoration dragon-corner border-2 border-yellow-400">
<CardHeader>
<CardTitle className="text-red-800 text-2xl font-bold chinese-text-shadow flex items-center">
<Calendar className="mr-2 h-6 w-6 text-yellow-600" />
</CardTitle>
</CardHeader>
<CardContent>
<div className="bg-gradient-to-br from-red-50 to-yellow-50 rounded-lg p-6">
<div className="space-y-6">
{fullBaziAnalysisData.dayun_analysis.detailed_yearly_analysis.map((yearData: any, index: number) => (
<div key={index} className="bg-white p-4 rounded-lg border-2 border-yellow-300">
<div className="flex items-center justify-between mb-3">
<h4 className="font-bold text-red-800 text-lg">
{yearData.year}{yearData.age}{yearData.year_ganzhi}
</h4>
<span className="text-sm text-red-600 bg-red-50 px-2 py-1 rounded">
{yearData.year_ten_god}
</span>
</div>
<div className="grid md:grid-cols-2 gap-4">
<div className="space-y-3">
<div className="border-l-4 border-blue-400 pl-3">
<h5 className="font-semibold text-red-800 text-sm"></h5>
<p className="text-red-700 text-xs leading-relaxed">{yearData.overall_fortune}</p>
</div>
<div className="border-l-4 border-green-400 pl-3">
<h5 className="font-semibold text-red-800 text-sm"></h5>
<p className="text-red-700 text-xs leading-relaxed">{yearData.career_fortune}</p>
</div>
<div className="border-l-4 border-yellow-400 pl-3">
<h5 className="font-semibold text-red-800 text-sm"></h5>
<p className="text-red-700 text-xs leading-relaxed">{yearData.wealth_fortune}</p>
</div>
</div>
<div className="space-y-3">
<div className="border-l-4 border-pink-400 pl-3">
<h5 className="font-semibold text-red-800 text-sm"></h5>
<p className="text-red-700 text-xs leading-relaxed">{yearData.relationship_fortune}</p>
</div>
<div className="border-l-4 border-purple-400 pl-3">
<h5 className="font-semibold text-red-800 text-sm"></h5>
<p className="text-red-700 text-xs leading-relaxed">{yearData.health_fortune}</p>
</div>
<div className="border-l-4 border-orange-400 pl-3">
<h5 className="font-semibold text-red-800 text-sm"></h5>
<p className="text-red-700 text-xs leading-relaxed">{yearData.key_advice}</p>
</div>
</div>
</div>
{yearData.monthly_highlights && yearData.monthly_highlights.length > 0 && (
<div className="mt-3 pt-3 border-t border-yellow-200">
<h5 className="font-semibold text-red-800 text-sm mb-2"></h5>
<div className="space-y-1">
{yearData.monthly_highlights.map((highlight: string, hIndex: number) => (
<p key={hIndex} className="text-red-700 text-xs"> {highlight}</p>
))}
</div>
</div>
)}
</div>
))}
</div>
</div>
</CardContent>
</Card>
)}
{/* 专业人生指导 */}
{fullBaziAnalysisData && (
<Card className="chinese-card-decoration dragon-corner border-2 border-yellow-400">
@@ -615,6 +883,52 @@ const BaziAnalysisDisplay: React.FC<BaziAnalysisDisplayProps> = ({ birthDate })
</Card>
)}
{/* 现代应用建议 */}
{fullBaziAnalysisData?.modern_applications && (
<Card className="chinese-card-decoration dragon-corner border-2 border-yellow-400">
<CardHeader>
<CardTitle className="text-red-800 text-2xl font-bold chinese-text-shadow flex items-center">
<BarChart3 className="mr-2 h-6 w-6 text-yellow-600" />
</CardTitle>
</CardHeader>
<CardContent>
<div className="bg-gradient-to-br from-red-50 to-yellow-50 rounded-lg p-6">
<div className="grid md:grid-cols-2 gap-6">
<div className="space-y-4">
<div className="bg-white p-4 rounded-lg border-l-4 border-blue-500">
<h4 className="font-bold text-red-800 mb-2"></h4>
<p className="text-red-700 leading-relaxed text-sm">
{fullBaziAnalysisData.modern_applications.lifestyle_recommendations}
</p>
</div>
<div className="bg-white p-4 rounded-lg border-l-4 border-green-500">
<h4 className="font-bold text-red-800 mb-2"></h4>
<p className="text-red-700 leading-relaxed text-sm">
{fullBaziAnalysisData.modern_applications.career_strategies}
</p>
</div>
</div>
<div className="space-y-4">
<div className="bg-white p-4 rounded-lg border-l-4 border-purple-500">
<h4 className="font-bold text-red-800 mb-2"></h4>
<p className="text-red-700 leading-relaxed text-sm">
{fullBaziAnalysisData.modern_applications.relationship_advice}
</p>
</div>
<div className="bg-white p-4 rounded-lg border-l-4 border-orange-500">
<h4 className="font-bold text-red-800 mb-2"></h4>
<p className="text-red-700 leading-relaxed text-sm">
{fullBaziAnalysisData.modern_applications.decision_making}
</p>
</div>
</div>
</div>
</div>
</CardContent>
</Card>
)}
{/* 人生指导建议 */}
<Card className="chinese-card-decoration dragon-corner border-2 border-yellow-400">
<CardHeader>

View File

@@ -0,0 +1,774 @@
import React, { useState, useEffect } from 'react';
import { Radar, RadarChart, PolarGrid, PolarAngleAxis, PolarRadiusAxis, ResponsiveContainer } from 'recharts';
import { Calendar, Star, BookOpen, Sparkles, User, BarChart3, Zap, TrendingUp, Loader2, Clock, Target, Heart, DollarSign, Activity } from 'lucide-react';
import { Card, CardContent, CardHeader, CardTitle } from './ui/Card';
import { localApi } from '../lib/localApi';
interface CompleteBaziAnalysisProps {
birthDate: {
date: string;
time: string;
name?: string;
gender?: string;
};
}
const CompleteBaziAnalysis: React.FC<CompleteBaziAnalysisProps> = ({ birthDate }) => {
const [isLoading, setIsLoading] = useState(true);
const [error, setError] = useState<string | null>(null);
const [analysisData, setAnalysisData] = useState<any>(null);
// 五行颜色配置
const elementColors: { [key: string]: string } = {
'木': '#22c55e', // 绿色
'火': '#ef4444', // 红色
'土': '#eab308', // 黄色
'金': '#64748b', // 银色
'水': '#3b82f6' // 蓝色
};
// 五行符号配置
const elementSymbols: { [key: string]: string } = {
'木': '🌲',
'火': '🔥',
'土': '⛰️',
'金': '⚡',
'水': '💧'
};
// 十神颜色配置
const tenGodColors: { [key: string]: string } = {
'正官': 'bg-blue-100 text-blue-800 border-blue-300',
'七杀': 'bg-red-100 text-red-800 border-red-300',
'正财': 'bg-green-100 text-green-800 border-green-300',
'偏财': 'bg-yellow-100 text-yellow-800 border-yellow-300',
'正印': 'bg-purple-100 text-purple-800 border-purple-300',
'偏印': 'bg-indigo-100 text-indigo-800 border-indigo-300',
'食神': 'bg-pink-100 text-pink-800 border-pink-300',
'伤官': 'bg-orange-100 text-orange-800 border-orange-300',
'比肩': 'bg-gray-100 text-gray-800 border-gray-300',
'劫财': 'bg-slate-100 text-slate-800 border-slate-300',
'日主': 'bg-amber-100 text-amber-800 border-amber-300'
};
useEffect(() => {
const fetchAnalysisData = async () => {
try {
setIsLoading(true);
setError(null);
const birthData = {
name: birthDate.name || '用户',
birth_date: birthDate.date,
birth_time: birthDate.time,
gender: birthDate.gender || 'male'
};
const baziResponse = await localApi.analysis.bazi(birthData);
if (baziResponse.error) {
throw new Error(baziResponse.error.message || '八字分析失败');
}
const analysisResult = baziResponse.data?.analysis;
if (!analysisResult) {
throw new Error('分析结果为空');
}
setAnalysisData(analysisResult);
} catch (err) {
console.error('获取分析数据出错:', err);
setError(err instanceof Error ? err.message : '分析数据获取失败,请稍后重试');
} finally {
setIsLoading(false);
}
};
if (birthDate?.date) {
fetchAnalysisData();
}
}, [birthDate]);
// 渲染加载状态
if (isLoading) {
return (
<div className="min-h-screen flex items-center justify-center bg-gradient-to-br from-red-50 to-yellow-50">
<Card className="chinese-card-decoration border-2 border-yellow-400 p-8">
<CardContent className="text-center">
<Loader2 className="h-12 w-12 animate-spin text-red-600 mx-auto mb-4" />
<h3 className="text-xl font-bold text-red-800 mb-2"></h3>
<p className="text-red-600">...</p>
</CardContent>
</Card>
</div>
);
}
// 渲染错误状态
if (error) {
return (
<div className="min-h-screen flex items-center justify-center bg-gradient-to-br from-red-50 to-yellow-50">
<Card className="chinese-card-decoration border-2 border-red-400 p-8">
<CardContent className="text-center">
<div className="text-6xl mb-4"></div>
<h3 className="text-xl font-bold text-red-800 mb-2"></h3>
<p className="text-red-600 mb-4">{error}</p>
<button
onClick={() => window.location.reload()}
className="px-4 py-2 bg-red-600 text-white rounded-lg hover:bg-red-700 transition-colors"
>
</button>
</CardContent>
</Card>
</div>
);
}
if (!analysisData) {
return (
<div className="min-h-screen flex items-center justify-center bg-gradient-to-br from-red-50 to-yellow-50">
<Card className="chinese-card-decoration border-2 border-yellow-400 p-8">
<CardContent className="text-center">
<div className="text-6xl mb-4"></div>
<h3 className="text-xl font-bold text-red-800 mb-2"></h3>
<p className="text-red-600"></p>
</CardContent>
</Card>
</div>
);
}
// 渲染四柱信息卡片
const renderPillarCard = (pillar: any, pillarName: string, description: string) => {
if (!pillar) return null;
return (
<Card className="chinese-card-decoration hover:shadow-xl transition-all duration-300 border-2 border-yellow-400">
<CardHeader className="text-center pb-2">
<CardTitle className="text-red-800 text-lg font-bold chinese-text-shadow">
{pillarName}
</CardTitle>
<p className="text-red-600 text-xs">{description}</p>
</CardHeader>
<CardContent className="space-y-3">
<div className="text-center">
<div className="text-3xl font-bold text-red-800 mb-2">
{pillar.stem}{pillar.branch}
</div>
<div className="flex justify-center space-x-2 mb-3">
<span className={`px-2 py-1 rounded text-xs font-medium border ${tenGodColors[pillar.ten_god] || 'bg-gray-100 text-gray-800'}`}>
{pillar.ten_god}
</span>
<span className="px-2 py-1 rounded text-xs font-medium bg-blue-100 text-blue-800 border border-blue-300">
{pillar.element}
</span>
</div>
</div>
{pillar.hidden_stems && pillar.hidden_stems.length > 0 && (
<div className="border-t pt-2">
<h5 className="text-xs font-semibold text-red-800 mb-1"></h5>
<div className="flex flex-wrap gap-1">
{pillar.hidden_stems.map((stem: string, index: number) => (
<span key={index} className="px-1 py-0.5 bg-gray-100 text-gray-700 rounded text-xs">
{stem}
</span>
))}
</div>
</div>
)}
</CardContent>
</Card>
);
};
// 渲染五行雷达图
const renderWuxingRadar = () => {
if (!analysisData.wuxing_analysis?.element_distribution) return null;
const elements = analysisData.wuxing_analysis.element_distribution;
const radarData = Object.entries(elements).map(([element, count]) => ({
element,
value: count as number,
fullMark: 6
}));
return (
<ResponsiveContainer width="100%" height={300}>
<RadarChart data={radarData}>
<PolarGrid stroke="#dc2626" />
<PolarAngleAxis
dataKey="element"
tick={{ fill: '#dc2626', fontSize: 14, fontWeight: 'bold' }}
/>
<PolarRadiusAxis
angle={90}
domain={[0, 6]}
tick={{ fill: '#b91c1c', fontSize: 12 }}
/>
<Radar
name="五行强度"
dataKey="value"
stroke="#dc2626"
fill="rgba(220, 38, 38, 0.3)"
fillOpacity={0.6}
strokeWidth={2}
/>
</RadarChart>
</ResponsiveContainer>
);
};
// 渲染五行分布卡片
const renderElementCards = () => {
if (!analysisData.wuxing_analysis?.element_distribution) return null;
const elements = analysisData.wuxing_analysis.element_distribution;
const total = Object.values(elements).reduce((sum: number, count: any) => sum + (typeof count === 'number' ? count : 0), 0);
return (
<div className="grid grid-cols-5 gap-4">
{Object.entries(elements).map(([element, count]) => {
const percentage = total > 0 ? Math.round(((count as number) / total) * 100) : 0;
const strength = (count as number) >= 3 ? '旺' : (count as number) >= 2 ? '中' : '弱';
return (
<Card key={element} className="text-center hover:shadow-xl transition-all duration-300 chinese-card-decoration border-2 border-yellow-400">
<CardContent className="p-4">
<div className="text-3xl mb-2">{elementSymbols[element]}</div>
<h3 className="font-bold text-red-800 text-lg mb-2 chinese-text-shadow">{element}</h3>
<div className="text-2xl font-bold text-yellow-600 mb-1">{count}</div>
<div className="text-sm text-gray-600 mb-2">{percentage}%</div>
<div className={`text-sm font-medium mb-2 ${
strength === '旺' ? 'text-green-600' :
strength === '中' ? 'text-yellow-600' : 'text-orange-600'
}`}>
{strength}
</div>
<div className="w-full h-3 bg-gray-200 rounded-full overflow-hidden">
<div
className="h-full rounded-full transition-all duration-1000"
style={{
width: `${percentage}%`,
backgroundColor: elementColors[element]
}}
/>
</div>
</CardContent>
</Card>
);
})}
</div>
);
};
return (
<div className="min-h-screen bg-gradient-to-br from-red-50 to-yellow-50 py-8">
<div className="max-w-7xl mx-auto px-4 space-y-8">
{/* 标题和基本信息 */}
<Card className="chinese-card-decoration dragon-corner border-2 border-yellow-400">
<CardHeader className="text-center">
<CardTitle className="text-red-800 text-3xl font-bold chinese-text-shadow">
{analysisData.basic_info?.personal_data?.name || '用户'}
</CardTitle>
<div className="flex justify-center space-x-6 mt-4 text-red-700">
<div className="flex items-center space-x-2">
<Calendar className="h-5 w-5" />
<span>{analysisData.basic_info?.personal_data?.birth_date}</span>
</div>
<div className="flex items-center space-x-2">
<Clock className="h-5 w-5" />
<span>{analysisData.basic_info?.personal_data?.birth_time}</span>
</div>
<div className="flex items-center space-x-2">
<User className="h-5 w-5" />
<span>{analysisData.basic_info?.personal_data?.gender}</span>
</div>
</div>
</CardHeader>
<CardContent>
<div className="text-center">
<div className="text-2xl font-bold text-red-800 mb-4">
{analysisData.basic_info?.bazi_chart?.complete_chart}
</div>
<div className="grid md:grid-cols-2 gap-4">
<div className="bg-white p-4 rounded-lg border-l-4 border-red-500">
<h4 className="font-bold text-red-800 mb-2"></h4>
<p className="text-red-700">
{analysisData.basic_info?.bazi_chart?.day_master}{analysisData.basic_info?.bazi_chart?.day_master_element}
</p>
<p className="text-red-700">
{analysisData.basic_info?.bazi_chart?.element_strength?.strength_level}
</p>
</div>
<div className="bg-white p-4 rounded-lg border-l-4 border-blue-500">
<h4 className="font-bold text-red-800 mb-2"></h4>
<p className="text-red-700 text-sm">
{analysisData.basic_info?.bazi_chart?.element_strength?.use_god_analysis?.analysis}
</p>
</div>
</div>
</div>
</CardContent>
</Card>
{/* 四柱详细信息 */}
<Card className="chinese-card-decoration dragon-corner border-2 border-yellow-400">
<CardHeader>
<CardTitle className="text-red-800 text-2xl font-bold chinese-text-shadow text-center">
</CardTitle>
</CardHeader>
<CardContent>
<div className="grid lg:grid-cols-4 gap-6 mb-6">
{renderPillarCard(analysisData.basic_info?.bazi_chart?.year_pillar, '年柱', '祖辈与早年运势')}
{renderPillarCard(analysisData.basic_info?.bazi_chart?.month_pillar, '月柱', '父母与青年运势')}
{renderPillarCard(analysisData.basic_info?.bazi_chart?.day_pillar, '日柱', '自身与配偶')}
{renderPillarCard(analysisData.basic_info?.bazi_chart?.hour_pillar, '时柱', '子女与晚年运势')}
</div>
</CardContent>
</Card>
{/* 四柱详细解释 */}
{analysisData.basic_info?.pillar_interpretations && (
<Card className="chinese-card-decoration dragon-corner border-2 border-yellow-400">
<CardHeader>
<CardTitle className="text-red-800 text-2xl font-bold chinese-text-shadow flex items-center">
<BookOpen className="mr-2 h-6 w-6 text-yellow-600" />
</CardTitle>
</CardHeader>
<CardContent>
<div className="bg-gradient-to-br from-red-50 to-yellow-50 rounded-lg p-6">
<div className="space-y-6">
<div className="bg-white p-4 rounded-lg border-l-4 border-purple-500">
<h4 className="font-bold text-red-800 mb-2 flex items-center">
<span className="mr-2">🏛</span>
</h4>
<p className="text-red-700 leading-relaxed text-sm">
{analysisData.basic_info.pillar_interpretations.year_pillar}
</p>
</div>
<div className="bg-white p-4 rounded-lg border-l-4 border-blue-500">
<h4 className="font-bold text-red-800 mb-2 flex items-center">
<span className="mr-2">🌟</span>
</h4>
<p className="text-red-700 leading-relaxed text-sm">
{analysisData.basic_info.pillar_interpretations.month_pillar}
</p>
</div>
<div className="bg-white p-4 rounded-lg border-l-4 border-green-500">
<h4 className="font-bold text-red-800 mb-2 flex items-center">
<span className="mr-2">💎</span>
</h4>
<p className="text-red-700 leading-relaxed text-sm">
{analysisData.basic_info.pillar_interpretations.day_pillar}
</p>
</div>
<div className="bg-white p-4 rounded-lg border-l-4 border-orange-500">
<h4 className="font-bold text-red-800 mb-2 flex items-center">
<span className="mr-2">🌅</span>
</h4>
<p className="text-red-700 leading-relaxed text-sm">
{analysisData.basic_info.pillar_interpretations.hour_pillar}
</p>
</div>
</div>
</div>
</CardContent>
</Card>
)}
{/* 五行能量分布 */}
<Card className="chinese-card-decoration dragon-corner border-2 border-yellow-400">
<CardHeader>
<CardTitle className="text-red-800 text-2xl font-bold chinese-text-shadow text-center">
</CardTitle>
</CardHeader>
<CardContent>
<div className="space-y-6">
{renderElementCards()}
<div className="grid md:grid-cols-2 gap-6">
<div className="bg-gradient-to-br from-red-50 to-yellow-50 rounded-lg p-6">
<h4 className="font-bold text-red-800 mb-4 text-center"></h4>
{renderWuxingRadar()}
</div>
<div className="space-y-4">
<div className="bg-white p-4 rounded-lg border-l-4 border-yellow-500">
<h4 className="font-bold text-red-800 mb-2"></h4>
<p className="text-red-700 leading-relaxed text-sm">
{analysisData.wuxing_analysis?.balance_analysis}
</p>
</div>
<div className="bg-white p-4 rounded-lg border-l-4 border-green-500">
<h4 className="font-bold text-red-800 mb-2"></h4>
<p className="text-red-700 leading-relaxed text-sm">
{analysisData.wuxing_analysis?.personality_traits}
</p>
</div>
<div className="bg-white p-4 rounded-lg border-l-4 border-blue-500">
<h4 className="font-bold text-red-800 mb-2"></h4>
<p className="text-red-700 leading-relaxed text-sm">
{analysisData.wuxing_analysis?.improvement_suggestions}
</p>
</div>
</div>
</div>
</div>
</CardContent>
</Card>
{/* 格局分析 */}
{analysisData.geju_analysis && (
<Card className="chinese-card-decoration dragon-corner border-2 border-yellow-400">
<CardHeader>
<CardTitle className="text-red-800 text-2xl font-bold chinese-text-shadow flex items-center">
<Star className="mr-2 h-6 w-6 text-yellow-600" />
</CardTitle>
</CardHeader>
<CardContent>
<div className="bg-gradient-to-br from-red-50 to-yellow-50 rounded-lg p-6">
<div className="grid md:grid-cols-2 gap-6">
<div className="space-y-4">
<div className="bg-white p-4 rounded-lg border-l-4 border-purple-500">
<h4 className="font-bold text-red-800 mb-2"></h4>
<div className="flex items-center space-x-2">
<span className="text-2xl font-bold text-purple-600">
{analysisData.geju_analysis.pattern_type}
</span>
<span className="px-2 py-1 bg-purple-100 text-purple-800 rounded text-sm">
{analysisData.geju_analysis.pattern_strength}
</span>
</div>
</div>
<div className="bg-white p-4 rounded-lg border-l-4 border-blue-500">
<h4 className="font-bold text-red-800 mb-2"></h4>
<p className="text-red-700 leading-relaxed text-sm">
{analysisData.geju_analysis.characteristics}
</p>
</div>
</div>
<div className="space-y-4">
<div className="bg-white p-4 rounded-lg border-l-4 border-green-500">
<h4 className="font-bold text-red-800 mb-2"></h4>
<p className="text-red-700 leading-relaxed text-sm">
{analysisData.geju_analysis.career_path}
</p>
</div>
<div className="bg-white p-4 rounded-lg border-l-4 border-orange-500">
<h4 className="font-bold text-red-800 mb-2"></h4>
<p className="text-red-700 leading-relaxed text-sm">
{analysisData.geju_analysis.life_meaning}
</p>
</div>
</div>
</div>
{analysisData.geju_analysis.development_strategy && (
<div className="mt-4 bg-white p-4 rounded-lg border-l-4 border-yellow-500">
<h4 className="font-bold text-red-800 mb-2"></h4>
<p className="text-red-700 leading-relaxed text-sm">
{analysisData.geju_analysis.development_strategy}
</p>
</div>
)}
</div>
</CardContent>
</Card>
)}
{/* 大运流年分析 */}
{analysisData.dayun_analysis && (
<Card className="chinese-card-decoration dragon-corner border-2 border-yellow-400">
<CardHeader>
<CardTitle className="text-red-800 text-2xl font-bold chinese-text-shadow flex items-center">
<TrendingUp className="mr-2 h-6 w-6 text-yellow-600" />
</CardTitle>
</CardHeader>
<CardContent>
<div className="bg-gradient-to-br from-red-50 to-yellow-50 rounded-lg p-6">
<div className="grid md:grid-cols-3 gap-6 mb-6">
<div className="bg-white p-4 rounded-lg border-l-4 border-red-500">
<h4 className="font-bold text-red-800 mb-2"></h4>
<p className="text-red-700">{analysisData.dayun_analysis.start_luck_age}</p>
<p className="text-red-700">{analysisData.dayun_analysis.current_age}</p>
{analysisData.dayun_analysis.current_dayun && (
<p className="text-red-700">
{analysisData.dayun_analysis.current_dayun.ganzhi}
{analysisData.dayun_analysis.current_dayun.start_age}-{analysisData.dayun_analysis.current_dayun.end_age}
</p>
)}
</div>
<div className="bg-white p-4 rounded-lg border-l-4 border-blue-500">
<h4 className="font-bold text-red-800 mb-2"></h4>
<p className="text-red-700 leading-relaxed text-sm">
{analysisData.dayun_analysis.dayun_influence}
</p>
</div>
<div className="bg-white p-4 rounded-lg border-l-4 border-green-500">
<h4 className="font-bold text-red-800 mb-2"></h4>
<p className="text-red-700 leading-relaxed text-sm">
{analysisData.dayun_analysis.yearly_fortune}
</p>
</div>
</div>
{/* 大运序列 */}
{analysisData.dayun_analysis.dayun_sequence && (
<div className="mb-6">
<h4 className="font-bold text-red-800 mb-4 text-center"></h4>
<div className="grid md:grid-cols-4 gap-3">
{analysisData.dayun_analysis.dayun_sequence.map((dayun: any, index: number) => (
<div key={index} className={`p-3 rounded-lg border-2 ${
analysisData.dayun_analysis.current_dayun &&
dayun.ganzhi === analysisData.dayun_analysis.current_dayun.ganzhi
? 'bg-yellow-100 border-yellow-400'
: 'bg-white border-gray-300'
}`}>
<div className="text-center">
<div className="font-bold text-red-800">{dayun.ganzhi}</div>
<div className="text-sm text-red-600">{dayun.start_age}-{dayun.end_age}</div>
<div className={`text-xs px-2 py-1 rounded mt-1 ${tenGodColors[dayun.ten_god] || 'bg-gray-100 text-gray-800'}`}>
{dayun.ten_god}
</div>
</div>
</div>
))}
</div>
</div>
)}
<div className="bg-white p-4 rounded-lg border-l-4 border-orange-500">
<h4 className="font-bold text-red-800 mb-2"></h4>
<p className="text-red-700 leading-relaxed text-sm">
{analysisData.dayun_analysis.future_outlook}
</p>
</div>
</div>
</CardContent>
</Card>
)}
{/* 详细流年分析 */}
{analysisData.dayun_analysis?.detailed_yearly_analysis && (
<Card className="chinese-card-decoration dragon-corner border-2 border-yellow-400">
<CardHeader>
<CardTitle className="text-red-800 text-2xl font-bold chinese-text-shadow flex items-center">
<Calendar className="mr-2 h-6 w-6 text-yellow-600" />
</CardTitle>
</CardHeader>
<CardContent>
<div className="bg-gradient-to-br from-red-50 to-yellow-50 rounded-lg p-6">
<div className="space-y-6">
{analysisData.dayun_analysis.detailed_yearly_analysis.map((yearData: any, index: number) => (
<div key={index} className="bg-white p-6 rounded-lg border-2 border-yellow-300 shadow-lg">
<div className="flex items-center justify-between mb-4">
<h4 className="font-bold text-red-800 text-xl">
{yearData.year}{yearData.age}{yearData.year_ganzhi}
</h4>
<div className="flex space-x-2">
<span className={`text-sm px-3 py-1 rounded-full ${tenGodColors[yearData.year_ten_god] || 'bg-gray-100 text-gray-800'}`}>
{yearData.year_ten_god}
</span>
<span className="text-sm text-blue-600 bg-blue-50 px-3 py-1 rounded-full border border-blue-300">
{yearData.dayun_period}
</span>
</div>
</div>
<div className="grid md:grid-cols-2 lg:grid-cols-3 gap-4">
<div className="space-y-3">
<div className="border-l-4 border-blue-400 pl-3">
<h5 className="font-semibold text-red-800 text-sm flex items-center">
<Target className="h-4 w-4 mr-1" />
</h5>
<p className="text-red-700 text-xs leading-relaxed">{yearData.overall_fortune}</p>
</div>
<div className="border-l-4 border-green-400 pl-3">
<h5 className="font-semibold text-red-800 text-sm flex items-center">
<BarChart3 className="h-4 w-4 mr-1" />
</h5>
<p className="text-red-700 text-xs leading-relaxed">{yearData.career_fortune}</p>
</div>
</div>
<div className="space-y-3">
<div className="border-l-4 border-yellow-400 pl-3">
<h5 className="font-semibold text-red-800 text-sm flex items-center">
<DollarSign className="h-4 w-4 mr-1" />
</h5>
<p className="text-red-700 text-xs leading-relaxed">{yearData.wealth_fortune}</p>
</div>
<div className="border-l-4 border-pink-400 pl-3">
<h5 className="font-semibold text-red-800 text-sm flex items-center">
<Heart className="h-4 w-4 mr-1" />
</h5>
<p className="text-red-700 text-xs leading-relaxed">{yearData.relationship_fortune}</p>
</div>
</div>
<div className="space-y-3">
<div className="border-l-4 border-purple-400 pl-3">
<h5 className="font-semibold text-red-800 text-sm flex items-center">
<Activity className="h-4 w-4 mr-1" />
</h5>
<p className="text-red-700 text-xs leading-relaxed">{yearData.health_fortune}</p>
</div>
<div className="border-l-4 border-orange-400 pl-3">
<h5 className="font-semibold text-red-800 text-sm flex items-center">
<Sparkles className="h-4 w-4 mr-1" />
</h5>
<p className="text-red-700 text-xs leading-relaxed">{yearData.key_advice}</p>
</div>
</div>
</div>
{yearData.monthly_highlights && yearData.monthly_highlights.length > 0 && (
<div className="mt-4 pt-4 border-t border-yellow-200">
<h5 className="font-semibold text-red-800 text-sm mb-2 flex items-center">
<Calendar className="h-4 w-4 mr-1" />
</h5>
<div className="grid md:grid-cols-2 gap-2">
{yearData.monthly_highlights.map((highlight: string, hIndex: number) => (
<p key={hIndex} className="text-red-700 text-xs bg-yellow-50 p-2 rounded"> {highlight}</p>
))}
</div>
</div>
)}
</div>
))}
</div>
</div>
</CardContent>
</Card>
)}
{/* 专业人生指导 */}
{analysisData.life_guidance && (
<Card className="chinese-card-decoration dragon-corner border-2 border-yellow-400">
<CardHeader>
<CardTitle className="text-red-800 text-2xl font-bold chinese-text-shadow flex items-center">
<BookOpen className="mr-2 h-6 w-6 text-yellow-600" />
</CardTitle>
</CardHeader>
<CardContent>
<div className="bg-gradient-to-br from-red-50 to-yellow-50 rounded-lg p-6">
<div className="grid md:grid-cols-2 gap-6">
<div className="space-y-4">
<div className="bg-white p-4 rounded-lg border-l-4 border-blue-500">
<h4 className="font-bold text-red-800 mb-2"></h4>
<p className="text-red-700 leading-relaxed text-sm">
{analysisData.life_guidance.career_development}
</p>
</div>
<div className="bg-white p-4 rounded-lg border-l-4 border-green-500">
<h4 className="font-bold text-red-800 mb-2"></h4>
<p className="text-red-700 leading-relaxed text-sm">
{analysisData.life_guidance.wealth_management}
</p>
</div>
<div className="bg-white p-4 rounded-lg border-l-4 border-pink-500">
<h4 className="font-bold text-red-800 mb-2"></h4>
<p className="text-red-700 leading-relaxed text-sm">
{analysisData.life_guidance.marriage_relationships}
</p>
</div>
</div>
<div className="space-y-4">
<div className="bg-white p-4 rounded-lg border-l-4 border-purple-500">
<h4 className="font-bold text-red-800 mb-2"></h4>
<p className="text-red-700 leading-relaxed text-sm">
{analysisData.life_guidance.health_wellness}
</p>
</div>
<div className="bg-white p-4 rounded-lg border-l-4 border-yellow-500">
<h4 className="font-bold text-red-800 mb-2"></h4>
<p className="text-red-700 leading-relaxed text-sm">
{analysisData.life_guidance.personal_development}
</p>
</div>
<div className="bg-white p-4 rounded-lg border-l-4 border-indigo-500">
<h4 className="font-bold text-red-800 mb-2"></h4>
<p className="text-red-700 leading-relaxed text-sm">
{analysisData.life_guidance.overall_summary}
</p>
</div>
</div>
</div>
</div>
</CardContent>
</Card>
)}
{/* 现代应用建议 */}
{analysisData.modern_applications && (
<Card className="chinese-card-decoration dragon-corner border-2 border-yellow-400">
<CardHeader>
<CardTitle className="text-red-800 text-2xl font-bold chinese-text-shadow flex items-center">
<Zap className="mr-2 h-6 w-6 text-yellow-600" />
</CardTitle>
</CardHeader>
<CardContent>
<div className="bg-gradient-to-br from-red-50 to-yellow-50 rounded-lg p-6">
<div className="grid md:grid-cols-2 gap-6">
<div className="space-y-4">
<div className="bg-white p-4 rounded-lg border-l-4 border-blue-500">
<h4 className="font-bold text-red-800 mb-2"></h4>
<p className="text-red-700 leading-relaxed text-sm">
{analysisData.modern_applications.lifestyle_recommendations}
</p>
</div>
<div className="bg-white p-4 rounded-lg border-l-4 border-green-500">
<h4 className="font-bold text-red-800 mb-2"></h4>
<p className="text-red-700 leading-relaxed text-sm">
{analysisData.modern_applications.career_strategies}
</p>
</div>
</div>
<div className="space-y-4">
<div className="bg-white p-4 rounded-lg border-l-4 border-purple-500">
<h4 className="font-bold text-red-800 mb-2"></h4>
<p className="text-red-700 leading-relaxed text-sm">
{analysisData.modern_applications.relationship_advice}
</p>
</div>
<div className="bg-white p-4 rounded-lg border-l-4 border-orange-500">
<h4 className="font-bold text-red-800 mb-2"></h4>
<p className="text-red-700 leading-relaxed text-sm">
{analysisData.modern_applications.decision_making}
</p>
</div>
</div>
</div>
</div>
</CardContent>
</Card>
)}
{/* 分析报告尾部 */}
<Card className="chinese-card-decoration dragon-corner border-2 border-yellow-400">
<CardContent className="text-center py-8">
<div className="text-red-800">
<p className="text-lg font-bold mb-2"></p>
<p className="text-sm">{analysisData.analysis_date}</p>
<p className="text-xs mt-4 text-red-600">
</p>
</div>
</CardContent>
</Card>
</div>
</div>
);
};
export default CompleteBaziAnalysis;

View File

@@ -1,418 +1,273 @@
import React, { useState } from 'react';
import { Calendar, Clock, Star, BookOpen, Sparkles, User } from 'lucide-react';
import { Calendar, Clock, User, ArrowLeft } from 'lucide-react';
import { Button } from '../components/ui/Button';
import { Card, CardContent, CardHeader, CardTitle } from '../components/ui/Card';
import { localApi } from '../lib/localApi';
import CompleteBaziAnalysis from '../components/CompleteBaziAnalysis';
import { useAuth } from '../contexts/AuthContext';
import { toast } from 'sonner';
import { useNavigate } from 'react-router-dom';
// 生辰八字详情数据接口 - 匹配后端返回结构
interface PillarInfo {
tiangan: string;
dizhi: string;
tianganWuxing: string;
dizhiWuxing: string;
tianganYinYang: string;
dizhiYinYang: string;
combination: string;
pillarName: string;
shengxiao?: string;
tianganMeaning?: string;
dizhiMeaning?: string;
}
interface BaziApiResponse {
baziDetails: {
year: PillarInfo;
month: PillarInfo;
day: PillarInfo;
hour: PillarInfo;
};
rizhu: {
tiangan: string;
wuxing: string;
yinyang: string;
description: string;
meaning?: string;
};
summary: {
fullBazi: string;
birthInfo: {
solarDate: string;
birthTime: string;
year: number;
month: number;
day: number;
hour: number;
};
pillars: PillarInfo[];
};
interpretation: {
overall: string;
yearPillar: string;
monthPillar: string;
dayPillar: string;
hourPillar: string;
};
interface BirthData {
date: string;
time: string;
name?: string;
gender?: string;
}
const BaziDetailsPage: React.FC = () => {
const { user } = useAuth();
const [birthDate, setBirthDate] = useState('');
const [birthTime, setBirthTime] = useState('12:00');
const [baziData, setBaziData] = useState<BaziApiResponse | null>(null);
const [isLoading, setIsLoading] = useState(false);
const [error, setError] = useState<string | null>(null);
const navigate = useNavigate();
const [showAnalysis, setShowAnalysis] = useState(false);
const [birthData, setBirthData] = useState<BirthData>({
date: '',
time: '12:00',
name: user?.name || '',
gender: 'male'
});
// 五行颜色配置
const wuxingColors: { [key: string]: string } = {
'木': 'text-green-600 bg-green-50 border-green-300',
'火': 'text-red-600 bg-red-50 border-red-300',
'土': 'text-yellow-600 bg-yellow-50 border-yellow-300',
'金': 'text-gray-600 bg-gray-50 border-gray-300',
'水': 'text-blue-600 bg-blue-50 border-blue-300'
const handleInputChange = (field: keyof BirthData, value: string) => {
setBirthData(prev => ({
...prev,
[field]: value
}));
};
// 阴阳颜色配置
const yinyangColors: { [key: string]: string } = {
'阳': 'text-orange-600 bg-orange-50 border-orange-300',
'阴': 'text-purple-600 bg-purple-50 border-purple-300'
};
// 获取八字详细信息
const fetchBaziDetails = async () => {
if (!birthDate) {
const handleAnalyze = () => {
if (!birthData.date) {
toast.error('请选择您的出生日期');
return;
}
setIsLoading(true);
setError(null);
if (!birthData.time) {
toast.error('请选择您的出生时间');
return;
}
try {
// 调用本地API
const response = await localApi.functions.invoke('bazi-details', {
body: {
birthDate,
birthTime
}
});
setShowAnalysis(true);
toast.success('开始进行专业八字分析...');
};
if (response.error) {
throw new Error(response.error.message);
}
if (response.data?.data) {
setBaziData(response.data.data);
toast.success('八字详情分析完成!');
} else {
throw new Error('排盘结果为空');
}
} catch (err: any) {
console.error('八字排盘错误:', err);
setError(err.message || '分析失败,请稍后重试');
toast.error('分析失败,请稍后重试');
} finally {
setIsLoading(false);
const handleBack = () => {
if (showAnalysis) {
setShowAnalysis(false);
} else {
navigate('/analysis');
}
};
// 渲染四柱信息卡片
const renderPillarCard = (pillar: PillarInfo | null | undefined, index: number) => {
// 防护性检查:确保 pillar 对象存在
if (!pillar) {
return (
<Card key={index} className="chinese-card-decoration hover:shadow-xl transition-all duration-300 border-2 border-yellow-400">
<CardContent className="p-8 text-center">
<p className="text-red-600">...</p>
</CardContent>
</Card>
);
}
const pillarNames = ['年柱', '月柱', '日柱', '时柱'];
const pillarDescriptions = [
'代表祖辈与早年运势',
'代表父母与青年运势',
'代表自身与配偶',
'代表子女与晚年运势'
];
const handleReset = () => {
setBirthData({
date: '',
time: '12:00',
name: user?.name || '',
gender: 'male'
});
setShowAnalysis(false);
};
// 如果显示分析结果直接渲染CompleteBaziAnalysis组件
if (showAnalysis) {
return (
<Card key={index} className="chinese-card-decoration hover:shadow-xl transition-all duration-300 border-2 border-yellow-400">
<CardHeader className="text-center">
<CardTitle className="text-red-800 text-xl font-bold chinese-text-shadow">
{pillarNames[index] || '未知柱'}
</CardTitle>
<p className="text-red-600 text-sm">{pillarDescriptions[index] || '描述加载中'}</p>
</CardHeader>
<CardContent className="space-y-4">
{/* 天干地支大显示 */}
<div className="text-center">
<div className="text-4xl font-bold text-red-800 chinese-text-shadow mb-2">
{pillar?.combination || '未知'}
</div>
<div className="text-sm text-gray-600">
{pillar?.tiangan || '未知'} ({pillar?.tianganYinYang || '未知'}) + {pillar?.dizhi || '未知'} ({pillar?.dizhiYinYang || '未知'})
<div className="min-h-screen bg-gradient-to-br from-red-50 to-yellow-50">
{/* 顶部导航栏 */}
<div className="bg-white shadow-sm border-b border-yellow-200 sticky top-0 z-10">
<div className="max-w-7xl mx-auto px-4 py-4">
<div className="flex items-center justify-between">
<Button
onClick={handleBack}
variant="outline"
className="flex items-center space-x-2 border-red-300 text-red-700 hover:bg-red-50"
>
<ArrowLeft className="h-4 w-4" />
<span></span>
</Button>
<div className="text-center">
<h1 className="text-xl font-bold text-red-800"></h1>
<p className="text-sm text-red-600">
{birthData.name} {birthData.date} {birthData.time}
</p>
</div>
<Button
onClick={handleReset}
variant="outline"
className="border-yellow-300 text-yellow-700 hover:bg-yellow-50"
>
</Button>
</div>
</div>
{/* 天干信息 */}
<div className="bg-gradient-to-r from-red-50 to-yellow-50 rounded-lg p-3">
<h4 className="font-bold text-red-700 mb-2">{pillar?.tiangan || '未知'}</h4>
<div className="grid grid-cols-2 gap-2 text-sm">
<div className={`px-2 py-1 rounded border ${pillar?.tianganWuxing && wuxingColors[pillar.tianganWuxing] ? wuxingColors[pillar.tianganWuxing] : 'bg-gray-50 border-gray-300 text-gray-600'}`}>
{pillar?.tianganWuxing || '未知'}
</div>
<div className={`px-2 py-1 rounded border ${pillar?.tianganYinYang && yinyangColors[pillar.tianganYinYang] ? yinyangColors[pillar.tianganYinYang] : 'bg-gray-50 border-gray-300 text-gray-600'}`}>
{pillar?.tianganYinYang || '未知'}
</div>
</div>
</div>
{/* 地支信息 */}
<div className="bg-gradient-to-r from-yellow-50 to-red-50 rounded-lg p-3">
<h4 className="font-bold text-red-700 mb-2">{pillar?.dizhi || '未知'}</h4>
<div className="grid grid-cols-2 gap-2 text-sm">
<div className={`px-2 py-1 rounded border ${pillar?.dizhiWuxing && wuxingColors[pillar.dizhiWuxing] ? wuxingColors[pillar.dizhiWuxing] : 'bg-gray-50 border-gray-300 text-gray-600'}`}>
{pillar?.dizhiWuxing || '未知'}
</div>
<div className={`px-2 py-1 rounded border ${pillar?.dizhiYinYang && yinyangColors[pillar.dizhiYinYang] ? yinyangColors[pillar.dizhiYinYang] : 'bg-gray-50 border-gray-300 text-gray-600'}`}>
{pillar?.dizhiYinYang || '未知'}
</div>
</div>
</div>
</CardContent>
</Card>
</div>
{/* 分析结果 */}
<CompleteBaziAnalysis birthDate={birthData} />
</div>
);
};
}
// 显示输入表单
return (
<div className="space-y-8 relative">
{/* 页面装饰背景 */}
<div className="absolute top-0 left-0 w-32 h-32 opacity-20 pointer-events-none">
<img
src="/chinese_traditional_golden_ornate_frame.png"
alt=""
className="w-full h-full object-contain"
/>
</div>
<div className="absolute top-20 right-0 w-32 h-32 opacity-20 pointer-events-none">
<img
src="/chinese_traditional_golden_ornate_frame.png"
alt=""
className="w-full h-full object-contain rotate-180"
/>
</div>
{/* 标题区域 */}
<div className="text-center space-y-4 relative z-10">
<div className="w-16 h-16 mx-auto bg-gradient-to-br from-yellow-400 to-amber-600 rounded-full flex items-center justify-center shadow-2xl border-3 border-red-600">
<BookOpen className="w-8 h-8 text-red-800" />
<div className="min-h-screen bg-gradient-to-br from-red-50 to-yellow-50 py-8">
<div className="max-w-4xl mx-auto px-4">
{/* 返回按钮 */}
<div className="mb-6">
<Button
onClick={handleBack}
variant="outline"
className="flex items-center space-x-2 border-red-300 text-red-700 hover:bg-red-50"
>
<ArrowLeft className="h-4 w-4" />
<span></span>
</Button>
</div>
<h1 className="text-4xl md:text-5xl font-bold text-red-800 chinese-text-shadow font-serif">
<span className="block text-lg text-yellow-600 mt-2 font-normal">
</span>
</h1>
</div>
{/* 输入区域 */}
<Card className="chinese-card-decoration dragon-corner border-2 border-yellow-400">
<CardHeader>
<CardTitle className="text-red-800 text-2xl font-bold chinese-text-shadow flex items-center">
<Calendar className="mr-2 h-6 w-6 text-yellow-600" />
</CardTitle>
</CardHeader>
<CardContent>
<div className="grid md:grid-cols-2 gap-6">
<div>
<label className="block text-sm font-medium text-red-700 mb-2">
*
</label>
<input
type="date"
value={birthDate}
onChange={(e) => setBirthDate(e.target.value)}
className="w-full px-4 py-3 border-2 border-yellow-400 rounded-lg focus:outline-none focus:ring-2 focus:ring-red-500 bg-white text-red-800"
required
/>
</div>
<div>
<label className="block text-sm font-medium text-red-700 mb-2">
</label>
<input
type="time"
value={birthTime}
onChange={(e) => setBirthTime(e.target.value)}
className="w-full px-4 py-3 border-2 border-yellow-400 rounded-lg focus:outline-none focus:ring-2 focus:ring-red-500 bg-white text-red-800"
/>
</div>
</div>
<div className="mt-6">
<Button
onClick={fetchBaziDetails}
disabled={isLoading || !birthDate}
size="lg"
className="w-full chinese-red-glow text-white hover:shadow-xl transition-all duration-300 border-2 border-yellow-400"
>
{isLoading ? (
<>...</>
) : (
<>
<Star className="mr-2 h-5 w-5" />
</>
)}
</Button>
</div>
</CardContent>
</Card>
{/* 错误提示 */}
{error && (
<Card className="border-red-400 bg-red-50">
<CardContent className="p-4">
<p className="text-red-700 text-center">{error}</p>
</CardContent>
{/* 主标题 */}
<Card className="chinese-card-decoration dragon-corner border-2 border-yellow-400 mb-8">
<CardHeader className="text-center">
<CardTitle className="text-red-800 text-3xl font-bold chinese-text-shadow">
</CardTitle>
<p className="text-red-600 mt-2">
</p>
</CardHeader>
</Card>
)}
{/* 加载状态 */}
{isLoading && (
{/* 输入表单 */}
<Card className="chinese-card-decoration border-2 border-yellow-400">
<CardContent className="p-8">
<div className="text-center space-y-4">
<div className="w-16 h-16 mx-auto border-4 border-red-600 border-t-transparent rounded-full animate-spin"></div>
<p className="text-red-700 text-lg font-medium">...</p>
<p className="text-red-600 text-sm"></p>
<CardHeader>
<CardTitle className="text-red-800 text-xl font-bold chinese-text-shadow text-center">
</CardTitle>
</CardHeader>
<CardContent>
<div className="bg-gradient-to-br from-red-50 to-yellow-50 rounded-lg p-8">
<div className="space-y-6">
{/* 姓名输入 */}
<div className="space-y-2">
<label className="flex items-center text-red-800 font-semibold">
<User className="h-5 w-5 mr-2 text-yellow-600" />
</label>
<input
type="text"
value={birthData.name}
onChange={(e) => handleInputChange('name', e.target.value)}
placeholder="请输入您的姓名"
className="w-full px-4 py-3 border-2 border-yellow-300 rounded-lg focus:border-red-400 focus:outline-none transition-colors bg-white"
/>
</div>
{/* 性别选择 */}
<div className="space-y-2">
<label className="flex items-center text-red-800 font-semibold">
<User className="h-5 w-5 mr-2 text-yellow-600" />
</label>
<div className="flex space-x-4">
<label className="flex items-center space-x-2 cursor-pointer">
<input
type="radio"
name="gender"
value="male"
checked={birthData.gender === 'male'}
onChange={(e) => handleInputChange('gender', e.target.value)}
className="text-red-600 focus:ring-red-500"
/>
<span className="text-red-700"></span>
</label>
<label className="flex items-center space-x-2 cursor-pointer">
<input
type="radio"
name="gender"
value="female"
checked={birthData.gender === 'female'}
onChange={(e) => handleInputChange('gender', e.target.value)}
className="text-red-600 focus:ring-red-500"
/>
<span className="text-red-700"></span>
</label>
</div>
</div>
{/* 出生日期 */}
<div className="space-y-2">
<label className="flex items-center text-red-800 font-semibold">
<Calendar className="h-5 w-5 mr-2 text-yellow-600" />
*
</label>
<input
type="date"
value={birthData.date}
onChange={(e) => handleInputChange('date', e.target.value)}
className="w-full px-4 py-3 border-2 border-yellow-300 rounded-lg focus:border-red-400 focus:outline-none transition-colors bg-white"
required
/>
</div>
{/* 出生时间 */}
<div className="space-y-2">
<label className="flex items-center text-red-800 font-semibold">
<Clock className="h-5 w-5 mr-2 text-yellow-600" />
*
</label>
<input
type="time"
value={birthData.time}
onChange={(e) => handleInputChange('time', e.target.value)}
className="w-full px-4 py-3 border-2 border-yellow-300 rounded-lg focus:border-red-400 focus:outline-none transition-colors bg-white"
required
/>
<p className="text-sm text-red-600">
</p>
</div>
{/* 分析按钮 */}
<div className="pt-6">
<Button
onClick={handleAnalyze}
className="w-full bg-gradient-to-r from-red-600 to-yellow-600 hover:from-red-700 hover:to-yellow-700 text-white font-bold py-4 px-8 rounded-lg text-lg transition-all duration-300 transform hover:scale-105 shadow-lg"
>
</Button>
</div>
</div>
</div>
</CardContent>
</Card>
)}
{/* 八字详情结果 */}
{baziData && !isLoading && (
<div className="space-y-8">
{/* 八字概览 */}
<Card className="chinese-card-decoration dragon-corner border-2 border-yellow-400">
<CardHeader>
<CardTitle className="text-red-800 text-2xl font-bold chinese-text-shadow text-center">
</CardTitle>
</CardHeader>
<CardContent>
<div className="bg-gradient-to-br from-red-50 to-yellow-50 rounded-lg p-6">
<div className="text-center">
<h3 className="text-3xl font-bold text-red-800 chinese-text-shadow mb-4">
{baziData.summary?.fullBazi || '未知'}
</h3>
<p className="text-red-600 text-lg mb-4">
{baziData.summary?.birthInfo?.solarDate || '未知'} {baziData.summary?.birthInfo?.birthTime || '未知'}
</p>
<p className="text-red-700 leading-relaxed">
{baziData.interpretation?.overall || '暂无详细分析'}
</p>
{/* 说明信息 */}
<Card className="chinese-card-decoration border-2 border-yellow-400 mt-8">
<CardContent className="p-6">
<div className="text-center text-red-700">
<h3 className="font-bold text-lg mb-4"></h3>
<div className="grid md:grid-cols-2 lg:grid-cols-4 gap-4 text-sm">
<div className="bg-white p-3 rounded-lg border border-yellow-300">
<div className="font-semibold mb-1">🏛 </div>
<div></div>
</div>
<div className="bg-white p-3 rounded-lg border border-yellow-300">
<div className="font-semibold mb-1"> </div>
<div></div>
</div>
<div className="bg-white p-3 rounded-lg border border-yellow-300">
<div className="font-semibold mb-1">🌟 </div>
<div></div>
</div>
<div className="bg-white p-3 rounded-lg border border-yellow-300">
<div className="font-semibold mb-1">📅 </div>
<div></div>
</div>
</div>
</CardContent>
</Card>
{/* 日主信息 */}
<Card className="chinese-card-decoration dragon-corner border-2 border-yellow-400">
<CardHeader>
<CardTitle className="text-red-800 text-2xl font-bold chinese-text-shadow flex items-center">
<User className="mr-2 h-6 w-6 text-yellow-600" />
</CardTitle>
</CardHeader>
<CardContent>
<div className="bg-gradient-to-br from-red-50 to-yellow-50 rounded-lg p-6">
<div className="text-center">
<div className="text-6xl font-bold text-red-800 chinese-text-shadow mb-4">
{baziData.rizhu?.tiangan || '未知'}
</div>
<div className="grid md:grid-cols-2 gap-4 mb-4">
<div className={`px-4 py-2 rounded-lg border-2 ${baziData.rizhu?.wuxing ? wuxingColors[baziData.rizhu.wuxing] || 'bg-gray-50 border-gray-300' : 'bg-gray-50 border-gray-300'}`}>
<span className="font-bold">{baziData.rizhu?.wuxing || '未知'}</span>
</div>
<div className={`px-4 py-2 rounded-lg border-2 ${baziData.rizhu?.yinyang ? yinyangColors[baziData.rizhu.yinyang] || 'bg-gray-50 border-gray-300' : 'bg-gray-50 border-gray-300'}`}>
<span className="font-bold">{baziData.rizhu?.yinyang || '未知'}</span>
</div>
</div>
<p className="text-red-700 leading-relaxed">
{baziData.rizhu?.description || '暂无详细描述'}
</p>
</div>
</div>
</CardContent>
</Card>
{/* 四柱详细信息 */}
<Card className="chinese-card-decoration dragon-corner border-2 border-yellow-400">
<CardHeader>
<CardTitle className="text-red-800 text-2xl font-bold chinese-text-shadow text-center">
</CardTitle>
</CardHeader>
<CardContent>
<div className="grid lg:grid-cols-2 xl:grid-cols-4 gap-6">
{baziData?.baziDetails ? [
baziData.baziDetails.year,
baziData.baziDetails.month,
baziData.baziDetails.day,
baziData.baziDetails.hour
].map((pillar, index) =>
renderPillarCard(pillar, index)
) : (
<div className="col-span-full text-center text-red-600 py-8">
...
</div>
)}
</div>
</CardContent>
</Card>
{/* 命理解释 */}
<Card className="chinese-card-decoration dragon-corner border-2 border-yellow-400">
<CardHeader>
<CardTitle className="text-red-800 text-2xl font-bold chinese-text-shadow flex items-center">
<Sparkles className="mr-2 h-6 w-6 text-yellow-600" />
</CardTitle>
</CardHeader>
<CardContent>
<div className="bg-gradient-to-br from-red-50 to-yellow-50 rounded-lg p-6">
<div className="space-y-4">
{baziData.interpretation && Object.entries(baziData.interpretation).map(([key, value], index) => {
if (key === 'overall') return null; // 已在概览中显示
const titles: { [key: string]: string } = {
yearPillar: '年柱解释',
monthPillar: '月柱解释',
dayPillar: '日柱解释',
hourPillar: '时柱解释'
};
return (
<div key={key} className="flex items-start space-x-3 p-4 bg-white rounded-lg border-l-4 border-yellow-500">
<div className="w-8 h-8 bg-gradient-to-br from-yellow-400 to-amber-500 rounded-full flex items-center justify-center flex-shrink-0">
<span className="text-red-800 font-bold text-sm">{index}</span>
</div>
<div className="flex-1">
<h4 className="font-bold text-red-800 mb-1">{titles[key] || '说明'}</h4>
<p className="text-red-700 font-medium">{value}</p>
</div>
</div>
);
})}
</div>
</div>
</CardContent>
</Card>
</div>
)}
<p className="text-xs mt-4 text-red-600">
</p>
</div>
</CardContent>
</Card>
</div>
</div>
);
};