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

Binary file not shown.

Binary file not shown.

File diff suppressed because it is too large Load Diff

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>
);
};

Binary file not shown.

Binary file not shown.

Binary file not shown.