dsh-metaphysics 0.57.0 → 0.58.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +1204 -1115
- package/README.md +20 -8
- package/client.almanac.js +59 -58
- package/client.bazi.js +102 -101
- package/client.chenggu.js +59 -58
- package/client.convert.js +59 -58
- package/client.daliuren.js +94 -93
- package/client.fengshui.js +58 -57
- package/client.hepan.js +103 -102
- package/client.js +353 -277
- package/client.liuyao.js +96 -95
- package/client.mbti.js +59 -58
- package/client.name.js +56 -55
- package/client.numerology.js +59 -58
- package/client.qimen.js +94 -93
- package/client.star.js +59 -58
- package/client.tarot.js +93 -92
- package/client.trend.js +96 -95
- package/client.xiaoliuren.js +59 -58
- package/client.zeri.js +59 -58
- package/client.ziwei.js +102 -101
- package/engine/bridge.js +211 -0
- package/engine/citations.js +57 -3
- package/engine/diff.js +54 -0
- package/engine/hepan.js +43 -0
- package/engine/index.js +111 -43
- package/engine/interpret-knowledge.js +3 -2
- package/engine/trace.js +18 -18
- package/engine/zeri.js +89 -13
- package/index.js +18 -2
- package/package.json +116 -115
- package//345/221/275/347/220/206/346/216/222/347/233/230.html +872 -95
|
@@ -346,7 +346,7 @@ h3::after{content:"";flex:1;height:1px;background:linear-gradient(90deg,var(--li
|
|
|
346
346
|
<div class="wrap">
|
|
347
347
|
<header>
|
|
348
348
|
<div class="brand-row">
|
|
349
|
-
<div class="brand"><span class="seal" aria-hidden="true">命</span><span class="brand-txt">命理排盘<span class="ver">v0.
|
|
349
|
+
<div class="brand"><span class="seal" aria-hidden="true">命</span><span class="brand-txt">命理排盘<span class="ver">v0.58.0</span><small>八字 · 紫微 · 六爻 · 梅花 · 六壬 · 奇门 · 合盘 · 塔罗 全部在本机计算</small></span></div>
|
|
350
350
|
<div class="view-switch">
|
|
351
351
|
<span class="hint">熟练了可以换回平铺</span>
|
|
352
352
|
<span class="seg" role="group" aria-label="首屏样式">
|
|
@@ -455,6 +455,17 @@ h3::after{content:"";flex:1;height:1px;background:linear-gradient(90deg,var(--li
|
|
|
455
455
|
<button data-chart="ziwei">紫微</button>
|
|
456
456
|
</div>
|
|
457
457
|
</div>
|
|
458
|
+
<label class="field check" title="再加一个人:你×对方、你×第三方、对方×第三方三对并列,不合成三人总分">
|
|
459
|
+
<input id="h3on" type="checkbox"> 三方合盘(加入第三人)
|
|
460
|
+
</label>
|
|
461
|
+
<div class="grp" id="for-hepan3" style="display:none">
|
|
462
|
+
<label class="field">第三方出生日期<input id="p2date" type="date" value="1990-01-01"></label>
|
|
463
|
+
<label class="field">第三方出生时刻<input id="p2time" type="time" value="12:00"></label>
|
|
464
|
+
<label class="field">第三方性别
|
|
465
|
+
<select id="p2gender"><option value="female">女</option><option value="male">男</option></select>
|
|
466
|
+
</label>
|
|
467
|
+
<label class="field">第三方出生地(可空)<input id="p2addr" placeholder="如 广东广州"></label>
|
|
468
|
+
</div>
|
|
458
469
|
</div>
|
|
459
470
|
<!-- 奇门:换局规则(fieldset + 原生 radio)与取用方向;换规则会重起局 -->
|
|
460
471
|
<div class="grp" id="for-qimen" style="display:none">
|
|
@@ -518,6 +529,9 @@ h3::after{content:"";flex:1;height:1px;background:linear-gradient(90deg,var(--li
|
|
|
518
529
|
</select>
|
|
519
530
|
</label>
|
|
520
531
|
<label class="field">看多少天<input id="zdays" type="number" min="1" max="60" value="30"></label>
|
|
532
|
+
<label class="field">候选日(可空,只比这几天)
|
|
533
|
+
<input id="zcand" placeholder="如 2026-12-01, 2026-12-08, 2026-12-15">
|
|
534
|
+
</label>
|
|
521
535
|
<label class="field">当事人属相(可空)
|
|
522
536
|
<select id="zsheng">
|
|
523
537
|
<option value="">不排除</option>
|
|
@@ -616,7 +630,7 @@ h3::after{content:"";flex:1;height:1px;background:linear-gradient(90deg,var(--li
|
|
|
616
630
|
<h3>对拍:把典籍当期望值</h3>
|
|
617
631
|
<ul><li>六爻:京房八宫卦序与世应,64 卦全表;旬空六旬表</li><li>六壬:日干寄宫、中气换将;紫微:纳音定五行局、紫微天府相照</li><li>卦例:《增删卜易》9 条 +《卜筮正宗》128 条(93 成对 + 35 静卦),核本变卦 / 动爻数 / 卦宫 / 缺爻 / 旬空</li><li>奇门:20 个时辰 × 9 宫,核四柱 / 局数 / 值符 / 值使 / 星门神 / 天盘干</li></ul>
|
|
618
632
|
<h3>工程验证</h3>
|
|
619
|
-
<ul><li>测试:
|
|
633
|
+
<ul><li>测试:128 个测试文件 / 6024 条断言;七道闸(回归 · 断言基线 · 测试可失败自检 · HTML 注入面 · 工程约定 · 产物宿主形态 · 随机扫射)</li><li>可复现:seed / asOf 显式;同一输入同一输出</li><li>口径:晚子时、真太阳时、奇门三流派、闰月归月都可切换,并有字段级差异对照</li></ul>
|
|
620
634
|
<h3>已知边界(不猜)</h3>
|
|
621
635
|
<ul><li>六爻应期(具体哪天应验)不做;卦例只录动爻个数,不录爻位</li><li>财富/健康只给结构判断与档位,不给金额区间、不作诊断</li><li>紫微飞星已做「单一支化逐宫追到成环」与「三方四正 16 条按落宫归堆」,但都未叠大限流年四化、不折吉凶;八字交运日已能算到日/时,但未与第三方排盘案例批量对拍</li></ul>
|
|
622
636
|
<p class="note">内容用于传统文化学习与自我探索,不构成医疗、投资、法律或人事决策依据。</p>
|
|
@@ -1001,8 +1015,62 @@ const CALIBER_SWITCHES = {
|
|
|
1001
1015
|
},
|
|
1002
1016
|
],
|
|
1003
1017
|
};
|
|
1018
|
+
|
|
1019
|
+
/**
|
|
1020
|
+
* 口径并排:把「换口径后哪几条结论变了」抽成两列表格。
|
|
1021
|
+
* 只读结果里的既有字段,不重算、不判定哪套更准;字段缺省时留空,不编值。
|
|
1022
|
+
*/
|
|
1023
|
+
const CALIBER_VIEWS = {
|
|
1024
|
+
bazi: [
|
|
1025
|
+
{ path: 'pillars.day.ganzhi', label: '日柱' },
|
|
1026
|
+
{ path: 'pillars.hour.ganzhi', label: '时柱' },
|
|
1027
|
+
{ path: 'strength.level', label: '旺衰' },
|
|
1028
|
+
{ path: 'frame.primaryName', label: '格局' },
|
|
1029
|
+
{ path: 'frame.yongshen.favor', label: '喜用' },
|
|
1030
|
+
{ path: 'dayun.jiao.at', label: '起运' },
|
|
1031
|
+
],
|
|
1032
|
+
ziwei: [
|
|
1033
|
+
{ path: 'soul.ganzhi', label: '命宫' },
|
|
1034
|
+
{ path: 'body.branch', label: '身宫支' },
|
|
1035
|
+
{ path: 'bureau.name', label: '五行局' },
|
|
1036
|
+
{ path: 'bodyMaster', label: '身主' },
|
|
1037
|
+
{ path: 'daxianList.0.ages', label: '起大限' },
|
|
1038
|
+
],
|
|
1039
|
+
qimen: [
|
|
1040
|
+
{ path: 'siZhu.day', label: '日柱' },
|
|
1041
|
+
{ path: 'ju', label: '局数' },
|
|
1042
|
+
{ path: 'zhiFu.star', label: '值符' },
|
|
1043
|
+
{ path: 'zhiShi.gate', label: '值使' },
|
|
1044
|
+
],
|
|
1045
|
+
};
|
|
1046
|
+
|
|
1047
|
+
function pickPath(obj, path) {
|
|
1048
|
+
let cur = obj;
|
|
1049
|
+
for (const seg of String(path).split('.')) {
|
|
1050
|
+
if (cur === null || cur === undefined) return undefined;
|
|
1051
|
+
cur = cur[seg];
|
|
1052
|
+
}
|
|
1053
|
+
return cur;
|
|
1054
|
+
}
|
|
1055
|
+
|
|
1056
|
+
/** 并排单元格:数组用「、」串起,缺省留空,不把 undefined 写成字符串 */
|
|
1057
|
+
function viewCell(value) {
|
|
1058
|
+
if (value === undefined || value === null) return '—';
|
|
1059
|
+
if (Array.isArray(value)) return value.join('、');
|
|
1060
|
+
if (typeof value === 'object') return '—';
|
|
1061
|
+
return String(value);
|
|
1062
|
+
}
|
|
1063
|
+
|
|
1064
|
+
function viewRows(system, current, alt) {
|
|
1065
|
+
const defs = CALIBER_VIEWS[system] || [];
|
|
1066
|
+
return defs.map((d) => {
|
|
1067
|
+
const a = viewCell(pickPath(current, d.path));
|
|
1068
|
+
const b = viewCell(pickPath(alt, d.path));
|
|
1069
|
+
return { key: d.path, label: d.label, current: a, alt: b, changed: a !== b };
|
|
1070
|
+
});
|
|
1071
|
+
}
|
|
1004
1072
|
|
|
1005
|
-
return { diffObjects, labelOf, categoryOf, rootOf, sortDiffs, summarize, formatValue, renderDiff, CALIBER_SWITCHES };
|
|
1073
|
+
return { diffObjects, labelOf, categoryOf, rootOf, sortDiffs, summarize, formatValue, renderDiff, CALIBER_SWITCHES, CALIBER_VIEWS, viewCell, viewRows };
|
|
1006
1074
|
})();
|
|
1007
1075
|
|
|
1008
1076
|
__mods.fingerprint = (function () {
|
|
@@ -9087,6 +9155,49 @@ function yearBranchRelation(a, b) {
|
|
|
9087
9155
|
return { name: '无直接关系', score: 0, text: '属相之间没有合冲刑害,表层影响小,以日柱为主' };
|
|
9088
9156
|
}
|
|
9089
9157
|
|
|
9158
|
+
/**
|
|
9159
|
+
* 三方合盘:把三对两两结果并列,不合成「三人总分」。
|
|
9160
|
+
* 任何两两关系都独立成立,合成一个数会把「和谁相处」混为一谈;
|
|
9161
|
+
* 这里只做三件事:列出每对的分数与三维度、指出最顺/最需磨合的一对、找出差异最大的一维。
|
|
9162
|
+
* @param {{key:string,a:string,b:string,result:object}[]} pairs 三对结果(顺序固定为 a-b、a-c、b-c)
|
|
9163
|
+
*/
|
|
9164
|
+
function tripleOf(pairs) {
|
|
9165
|
+
const brief = (r) => {
|
|
9166
|
+
const s = (r && r.score) || {};
|
|
9167
|
+
return {
|
|
9168
|
+
total: typeof s.total === 'number' ? s.total : null,
|
|
9169
|
+
level: s.level || '',
|
|
9170
|
+
dimensions: (Array.isArray(r && r.dimensions) ? r.dimensions : [])
|
|
9171
|
+
.map((d) => ({ key: d.key, label: d.label, score: typeof d.score === 'number' ? d.score : 0 })),
|
|
9172
|
+
};
|
|
9173
|
+
};
|
|
9174
|
+
const rows = pairs.map((p) => ({ key: p.key, a: p.a, b: p.b, ...brief(p.result) }));
|
|
9175
|
+
const sorted = rows.filter((x) => typeof x.total === 'number').sort((x, y) => y.total - x.total);
|
|
9176
|
+
const best = sorted[0] || null;
|
|
9177
|
+
const hardest = sorted[sorted.length - 1] || null;
|
|
9178
|
+
// 同一维度在三对里差得最多的一维:三方之间最敏感的地方
|
|
9179
|
+
let focus = null;
|
|
9180
|
+
const keys = rows[0] ? rows[0].dimensions.map((d) => d.key) : [];
|
|
9181
|
+
for (const key of keys) {
|
|
9182
|
+
const vals = rows.map((r) => (r.dimensions.find((d) => d.key === key) || {}));
|
|
9183
|
+
const scores = vals.map((d) => (typeof d.score === 'number' ? d.score : 0));
|
|
9184
|
+
const spread = Math.max(...scores) - Math.min(...scores);
|
|
9185
|
+
if (!focus || spread > focus.spread) {
|
|
9186
|
+
focus = { key, label: (vals[0] && vals[0].label) || key, spread };
|
|
9187
|
+
}
|
|
9188
|
+
}
|
|
9189
|
+
return {
|
|
9190
|
+
pairs: rows,
|
|
9191
|
+
best: best ? { key: best.key, a: best.a, b: best.b, total: best.total, level: best.level } : null,
|
|
9192
|
+
hardest: hardest && hardest !== best
|
|
9193
|
+
? { key: hardest.key, a: hardest.a, b: hardest.b, total: hardest.total, level: hardest.level }
|
|
9194
|
+
: null,
|
|
9195
|
+
spread: best && hardest ? best.total - hardest.total : 0,
|
|
9196
|
+
focus,
|
|
9197
|
+
note: '三方只把三对两两关系并列,不合成「三人总分」:和不同的人相处是不同的事,合成一个数会把差异抹平。',
|
|
9198
|
+
};
|
|
9199
|
+
}
|
|
9200
|
+
|
|
9090
9201
|
/** 合盘主函数 */
|
|
9091
9202
|
function buildHepan(input) {
|
|
9092
9203
|
assertCivil(input && input.a, 'a');
|
|
@@ -9253,7 +9364,7 @@ function buildHepan(input) {
|
|
|
9253
9364
|
};
|
|
9254
9365
|
}
|
|
9255
9366
|
|
|
9256
|
-
return { buildHepan };
|
|
9367
|
+
return { tripleOf, buildHepan };
|
|
9257
9368
|
})();
|
|
9258
9369
|
|
|
9259
9370
|
__mods.trend = (function () {
|
|
@@ -13748,23 +13859,68 @@ const VERDICT = (score, ctx) => {
|
|
|
13748
13859
|
* @param {{task:string, startDate:string, days?:number, shengXiao?:string}} input
|
|
13749
13860
|
* startDate 必填(公历 YYYY-MM-DD),避免引擎在无时钟环境下静默取「今天」。
|
|
13750
13861
|
*/
|
|
13862
|
+
/** 严格解析公历日期:2 月 30 日这类不存在的日子直接判非法,不滚到下个月 */
|
|
13863
|
+
function parseYmd(raw) {
|
|
13864
|
+
const m = /^(\d{4})-(\d{1,2})-(\d{1,2})$/.exec(String(raw || '').trim());
|
|
13865
|
+
if (!m) return null;
|
|
13866
|
+
const year = Number(m[1]);
|
|
13867
|
+
const month = Number(m[2]);
|
|
13868
|
+
const day = Number(m[3]);
|
|
13869
|
+
if (month < 1 || month > 12 || day < 1) return null;
|
|
13870
|
+
const back = fromJDN(toJDN(year, month, day));
|
|
13871
|
+
return back.year === year && back.month === month && back.day === day ? { year, month, day } : null;
|
|
13872
|
+
}
|
|
13873
|
+
|
|
13874
|
+
const ymd = (p) => `${p.year}-${String(p.month).padStart(2, '0')}-${String(p.day).padStart(2, '0')}`;
|
|
13875
|
+
|
|
13876
|
+
/**
|
|
13877
|
+
* 择日主函数。
|
|
13878
|
+
* @param {{task:string, startDate:string, days?:number, shengXiao?:string,
|
|
13879
|
+
* candidates?:string[]}} input
|
|
13880
|
+
* startDate 必填(公历 YYYY-MM-DD),避免引擎在无时钟环境下静默取「今天」。
|
|
13881
|
+
* candidates 给了就只比这几个候选日(2–12 个,去重、保持输入顺序),startDate 与 days 不参与。
|
|
13882
|
+
*/
|
|
13751
13883
|
function buildZeri(input) {
|
|
13752
13884
|
const task = input.task;
|
|
13753
13885
|
if (!TASKS[task]) throw new Error(`不支持的事项「${task}」。支持:${Object.keys(TASKS).join('、')}`);
|
|
13754
|
-
const m = /^(\d{4})-(\d{1,2})-(\d{1,2})$/.exec(String(input.startDate || '').trim());
|
|
13755
|
-
if (!m) throw new Error('择日需要 startDate(公历 YYYY-MM-DD)——请向用户询问起算日,不要代填');
|
|
13756
|
-
const start = { year: Number(m[1]), month: Number(m[2]), day: Number(m[3]) };
|
|
13757
|
-
const days = Math.max(1, Math.min(60, Number.isInteger(input.days) ? input.days : 30));
|
|
13758
13886
|
const shengXiaoRaw = typeof input.shengXiao === 'string' ? input.shengXiao.trim() : '';
|
|
13759
13887
|
const shengXiao = shengXiaoRaw || null;
|
|
13760
13888
|
if (shengXiao && !SHENGXIAO.includes(shengXiao)) throw new Error(`shengXiao 须为生肖名:${SHENGXIAO.join('、')}`);
|
|
13761
13889
|
|
|
13890
|
+
// 候选日模式:只比用户给的这几天,不再按区间扫
|
|
13891
|
+
let candidates = null;
|
|
13892
|
+
if (input.candidates !== undefined && input.candidates !== null) {
|
|
13893
|
+
if (!Array.isArray(input.candidates)) {
|
|
13894
|
+
throw new Error('candidates 须为日期数组,例如 ["2026-12-01","2026-12-08","2026-12-15"]');
|
|
13895
|
+
}
|
|
13896
|
+
if (input.candidates.length < 2) throw new Error('candidates 至少给 2 个候选日;只想看一天请用 startDate + days: 1');
|
|
13897
|
+
if (input.candidates.length > 12) throw new Error('candidates 最多 12 个候选日;更多请用 startDate + days 扫区间');
|
|
13898
|
+
const seen = new Set();
|
|
13899
|
+
candidates = [];
|
|
13900
|
+
for (const raw of input.candidates) {
|
|
13901
|
+
const p = parseYmd(raw);
|
|
13902
|
+
if (!p) throw new Error(`候选日不是真实存在的公历日期:「${raw}」,请用 2026-12-01 这种格式`);
|
|
13903
|
+
const key = ymd(p);
|
|
13904
|
+
if (seen.has(key)) continue;
|
|
13905
|
+
seen.add(key);
|
|
13906
|
+
candidates.push(p);
|
|
13907
|
+
}
|
|
13908
|
+
if (candidates.length < 2) throw new Error('候选日去重后不足 2 天;至少要两个不同的日子才谈得上比较');
|
|
13909
|
+
}
|
|
13910
|
+
|
|
13911
|
+
let start = null;
|
|
13912
|
+
let days = 0;
|
|
13913
|
+
if (!candidates) {
|
|
13914
|
+
const m = /^(\d{4})-(\d{1,2})-(\d{1,2})$/.exec(String(input.startDate || '').trim());
|
|
13915
|
+
if (!m) throw new Error('择日需要 startDate(公历 YYYY-MM-DD)——请向用户询问起算日,不要代填');
|
|
13916
|
+
start = { year: Number(m[1]), month: Number(m[2]), day: Number(m[3]) };
|
|
13917
|
+
days = Math.max(1, Math.min(60, Number.isInteger(input.days) ? input.days : 30));
|
|
13918
|
+
}
|
|
13919
|
+
|
|
13762
13920
|
const spec = TASKS[task];
|
|
13763
13921
|
const zhixingSet = ZHIXING_FAVOR[spec.category];
|
|
13764
|
-
|
|
13765
|
-
const
|
|
13766
|
-
for (let i = 0; i < days; i++) {
|
|
13767
|
-
const p = fromJDN(startJdn + i);
|
|
13922
|
+
|
|
13923
|
+
const makeRow = (p) => {
|
|
13768
13924
|
const a = buildAlmanac(p);
|
|
13769
13925
|
let score = 0;
|
|
13770
13926
|
const reasons = [];
|
|
@@ -13781,7 +13937,7 @@ function buildZeri(input) {
|
|
|
13781
13937
|
if (a.zhiXing === '危' && spec.category !== 'travel' && spec.category !== 'worship') { score -= 1; reasons.push('危日(-1)'); }
|
|
13782
13938
|
|
|
13783
13939
|
const v = VERDICT(score, { chongSelf, jiHit });
|
|
13784
|
-
|
|
13940
|
+
return {
|
|
13785
13941
|
date: a.meta.date,
|
|
13786
13942
|
weekday: a.meta.weekday,
|
|
13787
13943
|
lunar: a.lunar.text,
|
|
@@ -13795,17 +13951,47 @@ function buildZeri(input) {
|
|
|
13795
13951
|
verdict: v.verdict,
|
|
13796
13952
|
verdictText: v.text,
|
|
13797
13953
|
...(v.excluded ? { excluded: true } : {}),
|
|
13798
|
-
}
|
|
13799
|
-
}
|
|
13954
|
+
};
|
|
13955
|
+
};
|
|
13956
|
+
|
|
13957
|
+
const list = candidates
|
|
13958
|
+
? candidates.map(makeRow)
|
|
13959
|
+
: Array.from({ length: days }, (_, i) => makeRow(fromJDN(toJDN(start.year, start.month, start.day) + i)));
|
|
13800
13960
|
|
|
13801
13961
|
const ranked = list.slice().sort((x, y) => y.score - x.score || (x.date < y.date ? -1 : 1));
|
|
13962
|
+
const startDate = candidates
|
|
13963
|
+
? candidates.slice().sort((x, y) => (ymd(x) < ymd(y) ? -1 : 1)).map(ymd)[0]
|
|
13964
|
+
: ymd(start);
|
|
13965
|
+
|
|
13966
|
+
// 候选日比较:只在这几天里比,给出最高分和同分者;不换算成「好多少」
|
|
13967
|
+
let compare = null;
|
|
13968
|
+
if (candidates) {
|
|
13969
|
+
const pool = ranked.filter((d) => !d.excluded);
|
|
13970
|
+
const top = pool[0] || null;
|
|
13971
|
+
const scores = list.map((d) => d.score);
|
|
13972
|
+
compare = {
|
|
13973
|
+
mode: 'candidates',
|
|
13974
|
+
count: list.length,
|
|
13975
|
+
best: top ? {
|
|
13976
|
+
date: top.date,
|
|
13977
|
+
score: top.score,
|
|
13978
|
+
verdict: top.verdict,
|
|
13979
|
+
ties: pool.filter((d) => d.score === top.score).map((d) => d.date),
|
|
13980
|
+
} : null,
|
|
13981
|
+
spread: Math.max(...scores) - Math.min(...scores),
|
|
13982
|
+
note: '候选日只在这几天里比:分数来自建除宜忌与冲煞,胜出只表示更合所问事项,不代表「好多少」。',
|
|
13983
|
+
};
|
|
13984
|
+
}
|
|
13985
|
+
|
|
13802
13986
|
return {
|
|
13803
13987
|
system: 'zeri',
|
|
13804
13988
|
meta: {
|
|
13805
13989
|
task,
|
|
13806
|
-
|
|
13807
|
-
|
|
13990
|
+
mode: candidates ? 'candidates' : 'range',
|
|
13991
|
+
startDate,
|
|
13992
|
+
days: list.length,
|
|
13808
13993
|
shengXiao,
|
|
13994
|
+
...(candidates ? { candidates: candidates.map(ymd) } : {}),
|
|
13809
13995
|
scope: shengXiao
|
|
13810
13996
|
? `宜忌按建除简表;已给当事人属相(${shengXiao}),凡当日冲该属相径直排除。评分仅用于排序引导。`
|
|
13811
13997
|
: '宜忌按建除简表;未给当事人属相,未做冲属相排除。评分仅用于排序引导。',
|
|
@@ -13814,6 +14000,7 @@ function buildZeri(input) {
|
|
|
13814
14000
|
usable: ranked.filter((d) => d.verdict === '可用' && !d.excluded).slice(0, 5).map((d) => d.date),
|
|
13815
14001
|
avoid: list.filter((d) => d.verdict === '回避').map((d) => `${d.date}(${d.reasons.join(';') || '综合减分'})`),
|
|
13816
14002
|
days: list,
|
|
14003
|
+
...(compare ? { compare } : {}),
|
|
13817
14004
|
excludedCount: list.filter((d) => d.excluded).length,
|
|
13818
14005
|
sources: ['宜忌:建除十二神通行简表', '建除适配与冲煞:传统通行口径', '排序:宜忌加减分 + 属相排除(确定性)'],
|
|
13819
14006
|
disclaimer: '择日为传统黄历口径的本地计算,属民俗参考;评分只用于排序,不构成择日结论。',
|
|
@@ -14384,7 +14571,8 @@ const KNOWLEDGE = [
|
|
|
14384
14571
|
system: 'bazi', dims: ['overall', 'career'],
|
|
14385
14572
|
gist: '天干是露在外面的样子,地支是藏在地下的根。',
|
|
14386
14573
|
detail: '同一个五行,透在天干叫「显」,扎在地支叫「有根」。只有天干没有地支,是看着有、使不上劲;只有地支不通天干,是有实力、不显眼。判断一个人能不能扛事,看的就是根有多深。',
|
|
14387
|
-
source: '
|
|
14574
|
+
source: '《滴天髓·干支总论》「天全一气,不可使地德莫之载;地全三物,不可使天道莫之容」',
|
|
14575
|
+
note: '常被署给《滴天髓》的「地支所藏之干,本静以待用,透出干头,则显其用矣。故干以通根为美,支以透出为贵」,出自《子平真诠》卷一论十干十二支的注文——滴天髓底本与沈孝瞻原文里都查不到这句,注文自己引的才是上面那句滴天髓。所以本卡引可核的原文,注文只作说明。',
|
|
14388
14576
|
status: 'settled',
|
|
14389
14577
|
},
|
|
14390
14578
|
{
|
|
@@ -14731,7 +14919,7 @@ const KNOWLEDGE = [
|
|
|
14731
14919
|
detail: '进神退神本身只说明这一爻的向外趋势:用神化进为得力,忌神化进则为患加剧;用神化退是力衰,忌神化退反而是解脱。传统还要看它在本月令衰旺、有没有日辰生扶,空亡或受克时进者未必能进。本插件把进退与爻位标出来,再按月建五态给一档力重——得令则进得动、退得慢,失令则进得虚、退得实,月破压过一切;只给档位,不折成加权分数,吉凶仍落在用神与忌神那一层判。',
|
|
14732
14920
|
source: '《增删卜易》进神退神章、用神章',
|
|
14733
14921
|
status: 'variant',
|
|
14734
|
-
note: '
|
|
14922
|
+
note: '土之进退次序各家说法不完全一致,有以丑→辰→未→戌为进、也有只取辰戌丑未相对者;本插件按丑辰未戌成环处理,并用《增删卜易》卦例中「未土化戌土为进」「戌土化未土为退」对拍。喜忌分档的出处是《增删卜易》卷二·進神退神章第二十九:「所喜者宜於化進,所忌者宜化退神」;把「喜/忌」具体落到用神/忌神两档,是本插件的引申。',
|
|
14735
14923
|
},
|
|
14736
14924
|
|
|
14737
14925
|
// ── 梅花 / 六壬 / 奇门 / 塔罗 ───────────────────────────
|
|
@@ -14891,6 +15079,11 @@ const CITATION_BOOKS = {
|
|
|
14891
15079
|
author: '佚名',
|
|
14892
15080
|
base: 'https://www.diancang.xyz/xuanxuewushu/qiongtongbaojian/',
|
|
14893
15081
|
},
|
|
15082
|
+
daliuren: {
|
|
15083
|
+
name: '大六壬大全',
|
|
15084
|
+
author: '明·郭载騋校(四库全书本)',
|
|
15085
|
+
base: 'https://zh.wikisource.org/wiki/六壬大全',
|
|
15086
|
+
},
|
|
14894
15087
|
qimen: {
|
|
14895
15088
|
name: '奇门遁甲统宗',
|
|
14896
15089
|
author: '旧题诸葛亮(传)',
|
|
@@ -14901,6 +15094,11 @@ const CITATION_BOOKS = {
|
|
|
14901
15094
|
author: '宋·邵雍(传)',
|
|
14902
15095
|
base: 'https://www.diancang.xyz/xuanxuewushu/meihuayishu/',
|
|
14903
15096
|
},
|
|
15097
|
+
ditian: {
|
|
15098
|
+
name: '滴天髓',
|
|
15099
|
+
author: '明·京图(通行本)',
|
|
15100
|
+
base: 'https://www.diancang.xyz/xuanxuewushu/ditiansui/',
|
|
15101
|
+
},
|
|
14904
15102
|
liuyao: {
|
|
14905
15103
|
name: '增删卜易',
|
|
14906
15104
|
author: '清·野鹤老人',
|
|
@@ -15303,6 +15501,41 @@ const CITATIONS = [
|
|
|
15303
15501
|
plain: '应期先分动静:静爻在逢值或逢冲之日应,动爻在逢合或逢值之日应;其余按入墓、旬空、月破等逐条推。',
|
|
15304
15502
|
url: 'https://zh.wikisource.org/wiki/增刪卜易/26又3',
|
|
15305
15503
|
},
|
|
15504
|
+
{
|
|
15505
|
+
id: 'dl-jigong',
|
|
15506
|
+
book: 'daliuren',
|
|
15507
|
+
chapter: '卷一·起例(十干寄宫)',
|
|
15508
|
+
quote: '甲课寅兮乙课辰,丙戊课巳不须论。丁己课未庚申上,辛戌壬亥是其真。癸课原来丑宫坐,分明不用四正神。',
|
|
15509
|
+
plain: '十干寄宫是四课起手:甲寄寅、乙寄辰、丙戊寄巳、丁己寄未、庚寄申、辛寄戌、壬寄亥、癸寄丑;丙戊、丁己各同宫,子午卯酉四个正位不寄。',
|
|
15510
|
+
url: 'https://zh.wikisource.org/wiki/六壬大全/1',
|
|
15511
|
+
},
|
|
15512
|
+
{
|
|
15513
|
+
id: 'dl-zeike',
|
|
15514
|
+
book: 'daliuren',
|
|
15515
|
+
chapter: '卷一·起例(贼克法)',
|
|
15516
|
+
quote: '取课先从下贼呼,如无下贼上克初。初传之上名中次,中上加临是末居。三传既定天盘将,此是入式法第一。',
|
|
15517
|
+
plain: '九宗门取初传先看下贼上,没有下贼再看上克下;初传定后,中传取初传所乘之神,末传再顺取一位,三传齐了才布天盘天将。',
|
|
15518
|
+
url: 'https://zh.wikisource.org/wiki/六壬大全/1',
|
|
15519
|
+
},
|
|
15520
|
+
{
|
|
15521
|
+
id: 'dl-yuejiang',
|
|
15522
|
+
book: 'daliuren',
|
|
15523
|
+
chapter: '卷二·神将释(月将)',
|
|
15524
|
+
quote: '逆布者谓之月将,其实即十二神也。',
|
|
15525
|
+
plain: '月将就是十二支神,按太阳过宫排:以登明为首逆布十二宫;起课时把月将加在占时上,顺布成天盘。',
|
|
15526
|
+
url: 'https://zh.wikisource.org/wiki/六壬大全/2',
|
|
15527
|
+
},
|
|
15528
|
+
{
|
|
15529
|
+
id: 'dt-yiqi-tonggen',
|
|
15530
|
+
book: 'ditian',
|
|
15531
|
+
chapter: '干支总论',
|
|
15532
|
+
quote: '天全一气,不可使地德莫之载。地全三物,不可使天道莫之容。',
|
|
15533
|
+
plain: '四柱天干同属一气,还得地支能承载它;地支所藏三干若不透到天干,也显不出用处——通根与透出是一件事的两面。',
|
|
15534
|
+
url: 'https://www.diancang.xyz/xuanxuewushu/ditiansui/153091.html',
|
|
15535
|
+
// 2026-11-01 复核:这一句是《子平真诠》论十干十二支注文里明确引作《滴天髓》的话,
|
|
15536
|
+
// 在滴天髓底本(干支总论)里能逐字对上,所以才是「通根透干」可核的原文。
|
|
15537
|
+
note: '《子平真诠》卷一论十干十二支注文引作《滴天髓》语,底本干支总论原文可核。',
|
|
15538
|
+
},
|
|
15306
15539
|
];
|
|
15307
15540
|
|
|
15308
15541
|
const BOOK_OF = {
|
|
@@ -15335,7 +15568,14 @@ const CARD_CITATIONS = {
|
|
|
15335
15568
|
'bazi-congge-yuanwen': { refs: ['smt-congsha', 'smt-congcai'] },
|
|
15336
15569
|
'bazi-tongguan': { refs: ['dt-tongguan'] },
|
|
15337
15570
|
// ── 八字:通行说,暂未定位单一条文
|
|
15338
|
-
'bazi-tonggen-tougan': {
|
|
15571
|
+
'bazi-tonggen-tougan': {
|
|
15572
|
+
refs: ['dt-yiqi-tonggen'],
|
|
15573
|
+
// 通行说法(「地支所藏之干,本静以待用…干以通根为美,支以透出为贵」)常被署成《滴天髓》,
|
|
15574
|
+
// 2026-11-01 核过:滴天髓底本与《子平真诠》沈孝瞻原文(论十干十二支)里都查不到这句,
|
|
15575
|
+
// 它出现在《子平真诠》评注本该章的注文(附支藏人元图、人元司令表之后的按语)。
|
|
15576
|
+
// 注文自身引的《滴天髓》「天全一气…」才是原本,故本卡只引可核的那句,注文只作说明不冒充原文。
|
|
15577
|
+
note: '通行说两句出自《子平真诠》卷一注文,非《滴天髓》正文;注文所引的滴天髓句见本条。',
|
|
15578
|
+
},
|
|
15339
15579
|
'bazi-wangxiang': { refs: ['smt-wangxiang'] },
|
|
15340
15580
|
'bazi-shensha': { refs: ['smt-shensha'] },
|
|
15341
15581
|
'bazi-tiaohou': { refs: ['qt-tiaohou'] },
|
|
@@ -15369,11 +15609,13 @@ const CARD_CITATIONS = {
|
|
|
15369
15609
|
'liuyao-fuyin': { refs: ['zs-fuyin'] },
|
|
15370
15610
|
'liuyao-jintui': { refs: ['zs-jintui'] },
|
|
15371
15611
|
'liuyao-yingqi': { refs: ['zs-yingqi'] },
|
|
15372
|
-
|
|
15612
|
+
// 2026-11-01 复核:底本卷二·進神退神章第二十九原文「所喜者宜於化進,所忌者宜化退神」
|
|
15613
|
+
// 已经把喜忌落在进退上,可作本条依据;把「喜/忌」具体落到用神/忌神两档仍是本插件的引申(见知识卡 note)
|
|
15614
|
+
'liuyao-jintui-yong': { refs: ['zs-jintui'] },
|
|
15373
15615
|
'liuyao-yingqi-exact': { status: 'todo', book: '增删卜易', chapter: '各門類應期總注章', reason: '逐条应期尚未实现,等 20+ 条有应验日期的卦例' },
|
|
15374
15616
|
// ── 其他体系
|
|
15375
15617
|
'meihua-tiyong': { refs: ['mh-tiyong'] },
|
|
15376
|
-
'daliuren-sike': {
|
|
15618
|
+
'daliuren-sike': { refs: ['dl-jigong', 'dl-zeike'] },
|
|
15377
15619
|
'qimen-yinyang': { refs: ['qm-yinyang'] },
|
|
15378
15620
|
'qimen-jixing': { refs: ['qm-jixing'] },
|
|
15379
15621
|
'qimen-rumu': { refs: ['qm-rumu'] },
|
|
@@ -18442,7 +18684,7 @@ function liuyaoTrace(r, opts = {}) {
|
|
|
18442
18684
|
['变卦', bian ? `${text(bian.name)}(上${text(bian.upper)}·下${text(bian.lower)})` : '无'],
|
|
18443
18685
|
],
|
|
18444
18686
|
lines.map((l) => `${l.position}爻 ${text(l.ganzhi)}${text(l.element)}`).join('|'),
|
|
18445
|
-
'
|
|
18687
|
+
'京房纳甲(八宫纳甲表);实现:engine/liuyao.js · NAJIA(规则表,本仓未录入古籍章页)',
|
|
18446
18688
|
);
|
|
18447
18689
|
|
|
18448
18690
|
const liuqin = step(
|
|
@@ -18455,7 +18697,7 @@ function liuyaoTrace(r, opts = {}) {
|
|
|
18455
18697
|
['日干起六神', `${text(r.dayGanZhi && r.dayGanZhi.stem)}日`],
|
|
18456
18698
|
],
|
|
18457
18699
|
lines.map((l) => `${l.position}爻 ${text(l.liuqin)}(${text(l.element)})`).join('|'),
|
|
18458
|
-
'
|
|
18700
|
+
'五行生克定六亲;取用可对读《增删卜易》卷一·用神章第八;实现:engine/liuyao.js · liuqin()',
|
|
18459
18701
|
);
|
|
18460
18702
|
|
|
18461
18703
|
const shiying = step(
|
|
@@ -18469,7 +18711,7 @@ function liuyaoTrace(r, opts = {}) {
|
|
|
18469
18711
|
],
|
|
18470
18712
|
`${text(ben.shi)}爻${shi ? ` ${text(shi.liuqin)}·${text(shi.ganzhi)}${text(shi.element)}` : ''}为世,`
|
|
18471
18713
|
+ `${text(ben.ying)}爻${ying ? ` ${text(ying.liuqin)}·${text(ying.ganzhi)}${text(ying.element)}` : ''}为应`,
|
|
18472
|
-
'
|
|
18714
|
+
'八宫世应诀(世爻序与应爻隔三位);实现:engine/liuyao.js · SHI_POSITION(通行动诀,本仓未录入古籍章页)',
|
|
18473
18715
|
);
|
|
18474
18716
|
|
|
18475
18717
|
let fushen;
|
|
@@ -18483,7 +18725,7 @@ function liuyaoTrace(r, opts = {}) {
|
|
|
18483
18725
|
'用神不上卦时,回本宫首卦的同位取伏神;飞伏之间只判生克与助压。',
|
|
18484
18726
|
[['用神', ug && ug.ask ? ug.ask : '未取']],
|
|
18485
18727
|
`${why};本层不适用。`,
|
|
18486
|
-
'engine/liuyao.js · yongshenOf()',
|
|
18728
|
+
'取伏之法参《增删卜易》卷二·飛伏神章第二十八;实现:engine/liuyao.js · yongshenOf()',
|
|
18487
18729
|
);
|
|
18488
18730
|
} else if (ug.missing) {
|
|
18489
18731
|
const hidden = (ug.fushen || []).map((f) => {
|
|
@@ -18503,7 +18745,7 @@ function liuyaoTrace(r, opts = {}) {
|
|
|
18503
18745
|
['月建 / 日辰', `${text(month && month.branch)} / ${text(day && day.branch)}`],
|
|
18504
18746
|
],
|
|
18505
18747
|
`${hidden || '本卦无此六亲,首卦同位也未取到伏神'}${access ? `;出伏:${access}` : ''}`,
|
|
18506
|
-
'
|
|
18748
|
+
'《增删卜易》卷二·飛伏神章第二十八(用神不現,尋得伏神而遇生扶有用);实现:engine/liuyao.js · fushenAccess()',
|
|
18507
18749
|
);
|
|
18508
18750
|
} else {
|
|
18509
18751
|
fushen = step(
|
|
@@ -18515,7 +18757,7 @@ function liuyaoTrace(r, opts = {}) {
|
|
|
18515
18757
|
['在位', list((ug.found || []).map((l) => `${l.position}爻`))],
|
|
18516
18758
|
],
|
|
18517
18759
|
`用神 ${ug.liuqin} 已在 ${list((ug.found || []).map((l) => `${l.position}爻`))},无需伏神。`,
|
|
18518
|
-
'engine/liuyao.js · yongshenOf()',
|
|
18760
|
+
'取伏之法参《增删卜易》卷二·飛伏神章第二十八;实现:engine/liuyao.js · yongshenOf()',
|
|
18519
18761
|
);
|
|
18520
18762
|
}
|
|
18521
18763
|
|
|
@@ -18536,7 +18778,7 @@ function liuyaoTrace(r, opts = {}) {
|
|
|
18536
18778
|
useStrength ? `用神:${useStrength}${us.labels && us.labels.length ? `(${us.labels.join('、')})` : ''}` : '用神:未取,只看全卦',
|
|
18537
18779
|
hs && hs.labels && hs.labels.length ? `全卦:${hs.labels.join('、')}` : '',
|
|
18538
18780
|
].filter(Boolean).join(';'),
|
|
18539
|
-
'
|
|
18781
|
+
'《增删卜易》卷一·月將章第十六、日辰章第十七;实现:engine/liuyao.js · yongshenStrength / hexagramStrength',
|
|
18540
18782
|
);
|
|
18541
18783
|
|
|
18542
18784
|
return [najia, liuqin, shiying, fushen, wangsheng];
|
|
@@ -18565,7 +18807,7 @@ function baziTrace(r) {
|
|
|
18565
18807
|
['虚透', list(parts.virtualHelpers, '无')],
|
|
18566
18808
|
],
|
|
18567
18809
|
`${text(s.level)}(${text(s.score)} 分):${text(s.detail)}`,
|
|
18568
|
-
'engine/bazi.js ·
|
|
18810
|
+
'本仓量化规则(月令权重 3/2/1/0.5/0、藏干通根、三会/三合/六合折根、虚透减半;非古籍定数);实现:engine/bazi.js · 旺衰量化',
|
|
18569
18811
|
);
|
|
18570
18812
|
|
|
18571
18813
|
const primaryName = text(frame.primaryName || (primary && primary.name), '未取格局');
|
|
@@ -18597,7 +18839,7 @@ function baziTrace(r) {
|
|
|
18597
18839
|
['调候', tiaohou && tiaohou.kind ? `${text(tiaohou.kind)}${need && need.element ? ` → ${text(need.element)}` : ''}` : '—'],
|
|
18598
18840
|
],
|
|
18599
18841
|
`${resultParts}`,
|
|
18600
|
-
'
|
|
18842
|
+
'《子平真诠》八、论用神(专求月令);调候参《穷通宝鉴》五行总论;实现:engine/geju.js · yongshenOf()',
|
|
18601
18843
|
);
|
|
18602
18844
|
|
|
18603
18845
|
const p = r.palaces && typeof r.palaces === 'object' ? r.palaces : {};
|
|
@@ -18614,7 +18856,7 @@ function baziTrace(r) {
|
|
|
18614
18856
|
['命宫 / 身宫', `${text(p.minggong && p.minggong.ganzhi)} / ${text(p.shengong && p.shengong.ganzhi)}`],
|
|
18615
18857
|
],
|
|
18616
18858
|
`胎元 ${text(p.taiyuan.ganzhi)};${p.taixi ? `胎息 ${text(p.taixi.ganzhi)};` : ''}命宫 ${text(p.minggong && p.minggong.ganzhi)};身宫 ${text(p.shengong && p.shengong.ganzhi)}。四宫是辅助坐标,不单独折吉凶。`,
|
|
18617
|
-
'Tyme
|
|
18859
|
+
'Tyme 1.5.4 四宫口径(与 ziwei.pub 704 组日期对拍);本仓未直接照录古籍章页;实现:engine/bazi.js · palaces',
|
|
18618
18860
|
) : null;
|
|
18619
18861
|
|
|
18620
18862
|
return [strength, geju, ...(palaces ? [palaces] : [])];
|
|
@@ -18637,7 +18879,7 @@ function ziweiTrace(r) {
|
|
|
18637
18879
|
['命中数', `${patterns.length} / ${total}`],
|
|
18638
18880
|
],
|
|
18639
18881
|
patterns.length ? `命中 ${patterns.length} 条:${hit}` : '未命中已收录的格局条件(不表示命盘无结构,只是不在已收录清单内)。',
|
|
18640
|
-
'
|
|
18882
|
+
'《紫微斗数全书》卷三·论人命入格、谈星要论;实现:engine/ziwei.js · detectPatterns()',
|
|
18641
18883
|
)];
|
|
18642
18884
|
}
|
|
18643
18885
|
|
|
@@ -18663,7 +18905,7 @@ function qimenTrace(r, opts = {}) {
|
|
|
18663
18905
|
['空亡 / 驿马', `${list(r.kongWang)} / ${text(ma.zhi)}${ma.gong ? `(${ma.gong}宫)` : ''}`],
|
|
18664
18906
|
],
|
|
18665
18907
|
palaces.map((p) => `${p.palace}宫 天${text(p.tianPan)}·${text(p.star)}·${text(p.gate)}·${text(p.god)}`).join('|') || '无九宫数据',
|
|
18666
|
-
'engine/qimen.js · buildQimen()',
|
|
18908
|
+
'《奇门遁甲统宗》卷一·奇门秘诀总赋(阴阳二遁、五日换元、超神接气);实现:engine/qimen.js · buildQimen()',
|
|
18667
18909
|
);
|
|
18668
18910
|
|
|
18669
18911
|
const ys = qimenYongshen(r, opts.direction);
|
|
@@ -18684,7 +18926,7 @@ function qimenTrace(r, opts = {}) {
|
|
|
18684
18926
|
? `${ys.note};${found.map((x) => `${x.key} 落 ${x.palace.palace}宫(${text(x.palace.star)}·${text(x.palace.gate)}·${text(x.palace.god)})`).join(',')}`
|
|
18685
18927
|
+ (missing.length ? `;${missing.join('、')} 未上盘` : '')
|
|
18686
18928
|
: `${ys.label}的用神均未上盘,属未决项;先按值符落宫参照。`,
|
|
18687
|
-
'
|
|
18929
|
+
'本仓用神表(按所问之事取用,非古籍原文);实现:engine/qimen-interpret.js · qimenYongshen()',
|
|
18688
18930
|
);
|
|
18689
18931
|
|
|
18690
18932
|
const order = ['旺', '相', '休', '囚', '死'];
|
|
@@ -18701,7 +18943,7 @@ function qimenTrace(r, opts = {}) {
|
|
|
18701
18943
|
['天盘干', stateLine(states.stem)],
|
|
18702
18944
|
],
|
|
18703
18945
|
`门:${stateLine(states.gate)};星:${stateLine(states.star)};天盘干:${stateLine(states.stem)}`,
|
|
18704
|
-
'
|
|
18946
|
+
'《奇门遁甲统宗》卷三(旺相休囚);实现:engine/qimen.js · groupByState()',
|
|
18705
18947
|
);
|
|
18706
18948
|
|
|
18707
18949
|
const forced = [];
|
|
@@ -18740,7 +18982,7 @@ function qimenTrace(r, opts = {}) {
|
|
|
18740
18982
|
['空亡', kongCells.join('、') || '无'],
|
|
18741
18983
|
],
|
|
18742
18984
|
`门迫 ${forced.length} 处、宫制门 ${controlled.length} 处;天盘干与宫:${stemLine};击刑 ${jiXing.length} 处、三奇入墓 ${ruMu.length} 处、六仪入墓 ${liuYiRuMu.length} 处、落空 ${kongCells.length} 宫。十干克应:${ganying || '—'}。`,
|
|
18743
|
-
'
|
|
18985
|
+
'《奇门遁甲统宗》卷二(八门迫制)、卷十一·玄机赋(中)(六仪击刑、三奇入墓);十干克应为本仓表;实现:engine/qimen.js · gatePalaceRelation() / stemPalaceRelation()、engine/qimen-ganying.js',
|
|
18744
18986
|
);
|
|
18745
18987
|
|
|
18746
18988
|
return [facts, yongshen, wangshuai, relations];
|
|
@@ -18766,7 +19008,7 @@ function daliurenTrace(r) {
|
|
|
18766
19008
|
['昼夜 / 贵人', `${text(r.dayNight)} / ${text(gr.zhi)}(${gr.reverse ? '逆布' : '顺布'})`],
|
|
18767
19009
|
],
|
|
18768
19010
|
pan.map((p) => `${p.di}→${p.tian}${p.jiang ? `(${text(p.jiang)})` : ''}`).join('|') || '无天地盘数据',
|
|
18769
|
-
'
|
|
19011
|
+
'《大六壬大全》卷二·神将释(月将为十二支神,登明为首逆布);加时布盘属通行口诀,未标卷次;实现:engine/daliuren.js · tianDiPan()',
|
|
18770
19012
|
);
|
|
18771
19013
|
|
|
18772
19014
|
const sike = step(
|
|
@@ -18779,7 +19021,7 @@ function daliurenTrace(r) {
|
|
|
18779
19021
|
['四课', ke.map((k) => `${k.position}课 ${text(k.shang)}上${text(k.xia)}下`).join(';') || '—'],
|
|
18780
19022
|
],
|
|
18781
19023
|
ke.map((k) => `${k.position}课 ${text(k.shang)}上${text(k.xia)}下${k.jiang ? `·${text(k.jiang)}` : ''}:${text(k.relation)}`).join(';') || '无四课数据',
|
|
18782
|
-
'engine/daliuren.js · siKe()',
|
|
19024
|
+
'《大六壬大全》卷一·起例(十干寄宫);四课余下三课按通行口诀;实现:engine/daliuren.js · siKe()',
|
|
18783
19025
|
);
|
|
18784
19026
|
|
|
18785
19027
|
const chuanLine = (c) => (c
|
|
@@ -18796,7 +19038,7 @@ function daliurenTrace(r) {
|
|
|
18796
19038
|
['末传', chuanLine(sc.mo)],
|
|
18797
19039
|
],
|
|
18798
19040
|
`${chuanLine(sc.chu)} → ${chuanLine(sc.zhong)} → ${chuanLine(sc.mo)}`,
|
|
18799
|
-
'engine/daliuren.js · chuan()',
|
|
19041
|
+
'《大六壬大全》卷一·起例(贼克法为首法,比用、涉害、遥克、昴星、八专、伏吟、返吟依次列后);实现:engine/daliuren.js · chuan()',
|
|
18800
19042
|
);
|
|
18801
19043
|
|
|
18802
19044
|
return [tiandipan, sike, sanchuan];
|
|
@@ -19245,6 +19487,221 @@ const handleChecksum = checksum;
|
|
|
19245
19487
|
return { handlePayload, encodeHandle, decodeHandle, resolveHandle, HANDLE_PREFIX, handleChecksum };
|
|
19246
19488
|
})();
|
|
19247
19489
|
|
|
19490
|
+
__mods.bridge = (function () {
|
|
19491
|
+
// C3 对话桥替代方案:深链 / 复制 / 结构化参数。
|
|
19492
|
+
//
|
|
19493
|
+
// 背景:面板原本靠会话槽位 conversation.input.right 拿 inputActions,再把提问写进输入框。
|
|
19494
|
+
// 那个槽位不稳定(宿主改过名、也可能根本没声明),桥一断用户就没有路把盘送进对话。
|
|
19495
|
+
// 这里给三条不依赖宿主的通道,桥能用时仍然优先用桥:
|
|
19496
|
+
//
|
|
19497
|
+
// 1. 深链:把「排这张盘用的参数」编码进 URL 片段(#mp=<handle>&q=<问题>)。
|
|
19498
|
+
// 打开链接的人(换台机器、发给朋友、自己收藏)能还原同一张盘,不必重报出生信息。
|
|
19499
|
+
// 2. 复制:两档现成文本——完整提问(含盘面摘要与回答骨架)与一句话精简版。
|
|
19500
|
+
// 3. 结构化参数:一段 JSON,助手可以直接拿 args 去调 compute,不必解析自然语言。
|
|
19501
|
+
//
|
|
19502
|
+
// 约定:
|
|
19503
|
+
// * 深链里用的是 C1 句柄(mc1.<body>.<校验位>),所以截断/改字会明确报错,不会静默排成另一张盘;
|
|
19504
|
+
// * 链接只带「排盘参数 + 一句问题」,不带摘要、不带解析——摘要可能有好几 KB,URL 装不下;
|
|
19505
|
+
// * 这里只做编解码与文案拼装,不排盘、不调用模型,node 与浏览器都能跑(不依赖 Buffer / btoa)。
|
|
19506
|
+
const { encodeHandle, decodeHandle, HANDLE_PREFIX } = (__mods.handle || {});
|
|
19507
|
+
const { askPrompt } = (__mods.followup || {});
|
|
19508
|
+
|
|
19509
|
+
/** 问题进链接前截断的长度:再长就交给「复制完整提问」那一档 */
|
|
19510
|
+
const LINK_MAX_QUESTION = 200;
|
|
19511
|
+
/** 精简版里问题的截断长度 */
|
|
19512
|
+
const BRIEF_MAX_QUESTION = 60;
|
|
19513
|
+
|
|
19514
|
+
const SYSTEM_LABEL = {
|
|
19515
|
+
bazi: '八字', ziwei: '紫微', liuyao: '六爻', meihua: '梅花', daliuren: '大六壬',
|
|
19516
|
+
qimen: '奇门', hepan: '合盘', tarot: '塔罗', almanac: '黄历', zeri: '择日',
|
|
19517
|
+
chenggu: '称骨', xiaoliuren: '小六壬', star: '星座', numerology: '生命数字',
|
|
19518
|
+
mbti: '人格', trend: '人生K线', fengshui: '风水', name: '起名', convert: '农历',
|
|
19519
|
+
};
|
|
19520
|
+
|
|
19521
|
+
const labelOf = (system) => SYSTEM_LABEL[system] || system || '未知体系';
|
|
19522
|
+
|
|
19523
|
+
/** 只保留 URL 片段能用的一段:去掉已有 hash,其余原样 */
|
|
19524
|
+
function baseOf(base) {
|
|
19525
|
+
const s = String(base || '');
|
|
19526
|
+
const i = s.indexOf('#');
|
|
19527
|
+
return i < 0 ? s : s.slice(0, i);
|
|
19528
|
+
}
|
|
19529
|
+
|
|
19530
|
+
function encodeQuestion(q) {
|
|
19531
|
+
try { return encodeURIComponent(q); } catch (e) { return ''; }
|
|
19532
|
+
}
|
|
19533
|
+
|
|
19534
|
+
function decodeQuestion(s) {
|
|
19535
|
+
try { return decodeURIComponent(s); } catch (e) { return String(s || ''); }
|
|
19536
|
+
}
|
|
19537
|
+
|
|
19538
|
+
/**
|
|
19539
|
+
* 参数 → 深链。没有可编码的参数(或参数里只有输出开关)时返回 ''。
|
|
19540
|
+
*
|
|
19541
|
+
* askLink({ system: 'bazi', datetime: '1990-05-12T14:30', gender: 'female' }, { question: '2027 年事业', base: url })
|
|
19542
|
+
* → '<url>#mp=mc1.xxx.yyy&q=2027%20%E5%B9%B4%E4%BA%8B%E4%B8%9A'
|
|
19543
|
+
*
|
|
19544
|
+
* 也接受现成句柄:askLink({ handle: r.handle }, { question, base })。
|
|
19545
|
+
*/
|
|
19546
|
+
function askLink(args = {}, opts = {}) {
|
|
19547
|
+
const o = opts || {};
|
|
19548
|
+
const raw = String(o.handle || (args && args.handle) || '').trim();
|
|
19549
|
+
const handle = raw || encodeHandle(args || {});
|
|
19550
|
+
if (!handle || !handle.startsWith(HANDLE_PREFIX)) return '';
|
|
19551
|
+
const q = String(o.question || '').trim().slice(0, LINK_MAX_QUESTION);
|
|
19552
|
+
const parts = [`mp=${handle}`];
|
|
19553
|
+
if (q) parts.push(`q=${encodeQuestion(q)}`);
|
|
19554
|
+
return `${baseOf(o.base)}#${parts.join('&')}`;
|
|
19555
|
+
}
|
|
19556
|
+
|
|
19557
|
+
/**
|
|
19558
|
+
* 深链 → 参数。任何不合法都不抛异常,返回 { ok:false, code, message },由调用方决定是
|
|
19559
|
+
* 静默忽略还是提示一句——分享出去的老链接、被聊天软件截断的链接都会走到这里。
|
|
19560
|
+
*
|
|
19561
|
+
* 认这几种写法:完整 URL、只有片段(#mp=…)、只有查询串(?mp=…)、裸的 mp=…
|
|
19562
|
+
*/
|
|
19563
|
+
function parseAskLink(text) {
|
|
19564
|
+
const raw = String(text || '').trim();
|
|
19565
|
+
if (!raw) return { ok: false, code: 'E_LINK', message: '没有链接内容:请粘贴带 #mp= 的完整链接' };
|
|
19566
|
+
|
|
19567
|
+
// 片段的优先级高于查询串:链接里同时有时,以片段为准(那是本插件自己写的)
|
|
19568
|
+
let body = '';
|
|
19569
|
+
const hashAt = raw.indexOf('#');
|
|
19570
|
+
if (hashAt >= 0 && raw.slice(hashAt + 1).trim()) body = raw.slice(hashAt + 1).trim();
|
|
19571
|
+
else {
|
|
19572
|
+
const qAt = raw.indexOf('?');
|
|
19573
|
+
if (qAt >= 0 && raw.slice(qAt + 1).trim()) body = raw.slice(qAt + 1).trim();
|
|
19574
|
+
else if (/^mp=/.test(raw)) body = raw;
|
|
19575
|
+
}
|
|
19576
|
+
if (!body) return { ok: false, code: 'E_LINK', message: '链接里没有 #mp= 片段:这不是本插件生成的深链' };
|
|
19577
|
+
|
|
19578
|
+
const fields = {};
|
|
19579
|
+
for (const seg of body.split('&')) {
|
|
19580
|
+
if (!seg) continue;
|
|
19581
|
+
const eq = seg.indexOf('=');
|
|
19582
|
+
if (eq <= 0) continue;
|
|
19583
|
+
const k = seg.slice(0, eq);
|
|
19584
|
+
const v = seg.slice(eq + 1);
|
|
19585
|
+
if (k === 'mp') fields.mp = v;
|
|
19586
|
+
else if (k === 'q') fields.q = v;
|
|
19587
|
+
}
|
|
19588
|
+
if (!fields.mp) return { ok: false, code: 'E_LINK', message: '链接里没有 mp 参数(只有问题没有盘):无法还原盘面' };
|
|
19589
|
+
|
|
19590
|
+
let args;
|
|
19591
|
+
try {
|
|
19592
|
+
args = decodeHandle(fields.mp);
|
|
19593
|
+
} catch (e) {
|
|
19594
|
+
return { ok: false, code: (e && e.code) || 'E_HANDLE', message: (e && e.message) || '句柄无法解析' };
|
|
19595
|
+
}
|
|
19596
|
+
const question = fields.q ? decodeQuestion(fields.q).slice(0, LINK_MAX_QUESTION) : '';
|
|
19597
|
+
return {
|
|
19598
|
+
ok: true,
|
|
19599
|
+
handle: fields.mp,
|
|
19600
|
+
args,
|
|
19601
|
+
question,
|
|
19602
|
+
system: args.system || '',
|
|
19603
|
+
label: labelOf(args.system),
|
|
19604
|
+
};
|
|
19605
|
+
}
|
|
19606
|
+
|
|
19607
|
+
const splitDateTime = (dt) => {
|
|
19608
|
+
const m = /^(\d{4}-\d{1,2}-\d{1,2})[T ](\d{1,2}:\d{2})/.exec(String(dt || '').trim());
|
|
19609
|
+
if (!m) return { date: '', time: '' };
|
|
19610
|
+
const [y, mo, d] = m[1].split('-');
|
|
19611
|
+
return { date: `${y}-${String(mo).padStart(2, '0')}-${String(d).padStart(2, '0')}`, time: m[2].padStart(5, '0') };
|
|
19612
|
+
};
|
|
19613
|
+
|
|
19614
|
+
/**
|
|
19615
|
+
* 这些参数能原样填回表单;其余(partner / candidates / answers / mode / upper …)
|
|
19616
|
+
* 由调用方提示「未还原」——只报真的填回去的,不假装全还原了。
|
|
19617
|
+
*/
|
|
19618
|
+
const FORM_KEYS = ['gender', 'address', 'trueSolar', 'lateZi', 'lines'];
|
|
19619
|
+
|
|
19620
|
+
/**
|
|
19621
|
+
* 深链 → 表单初值。面板与独立页共用一份,避免两边各自猜字段名。
|
|
19622
|
+
* 返回 { tab, date, time, ... , question, restored[], rest{} };rest 是没还原的参数,用于如实提示。
|
|
19623
|
+
*/
|
|
19624
|
+
function formOf(parsed) {
|
|
19625
|
+
const p = parsed && parsed.ok ? parsed : null;
|
|
19626
|
+
const args = (p && p.args) || {};
|
|
19627
|
+
const dt = splitDateTime(args.datetime);
|
|
19628
|
+
const out = {
|
|
19629
|
+
tab: args.system || '',
|
|
19630
|
+
date: dt.date,
|
|
19631
|
+
time: dt.time,
|
|
19632
|
+
question: (p && p.question) || '',
|
|
19633
|
+
restored: [],
|
|
19634
|
+
rest: {},
|
|
19635
|
+
};
|
|
19636
|
+
if (out.tab) out.restored.push('system');
|
|
19637
|
+
if (dt.date) out.restored.push('datetime');
|
|
19638
|
+
for (const k of FORM_KEYS) {
|
|
19639
|
+
if (args[k] === undefined || args[k] === null) continue;
|
|
19640
|
+
out[k] = args[k];
|
|
19641
|
+
out.restored.push(k);
|
|
19642
|
+
}
|
|
19643
|
+
// 问题不是盘面参数,但它跟着链接走,单独记一笔
|
|
19644
|
+
if (out.question) out.restored.push('question');
|
|
19645
|
+
for (const [k, v] of Object.entries(args)) {
|
|
19646
|
+
if (k === 'system' || k === 'datetime' || FORM_KEYS.includes(k)) continue;
|
|
19647
|
+
out.rest[k] = v;
|
|
19648
|
+
}
|
|
19649
|
+
return out;
|
|
19650
|
+
}
|
|
19651
|
+
|
|
19652
|
+
/** 未还原参数的提示文案:空对象返回 ''(不打扰) */
|
|
19653
|
+
function restNote(form) {
|
|
19654
|
+
const keys = Object.keys((form && form.rest) || {});
|
|
19655
|
+
if (!keys.length) return '';
|
|
19656
|
+
return `链接里还有 ${keys.length} 项没填回表单(${keys.join('、')}):这些只在引擎调用里生效,页面上按默认值显示。`;
|
|
19657
|
+
}
|
|
19658
|
+
|
|
19659
|
+
/**
|
|
19660
|
+
* 一张盘 → 三档复制文本 + 深链。
|
|
19661
|
+
*
|
|
19662
|
+
* full 完整提问(与桥发出去的是同一份,含盘面摘要、层次与回答骨架)
|
|
19663
|
+
* brief 一句话版:体系 + 句柄 + 问题,够助手认出是哪张盘
|
|
19664
|
+
* structured 结构化参数(JSON),助手可直接照 args 调 compute
|
|
19665
|
+
* link 深链(没有句柄时为空)
|
|
19666
|
+
*/
|
|
19667
|
+
function askPackages(result, opts = {}) {
|
|
19668
|
+
const o = opts || {};
|
|
19669
|
+
const r = result || {};
|
|
19670
|
+
const system = r.system || '';
|
|
19671
|
+
const label = labelOf(system);
|
|
19672
|
+
const question = String(o.question || '').trim();
|
|
19673
|
+
const handle = String(r.handle || '').trim();
|
|
19674
|
+
|
|
19675
|
+
let full = '';
|
|
19676
|
+
try {
|
|
19677
|
+
const p = askPrompt(r, { question, interpret: o.interpret });
|
|
19678
|
+
full = (p && p.text) || String(r.summary || '');
|
|
19679
|
+
} catch (e) {
|
|
19680
|
+
full = String(r.summary || '');
|
|
19681
|
+
}
|
|
19682
|
+
|
|
19683
|
+
const briefQ = question
|
|
19684
|
+
? (question.length > BRIEF_MAX_QUESTION ? `${question.slice(0, BRIEF_MAX_QUESTION)}…` : question)
|
|
19685
|
+
: '(未填问题:按摘要讲主线)';
|
|
19686
|
+
const brief = handle
|
|
19687
|
+
? `【命理追问】${label}|${handle}|问:${briefQ}`
|
|
19688
|
+
: `【命理追问】${label}|问:${briefQ}`;
|
|
19689
|
+
|
|
19690
|
+
const args = {};
|
|
19691
|
+
if (handle) args.handle = handle;
|
|
19692
|
+
if (system) args.system = system;
|
|
19693
|
+
if (question) args.question = question;
|
|
19694
|
+
const structured = JSON.stringify({ tool: 'metaphysics', v: 1, args }, null, 2);
|
|
19695
|
+
|
|
19696
|
+
const link = handle ? askLink({ handle }, { question, base: o.base }) : '';
|
|
19697
|
+
|
|
19698
|
+
return { system, label, handle, question, full, brief, structured, link };
|
|
19699
|
+
}
|
|
19700
|
+
|
|
19701
|
+
|
|
19702
|
+
return { LINK_MAX_QUESTION, askLink, parseAskLink, formOf, restNote, askPackages };
|
|
19703
|
+
})();
|
|
19704
|
+
|
|
19248
19705
|
__mods.index = (function () {
|
|
19249
19706
|
// 统一入口:解析参数、分派到各体系、生成可读摘要
|
|
19250
19707
|
const { buildBazi } = (__mods.bazi || {});
|
|
@@ -19253,13 +19710,14 @@ const { buildZiwei, BRIGHTNESS, flowOf, asOfParts, ZIWEI_SCHOOL_DEFAULTS } = (__
|
|
|
19253
19710
|
const { drawTarot } = (__mods.tarot || {});
|
|
19254
19711
|
const { buildMeihua } = (__mods.meihua || {});
|
|
19255
19712
|
const { buildDaliuren } = (__mods.daliuren || {});
|
|
19256
|
-
const { buildHepan } = (__mods.hepan || {});
|
|
19713
|
+
const { buildHepan, tripleOf } = (__mods.hepan || {});
|
|
19257
19714
|
const { buildZiweiHepan } = (__mods['ziwei-hepan'] || {});
|
|
19258
19715
|
const { buildQimen, buildSnapshot } = (__mods.qimen || {});
|
|
19259
19716
|
const { geocodeAddress, resolveAddress } = (__mods.geo || {});
|
|
19260
19717
|
const { toJDN, ganzhiName, dayGanzhiIndex } = (__mods.calendar || {});
|
|
19261
19718
|
const { randomOf, isSeed } = (__mods.random || {});
|
|
19262
19719
|
const { encodeHandle, decodeHandle, resolveHandle } = (__mods.handle || {});
|
|
19720
|
+
const { askLink, parseAskLink, formOf, restNote, askPackages } = (__mods.bridge || {});
|
|
19263
19721
|
const { parseRevision, displayValue } = (__mods.revise || {});
|
|
19264
19722
|
const { interpret, DIRECTIONS, DIRECTION_DEFAULT } = (__mods.interpret || {});
|
|
19265
19723
|
const { followup, askPrompt } = (__mods.followup || {});
|
|
@@ -19275,7 +19733,7 @@ const { buildNumerology } = (__mods.numerology || {});
|
|
|
19275
19733
|
const { buildMbti, mbtiPaper } = (__mods.mbti || {});
|
|
19276
19734
|
const { buildConvert } = (__mods.convert || {});
|
|
19277
19735
|
const { classicsFor, classicsText, suiyunClassicOf, classicOf, CLASSIC_BOOKS } = (__mods.classics || {});
|
|
19278
|
-
const { diffObjects, summarize, sortDiffs, labelOf, rootOf, categoryOf, formatValue, CALIBER_SWITCHES } = (__mods.diff || {});
|
|
19736
|
+
const { diffObjects, summarize, sortDiffs, labelOf, rootOf, categoryOf, formatValue, CALIBER_SWITCHES, viewRows } = (__mods.diff || {});
|
|
19279
19737
|
const { canonicalJson, fingerprintOf } = (__mods.fingerprint || {});
|
|
19280
19738
|
const { buildTrend } = (__mods.trend || {});
|
|
19281
19739
|
const { buildFengshui } = (__mods.fengshui || {});
|
|
@@ -19545,6 +20003,26 @@ function qimenText(r) {
|
|
|
19545
20003
|
].join('\n');
|
|
19546
20004
|
}
|
|
19547
20005
|
|
|
20006
|
+
function tripleText(t) {
|
|
20007
|
+
if (!t || !Array.isArray(t.pairs) || !t.pairs.length) return [];
|
|
20008
|
+
const lines = [
|
|
20009
|
+
'【三方合盘】三对两两并列,不合成「三人总分」',
|
|
20010
|
+
...t.pairs.map((p) => `· ${p.a}–${p.b}:${p.level || '—'}(${p.total == null ? '—' : `${p.total}/100`})`),
|
|
20011
|
+
];
|
|
20012
|
+
if (t.best) {
|
|
20013
|
+
// 差 1–2 分时点名「最顺/最需磨合」是假精确:三对分数接近就直说接近,不排名
|
|
20014
|
+
if (t.spread >= 3) {
|
|
20015
|
+
lines.push(` 最顺:${t.best.a}–${t.best.b}(${t.best.total}/100)`
|
|
20016
|
+
+ (t.hardest ? `;最需磨合:${t.hardest.a}–${t.hardest.b}(${t.hardest.total}/100)` : ''));
|
|
20017
|
+
} else {
|
|
20018
|
+
lines.push(` 三对分数接近(最高与最低相差 ${t.spread} 分),不排名`);
|
|
20019
|
+
}
|
|
20020
|
+
}
|
|
20021
|
+
if (t.focus && t.focus.spread >= 3) lines.push(` 差异最大的一维:${t.focus.label}(三对相差 ${t.focus.spread} 分)`);
|
|
20022
|
+
if (t.note) lines.push(`※ ${t.note}`);
|
|
20023
|
+
return lines;
|
|
20024
|
+
}
|
|
20025
|
+
|
|
19548
20026
|
function hepanText(r) {
|
|
19549
20027
|
if (r.chart === 'ziwei') {
|
|
19550
20028
|
const d = (x) => `${x.label}:${x.score >= 0 ? '+' : ''}${x.score} ${x.text}`;
|
|
@@ -19554,6 +20032,7 @@ function hepanText(r) {
|
|
|
19554
20032
|
`对方 命宫 ${r.b.soul.branch}(${r.b.soulStars.join('、') || '无主星'}) 夫妻 ${r.b.spouseStars.join('、') || '无主星'} ${r.b.bureau}`,
|
|
19555
20033
|
...r.dimensions.map(d),
|
|
19556
20034
|
...r.interactions.map((h) => ` · ${h.text}`),
|
|
20035
|
+
...tripleText(r.triple),
|
|
19557
20036
|
...r.notes.map((n) => `※ ${n}`),
|
|
19558
20037
|
].join('\n');
|
|
19559
20038
|
}
|
|
@@ -19577,6 +20056,7 @@ function hepanText(r) {
|
|
|
19577
20056
|
`年命纳音:${r.detail.nayin.a} / ${r.detail.nayin.b}(${r.detail.nayin.label})——${r.detail.nayin.text}`,
|
|
19578
20057
|
`吉凶象:吉 ${r.detail.tally.good} / 凶 ${r.detail.tally.bad}(${r.detail.tally.lean};分档只数象,总分以上面三维度为准)`,
|
|
19579
20058
|
] : []),
|
|
20059
|
+
...tripleText(r.triple),
|
|
19580
20060
|
...r.notes.map((n) => `※ ${n}`),
|
|
19581
20061
|
].join('\n');
|
|
19582
20062
|
}
|
|
@@ -19807,11 +20287,14 @@ function shadowParams(args, result) {
|
|
|
19807
20287
|
};
|
|
19808
20288
|
}
|
|
19809
20289
|
if (system === 'hepan') {
|
|
19810
|
-
|
|
20290
|
+
const params = {
|
|
19811
20291
|
chart: args.chart === 'ziwei' ? 'ziwei' : 'bazi',
|
|
19812
20292
|
a: shadowPerson({ ...args, datetime: args.datetime }),
|
|
19813
20293
|
b: shadowPerson(args.partner || {}),
|
|
19814
20294
|
};
|
|
20295
|
+
// 第三方进指纹:同一对加上不同的第三人,是另一组盘
|
|
20296
|
+
if (args.partner2) params.c = shadowPerson(args.partner2);
|
|
20297
|
+
return params;
|
|
19815
20298
|
}
|
|
19816
20299
|
|
|
19817
20300
|
// 其余体系:只剔除解析/附加模块参数,其余原样归一化
|
|
@@ -19968,6 +20451,11 @@ function compute(input = {}) {
|
|
|
19968
20451
|
ignored: rev.ignored,
|
|
19969
20452
|
};
|
|
19970
20453
|
}
|
|
20454
|
+
// partner2: null 视同未传:先归一化再排盘,否则同一对盘会因为显式 null 多出一个句柄
|
|
20455
|
+
if (args.partner2 === null) {
|
|
20456
|
+
args = { ...args };
|
|
20457
|
+
delete args.partner2;
|
|
20458
|
+
}
|
|
19971
20459
|
const r = jsonSafe(computeRaw(args));
|
|
19972
20460
|
if (revision) r.revision = revision;
|
|
19973
20461
|
if (r && r.system === 'bazi') {
|
|
@@ -20149,11 +20637,12 @@ function caliberCompare(args = {}, opts = {}) {
|
|
|
20149
20637
|
const alt = sw.alt(current);
|
|
20150
20638
|
let diffs = [];
|
|
20151
20639
|
let error = '';
|
|
20640
|
+
let cur = null;
|
|
20641
|
+
let altRes = null;
|
|
20152
20642
|
try {
|
|
20153
|
-
|
|
20154
|
-
|
|
20155
|
-
|
|
20156
|
-
);
|
|
20643
|
+
cur = compute({ ...base, [sw.key]: current });
|
|
20644
|
+
altRes = compute({ ...base, [sw.key]: alt });
|
|
20645
|
+
diffs = diffObjects(cur, altRes);
|
|
20157
20646
|
} catch (e) {
|
|
20158
20647
|
error = (e && e.message) || String(e);
|
|
20159
20648
|
}
|
|
@@ -20181,6 +20670,7 @@ function caliberCompare(args = {}, opts = {}) {
|
|
|
20181
20670
|
altLabel: sw.label(alt),
|
|
20182
20671
|
total: diffs.length,
|
|
20183
20672
|
summary: error ? '' : summarize(diffs),
|
|
20673
|
+
views: error ? [] : viewRows(system, cur, altRes),
|
|
20184
20674
|
roots,
|
|
20185
20675
|
categories,
|
|
20186
20676
|
error,
|
|
@@ -20293,31 +20783,42 @@ function computeRaw(args = {}) {
|
|
|
20293
20783
|
if (!partner.datetime) {
|
|
20294
20784
|
throw fail('E_PARTNER', '缺少 partner.datetime(对方的公历出生时间),例如 partner: {"datetime":"1992-03-03T10:00","gender":"female"}');
|
|
20295
20785
|
}
|
|
20296
|
-
const
|
|
20297
|
-
|
|
20786
|
+
const partner2 = args.partner2;
|
|
20787
|
+
if (partner2 !== undefined && partner2 !== null) {
|
|
20788
|
+
if (typeof partner2 !== 'object' || Array.isArray(partner2)) {
|
|
20789
|
+
throw fail('E_PARTNER', 'partner2 需要 { datetime, gender, address? };不想看三方就整段省略(null 视同未传)');
|
|
20790
|
+
}
|
|
20791
|
+
if (!partner2.datetime) {
|
|
20792
|
+
throw fail('E_PARTNER', '缺少 partner2.datetime(第三方的公历出生时间),例如 partner2: {"datetime":"1988-09-09T08:00","gender":"male"}');
|
|
20793
|
+
}
|
|
20794
|
+
}
|
|
20298
20795
|
const chart = args.chart === 'ziwei' ? 'ziwei' : 'bazi';
|
|
20299
20796
|
// 性别决定双方大运/大限顺逆,两边都不能用默认值
|
|
20300
|
-
const
|
|
20301
|
-
|
|
20302
|
-
|
|
20303
|
-
|
|
20304
|
-
|
|
20305
|
-
|
|
20306
|
-
|
|
20307
|
-
|
|
20308
|
-
|
|
20309
|
-
|
|
20310
|
-
|
|
20311
|
-
|
|
20312
|
-
gender: requireGender(partner.gender, '对方'),
|
|
20313
|
-
trueSolar: partner.trueSolar,
|
|
20314
|
-
lateZi: partner.lateZi,
|
|
20315
|
-
dst: partner.dst !== false,
|
|
20316
|
-
address: typeof partner.address === 'string' ? partner.address : undefined,
|
|
20317
|
-
longitude: typeof partner.longitude === 'number' ? partner.longitude : undefined,
|
|
20318
|
-
geo: partner.geo && typeof partner.geo === 'object' ? partner.geo : undefined,
|
|
20797
|
+
const sideOf = (src, who) => {
|
|
20798
|
+
const dt = parseDateTime(src.datetime);
|
|
20799
|
+
return {
|
|
20800
|
+
...dt,
|
|
20801
|
+
gender: requireGender(src.gender, who),
|
|
20802
|
+
trueSolar: src.trueSolar,
|
|
20803
|
+
lateZi: src.lateZi,
|
|
20804
|
+
dst: src.dst !== false,
|
|
20805
|
+
address: typeof src.address === 'string' ? src.address : undefined,
|
|
20806
|
+
longitude: typeof src.longitude === 'number' ? src.longitude : undefined,
|
|
20807
|
+
geo: src.geo && typeof src.geo === 'object' ? src.geo : undefined,
|
|
20808
|
+
};
|
|
20319
20809
|
};
|
|
20320
|
-
const
|
|
20810
|
+
const sideA = sideOf(args, '你(主方)');
|
|
20811
|
+
const sideB = sideOf(partner, '对方');
|
|
20812
|
+
const build = (x, y) => (chart === 'ziwei' ? buildZiweiHepan({ a: x, b: y }) : buildHepan({ a: x, b: y }));
|
|
20813
|
+
const result = build(sideA, sideB);
|
|
20814
|
+
if (partner2) {
|
|
20815
|
+
const sideC = sideOf(partner2, '第三方');
|
|
20816
|
+
result.triple = tripleOf([
|
|
20817
|
+
{ key: 'a-b', a: '你', b: '对方', result },
|
|
20818
|
+
{ key: 'a-c', a: '你', b: '第三方', result: build(sideA, sideC) },
|
|
20819
|
+
{ key: 'b-c', a: '对方', b: '第三方', result: build(sideB, sideC) },
|
|
20820
|
+
]);
|
|
20821
|
+
}
|
|
20321
20822
|
return { ...result, summary: hepanText(result) };
|
|
20322
20823
|
}
|
|
20323
20824
|
if (system === 'meihua') {
|
|
@@ -20404,16 +20905,23 @@ function computeRaw(args = {}) {
|
|
|
20404
20905
|
return { ...result, summary: almanacText(result) };
|
|
20405
20906
|
}
|
|
20406
20907
|
if (system === 'zeri') {
|
|
20407
|
-
|
|
20408
|
-
|
|
20409
|
-
|
|
20908
|
+
const candMode = args.candidates !== undefined && args.candidates !== null;
|
|
20909
|
+
// startDate 必须显式:择日要「从哪天起看」,缺了不能拿本机今天静默代填;候选日模式改用 candidates
|
|
20910
|
+
if (!candMode && (typeof args.startDate !== 'string' || !args.startDate.trim())) {
|
|
20911
|
+
throw fail('E_INPUT', '择日需要 startDate(公历 YYYY-MM-DD,起算日);可另给 days(1–60,默认 30)与 shengXiao(当事人属相)。只想比几天,改给 candidates: ["2026-12-01","2026-12-08"]');
|
|
20912
|
+
}
|
|
20913
|
+
let result;
|
|
20914
|
+
try {
|
|
20915
|
+
result = buildZeri({
|
|
20916
|
+
task: args.task,
|
|
20917
|
+
startDate: args.startDate,
|
|
20918
|
+
days: args.days,
|
|
20919
|
+
shengXiao: args.shengXiao,
|
|
20920
|
+
candidates: args.candidates,
|
|
20921
|
+
});
|
|
20922
|
+
} catch (e) {
|
|
20923
|
+
throw fail('E_INPUT', (e && e.message) || String(e));
|
|
20410
20924
|
}
|
|
20411
|
-
const result = buildZeri({
|
|
20412
|
-
task: args.task,
|
|
20413
|
-
startDate: args.startDate,
|
|
20414
|
-
days: args.days,
|
|
20415
|
-
shengXiao: args.shengXiao,
|
|
20416
|
-
});
|
|
20417
20925
|
return { ...result, summary: zeriText(result) };
|
|
20418
20926
|
}
|
|
20419
20927
|
if (system === 'chenggu') {
|
|
@@ -20603,16 +21111,27 @@ function almanacText(r) {
|
|
|
20603
21111
|
|
|
20604
21112
|
/** 择日文本 */
|
|
20605
21113
|
function zeriText(r) {
|
|
20606
|
-
const
|
|
20607
|
-
const
|
|
21114
|
+
const cand = r.compare && r.compare.mode === 'candidates';
|
|
21115
|
+
const head = cand
|
|
21116
|
+
? `【择日·${r.meta.task}】候选 ${r.meta.days} 天:${r.meta.candidates.join('、')}${r.meta.shengXiao ? ` 当事人属${r.meta.shengXiao}` : '(未给属相,未做冲煞排除)'}`
|
|
21117
|
+
: `【择日·${r.meta.task}】${r.meta.startDate} 起 ${r.meta.days} 天${r.meta.shengXiao ? ` 当事人属${r.meta.shengXiao}` : '(未给属相,未做冲煞排除)'}`;
|
|
21118
|
+
const top = r.days.slice().sort((a, b) => b.score - a.score).slice(0, cand ? 12 : 5)
|
|
20608
21119
|
.map((d) => `· ${d.date} ${d.weekday} ${d.verdict}(${d.score}分) 宜${d.yi.slice(0, 3).join('/')} ${d.reasons.join(';') || '无特别加减分'}`);
|
|
21120
|
+
const bestLine = cand
|
|
21121
|
+
? (r.compare.best
|
|
21122
|
+
? `这一组里最优:${r.compare.best.date}(${r.compare.best.score}分,${r.compare.best.verdict})`
|
|
21123
|
+
+ (r.compare.best.ties.length > 1 ? `;同分:${r.compare.best.ties.filter((d) => d !== r.compare.best.date).join('、')}` : '')
|
|
21124
|
+
: '这一组都因冲属相被排除,没有可用的日子')
|
|
21125
|
+
: `推荐日:${r.best.length ? r.best.join('、') : '本期无(都不够推荐分)'}`;
|
|
20609
21126
|
return [
|
|
20610
21127
|
head,
|
|
20611
|
-
|
|
21128
|
+
bestLine,
|
|
21129
|
+
`推荐日:${r.best.length ? r.best.join('、') : '无'}`,
|
|
20612
21130
|
`可用日:${r.usable.length ? r.usable.join('、') : '无'}`,
|
|
20613
21131
|
`回避日:${r.avoid.length ? r.avoid.slice(0, 6).join('、') : '无'}`,
|
|
20614
|
-
'逐日排序:',
|
|
21132
|
+
cand ? '候选日逐日:' : '逐日排序:',
|
|
20615
21133
|
...top,
|
|
21134
|
+
...(cand ? [`比较口径:${r.compare.note}`] : []),
|
|
20616
21135
|
`口径:${r.meta.scope}`,
|
|
20617
21136
|
].join('\n');
|
|
20618
21137
|
}
|
|
@@ -20862,7 +21381,11 @@ function renderText(result) {
|
|
|
20862
21381
|
+ c.switches.map((s) => {
|
|
20863
21382
|
if (s.error) return `· ${s.title}:未能对照(${s.error})`;
|
|
20864
21383
|
if (!s.total) return `· ${s.title}:${s.currentLabel} → ${s.altLabel},本盘无变化`;
|
|
20865
|
-
|
|
21384
|
+
const changed = (Array.isArray(s.views) ? s.views : []).filter((v) => v.changed);
|
|
21385
|
+
const side = changed.length
|
|
21386
|
+
? `\n 并排:${changed.map((v) => `${v.label} ${v.current} → ${v.alt}`).join(';')}`
|
|
21387
|
+
: '';
|
|
21388
|
+
return `· ${s.title}:${s.currentLabel} → ${s.altLabel}\n ${s.summary}${side}`;
|
|
20866
21389
|
}).join('\n'));
|
|
20867
21390
|
}
|
|
20868
21391
|
if (result.followup) {
|
|
@@ -20896,6 +21419,8 @@ function renderText(result) {
|
|
|
20896
21419
|
|
|
20897
21420
|
|
|
20898
21421
|
|
|
21422
|
+
// C3 对话桥替代:深链编解码、表单还原、三档复制包(面板与独立页共用一份)
|
|
21423
|
+
|
|
20899
21424
|
|
|
20900
21425
|
|
|
20901
21426
|
|
|
@@ -20906,11 +21431,11 @@ function renderText(result) {
|
|
|
20906
21431
|
|
|
20907
21432
|
|
|
20908
21433
|
|
|
20909
|
-
return { interpret, DIRECTIONS, DIRECTION_DEFAULT, BRIGHTNESS, flowOf, geocodeAddress, resolveAddress, buildBazi, buildLiuyao, tossLines, followup, askPrompt, pastSignals, verifyFit, rectifyBirth, crossCheck, crossText, crossSection, buildAlmanac, buildZeri, buildChenggu, buildXiaoliuren, buildStar, buildNumerology, buildConvert, buildTrend, buildFengshui, buildName, buildTrace, traceMarkdown, encodeHandle, decodeHandle, resolveHandle, parseDateTime, chartFingerprint, shadowSnapshot, shadowDiff, compute, interpretText, caliberCompare, renderText };
|
|
21434
|
+
return { interpret, DIRECTIONS, DIRECTION_DEFAULT, BRIGHTNESS, flowOf, geocodeAddress, resolveAddress, buildBazi, buildLiuyao, tossLines, followup, askPrompt, pastSignals, verifyFit, rectifyBirth, crossCheck, crossText, crossSection, buildAlmanac, buildZeri, buildChenggu, buildXiaoliuren, buildStar, buildNumerology, buildConvert, buildTrend, buildFengshui, buildName, buildTrace, traceMarkdown, encodeHandle, decodeHandle, resolveHandle, askLink, parseAskLink, formOf, restNote, askPackages, parseDateTime, chartFingerprint, shadowSnapshot, shadowDiff, compute, interpretText, caliberCompare, renderText };
|
|
20910
21435
|
})();
|
|
20911
21436
|
|
|
20912
21437
|
window.Engine = __mods.index;
|
|
20913
|
-
window.__MP_VER = "0.
|
|
21438
|
+
window.__MP_VER = "0.58.0";
|
|
20914
21439
|
</script>
|
|
20915
21440
|
<script>
|
|
20916
21441
|
// 两个 UI 共用的表单规则:宿主面板(src/app.js)与独立页(src/web-ui.js)。
|
|
@@ -21271,7 +21796,19 @@ window.__MP_VER = "0.57.0";
|
|
|
21271
21796
|
.mp-hp-inter li b.m-科{background:var(--ke)}
|
|
21272
21797
|
.mp-hp-inter li b.m-忌{background:var(--ji)}
|
|
21273
21798
|
.mp-hp-inter li i{font-style:normal;color:var(--red);font-size:10px;margin-left:3px}.mp-hp-notes{margin-top:12px;font-size:11px;color:var(--muted);line-height:1.7}
|
|
21274
|
-
.mp-hp-notes p{margin:2px 0}
|
|
21799
|
+
.mp-hp-notes p{margin:2px 0}
|
|
21800
|
+
/* 三方合盘:三对并列的小表。不合并总分,所以没有「三人合计」那一行 */
|
|
21801
|
+
.mp-hp-triple{margin-top:12px;border-top:1px solid var(--line);padding-top:10px}
|
|
21802
|
+
.mp-hp-triple table{width:100%;border-collapse:collapse;font-size:12px;line-height:1.7;margin-top:6px}
|
|
21803
|
+
.mp-hp-triple th,.mp-hp-triple td{text-align:left;padding:4px 6px;border-bottom:1px dashed var(--line);white-space:nowrap}
|
|
21804
|
+
.mp-hp-triple thead th{font-size:11px;color:var(--muted);font-weight:600}
|
|
21805
|
+
.mp-hp-triple tbody th{font-weight:600;color:var(--ink)}
|
|
21806
|
+
.mp-hp-triple tbody td{font-variant-numeric:tabular-nums;color:var(--ink2)}
|
|
21807
|
+
.mp-hp-triple tbody td:first-of-type{font-weight:700;color:var(--red)}
|
|
21808
|
+
.mp-hp-lv{margin-left:6px;font-size:11px;color:var(--muted);font-weight:400}
|
|
21809
|
+
.mp-hp-triple p{margin:6px 0 0;font-size:11px;color:var(--muted);line-height:1.7}
|
|
21810
|
+
.mp-hp-triple p b{color:var(--ink)}
|
|
21811
|
+
.mp-dl-ss{margin-top:10px;display:flex;gap:10px;flex-wrap:wrap;align-items:baseline;font-size:12px}
|
|
21275
21812
|
.mp-dl-ss b{font-weight:600;color:var(--muted);font-size:11px;letter-spacing:.1em}
|
|
21276
21813
|
.mp-dl-ss span{color:var(--ink);cursor:help}
|
|
21277
21814
|
.mp-dl-ss i{font-style:normal;color:var(--red);margin-left:2px;font-weight:600}.mp-dl-pan2{display:grid;grid-template-columns:repeat(4,1fr);grid-template-rows:repeat(4,minmax(84px,auto));border:1px solid var(--line);background:var(--bg)}
|
|
@@ -21542,6 +22079,12 @@ window.__MP_VER = "0.57.0";
|
|
|
21542
22079
|
.mp-cdiff-cats{display:flex;flex-wrap:wrap;gap:4px 6px;margin-top:6px}
|
|
21543
22080
|
.mp-cdiff-cat{font-size:11px;color:var(--muted);border:1px solid var(--line);border-radius:999px;padding:0 7px;background:var(--band)}
|
|
21544
22081
|
.mp-cdiff-same{font-size:12px;color:var(--muted);margin-top:4px}
|
|
22082
|
+
.mp-cview{width:100%;margin-top:7px;border-collapse:collapse;font-size:12px;line-height:1.6}
|
|
22083
|
+
.mp-cview th,.mp-cview td{text-align:left;padding:3px 6px;border-bottom:1px dashed var(--line);vertical-align:top}
|
|
22084
|
+
.mp-cview thead th{font-size:11px;font-weight:600;color:var(--label);letter-spacing:.04em;white-space:nowrap}
|
|
22085
|
+
.mp-cview tbody th{font-weight:400;color:var(--muted);white-space:nowrap}
|
|
22086
|
+
.mp-cview tbody td{color:var(--ink2);font-variant-numeric:tabular-nums}
|
|
22087
|
+
.mp-cview tbody td:last-child{color:var(--ink);font-weight:700}
|
|
21545
22088
|
.mp-cdiff-note{margin-top:8px;padding-top:7px;border-top:1px dashed var(--line);font-size:11.5px;line-height:1.7;color:var(--muted)}
|
|
21546
22089
|
@media (max-width:640px){
|
|
21547
22090
|
.mp-cdiff>summary{padding:8px 11px}
|
|
@@ -21749,6 +22292,33 @@ window.__MP_VER = "0.57.0";
|
|
|
21749
22292
|
.mp-fu.miss .mp-fu-h{color:var(--red)}
|
|
21750
22293
|
.mp-fu-ev{margin-top:5px;font-size:12px;color:var(--muted);font-family:system-ui,sans-serif}
|
|
21751
22294
|
.mp-quick{margin:12px 16px;border:1px solid var(--line);border-radius:8px;background:var(--card);padding:12px 14px}
|
|
22295
|
+
/* 择日候选比较:只在给了候选日时出表;窄屏横向滚动,不把理由挤成竖排 */
|
|
22296
|
+
.mp-zr{margin:12px 16px;border:1px solid var(--line);border-radius:8px;background:var(--card);padding:12px 14px;font-family:system-ui,sans-serif}
|
|
22297
|
+
.mp-zr-h{display:flex;align-items:baseline;gap:10px;flex-wrap:wrap;margin-bottom:8px}
|
|
22298
|
+
.mp-zr-h b{font-size:14px;color:var(--red)}
|
|
22299
|
+
.mp-zr-h span{font-size:12px;color:var(--muted)}
|
|
22300
|
+
.mp-zr-lead{font-size:13px;color:var(--ink);margin-bottom:8px}
|
|
22301
|
+
.mp-zr-lead b{color:var(--red)}
|
|
22302
|
+
.mp-zr-t{width:100%;border-collapse:collapse;font-size:12px;line-height:1.7}
|
|
22303
|
+
.mp-zr-t th,.mp-zr-t td{text-align:left;padding:5px 7px;border-bottom:1px dashed var(--line);vertical-align:top}
|
|
22304
|
+
.mp-zr-t thead th{font-size:11px;color:var(--muted);font-weight:600;white-space:nowrap}
|
|
22305
|
+
.mp-zr-t tbody th{font-weight:600;color:var(--ink);white-space:nowrap}
|
|
22306
|
+
.mp-zr-t tbody th em,.mp-zr-t tbody td em{display:block;font-style:normal;font-size:11px;color:var(--muted);font-weight:400}
|
|
22307
|
+
.mp-zr-t tbody td b{color:var(--ink2)}
|
|
22308
|
+
.mp-zr-t tbody tr.top{background:color-mix(in srgb,var(--red) 7%,transparent)}
|
|
22309
|
+
.mp-zr-t tbody tr.off{opacity:.72}
|
|
22310
|
+
.mp-zr-best{display:inline-block;margin-left:6px;padding:0 5px;border-radius:3px;background:var(--red);color:var(--on-accent);font-size:10px;font-style:normal;line-height:16px;vertical-align:1px}
|
|
22311
|
+
.mp-zr-score{font-variant-numeric:tabular-nums;font-weight:700;color:var(--ink)}
|
|
22312
|
+
.mp-zr-v{display:inline-block;padding:0 6px;border-radius:3px;border:1px solid var(--line);font-size:11px;white-space:nowrap}
|
|
22313
|
+
.mp-zr-v.good{color:var(--lu);border-color:var(--lu)}
|
|
22314
|
+
.mp-zr-v.bad{color:var(--ji);border-color:var(--ji)}
|
|
22315
|
+
.mp-zr-why{color:var(--ink2);min-width:180px}
|
|
22316
|
+
.mp-zr-lists{display:flex;flex-wrap:wrap;gap:6px 14px;margin-top:9px;font-size:11.5px;color:var(--ink2)}
|
|
22317
|
+
.mp-zr-lists em{font-style:normal;color:var(--muted);margin-right:4px}
|
|
22318
|
+
.mp-zr-note{margin:6px 0 0;font-size:11px;color:var(--muted);line-height:1.7}
|
|
22319
|
+
/* 首屏耗时:只回显本机实测,低调放在结果底部 */
|
|
22320
|
+
.mp-perf{margin:10px 0 0;font-size:11px;line-height:1.7;color:var(--muted);font-family:system-ui,sans-serif;font-variant-numeric:tabular-nums}
|
|
22321
|
+
@media (max-width:640px){.mp-zr{margin:10px;padding:10px;overflow-x:auto}.mp-zr-t{min-width:560px}}
|
|
21752
22322
|
.mp-cg{margin:12px 16px;border:1px solid var(--line);border-radius:8px;background:var(--card);padding:12px 14px;font-family:system-ui,sans-serif}
|
|
21753
22323
|
.mp-cg-h{display:flex;align-items:baseline;gap:10px;flex-wrap:wrap;margin-bottom:9px}
|
|
21754
22324
|
.mp-cg-h b{font-size:13px;color:var(--ink);font-weight:600}
|
|
@@ -22802,6 +23372,31 @@ window.__MP_VER = "0.57.0";
|
|
|
22802
23372
|
</div>`;
|
|
22803
23373
|
};
|
|
22804
23374
|
|
|
23375
|
+
// 三方合盘表:只并列三对,不给「三人合计」——合成总分会把「和谁相处」混为一谈
|
|
23376
|
+
const hepanTripleHTML = (t) => {
|
|
23377
|
+
if (!t || !Array.isArray(t.pairs) || !t.pairs.length) return '';
|
|
23378
|
+
const dims = t.pairs[0].dimensions || [];
|
|
23379
|
+
const head = `<tr><th scope="col">一对</th><th scope="col">契合度</th>`
|
|
23380
|
+
+ dims.map((d) => `<th scope="col">${esc(d.label)}</th>`).join('') + '</tr>';
|
|
23381
|
+
const rows = t.pairs.map((p) => `<tr><th scope="row">${esc(p.a)} × ${esc(p.b)}</th>`
|
|
23382
|
+
+ `<td>${p.total == null ? '—' : `${esc(p.total)}/100`}`
|
|
23383
|
+
+ `<span class="mp-hp-lv">${esc(p.level || '')}</span></td>`
|
|
23384
|
+
+ (p.dimensions || []).map((d) => `<td>${d.score >= 0 ? '+' : ''}${esc(d.score)}</td>`).join('')
|
|
23385
|
+
+ '</tr>').join('');
|
|
23386
|
+
const lead = t.spread >= 3 && t.best
|
|
23387
|
+
? `最顺:<b>${esc(t.best.a)} × ${esc(t.best.b)}</b>(${esc(t.best.total)}/100)`
|
|
23388
|
+
+ (t.hardest ? `;最需磨合:<b>${esc(t.hardest.a)} × ${esc(t.hardest.b)}</b>(${esc(t.hardest.total)}/100)` : '')
|
|
23389
|
+
: `三对分数接近(相差 ${esc(t.spread)} 分),不排名`;
|
|
23390
|
+
const focus = t.focus && t.focus.spread >= 3
|
|
23391
|
+
? `<p>差异最大的一维:<b>${esc(t.focus.label)}</b>(三对相差 ${esc(t.focus.spread)} 分)</p>` : '';
|
|
23392
|
+
return `<div class="mp-hp-triple">
|
|
23393
|
+
<div class="mp-hp-ith">三方合盘 · 三对并列</div>
|
|
23394
|
+
<table><thead>${head}</thead><tbody>${rows}</tbody></table>
|
|
23395
|
+
<p>${lead}</p>${focus}
|
|
23396
|
+
<p>${esc(t.note || '')}</p>
|
|
23397
|
+
</div>`;
|
|
23398
|
+
};
|
|
23399
|
+
|
|
22805
23400
|
pp.hepanHTML = function (r) {
|
|
22806
23401
|
const side = (x, who) => `<div class="mp-hp-side">
|
|
22807
23402
|
<div class="mp-hp-who">${who}</div>
|
|
@@ -22819,6 +23414,7 @@ window.__MP_VER = "0.57.0";
|
|
|
22819
23414
|
<div class="mp-hp-head"><b>${r.score.level}</b><span>${r.score.total}/100</span></div>
|
|
22820
23415
|
<div class="mp-hp-sides">${side(r.a, '你')}${side(r.b, '对方')}</div>
|
|
22821
23416
|
<div class="mp-hp-dims">${dims}</div>
|
|
23417
|
+
${hepanTripleHTML(r.triple)}
|
|
22822
23418
|
<div class="mp-hp-notes">${r.notes.map((n) => `<p>${n}</p>`).join('')}</div>
|
|
22823
23419
|
</div>`;
|
|
22824
23420
|
};
|
|
@@ -22936,6 +23532,7 @@ window.__MP_VER = "0.57.0";
|
|
|
22936
23532
|
<div><div class="mp-hp-ith">你的四化 落在对方</div><ul>${list('你') || '<li>无</li>'}</ul></div>
|
|
22937
23533
|
<div><div class="mp-hp-ith">对方的四化 落在你</div><ul>${list('对方') || '<li>无</li>'}</ul></div>
|
|
22938
23534
|
</div>
|
|
23535
|
+
${hepanTripleHTML(r.triple)}
|
|
22939
23536
|
<div class="mp-hp-notes">${r.notes.map((n) => `<p>${n}</p>`).join('')}</div>
|
|
22940
23537
|
</div>`;
|
|
22941
23538
|
};
|
|
@@ -23639,6 +24236,14 @@ window.__MP_VER = "0.57.0";
|
|
|
23639
24236
|
+ `<span class="mp-cdiff-k">${esc(r.label)}</span><span class="mp-cdiff-from">${esc(r.from)}</span>`
|
|
23640
24237
|
+ `<span class="mp-cdiff-arrow" aria-hidden="true">→</span><span class="mp-cdiff-to">${esc(r.to)}</span></div>`).join('')}</div>`;
|
|
23641
24238
|
};
|
|
24239
|
+
const viewsHTML = (row) => {
|
|
24240
|
+
const vs = (Array.isArray(row.views) ? row.views : []).filter((v) => v && v.changed);
|
|
24241
|
+
if (row.error || !row.total || !vs.length) return '';
|
|
24242
|
+
return `<table class="mp-cview"><thead><tr><th scope="col">项</th>`
|
|
24243
|
+
+ `<th scope="col">${esc(row.currentLabel)}</th><th scope="col">${esc(row.altLabel)}</th></tr></thead><tbody>`
|
|
24244
|
+
+ vs.map((v) => `<tr><th scope="row">${esc(v.label)}</th><td>${esc(v.current)}</td><td>${esc(v.alt)}</td></tr>`).join('')
|
|
24245
|
+
+ '</tbody></table>';
|
|
24246
|
+
};
|
|
23642
24247
|
const catsHTML = (row) => {
|
|
23643
24248
|
const cats = Array.isArray(row.categories) ? row.categories : [];
|
|
23644
24249
|
if (row.error || !row.total || !cats.length) return '';
|
|
@@ -23651,7 +24256,7 @@ window.__MP_VER = "0.57.0";
|
|
|
23651
24256
|
+ `<span class="mp-cdiff-count${row.total > 0 ? ' on' : ''}">${row.error ? '未能对照' : row.total ? `${Number(row.total) || 0} 处` : '无变化'}</span></div>`
|
|
23652
24257
|
+ `<div class="mp-cdiff-ab"><span class="mp-cdiff-pill now">当前 · ${esc(row.currentLabel)}</span>`
|
|
23653
24258
|
+ `<span class="mp-cdiff-arrow" aria-hidden="true">→</span><span class="mp-cdiff-pill alt">换成 · ${esc(row.altLabel)}</span></div>`
|
|
23654
|
-
+ `${rootsHTML(row)}${catsHTML(row)}</div>`).join('');
|
|
24259
|
+
+ `${rootsHTML(row)}${viewsHTML(row)}${catsHTML(row)}</div>`).join('');
|
|
23655
24260
|
return `<details class="mp-cdiff" aria-label="口径对照">
|
|
23656
24261
|
<summary><span class="mp-cdiff-title">口径对照</span><span class="mp-cdiff-lead">${esc(lead)}</span><span class="mp-cdiff-more">展开逐字差异</span></summary>
|
|
23657
24262
|
<div class="mp-cdiff-body">${rows}
|
|
@@ -24013,6 +24618,67 @@ window.__MP_VER = "0.57.0";
|
|
|
24013
24618
|
return fsNatal(r, m);
|
|
24014
24619
|
};
|
|
24015
24620
|
|
|
24621
|
+
/**
|
|
24622
|
+
* 首屏耗时:引擎取块 / 排盘 / 渲染三段,全部来自调用方实测。
|
|
24623
|
+
* 只在本机显示,不上报;缺哪段就省略哪段,不编数字。
|
|
24624
|
+
*/
|
|
24625
|
+
pp.perfHTML = function (p) {
|
|
24626
|
+
if (!p) return '';
|
|
24627
|
+
const ms = (v) => (typeof v === 'number' && isFinite(v) ? `${Math.max(0, Math.round(v))}ms` : '');
|
|
24628
|
+
const cached = p.cached === true ? '(已缓存)' : p.cached === false ? '(首次加载)' : '';
|
|
24629
|
+
const segs = [];
|
|
24630
|
+
if (ms(p.engineMs)) segs.push(`引擎 ${ms(p.engineMs)}${cached}`);
|
|
24631
|
+
if (ms(p.computeMs)) segs.push(`排盘 ${ms(p.computeMs)}`);
|
|
24632
|
+
if (ms(p.renderMs)) segs.push(`渲染 ${ms(p.renderMs)}`);
|
|
24633
|
+
const total = ms(p.totalMs);
|
|
24634
|
+
if (!segs.length && !total) return '';
|
|
24635
|
+
const head = total ? `首屏 ${total}` : '耗时';
|
|
24636
|
+
const body = segs.length ? `${head}|${segs.join(' · ')}` : head;
|
|
24637
|
+
return `<div class="mp-perf" title="本机这一次的实测耗时,不含网络与服务端;换机器、首次加载与已缓存会不同">${body}</div>`;
|
|
24638
|
+
};
|
|
24639
|
+
|
|
24640
|
+
/**
|
|
24641
|
+
* 择日候选日并排:只在给了 candidates 时出表(区间模式仍用文字版,避免 60 行表淹没面板)。
|
|
24642
|
+
* 逐日一行,标出这一组里的最优;表里只搬引擎已算好的分数与理由,不重新判吉凶。
|
|
24643
|
+
*/
|
|
24644
|
+
pp.zeriHTML = function (r) {
|
|
24645
|
+
if (!r || !r.compare || r.compare.mode !== 'candidates' || !Array.isArray(r.days)) return pp.quickHTML(r);
|
|
24646
|
+
const meta = r.meta || {};
|
|
24647
|
+
const bestDate = r.compare.best ? r.compare.best.date : '';
|
|
24648
|
+
const rows = r.days.map((d) => {
|
|
24649
|
+
const isBest = d.date === bestDate;
|
|
24650
|
+
const cls = d.excluded || d.verdict === '回避' ? 'bad' : d.verdict === '推荐' ? 'good' : '';
|
|
24651
|
+
return `<tr class="mp-zr-row${isBest ? ' top' : ''}${d.excluded ? ' off' : ''}">
|
|
24652
|
+
<th scope="row"><b>${esc(d.date)}</b>${isBest ? '<i class="mp-zr-best">最优</i>' : ''}
|
|
24653
|
+
<em>${esc(d.weekday)} ${esc(d.lunar)}</em></th>
|
|
24654
|
+
<td><b>${esc(d.dayGanZhi)}</b><em>${esc(d.zhiXing)}日</em></td>
|
|
24655
|
+
<td class="mp-zr-score">${esc(d.score)}</td>
|
|
24656
|
+
<td><span class="mp-zr-v ${cls}">${esc(d.verdict)}</span></td>
|
|
24657
|
+
<td class="mp-zr-why">${esc(d.reasons.join(';') || '无特别加减分')}</td>
|
|
24658
|
+
</tr>`;
|
|
24659
|
+
}).join('');
|
|
24660
|
+
const list = (label, arr) => `<span><em>${esc(label)}</em>${arr.length ? esc(arr.join('、')) : '无'}</span>`;
|
|
24661
|
+
const bestLead = r.compare.best
|
|
24662
|
+
? `这一组最优:<b>${esc(r.compare.best.date)}</b>(${esc(r.compare.best.score)} 分 · ${esc(r.compare.best.verdict)})`
|
|
24663
|
+
+ (r.compare.best.ties.length > 1
|
|
24664
|
+
? `;同分 ${esc(r.compare.best.ties.filter((d) => d !== r.compare.best.date).join('、'))}` : '')
|
|
24665
|
+
: '这一组都因冲属相被排除,没有可用的日子';
|
|
24666
|
+
return `<section class="mp-zr" aria-label="择日候选比较">
|
|
24667
|
+
<div class="mp-zr-h"><b>择日 · ${esc(meta.task || '')}</b>
|
|
24668
|
+
<span>候选 ${esc(meta.days)} 天${meta.shengXiao ? ` 当事人属${esc(meta.shengXiao)}` : '(未给属相,未做冲煞排除)'}</span></div>
|
|
24669
|
+
<div class="mp-zr-lead">${bestLead}</div>
|
|
24670
|
+
<table class="mp-zr-t"><thead><tr>
|
|
24671
|
+
<th scope="col">日期</th><th scope="col">日干支 / 建除</th><th scope="col">分</th><th scope="col">结论</th><th scope="col">理由</th>
|
|
24672
|
+
</tr></thead><tbody>${rows}</tbody></table>
|
|
24673
|
+
<div class="mp-zr-lists">
|
|
24674
|
+
${list('推荐', r.best)}${list('可用', r.usable)}${list('回避', r.avoid)}
|
|
24675
|
+
</div>
|
|
24676
|
+
<p class="mp-zr-note">${esc(r.compare.note || '')}</p>
|
|
24677
|
+
<p class="mp-zr-note">${esc(meta.scope || '')}</p>
|
|
24678
|
+
<p class="mp-zr-note">${esc(r.disclaimer || '')}</p>
|
|
24679
|
+
</section>`;
|
|
24680
|
+
};
|
|
24681
|
+
|
|
24016
24682
|
pp.quickHTML = function (r) {
|
|
24017
24683
|
if (!r) return '';
|
|
24018
24684
|
const LABEL = {
|
|
@@ -24168,6 +24834,8 @@ document.head.appendChild(Object.assign(document.createElement('style'), { textC
|
|
|
24168
24834
|
|
|
24169
24835
|
const el = (id) => document.getElementById(id);
|
|
24170
24836
|
const pad = (n) => String(n).padStart(2, '0');
|
|
24837
|
+
// 首屏耗时只在本机显示,不上报
|
|
24838
|
+
const mpNow = () => (typeof performance !== 'undefined' && performance.now ? performance.now() : Date.now());
|
|
24171
24839
|
|
|
24172
24840
|
// ── 首屏:最近用过 / 分组 ↔ 平铺 ────────────────────────────────
|
|
24173
24841
|
// 只记「点过哪个入口」,不存任何输入内容;读写失败(隐私模式、禁用存储)就降级成不记。
|
|
@@ -24415,9 +25083,8 @@ document.head.appendChild(Object.assign(document.createElement('style'), { textC
|
|
|
24415
25083
|
done(legacy());
|
|
24416
25084
|
}
|
|
24417
25085
|
|
|
24418
|
-
/**
|
|
24419
|
-
function
|
|
24420
|
-
const text = btn.getAttribute(attr) || '';
|
|
25086
|
+
/** 任意文本一键复制:优先 Clipboard API,失败退回 textarea + execCommand */
|
|
25087
|
+
function copyPlain(btn, text, idle) {
|
|
24421
25088
|
if (!text) return;
|
|
24422
25089
|
const done = (ok) => {
|
|
24423
25090
|
btn.textContent = ok ? '已复制' : '复制失败';
|
|
@@ -24445,9 +25112,66 @@ document.head.appendChild(Object.assign(document.createElement('style'), { textC
|
|
|
24445
25112
|
} catch (e) { /* 走下面的兜底 */ }
|
|
24446
25113
|
done(legacy());
|
|
24447
25114
|
}
|
|
25115
|
+
function copyAttr(btn, attr, idle) { return copyPlain(btn, btn.getAttribute(attr) || '', idle); }
|
|
24448
25116
|
const copyShadow = (btn) => copyAttr(btn, 'data-shadow-copy', '复制 ID');
|
|
24449
25117
|
const copyHandle = (btn) => copyAttr(btn, 'data-handle-copy', '复制句柄');
|
|
24450
25118
|
|
|
25119
|
+
// ── C3 桥替代(独立页):复制提问 / 深链 ────────────────────────
|
|
25120
|
+
// 独立页没有对话输入框,这几档就是「把盘交给助手」的全部出口:
|
|
25121
|
+
// 完整提问贴进任意对话窗、深链发给自己或别人。
|
|
25122
|
+
const pageBase = () => {
|
|
25123
|
+
try { return String(location.href).split('#')[0]; } catch (e) { return ''; }
|
|
25124
|
+
};
|
|
25125
|
+
function bridgePackages() {
|
|
25126
|
+
if (!last || !last.r || !window.Engine || !window.Engine.askPackages) return null;
|
|
25127
|
+
const q = el('question') ? el('question').value.trim() : '';
|
|
25128
|
+
try { return window.Engine.askPackages(last.r, { question: q, base: pageBase() }); }
|
|
25129
|
+
catch (e) { return null; }
|
|
25130
|
+
}
|
|
25131
|
+
|
|
25132
|
+
// ── C3 深链:打开带 #mp= 的链接时还原同一张盘 ──────────────────
|
|
25133
|
+
/** 读链接里的 #mp=;没有或不合法的返回 null(不报错,照旧排默认盘) */
|
|
25134
|
+
function readDeepLink() {
|
|
25135
|
+
try {
|
|
25136
|
+
const href = String(location.href || '');
|
|
25137
|
+
if (href.indexOf('mp=') < 0 || !window.Engine || !window.Engine.parseAskLink) return null;
|
|
25138
|
+
const deep = window.Engine.parseAskLink(href);
|
|
25139
|
+
if (!deep || !deep.ok) return null;
|
|
25140
|
+
const form = window.Engine.formOf ? window.Engine.formOf(deep) : null;
|
|
25141
|
+
if (!form || !form.tab) return null;
|
|
25142
|
+
if (!document.querySelector('.tab[data-tab="' + form.tab + '"], .tool[data-tab="' + form.tab + '"]')) return null;
|
|
25143
|
+
return { tab: form.tab, form, label: deep.label, question: deep.question };
|
|
25144
|
+
} catch (e) { return null; }
|
|
25145
|
+
}
|
|
25146
|
+
/** 把链接里的参数填进表单(在 setTab 之后调用:setTab 会按体系改字段可见性) */
|
|
25147
|
+
function fillDeepForm(form) {
|
|
25148
|
+
if (!form) return;
|
|
25149
|
+
const puts = [['date', form.date], ['time', form.time], ['gender', form.gender],
|
|
25150
|
+
['addr', typeof form.address === 'string' ? form.address : '']];
|
|
25151
|
+
for (const pair of puts) {
|
|
25152
|
+
const node = el(pair[0]);
|
|
25153
|
+
if (node && pair[1]) node.value = pair[1];
|
|
25154
|
+
}
|
|
25155
|
+
if (typeof form.trueSolar === 'boolean' && el('ts')) el('ts').checked = form.trueSolar;
|
|
25156
|
+
if (typeof form.lateZi === 'boolean' && el('latezi')) el('latezi').checked = form.lateZi;
|
|
25157
|
+
if (form.lines !== undefined && el('lines')) {
|
|
25158
|
+
el('lines').value = Array.isArray(form.lines) ? form.lines.join(' ') : String(form.lines);
|
|
25159
|
+
}
|
|
25160
|
+
if (form.question && el('question')) el('question').value = form.question;
|
|
25161
|
+
}
|
|
25162
|
+
/** 还原提示:写明哪张盘、什么问题,以及哪些参数没填回表单(不悄悄丢参数) */
|
|
25163
|
+
function deepNote(deep) {
|
|
25164
|
+
const out = el('out');
|
|
25165
|
+
if (!out || !deep) return;
|
|
25166
|
+
const rest = window.Engine && window.Engine.restNote ? window.Engine.restNote(deep.form) : '';
|
|
25167
|
+
const line = '已按链接还原:' + deep.label + (deep.question ? '|' + deep.question : '') + (rest ? ' ' + rest : '');
|
|
25168
|
+
// 用文本节点而不是拼 HTML:链接里的问题原样显示,也不会被当成标签解析
|
|
25169
|
+
const note = document.createElement('div');
|
|
25170
|
+
note.className = 'note';
|
|
25171
|
+
note.textContent = line;
|
|
25172
|
+
out.insertBefore(note, out.firstChild);
|
|
25173
|
+
}
|
|
25174
|
+
|
|
24451
25175
|
/** 推导链抽屉:打开时把焦点交给关闭按钮,关闭后回到入口 */
|
|
24452
25176
|
function traceOpen() {
|
|
24453
25177
|
const layer = document.getElementById('mp-trace-layer');
|
|
@@ -24491,6 +25215,11 @@ document.head.appendChild(Object.assign(document.createElement('style'), { textC
|
|
|
24491
25215
|
+ (last.tab === 'bazi' ? `<div id="vbox">${verifyHTML()}</div><div id="rectbox">${rectifyHTML()}</div>` : '')
|
|
24492
25216
|
+ exportBar();
|
|
24493
25217
|
last.it = it;
|
|
25218
|
+
// 耗时行跟盘走:方向切换等重绘会把 innerHTML 整个换掉,不在这里补一次就会丢
|
|
25219
|
+
if (last.perf) {
|
|
25220
|
+
const perfHtml = window.__PP.perfHTML(last.perf);
|
|
25221
|
+
if (perfHtml) el('out').insertAdjacentHTML('beforeend', perfHtml);
|
|
25222
|
+
}
|
|
24494
25223
|
}
|
|
24495
25224
|
|
|
24496
25225
|
/** 追问区:只对已算好的盘发问,不重新起盘 */
|
|
@@ -24498,6 +25227,7 @@ document.head.appendChild(Object.assign(document.createElement('style'), { textC
|
|
|
24498
25227
|
return `<div class="askbox no-print">
|
|
24499
25228
|
<input id="askq" maxlength="120" placeholder="追问:如 2027 年事业怎么样(回车提交)">
|
|
24500
25229
|
<button type="button" data-act="ask">追问</button>
|
|
25230
|
+
<button type="button" data-act="copyask" title="把这张盘和你的问题整理成一段提问,粘到任意对话窗都能用">复制提问</button>
|
|
24501
25231
|
<span class="hint">只回答盘面能支持的部分;找不到会说找不到</span>
|
|
24502
25232
|
</div><div id="fout"></div>`;
|
|
24503
25233
|
}
|
|
@@ -24581,7 +25311,8 @@ document.head.appendChild(Object.assign(document.createElement('style'), { textC
|
|
|
24581
25311
|
<button type="button" data-act="print">打印</button>
|
|
24582
25312
|
<button type="button" data-act="md">导出 .md</button>
|
|
24583
25313
|
<button type="button" data-act="png">存为摘要长图</button>
|
|
24584
|
-
<
|
|
25314
|
+
<button type="button" data-act="link" title="把这张盘的参数编进链接:打开就还原同一张盘,也能直接发给别人">复制深链</button>
|
|
25315
|
+
<span class="hint">长图是 PNG 摘要卡(一页读懂 + 首张表);.md 存档;深链还原同一张盘(编码不是加密,别公开贴)</span>
|
|
24585
25316
|
</div>`;
|
|
24586
25317
|
}
|
|
24587
25318
|
|
|
@@ -24590,6 +25321,16 @@ document.head.appendChild(Object.assign(document.createElement('style'), { textC
|
|
|
24590
25321
|
if (!b) return;
|
|
24591
25322
|
if (b.dataset.act === 'md') { downloadReport(); return; }
|
|
24592
25323
|
if (b.dataset.act === 'png') { downloadCard(); return; }
|
|
25324
|
+
if (b.dataset.act === 'link') {
|
|
25325
|
+
const pkg = bridgePackages();
|
|
25326
|
+
if (!pkg || !pkg.link) {
|
|
25327
|
+
b.textContent = '没有深链';
|
|
25328
|
+
setTimeout(() => { b.textContent = '复制深链'; }, 1400);
|
|
25329
|
+
return;
|
|
25330
|
+
}
|
|
25331
|
+
copyPlain(b, pkg.link, '复制深链');
|
|
25332
|
+
return;
|
|
25333
|
+
}
|
|
24593
25334
|
window.print();
|
|
24594
25335
|
}
|
|
24595
25336
|
|
|
@@ -24621,6 +25362,7 @@ document.head.appendChild(Object.assign(document.createElement('style'), { textC
|
|
|
24621
25362
|
}
|
|
24622
25363
|
|
|
24623
25364
|
async function run() {
|
|
25365
|
+
const runStart = mpNow();
|
|
24624
25366
|
const date = el('date').value;
|
|
24625
25367
|
const time = el('time').value;
|
|
24626
25368
|
let lineWarn = '';
|
|
@@ -24663,6 +25405,14 @@ document.head.appendChild(Object.assign(document.createElement('style'), { textC
|
|
|
24663
25405
|
address: el('paddr').value.trim() || undefined,
|
|
24664
25406
|
trueSolar: el('ts').checked,
|
|
24665
25407
|
};
|
|
25408
|
+
if (el('h3on') && el('h3on').checked) {
|
|
25409
|
+
args.partner2 = {
|
|
25410
|
+
datetime: `${el('p2date').value}T${el('p2time').value}`,
|
|
25411
|
+
gender: el('p2gender').value,
|
|
25412
|
+
address: el('p2addr').value.trim() || undefined,
|
|
25413
|
+
trueSolar: el('ts').checked,
|
|
25414
|
+
};
|
|
25415
|
+
}
|
|
24666
25416
|
}
|
|
24667
25417
|
// 八字/紫微可勾选「交叉验证」:同一份出生信息再排另一套盘并列对照
|
|
24668
25418
|
if ((tab === 'bazi' || tab === 'ziwei') && el('cross') && el('cross').checked) args.cross = true;
|
|
@@ -24699,6 +25449,8 @@ document.head.appendChild(Object.assign(document.createElement('style'), { textC
|
|
|
24699
25449
|
args.task = el('ztask').value;
|
|
24700
25450
|
args.days = Number(el('zdays').value) || 30;
|
|
24701
25451
|
if (el('zsheng').value) args.shengXiao = el('zsheng').value;
|
|
25452
|
+
const cand = el('zcand') ? el('zcand').value.split(/[,,、\s]+/).filter(Boolean) : [];
|
|
25453
|
+
if (cand.length) args.candidates = cand;
|
|
24702
25454
|
}
|
|
24703
25455
|
if (tab === 'xiaoliuren') {
|
|
24704
25456
|
args.mode = xlrMode;
|
|
@@ -24765,7 +25517,9 @@ document.head.appendChild(Object.assign(document.createElement('style'), { textC
|
|
|
24765
25517
|
// 奇门的用神同样由方向定,推导链的「用神落宫」层要用到
|
|
24766
25518
|
if (tab === 'qimen') args.direction = el('qdir').value;
|
|
24767
25519
|
try {
|
|
25520
|
+
const computeStart = mpNow();
|
|
24768
25521
|
const r = window.Engine.compute(args);
|
|
25522
|
+
const computeMs = mpNow() - computeStart;
|
|
24769
25523
|
// 影子盘:先与本地上一张同 baseId 的盘比对,再存档;失败不影响主结果
|
|
24770
25524
|
let shadowDiff = null;
|
|
24771
25525
|
try { shadowDiff = window.__PP.shadowTrack ? window.__PP.shadowTrack(r) : null; } catch (e) { shadowDiff = null; }
|
|
@@ -24796,6 +25550,7 @@ document.head.appendChild(Object.assign(document.createElement('style'), { textC
|
|
|
24796
25550
|
: tab === 'tarot' ? window.__PP.tarotHTML(r)
|
|
24797
25551
|
: tab === 'liuyao' ? window.__PP.liuyaoHTML(r)
|
|
24798
25552
|
: tab === 'almanac' ? window.__PP.almanacHTML(r)
|
|
25553
|
+
: tab === 'zeri' ? window.__PP.zeriHTML(r)
|
|
24799
25554
|
: tab === 'chenggu' ? window.__PP.chengguHTML(r)
|
|
24800
25555
|
: tab === 'trend' ? window.__PP.trendHTML(r)
|
|
24801
25556
|
: tab === 'name' ? window.__PP.nameHTML(r)
|
|
@@ -24803,7 +25558,13 @@ document.head.appendChild(Object.assign(document.createElement('style'), { textC
|
|
|
24803
25558
|
: tab === 'fengshui' ? window.__PP.fengshuiHTML(r)
|
|
24804
25559
|
: window.__PP.quickHTML(r));
|
|
24805
25560
|
last = { r, html, tab, date, time, timed, trend, caliber, shadowDiff };
|
|
25561
|
+
const paintStart = mpNow();
|
|
24806
25562
|
paint();
|
|
25563
|
+
// 渲染=组 HTML + 写入 DOM 的同步时间;浏览器真正绘制在下一帧,这里不假装量到
|
|
25564
|
+
const renderMs = mpNow() - paintStart;
|
|
25565
|
+
last.perf = { computeMs, renderMs, totalMs: mpNow() - runStart };
|
|
25566
|
+
const perfHtml = window.__PP.perfHTML(last.perf);
|
|
25567
|
+
if (perfHtml) el('out').insertAdjacentHTML('beforeend', perfHtml);
|
|
24807
25568
|
} catch (e) {
|
|
24808
25569
|
last = null;
|
|
24809
25570
|
// 错误文案里会带出用户输入的原文(如非法的 datetime),只能走 textContent,不能拼进 innerHTML
|
|
@@ -25176,10 +25937,17 @@ document.head.appendChild(Object.assign(document.createElement('style'), { textC
|
|
|
25176
25937
|
document.querySelectorAll('#hpchart button').forEach((x) => x.classList.toggle('on', x === b));
|
|
25177
25938
|
run();
|
|
25178
25939
|
}));
|
|
25179
|
-
['pdate', 'ptime', 'pgender', 'paddr'].forEach((id) => {
|
|
25940
|
+
['pdate', 'ptime', 'pgender', 'paddr', 'p2date', 'p2time', 'p2gender', 'p2addr'].forEach((id) => {
|
|
25180
25941
|
const node = el(id);
|
|
25181
25942
|
if (node) node.addEventListener('change', run);
|
|
25182
25943
|
});
|
|
25944
|
+
// 三方合盘:勾选后才展开第三人的四个字段
|
|
25945
|
+
const h3 = el('h3on');
|
|
25946
|
+
if (h3) h3.addEventListener('change', () => {
|
|
25947
|
+
const box = el('for-hepan3');
|
|
25948
|
+
if (box) box.style.display = h3.checked ? '' : 'none';
|
|
25949
|
+
run();
|
|
25950
|
+
});
|
|
25183
25951
|
// 奇门:换局规则改动重起局;询问方向改动只重出解读(用神跟着方向走)
|
|
25184
25952
|
document.querySelectorAll('input[name="qimen_rule"]').forEach((n) => n.addEventListener('change', run));
|
|
25185
25953
|
const qdir = el('qdir');
|
|
@@ -25202,7 +25970,7 @@ document.head.appendChild(Object.assign(document.createElement('style'), { textC
|
|
|
25202
25970
|
seg('#fsmode button', (v) => { fsMode = v; });
|
|
25203
25971
|
seg('#nnmode button', (v) => { nnMode = v; });
|
|
25204
25972
|
seg('#convertmode button', (v) => { convMode = v; syncQuick(); run(); });
|
|
25205
|
-
['ztask', 'zdays', 'zsheng', 'num1', 'num2', 'num3', 'toage', 'sitting', 'facing', 'fsperiod', 'fsoffset', 'fsdate', 'fstime', 'surname', 'givenname', 'nlimit', 'lunardate', 'leapm']
|
|
25973
|
+
['ztask', 'zdays', 'zsheng', 'zcand', 'num1', 'num2', 'num3', 'toage', 'sitting', 'facing', 'fsperiod', 'fsoffset', 'fsdate', 'fstime', 'surname', 'givenname', 'nlimit', 'lunardate', 'leapm']
|
|
25206
25974
|
.forEach((id) => { const n = el(id); if (n) n.addEventListener('change', run); });
|
|
25207
25975
|
// 方法页:按钮开合(静态内容,无网络)
|
|
25208
25976
|
const mOpen = el('method-open');
|
|
@@ -25254,6 +26022,12 @@ document.head.appendChild(Object.assign(document.createElement('style'), { textC
|
|
|
25254
26022
|
if (rc) { doRectify(); return; }
|
|
25255
26023
|
const askBtn = e.target && e.target.closest ? e.target.closest('[data-act="ask"]') : null;
|
|
25256
26024
|
if (askBtn) { doAsk(); return; }
|
|
26025
|
+
const copyAsk = e.target && e.target.closest ? e.target.closest('[data-act="copyask"]') : null;
|
|
26026
|
+
if (copyAsk) {
|
|
26027
|
+
const pkg = bridgePackages();
|
|
26028
|
+
if (pkg) copyPlain(copyAsk, pkg.full, '复制提问');
|
|
26029
|
+
return;
|
|
26030
|
+
}
|
|
25257
26031
|
onExportClick(e);
|
|
25258
26032
|
const head = e.target && e.target.closest ? e.target.closest('.mp-it-head') : null;
|
|
25259
26033
|
if (!head) return;
|
|
@@ -25280,8 +26054,11 @@ document.head.appendChild(Object.assign(document.createElement('style'), { textC
|
|
|
25280
26054
|
document.querySelectorAll('.tab, .tool').forEach((b) => b.addEventListener('click', () => {
|
|
25281
26055
|
if (needAsk(tab) && !el('question').value.trim() && el('question').focus) el('question').focus();
|
|
25282
26056
|
}));
|
|
25283
|
-
|
|
25284
|
-
|
|
26057
|
+
// C3 深链:带 #mp= 打开时先还原那张盘;没有就照旧是默认示例盘
|
|
26058
|
+
const deep = readDeepLink();
|
|
26059
|
+
setTab(deep ? deep.tab : 'ziwei');
|
|
26060
|
+
if (deep) fillDeepForm(deep.form);
|
|
26061
|
+
Promise.resolve(run()).then(() => { if (deep) deepNote(deep); });
|
|
25285
26062
|
});
|
|
25286
26063
|
})();
|
|
25287
26064
|
|