Files
Jyotisha/jyotish-app/analysis-renderers.js
T
732642856 2457e23271 feat: Jyotish Web App v4.3 完整印度占星前端应用
技术栈: Vite + 原生HTML/JS/CSS + swisseph-wasm
功能模块:
- 排盘引擎: 行星/宫位/Dasha/Yoga/相位/燃烧检测/130+城市
- 高级计算: Karaka/Arudha/16分盘/Ashtakavarga/Shadbala/Panchanga/Chara Dasha
- 73个Yoga识别 (Pancha Mahapurusha + 扩展A/B)
- 生时校正v2.0: K.N.Rao方法论, 多维评分
- Transit v2.0: 日期选择/全行星/AV评分/Sade Sati
- 深度分析7子模块 + 扩展分析5子模块
- 导出v2.0: JSON 100%覆盖Python 13模块 + SVG/PNG
- i18n中英双语 + 点读机95词条 + AI聊天面板
- 认证+订阅 (JWT/Apple Sign-In/IAP)
- 浅色极简主题 (fatetell.com风格)
2026-04-30 03:51:30 +08:00

232 lines
15 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* Deep Analysis Renderers v1.0
* 渲染: Raman功能表 · PACDARES · 宫位互影响 · Vargottama · 频率 · 三角验证 · Raman评级
*/
import { PLANET_CN, PLANET_SYMBOLS, SIGNS_CN, SIGNS } from './jyotish-engine.js';
import { RAMAN_TABLES, HOUSE_DOMAINS } from './analysis-deep.js';
import { t, signName, planetName, houseLabel } from './i18n.js';
const $ = id => document.getElementById(id);
// ============================================================================
// 1. Raman 功能吉凶星表
// ============================================================================
export function renderFunctionalNature(ascSign) {
const c = $('raman-fn-table'); if (!c) return;
const t = RAMAN_TABLES[ascSign]; if (!t) { c.innerHTML = '<p>无数据</p>'; return; }
const FN_LABEL = {
best_benefic: { text: '最佳吉星', cls: 'fn-best' },
benefic: { text: '吉星', cls: 'fn-good' },
yogakaraka: { text: 'Yogakaraka', cls: 'fn-yk' },
neutral: { text: '中性', cls: 'fn-neutral' },
malefic: { text: '凶星', cls: 'fn-bad' },
worst: { text: '最凶', cls: 'fn-worst' },
};
const mkBadges = (list, key) => list.map(p =>
`<span class="fn-badge ${FN_LABEL[key].cls}">${PLANET_SYMBOLS[p]||''} ${PLANET_CN[p]}</span>`
).join('');
let html = `<div class="fn-row fn-best-row"><span class="fn-label">最佳吉星</span><div class="fn-badges">${mkBadges(t.best,'best_benefic')}</div></div>`;
if (t.yk) html += `<div class="fn-row fn-yk-row"><span class="fn-label">Yogakaraka</span><div class="fn-badges"><span class="fn-badge fn-yk">${PLANET_SYMBOLS[t.yk]||''} ${PLANET_CN[t.yk]}</span></div></div>`;
html += `<div class="fn-row fn-good-row"><span class="fn-label">吉星</span><div class="fn-badges">${mkBadges(t.good,'benefic')}</div></div>`;
html += `<div class="fn-row fn-neutral-row"><span class="fn-label">中性</span><div class="fn-badges">${mkBadges(t.neutral,'neutral')}</div></div>`;
html += `<div class="fn-row fn-bad-row"><span class="fn-label">凶星</span><div class="fn-badges">${mkBadges(t.bad,'malefic')}</div></div>`;
if (t.worst.length) html += `<div class="fn-row fn-worst-row"><span class="fn-label">最凶</span><div class="fn-badges">${mkBadges(t.worst,'worst')}</div></div>`;
html += `<div class="fn-row fn-maraka-row"><span class="fn-label">Maraka</span><div class="fn-badges">${mkBadges(t.maraka,'worst')}</div></div>`;
c.innerHTML = html;
}
// ============================================================================
// 2. PACDARES 八维分析
// ============================================================================
export function renderPACDARES(pac) {
const c = $('pacdares-grid'); if (!c) return;
const SECTIONS = [
{ key:'P', title:'P · Position 位置', icon:'P', desc:'行星本位:落宫·星座·状态·功能属性' },
{ key:'A', title:'A · Aspect 相位', icon:'A', desc:'行星接收的相位影响' },
{ key:'C', title:'C · Conjunction 合相', icon:'C', desc:'同宫行星合相关系与能量污染' },
{ key:'D', title:'D · Dhana 财富', icon:'D', desc:'财富Yoga检测' },
{ key:'Ar', title:'Ar · Arishta 凶象', icon:'!', desc:'凶象检测' },
{ key:'R', title:'R · Raja 王者', icon:'R', desc:'Raja Yoga & Yogakaraka' },
{ key:'E', title:'E · Exchange 互换', icon:'E', desc:'Parivartana 宫主星互换' },
{ key:'S', title:'S · Special 特殊', icon:'S', desc:'逆行·燃烧·入旺·落陷' },
];
let html = '';
for (const s of SECTIONS) {
const items = pac[s.key] || [];
const count = items.length;
html += `<div class="pac-section"><div class="pac-header"><span class="pac-icon">${s.icon}</span><span class="pac-title">${s.title}</span><span class="pac-count">${count}</span></div><div class="pac-desc">${s.desc}</div>`;
if (count === 0) {
html += `<div class="pac-empty">未检测到</div>`;
} else {
html += `<div class="pac-items">`;
for (const it of items) html += renderPACItem(s.key, it);
html += `</div>`;
}
html += `</div>`;
}
c.innerHTML = html;
}
function renderPACItem(key, it) {
switch(key) {
case 'P': return `<div class="pac-item pac-pos"><span class="pi-planet">${PLANET_SYMBOLS[it.planet]||''} ${it.pcn}</span><span class="pi-detail">${it.scn} H${it.house} ${it.status||''} ${it.retro?'℞':''} ${it.combust?'[燃]':''}</span><span class="pi-fn fn-${fnCls(it.fn)}">${fnLabel(it.fn)}</span><span class="pi-hr">主管H${it.hr.join('/')}</span></div>`;
case 'A': return `<div class="pac-item pac-asp"><span class="pi-planet">${PLANET_SYMBOLS[it.planet]||''} ${it.pcn} (H${it.house})</span>${it.received.map(r=>`<span class="pi-recv ${r.impact==='吉'?'recv-good':r.impact==='凶'?'recv-bad':'recv-neut'}">${PLANET_CN[r.from]} ${r.type}${r.impact}</span>`).join('')}</div>`;
case 'C': return `<div class="pac-item pac-conj${it.pollution?' pac-poll':''}"><span class="pi-planets">${it.p1cn} + ${it.p2cn}</span><span class="pi-house">H${it.house}</span>${it.pollution?'<span class="pi-poll-badge">[!] 能量污染</span>':''}<span class="pi-fn fn-${fnCls(it.f1)}">${fnLabel(it.f1)}</span><span class="pi-fn fn-${fnCls(it.f2)}">${fnLabel(it.f2)}</span></div>`;
case 'D': return `<div class="pac-item pac-dhana"><span class="pi-type">${it.type}</span><span class="pi-lords">${it.lcn}</span><span class="pi-house">H${it.house}</span><span class="pi-str">${it.str}</span></div>`;
case 'Ar': return `<div class="pac-item pac-arishta"><span class="pi-type">${it.type}</span><span class="pi-desc">${it.desc}</span></div>`;
case 'R': return `<div class="pac-item pac-raja"><span class="pi-type">${it.type}</span><span class="pi-lords">${it.lcn}</span><span class="pi-house">H${it.house}</span><span class="pi-str">${it.str}</span></div>`;
case 'E': return `<div class="pac-item pac-exchg"><span class="pi-type">${it.type} (${it.nature})</span><span class="pi-desc">${it.desc}</span></div>`;
case 'S': return `<div class="pac-item pac-special"><span class="pi-type">${it.type}</span><span class="pi-desc">${it.desc}</span></div>`;
default: return '';
}
}
function fnCls(fn) { return fn==='yogakaraka'?'yk':fn==='best_benefic'?'best':fn==='benefic'?'good':fn==='malefic'?'bad':fn==='worst'?'worst':'neut'; }
function fnLabel(fn) { return fn==='yogakaraka'?'YK':fn==='best_benefic'?'大吉':fn==='benefic'?'吉':fn==='malefic'?'凶':fn==='worst'?'大凶':'中'; }
// ============================================================================
// 3. 宫位互影响矩阵
// ============================================================================
export function renderHouseInfluence(matrix) {
const c = $('house-influence-grid'); if (!c) return;
let html = '';
for (const h of matrix) {
const vd = h.verdict;
const vc = vd==='强力'?'vd-strong':vd==='吉'?'vd-good':vd==='中'?'vd-neut':vd==='弱'?'vd-weak':'vd-afflicted';
html += `<div class="hi-card ${vc}"><div class="hi-header"><span class="hi-num">H${h.house}</span><span class="hi-sign">${h.scn}</span><span class="hi-lord">主: ${h.lcn}</span></div><div class="hi-score ${vc}">${h.total>0?'+':''}${h.total}</div><div class="hi-verdict ${vc}">${h.verdict}</div><div class="hi-items">`;
for (const it of h.items) {
const ic = it.sc > 0 ? 'hi-pos' : it.sc < 0 ? 'hi-neg' : 'hi-zero';
html += `<div class="hi-influence ${ic}"><span class="hi-src">${it.src}</span><span class="hi-sc">${it.sc>0?'+':''}${it.sc}</span></div>`;
}
html += `</div></div>`;
}
c.innerHTML = html;
}
// ============================================================================
// 4. Vargottama 检测
// ============================================================================
export function renderVargottama(vgList) {
const c = $('vargottama-section'); if (!c) return;
if (!vgList.length) { c.innerHTML = '<div class="vg-empty">未检测到 Vargottama 行星</div>'; return; }
let html = '<div class="vg-grid">';
for (const v of vgList) {
html += `<div class="vg-card"><span class="vg-planet">${PLANET_SYMBOLS[v.planet]||''} ${v.pcn}</span><span class="vg-badge">Vargottama</span><span class="vg-sign">${v.d1}</span></div>`;
}
html += '</div>';
html += '<div class="vg-note">Vargottama:行星在本命盘(D1)和九分盘(D9)落入同一星座,能量倍增。无论吉凶,该行星能量都会被放大。</div>';
c.innerHTML = html;
}
// ============================================================================
// 5. 行星频率分析
// ============================================================================
export function renderFrequency(freq) {
const c = $('frequency-grid'); if (!c) return;
let html = '';
for (const [pn, f] of Object.entries(freq)) {
const lc = f.pct>=50?'freq-vhigh':f.pct>=40?'freq-high':f.pct>=30?'freq-mid':'freq-low';
html += `<div class="freq-card"><div class="freq-header"><span class="freq-planet">${PLANET_SYMBOLS[pn]||''} ${f.pcn}</span><span class="freq-pct ${lc}">${f.pct}%</span></div><div class="freq-top">${f.topScn} (${f.count}/${16})</div><div class="freq-level ${lc}">${f.level}</div><div class="freq-bar-wrap"><div class="freq-bar" style="width:${f.pct}%"></div></div><div class="freq-dist">${f.dist.map(d=>`${d.scn} ${d.p}%`).join(' · ')}</div></div>`;
}
c.innerHTML = html;
}
// ============================================================================
// 6. D1 × D9 × D10 三角验证
// ============================================================================
export function renderTriangle(tri) {
const c = $('triangle-table'); if (!c) return;
let html = `<table class="tri-table"><thead><tr><th>行星</th><th>D1</th><th>D1状态</th><th>D9</th><th>D9状态</th><th>D10</th><th>D10状态</th><th>Vg</th><th>判定</th></tr></thead><tbody>`;
for (const t of tri) {
const vc = t.verdict.includes('双重强势')?'tri-best':t.verdict.includes('未兑现')?'tri-break':t.verdict.includes('反击')?'tri-comeback':t.verdict.includes('双重弱势')?'tri-worst':'tri-ok';
html += `<tr><td>${PLANET_SYMBOLS[t.planet]||''} ${t.pcn}</td><td>${t.d1sign}</td><td class="${stCls(t.d1st)}">${t.d1st}</td><td>${t.d9sign}</td><td class="${stCls(t.d9st)}">${t.d9st}</td><td>${t.d10sign}</td><td class="${stCls(t.d10st)}">${t.d10st}</td><td>${t.vargottama?'✓':'—'}</td><td class="${vc}">${t.verdict}</td></tr>`;
}
html += '</tbody></table>';
c.innerHTML = html;
}
function stCls(st) { return st==='入旺'||st==='入庙'?'st-good':st==='落陷'?'st-bad':'st-neut'; }
// ============================================================================
// 7. Raman 宫位评级(六步法)
// ============================================================================
export function renderRamanGrades(scores) {
const c = $('raman-grades-grid'); if (!c) return;
let html = '';
for (const s of scores) {
const gc = s.grade==='A'?'grade-a':s.grade==='B'?'grade-b':s.grade==='C'?'grade-c':s.grade==='D'?'grade-d':'grade-f';
const domain = HOUSE_DOMAINS[s.house];
html += `<div class="rg-card ${gc}"><div class="rg-top"><span class="rg-num">H${s.house}</span><span class="rg-sign">${s.scn}</span><span class="rg-grade ${gc}">${s.grade}</span></div><div class="rg-domain">${domain?domain.name:''}</div><div class="rg-scores"><span class="rg-pos">+${s.pos}</span> / <span class="rg-neg">-${s.neg}</span> = <span class="rg-net">${s.net>0?'+':''}${s.net}</span></div><div class="rg-lord">宫主: ${s.lcn}</div><div class="rg-details">`;
for (const d of s.details) html += `<div class="rg-detail ${d.startsWith('✓')?'rd-pos':d.startsWith('✗')?'rd-neg':'rd-neut'}">${d}</div>`;
html += '</div></div>';
}
c.innerHTML = html;
}
// ============================================================================
// 8. Trikona-Kendra 三方四正映射渲染
// ============================================================================
export function renderTrikonaKendra(data) {
const c = document.getElementById('trikona-kendra-grid');
if (!c || !data) return;
const { houseLords, trikonaConnections, kendraConnections, crossConnections, overallRating, hasRaja } = data;
let html = `<div class="tk-rating">${overallRating}</div>`;
// 关键宫位宫主星一览
html += `<div class="tk-section"><h5 class="tk-subtitle">关键宫位宫主星</h5><div class="tk-lords-grid">`;
const keyHouses = [1, 4, 5, 7, 9, 10];
const houseTag = { 1: '命宫', 4: '家庭', 5: '创意', 7: '婚姻', 9: '命运', 10: '事业' };
const groupTag = h => [1,5,9].includes(h) ? '三方' : '四正';
for (const h of keyHouses) {
const hl = houseLords[h];
const tag = houseTag[h] || '';
const grp = groupTag(h);
html += `<div class="tk-lord-card">
<div class="tk-lord-house">H${h} <small>${tag}</small> <span class="tk-badge tk-badge-${grp === '三方' ? 'trikona' : 'kendra'}">${grp}</span></div>
<div class="tk-lord-name">${hl.lord_cn} (${hl.lord})</div>
<div class="tk-lord-in">→ H${hl.lordInHouse || '?'} ${hl.lordStatus || ''} ${hl.lordRetro ? '℞' : ''}</div>
</div>`;
}
html += '</div></div>';
// 三方连接
if (trikonaConnections.length > 0) {
html += `<div class="tk-section"><h5 class="tk-subtitle">Trikona 三方连接 (1-5-9)</h5><div class="tk-connections">`;
for (const conn of trikonaConnections) {
const qClass = conn.quality === 'excellent' ? 'tk-exc' : conn.quality === 'strong' ? 'tk-str' : 'tk-good';
html += `<div class="tk-conn ${qClass}"><span class="tk-conn-icon">${conn.quality === 'excellent' ? '★' : conn.quality === 'strong' ? '◆' : '→'}</span> ${conn.desc}</div>`;
}
html += '</div></div>';
} else {
html += `<div class="tk-section"><h5 class="tk-subtitle">Trikona 三方连接 (1-5-9)</h5><div class="tk-empty">三方宫主星之间无直接连接</div></div>`;
}
// 四正连接
if (kendraConnections.length > 0) {
html += `<div class="tk-section"><h5 class="tk-subtitle">Kendra 四正连接 (1-4-7-10)</h5><div class="tk-connections">`;
for (const conn of kendraConnections) {
const qClass = conn.quality === 'excellent' ? 'tk-exc' : conn.quality === 'strong' ? 'tk-str' : 'tk-good';
html += `<div class="tk-conn ${qClass}"><span class="tk-conn-icon">${conn.quality === 'excellent' ? '★' : conn.quality === 'strong' ? '◆' : '→'}</span> ${conn.desc}</div>`;
}
html += '</div></div>';
} else {
html += `<div class="tk-section"><h5 class="tk-subtitle">Kendra 四正连接 (1-4-7-10)</h5><div class="tk-empty">四正宫主星之间无直接连接</div></div>`;
}
// 三方×四正交叉
if (crossConnections.length > 0) {
html += `<div class="tk-section"><h5 class="tk-subtitle">Trikona × Kendra 交叉连接 (Raja Yoga)</h5><div class="tk-connections">`;
for (const conn of crossConnections) {
const qClass = conn.quality === 'maha-raja' ? 'tk-maha' : 'tk-raja';
html += `<div class="tk-conn ${qClass}"><span class="tk-conn-icon">${conn.quality === 'maha-raja' ? '★★' : '★'}</span> ${conn.desc}</div>`;
}
html += '</div></div>';
} else {
html += `<div class="tk-section"><h5 class="tk-subtitle">Trikona × Kendra 交叉连接</h5><div class="tk-empty">三方与四正之间无交叉 Raja Yoga 连接</div></div>`;
}
c.innerHTML = html;
}