dsh-metaphysics 0.17.0 → 0.18.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 +99 -14
- package/README.md +54 -14
- package/client.js +2098 -35
- package/engine/almanac.js +215 -0
- package/engine/bazi.js +10 -2
- package/engine/chenggu.js +126 -0
- package/engine/dst.js +68 -0
- package/engine/fengshui.js +288 -0
- package/engine/geo.js +1 -1
- package/engine/hepan.js +82 -1
- package/engine/index.js +277 -2
- package/engine/name.js +303 -0
- package/engine/numerology.js +60 -0
- package/engine/rectify.js +1 -0
- package/engine/star.js +82 -0
- package/engine/trend.js +193 -0
- package/engine/xiaoliuren.js +64 -0
- package/engine/zeri.js +127 -0
- package/engine/ziwei.js +7 -2
- package/index.js +93 -25
- package/locale/en.json +1 -1
- package/locale/zh.json +1 -1
- package/package.json +17 -4
package/client.js
CHANGED
|
@@ -746,7 +746,7 @@ function resolveAddress(input) {
|
|
|
746
746
|
|
|
747
747
|
const DETAIL = /(区|县|镇|乡|村|小区|街道|路|号|巷|弄|组|队)/;
|
|
748
748
|
|
|
749
|
-
const USER_AGENT = 'dsh-metaphysics/0.
|
|
749
|
+
const USER_AGENT = 'dsh-metaphysics/0.18.0 (+https://github.com/xuanfu-web/dsh-metaphysics)';
|
|
750
750
|
|
|
751
751
|
async function onlineLookup(text, { timeoutMs = 2500 } = {}) {
|
|
752
752
|
if (typeof fetch !== 'function') return null;
|
|
@@ -821,6 +821,63 @@ function isSeed(v) {
|
|
|
821
821
|
return { mulberry32, randomOf, isSeed };
|
|
822
822
|
})();
|
|
823
823
|
|
|
824
|
+
__mods.dst = (function () {
|
|
825
|
+
const { daysInMonth } = __mods.validate;
|
|
826
|
+
|
|
827
|
+
const DST_SPANS = {
|
|
828
|
+
1986: ['1986-05-04', '1986-09-14'],
|
|
829
|
+
1987: ['1987-04-12', '1987-09-13'],
|
|
830
|
+
1988: ['1988-04-10', '1988-09-11'],
|
|
831
|
+
1989: ['1989-04-16', '1989-09-17'],
|
|
832
|
+
1990: ['1990-04-15', '1990-09-16'],
|
|
833
|
+
1991: ['1991-04-14', '1991-09-15'],
|
|
834
|
+
};
|
|
835
|
+
|
|
836
|
+
const ORD = (y, m, d) => y * 10000 + m * 100 + d;
|
|
837
|
+
|
|
838
|
+
function inDstSpan(t) {
|
|
839
|
+
const span = DST_SPANS[t.year];
|
|
840
|
+
if (!span) return false;
|
|
841
|
+
const [sy, sm, sd] = span[0].split('-').map(Number);
|
|
842
|
+
const [ey, em, ed] = span[1].split('-').map(Number);
|
|
843
|
+
const n = ORD(t.year, t.month, t.day);
|
|
844
|
+
if (n < ORD(sy, sm, sd) || n > ORD(ey, em, ed)) return false;
|
|
845
|
+
const minutes = t.hour * 60 + (t.minute || 0);
|
|
846
|
+
if (n === ORD(sy, sm, sd) && minutes < 120) return false;
|
|
847
|
+
if (n === ORD(ey, em, ed) && minutes >= 120) return false;
|
|
848
|
+
return true;
|
|
849
|
+
}
|
|
850
|
+
|
|
851
|
+
function applyDst(t) {
|
|
852
|
+
const minute = t.minute || 0;
|
|
853
|
+
if (!inDstSpan(t)) return { year: t.year, month: t.month, day: t.day, hour: t.hour, minute, applied: false, note: null };
|
|
854
|
+
let { year, month, day, hour } = t;
|
|
855
|
+
hour -= 1;
|
|
856
|
+
if (hour < 0) {
|
|
857
|
+
hour += 24;
|
|
858
|
+
day -= 1;
|
|
859
|
+
if (day < 1) {
|
|
860
|
+
month -= 1;
|
|
861
|
+
if (month < 1) { month = 12; year -= 1; }
|
|
862
|
+
day = daysInMonth(year, month);
|
|
863
|
+
}
|
|
864
|
+
}
|
|
865
|
+
return {
|
|
866
|
+
year, month, day, hour, minute,
|
|
867
|
+
applied: true,
|
|
868
|
+
note: `检出自 ${DST_SPANS[t.year][0]} 至 ${DST_SPANS[t.year][1]} 的中国夏令时:出生钟面时间已减 1 小时校正`,
|
|
869
|
+
};
|
|
870
|
+
}
|
|
871
|
+
|
|
872
|
+
function withDstNote(notes, dst) {
|
|
873
|
+
const out = Array.isArray(notes) ? notes.slice() : [];
|
|
874
|
+
if (dst && dst.applied && dst.note) out.push(dst.note);
|
|
875
|
+
return out;
|
|
876
|
+
}
|
|
877
|
+
|
|
878
|
+
return { DST_SPANS, inDstSpan, applyDst, withDstNote };
|
|
879
|
+
})();
|
|
880
|
+
|
|
824
881
|
__mods.shensha = (function () {
|
|
825
882
|
const { BRANCHES } = __mods.calendar;
|
|
826
883
|
|
|
@@ -1532,6 +1589,7 @@ const { STEMS, BRANCHES, TERM_NAMES, ganzhiName, dayGanzhiIndex, jdToParts, part
|
|
|
1532
1589
|
const { resolveAddress } = __mods.geo;
|
|
1533
1590
|
const { computeBaziShensha } = __mods.shensha;
|
|
1534
1591
|
const { assertCivil } = __mods.validate;
|
|
1592
|
+
const { applyDst } = __mods.dst;
|
|
1535
1593
|
const { nayinOf } = __mods.nayin;
|
|
1536
1594
|
const { SANHE } = __mods.sanhe;
|
|
1537
1595
|
const { analyzeBaziFrame, scoreLuck } = __mods.geju;
|
|
@@ -1716,9 +1774,13 @@ function applyTrueSolar(jd, longitude) {
|
|
|
1716
1774
|
|
|
1717
1775
|
function buildBazi(input) {
|
|
1718
1776
|
assertCivil(input);
|
|
1719
|
-
|
|
1777
|
+
let { year, month, day, hour = 0, minute = 0, gender = 'male' } = input;
|
|
1720
1778
|
const lateZi = input.lateZi !== false;
|
|
1721
1779
|
const useTrueSolar = input.trueSolar !== false;
|
|
1780
|
+
const dst = input.dst === false ? { applied: false, note: null } : applyDst({ year, month, day, hour, minute });
|
|
1781
|
+
if (dst.applied) {
|
|
1782
|
+
year = dst.year; month = dst.month; day = dst.day; hour = dst.hour; minute = dst.minute;
|
|
1783
|
+
}
|
|
1722
1784
|
|
|
1723
1785
|
const civilJd = partsToJd({ year, month, day, hour, minute });
|
|
1724
1786
|
const geo = input.geo || (input.address ? resolveAddress(input.address) : null);
|
|
@@ -1952,12 +2014,13 @@ function buildBazi(input) {
|
|
|
1952
2014
|
geo,
|
|
1953
2015
|
lateZi,
|
|
1954
2016
|
trueSolar: useTrueSolar,
|
|
2017
|
+
dst: { applied: !!dst.applied, note: dst.note || null },
|
|
1955
2018
|
},
|
|
1956
2019
|
solarTime: {
|
|
1957
2020
|
civilText: `${year}-${String(month).padStart(2, '0')}-${String(day).padStart(2, '0')} ${String(hour).padStart(2, '0')}:${String(minute).padStart(2, '0')}`,
|
|
1958
2021
|
trueSolarText: formatJd(jd),
|
|
1959
2022
|
offsetMinutes: solar.offsetMinutes,
|
|
1960
|
-
note: solar.note,
|
|
2023
|
+
note: dst.applied ? `${dst.note};${solar.note}` : solar.note,
|
|
1961
2024
|
lunarYearForPillar: solarYear,
|
|
1962
2025
|
},
|
|
1963
2026
|
jieqi: {
|
|
@@ -2216,6 +2279,7 @@ __mods.ziwei = (function () {
|
|
|
2216
2279
|
const { STEMS, BRANCHES, ganzhiName, sexagenaryIndex, jdToParts, partsToJd, formatJd } = __mods.calendar;
|
|
2217
2280
|
const { solarToLunar } = __mods.lunar;
|
|
2218
2281
|
const { applyTrueSolar } = __mods.bazi;
|
|
2282
|
+
const { applyDst } = __mods.dst;
|
|
2219
2283
|
const { assertCivil } = __mods.validate;
|
|
2220
2284
|
const { nayinOf } = __mods.nayin;
|
|
2221
2285
|
const { resolveAddress } = __mods.geo;
|
|
@@ -2390,7 +2454,9 @@ function buildZiwei(input) {
|
|
|
2390
2454
|
const longitude = typeof input.longitude === 'number'
|
|
2391
2455
|
? input.longitude
|
|
2392
2456
|
: (geo && typeof geo.lon === 'number' ? geo.lon : undefined);
|
|
2393
|
-
const
|
|
2457
|
+
const civilJdRaw = partsToJd({ year: cy, month: cm, day: cd, hour: ch, minute: cmin });
|
|
2458
|
+
const dst = input.dst === false ? { applied: false, note: null } : applyDst({ year: cy, month: cm, day: cd, hour: ch, minute: cmin });
|
|
2459
|
+
const civilJd = dst.applied ? partsToJd({ year: dst.year, month: dst.month, day: dst.day, hour: dst.hour, minute: dst.minute }) : civilJdRaw;
|
|
2394
2460
|
const solar = useTrueSolar
|
|
2395
2461
|
? applyTrueSolar(civilJd, longitude)
|
|
2396
2462
|
: { offsetMinutes: 0, jd: civilJd, note: '未启用真太阳时' };
|
|
@@ -2627,12 +2693,13 @@ function buildZiwei(input) {
|
|
|
2627
2693
|
address: input.address || null,
|
|
2628
2694
|
geo,
|
|
2629
2695
|
trueSolar: useTrueSolar,
|
|
2696
|
+
dst: { applied: !!dst.applied, note: dst.note || null },
|
|
2630
2697
|
},
|
|
2631
2698
|
solarTime: {
|
|
2632
2699
|
civilText: `${cy}-${String(cm).padStart(2, '0')}-${String(cd).padStart(2, '0')} ${String(ch).padStart(2, '0')}:${String(cmin).padStart(2, '0')}`,
|
|
2633
2700
|
trueSolarText: formatJd(solar.jd),
|
|
2634
2701
|
offsetMinutes: solar.offsetMinutes,
|
|
2635
|
-
note: solar.note,
|
|
2702
|
+
note: dst.applied ? `${dst.note};${solar.note}` : solar.note,
|
|
2636
2703
|
},
|
|
2637
2704
|
lunar: {
|
|
2638
2705
|
year: lunarRaw.lunarYear,
|
|
@@ -3792,7 +3859,7 @@ return { ZHI, GAN, TIAN_JIANG, JIANG_FULL, JIEQI_YUE_JIANG, GAN_JI_GONG, calcShe
|
|
|
3792
3859
|
})();
|
|
3793
3860
|
|
|
3794
3861
|
__mods.hepan = (function () {
|
|
3795
|
-
const { buildBazi } = __mods.bazi;
|
|
3862
|
+
const { buildBazi, tenGod } = __mods.bazi;
|
|
3796
3863
|
const { STEMS, BRANCHES } = __mods.calendar;
|
|
3797
3864
|
const { assertCivil } = __mods.validate;
|
|
3798
3865
|
const { SANHE_GROUPS } = __mods.sanhe;
|
|
@@ -3835,6 +3902,20 @@ function wxToMe(other, me) {
|
|
|
3835
3902
|
return { name: '克我', score: -12, text: '对方克我,压力感偏重,宜把话说开' };
|
|
3836
3903
|
}
|
|
3837
3904
|
|
|
3905
|
+
function yearBranchRelation(a, b) {
|
|
3906
|
+
if (a === b) return { name: '同属相(自刑之一)', score: -3, text: '属相相同,传统称「自刑」,容易互相较劲、谁也不让谁' };
|
|
3907
|
+
if (pairIn(LIU_HE, a, b)) return { name: '六合', score: 3, text: '属相六合,初印象与长辈缘通常不错' };
|
|
3908
|
+
if (SANHE_GROUPS.some((g) => g.branches.includes(a) && g.branches.includes(b))) {
|
|
3909
|
+
return { name: '三合', score: 3, text: '属相三合,容易有共同话题与共同朋友' };
|
|
3910
|
+
}
|
|
3911
|
+
if (pairIn(CHONG, a, b)) return { name: '六冲', score: -3, text: '属相六冲,节奏与立场容易相反,重大决定前宜多对一遍' };
|
|
3912
|
+
if (pairIn(HAI, a, b)) return { name: '相害', score: -2, text: '属相相害,容易因第三方或误会生嫌隙' };
|
|
3913
|
+
for (const g of XING) {
|
|
3914
|
+
if (g.includes(a) && g.includes(b)) return { name: '相刑', score: -2, text: '属相相刑,摩擦多起于相处细节' };
|
|
3915
|
+
}
|
|
3916
|
+
return { name: '无直接关系', score: 0, text: '属相之间没有合冲刑害,表层影响小,以日柱为主' };
|
|
3917
|
+
}
|
|
3918
|
+
|
|
3838
3919
|
function buildHepan(input) {
|
|
3839
3920
|
assertCivil(input && input.a, 'a');
|
|
3840
3921
|
assertCivil(input && input.b, 'b');
|
|
@@ -3885,6 +3966,68 @@ function buildHepan(input) {
|
|
|
3885
3966
|
const pct = Math.max(0, Math.min(100, Math.round(((raw + 27) / 83) * 100)));
|
|
3886
3967
|
const level = pct >= 75 ? '契合度较高' : pct >= 55 ? '中等偏顺' : pct >= 40 ? '需要磨合' : '差异较大';
|
|
3887
3968
|
|
|
3969
|
+
const aYear = A.pillars.year;
|
|
3970
|
+
const bYear = B.pillars.year;
|
|
3971
|
+
const yearRel = yearBranchRelation(aYear.branch, bYear.branch);
|
|
3972
|
+
const HE5 = [['甲', '己'], ['乙', '庚'], ['丙', '辛'], ['丁', '壬'], ['戊', '癸']];
|
|
3973
|
+
const he5 = HE5.some((p) => p.includes(aStem) && p.includes(bStem) && aStem !== bStem);
|
|
3974
|
+
const godA = tenGod(aStem, bStem);
|
|
3975
|
+
const godB = tenGod(bStem, aStem);
|
|
3976
|
+
const A_BR = ['year', 'month', 'day', 'hour'].map((k) => A.pillars[k].branch);
|
|
3977
|
+
const B_BR = ['year', 'month', 'day', 'hour'].map((k) => B.pillars[k].branch);
|
|
3978
|
+
const cross = [];
|
|
3979
|
+
for (const x of A_BR) {
|
|
3980
|
+
for (const y of B_BR) {
|
|
3981
|
+
if (pairIn(LIU_HE, x, y)) cross.push({ a: x, b: y, name: '六合', score: 1, text: '跨盘六合,易有自然的亲近与默契' });
|
|
3982
|
+
else if (SANHE_GROUPS.some((g) => g.branches.includes(x) && g.branches.includes(y))) {
|
|
3983
|
+
cross.push({ a: x, b: y, name: '半合', score: 1, text: '跨盘半合,某些事上容易一拍即合' });
|
|
3984
|
+
} else if (pairIn(CHONG, x, y)) cross.push({ a: x, b: y, name: '相冲', score: -1, text: '跨盘相冲,某些领域节奏相反,需要分工' });
|
|
3985
|
+
else if (pairIn(HAI, x, y)) cross.push({ a: x, b: y, name: '相害', score: -1, text: '跨盘相害,容易在第三方或琐事上起误会' });
|
|
3986
|
+
}
|
|
3987
|
+
}
|
|
3988
|
+
const uniqCross = [];
|
|
3989
|
+
const seen = new Set();
|
|
3990
|
+
for (const c of cross) {
|
|
3991
|
+
const k = `${c.name}${[c.a, c.b].sort().join('')}`;
|
|
3992
|
+
if (seen.has(k)) continue;
|
|
3993
|
+
seen.add(k);
|
|
3994
|
+
uniqCross.push(c);
|
|
3995
|
+
}
|
|
3996
|
+
const nyA = aYear.nayin;
|
|
3997
|
+
const nyB = bYear.nayin;
|
|
3998
|
+
const naEl = nyA[nyA.length - 1];
|
|
3999
|
+
const nbEl = nyB[nyB.length - 1];
|
|
4000
|
+
const nayinRel = naEl === nbEl
|
|
4001
|
+
? { name: '纳音比和', score: 0, text: '两人年命纳音同五行,底色相近' }
|
|
4002
|
+
: SHENG[naEl] === nbEl
|
|
4003
|
+
? { name: '纳音相生', score: 1, text: `你的年命纳音属${naEl}生对方${nbEl},你多是先付出的一方` }
|
|
4004
|
+
: SHENG[nbEl] === naEl
|
|
4005
|
+
? { name: '纳音相生', score: 1, text: `对方年命纳音属${nbEl}生你${naEl},你容易得到照应` }
|
|
4006
|
+
: KE[naEl] === nbEl
|
|
4007
|
+
? { name: '纳音相克', score: -1, text: `你的年命纳音属${naEl}克对方${nbEl},主导感偏强` }
|
|
4008
|
+
: { name: '纳音相克', score: -1, text: `对方年命纳音属${nbEl}克你${naEl},压力感偏重` };
|
|
4009
|
+
const good = (yearRel.score > 0 ? 1 : 0) + (he5 ? 1 : 0) + (gong.score > 5 ? 1 : 0)
|
|
4010
|
+
+ uniqCross.filter((c) => c.score > 0).length + (nayinRel.score > 0 ? 1 : 0);
|
|
4011
|
+
const bad = (yearRel.score < 0 ? 1 : 0) + (gong.score < 0 ? 1 : 0)
|
|
4012
|
+
+ uniqCross.filter((c) => c.score < 0).length + (nayinRel.score < 0 ? 1 : 0);
|
|
4013
|
+
const lean = good - bad >= 3 ? '吉象偏多' : good - bad >= 1 ? '吉略多于凶' : good - bad === 0 ? '吉凶相当' : '凶象偏多';
|
|
4014
|
+
|
|
4015
|
+
const detail = {
|
|
4016
|
+
yearRelation: { label: yearRel.name, text: `属相:你${aYear.branch}(${aYear.ganzhi}),对方${bYear.branch}(${bYear.ganzhi})。${yearRel.text}。` },
|
|
4017
|
+
stems: {
|
|
4018
|
+
he5,
|
|
4019
|
+
tenGodToB: godA,
|
|
4020
|
+
tenGodToA: godB,
|
|
4021
|
+
text: he5
|
|
4022
|
+
? `日干五合(${aStem}${bStem}),传统称「天干五合」,主彼此吸引强,也容易黏在一起分不开。`
|
|
4023
|
+
: `日干无五合:你在对方盘里是「${godA}」,对方在你盘里是「${godB}」。`,
|
|
4024
|
+
},
|
|
4025
|
+
crossBranches: uniqCross.slice(0, 8),
|
|
4026
|
+
nayin: { a: nyA, b: nyB, label: nayinRel.name, text: nayinRel.text },
|
|
4027
|
+
tally: { good, bad, lean },
|
|
4028
|
+
framing: '属相(年支)只影响表层印象,传统合婚以日柱为主;分档只说明吉凶象多少,不作「必配/必离」断言。',
|
|
4029
|
+
};
|
|
4030
|
+
|
|
3888
4031
|
const dim = (key, label, score, text, extra) => ({ key, label, score: Math.round(score), text, ...(extra || {}) });
|
|
3889
4032
|
|
|
3890
4033
|
return {
|
|
@@ -3916,6 +4059,7 @@ function buildHepan(input) {
|
|
|
3916
4059
|
summary: B.summary,
|
|
3917
4060
|
},
|
|
3918
4061
|
score: { total: pct, level },
|
|
4062
|
+
detail,
|
|
3919
4063
|
dimensions: [
|
|
3920
4064
|
dim('stem', '日主关系', stemScore, `你的日主 ${aStem}${aWx},对方 ${bStem}${bWx}。${aToB.text};${bToA.text}。`),
|
|
3921
4065
|
dim('gong', '夫妻宫(日支)', gong.score, `${gong.name}:${gong.text}。你日支 ${aZhi},对方日支 ${bZhi}。`),
|
|
@@ -3931,6 +4075,731 @@ function buildHepan(input) {
|
|
|
3931
4075
|
return { buildHepan };
|
|
3932
4076
|
})();
|
|
3933
4077
|
|
|
4078
|
+
__mods.trend = (function () {
|
|
4079
|
+
const { buildBazi, HIDDEN_STEMS, tenGod } = __mods.bazi;
|
|
4080
|
+
const { ganzhiName, sexagenaryIndex, STEMS, BRANCHES } = __mods.calendar;
|
|
4081
|
+
|
|
4082
|
+
const SHISHEN_WEAK = {
|
|
4083
|
+
正印: 2, 偏印: 2, 比肩: 2, 劫财: 1,
|
|
4084
|
+
食神: -1, 伤官: -2, 偏财: -2, 正财: -2,
|
|
4085
|
+
正官: -1, 七杀: -3,
|
|
4086
|
+
};
|
|
4087
|
+
const SHISHEN_STRONG = {
|
|
4088
|
+
正印: -2, 偏印: -2, 比肩: -2, 劫财: -1,
|
|
4089
|
+
食神: 1, 伤官: 1, 偏财: 2, 正财: 2,
|
|
4090
|
+
正官: 2, 七杀: 1,
|
|
4091
|
+
};
|
|
4092
|
+
const SHISHEN_NEUTRAL = {
|
|
4093
|
+
正印: 1, 偏印: 0, 比肩: 0, 劫财: -1,
|
|
4094
|
+
食神: 1, 伤官: -2, 偏财: 1, 正财: 2,
|
|
4095
|
+
正官: 2, 七杀: -2,
|
|
4096
|
+
};
|
|
4097
|
+
|
|
4098
|
+
const FAVOR = { 身强: 'strong', 偏强: 'strong', 中和: 'neutral', 偏弱: 'weak', 身弱: 'weak' };
|
|
4099
|
+
const TABLE_OF = { strong: SHISHEN_STRONG, neutral: SHISHEN_NEUTRAL, weak: SHISHEN_WEAK };
|
|
4100
|
+
|
|
4101
|
+
const LEVEL_CUTS = [0.5, 1.5, 2.5, 4];
|
|
4102
|
+
const BOUNDARY_EPS = 0.3;
|
|
4103
|
+
|
|
4104
|
+
const clamp = (v, lo, hi) => Math.max(lo, Math.min(hi, v));
|
|
4105
|
+
|
|
4106
|
+
function tenGodOfZhi(dayStem, branch) {
|
|
4107
|
+
const main = HIDDEN_STEMS[branch] && HIDDEN_STEMS[branch][0];
|
|
4108
|
+
return main ? tenGod(dayStem, main) : null;
|
|
4109
|
+
}
|
|
4110
|
+
|
|
4111
|
+
const yearGanzhi = (year) => ganzhiName(((year - 4) % 60 + 60) % 60);
|
|
4112
|
+
|
|
4113
|
+
function extendDayun(list, direction, toAge) {
|
|
4114
|
+
const out = list.slice();
|
|
4115
|
+
const step = direction === '逆行' ? -1 : 1;
|
|
4116
|
+
let guard = 0;
|
|
4117
|
+
while (out.length && out[out.length - 1].startAge <= toAge && guard < 24) {
|
|
4118
|
+
const last = out[out.length - 1];
|
|
4119
|
+
const lastIdx = sexagenaryIndex(STEMS.indexOf(last.ganzhi[0]), BRANCHES.indexOf(last.ganzhi[1]));
|
|
4120
|
+
const nextIdx = (lastIdx + step + 60) % 60;
|
|
4121
|
+
const g = ganzhiName(nextIdx);
|
|
4122
|
+
out.push({ step: last.step + 1, startAge: last.startAge + 10, ganzhi: g.name, stem: g.stem, branch: g.branch, stemTenGod: last.stemTenGod });
|
|
4123
|
+
guard++;
|
|
4124
|
+
}
|
|
4125
|
+
return out;
|
|
4126
|
+
}
|
|
4127
|
+
|
|
4128
|
+
function buildTrend(input) {
|
|
4129
|
+
const gender = input.gender;
|
|
4130
|
+
if (gender !== 'male' && gender !== 'female') {
|
|
4131
|
+
throw new Error('人生 K 线需要性别:大运顺逆由性别决定,不可缺省');
|
|
4132
|
+
}
|
|
4133
|
+
const toAge = clamp(Number.isInteger(input.toAge) ? input.toAge : 100, 10, 100);
|
|
4134
|
+
const r = buildBazi({
|
|
4135
|
+
year: input.year,
|
|
4136
|
+
month: input.month,
|
|
4137
|
+
day: input.day,
|
|
4138
|
+
hour: Number.isInteger(input.hour) ? input.hour : 12,
|
|
4139
|
+
minute: Number.isInteger(input.minute) ? input.minute : 0,
|
|
4140
|
+
gender,
|
|
4141
|
+
lateZi: input.lateZi !== false,
|
|
4142
|
+
trueSolar: input.trueSolar !== false,
|
|
4143
|
+
dst: input.dst !== false,
|
|
4144
|
+
address: typeof input.address === 'string' ? input.address : undefined,
|
|
4145
|
+
longitude: typeof input.longitude === 'number' ? input.longitude : undefined,
|
|
4146
|
+
geo: input.geo && typeof input.geo === 'object' ? input.geo : undefined,
|
|
4147
|
+
});
|
|
4148
|
+
|
|
4149
|
+
const dayStem = r.pillars.day.stem;
|
|
4150
|
+
const table = TABLE_OF[FAVOR[r.strength.level] || 'neutral'];
|
|
4151
|
+
const rawBound = 5 * Math.max(...Object.values(table).map(Math.abs));
|
|
4152
|
+
const scoreOf = (god) => table[god] ?? 0;
|
|
4153
|
+
const boundary = LEVEL_CUTS.some((c) => Math.abs(r.strength.score - c) <= BOUNDARY_EPS);
|
|
4154
|
+
|
|
4155
|
+
const dayun = extendDayun(r.dayun.list, r.dayun.direction, toAge);
|
|
4156
|
+
const segments = dayun
|
|
4157
|
+
.map((d, i) => ({
|
|
4158
|
+
ganZhi: d.ganzhi,
|
|
4159
|
+
startAge: d.startAge,
|
|
4160
|
+
endAge: (dayun[i + 1] ? dayun[i + 1].startAge : d.startAge + 10) - 1,
|
|
4161
|
+
}))
|
|
4162
|
+
.filter((s) => s.startAge <= toAge);
|
|
4163
|
+
|
|
4164
|
+
const birthYear = Number(String(r.solarTime.civilText).slice(0, 4));
|
|
4165
|
+
const points = [];
|
|
4166
|
+
let prev = 50;
|
|
4167
|
+
for (let age = 1; age <= toAge; age++) {
|
|
4168
|
+
const year = birthYear + age - 1;
|
|
4169
|
+
const gz = yearGanzhi(year);
|
|
4170
|
+
const seg = segments.find((s) => age >= s.startAge && age <= s.endAge) || null;
|
|
4171
|
+
const godGan = tenGod(dayStem, gz.stem);
|
|
4172
|
+
const godZhi = tenGodOfZhi(dayStem, gz.branch);
|
|
4173
|
+
const dyGodGan = seg ? tenGod(dayStem, seg.ganZhi[0]) : null;
|
|
4174
|
+
const dyGodZhi = seg ? tenGodOfZhi(dayStem, seg.ganZhi[1]) : null;
|
|
4175
|
+
|
|
4176
|
+
const raw = scoreOf(godGan) + scoreOf(godZhi)
|
|
4177
|
+
+ (seg ? (scoreOf(dyGodGan) + scoreOf(dyGodZhi)) * 1.5 : 0);
|
|
4178
|
+
const score = clamp(Math.round(((raw + rawBound) / (rawBound * 2)) * 100), 2, 99);
|
|
4179
|
+
points.push({
|
|
4180
|
+
age,
|
|
4181
|
+
year,
|
|
4182
|
+
ganZhi: gz.name,
|
|
4183
|
+
dayunGanZhi: seg ? seg.ganZhi : null,
|
|
4184
|
+
shiShenGan: godGan,
|
|
4185
|
+
shiShenZhi: godZhi,
|
|
4186
|
+
score,
|
|
4187
|
+
kline: {
|
|
4188
|
+
open: prev,
|
|
4189
|
+
close: score,
|
|
4190
|
+
high: clamp(score + 4, 0, 100),
|
|
4191
|
+
low: clamp(score - 4, 0, 100),
|
|
4192
|
+
},
|
|
4193
|
+
});
|
|
4194
|
+
prev = score;
|
|
4195
|
+
}
|
|
4196
|
+
|
|
4197
|
+
const sorted = points.slice().sort((a, b) => b.score - a.score);
|
|
4198
|
+
return {
|
|
4199
|
+
system: 'trend',
|
|
4200
|
+
meta: {
|
|
4201
|
+
birth: String(r.solarTime.civilText).slice(0, 10),
|
|
4202
|
+
gender,
|
|
4203
|
+
dayMaster: `${dayStem}(${r.dayMaster.yinYang}${r.dayMaster.element})`,
|
|
4204
|
+
strength: { level: r.strength.level, score: r.strength.score, boundary },
|
|
4205
|
+
toAge,
|
|
4206
|
+
...(boundary
|
|
4207
|
+
? {
|
|
4208
|
+
boundaryNote:
|
|
4209
|
+
`旺衰分 ${r.strength.score} 正落在换档阈值附近(阈值 ${LEVEL_CUTS.join(' / ')},容差 ${BOUNDARY_EPS}):`
|
|
4210
|
+
+ '多一个同类干支或少一个就会换计分表,整套正负号随之翻转——因此这里不出「最好 / 最差年份」的排序结论。',
|
|
4211
|
+
}
|
|
4212
|
+
: {}),
|
|
4213
|
+
},
|
|
4214
|
+
segments,
|
|
4215
|
+
points,
|
|
4216
|
+
highlights: boundary
|
|
4217
|
+
? { suppressed: '旺衰落在换档边界,年份排序不可靠,不给顺位 / 谨慎年份', best: [], worst: [] }
|
|
4218
|
+
: {
|
|
4219
|
+
best: sorted.slice(0, 3).map((p) => `${p.age}岁(${p.year},${p.ganZhi},${p.score}分)`),
|
|
4220
|
+
worst: sorted.slice(-3).reverse().map((p) => `${p.age}岁(${p.year},${p.ganZhi},${p.score}分)`),
|
|
4221
|
+
},
|
|
4222
|
+
sparkline: asciiSparkline(points),
|
|
4223
|
+
sources: [
|
|
4224
|
+
'运势分:流年 / 大运干支十神对日主的简化启发式计分(旺衰方向决定正负号)',
|
|
4225
|
+
'K 线形态:收盘 = 当年分,开盘 = 上年分,高低各 ±4(确定性生成)',
|
|
4226
|
+
],
|
|
4227
|
+
disclaimer: boundary
|
|
4228
|
+
? '人生 K 线为简化启发式的可视化;本盘旺衰判定处于换档边界,趋势与年份排序均不可靠,仅供展示,不构成任何现实决策依据。'
|
|
4229
|
+
: '人生 K 线为简化启发式的可视化,趋势形态仅供娱乐参考,不构成任何现实决策依据;十年大运内每年分值不等于真实吉凶。',
|
|
4230
|
+
};
|
|
4231
|
+
}
|
|
4232
|
+
|
|
4233
|
+
function asciiSparkline(points) {
|
|
4234
|
+
const BLOCKS = ['▁', '▂', '▃', '▄', '▅', '▆', '▇', '█'];
|
|
4235
|
+
const lines = [];
|
|
4236
|
+
const groups = [];
|
|
4237
|
+
for (const p of points) {
|
|
4238
|
+
const key = p.dayunGanZhi || '起运前';
|
|
4239
|
+
const last = groups[groups.length - 1];
|
|
4240
|
+
if (!last || last.key !== key) groups.push({ key, points: [] });
|
|
4241
|
+
groups[groups.length - 1].points.push(p);
|
|
4242
|
+
}
|
|
4243
|
+
for (const g of groups) {
|
|
4244
|
+
const bars = g.points
|
|
4245
|
+
.map((p) => BLOCKS[clamp(Math.floor((p.score / 100) * BLOCKS.length), 0, BLOCKS.length - 1)])
|
|
4246
|
+
.join('');
|
|
4247
|
+
lines.push(`${g.key}(${g.points[0].age}–${g.points[g.points.length - 1].age}岁):${bars}`);
|
|
4248
|
+
}
|
|
4249
|
+
return lines;
|
|
4250
|
+
}
|
|
4251
|
+
|
|
4252
|
+
return { buildTrend };
|
|
4253
|
+
})();
|
|
4254
|
+
|
|
4255
|
+
__mods.fengshui = (function () {
|
|
4256
|
+
const { partsToJd, termInstantJd } = __mods.calendar;
|
|
4257
|
+
const { applyDst } = __mods.dst;
|
|
4258
|
+
|
|
4259
|
+
const NINE_STARS = {
|
|
4260
|
+
1: { name: '一白', color: '白', element: '水', star: '贪狼', lucky: true },
|
|
4261
|
+
2: { name: '二黑', color: '黑', element: '土', star: '巨门', lucky: false },
|
|
4262
|
+
3: { name: '三碧', color: '碧', element: '木', star: '禄存', lucky: false },
|
|
4263
|
+
4: { name: '四绿', color: '绿', element: '木', star: '文曲', lucky: true },
|
|
4264
|
+
5: { name: '五黄', color: '黄', element: '土', star: '廉贞', lucky: false },
|
|
4265
|
+
6: { name: '六白', color: '白', element: '金', star: '武曲', lucky: true },
|
|
4266
|
+
7: { name: '七赤', color: '赤', element: '金', star: '破军', lucky: false },
|
|
4267
|
+
8: { name: '八白', color: '白', element: '土', star: '左辅', lucky: true },
|
|
4268
|
+
9: { name: '九紫', color: '紫', element: '火', star: '右弼', lucky: true },
|
|
4269
|
+
};
|
|
4270
|
+
|
|
4271
|
+
const PALACE_DIRECTION = {
|
|
4272
|
+
坎: '正北', 艮: '东北', 震: '正东', 巽: '东南',
|
|
4273
|
+
离: '正南', 坤: '西南', 兑: '正西', 乾: '西北',
|
|
4274
|
+
};
|
|
4275
|
+
|
|
4276
|
+
const YOUNIAN_ORDER = ['坎', '艮', '震', '巽', '离', '坤', '兑', '乾'];
|
|
4277
|
+
const DAYOUTIAN = {
|
|
4278
|
+
乾: ['六煞', '天医', '五鬼', '祸害', '绝命', '延年', '生气'],
|
|
4279
|
+
坎: ['五鬼', '天医', '生气', '延年', '绝命', '祸害', '六煞'],
|
|
4280
|
+
艮: ['六煞', '绝命', '祸害', '生气', '延年', '天医', '五鬼'],
|
|
4281
|
+
震: ['延年', '生气', '祸害', '绝命', '五鬼', '天医', '六煞'],
|
|
4282
|
+
巽: ['天医', '五鬼', '六煞', '祸害', '生气', '绝命', '延年'],
|
|
4283
|
+
离: ['六煞', '五鬼', '绝命', '延年', '祸害', '生气', '天医'],
|
|
4284
|
+
坤: ['天医', '延年', '绝命', '生气', '祸害', '五鬼', '六煞'],
|
|
4285
|
+
兑: ['生气', '祸害', '延年', '绝命', '六煞', '五鬼', '天医'],
|
|
4286
|
+
};
|
|
4287
|
+
|
|
4288
|
+
const LUCKY_STARS = ['生气', '延年', '天医', '伏位'];
|
|
4289
|
+
const UNLUCKY_STARS = ['绝命', '五鬼', '六煞', '祸害'];
|
|
4290
|
+
|
|
4291
|
+
const MOUNTAINS_24 = [
|
|
4292
|
+
{ m: '壬', gua: '坎', deg: 345 }, { m: '子', gua: '坎', deg: 0 }, { m: '癸', gua: '坎', deg: 15 },
|
|
4293
|
+
{ m: '丑', gua: '艮', deg: 30 }, { m: '艮', gua: '艮', deg: 45 }, { m: '寅', gua: '艮', deg: 60 },
|
|
4294
|
+
{ m: '甲', gua: '震', deg: 75 }, { m: '卯', gua: '震', deg: 90 }, { m: '乙', gua: '震', deg: 105 },
|
|
4295
|
+
{ m: '辰', gua: '巽', deg: 120 }, { m: '巽', gua: '巽', deg: 135 }, { m: '巳', gua: '巽', deg: 150 },
|
|
4296
|
+
{ m: '丙', gua: '离', deg: 165 }, { m: '午', gua: '离', deg: 180 }, { m: '丁', gua: '离', deg: 195 },
|
|
4297
|
+
{ m: '未', gua: '坤', deg: 210 }, { m: '坤', gua: '坤', deg: 225 }, { m: '申', gua: '坤', deg: 240 },
|
|
4298
|
+
{ m: '庚', gua: '兑', deg: 255 }, { m: '酉', gua: '兑', deg: 270 }, { m: '辛', gua: '兑', deg: 285 },
|
|
4299
|
+
{ m: '戌', gua: '乾', deg: 300 }, { m: '乾', gua: '乾', deg: 315 }, { m: '亥', gua: '乾', deg: 330 },
|
|
4300
|
+
];
|
|
4301
|
+
|
|
4302
|
+
const MOUNTAIN_INDEX = new Map(MOUNTAINS_24.map((x, i) => [x.m, i]));
|
|
4303
|
+
|
|
4304
|
+
function mountainInfo(name) {
|
|
4305
|
+
const i = MOUNTAIN_INDEX.get(name);
|
|
4306
|
+
if (i === undefined) {
|
|
4307
|
+
throw new Error(`坐向必须是 24 山之一(${MOUNTAINS_24.map((x) => x.m).join('')}),收到「${name}」——请按罗盘 24 山报坐或报向,不要自造方位词。`);
|
|
4308
|
+
}
|
|
4309
|
+
return { ...MOUNTAINS_24[i], index: i };
|
|
4310
|
+
}
|
|
4311
|
+
|
|
4312
|
+
function sittingFromFacing(facing) {
|
|
4313
|
+
const f = mountainInfo(facing);
|
|
4314
|
+
const i = (f.index + 12) % 24;
|
|
4315
|
+
return { ...MOUNTAINS_24[i], index: i };
|
|
4316
|
+
}
|
|
4317
|
+
|
|
4318
|
+
const guaGroup = (gua) => (['坎', '离', '震', '巽'].includes(gua) ? '东四' : '西四');
|
|
4319
|
+
|
|
4320
|
+
const STAR_LAYOUT_ROLE = {
|
|
4321
|
+
生气: '大吉:最宜开门、主卧、灶口朝向',
|
|
4322
|
+
延年: '大吉:宜开门、主卧(利感情与人缘)',
|
|
4323
|
+
天医: '次吉:宜主卧、书房、老人房(利健康)',
|
|
4324
|
+
伏位: '次吉:宜卧室、神位(主平稳)',
|
|
4325
|
+
祸害: '小凶:宜厕所、储物间(宜静不宜动)',
|
|
4326
|
+
六煞: '次凶:宜厕所、储物间',
|
|
4327
|
+
五鬼: '大凶:宜厕所,或以灶坐压之(传统「火烧五鬼」说)',
|
|
4328
|
+
绝命: '最凶:宜灶坐压之、储物,忌门床',
|
|
4329
|
+
};
|
|
4330
|
+
|
|
4331
|
+
const GUA_BY_NUM = ['坎', '坤', '震', '巽', '中', '乾', '兑', '艮', '离'];
|
|
4332
|
+
|
|
4333
|
+
function mingGua(birthYear, gender) {
|
|
4334
|
+
const tail = ((birthYear % 100) + 100) % 100;
|
|
4335
|
+
const century = birthYear >= 2000 ? { male: 99, female: 3 } : { male: 100, female: 4 };
|
|
4336
|
+
const mod9 = (n) => { const r = ((n % 9) + 9) % 9; return r === 0 ? 9 : r; };
|
|
4337
|
+
let num = gender === 'male' ? mod9(century.male - tail) : mod9(tail - century.female);
|
|
4338
|
+
if (num === 5) num = gender === 'male' ? 2 : 8;
|
|
4339
|
+
const gua = GUA_BY_NUM[num - 1];
|
|
4340
|
+
if (gua === '中') throw new Error('命卦计算异常:落中宫');
|
|
4341
|
+
return { num, gua, direction: PALACE_DIRECTION[gua] };
|
|
4342
|
+
}
|
|
4343
|
+
|
|
4344
|
+
function eightStars(gua) {
|
|
4345
|
+
const result = { 伏位: { palace: gua, direction: PALACE_DIRECTION[gua] } };
|
|
4346
|
+
const start = YOUNIAN_ORDER.indexOf(gua);
|
|
4347
|
+
for (let step = 1; step <= 7; step++) {
|
|
4348
|
+
const palace = YOUNIAN_ORDER[(start + step) % 8];
|
|
4349
|
+
result[DAYOUTIAN[gua][step - 1]] = { palace, direction: PALACE_DIRECTION[palace] };
|
|
4350
|
+
}
|
|
4351
|
+
return result;
|
|
4352
|
+
}
|
|
4353
|
+
|
|
4354
|
+
function guaYearOf(year, month, day) {
|
|
4355
|
+
const birthJd = partsToJd({ year, month, day, hour: 12, minute: 0 });
|
|
4356
|
+
return birthJd < termInstantJd(year, 2) ? year - 1 : year;
|
|
4357
|
+
}
|
|
4358
|
+
|
|
4359
|
+
function yearZibaiStar(year) {
|
|
4360
|
+
const n = (1 + (2026 - year)) % 9;
|
|
4361
|
+
return n <= 0 ? n + 9 : n;
|
|
4362
|
+
}
|
|
4363
|
+
|
|
4364
|
+
const PALACE_ORDER = ['坎', '坤', '震', '巽', '中', '乾', '兑', '艮', '离']; // 洛书数 1–9 对应宫
|
|
4365
|
+
|
|
4366
|
+
function yearlyFlying(year) {
|
|
4367
|
+
const center = yearZibaiStar(year);
|
|
4368
|
+
const palaces = [];
|
|
4369
|
+
for (const palace of PALACE_ORDER) {
|
|
4370
|
+
const k = PALACE_ORDER.indexOf(palace) + 1;
|
|
4371
|
+
const num = ((((k - 5 + center - 1) % 9) + 9) % 9) + 1;
|
|
4372
|
+
const s = NINE_STARS[num];
|
|
4373
|
+
palaces.push({ palace, direction: palace === '中' ? '中宫' : PALACE_DIRECTION[palace], num, name: s.name, star: s.star, element: s.element, lucky: s.lucky });
|
|
4374
|
+
}
|
|
4375
|
+
return { center, centerName: NINE_STARS[center].name, palaces };
|
|
4376
|
+
}
|
|
4377
|
+
|
|
4378
|
+
const pad2 = (n) => String(n).padStart(2, '0');
|
|
4379
|
+
|
|
4380
|
+
function civilDate(input) {
|
|
4381
|
+
const dst = input.dst === false
|
|
4382
|
+
? { year: input.year, month: input.month, day: input.day, hour: 0, minute: 0, applied: false, note: null }
|
|
4383
|
+
: applyDst({ year: input.year, month: input.month, day: input.day, hour: 0, minute: 0 });
|
|
4384
|
+
return { year: dst.year, month: dst.month, day: dst.day, dst };
|
|
4385
|
+
}
|
|
4386
|
+
|
|
4387
|
+
const SOURCES = [
|
|
4388
|
+
'大游年歌(八宅通行口径)',
|
|
4389
|
+
'年家紫白飞星(纯公式:以 2026 丙午一白入中、逐年退一位、九年一轮;按立春换年)',
|
|
4390
|
+
'24 山归属与度数(八宅通行口径,每山 15°)',
|
|
4391
|
+
];
|
|
4392
|
+
|
|
4393
|
+
const DISCLAIMER = '八宅与飞星为传统通行口径的本地计算,属民俗参考,不构成现场勘舆结论,仅供娱乐;实际户型以现场为准。';
|
|
4394
|
+
|
|
4395
|
+
function buildFengshui(input) {
|
|
4396
|
+
if (input.action === 'advise') return buildAdvise(input);
|
|
4397
|
+
const gender = input.gender;
|
|
4398
|
+
if (gender !== 'male' && gender !== 'female') {
|
|
4399
|
+
throw new Error('风水命卦需要性别:男女命卦算法不同,不可缺省');
|
|
4400
|
+
}
|
|
4401
|
+
if (!Number.isInteger(input.year) || !Number.isInteger(input.month) || !Number.isInteger(input.day)) {
|
|
4402
|
+
throw new Error('风水命卦需要完整出生年月日(公历)——只知道年份不够:命卦按立春界划分出生年');
|
|
4403
|
+
}
|
|
4404
|
+
const civil = civilDate(input);
|
|
4405
|
+
const guaYear = guaYearOf(civil.year, civil.month, civil.day);
|
|
4406
|
+
const { num, gua, direction } = mingGua(guaYear, gender);
|
|
4407
|
+
const stars = eightStars(gua);
|
|
4408
|
+
const eastFour = ['坎', '离', '震', '巽'].includes(gua);
|
|
4409
|
+
const fortuneYear = Number.isInteger(input.fortuneYear) ? input.fortuneYear : null;
|
|
4410
|
+
|
|
4411
|
+
return {
|
|
4412
|
+
system: 'fengshui',
|
|
4413
|
+
meta: {
|
|
4414
|
+
birth: `${civil.year}-${pad2(civil.month)}-${pad2(civil.day)}`,
|
|
4415
|
+
guaYear,
|
|
4416
|
+
gender: gender === 'male' ? '男' : '女',
|
|
4417
|
+
fortuneYear,
|
|
4418
|
+
...(civil.dst.applied ? { dst: civil.dst.note } : {}),
|
|
4419
|
+
...(fortuneYear === null ? { note: '未给流年:未算年紫白飞星(需要时补 fortuneYear)。' } : {}),
|
|
4420
|
+
scope: '八宅命卦 + 大游年八星 + 年紫白;不含玄空飞星各运盘、不含形峦与现场勘舆。',
|
|
4421
|
+
},
|
|
4422
|
+
mingGua: {
|
|
4423
|
+
num,
|
|
4424
|
+
gua,
|
|
4425
|
+
direction,
|
|
4426
|
+
group: eastFour ? '东四命' : '西四命',
|
|
4427
|
+
compatible: eastFour ? '宜东四宅(坐坎离震巽)' : '宜西四宅(坐乾坤艮兑)',
|
|
4428
|
+
},
|
|
4429
|
+
luckyDirections: LUCKY_STARS.map((s) => ({ star: s, ...stars[s] })),
|
|
4430
|
+
unluckyDirections: UNLUCKY_STARS.map((s) => ({ star: s, ...stars[s] })),
|
|
4431
|
+
...(fortuneYear === null ? {} : { yearlyFlying: yearlyFlying(fortuneYear) }),
|
|
4432
|
+
sources: SOURCES,
|
|
4433
|
+
disclaimer: DISCLAIMER,
|
|
4434
|
+
};
|
|
4435
|
+
}
|
|
4436
|
+
|
|
4437
|
+
function buildAdvise(input) {
|
|
4438
|
+
const hasSitting = typeof input.sitting === 'string' && input.sitting.trim() !== '';
|
|
4439
|
+
const hasFacing = typeof input.facing === 'string' && input.facing.trim() !== '';
|
|
4440
|
+
if (!hasSitting && !hasFacing) {
|
|
4441
|
+
throw new Error('缺少坐向:sitting(坐山)或 facing(向山)至少给一个——按罗盘 24 山报坐或报向,例如「坐北朝南」对应坐子向午');
|
|
4442
|
+
}
|
|
4443
|
+
if (hasSitting && hasFacing) {
|
|
4444
|
+
const s = mountainInfo(input.sitting.trim());
|
|
4445
|
+
const f = mountainInfo(input.facing.trim());
|
|
4446
|
+
if ((s.index + 12) % 24 !== f.index) {
|
|
4447
|
+
throw new Error(`坐向不自洽:坐${s.m}(${s.deg}°)与向${f.m}(${f.deg}°)不是对宫(相差应 180°,实测 ${((f.deg - s.deg) + 360) % 360}°)——请核对罗盘读数。`);
|
|
4448
|
+
}
|
|
4449
|
+
}
|
|
4450
|
+
const sitting = hasSitting ? mountainInfo(input.sitting.trim()) : sittingFromFacing(input.facing.trim());
|
|
4451
|
+
const facing = MOUNTAINS_24[(sitting.index + 12) % 24];
|
|
4452
|
+
const houseGua = sitting.gua;
|
|
4453
|
+
const stars = eightStars(houseGua);
|
|
4454
|
+
const lucky = LUCKY_STARS.map((s) => stars[s]);
|
|
4455
|
+
const unlucky = UNLUCKY_STARS.map((s) => stars[s]);
|
|
4456
|
+
const dir = (x) => `${x.direction}(${x.palace}宫)`;
|
|
4457
|
+
|
|
4458
|
+
const starChart = [...LUCKY_STARS, ...UNLUCKY_STARS].map((s) => ({
|
|
4459
|
+
star: s,
|
|
4460
|
+
lucky: LUCKY_STARS.includes(s),
|
|
4461
|
+
...stars[s],
|
|
4462
|
+
role: STAR_LAYOUT_ROLE[s],
|
|
4463
|
+
}));
|
|
4464
|
+
|
|
4465
|
+
const layout = {
|
|
4466
|
+
door: `门宜开于吉方:首选生气方 ${dir(lucky[0])},次选延年方 ${dir(lucky[1])}、天医方 ${dir(lucky[2])}。`,
|
|
4467
|
+
stove: `灶宜「坐凶向吉」:灶身压凶方(首选绝命方 ${dir(unlucky[0])}、五鬼方 ${dir(unlucky[1])}),灶口朝吉方。`,
|
|
4468
|
+
bed: `主卧与床头宜安吉方:生气方 ${dir(lucky[0])} 或延年方 ${dir(lucky[1])};床头宜向吉方。`,
|
|
4469
|
+
toilet: `厕所宜压凶方:祸害方 ${dir(unlucky[3])} 或六煞方 ${dir(unlucky[2])}(污去凶气,宜静常闭)。`,
|
|
4470
|
+
};
|
|
4471
|
+
|
|
4472
|
+
let mingMatch = null;
|
|
4473
|
+
const hasBirth = Number.isInteger(input.year) && Number.isInteger(input.month) && Number.isInteger(input.day)
|
|
4474
|
+
&& (input.gender === 'male' || input.gender === 'female');
|
|
4475
|
+
if (hasBirth) {
|
|
4476
|
+
const civil = civilDate(input);
|
|
4477
|
+
const guaYear = guaYearOf(civil.year, civil.month, civil.day);
|
|
4478
|
+
const ming = mingGua(guaYear, input.gender);
|
|
4479
|
+
const mGroup = guaGroup(ming.gua);
|
|
4480
|
+
const hGroup = guaGroup(houseGua);
|
|
4481
|
+
const same = mGroup === hGroup;
|
|
4482
|
+
const mingLucky = LUCKY_STARS.map((s) => `${s}位${eightStars(ming.gua)[s].direction}`).join('、');
|
|
4483
|
+
mingMatch = {
|
|
4484
|
+
birth: `${civil.year}-${pad2(civil.month)}-${pad2(civil.day)}`,
|
|
4485
|
+
guaYear,
|
|
4486
|
+
gender: input.gender === 'male' ? '男' : '女',
|
|
4487
|
+
mingGua: { gua: ming.gua, direction: ming.direction, group: `${mGroup}命` },
|
|
4488
|
+
houseGua: { gua: houseGua, group: `${hGroup}宅` },
|
|
4489
|
+
sameGroup: same,
|
|
4490
|
+
text: same
|
|
4491
|
+
? `命宅相配(${mGroup}命住${hGroup}宅):以宅之八星盘布局即可,个人吉方与宅吉方大体同向。`
|
|
4492
|
+
: `命宅不配(${mGroup}命住${hGroup}宅):传统调停法——门与床以命卦吉方为先(此命四吉方:${mingLucky}),灶仍按宅卦坐凶向吉,两盘各自取用。`,
|
|
4493
|
+
};
|
|
4494
|
+
}
|
|
4495
|
+
|
|
4496
|
+
return {
|
|
4497
|
+
system: 'fengshui',
|
|
4498
|
+
meta: {
|
|
4499
|
+
action: 'advise',
|
|
4500
|
+
...(hasBirth ? {} : { birthNote: '未提供生辰:未做命宅配;如需按个人命卦调停门床,请补充出生年月日与性别。' }),
|
|
4501
|
+
sitting: `${sitting.m}山(罗盘 ${sitting.deg}°)`,
|
|
4502
|
+
facing: `${facing.m}山(罗盘 ${facing.deg}°)`,
|
|
4503
|
+
houseGua,
|
|
4504
|
+
houseGroup: `${guaGroup(houseGua)}宅`,
|
|
4505
|
+
scope: '门主灶厕走向按八宅通行口径给倾向,不含形峦、不含玄空飞星各运盘、不是现场勘舆结论。',
|
|
4506
|
+
},
|
|
4507
|
+
houseStars: starChart,
|
|
4508
|
+
layout,
|
|
4509
|
+
...(mingMatch ? { mingMatch } : {}),
|
|
4510
|
+
sources: SOURCES,
|
|
4511
|
+
disclaimer: DISCLAIMER,
|
|
4512
|
+
};
|
|
4513
|
+
}
|
|
4514
|
+
|
|
4515
|
+
return { NINE_STARS, PALACE_DIRECTION, DAYOUTIAN, LUCKY_STARS, UNLUCKY_STARS, MOUNTAINS_24, mountainInfo, sittingFromFacing, STAR_LAYOUT_ROLE, mingGua, eightStars, guaYearOf, yearZibaiStar, yearlyFlying, buildFengshui };
|
|
4516
|
+
})();
|
|
4517
|
+
|
|
4518
|
+
__mods.name = (function () {
|
|
4519
|
+
const { buildBazi } = __mods.bazi;
|
|
4520
|
+
const { assertCivil } = __mods.validate;
|
|
4521
|
+
|
|
4522
|
+
const SHENG = { 木: '火', 火: '土', 土: '金', 金: '水', 水: '木' };
|
|
4523
|
+
const KE = { 木: '土', 土: '水', 水: '火', 火: '金', 金: '木' };
|
|
4524
|
+
const SHENG_BY = { 火: '木', 土: '火', 金: '土', 水: '金', 木: '水' }; // 生我者
|
|
4525
|
+
const KE_BY = { 土: '木', 水: '土', 火: '水', 金: '火', 木: '金' }; // 克我者
|
|
4526
|
+
const ALL = ['木', '火', '土', '金', '水'];
|
|
4527
|
+
|
|
4528
|
+
const CHAR_LIB = [
|
|
4529
|
+
['梓', '木', '梓树,喻成长、手艺与念乡', 'n'], ['桐', '木', '梧桐,高洁、引来凤凰', 'n'],
|
|
4530
|
+
['楠', '木', '楠木,坚韧贵重', 'n'], ['楷', '木', '楷模,端正可法', 'm'],
|
|
4531
|
+
['松', '木', '松柏常青,坚韧长寿', 'm'], ['柏', '木', '柏树,刚直耐寒', 'm'],
|
|
4532
|
+
['枫', '木', '枫叶,热烈与秋意', 'n'], ['榕', '木', '榕树,荫庇包容', 'n'],
|
|
4533
|
+
['槿', '木', '木槿,朝开暮落、坚韧', 'f'], ['棠', '木', '海棠,明媚雅致', 'f'],
|
|
4534
|
+
['楚', '木', '清楚、楚楚有致', 'n'], ['柯', '木', '草木枝干,条理分明', 'm'],
|
|
4535
|
+
['桓', '木', '桓桓武貌,栋梁', 'm'], ['榆', '木', '榆树,安稳敦厚', 'n'],
|
|
4536
|
+
['栩', '木', '栩栩生动', 'n'], ['榛', '木', '榛果,丰实', 'n'],
|
|
4537
|
+
['芸', '木', '芸香,清雅好学', 'f'], ['芷', '木', '白芷,芳洁', 'f'],
|
|
4538
|
+
['芮', '木', '草初生,柔而韧', 'f'], ['若', '木', '若木,从容', 'f'],
|
|
4539
|
+
['茜', '木', '茜草,明丽', 'f'], ['茵', '木', '绿茵,葱茏', 'f'],
|
|
4540
|
+
['茗', '木', '茶芽,清雅', 'f'], ['莎', '木', '莎草,柔韧', 'f'],
|
|
4541
|
+
['茉', '木', '茉莉,素净芬芳', 'f'], ['筱', '木', '小竹,清秀有节', 'f'],
|
|
4542
|
+
['蓉', '木', '芙蓉,明艳', 'f'], ['萱', '木', '萱草,忘忧', 'f'],
|
|
4543
|
+
['蕙', '木', '蕙兰,芳心', 'f'], ['蕊', '木', '花蕊,含秀', 'f'],
|
|
4544
|
+
['菲', '木', '芳菲,繁茂', 'f'], ['蔓', '木', '藤蔓,绵延', 'f'],
|
|
4545
|
+
['荣', '木', '繁荣,荣光', 'm'], ['茂', '木', '茂盛,丰隆', 'm'],
|
|
4546
|
+
['栋', '木', '栋梁', 'm'], ['梁', '木', '桥梁、栋梁', 'm'],
|
|
4547
|
+
['朴', '木', '质朴', 'm'], ['本', '木', '根本', 'm'],
|
|
4548
|
+
['炎', '火', '火盛,热烈', 'm'], ['灿', '火', '灿烂', 'n'],
|
|
4549
|
+
['炫', '火', '光彩炫目', 'm'], ['炜', '火', '光明', 'm'],
|
|
4550
|
+
['烨', '火', '火光盛明', 'm'], ['煜', '火', '照耀', 'm'],
|
|
4551
|
+
['熙', '火', '光明和乐', 'n'], ['煊', '火', '温暖显赫', 'm'],
|
|
4552
|
+
['焕', '火', '焕然一新', 'm'], ['熠', '火', '熠熠生辉', 'n'],
|
|
4553
|
+
['炅', '火', '日光', 'n'], ['昱', '火', '日光,明亮', 'm'],
|
|
4554
|
+
['晗', '火', '天将明', 'f'], ['旭', '火', '旭日初升', 'm'],
|
|
4555
|
+
['昊', '火', '广大如天', 'm'], ['晟', '火', '光明兴盛', 'm'],
|
|
4556
|
+
['晨', '火', '清晨', 'n'], ['景', '火', '光景、景仰', 'n'],
|
|
4557
|
+
['曜', '火', '日光照耀', 'm'], ['曦', '火', '晨曦', 'n'],
|
|
4558
|
+
['朗', '火', '明朗', 'm'], ['昭', '火', '昭明', 'n'],
|
|
4559
|
+
['昕', '火', '黎明', 'f'], ['昀', '火', '日光', 'f'],
|
|
4560
|
+
['暖', '火', '温暖', 'f'], ['灵', '火', '灵秀', 'f'],
|
|
4561
|
+
['丹', '火', '赤诚', 'f'], ['彤', '火', '朱红', 'f'],
|
|
4562
|
+
['冉', '火', '冉冉上升', 'f'], ['南', '火', '南方,向阳', 'n'],
|
|
4563
|
+
['坤', '土', '大地,厚德', 'm'], ['垚', '土', '山高', 'm'],
|
|
4564
|
+
['培', '土', '培育', 'm'], ['城', '土', '城池,坚固', 'm'],
|
|
4565
|
+
['域', '土', '疆域', 'm'], ['坚', '土', '坚定', 'm'],
|
|
4566
|
+
['墨', '土', '笔墨,文气', 'n'], ['均', '土', '均衡', 'm'],
|
|
4567
|
+
['坦', '土', '坦荡', 'm'], ['基', '土', '根基', 'm'],
|
|
4568
|
+
['堂', '土', '堂堂正正', 'm'], ['圣', '土', '圣明', 'm'],
|
|
4569
|
+
['在', '土', '存在、自在', 'n'], ['岳', '土', '山岳', 'm'],
|
|
4570
|
+
['峰', '土', '山峰', 'm'], ['峻', '土', '高峻', 'm'],
|
|
4571
|
+
['岚', '土', '山间雾气', 'f'], ['岩', '土', '岩石,刚毅', 'm'],
|
|
4572
|
+
['崇', '土', '崇高', 'm'], ['岐', '土', '岐山,分岔有路', 'm'],
|
|
4573
|
+
['屿', '土', '小岛', 'n'], ['磊', '土', '磊落', 'm'],
|
|
4574
|
+
['尧', '土', '古圣王,高远', 'm'], ['圆', '土', '圆满', 'f'],
|
|
4575
|
+
['恩', '土', '恩厚', 'n'], ['惟', '土', '思惟', 'm'],
|
|
4576
|
+
['锋', '金', '锋芒', 'm'], ['钢', '金', '刚强', 'm'],
|
|
4577
|
+
['钧', '金', '权衡、钧衡', 'm'], ['铭', '金', '铭记', 'm'],
|
|
4578
|
+
['锐', '金', '锐进', 'm'], ['锡', '金', '赐予', 'm'],
|
|
4579
|
+
['锦', '金', '锦绣', 'n'], ['鑫', '金', '兴盛(三金)', 'm'],
|
|
4580
|
+
['键', '金', '关键、强健', 'm'], ['鉴', '金', '明鉴', 'm'],
|
|
4581
|
+
['钦', '金', '钦敬', 'm'], ['镇', '金', '镇守', 'm'],
|
|
4582
|
+
['铮', '金', '铮铮有声', 'm'], ['钰', '金', '珍宝', 'n'],
|
|
4583
|
+
['铠', '金', '铠甲', 'm'], ['珊', '金', '珊瑚', 'f'],
|
|
4584
|
+
['珠', '金', '明珠', 'f'], ['琳', '金', '美玉', 'f'],
|
|
4585
|
+
['琦', '金', '美玉、奇伟', 'n'], ['琪', '金', '美玉', 'f'],
|
|
4586
|
+
['瑜', '金', '美玉、瑜亮', 'n'], ['瑞', '金', '祥瑞', 'n'],
|
|
4587
|
+
['瑄', '金', '璧玉', 'f'], ['璇', '金', '美玉', 'f'],
|
|
4588
|
+
['璟', '金', '玉光', 'n'], ['璐', '金', '美玉', 'f'],
|
|
4589
|
+
['瑾', '金', '美玉,谨守', 'f'], ['珏', '金', '双玉', 'n'],
|
|
4590
|
+
['环', '金', '圆环,周全', 'f'], ['玲', '金', '玲珑', 'f'],
|
|
4591
|
+
['珑', '金', '玲珑', 'f'], ['瑶', '金', '瑶华', 'f'],
|
|
4592
|
+
['莹', '金', '晶莹', 'f'], ['铎', '金', '铃铎,传声', 'm'],
|
|
4593
|
+
['江', '水', '江河,开阔', 'm'], ['河', '水', '河流', 'm'],
|
|
4594
|
+
['海', '水', '海纳百川', 'm'], ['洋', '水', '海洋,宽广', 'n'],
|
|
4595
|
+
['波', '水', '波澜', 'm'], ['涛', '水', '波涛', 'm'],
|
|
4596
|
+
['涵', '水', '包涵、涵养', 'f'], ['清', '水', '清澈', 'n'],
|
|
4597
|
+
['澈', '水', '澄澈', 'm'], ['澄', '水', '澄明', 'n'],
|
|
4598
|
+
['润', '水', '温润', 'm'], ['泽', '水', '恩泽', 'm'],
|
|
4599
|
+
['沛', '水', '充沛', 'm'], ['沁', '水', '沁润', 'f'],
|
|
4600
|
+
['泓', '水', '水深而广', 'm'], ['泉', '水', '源泉', 'n'],
|
|
4601
|
+
['汐', '水', '潮汐', 'f'], ['淇', '水', '淇水', 'f'],
|
|
4602
|
+
['鸿', '水', '鸿鹄、宏大', 'm'], ['滋', '水', '滋润', 'n'],
|
|
4603
|
+
['淼', '水', '水势浩渺', 'n'], ['雨', '水', '甘霖', 'n'],
|
|
4604
|
+
['雪', '水', '洁白', 'f'], ['霖', '水', '甘霖', 'm'],
|
|
4605
|
+
['霏', '水', '雨雪纷飞', 'f'], ['露', '水', '露华', 'f'],
|
|
4606
|
+
['霆', '水', '雷霆', 'm'], ['雯', '水', '云纹', 'f'],
|
|
4607
|
+
['霓', '水', '虹霓', 'f'], ['湉', '水', '水流平缓', 'f'],
|
|
4608
|
+
['沐', '水', '沐浴,受润泽', 'n'], ['宸', '木', '屋宇、北极星所居,喻尊贵', 'm'],
|
|
4609
|
+
['轩', '土', '高轩,气宇轩昂', 'm'], ['逸', '土', '安逸、超逸', 'm'],
|
|
4610
|
+
['然', '火', '自然、坦然', 'n'], ['承', '金', '承继担当', 'm'],
|
|
4611
|
+
['语', '木', '言语,温言', 'f'], ['诺', '火', '诺言,信守', 'n'],
|
|
4612
|
+
['诗', '金', '诗书,文气', 'f'], ['梦', '木', '梦想', 'f'],
|
|
4613
|
+
['思', '金', '思虑,才思', 'n'], ['悦', '金', '喜悦', 'f'],
|
|
4614
|
+
['欣', '木', '欢欣', 'f'], ['嘉', '木', '嘉美', 'n'],
|
|
4615
|
+
['佳', '木', '佳好', 'f'], ['博', '水', '博学', 'm'],
|
|
4616
|
+
['文', '水', '文采', 'n'], ['睿', '金', '睿智', 'm'],
|
|
4617
|
+
['宇', '土', '器宇、宇宙', 'm'], ['亦', '土', '也,从容', 'n'],
|
|
4618
|
+
['知', '火', '知晓,明理', 'n'], ['星', '火', '星辰', 'n'],
|
|
4619
|
+
['月', '木', '明月', 'f'], ['言', '木', '言语,信', 'n'],
|
|
4620
|
+
['和', '水', '和顺', 'n'], ['安', '土', '安宁', 'n'],
|
|
4621
|
+
['宁', '火', '安宁', 'f'],
|
|
4622
|
+
];
|
|
4623
|
+
|
|
4624
|
+
const EL_INDEX = new Map();
|
|
4625
|
+
for (const c of CHAR_LIB) {
|
|
4626
|
+
if (!EL_INDEX.has(c[0])) EL_INDEX.set(c[0], { c: c[0], el: c[1], meaning: c[2], g: c[3], order: EL_INDEX.size });
|
|
4627
|
+
}
|
|
4628
|
+
|
|
4629
|
+
const KIND_LABEL = { m: '男', f: '女', n: '中' };
|
|
4630
|
+
|
|
4631
|
+
function favorableElements(strength) {
|
|
4632
|
+
const level = strength && strength.level ? strength.level : null;
|
|
4633
|
+
const dayEl = strength && strength.dayElement ? strength.dayElement : null;
|
|
4634
|
+
if (!dayEl || !level) return { list: ALL.slice(), basis: '未取得旺衰:不筛五行,只做用字与字义建议' };
|
|
4635
|
+
if (level === '身强' || level === '偏强') {
|
|
4636
|
+
return {
|
|
4637
|
+
list: [KE_BY[dayEl], SHENG[dayEl], KE[dayEl]].filter(Boolean),
|
|
4638
|
+
basis: `${level}(${dayEl}):宜克、泄、耗——用${KE_BY[dayEl]}(克我)、${SHENG[dayEl]}(我生)、${KE[dayEl]}(我克)`,
|
|
4639
|
+
};
|
|
4640
|
+
}
|
|
4641
|
+
if (level === '身弱' || level === '偏弱') {
|
|
4642
|
+
return {
|
|
4643
|
+
list: [dayEl, SHENG_BY[dayEl]].filter(Boolean),
|
|
4644
|
+
basis: `${level}(${dayEl}):宜生、扶——用${dayEl}(同我)、${SHENG_BY[dayEl]}(生我)`,
|
|
4645
|
+
};
|
|
4646
|
+
}
|
|
4647
|
+
return { list: ALL.slice(), basis: `${level}:中和不偏,五行皆可用,以字义与音形为先` };
|
|
4648
|
+
}
|
|
4649
|
+
|
|
4650
|
+
function charInfo(ch) {
|
|
4651
|
+
return EL_INDEX.get(ch) || null;
|
|
4652
|
+
}
|
|
4653
|
+
|
|
4654
|
+
function assessName(input) {
|
|
4655
|
+
const surname = String(input.surname || '').trim();
|
|
4656
|
+
if (!surname) throw new Error('缺少姓氏:请给 surname(如「李」)');
|
|
4657
|
+
const given = String(input.givenName || '').trim();
|
|
4658
|
+
if (!given) throw new Error('缺少名字:请给 givenName(1–2 个汉字,如「沐宸」)');
|
|
4659
|
+
if ([...given].length > 2) throw new Error('名请给 1–2 个汉字;复姓请把复姓整体写在 surname,名只留 1–2 字');
|
|
4660
|
+
|
|
4661
|
+
let favorable = { list: [], basis: '' };
|
|
4662
|
+
let strength = null;
|
|
4663
|
+
let baziNote = null;
|
|
4664
|
+
const hasBirth = Number.isInteger(input.year) && Number.isInteger(input.month) && Number.isInteger(input.day);
|
|
4665
|
+
if (hasBirth) {
|
|
4666
|
+
assertCivil(input, 'birth');
|
|
4667
|
+
const r = buildBazi({
|
|
4668
|
+
year: input.year, month: input.month, day: input.day,
|
|
4669
|
+
hour: Number.isInteger(input.hour) ? input.hour : 12,
|
|
4670
|
+
minute: Number.isInteger(input.minute) ? input.minute : 0,
|
|
4671
|
+
gender: input.gender || 'male',
|
|
4672
|
+
lateZi: input.lateZi !== false,
|
|
4673
|
+
trueSolar: input.trueSolar !== false,
|
|
4674
|
+
dst: input.dst !== false,
|
|
4675
|
+
longitude: input.longitude, address: input.address, geo: input.geo,
|
|
4676
|
+
});
|
|
4677
|
+
strength = { level: r.strength.level, score: r.strength.score, dayElement: r.pillars.day.stemElement };
|
|
4678
|
+
favorable = favorableElements(strength);
|
|
4679
|
+
baziNote = { dayMaster: r.dayMaster, solarNote: r.solarTime ? r.solarTime.note : '' };
|
|
4680
|
+
}
|
|
4681
|
+
|
|
4682
|
+
const chars = [...given].map((ch) => {
|
|
4683
|
+
const info = charInfo(ch);
|
|
4684
|
+
const inFav = info ? favorable.list.includes(info.el) : null;
|
|
4685
|
+
return {
|
|
4686
|
+
char: ch,
|
|
4687
|
+
element: info ? info.el : '未收录',
|
|
4688
|
+
meaning: info ? info.meaning : '字库未收录,未推断五行',
|
|
4689
|
+
genderHint: info ? KIND_LABEL[info.g] : null,
|
|
4690
|
+
favorable: inFav,
|
|
4691
|
+
};
|
|
4692
|
+
});
|
|
4693
|
+
const known = chars.filter((c) => c.element !== '未收录').length;
|
|
4694
|
+
const favCount = chars.filter((c) => c.favorable === true).length;
|
|
4695
|
+
let verdict;
|
|
4696
|
+
if (!hasBirth) verdict = '只做了字义与五行归类;给出生年月日才能判断用字是否合喜用。';
|
|
4697
|
+
else if (favCount === chars.length && chars.length > 0) verdict = `用字都落在喜用(${favorable.list.join('、')})上,与命局方向一致。`;
|
|
4698
|
+
else if (favCount > 0) verdict = `部分用字合喜用(${favCount}/${chars.length}),其余可再斟酌;喜用为 ${favorable.list.join('、')}。`;
|
|
4699
|
+
else verdict = `用字未落在喜用(${favorable.list.join('、')})上;不必然是坏名,但若想贴合命局可换字再看。`;
|
|
4700
|
+
|
|
4701
|
+
return {
|
|
4702
|
+
system: 'name',
|
|
4703
|
+
action: 'assess',
|
|
4704
|
+
surname,
|
|
4705
|
+
givenName: given,
|
|
4706
|
+
fullName: surname + given,
|
|
4707
|
+
...(hasBirth ? {
|
|
4708
|
+
bazi: {
|
|
4709
|
+
...baziNote,
|
|
4710
|
+
strength,
|
|
4711
|
+
favorable: favorable.list,
|
|
4712
|
+
basis: favorable.basis,
|
|
4713
|
+
},
|
|
4714
|
+
} : {}),
|
|
4715
|
+
chars,
|
|
4716
|
+
stats: { chars: chars.length, known, favorableCount: favCount },
|
|
4717
|
+
verdict,
|
|
4718
|
+
notes: [
|
|
4719
|
+
'字之五行按通行字义/偏旁归类,自建字库有限:未收录的字不猜五行,如实标注。',
|
|
4720
|
+
'不做五格剖象(需康熙字典笔画),也不做音韵格律判断——谐音、拗口请自己读三遍或用方言读一遍。',
|
|
4721
|
+
'起名只做用字参考,不替父母做决定。',
|
|
4722
|
+
],
|
|
4723
|
+
};
|
|
4724
|
+
}
|
|
4725
|
+
|
|
4726
|
+
function generateNames(input) {
|
|
4727
|
+
const surname = String(input.surname || '').trim();
|
|
4728
|
+
if (!surname) throw new Error('缺少姓氏:请给 surname(如「李」)');
|
|
4729
|
+
if (!Number.isInteger(input.year) || !Number.isInteger(input.month) || !Number.isInteger(input.day)) {
|
|
4730
|
+
throw new Error('生成候选名需要完整出生年月日(公历):不给生辰无法判断喜用五行');
|
|
4731
|
+
}
|
|
4732
|
+
assertCivil(input, 'birth');
|
|
4733
|
+
const gender = input.gender === 'female' ? 'female' : 'male';
|
|
4734
|
+
const limit = Math.max(1, Math.min(20, Number.isInteger(input.limit) ? input.limit : 10));
|
|
4735
|
+
const r = buildBazi({
|
|
4736
|
+
year: input.year, month: input.month, day: input.day,
|
|
4737
|
+
hour: Number.isInteger(input.hour) ? input.hour : 12,
|
|
4738
|
+
minute: Number.isInteger(input.minute) ? input.minute : 0,
|
|
4739
|
+
gender,
|
|
4740
|
+
lateZi: input.lateZi !== false,
|
|
4741
|
+
trueSolar: input.trueSolar !== false,
|
|
4742
|
+
dst: input.dst !== false,
|
|
4743
|
+
longitude: input.longitude, address: input.address, geo: input.geo,
|
|
4744
|
+
});
|
|
4745
|
+
const strength = { level: r.strength.level, score: r.strength.score, dayElement: r.pillars.day.stemElement };
|
|
4746
|
+
const fav = favorableElements(strength);
|
|
4747
|
+
const pool = CHAR_LIB.filter((c) => fav.list.includes(c[1]) && c[0] !== surname);
|
|
4748
|
+
const gPref = gender === 'male' ? 'm' : 'f';
|
|
4749
|
+
const scored = [];
|
|
4750
|
+
for (let i = 0; i < pool.length; i++) {
|
|
4751
|
+
for (let j = 0; j < pool.length; j++) {
|
|
4752
|
+
if (i === j) continue;
|
|
4753
|
+
const a = pool[i];
|
|
4754
|
+
const b = pool[j];
|
|
4755
|
+
const fit = (x) => (x[3] === gPref || x[3] === 'n' ? 1 : 0);
|
|
4756
|
+
const gScore = fit(a) + fit(b) - (a[3] !== gPref && a[3] !== 'n' && b[3] !== gPref && b[3] !== 'n' ? 2 : 0);
|
|
4757
|
+
scored.push({
|
|
4758
|
+
name: a[0] + b[0],
|
|
4759
|
+
chars: [
|
|
4760
|
+
{ char: a[0], element: a[1], meaning: a[2] },
|
|
4761
|
+
{ char: b[0], element: b[1], meaning: b[2] },
|
|
4762
|
+
],
|
|
4763
|
+
elements: [a[1], b[1]],
|
|
4764
|
+
allFavorable: fav.list.includes(a[1]) && fav.list.includes(b[1]),
|
|
4765
|
+
genderScore: gScore,
|
|
4766
|
+
order: i * 1000 + j,
|
|
4767
|
+
});
|
|
4768
|
+
}
|
|
4769
|
+
}
|
|
4770
|
+
scored.sort((x, y) => (y.allFavorable - x.allFavorable) || (y.genderScore - x.genderScore) || (x.order - y.order));
|
|
4771
|
+
const out = [];
|
|
4772
|
+
const used = new Set();
|
|
4773
|
+
for (const cand of scored) {
|
|
4774
|
+
if (out.length >= limit) break;
|
|
4775
|
+
const key = [...cand.name].sort().join('');
|
|
4776
|
+
if (used.has(key)) continue;
|
|
4777
|
+
used.add(key);
|
|
4778
|
+
out.push({ rank: out.length + 1, name: surname + cand.name, given: cand.name, chars: cand.chars });
|
|
4779
|
+
}
|
|
4780
|
+
return {
|
|
4781
|
+
system: 'name',
|
|
4782
|
+
action: 'generate',
|
|
4783
|
+
surname,
|
|
4784
|
+
gender: gender === 'male' ? '男' : '女',
|
|
4785
|
+
bazi: { dayMaster: r.dayMaster, strength, favorable: fav.list, basis: fav.basis },
|
|
4786
|
+
candidates: out,
|
|
4787
|
+
notes: [
|
|
4788
|
+
`字库共 ${CHAR_LIB.length} 字,只在喜用五行(${fav.list.join('、')})里选字;未覆盖的生僻好字可手工评估。`,
|
|
4789
|
+
'候选按合喜用与性别倾向确定性排序,不是「吉名」判定;音韵与方言谐音请自行读念检查。',
|
|
4790
|
+
'不做五格剖象(需康熙字典笔画)。',
|
|
4791
|
+
],
|
|
4792
|
+
};
|
|
4793
|
+
}
|
|
4794
|
+
|
|
4795
|
+
function buildName(input) {
|
|
4796
|
+
if (input.action === 'generate') return generateNames(input);
|
|
4797
|
+
return assessName(input);
|
|
4798
|
+
}
|
|
4799
|
+
|
|
4800
|
+
return { CHAR_LIB, favorableElements, assessName, generateNames, buildName };
|
|
4801
|
+
})();
|
|
4802
|
+
|
|
3934
4803
|
__mods['ziwei-hepan'] = (function () {
|
|
3935
4804
|
const { buildZiwei } = __mods.ziwei;
|
|
3936
4805
|
const { assertCivil } = __mods.validate;
|
|
@@ -4861,6 +5730,632 @@ const TAROT_ELEMENT_FOCUS = {
|
|
|
4861
5730
|
return { TRIGRAM, YAO_POSITION, MOVING_NATURE, HEX64, HEX_BY_CORE, ZIWEI_STARS, WUXING_INDUSTRY, WUXING_ORGAN, TENGOD_STYLE, LIUREN_JIANG, DISCLAIMER, TAROT_ELEMENT_FOCUS };
|
|
4862
5731
|
})();
|
|
4863
5732
|
|
|
5733
|
+
__mods.almanac = (function () {
|
|
5734
|
+
const { solarToLunar } = __mods.lunar;
|
|
5735
|
+
const { toJDN, fromJDN, dayGanzhiIndex, ganzhiName, termWindow, termInstantJd, partsToJd, STEMS, BRANCHES, TERM_NAMES } = __mods.calendar;
|
|
5736
|
+
const { HEX64 } = __mods['interpret-data'];
|
|
5737
|
+
const { assertCivil, daysInMonth } = __mods.validate;
|
|
5738
|
+
|
|
5739
|
+
const ZHIXING = ['建', '除', '满', '平', '定', '执', '破', '危', '成', '收', '开', '闭'];
|
|
5740
|
+
|
|
5741
|
+
const ZHIXING_YI = {
|
|
5742
|
+
建: ['出行', '上任', '会友', '上书', '见工'],
|
|
5743
|
+
除: ['除服', '疗病', '扫舍', '出行'],
|
|
5744
|
+
满: ['祈福', '祭祀', '结亲', '开市', '交易'],
|
|
5745
|
+
平: ['修饰垣墙', '平治道涂'],
|
|
5746
|
+
定: ['嫁娶', '订盟', '纳采', '祭祀', '祈福'],
|
|
5747
|
+
执: ['祭祀', '祈福', '捕捉', '纳畜'],
|
|
5748
|
+
破: ['破屋', '坏垣', '求医'],
|
|
5749
|
+
危: ['祭祀', '祈福', '安床'],
|
|
5750
|
+
成: ['嫁娶', '开市', '入学', '上任', '修造'],
|
|
5751
|
+
收: ['纳财', '收账', '进人口'],
|
|
5752
|
+
开: ['开市', '交易', '嫁娶', '入学', '动土'],
|
|
5753
|
+
闭: ['筑堤', '埋穴', '补垣'],
|
|
5754
|
+
};
|
|
5755
|
+
|
|
5756
|
+
const ZHIXING_JI = {
|
|
5757
|
+
建: ['动土', '开仓', '出货财', '安葬'],
|
|
5758
|
+
除: ['求财', '赴任', '嫁娶'],
|
|
5759
|
+
满: ['服药', '栽种', '安葬'],
|
|
5760
|
+
平: ['祈福', '求嗣'],
|
|
5761
|
+
定: ['诉讼', '出行'],
|
|
5762
|
+
执: ['开市', '求财', '出行'],
|
|
5763
|
+
破: ['嫁娶', '开市', '动土'],
|
|
5764
|
+
危: ['登高', '行船', '出行'],
|
|
5765
|
+
成: ['诉讼'],
|
|
5766
|
+
收: ['开市', '出行', '安葬'],
|
|
5767
|
+
开: ['安葬'],
|
|
5768
|
+
闭: ['开市', '出行', '求医'],
|
|
5769
|
+
};
|
|
5770
|
+
|
|
5771
|
+
const CAI_SHEN = { 甲: '东北', 乙: '东北', 丙: '西南', 丁: '西南', 戊: '正北', 己: '正北', 庚: '正东', 辛: '正东', 壬: '正南', 癸: '正南' };
|
|
5772
|
+
|
|
5773
|
+
const XI_SHEN = { 甲: '东北', 乙: '西北', 丙: '西南', 丁: '正南', 戊: '东南', 己: '东北', 庚: '西北', 辛: '西南', 壬: '正南', 癸: '东南' };
|
|
5774
|
+
|
|
5775
|
+
const SHENGXIAO = ['鼠', '牛', '虎', '兔', '龙', '蛇', '马', '羊', '猴', '鸡', '狗', '猪'];
|
|
5776
|
+
const SHA_BY_GROUP = { 申子辰: '南', 寅午戌: '北', 巳酉丑: '东', 亥卯未: '西' };
|
|
5777
|
+
function shaOf(branch) {
|
|
5778
|
+
for (const [g, v] of Object.entries(SHA_BY_GROUP)) if (g.includes(branch)) return v;
|
|
5779
|
+
return null;
|
|
5780
|
+
}
|
|
5781
|
+
|
|
5782
|
+
function hourToBranch(hour, minute = 0) {
|
|
5783
|
+
const h = Number.isInteger(hour) ? ((hour % 24) + 24) % 24 : 12;
|
|
5784
|
+
return BRANCHES[Math.floor(((h + 1) % 24) / 2)];
|
|
5785
|
+
}
|
|
5786
|
+
|
|
5787
|
+
function zhixingOf(dayBranch, monthBranch) {
|
|
5788
|
+
const d = BRANCHES.indexOf(dayBranch);
|
|
5789
|
+
const m = BRANCHES.indexOf(monthBranch);
|
|
5790
|
+
return ZHIXING[((d - m) % 12 + 12) % 12];
|
|
5791
|
+
}
|
|
5792
|
+
|
|
5793
|
+
const MONTH_TEXT = ['正', '二', '三', '四', '五', '六', '七', '八', '九', '十', '冬', '腊'];
|
|
5794
|
+
const DAY_TENS = ['初', '十', '廿'];
|
|
5795
|
+
const DAY_UNITS = ['十', '一', '二', '三', '四', '五', '六', '七', '八', '九'];
|
|
5796
|
+
function dayText(day) {
|
|
5797
|
+
if (day === 10) return '初十';
|
|
5798
|
+
if (day === 20) return '二十';
|
|
5799
|
+
if (day === 30) return '三十';
|
|
5800
|
+
return `${DAY_TENS[Math.floor(day / 10)]}${DAY_UNITS[day % 10]}`;
|
|
5801
|
+
}
|
|
5802
|
+
function lunarMonthText(month, isLeap = false) {
|
|
5803
|
+
return `${isLeap ? '闰' : ''}${MONTH_TEXT[month - 1] || month}月`;
|
|
5804
|
+
}
|
|
5805
|
+
|
|
5806
|
+
function dailyHexagram(year, month, day) {
|
|
5807
|
+
const names = Object.keys(HEX64);
|
|
5808
|
+
if (!names.length) return null;
|
|
5809
|
+
const seed = (((year * 10007) ^ (month * 911) ^ (day * 97)) >>> 0) % names.length;
|
|
5810
|
+
const name = names[seed];
|
|
5811
|
+
return { name, ...HEX64[name] };
|
|
5812
|
+
}
|
|
5813
|
+
|
|
5814
|
+
const termName = (t) => (t && (t.name || TERM_NAMES[t.index])) || '';
|
|
5815
|
+
|
|
5816
|
+
function buildAlmanac(input) {
|
|
5817
|
+
assertCivil(input, '黄历');
|
|
5818
|
+
const { year, month, day } = input;
|
|
5819
|
+
if (day > daysInMonth(year, month)) {
|
|
5820
|
+
throw new Error(`非法日期:${year} 年 ${month} 月没有 ${day} 日`);
|
|
5821
|
+
}
|
|
5822
|
+
const jdNoon = partsToJd({ year, month, day, hour: 12, minute: 0 });
|
|
5823
|
+
const lunar = solarToLunar(year, month, day);
|
|
5824
|
+
const jdn = toJDN(year, month, day);
|
|
5825
|
+
|
|
5826
|
+
const lichunJd = termInstantJd(year, 2);
|
|
5827
|
+
const ganzhiYear = jdNoon < lichunJd ? year - 1 : year;
|
|
5828
|
+
const yearIdx = (((ganzhiYear - 4) % 60) + 60) % 60;
|
|
5829
|
+
const yearGz = ganzhiName(yearIdx);
|
|
5830
|
+
const springYear = lunar.lunarYear;
|
|
5831
|
+
const springGz = ganzhiName((((springYear - 4) % 60) + 60) % 60);
|
|
5832
|
+
|
|
5833
|
+
const jieTerms = termWindow(year, { jieOnly: true });
|
|
5834
|
+
let prevJie = null;
|
|
5835
|
+
let nextJie = null;
|
|
5836
|
+
for (const t of jieTerms) {
|
|
5837
|
+
if (t.jd <= jdNoon) prevJie = t; else { if (!nextJie) nextJie = t; }
|
|
5838
|
+
}
|
|
5839
|
+
const monthBranchIdx = ((2 + (prevJie.index - 2) / 2) % 12 + 12) % 12;
|
|
5840
|
+
const yinMonthStem = ((STEMS.indexOf(yearGz.stem) % 5) * 2 + 2) % 10;
|
|
5841
|
+
const monthStemIdx = (yinMonthStem + ((monthBranchIdx - 2 + 12) % 12)) % 10;
|
|
5842
|
+
const monthGz = ganzhiName(monthComposite(monthStemIdx, monthBranchIdx));
|
|
5843
|
+
|
|
5844
|
+
const dayGz = ganzhiName(dayGanzhiIndex(jdn));
|
|
5845
|
+
const dayBranch = dayGz.branch;
|
|
5846
|
+
const zhiXing = zhixingOf(dayBranch, BRANCHES[monthBranchIdx]);
|
|
5847
|
+
const chongIdx = (BRANCHES.indexOf(dayBranch) + 6) % 12;
|
|
5848
|
+
const chongShengXiao = SHENGXIAO[chongIdx];
|
|
5849
|
+
|
|
5850
|
+
const allTerms = termWindow(year);
|
|
5851
|
+
let prevTerm = null;
|
|
5852
|
+
let nextTerm = null;
|
|
5853
|
+
for (const t of allTerms) {
|
|
5854
|
+
if (t.jd <= jdNoon) prevTerm = t; else { if (!nextTerm) nextTerm = t; }
|
|
5855
|
+
}
|
|
5856
|
+
const fmtJdDate = (jd) => {
|
|
5857
|
+
const p = fromJDN(Math.floor(jd + 0.5));
|
|
5858
|
+
return `${p.year}-${String(p.month).padStart(2, '0')}-${String(p.day).padStart(2, '0')}`;
|
|
5859
|
+
};
|
|
5860
|
+
|
|
5861
|
+
const weekday = ['日', '一', '二', '三', '四', '五', '六'][(jdn + 1) % 7];
|
|
5862
|
+
const lunarText = `农历${springGz.name}年${lunarMonthText(lunar.month, lunar.isLeap)}${dayText(lunar.day)}`;
|
|
5863
|
+
const boundaryNote = yearGz.name !== springGz.name
|
|
5864
|
+
? `年界:本条按立春界作${yearGz.name}年·属${SHENGXIAO[BRANCHES.indexOf(yearGz.branch)]}(命理排盘用);春节界为${springGz.name}年·属${SHENGXIAO[BRANCHES.indexOf(springGz.branch)]},两种都对,取用场合不同`
|
|
5865
|
+
: null;
|
|
5866
|
+
|
|
5867
|
+
return {
|
|
5868
|
+
system: 'almanac',
|
|
5869
|
+
meta: {
|
|
5870
|
+
date: `${year}-${String(month).padStart(2, '0')}-${String(day).padStart(2, '0')}`,
|
|
5871
|
+
weekday: `星期${weekday}`,
|
|
5872
|
+
...(boundaryNote ? { boundaryNote } : {}),
|
|
5873
|
+
scope: '通行简表口径:宜忌只按建除十二神;逐日神煞/二十八宿/彭祖百忌/胎神等全量黄历数据未收录',
|
|
5874
|
+
},
|
|
5875
|
+
lunar: {
|
|
5876
|
+
year: springYear,
|
|
5877
|
+
month: lunar.month,
|
|
5878
|
+
day: lunar.day,
|
|
5879
|
+
isLeap: lunar.isLeap,
|
|
5880
|
+
text: lunarText,
|
|
5881
|
+
},
|
|
5882
|
+
ganzhi: {
|
|
5883
|
+
year: yearGz.name,
|
|
5884
|
+
month: monthGz.name,
|
|
5885
|
+
day: dayGz.name,
|
|
5886
|
+
shengXiao: SHENGXIAO[BRANCHES.indexOf(yearGz.branch)],
|
|
5887
|
+
springYear: springGz.name,
|
|
5888
|
+
springShengXiao: SHENGXIAO[BRANCHES.indexOf(springGz.branch)],
|
|
5889
|
+
},
|
|
5890
|
+
jieQi: {
|
|
5891
|
+
prev: prevTerm ? { name: termName(prevTerm), date: fmtJdDate(prevTerm.jd) } : null,
|
|
5892
|
+
next: nextTerm ? { name: termName(nextTerm), date: fmtJdDate(nextTerm.jd) } : null,
|
|
5893
|
+
},
|
|
5894
|
+
positions: { caiShen: CAI_SHEN[dayGz.stem] || null, xiShen: XI_SHEN[dayGz.stem] || null },
|
|
5895
|
+
zhiXing,
|
|
5896
|
+
yi: ZHIXING_YI[zhiXing].slice(),
|
|
5897
|
+
ji: ZHIXING_JI[zhiXing].slice(),
|
|
5898
|
+
chong: {
|
|
5899
|
+
shengXiao: chongShengXiao,
|
|
5900
|
+
text: `冲${chongShengXiao}`,
|
|
5901
|
+
sha: shaOf(dayBranch) ? `煞${shaOf(dayBranch)}` : null,
|
|
5902
|
+
},
|
|
5903
|
+
dailyHexagram: dailyHexagram(year, month, day),
|
|
5904
|
+
sources: [
|
|
5905
|
+
'农历与节气:本地天文历法(定朔 + 节气黄经)',
|
|
5906
|
+
'建除十二神与冲煞:传统通行口径',
|
|
5907
|
+
'宜忌:建除十二神通行简表(非逐日全量黄历)',
|
|
5908
|
+
'每日一卦:按公历日期确定性取卦(同日同卦)',
|
|
5909
|
+
],
|
|
5910
|
+
disclaimer: '黄历为传统民俗口径的本地确定性计算;宜忌仅按建除简表,属娱乐参考,不构成择日结论。',
|
|
5911
|
+
};
|
|
5912
|
+
}
|
|
5913
|
+
|
|
5914
|
+
function monthComposite(stemIdx, branchIdx) {
|
|
5915
|
+
for (let i = 0; i < 60; i++) {
|
|
5916
|
+
if (i % 10 === stemIdx && i % 12 === branchIdx) return i;
|
|
5917
|
+
}
|
|
5918
|
+
return 0;
|
|
5919
|
+
}
|
|
5920
|
+
|
|
5921
|
+
return { ZHIXING, ZHIXING_YI, ZHIXING_JI, SHENGXIAO, hourToBranch, zhixingOf, lunarMonthText, dailyHexagram, buildAlmanac };
|
|
5922
|
+
})();
|
|
5923
|
+
|
|
5924
|
+
__mods.star = (function () {
|
|
5925
|
+
const { daysInMonth } = __mods.validate;
|
|
5926
|
+
|
|
5927
|
+
const SIGNS = [
|
|
5928
|
+
{ name: '白羊座', element: '火象', planet: '火星', range: [3, 21, 4, 19], keywords: '热血、开拓、直来直往', compatible: '狮子、射手、天秤' },
|
|
5929
|
+
{ name: '金牛座', element: '土象', planet: '金星', range: [4, 20, 5, 20], keywords: '沉稳、务实、惜物恋旧', compatible: '处女、摩羯、巨蟹' },
|
|
5930
|
+
{ name: '双子座', element: '风象', planet: '水星', range: [5, 21, 6, 21], keywords: '灵动、好奇、能言善变', compatible: '天秤、水瓶、白羊' },
|
|
5931
|
+
{ name: '巨蟹座', element: '水象', planet: '月亮', range: [6, 22, 7, 22], keywords: '顾家、念旧、外柔内韧', compatible: '天蝎、双鱼、金牛' },
|
|
5932
|
+
{ name: '狮子座', element: '火象', planet: '太阳', range: [7, 23, 8, 22], keywords: '大方、自信、天生舞台感', compatible: '白羊、射手、天秤' },
|
|
5933
|
+
{ name: '处女座', element: '土象', planet: '水星', range: [8, 23, 9, 22], keywords: '细致、挑剔、完美主义', compatible: '金牛、摩羯、巨蟹' },
|
|
5934
|
+
{ name: '天秤座', element: '风象', planet: '金星', range: [9, 23, 10, 23], keywords: '优雅、权衡、社交高手', compatible: '双子、水瓶、狮子' },
|
|
5935
|
+
{ name: '天蝎座', element: '水象', planet: '冥王星', range: [10, 24, 11, 22], keywords: '深挚、洞察、爱憎分明', compatible: '巨蟹、双鱼、处女' },
|
|
5936
|
+
{ name: '射手座', element: '火象', planet: '木星', range: [11, 23, 12, 21], keywords: '乐观、自由、远方体质', compatible: '白羊、狮子、水瓶' },
|
|
5937
|
+
{ name: '摩羯座', element: '土象', planet: '土星', range: [12, 22, 1, 19], keywords: '自律、坚忍、大器晚成', compatible: '金牛、处女、天蝎' },
|
|
5938
|
+
{ name: '水瓶座', element: '风象', planet: '天王星', range: [1, 20, 2, 18], keywords: '独立、前卫、脑洞担当', compatible: '双子、天秤、射手' },
|
|
5939
|
+
{ name: '双鱼座', element: '水象', planet: '海王星', range: [2, 19, 3, 20], keywords: '温柔、共情、浪漫滤镜', compatible: '巨蟹、天蝎、金牛' },
|
|
5940
|
+
];
|
|
5941
|
+
|
|
5942
|
+
function signOfDate(month, day) {
|
|
5943
|
+
for (const s of SIGNS) {
|
|
5944
|
+
const [m1, d1, m2, d2] = s.range;
|
|
5945
|
+
if ((month === m1 && day >= d1) || (month === m2 && day <= d2)) return s;
|
|
5946
|
+
}
|
|
5947
|
+
return null;
|
|
5948
|
+
}
|
|
5949
|
+
|
|
5950
|
+
function daySeed(y, m, d, salt) {
|
|
5951
|
+
let x = ((y * 73856093) ^ (m * 19349663) ^ (d * 83492791) ^ Math.imul(salt, 2654435761)) >>> 0;
|
|
5952
|
+
x = Math.imul(x ^ (x >>> 13), 0x5bd1e995);
|
|
5953
|
+
return (x ^ (x >>> 15)) >>> 0;
|
|
5954
|
+
}
|
|
5955
|
+
|
|
5956
|
+
const COLORS = ['晨雾白', '海盐蓝', '苔原绿', '落日橘', '琥珀黄', '黛蓝', '藕荷粉', '石墨灰', '橄榄绿', '雾紫'];
|
|
5957
|
+
const TONE_TEXT = {
|
|
5958
|
+
5: '势头极好,主动出击。',
|
|
5959
|
+
4: '顺风缓行,乘势而为。',
|
|
5960
|
+
3: '平顺持中,稳字当头。',
|
|
5961
|
+
2: '略有阻滞,慢即是快。',
|
|
5962
|
+
1: '宜静宜守,养精蓄锐。',
|
|
5963
|
+
};
|
|
5964
|
+
|
|
5965
|
+
function buildStar(input) {
|
|
5966
|
+
const { year, month, day } = input || {};
|
|
5967
|
+
if (!Number.isInteger(year) || !Number.isInteger(month) || !Number.isInteger(day)
|
|
5968
|
+
|| year < 1900 || year > 2100 || month < 1 || month > 12 || day < 1 || day > daysInMonth(year, month)) {
|
|
5969
|
+
throw new Error('星座需要真实存在的公历日期(year 1900–2100、month、day)——请向用户询问');
|
|
5970
|
+
}
|
|
5971
|
+
const sign = signOfDate(month, day);
|
|
5972
|
+
if (!sign) throw new Error(`无法识别星座:${year}-${month}-${day}`);
|
|
5973
|
+
const dims = ['事业', '感情', '财运', '健康'].map((k, i) => {
|
|
5974
|
+
const stars = (daySeed(year, month, day, i + 1) % 5) + 1;
|
|
5975
|
+
return { name: k, stars, text: TONE_TEXT[stars] };
|
|
5976
|
+
});
|
|
5977
|
+
return {
|
|
5978
|
+
system: 'star',
|
|
5979
|
+
meta: { date: `${year}-${String(month).padStart(2, '0')}-${String(day).padStart(2, '0')}` },
|
|
5980
|
+
sign: {
|
|
5981
|
+
name: sign.name,
|
|
5982
|
+
element: sign.element,
|
|
5983
|
+
planet: sign.planet,
|
|
5984
|
+
keywords: sign.keywords,
|
|
5985
|
+
compatible: sign.compatible,
|
|
5986
|
+
range: `${sign.range[0]}月${sign.range[1]}日–${sign.range[2]}月${sign.range[3]}日`,
|
|
5987
|
+
},
|
|
5988
|
+
daily: {
|
|
5989
|
+
tone: TONE_TEXT[(daySeed(year, month, day, 9) % 5) + 1],
|
|
5990
|
+
dims,
|
|
5991
|
+
luckyColor: COLORS[daySeed(year, month, day, 10) % COLORS.length],
|
|
5992
|
+
luckyNumber: (daySeed(year, month, day, 11) % 9) + 1,
|
|
5993
|
+
},
|
|
5994
|
+
sources: ['星座区间:太阳过宫通行日期', '当日基调:日期哈希确定性生成(同日同结果)'],
|
|
5995
|
+
disclaimer: '星座档案为通行天文口径;当日基调纯娱乐向,同日同结果,不构成任何现实预测。',
|
|
5996
|
+
};
|
|
5997
|
+
}
|
|
5998
|
+
|
|
5999
|
+
return { SIGNS, signOfDate, buildStar };
|
|
6000
|
+
})();
|
|
6001
|
+
|
|
6002
|
+
__mods.numerology = (function () {
|
|
6003
|
+
const { daysInMonth } = __mods.validate;
|
|
6004
|
+
|
|
6005
|
+
const NUMBER_MEANING = {
|
|
6006
|
+
1: { title: '开创者', keywords: '独立、主见、起步快', tendency: '习惯自己拍板,适合从零开始的事。', lesson: '课题是学会合作与示弱,别把「我能行」当唯一答案。' },
|
|
6007
|
+
2: { title: '协调者', keywords: '细腻、共情、善配合', tendency: '擅长读空气、做黏合剂,团队里离不开的角色。', lesson: '课题是别过度讨好、别把委屈攒成怨气。' },
|
|
6008
|
+
3: { title: '表达者', keywords: '活跃、创意、感染力', tendency: '靠表达与点子吃饭,越有舞台越发光。', lesson: '课题是持续力与收尾,别让兴趣过载成半成品。' },
|
|
6009
|
+
4: { title: '建构者', keywords: '务实、秩序、靠得住', tendency: '擅长把混乱理成流程,慢工出细活。', lesson: '课题是别过度求稳、别用规则捆住自己和别人。' },
|
|
6010
|
+
5: { title: '探索者', keywords: '自由、变化、新鲜感', tendency: '适应力强,喜欢移动与尝鲜,厌烦一成不变。', lesson: '课题是承诺与专注,别把「换一个」当解药。' },
|
|
6011
|
+
6: { title: '照顾者', keywords: '责任、顾家、审美', tendency: '天然把身边人放进自己的责任清单。', lesson: '课题是别替别人扛过头,爱不等于包办。' },
|
|
6012
|
+
7: { title: '探究者', keywords: '内省、钻研、独处', tendency: '需要个人空间,喜欢把问题想透再动。', lesson: '课题是信任与敞开,别用距离保护自己。' },
|
|
6013
|
+
8: { title: '经营者', keywords: '资源、格局、执行力', tendency: '对钱与权有直觉,擅长把资源盘活。', lesson: '课题是平衡控制欲,别用成果定义自我价值。' },
|
|
6014
|
+
9: { title: '理想者', keywords: '情怀、包容、完成感', tendency: '看得远,愿意为更大的意义投入。', lesson: '课题是落地与界限,别在付出里丢了自己。' },
|
|
6015
|
+
11: { title: '直觉者(主数)', keywords: '敏感、灵感、感召力', tendency: '直觉极准,容易成为别人的灯,但情绪消耗也大。', lesson: '课题是把天赋落地成具体作品,而不是只停在感受。' },
|
|
6016
|
+
22: { title: '实践家(主数)', keywords: '愿景、系统、扛大梁', tendency: '能把大想法拆成可行工程,压力与能力同步放大。', lesson: '课题是别被宏大目标压住,先完成小闭环。' },
|
|
6017
|
+
33: { title: '疗愈者(主数)', keywords: '大爱、教导、承担', tendency: '为他人承担是本能,适合教导与疗愈型角色。', lesson: '课题是先照顾好自己,别把牺牲当价值。' },
|
|
6018
|
+
};
|
|
6019
|
+
|
|
6020
|
+
function reduceNumber(n, keepMasters = true) {
|
|
6021
|
+
let v = Math.abs(Math.trunc(n));
|
|
6022
|
+
while (v > 9) {
|
|
6023
|
+
if (keepMasters && (v === 11 || v === 22 || v === 33)) return v;
|
|
6024
|
+
v = String(v).split('').reduce((s, c) => s + Number(c), 0);
|
|
6025
|
+
}
|
|
6026
|
+
return v;
|
|
6027
|
+
}
|
|
6028
|
+
|
|
6029
|
+
const digitSum = (n) => String(Math.abs(Math.trunc(n))).split('').reduce((s, c) => s + Number(c), 0);
|
|
6030
|
+
|
|
6031
|
+
function buildNumerology(input) {
|
|
6032
|
+
const { year, month, day } = input || {};
|
|
6033
|
+
if (!Number.isInteger(year) || !Number.isInteger(month) || !Number.isInteger(day)
|
|
6034
|
+
|| year < 1900 || year > 2100 || month < 1 || month > 12 || day < 1 || day > daysInMonth(year, month)) {
|
|
6035
|
+
throw new Error('生命数字需要真实存在的公历出生日期(year 1900–2100、month、day)——请向用户询问,勿猜');
|
|
6036
|
+
}
|
|
6037
|
+
const lifePath = reduceNumber(digitSum(year) + digitSum(month) + digitSum(day));
|
|
6038
|
+
const birthday = reduceNumber(day);
|
|
6039
|
+
const attitude = reduceNumber(month + day);
|
|
6040
|
+
const parts = [
|
|
6041
|
+
{ key: 'lifePath', name: '生命路径数', number: lifePath, meaning: NUMBER_MEANING[lifePath] },
|
|
6042
|
+
{ key: 'birthday', name: '生日数', number: birthday, meaning: NUMBER_MEANING[birthday] },
|
|
6043
|
+
{ key: 'attitude', name: '态度数', number: attitude, meaning: NUMBER_MEANING[attitude] },
|
|
6044
|
+
];
|
|
6045
|
+
return {
|
|
6046
|
+
system: 'numerology',
|
|
6047
|
+
meta: { date: `${year}-${String(month).padStart(2, '0')}-${String(day).padStart(2, '0')}`, system: '西方数字学' },
|
|
6048
|
+
numbers: { lifePath, birthday, attitude },
|
|
6049
|
+
parts,
|
|
6050
|
+
primary: parts[0],
|
|
6051
|
+
sources: ['生命路径=出生年月日逐位求和归约;生日数=日归约;态度数=月+日归约(11/22/33 保留)'],
|
|
6052
|
+
disclaimer: '生命数字属西方数字学体系,与八字/紫微等东方术数口径不同、不可互相印证;仅供自我认识参考。',
|
|
6053
|
+
};
|
|
6054
|
+
}
|
|
6055
|
+
|
|
6056
|
+
return { NUMBER_MEANING, reduceNumber, buildNumerology };
|
|
6057
|
+
})();
|
|
6058
|
+
|
|
6059
|
+
__mods.chenggu = (function () {
|
|
6060
|
+
const { solarToLunar } = __mods.lunar;
|
|
6061
|
+
const { ganzhiName, toJDN, dayGanzhiIndex } = __mods.calendar;
|
|
6062
|
+
const { assertCivil } = __mods.validate;
|
|
6063
|
+
const { hourToBranch } = __mods.almanac;
|
|
6064
|
+
|
|
6065
|
+
const YEAR_WEIGHT = [
|
|
6066
|
+
12, 9, 6, 7, 12, 5, 9, 8, 7, 8,
|
|
6067
|
+
15, 9, 16, 8, 8, 19, 12, 6, 8, 7,
|
|
6068
|
+
5, 15, 6, 16, 15, 7, 9, 12, 10, 7,
|
|
6069
|
+
15, 6, 5, 14, 14, 9, 7, 7, 9, 12,
|
|
6070
|
+
8, 7, 13, 5, 14, 5, 9, 17, 5, 7,
|
|
6071
|
+
12, 8, 8, 6, 19, 6, 8, 16, 10, 7,
|
|
6072
|
+
];
|
|
6073
|
+
|
|
6074
|
+
const MONTH_WEIGHT = [6, 7, 18, 9, 5, 16, 9, 15, 18, 8, 9, 5];
|
|
6075
|
+
|
|
6076
|
+
const DAY_WEIGHT = [
|
|
6077
|
+
5, 10, 8, 15, 16, 15, 8, 16, 8, 16,
|
|
6078
|
+
9, 17, 8, 17, 10, 8, 9, 18, 5, 15,
|
|
6079
|
+
10, 9, 8, 9, 15, 18, 7, 8, 16, 6,
|
|
6080
|
+
];
|
|
6081
|
+
|
|
6082
|
+
const HOUR_WEIGHT = { 子: 16, 丑: 6, 寅: 7, 卯: 10, 辰: 9, 巳: 16, 午: 10, 未: 8, 申: 8, 酉: 9, 戌: 6, 亥: 6 };
|
|
6083
|
+
|
|
6084
|
+
const LIANG = ['一', '二', '三', '四', '五', '六', '七'];
|
|
6085
|
+
const QIAN = ['一', '二', '三', '四', '五', '六', '七', '八', '九'];
|
|
6086
|
+
const MONTH_TEXT = ['正', '二', '三', '四', '五', '六', '七', '八', '九', '十', '冬', '腊'];
|
|
6087
|
+
const DAY_TENS = ['初', '十', '廿'];
|
|
6088
|
+
const DAY_UNITS = ['十', '一', '二', '三', '四', '五', '六', '七', '八', '九'];
|
|
6089
|
+
|
|
6090
|
+
function formatWeight(qian) {
|
|
6091
|
+
const liang = Math.floor(qian / 10);
|
|
6092
|
+
const rem = qian % 10;
|
|
6093
|
+
const liangPart = liang > 0 ? `${LIANG[liang - 1]}两` : '';
|
|
6094
|
+
const remPart = rem > 0 ? `${QIAN[rem - 1]}钱` : liang > 0 ? '整' : '零';
|
|
6095
|
+
return `${liangPart}${remPart}`;
|
|
6096
|
+
}
|
|
6097
|
+
|
|
6098
|
+
function lunarDayText(day) {
|
|
6099
|
+
if (day === 10) return '初十';
|
|
6100
|
+
if (day === 20) return '二十';
|
|
6101
|
+
if (day === 30) return '三十';
|
|
6102
|
+
const ten = Math.floor(day / 10);
|
|
6103
|
+
const unit = day % 10;
|
|
6104
|
+
return `${DAY_TENS[ten]}${DAY_UNITS[unit]}`;
|
|
6105
|
+
}
|
|
6106
|
+
|
|
6107
|
+
function lunarMonthText(month, isLeap = false) {
|
|
6108
|
+
return `${isLeap ? '闰' : ''}${MONTH_TEXT[month - 1] || month}月`;
|
|
6109
|
+
}
|
|
6110
|
+
|
|
6111
|
+
function bandVerdict(qian) {
|
|
6112
|
+
if (qian >= 55) return { band: '上上', text: '骨重沉实,根基深厚,一生多逢贵人,谋事易成;守正持重,福泽可久。' };
|
|
6113
|
+
if (qian >= 45) return { band: '上', text: '骨重充实,中年前后渐入佳境,事业家宅两旺;忌骄忌躁,善守即善成。' };
|
|
6114
|
+
if (qian >= 35) return { band: '中上', text: '骨重适中,先劳后逸,凭本事立身;中年转运之象,稳扎稳打自见分晓。' };
|
|
6115
|
+
if (qian >= 25) return { band: '中', text: '骨重平平,成败多靠自身经营;宜有一技傍身、广结善缘,勤则补拙。' };
|
|
6116
|
+
return { band: '中下', text: '骨重较轻,早年多磨,凡事宜稳不宜急;积跬步以至千里,晚景反可安宁。' };
|
|
6117
|
+
}
|
|
6118
|
+
|
|
6119
|
+
function buildChenggu(input) {
|
|
6120
|
+
assertCivil(input, '称骨');
|
|
6121
|
+
const { year, month, day, hour } = input;
|
|
6122
|
+
const minute = input.minute || 0;
|
|
6123
|
+
if (!Number.isInteger(hour) || hour < 0 || hour > 23) {
|
|
6124
|
+
throw new Error('称骨必须给时辰:hour 为 0–23 的整数(缺时辰四柱不齐,骨重无法求和)');
|
|
6125
|
+
}
|
|
6126
|
+
const lunar = solarToLunar(year, month, day);
|
|
6127
|
+
const lunarYear = lunar.lunarYear;
|
|
6128
|
+
const lunarMonth = lunar.month;
|
|
6129
|
+
const lunarDay = lunar.day;
|
|
6130
|
+
const yearGz = ganzhiName((((lunarYear - 4) % 60) + 60) % 60);
|
|
6131
|
+
const hourBranch = hourToBranch(hour, minute);
|
|
6132
|
+
|
|
6133
|
+
const wYear = YEAR_WEIGHT[((lunarYear - 4) % 60 + 60) % 60];
|
|
6134
|
+
const wMonth = MONTH_WEIGHT[lunarMonth - 1];
|
|
6135
|
+
const wDay = DAY_WEIGHT[lunarDay - 1];
|
|
6136
|
+
const wHour = HOUR_WEIGHT[hourBranch];
|
|
6137
|
+
if ([wYear, wMonth, wDay, wHour].some((x) => typeof x !== 'number')) {
|
|
6138
|
+
throw new Error(`称骨表缺项:年${wYear} 月${wMonth} 日${wDay} 时${wHour}`);
|
|
6139
|
+
}
|
|
6140
|
+
const total = wYear + wMonth + wDay + wHour;
|
|
6141
|
+
const verdict = bandVerdict(total);
|
|
6142
|
+
const jdn = toJDN(year, month, day);
|
|
6143
|
+
|
|
6144
|
+
return {
|
|
6145
|
+
system: 'chenggu',
|
|
6146
|
+
input: { solar: `${year}-${String(month).padStart(2, '0')}-${String(day).padStart(2, '0')}`, hour, minute },
|
|
6147
|
+
lunar: {
|
|
6148
|
+
text: `农历${yearGz.name}年${lunarMonthText(lunarMonth, lunar.isLeap)}${lunarDayText(lunarDay)} ${hourBranch}时`,
|
|
6149
|
+
year: lunarYear,
|
|
6150
|
+
month: lunarMonth,
|
|
6151
|
+
day: lunarDay,
|
|
6152
|
+
isLeap: lunar.isLeap,
|
|
6153
|
+
hourBranch,
|
|
6154
|
+
},
|
|
6155
|
+
ganzhi: { year: yearGz.name, day: ganzhiName(dayGanzhiIndex(jdn)).name },
|
|
6156
|
+
weights: {
|
|
6157
|
+
year: { qian: wYear, label: `${yearGz.name}年 ${formatWeight(wYear)}` },
|
|
6158
|
+
month: { qian: wMonth, label: `农历${lunarMonthText(lunarMonth, lunar.isLeap)} ${formatWeight(wMonth)}` },
|
|
6159
|
+
day: { qian: wDay, label: `农历${lunarDayText(lunarDay)} ${formatWeight(wDay)}` },
|
|
6160
|
+
hour: { qian: wHour, label: `${hourBranch}时 ${formatWeight(wHour)}` },
|
|
6161
|
+
},
|
|
6162
|
+
total: { qian: total, label: formatWeight(total) },
|
|
6163
|
+
verdict,
|
|
6164
|
+
sources: [
|
|
6165
|
+
'骨重表:通行本袁天罡称骨(六十甲子年 / 十二月 / 三十日 / 十二时辰)',
|
|
6166
|
+
'批语:白话自撰,非原版称骨歌诀原文,可自行对照',
|
|
6167
|
+
],
|
|
6168
|
+
disclaimer: '骨重为通行表口径;批语为白话解读,属民俗娱乐参考,不构成命运断言。',
|
|
6169
|
+
};
|
|
6170
|
+
}
|
|
6171
|
+
|
|
6172
|
+
return { YEAR_WEIGHT, MONTH_WEIGHT, DAY_WEIGHT, HOUR_WEIGHT, formatWeight, lunarDayText, lunarMonthText, buildChenggu };
|
|
6173
|
+
})();
|
|
6174
|
+
|
|
6175
|
+
__mods.xiaoliuren = (function () {
|
|
6176
|
+
const { solarToLunar } = __mods.lunar;
|
|
6177
|
+
const { hourToBranch } = __mods.almanac;
|
|
6178
|
+
const { BRANCHES } = __mods.calendar;
|
|
6179
|
+
|
|
6180
|
+
const PALACES = {
|
|
6181
|
+
大安: { luck: '吉', text: '事事昌顺,宜静守不宜妄动;所问之事根基稳,缓一缓更妥。' },
|
|
6182
|
+
留连: { luck: '凶', text: '事多牵扯反复,暗中有耗;宜守不宜进,防拖延与口舌。' },
|
|
6183
|
+
速喜: { luck: '吉', text: '喜事将至,行动宜快;所问之事三日内外见分晓。' },
|
|
6184
|
+
赤口: { luck: '凶', text: '口舌是非、官讼之象;宜谨言慎行,凡事留凭证。' },
|
|
6185
|
+
小吉: { luck: '吉', text: '小有助力,慢慢来能成;合作与人际有贵人。' },
|
|
6186
|
+
空亡: { luck: '凶', text: '落空之象,谋事多不成;宜停一停,改期再议。' },
|
|
6187
|
+
};
|
|
6188
|
+
|
|
6189
|
+
const PALACE_ORDER = ['大安', '留连', '速喜', '赤口', '小吉', '空亡'];
|
|
6190
|
+
|
|
6191
|
+
const pick = (n) => PALACE_ORDER[(((n - 1) % 6) + 6) % 6];
|
|
6192
|
+
|
|
6193
|
+
function buildXiaoliuren(input) {
|
|
6194
|
+
const mode = input.mode === 'number' ? 'number' : 'time';
|
|
6195
|
+
let m;
|
|
6196
|
+
let d;
|
|
6197
|
+
let h;
|
|
6198
|
+
let originText;
|
|
6199
|
+
if (mode === 'time') {
|
|
6200
|
+
if (typeof input.hour !== 'number' || !Number.isInteger(input.hour) || input.hour < 0 || input.hour > 23) {
|
|
6201
|
+
throw new Error('时间起课必须同时给 date 与 hour(0–23):掐指按时辰推进,缺时辰不可代填');
|
|
6202
|
+
}
|
|
6203
|
+
const mt = /^(\d{4})-(\d{1,2})-(\d{1,2})$/.exec(String(input.date || '').trim());
|
|
6204
|
+
if (!mt) throw new Error('时间起课需要 date(公历 YYYY-MM-DD)——请向用户询问日期');
|
|
6205
|
+
const lunar = solarToLunar(Number(mt[1]), Number(mt[2]), Number(mt[3]));
|
|
6206
|
+
const branch = hourToBranch(input.hour);
|
|
6207
|
+
m = lunar.month;
|
|
6208
|
+
d = lunar.day;
|
|
6209
|
+
h = BRANCHES.indexOf(branch) + 1;
|
|
6210
|
+
originText = `农历${lunar.month}月${lunar.day}日 ${branch}时:月${m} → 日${d} → 时${h} 掐指落宫`;
|
|
6211
|
+
} else {
|
|
6212
|
+
const nums = [input.num1, input.num2, input.num3].map(Number);
|
|
6213
|
+
if (nums.some((x) => !Number.isInteger(x) || x < 1 || x > 99)) {
|
|
6214
|
+
throw new Error('报数起课需要三个 1–99 的正整数(随口报即可)——缺数请向用户索要,不要代填');
|
|
6215
|
+
}
|
|
6216
|
+
[m, d, h] = nums;
|
|
6217
|
+
originText = `报数 ${m}、${d}、${h}:依次按月/日/时位掐指落宫`;
|
|
6218
|
+
}
|
|
6219
|
+
const p1 = pick(m);
|
|
6220
|
+
const p2 = pick(PALACE_ORDER.indexOf(p1) + 1 + d - 1);
|
|
6221
|
+
const p3 = pick(PALACE_ORDER.indexOf(p2) + 1 + h - 1);
|
|
6222
|
+
const info = PALACES[p3];
|
|
6223
|
+
return {
|
|
6224
|
+
system: 'xiaoliuren',
|
|
6225
|
+
meta: { mode: mode === 'time' ? '时间起课' : '报数起课', question: input.question || null, originText },
|
|
6226
|
+
steps: [p1, p2, p3],
|
|
6227
|
+
result: { palace: p3, luck: info.luck, text: info.text },
|
|
6228
|
+
sources: ['掌诀口径:通行马前课(白话提炼,非原文照录)'],
|
|
6229
|
+
disclaimer: '小六壬为轻量快占,属民俗娱乐参考;重要事项请综合现实信息判断。',
|
|
6230
|
+
};
|
|
6231
|
+
}
|
|
6232
|
+
|
|
6233
|
+
return { PALACES, PALACE_ORDER, buildXiaoliuren };
|
|
6234
|
+
})();
|
|
6235
|
+
|
|
6236
|
+
__mods.zeri = (function () {
|
|
6237
|
+
const { fromJDN, toJDN } = __mods.calendar;
|
|
6238
|
+
const { buildAlmanac, SHENGXIAO } = __mods.almanac;
|
|
6239
|
+
|
|
6240
|
+
const TASKS = {
|
|
6241
|
+
嫁娶: { yi: ['嫁娶', '结亲', '订盟', '纳采'], related: ['会亲友', '裁衣'], category: 'marriage' },
|
|
6242
|
+
订婚: { yi: ['订盟', '纳采'], related: ['嫁娶', '会亲友'], category: 'marriage' },
|
|
6243
|
+
开业: { yi: ['开市', '立券', '交易'], related: ['纳财', '挂匾'], category: 'business' },
|
|
6244
|
+
入宅: { yi: ['入宅', '移徙'], related: ['安床', '修造', '扫舍'], category: 'move' },
|
|
6245
|
+
搬家: { yi: ['移徙', '入宅'], related: ['安床', '修造'], category: 'move' },
|
|
6246
|
+
动土: { yi: ['动土', '修造'], related: ['竖柱', '上梁'], category: 'construction' },
|
|
6247
|
+
装修: { yi: ['修造', '修饰垣墙'], related: ['动土'], category: 'construction' },
|
|
6248
|
+
安葬: { yi: ['安葬', '破土'], related: ['立碑', '埋穴'], category: 'burial' },
|
|
6249
|
+
出行: { yi: ['出行'], related: ['上任', '赴任'], category: 'travel' },
|
|
6250
|
+
祭祀: { yi: ['祭祀'], related: ['祈福', '求嗣'], category: 'worship' },
|
|
6251
|
+
签约: { yi: ['立券', '交易'], related: ['纳财', '开市'], category: 'contract' },
|
|
6252
|
+
求医: { yi: ['求医', '疗病'], related: ['针灸'], category: 'medical' },
|
|
6253
|
+
纳财: { yi: ['纳财', '收账'], related: ['开市', '立券'], category: 'contract' },
|
|
6254
|
+
};
|
|
6255
|
+
|
|
6256
|
+
const ZHIXING_FAVOR = {
|
|
6257
|
+
marriage: ['满', '定', '成', '开'],
|
|
6258
|
+
business: ['开', '成', '满', '收'],
|
|
6259
|
+
move: ['定', '成', '开', '除'],
|
|
6260
|
+
construction: ['建', '除', '成', '开'],
|
|
6261
|
+
burial: ['除', '定', '闭', '破'],
|
|
6262
|
+
travel: ['建', '成', '开', '除'],
|
|
6263
|
+
worship: ['除', '满', '平', '定', '执', '成', '开'],
|
|
6264
|
+
contract: ['定', '成', '收', '开'],
|
|
6265
|
+
medical: ['除', '开'],
|
|
6266
|
+
};
|
|
6267
|
+
|
|
6268
|
+
const VERDICT = (score, ctx) => {
|
|
6269
|
+
if (ctx.chongSelf) {
|
|
6270
|
+
return { verdict: '回避', text: `当日冲${ctx.chongSelf}(当事人属相):传统择日径直排除,不进推荐与可用名单。`, excluded: true };
|
|
6271
|
+
}
|
|
6272
|
+
if (score >= 3) return { verdict: '推荐', text: '宜项与建除相配,此日对所问事项顺遂。', excluded: false };
|
|
6273
|
+
if (score >= 1) {
|
|
6274
|
+
return {
|
|
6275
|
+
verdict: '可用',
|
|
6276
|
+
text: ctx.jiHit.length ? `可考虑,但当日忌${ctx.jiHit.join('、')}:宜项加分、忌项减分,未抵消干净。` : '未见冲克,可用;若有更好的日子,优先取推荐日。',
|
|
6277
|
+
excluded: false,
|
|
6278
|
+
};
|
|
6279
|
+
}
|
|
6280
|
+
if (score >= 0) return { verdict: '平平', text: ctx.jiHit.length ? `当日忌${ctx.jiHit.join('、')},宜项不相干,不作首选。` : '宜忌不相干,无助力也无冲克。', excluded: false };
|
|
6281
|
+
return { verdict: '回避', text: ctx.jiHit.length ? `当日忌${ctx.jiHit.join('、')},对所问事项不宜。` : '减分项多于加分项,此日不宜。', excluded: false };
|
|
6282
|
+
};
|
|
6283
|
+
|
|
6284
|
+
function buildZeri(input) {
|
|
6285
|
+
const task = input.task;
|
|
6286
|
+
if (!TASKS[task]) throw new Error(`不支持的事项「${task}」。支持:${Object.keys(TASKS).join('、')}`);
|
|
6287
|
+
const m = /^(\d{4})-(\d{1,2})-(\d{1,2})$/.exec(String(input.startDate || '').trim());
|
|
6288
|
+
if (!m) throw new Error('择日需要 startDate(公历 YYYY-MM-DD)——请向用户询问起算日,不要代填');
|
|
6289
|
+
const start = { year: Number(m[1]), month: Number(m[2]), day: Number(m[3]) };
|
|
6290
|
+
const days = Math.max(1, Math.min(60, Number.isInteger(input.days) ? input.days : 30));
|
|
6291
|
+
const shengXiaoRaw = typeof input.shengXiao === 'string' ? input.shengXiao.trim() : '';
|
|
6292
|
+
const shengXiao = shengXiaoRaw || null;
|
|
6293
|
+
if (shengXiao && !SHENGXIAO.includes(shengXiao)) throw new Error(`shengXiao 须为生肖名:${SHENGXIAO.join('、')}`);
|
|
6294
|
+
|
|
6295
|
+
const spec = TASKS[task];
|
|
6296
|
+
const zhixingSet = ZHIXING_FAVOR[spec.category];
|
|
6297
|
+
const startJdn = toJDN(start.year, start.month, start.day);
|
|
6298
|
+
const list = [];
|
|
6299
|
+
for (let i = 0; i < days; i++) {
|
|
6300
|
+
const p = fromJDN(startJdn + i);
|
|
6301
|
+
const a = buildAlmanac(p);
|
|
6302
|
+
let score = 0;
|
|
6303
|
+
const reasons = [];
|
|
6304
|
+
const yiHit = spec.yi.filter((w) => a.yi.includes(w));
|
|
6305
|
+
if (yiHit.length) { score += 2; reasons.push(`宜${yiHit.join('、')}(+2)`); }
|
|
6306
|
+
const relHit = spec.related.filter((w) => a.yi.includes(w));
|
|
6307
|
+
if (relHit.length) { score += 1; reasons.push(`关联宜项${relHit.join('、')}(+1)`); }
|
|
6308
|
+
const jiHit = [...spec.yi, ...spec.related].filter((w) => a.ji.includes(w));
|
|
6309
|
+
if (jiHit.length) { score -= 3; reasons.push(`忌${jiHit.join('、')}(-3)`); }
|
|
6310
|
+
const chongSelf = shengXiao && a.chong.shengXiao === shengXiao ? shengXiao : null;
|
|
6311
|
+
if (chongSelf) { score -= 2; reasons.push(`冲${shengXiao}(当事人属相,径直排除)`); }
|
|
6312
|
+
if (zhixingSet.includes(a.zhiXing)) { score += 1; reasons.push(`建除${a.zhiXing}神利${task}(+1)`); }
|
|
6313
|
+
if (a.zhiXing === '破' && spec.category !== 'burial') { score -= 1; reasons.push('破日(-1)'); }
|
|
6314
|
+
if (a.zhiXing === '危' && spec.category !== 'travel' && spec.category !== 'worship') { score -= 1; reasons.push('危日(-1)'); }
|
|
6315
|
+
|
|
6316
|
+
const v = VERDICT(score, { chongSelf, jiHit });
|
|
6317
|
+
list.push({
|
|
6318
|
+
date: a.meta.date,
|
|
6319
|
+
weekday: a.meta.weekday,
|
|
6320
|
+
lunar: a.lunar.text,
|
|
6321
|
+
dayGanZhi: a.ganzhi.day,
|
|
6322
|
+
zhiXing: a.zhiXing,
|
|
6323
|
+
yi: a.yi,
|
|
6324
|
+
ji: a.ji,
|
|
6325
|
+
chong: a.chong.shengXiao,
|
|
6326
|
+
score,
|
|
6327
|
+
reasons,
|
|
6328
|
+
verdict: v.verdict,
|
|
6329
|
+
verdictText: v.text,
|
|
6330
|
+
...(v.excluded ? { excluded: true } : {}),
|
|
6331
|
+
});
|
|
6332
|
+
}
|
|
6333
|
+
|
|
6334
|
+
const ranked = list.slice().sort((x, y) => y.score - x.score || (x.date < y.date ? -1 : 1));
|
|
6335
|
+
return {
|
|
6336
|
+
system: 'zeri',
|
|
6337
|
+
meta: {
|
|
6338
|
+
task,
|
|
6339
|
+
startDate: `${start.year}-${String(start.month).padStart(2, '0')}-${String(start.day).padStart(2, '0')}`,
|
|
6340
|
+
days,
|
|
6341
|
+
shengXiao,
|
|
6342
|
+
scope: shengXiao
|
|
6343
|
+
? `宜忌按建除简表;已给当事人属相(${shengXiao}),凡当日冲该属相径直排除。评分仅用于排序引导。`
|
|
6344
|
+
: '宜忌按建除简表;未给当事人属相,未做冲属相排除。评分仅用于排序引导。',
|
|
6345
|
+
},
|
|
6346
|
+
best: ranked.filter((d) => d.verdict === '推荐' && !d.excluded).slice(0, 5).map((d) => d.date),
|
|
6347
|
+
usable: ranked.filter((d) => d.verdict === '可用' && !d.excluded).slice(0, 5).map((d) => d.date),
|
|
6348
|
+
avoid: list.filter((d) => d.verdict === '回避').map((d) => `${d.date}(${d.reasons.join(';') || '综合减分'})`),
|
|
6349
|
+
days: list,
|
|
6350
|
+
excludedCount: list.filter((d) => d.excluded).length,
|
|
6351
|
+
sources: ['宜忌:建除十二神通行简表', '建除适配与冲煞:传统通行口径', '排序:宜忌加减分 + 属相排除(确定性)'],
|
|
6352
|
+
disclaimer: '择日为传统黄历口径的本地计算,属民俗参考;评分只用于排序,不构成择日结论。',
|
|
6353
|
+
};
|
|
6354
|
+
}
|
|
6355
|
+
|
|
6356
|
+
return { TASKS, buildZeri };
|
|
6357
|
+
})();
|
|
6358
|
+
|
|
4864
6359
|
__mods['interpret-plain'] = (function () {
|
|
4865
6360
|
|
|
4866
6361
|
const s = (v, fallback = '') => (v === undefined || v === null || v === '' ? fallback : String(v));
|
|
@@ -6956,6 +8451,7 @@ function rectifyBirth(opts) {
|
|
|
6956
8451
|
gender,
|
|
6957
8452
|
lateZi: opts.lateZi !== false,
|
|
6958
8453
|
trueSolar: opts.trueSolar !== false,
|
|
8454
|
+
dst: opts.dst !== false,
|
|
6959
8455
|
address: typeof opts.address === 'string' ? opts.address : undefined,
|
|
6960
8456
|
longitude: typeof opts.longitude === 'number' ? opts.longitude : undefined,
|
|
6961
8457
|
geo: opts.geo && typeof opts.geo === 'object' ? opts.geo : undefined,
|
|
@@ -7024,6 +8520,15 @@ const { followup } = __mods.followup;
|
|
|
7024
8520
|
const { crossCheck, crossText } = __mods.crosscheck;
|
|
7025
8521
|
const { pastSignals, verifyFit } = __mods.verify;
|
|
7026
8522
|
const { rectifyBirth } = __mods.rectify;
|
|
8523
|
+
const { buildAlmanac } = __mods.almanac;
|
|
8524
|
+
const { buildZeri } = __mods.zeri;
|
|
8525
|
+
const { buildChenggu } = __mods.chenggu;
|
|
8526
|
+
const { buildXiaoliuren } = __mods.xiaoliuren;
|
|
8527
|
+
const { buildStar } = __mods.star;
|
|
8528
|
+
const { buildNumerology } = __mods.numerology;
|
|
8529
|
+
const { buildTrend } = __mods.trend;
|
|
8530
|
+
const { buildFengshui } = __mods.fengshui;
|
|
8531
|
+
const { buildName } = __mods.name;
|
|
7027
8532
|
const { fail } = __mods.fail;
|
|
7028
8533
|
const { assertCivil } = __mods.validate;
|
|
7029
8534
|
|
|
@@ -7199,6 +8704,15 @@ function hepanText(r) {
|
|
|
7199
8704
|
`对方 ${r.b.pillars.join(' ')} 日主 ${r.b.dayMaster} 日支 ${r.b.dayBranch} ${r.b.strength || ''}`,
|
|
7200
8705
|
`真太阳时:你 ${solarLine(r.a)};对方 ${solarLine(r.b)}`,
|
|
7201
8706
|
...r.dimensions.map(d),
|
|
8707
|
+
...(r.detail ? [
|
|
8708
|
+
`属相:${r.detail.yearRelation.label}——${r.detail.yearRelation.text}`,
|
|
8709
|
+
`日干:${r.detail.stems.text}`,
|
|
8710
|
+
...(r.detail.crossBranches && r.detail.crossBranches.length
|
|
8711
|
+
? [`跨盘地支:${r.detail.crossBranches.map((c) => `${c.name}(${c.a}${c.b})`).join('、')}`]
|
|
8712
|
+
: []),
|
|
8713
|
+
`年命纳音:${r.detail.nayin.a} / ${r.detail.nayin.b}(${r.detail.nayin.label})——${r.detail.nayin.text}`,
|
|
8714
|
+
`吉凶象:吉 ${r.detail.tally.good} / 凶 ${r.detail.tally.bad}(${r.detail.tally.lean};分档只数象,总分以上面三维度为准)`,
|
|
8715
|
+
] : []),
|
|
7202
8716
|
...r.notes.map((n) => `※ ${n}`),
|
|
7203
8717
|
].join('\n');
|
|
7204
8718
|
}
|
|
@@ -7292,6 +8806,7 @@ function compute(args = {}) {
|
|
|
7292
8806
|
address: typeof args.address === 'string' ? args.address : undefined,
|
|
7293
8807
|
geo: args.geo && typeof args.geo === 'object' ? args.geo : undefined,
|
|
7294
8808
|
trueSolar: args.trueSolar !== false,
|
|
8809
|
+
dst: args.dst !== false,
|
|
7295
8810
|
};
|
|
7296
8811
|
const asOf = checkAsOf(args.asOf);
|
|
7297
8812
|
const other = r.system === 'bazi'
|
|
@@ -7313,6 +8828,7 @@ function compute(args = {}) {
|
|
|
7313
8828
|
longitude: typeof args.longitude === 'number' ? args.longitude : undefined,
|
|
7314
8829
|
address: typeof args.address === 'string' ? args.address : undefined,
|
|
7315
8830
|
geo: args.geo && typeof args.geo === 'object' ? args.geo : undefined,
|
|
8831
|
+
dst: args.dst !== false,
|
|
7316
8832
|
nowYear: from,
|
|
7317
8833
|
});
|
|
7318
8834
|
r.past = {
|
|
@@ -7330,6 +8846,7 @@ function compute(args = {}) {
|
|
|
7330
8846
|
hours: Array.isArray(args.rectifyHours) ? args.rectifyHours : undefined,
|
|
7331
8847
|
lateZi: args.lateZi !== false,
|
|
7332
8848
|
trueSolar: args.trueSolar !== false,
|
|
8849
|
+
dst: args.dst !== false,
|
|
7333
8850
|
longitude: typeof args.longitude === 'number' ? args.longitude : undefined,
|
|
7334
8851
|
address: typeof args.address === 'string' ? args.address : undefined,
|
|
7335
8852
|
geo: args.geo && typeof args.geo === 'object' ? args.geo : undefined,
|
|
@@ -7401,6 +8918,7 @@ function computeRaw(args = {}) {
|
|
|
7401
8918
|
geo: args.geo && typeof args.geo === 'object' ? args.geo : undefined,
|
|
7402
8919
|
lateZi: args.lateZi !== false,
|
|
7403
8920
|
trueSolar: args.trueSolar !== false,
|
|
8921
|
+
dst: args.dst !== false,
|
|
7404
8922
|
nowYear: asOf ? asOf.year : undefined,
|
|
7405
8923
|
});
|
|
7406
8924
|
return { ...result, ...(asOf ? { asOf } : {}), precision: dt.precision, summary: baziText(result) };
|
|
@@ -7416,6 +8934,7 @@ function computeRaw(args = {}) {
|
|
|
7416
8934
|
address: typeof args.address === 'string' ? args.address : undefined,
|
|
7417
8935
|
geo: args.geo && typeof args.geo === 'object' ? args.geo : undefined,
|
|
7418
8936
|
trueSolar: args.trueSolar,
|
|
8937
|
+
dst: args.dst !== false,
|
|
7419
8938
|
asOf,
|
|
7420
8939
|
});
|
|
7421
8940
|
return { ...result, precision: dt.precision, summary: ziweiText(result) };
|
|
@@ -7470,6 +8989,7 @@ function computeRaw(args = {}) {
|
|
|
7470
8989
|
gender: requireGender(args.gender, '你(主方)'),
|
|
7471
8990
|
trueSolar: args.trueSolar,
|
|
7472
8991
|
lateZi: args.lateZi,
|
|
8992
|
+
dst: args.dst !== false,
|
|
7473
8993
|
address: typeof args.address === 'string' ? args.address : undefined,
|
|
7474
8994
|
longitude: typeof args.longitude === 'number' ? args.longitude : undefined,
|
|
7475
8995
|
geo: args.geo && typeof args.geo === 'object' ? args.geo : undefined,
|
|
@@ -7479,6 +8999,7 @@ function computeRaw(args = {}) {
|
|
|
7479
8999
|
gender: requireGender(partner.gender, '对方'),
|
|
7480
9000
|
trueSolar: partner.trueSolar,
|
|
7481
9001
|
lateZi: partner.lateZi,
|
|
9002
|
+
dst: partner.dst !== false,
|
|
7482
9003
|
address: typeof partner.address === 'string' ? partner.address : undefined,
|
|
7483
9004
|
longitude: typeof partner.longitude === 'number' ? partner.longitude : undefined,
|
|
7484
9005
|
geo: partner.geo && typeof partner.geo === 'object' ? partner.geo : undefined,
|
|
@@ -7532,10 +9053,248 @@ function computeRaw(args = {}) {
|
|
|
7532
9053
|
if (args.seed !== undefined && args.seed !== null && args.seed !== '') result.seed = Number(args.seed);
|
|
7533
9054
|
return { ...result, ...(asOf ? { asOf } : {}), summary: liuyaoText(result) };
|
|
7534
9055
|
}
|
|
9056
|
+
if (system === 'almanac') {
|
|
9057
|
+
const dt = parseDateTime(args.datetime);
|
|
9058
|
+
const result = buildAlmanac({ year: dt.year, month: dt.month, day: dt.day });
|
|
9059
|
+
return { ...result, summary: almanacText(result) };
|
|
9060
|
+
}
|
|
9061
|
+
if (system === 'zeri') {
|
|
9062
|
+
if (typeof args.startDate !== 'string' || !args.startDate.trim()) {
|
|
9063
|
+
throw fail('E_INPUT', '择日需要 startDate(公历 YYYY-MM-DD,起算日);可另给 days(1–60,默认 30)与 shengXiao(当事人属相)');
|
|
9064
|
+
}
|
|
9065
|
+
const result = buildZeri({
|
|
9066
|
+
task: args.task,
|
|
9067
|
+
startDate: args.startDate,
|
|
9068
|
+
days: args.days,
|
|
9069
|
+
shengXiao: args.shengXiao,
|
|
9070
|
+
});
|
|
9071
|
+
return { ...result, summary: zeriText(result) };
|
|
9072
|
+
}
|
|
9073
|
+
if (system === 'chenggu') {
|
|
9074
|
+
const dt = parseDateTime(args.datetime);
|
|
9075
|
+
const hasTime = /[T\s]\d{1,2}:\d{1,2}/.test(String(args.datetime));
|
|
9076
|
+
const hour = Number.isInteger(args.hour) ? args.hour : hasTime ? dt.hour : null;
|
|
9077
|
+
if (hour === null || hour < 0 || hour > 23) {
|
|
9078
|
+
throw fail('E_INPUT', '称骨必须给时辰:datetime 带时分(如 1990-08-15T10:00),或额外给 hour(0–23)——缺时辰四柱不齐');
|
|
9079
|
+
}
|
|
9080
|
+
const result = buildChenggu({ year: dt.year, month: dt.month, day: dt.day, hour, minute: hasTime ? dt.minute : 0 });
|
|
9081
|
+
return { ...result, summary: chengguText(result) };
|
|
9082
|
+
}
|
|
9083
|
+
if (system === 'xiaoliuren') {
|
|
9084
|
+
const dt = args.datetime ? parseDateTime(args.datetime) : null;
|
|
9085
|
+
const result = buildXiaoliuren({
|
|
9086
|
+
mode: args.mode === 'number' ? 'number' : 'time',
|
|
9087
|
+
date: typeof args.date === 'string' && args.date.trim() ? args.date : dt ? `${dt.year}-${dt.month}-${dt.day}` : undefined,
|
|
9088
|
+
hour: Number.isInteger(args.hour) ? args.hour : dt && /[T\s]\d{1,2}:\d{1,2}/.test(String(args.datetime)) ? dt.hour : undefined,
|
|
9089
|
+
num1: args.num1,
|
|
9090
|
+
num2: args.num2,
|
|
9091
|
+
num3: args.num3,
|
|
9092
|
+
question: args.question,
|
|
9093
|
+
});
|
|
9094
|
+
return { ...result, summary: xiaoliurenText(result) };
|
|
9095
|
+
}
|
|
9096
|
+
if (system === 'star') {
|
|
9097
|
+
const dt = parseDateTime(args.datetime);
|
|
9098
|
+
const result = buildStar({ year: dt.year, month: dt.month, day: dt.day });
|
|
9099
|
+
return { ...result, summary: starText(result) };
|
|
9100
|
+
}
|
|
9101
|
+
if (system === 'numerology') {
|
|
9102
|
+
const dt = parseDateTime(args.datetime);
|
|
9103
|
+
const result = buildNumerology({ year: dt.year, month: dt.month, day: dt.day });
|
|
9104
|
+
return { ...result, summary: numerologyText(result) };
|
|
9105
|
+
}
|
|
9106
|
+
if (system === 'trend') {
|
|
9107
|
+
const dt = parseDateTime(args.datetime);
|
|
9108
|
+
const result = buildTrend({
|
|
9109
|
+
...dt,
|
|
9110
|
+
gender: requireGender(args.gender),
|
|
9111
|
+
toAge: args.toAge,
|
|
9112
|
+
lateZi: args.lateZi !== false,
|
|
9113
|
+
trueSolar: args.trueSolar !== false,
|
|
9114
|
+
dst: args.dst !== false,
|
|
9115
|
+
longitude: typeof args.longitude === 'number' ? args.longitude : undefined,
|
|
9116
|
+
address: typeof args.address === 'string' ? args.address : undefined,
|
|
9117
|
+
geo: args.geo && typeof args.geo === 'object' ? args.geo : undefined,
|
|
9118
|
+
});
|
|
9119
|
+
return { ...result, precision: dt.precision, summary: trendText(result) };
|
|
9120
|
+
}
|
|
9121
|
+
if (system === 'fengshui') {
|
|
9122
|
+
const action = args.action === 'advise' ? 'advise' : 'natal';
|
|
9123
|
+
if (action === 'advise') {
|
|
9124
|
+
const result = buildFengshui({
|
|
9125
|
+
action,
|
|
9126
|
+
sitting: args.sitting,
|
|
9127
|
+
facing: args.facing,
|
|
9128
|
+
...(args.datetime ? parseDateTime(args.datetime) : {}),
|
|
9129
|
+
...(args.gender === 'male' || args.gender === 'female' ? { gender: args.gender } : {}),
|
|
9130
|
+
dst: args.dst !== false,
|
|
9131
|
+
});
|
|
9132
|
+
return { ...result, summary: fengshuiText(result) };
|
|
9133
|
+
}
|
|
9134
|
+
const dt = parseDateTime(args.datetime);
|
|
9135
|
+
const result = buildFengshui({
|
|
9136
|
+
action: 'natal',
|
|
9137
|
+
year: dt.year, month: dt.month, day: dt.day,
|
|
9138
|
+
gender: requireGender(args.gender),
|
|
9139
|
+
fortuneYear: Number.isInteger(args.fortuneYear) ? args.fortuneYear : undefined,
|
|
9140
|
+
dst: args.dst !== false,
|
|
9141
|
+
});
|
|
9142
|
+
return { ...result, summary: fengshuiText(result) };
|
|
9143
|
+
}
|
|
9144
|
+
if (system === 'name') {
|
|
9145
|
+
const action = args.action === 'generate' ? 'generate' : 'assess';
|
|
9146
|
+
const birth = args.datetime ? parseDateTime(args.datetime) : {};
|
|
9147
|
+
const result = buildName({
|
|
9148
|
+
action,
|
|
9149
|
+
surname: args.surname,
|
|
9150
|
+
givenName: args.givenName,
|
|
9151
|
+
limit: args.limit,
|
|
9152
|
+
...birth,
|
|
9153
|
+
...(args.gender === 'male' || args.gender === 'female' ? { gender: args.gender } : {}),
|
|
9154
|
+
...(args.datetime ? {
|
|
9155
|
+
lateZi: args.lateZi !== false,
|
|
9156
|
+
trueSolar: args.trueSolar !== false,
|
|
9157
|
+
dst: args.dst !== false,
|
|
9158
|
+
longitude: typeof args.longitude === 'number' ? args.longitude : undefined,
|
|
9159
|
+
address: typeof args.address === 'string' ? args.address : undefined,
|
|
9160
|
+
geo: args.geo && typeof args.geo === 'object' ? args.geo : undefined,
|
|
9161
|
+
} : {}),
|
|
9162
|
+
});
|
|
9163
|
+
return { ...result, summary: nameText(result) };
|
|
9164
|
+
}
|
|
7535
9165
|
if (!system) {
|
|
7536
|
-
throw fail('E_INPUT', '缺少 system,可选:bazi(四柱八字)、ziwei(紫微斗数)、liuyao(六爻)、meihua(梅花易数)、daliuren(大六壬)、qimen(奇门遁甲)、hepan(合盘)、tarot
|
|
9166
|
+
throw fail('E_INPUT', '缺少 system,可选:bazi(四柱八字)、ziwei(紫微斗数)、liuyao(六爻)、meihua(梅花易数)、daliuren(大六壬)、qimen(奇门遁甲)、hepan(合盘)、tarot(塔罗)、almanac(黄历)、zeri(择日)、chenggu(称骨)、xiaoliuren(小六壬)、star(星座)、numerology(生命数字)、trend(人生 K 线)、fengshui(风水)、name(起名)。');
|
|
9167
|
+
}
|
|
9168
|
+
throw fail('E_SYSTEM', `未知体系「${system}」,可选:bazi(四柱八字)、ziwei(紫微斗数)、liuyao(六爻)、meihua(梅花易数)、daliuren(大六壬)、qimen(奇门遁甲)、hepan(合盘)、tarot(塔罗)、almanac(黄历)、zeri(择日)、chenggu(称骨)、xiaoliuren(小六壬)、star(星座)、numerology(生命数字)、trend(人生 K 线)、fengshui(风水)、name(起名)。`);
|
|
9169
|
+
}
|
|
9170
|
+
|
|
9171
|
+
function almanacText(r) {
|
|
9172
|
+
const g = r.ganzhi;
|
|
9173
|
+
const jq = r.jieQi && r.jieQi.next ? ` 下一节:${r.jieQi.next.name} ${r.jieQi.next.date}` : '';
|
|
9174
|
+
return [
|
|
9175
|
+
`【黄历】${r.meta.date} ${r.meta.weekday} ${r.lunar.text}`,
|
|
9176
|
+
`干支:${g.year}年 ${g.month}月 ${g.day}日 属${g.shengXiao}`,
|
|
9177
|
+
...(r.meta.boundaryNote ? [` ${r.meta.boundaryNote}`] : []),
|
|
9178
|
+
`建除:${r.zhiXing}日 冲:${r.chong.text}${r.chong.sha ? `(${r.chong.sha})` : ''} 财神:${r.positions.caiShen || '—'} 喜神:${r.positions.xiShen || '—'}`,
|
|
9179
|
+
`宜:${r.yi.join('、')}`,
|
|
9180
|
+
`忌:${r.ji.join('、')}`,
|
|
9181
|
+
`节气:上一节 ${r.jieQi.prev ? `${r.jieQi.prev.name} ${r.jieQi.prev.date}` : '—'}${jq}`,
|
|
9182
|
+
r.dailyHexagram ? `每日一卦:${r.dailyHexagram.name} ${r.dailyHexagram.meaning || ''}` : '',
|
|
9183
|
+
`口径:${r.meta.scope}`,
|
|
9184
|
+
].filter(Boolean).join('\n');
|
|
9185
|
+
}
|
|
9186
|
+
|
|
9187
|
+
function zeriText(r) {
|
|
9188
|
+
const head = `【择日·${r.meta.task}】${r.meta.startDate} 起 ${r.meta.days} 天${r.meta.shengXiao ? ` 当事人属${r.meta.shengXiao}` : '(未给属相,未做冲煞排除)'}`;
|
|
9189
|
+
const top = r.days.slice().sort((a, b) => b.score - a.score).slice(0, 5)
|
|
9190
|
+
.map((d) => `· ${d.date} ${d.weekday} ${d.verdict}(${d.score}分) 宜${d.yi.slice(0, 3).join('/')} ${d.reasons.join(';') || '无特别加减分'}`);
|
|
9191
|
+
return [
|
|
9192
|
+
head,
|
|
9193
|
+
`推荐日:${r.best.length ? r.best.join('、') : '本期无(都不够推荐分)'}`,
|
|
9194
|
+
`可用日:${r.usable.length ? r.usable.join('、') : '无'}`,
|
|
9195
|
+
`回避日:${r.avoid.length ? r.avoid.slice(0, 6).join('、') : '无'}`,
|
|
9196
|
+
'逐日排序:',
|
|
9197
|
+
...top,
|
|
9198
|
+
`口径:${r.meta.scope}`,
|
|
9199
|
+
].join('\n');
|
|
9200
|
+
}
|
|
9201
|
+
|
|
9202
|
+
function chengguText(r) {
|
|
9203
|
+
return [
|
|
9204
|
+
`【称骨】${r.input.solar} ${r.lunar.text}`,
|
|
9205
|
+
`四项:${r.weights.year.label} ${r.weights.month.label} ${r.weights.day.label} ${r.weights.hour.label}`,
|
|
9206
|
+
`合计:${r.total.label}(${r.verdict.band})`,
|
|
9207
|
+
`批语:${r.verdict.text}`,
|
|
9208
|
+
'口径:骨重为通行表口径,批语为白话自撰(非原版称骨歌诀原文)。',
|
|
9209
|
+
].join('\n');
|
|
9210
|
+
}
|
|
9211
|
+
|
|
9212
|
+
function xiaoliurenText(r) {
|
|
9213
|
+
return [
|
|
9214
|
+
`【小六壬·${r.meta.mode}】${r.meta.question ? `所问:${r.meta.question}` : ''}`,
|
|
9215
|
+
r.meta.originText,
|
|
9216
|
+
`落宫:${r.steps.join(' → ')}`,
|
|
9217
|
+
`断语:${r.result.palace}(${r.result.luck}) ${r.result.text}`,
|
|
9218
|
+
].join('\n');
|
|
9219
|
+
}
|
|
9220
|
+
|
|
9221
|
+
function starText(r) {
|
|
9222
|
+
return [
|
|
9223
|
+
`【星座】${r.meta.date} ${r.sign.name}(${r.sign.element}·守护星${r.sign.planet})`,
|
|
9224
|
+
`区间:${r.sign.range} 关键词:${r.sign.keywords} 契合:${r.sign.compatible}`,
|
|
9225
|
+
`当日基调:${r.daily.tone}`,
|
|
9226
|
+
r.daily.dims.map((d) => `· ${d.name}:${'★'.repeat(d.stars)}${'☆'.repeat(5 - d.stars)} ${d.text}`).join('\n'),
|
|
9227
|
+
`幸运色:${r.daily.luckyColor} 幸运数字:${r.daily.luckyNumber}`,
|
|
9228
|
+
'口径:当日基调由日期哈希确定性生成(同日同结果),纯娱乐向。',
|
|
9229
|
+
].join('\n');
|
|
9230
|
+
}
|
|
9231
|
+
|
|
9232
|
+
function numerologyText(r) {
|
|
9233
|
+
return [
|
|
9234
|
+
`【生命数字】${r.meta.date}(西方数字学,与八字/紫微不可互证)`,
|
|
9235
|
+
...r.parts.map((p) => `${p.name}:${p.number}(${p.meaning.title}) ${p.meaning.keywords}\n 倾向:${p.meaning.tendency}\n 课题:${p.meaning.lesson}`),
|
|
9236
|
+
].join('\n');
|
|
9237
|
+
}
|
|
9238
|
+
|
|
9239
|
+
function trendText(r) {
|
|
9240
|
+
const s = r.meta.strength;
|
|
9241
|
+
const dm = r.meta.dayMaster;
|
|
9242
|
+
const dmText = dm && typeof dm === 'object' ? `${dm.stem}(${dm.element})` : String(dm || '');
|
|
9243
|
+
return [
|
|
9244
|
+
`【人生 K 线】${r.meta.birth} ${r.meta.gender} 日主 ${dmText} 旺衰 ${s.level}(${s.score})`,
|
|
9245
|
+
r.meta.boundaryNote ? `※ ${r.meta.boundaryNote}` : '',
|
|
9246
|
+
`【大运分段】${r.segments.map((g) => `${g.ganZhi} ${g.startAge}-${g.endAge}岁`).join(' ')}`,
|
|
9247
|
+
`【走势】${Array.isArray(r.sparkline) ? r.sparkline.join(' ') : r.sparkline}`,
|
|
9248
|
+
...(r.highlights && r.highlights.best && r.highlights.best.length
|
|
9249
|
+
? [`【关键年】最佳 ${r.highlights.best.join(';')}${r.highlights.worst && r.highlights.worst.length ? `\n最需留意 ${r.highlights.worst.join(';')}` : ''}`]
|
|
9250
|
+
: []),
|
|
9251
|
+
`※ ${r.disclaimer}`,
|
|
9252
|
+
].filter(Boolean).join('\n');
|
|
9253
|
+
}
|
|
9254
|
+
|
|
9255
|
+
function fengshuiText(r) {
|
|
9256
|
+
if (r.meta.action === 'advise') {
|
|
9257
|
+
const match = r.mingMatch
|
|
9258
|
+
? [`【命宅配】${r.mingMatch.text}`]
|
|
9259
|
+
: [`※ ${r.meta.birthNote || '未提供生辰,未做命宅配。'}`];
|
|
9260
|
+
return [
|
|
9261
|
+
`【宅卦】坐 ${r.meta.sitting} 向 ${r.meta.facing} ${r.meta.houseGua}宅(${r.meta.houseGroup})`,
|
|
9262
|
+
`【八星】${r.houseStars.map((x) => `${x.star}·${x.direction}${x.palace ? `(${x.palace})` : ''}`).join(' ')}`,
|
|
9263
|
+
`【门】${r.layout.door}`,
|
|
9264
|
+
`【灶】${r.layout.stove}`,
|
|
9265
|
+
`【床】${r.layout.bed}`,
|
|
9266
|
+
`【厕】${r.layout.toilet}`,
|
|
9267
|
+
...match,
|
|
9268
|
+
`※ ${r.disclaimer}`,
|
|
9269
|
+
].join('\n');
|
|
9270
|
+
}
|
|
9271
|
+
const g = r.mingGua;
|
|
9272
|
+
return [
|
|
9273
|
+
`【命卦】${g.num}${g.gua}(${g.group}) 方位 ${g.direction}`,
|
|
9274
|
+
`【四吉】${r.luckyDirections.map((x) => `${x.star}·${x.direction}${x.palace ? `(${x.palace})` : ''}`).join(' ')}`,
|
|
9275
|
+
`【四凶】${r.unluckyDirections.map((x) => `${x.star}·${x.direction}`).join(' ')}`,
|
|
9276
|
+
...(r.yearlyFlying
|
|
9277
|
+
? [`【${r.meta.fortuneYear} 年九宫飞星】中宫 ${r.yearlyFlying.centerName};${r.yearlyFlying.palaces.filter((p) => p.palace !== '中').map((p) => `${p.palace}${p.name}`).join(' ')}`]
|
|
9278
|
+
: [`※ ${r.meta.note || ''}`]),
|
|
9279
|
+
`※ ${r.disclaimer}`,
|
|
9280
|
+
].filter(Boolean).join('\n');
|
|
9281
|
+
}
|
|
9282
|
+
|
|
9283
|
+
function nameText(r) {
|
|
9284
|
+
if (r.action === 'generate') {
|
|
9285
|
+
return [
|
|
9286
|
+
`【候选名】姓 ${r.surname}(${r.gender},喜用 ${r.bazi.favorable.join('、')})${r.bazi.basis}`,
|
|
9287
|
+
...r.candidates.map((c) => `${c.rank}. ${c.name} ${c.chars.map((x) => `${x.char}(${x.element})${x.meaning}`).join(' ')}`),
|
|
9288
|
+
...r.notes.map((n) => `※ ${n}`),
|
|
9289
|
+
].join('\n');
|
|
7537
9290
|
}
|
|
7538
|
-
|
|
9291
|
+
return [
|
|
9292
|
+
`【名字评估】${r.fullName}`,
|
|
9293
|
+
...r.chars.map((c) => `· ${c.char}:五行 ${c.element}${c.element !== '未收录' ? `(喜用${c.favorable ? '中' : '外'})` : ''},${c.meaning}`),
|
|
9294
|
+
r.bazi ? `【八字】日主 ${r.bazi.dayMaster && r.bazi.dayMaster.stem ? `${r.bazi.dayMaster.stem}(${r.bazi.dayMaster.element})` : r.bazi.dayMaster},旺衰 ${r.bazi.strength.level}(${r.bazi.strength.score}),${r.bazi.basis}` : '',
|
|
9295
|
+
`【结论】${r.verdict}`,
|
|
9296
|
+
...r.notes.map((n) => `※ ${n}`),
|
|
9297
|
+
].filter(Boolean).join('\n');
|
|
7539
9298
|
}
|
|
7540
9299
|
|
|
7541
9300
|
function renderText(result) {
|
|
@@ -7564,7 +9323,7 @@ function renderText(result) {
|
|
|
7564
9323
|
return parts.filter(Boolean).join('\n\n');
|
|
7565
9324
|
}
|
|
7566
9325
|
|
|
7567
|
-
return { interpret, DIRECTIONS, DIRECTION_DEFAULT, BRIGHTNESS, flowOf, geocodeAddress, resolveAddress, buildBazi, buildLiuyao, tossLines, followup, pastSignals, verifyFit, rectifyBirth, crossCheck, crossText, parseDateTime, compute, interpretText, renderText };
|
|
9326
|
+
return { interpret, DIRECTIONS, DIRECTION_DEFAULT, BRIGHTNESS, flowOf, geocodeAddress, resolveAddress, buildBazi, buildLiuyao, tossLines, followup, pastSignals, verifyFit, rectifyBirth, crossCheck, crossText, buildAlmanac, buildZeri, buildChenggu, buildXiaoliuren, buildStar, buildNumerology, buildTrend, buildFengshui, buildName, parseDateTime, compute, interpretText, renderText };
|
|
7568
9327
|
})();
|
|
7569
9328
|
|
|
7570
9329
|
const Engine = __mods.index;
|
|
@@ -7600,9 +9359,9 @@ return { interpret, DIRECTIONS, DIRECTION_DEFAULT, BRIGHTNESS, flowOf, geocodeAd
|
|
|
7600
9359
|
(function () {
|
|
7601
9360
|
const CSS = `
|
|
7602
9361
|
.pp,.mp-it{--ink:#1b1b1b;--ink2:#3f3a33;--muted:#6f6a60;--label:#8a8378;--faint:#a9a296;
|
|
7603
|
-
--line:#c8c1b2;--dimline:#b9b2a4;--hair:#
|
|
7604
|
-
--bg:#
|
|
7605
|
-
--tri:#
|
|
9362
|
+
--line:#c8c1b2;--dimline:#b9b2a4;--hair:#e9e2d3;
|
|
9363
|
+
--bg:#f6f1e6;--card:#fffdf8;--band:#fbf7ee;--hover:#f3ede1;--hover2:#ece5d6;
|
|
9364
|
+
--tri:#f3e8cd;--soul:#fae4dc;--sel:#fceee7;--link:#f9ece0;
|
|
7606
9365
|
--red:#b3261e;--red-rgb:179,38,30;--lu:#1f6f3f;--lu-rgb:31,111,63;
|
|
7607
9366
|
--quan:#8a5a00;--quan-rgb:138,90,0;--ke:#1f4e79;--ke-rgb:31,78,121;--ji:#b3261e;
|
|
7608
9367
|
--br-good:#4c7a5c;--br-bad:#9c5b56;--on-accent:#fff;--wx-metal:#8a6b1f;
|
|
@@ -7612,8 +9371,8 @@ return { interpret, DIRECTIONS, DIRECTION_DEFAULT, BRIGHTNESS, flowOf, geocodeAd
|
|
|
7612
9371
|
@media screen and (prefers-color-scheme:dark){
|
|
7613
9372
|
.pp,.mp-it{--ink:#eae5d9;--ink2:#d5cfc2;--muted:#aca598;--label:#a49c8e;--faint:#928b7e;
|
|
7614
9373
|
--line:#514a3e;--dimline:#5f5849;--hair:#2f2b23;
|
|
7615
|
-
--bg:#
|
|
7616
|
-
--tri:#
|
|
9374
|
+
--bg:#12110c;--card:#211f18;--band:#282419;--hover:#2e2a1f;--hover2:#383322;
|
|
9375
|
+
--tri:#332913;--soul:#3a231c;--sel:#43271f;--link:#33241c;
|
|
7617
9376
|
--red:#f08569;--red-rgb:240,133,105;--lu:#74c795;--lu-rgb:116,199,149;
|
|
7618
9377
|
--quan:#e2a95c;--quan-rgb:226,169,92;--ke:#86bcea;--ke-rgb:134,188,234;--ji:#f08569;
|
|
7619
9378
|
--br-good:#79c79a;--br-bad:#e09a8c;--on-accent:#14130f;--wx-metal:#dcb273;
|
|
@@ -7751,13 +9510,15 @@ return { interpret, DIRECTIONS, DIRECTION_DEFAULT, BRIGHTNESS, flowOf, geocodeAd
|
|
|
7751
9510
|
.pp-ssi.neutral{color:var(--quan);border-color:rgba(var(--quan-rgb),.3)}
|
|
7752
9511
|
.pp-ssi em{font-style:normal;font-size:12px;color:var(--muted);margin-left:2px}
|
|
7753
9512
|
|
|
7754
|
-
.mp-hex{display:
|
|
7755
|
-
.mp-hex i{flex:1;background:currentColor;border-radius:1.5px}
|
|
7756
|
-
.mp-hex.yin{gap:18%}
|
|
9513
|
+
.mp-hex{display:block;width:104px;height:12px;flex:0 0 auto;color:var(--ink);overflow:visible}
|
|
7757
9514
|
.mp-hex.hex-on{color:var(--red)}
|
|
7758
9515
|
.mp-hex.dim{color:var(--dimline)}
|
|
7759
9516
|
.mp-hex.sm{width:74px;height:9px}
|
|
7760
9517
|
.mp-hex.md{width:88px;height:10px}
|
|
9518
|
+
.mp-hy-ln{display:flex;align-items:center;gap:4px;min-width:0}
|
|
9519
|
+
.mp-hy-mv{flex:0 0 10px;font-style:normal;font-size:11px;line-height:1;color:var(--red);text-align:center}
|
|
9520
|
+
.mp-wxi{display:inline-block;width:12px;height:12px;vertical-align:-2px;margin-right:3px;flex:0 0 auto}
|
|
9521
|
+
.mp-trig{display:inline-block;width:14px;height:10px;vertical-align:-1px;margin-right:4px;color:currentColor}
|
|
7761
9522
|
|
|
7762
9523
|
.mp-hy-wrap{display:flex;gap:22px;flex-wrap:wrap;align-items:flex-start}
|
|
7763
9524
|
.mp-hy-box{border:1px solid var(--line);background:var(--card);padding:12px 16px 14px;min-width:250px}
|
|
@@ -7765,7 +9526,7 @@ return { interpret, DIRECTIONS, DIRECTION_DEFAULT, BRIGHTNESS, flowOf, geocodeAd
|
|
|
7765
9526
|
.mp-hy-h b{color:var(--red);font-size:14px}
|
|
7766
9527
|
.mp-hy-h span{color:var(--muted);font-size:12px}
|
|
7767
9528
|
.mp-hy{display:flex;flex-direction:column;gap:6px}
|
|
7768
|
-
.mp-hy-row{display:grid;grid-template-columns:18px 30px
|
|
9529
|
+
.mp-hy-row{display:grid;grid-template-columns:18px 30px 128px 60px 54px 40px;gap:8px;align-items:center;font-size:13px;font-family:system-ui,sans-serif;padding:1px 0}
|
|
7769
9530
|
.mp-hy-sy{color:var(--red);font-size:12px;font-weight:700}
|
|
7770
9531
|
.mp-hy-nm{color:var(--muted);font-size:12px}
|
|
7771
9532
|
.mp-hy-gz{letter-spacing:.04em}
|
|
@@ -7873,6 +9634,28 @@ return { interpret, DIRECTIONS, DIRECTION_DEFAULT, BRIGHTNESS, flowOf, geocodeAd
|
|
|
7873
9634
|
.mp-dl-c .dl-t3{font-size:12.5px;letter-spacing:.2em;margin-top:3px}
|
|
7874
9635
|
.mp-dl-legend{display:flex;gap:12px;justify-content:center;font-size:12px;color:var(--muted);font-family:system-ui,sans-serif;margin-top:6px}
|
|
7875
9636
|
|
|
9637
|
+
.mp-curve{margin-top:8px;border:1px solid var(--line);border-radius:8px;background:var(--card);padding:10px 12px 8px}
|
|
9638
|
+
.mp-curve-h{display:flex;align-items:baseline;gap:8px;flex-wrap:wrap;font-size:12px;color:var(--label);font-family:system-ui,sans-serif;margin-bottom:4px}
|
|
9639
|
+
.mp-curve-h b{font-size:12.5px;color:var(--ink);font-weight:600}
|
|
9640
|
+
.mp-curve-h span{font-size:11.5px;color:var(--muted)}
|
|
9641
|
+
.mp-curve svg{display:block;width:100%;height:auto;overflow:visible}
|
|
9642
|
+
.mp-curve .cv-band{opacity:.09}
|
|
9643
|
+
.mp-curve .cv-band.b-good{fill:var(--lu)}
|
|
9644
|
+
.mp-curve .cv-band.b-bad{fill:var(--red)}
|
|
9645
|
+
.mp-curve .cv-band.b-flat{fill:var(--muted)}
|
|
9646
|
+
.mp-curve .cv-ax{stroke:var(--dimline);stroke-width:.6}
|
|
9647
|
+
.mp-curve .cv-ax.zero{stroke:var(--line);stroke-width:.9}
|
|
9648
|
+
.mp-curve .cv-lbl{fill:var(--muted);font-size:8px;font-family:system-ui,sans-serif}
|
|
9649
|
+
.mp-curve .cv-lbl.age{font-size:8.5px}
|
|
9650
|
+
.mp-curve .cv-dy{fill:none;stroke:var(--quan);stroke-width:1.6;stroke-linejoin:round}
|
|
9651
|
+
.mp-curve .cv-dyfill{fill:var(--quan);opacity:.10}
|
|
9652
|
+
.mp-curve .cv-ln{fill:none;stroke:var(--ke);stroke-width:1.4;stroke-linejoin:round;stroke-linecap:round}
|
|
9653
|
+
.mp-curve .cv-dot{r:2}
|
|
9654
|
+
.mp-curve .cv-dot.now{r:3.4;stroke:var(--card);stroke-width:1.2}
|
|
9655
|
+
.mp-curve .cv-legend{display:flex;gap:12px;flex-wrap:wrap;margin-top:5px;font-size:11px;color:var(--muted);font-family:system-ui,sans-serif}
|
|
9656
|
+
.mp-curve .cv-legend i{display:inline-block;width:14px;height:2px;vertical-align:3px;margin-right:4px}
|
|
9657
|
+
.mp-curve .cv-tip{font-size:11px;color:var(--muted);line-height:1.6;margin-top:4px}
|
|
9658
|
+
@media (max-width:520px){.mp-curve .cv-lbl.age{font-size:9px}}
|
|
7876
9659
|
.mp-tt{padding:16px}
|
|
7877
9660
|
.mp-tt-head{font-size:12.5px;color:var(--muted);margin-bottom:12px;letter-spacing:.04em}
|
|
7878
9661
|
.mp-tt-grid{display:grid;grid-template-columns:repeat(auto-fit,minmax(172px,1fr));gap:10px}
|
|
@@ -7994,6 +9777,9 @@ return { interpret, DIRECTIONS, DIRECTION_DEFAULT, BRIGHTNESS, flowOf, geocodeAd
|
|
|
7994
9777
|
.mp-fu-h{font-size:12px;font-weight:700;letter-spacing:.08em;color:var(--lu);margin-bottom:4px;font-family:system-ui,sans-serif}
|
|
7995
9778
|
.mp-fu.miss .mp-fu-h{color:var(--red)}
|
|
7996
9779
|
.mp-fu-ev{margin-top:5px;font-size:12px;color:var(--muted);font-family:system-ui,sans-serif}
|
|
9780
|
+
.mp-quick{margin:12px 16px;border:1px solid var(--line);border-radius:8px;background:var(--card);padding:12px 14px}
|
|
9781
|
+
.mp-quick-h{font-size:13px;color:var(--lu);margin-bottom:6px;font-family:system-ui,sans-serif}
|
|
9782
|
+
.mp-quick-pre{margin:0;white-space:pre-wrap;word-break:break-word;font-family:ui-monospace,SFMono-Regular,Menlo,Consolas,monospace;font-size:13px;line-height:1.9;color:var(--ink2)}
|
|
7997
9783
|
.mp-it-dis{padding:11px 16px;border-top:1px solid var(--line);background:var(--band);font-size:12px;line-height:1.75;color:var(--muted);font-family:system-ui,sans-serif}
|
|
7998
9784
|
|
|
7999
9785
|
@media (max-width:640px){
|
|
@@ -8040,9 +9826,9 @@ return { interpret, DIRECTIONS, DIRECTION_DEFAULT, BRIGHTNESS, flowOf, geocodeAd
|
|
|
8040
9826
|
.mp-hy-box{min-width:0;width:100%}
|
|
8041
9827
|
.mp-hy-row{grid-template-columns:14px 32px minmax(64px,1fr) 46px 42px 32px;gap:6px;font-size:12.5px}
|
|
8042
9828
|
.mp-hy-row.hy-plain{grid-template-columns:32px minmax(72px,1fr)}
|
|
8043
|
-
.mp-hex{width:
|
|
8044
|
-
.mp-hex.sm{min-width:
|
|
8045
|
-
.mp-hex.md{min-width:
|
|
9829
|
+
.mp-hex{width:100%;max-width:104px;min-width:56px;height:12px;flex:1 1 auto}
|
|
9830
|
+
.mp-hex.sm{max-width:74px;min-width:52px;height:10px}
|
|
9831
|
+
.mp-hex.md{max-width:88px;min-width:56px;height:11px}
|
|
8046
9832
|
.mp-mh{padding:10px}
|
|
8047
9833
|
.mp-mh-main{gap:12px}
|
|
8048
9834
|
.mp-hexbox.hb-mini{min-width:100px}
|
|
@@ -8105,9 +9891,9 @@ return { interpret, DIRECTIONS, DIRECTION_DEFAULT, BRIGHTNESS, flowOf, geocodeAd
|
|
|
8105
9891
|
.mp-hy-box{min-width:0;width:100%}
|
|
8106
9892
|
.mp-hy-row{grid-template-columns:14px 32px minmax(64px,1fr) 46px 42px 32px;gap:6px;font-size:12.5px}
|
|
8107
9893
|
.mp-hy-row.hy-plain{grid-template-columns:32px minmax(72px,1fr)}
|
|
8108
|
-
.mp-hex{width:
|
|
8109
|
-
.mp-hex.sm{min-width:
|
|
8110
|
-
.mp-hex.md{min-width:
|
|
9894
|
+
.mp-hex{width:100%;max-width:104px;min-width:56px;height:12px;flex:1 1 auto}
|
|
9895
|
+
.mp-hex.sm{max-width:74px;min-width:52px;height:10px}
|
|
9896
|
+
.mp-hex.md{max-width:88px;min-width:56px;height:11px}
|
|
8111
9897
|
.mp-mh{padding:10px}
|
|
8112
9898
|
.mp-mh-main{gap:12px}
|
|
8113
9899
|
.mp-hexbox.hb-mini{min-width:100px}
|
|
@@ -8171,7 +9957,44 @@ return { interpret, DIRECTIONS, DIRECTION_DEFAULT, BRIGHTNESS, flowOf, geocodeAd
|
|
|
8171
9957
|
|
|
8172
9958
|
const esc = (s) => String(s == null ? '' : s).replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"');
|
|
8173
9959
|
|
|
8174
|
-
const hexBar = (yang, moving, size) =>
|
|
9960
|
+
const hexBar = (yang, moving, size) => {
|
|
9961
|
+
const W = 104, H = 12, bh = 7, y = (H - bh) / 2, r = 1.6;
|
|
9962
|
+
const gap = 18, seg = Math.round((W - gap) / 2);
|
|
9963
|
+
const bars = yang
|
|
9964
|
+
? `<rect x="0" y="${y}" width="${W}" height="${bh}" rx="${r}" fill="currentColor"/>`
|
|
9965
|
+
: `<rect x="0" y="${y}" width="${seg}" height="${bh}" rx="${r}" fill="currentColor"/>`
|
|
9966
|
+
+ `<rect x="${seg + gap}" y="${y}" width="${W - seg - gap}" height="${bh}" rx="${r}" fill="currentColor"/>`;
|
|
9967
|
+
return `<svg class="mp-hex${moving ? ' hex-on' : ''}${size ? ' ' + size : ''}" viewBox="0 0 ${W} ${H}" preserveAspectRatio="none" aria-hidden="true" focusable="false">${bars}</svg>`;
|
|
9968
|
+
};
|
|
9969
|
+
|
|
9970
|
+
const WX_ICON = {
|
|
9971
|
+
木: 'M6 1.6V10.8M6 5.2 2.6 2.2M6 7.4 9.4 4.4',
|
|
9972
|
+
火: 'M6 1.4C7.6 3.6 9.2 5.4 9.2 7.6A3.2 3.2 0 0 1 2.8 7.6C2.8 5.4 4.4 3.6 6 1.4Z',
|
|
9973
|
+
土: 'M1.6 9.6h8.8M6 2.4v7.2M3.4 5.6h5.2',
|
|
9974
|
+
金: 'M6 1.2 10.6 6 6 10.8 1.4 6Z',
|
|
9975
|
+
水: 'M1.4 7.2C2.9 4.2 4.5 10.2 6 7.2 7.5 4.2 9.1 10.2 10.6 7.2',
|
|
9976
|
+
};
|
|
9977
|
+
const wxIcon = (el) => {
|
|
9978
|
+
const d = WX_ICON[el];
|
|
9979
|
+
if (!d) return '';
|
|
9980
|
+
return `<svg class="mp-wxi" viewBox="0 0 12 12" aria-hidden="true" focusable="false"><path d="${d}" fill="none" stroke="currentColor" stroke-width="1.3" stroke-linecap="round" stroke-linejoin="round"/></svg>`;
|
|
9981
|
+
};
|
|
9982
|
+
|
|
9983
|
+
const triGlyph = (name) => {
|
|
9984
|
+
const bits = (TRI_BITS[name] || [0, 0, 0]).slice().reverse();
|
|
9985
|
+
const rows = bits.map((b, i) => {
|
|
9986
|
+
const y = i * 4;
|
|
9987
|
+
return b
|
|
9988
|
+
? `<rect x="0" y="${y}" width="14" height="2" rx="0.6" fill="currentColor"/>`
|
|
9989
|
+
: `<rect x="0" y="${y}" width="5.6" height="2" rx="0.6" fill="currentColor"/>`
|
|
9990
|
+
+ `<rect x="8.4" y="${y}" width="5.6" height="2" rx="0.6" fill="currentColor"/>`;
|
|
9991
|
+
}).join('');
|
|
9992
|
+
return `<svg class="mp-trig" viewBox="0 0 14 10" aria-hidden="true" focusable="false">${rows}</svg>`;
|
|
9993
|
+
};
|
|
9994
|
+
|
|
9995
|
+
pp._hexBar = hexBar;
|
|
9996
|
+
pp._wxIcon = wxIcon;
|
|
9997
|
+
pp._triGlyph = triGlyph;
|
|
8175
9998
|
|
|
8176
9999
|
function starsHTML(stars, viewHua) {
|
|
8177
10000
|
if (!stars || !stars.length) return '';
|
|
@@ -8472,10 +10295,13 @@ return { interpret, DIRECTIONS, DIRECTION_DEFAULT, BRIGHTNESS, flowOf, geocodeAd
|
|
|
8472
10295
|
const sh = o.shiYing
|
|
8473
10296
|
? (l.position === o.shi ? '<span class="mp-hy-sy">世</span>' : l.position === o.ying ? '<span class="mp-hy-sy">应</span>' : '<span></span>')
|
|
8474
10297
|
: '<span></span>';
|
|
10298
|
+
const mv = l.moving
|
|
10299
|
+
? `<i class="mp-hy-mv" title="动爻">${yang ? '○' : '×'}</i>`
|
|
10300
|
+
: '<i class="mp-hy-mv"></i>';
|
|
8475
10301
|
return `<div class="mp-hy-row">
|
|
8476
10302
|
${sh}
|
|
8477
10303
|
<span class="mp-hy-nm">${l.name || POS_NAME[l.position - 1]}</span>
|
|
8478
|
-
|
|
10304
|
+
<span class="mp-hy-ln">${hexBar(yang, l.moving)}${mv}</span>
|
|
8479
10305
|
<span class="mp-hy-gz">${esc(l.ganzhi || '')}</span>
|
|
8480
10306
|
<span class="mp-hy-lq">${esc(l.liuqin || '')}</span>
|
|
8481
10307
|
<span class="mp-hy-ls">${esc(l.liushen || '')}</span>
|
|
@@ -8515,7 +10341,7 @@ return { interpret, DIRECTIONS, DIRECTION_DEFAULT, BRIGHTNESS, flowOf, geocodeAd
|
|
|
8515
10341
|
const lowerLines = benBits.slice(3);
|
|
8516
10342
|
const movingInUpper = r.moving >= 4;
|
|
8517
10343
|
const trib = (label, tri, lines, isBody) => `<div class="mp-trib">
|
|
8518
|
-
<div class="mp-trib-h">${label} ${tri}${TRI_EL(tri)}<span class="mp-tritag ${isBody ? 'tritag-ti' : 'tritag-yong'}">${isBody ? '体' : '用'}</span></div>
|
|
10344
|
+
<div class="mp-trib-h">${label} ${triGlyph(tri)}${tri}${TRI_EL(tri)}<span class="mp-tritag ${isBody ? 'tritag-ti' : 'tritag-yong'}">${isBody ? '体' : '用'}</span></div>
|
|
8519
10345
|
<div class="mp-hy" style="gap:5px">${hexRows(lines, { plain: true })}</div>
|
|
8520
10346
|
</div>`;
|
|
8521
10347
|
const mainBox = `<div class="mp-hexbox">
|
|
@@ -8739,6 +10565,106 @@ return { interpret, DIRECTIONS, DIRECTION_DEFAULT, BRIGHTNESS, flowOf, geocodeAd
|
|
|
8739
10565
|
</div>`;
|
|
8740
10566
|
};
|
|
8741
10567
|
|
|
10568
|
+
pp.curveHTML = function (r) {
|
|
10569
|
+
const dyList = (r && r.dayun && Array.isArray(r.dayun.list)) ? r.dayun.list : [];
|
|
10570
|
+
const lnList = (r && Array.isArray(r.liunian)) ? r.liunian : [];
|
|
10571
|
+
if (dyList.length < 2) return '';
|
|
10572
|
+
|
|
10573
|
+
const num = (v) => (typeof v === 'number' && isFinite(v) ? v : 0);
|
|
10574
|
+
const dyScore = (d) => (d.luck ? num(d.luck.score) : 0);
|
|
10575
|
+
|
|
10576
|
+
const VMAX = 3.2;
|
|
10577
|
+
const VMIN = -2.2;
|
|
10578
|
+
const startAge = num(r.dayun.startAge && r.dayun.startAge.years);
|
|
10579
|
+
const dyEnd = dyList.length
|
|
10580
|
+
? dyList[dyList.length - 1].startAge + 10
|
|
10581
|
+
: startAge + 80;
|
|
10582
|
+
const lnAges = lnList.map((l) => num(l.age)).filter((a) => a > 0);
|
|
10583
|
+
const a0 = Math.min(startAge, ...(lnAges.length ? lnAges : [startAge]));
|
|
10584
|
+
const a1 = Math.max(dyEnd, ...(lnAges.length ? lnAges : [dyEnd]));
|
|
10585
|
+
const span = Math.max(1, a1 - a0);
|
|
10586
|
+
|
|
10587
|
+
const W = 640, H = 190;
|
|
10588
|
+
const M = { t: 14, r: 16, b: 26, l: 30 };
|
|
10589
|
+
const pw = W - M.l - M.r;
|
|
10590
|
+
const ph = H - M.t - M.b;
|
|
10591
|
+
const X = (age) => M.l + ((age - a0) / span) * pw;
|
|
10592
|
+
const Y = (v) => M.t + ph - ((v - VMIN) / (VMAX - VMIN)) * ph;
|
|
10593
|
+
|
|
10594
|
+
const BANDS = [
|
|
10595
|
+
{ from: 1.2, to: VMAX, cls: 'b-good' },
|
|
10596
|
+
{ from: 0.55, to: 1.2, cls: 'b-good' },
|
|
10597
|
+
{ from: -0.8, to: 0.55, cls: 'b-flat' },
|
|
10598
|
+
{ from: -1.45, to: -0.8, cls: 'b-bad' },
|
|
10599
|
+
{ from: VMIN, to: -1.45, cls: 'b-bad' },
|
|
10600
|
+
];
|
|
10601
|
+
const bands = BANDS.map((b) =>
|
|
10602
|
+
`<rect class="cv-band ${b.cls}" x="${M.l}" y="${Y(b.to).toFixed(1)}" width="${pw}" height="${(Y(b.from) - Y(b.to)).toFixed(1)}"/>`).join('');
|
|
10603
|
+
|
|
10604
|
+
const stepW = (10 / span) * pw;
|
|
10605
|
+
const dyPts = [];
|
|
10606
|
+
dyList.forEach((d, i) => {
|
|
10607
|
+
const a = num(d.startAge);
|
|
10608
|
+
const v = dyScore(d);
|
|
10609
|
+
const xL = Math.max(M.l, X(a));
|
|
10610
|
+
const xR = Math.min(M.l + pw, X(a + 10));
|
|
10611
|
+
dyPts.push(`${xL.toFixed(1)},${Y(v).toFixed(1)}`);
|
|
10612
|
+
dyPts.push(`${xR.toFixed(1)},${Y(v).toFixed(1)}`);
|
|
10613
|
+
});
|
|
10614
|
+
const dyLine = dyPts.length
|
|
10615
|
+
? `<polyline class="cv-dy" points="${dyPts.join(' ')}"/>` : '';
|
|
10616
|
+
const dyFill = dyPts.length
|
|
10617
|
+
? `<polygon class="cv-dyfill" points="${M.l},${(M.t + ph).toFixed(1)} ${dyPts.join(' ')} ${(M.l + pw).toFixed(1)},${(M.t + ph).toFixed(1)}"/>` : '';
|
|
10618
|
+
|
|
10619
|
+
const lnPts = lnList
|
|
10620
|
+
.filter((l) => l.luck && typeof l.luck.score === 'number')
|
|
10621
|
+
.map((l) => ({ x: X(num(l.age)), y: Y(num(l.luck.score)), y0: num(l.year), gz: l.ganzhi, tier: l.luck.tier, v: num(l.luck.score), age: num(l.age) }));
|
|
10622
|
+
const lnLine = lnPts.length > 1
|
|
10623
|
+
? `<polyline class="cv-ln" points="${lnPts.map((p) => `${p.x.toFixed(1)},${p.y.toFixed(1)}`).join(' ')}"/>` : '';
|
|
10624
|
+
|
|
10625
|
+
const TIER_FILL = { 大吉: 'var(--lu)', 吉: 'var(--lu)', 平: 'var(--muted)', 忌: 'var(--red)', 大忌: 'var(--red)' };
|
|
10626
|
+
const curYear = lnPts.length ? Math.min(...lnPts.map((p) => p.y0)) : null;
|
|
10627
|
+
const dots = lnPts.map((p) => {
|
|
10628
|
+
const isNow = p.y0 === curYear;
|
|
10629
|
+
return `<circle class="cv-dot${isNow ? ' now' : ''}" cx="${p.x.toFixed(1)}" cy="${p.y.toFixed(1)}" fill="${TIER_FILL[p.tier] || 'var(--muted)'}"><title>${esc(`${p.y0}年(${p.age}岁)${p.gz} ${p.tier} ${p.v}`)}</title></circle>`;
|
|
10630
|
+
}).join('');
|
|
10631
|
+
|
|
10632
|
+
const axes = `<line class="cv-ax zero" x1="${M.l}" y1="${Y(0).toFixed(1)}" x2="${M.l + pw}" y2="${Y(0).toFixed(1)}"/>`
|
|
10633
|
+
+ `<line class="cv-ax" x1="${M.l}" y1="${M.t}" x2="${M.l}" y2="${M.t + ph}"/>`;
|
|
10634
|
+
const tickStep = span > 60 ? 10 : 5;
|
|
10635
|
+
const ticks = [];
|
|
10636
|
+
for (let a = Math.ceil(a0 / tickStep) * tickStep; a <= a1; a += tickStep) {
|
|
10637
|
+
ticks.push(`<text class="cv-lbl age" x="${X(a).toFixed(1)}" y="${(M.t + ph + 13).toFixed(1)}" text-anchor="middle">${a}岁</text>`);
|
|
10638
|
+
}
|
|
10639
|
+
const yLabels = `<text class="cv-lbl" x="${(M.l - 5).toFixed(1)}" y="${(Y(2.4) + 3).toFixed(1)}" text-anchor="end">吉</text>`
|
|
10640
|
+
+ `<text class="cv-lbl" x="${(M.l - 5).toFixed(1)}" y="${(Y(0) + 3).toFixed(1)}" text-anchor="end">平</text>`
|
|
10641
|
+
+ `<text class="cv-lbl" x="${(M.l - 5).toFixed(1)}" y="${(Y(-1.8) + 3).toFixed(1)}" text-anchor="end">忌</text>`;
|
|
10642
|
+
|
|
10643
|
+
const sorted = dyList.slice().sort((a, b) => dyScore(b) - dyScore(a));
|
|
10644
|
+
const best = sorted[0];
|
|
10645
|
+
const worst = sorted[sorted.length - 1];
|
|
10646
|
+
const mark = best && dyScore(best) > 0.55
|
|
10647
|
+
? `<text class="cv-lbl" x="${X(num(best.startAge) + 5).toFixed(1)}" y="${(Y(dyScore(best)) - 5).toFixed(1)}" text-anchor="middle">▲${best.startAge}岁 ${best.luck.tier}</text>` : '';
|
|
10648
|
+
const markLo = worst && dyScore(worst) < -0.8
|
|
10649
|
+
? `<text class="cv-lbl" x="${X(num(worst.startAge) + 5).toFixed(1)}" y="${(Y(dyScore(worst)) + 11).toFixed(1)}" text-anchor="middle">▼${worst.startAge}岁 ${worst.luck.tier}</text>` : '';
|
|
10650
|
+
|
|
10651
|
+
const legend = `<div class="cv-legend">
|
|
10652
|
+
<span><i style="background:var(--quan)"></i>大运(阶梯,一步十年)</span>
|
|
10653
|
+
<span><i style="background:var(--ke)"></i>流年(逐年折线)</span>
|
|
10654
|
+
<span>绿带=吉 灰带=平 红带=忌</span>
|
|
10655
|
+
</div>`;
|
|
10656
|
+
|
|
10657
|
+
const tip = `<div class="cv-tip">曲线只呈现引擎已给出的行运评分(${VMIN} ~ ${VMAX}),不预测具体事件;档位与依据见下方「大运 / 流年」各节。悬停看逐年详情。</div>`;
|
|
10658
|
+
|
|
10659
|
+
return `<div class="mp-curve">
|
|
10660
|
+
<div class="mp-curve-h"><b>人生运势曲线</b><span>${a0}–${a1} 岁 ${r.dayun.rule},${startAge} 岁起运</span></div>
|
|
10661
|
+
<svg viewBox="0 0 ${W} ${H}" role="img" aria-label="人生运势曲线:${a0} 到 ${a1} 岁的大运与流年评分走势">
|
|
10662
|
+
${bands}${axes}${dyFill}${dyLine}${lnLine}${dots}${ticks.join('')}${yLabels}${mark}${markLo}
|
|
10663
|
+
</svg>
|
|
10664
|
+
${legend}${tip}
|
|
10665
|
+
</div>`;
|
|
10666
|
+
};
|
|
10667
|
+
|
|
8742
10668
|
pp.baziHTML = function (r) {
|
|
8743
10669
|
const cols = [['year', '年柱'], ['month', '月柱'], ['day', '日柱'], ['hour', '时柱']];
|
|
8744
10670
|
const SHORT = { year: '年', month: '月', day: '日', hour: '时' };
|
|
@@ -8751,7 +10677,7 @@ return { interpret, DIRECTIONS, DIRECTION_DEFAULT, BRIGHTNESS, flowOf, geocodeAd
|
|
|
8751
10677
|
偏印: 'tg-yin', 正印: 'tg-yin',
|
|
8752
10678
|
};
|
|
8753
10679
|
const tg = (name) => `<span class="tg ${TG[name] || ''}">${name}</span>`;
|
|
8754
|
-
const el = (e) => `<span class="wx-${ELC[e] || 'earth'}">${e}</span>`;
|
|
10680
|
+
const el = (e) => `<span class="wx-${ELC[e] || 'earth'}">${wxIcon(e)}${e}</span>`;
|
|
8755
10681
|
const ssTag = (x) => `<span class="ss-s ${x.luck || 'neutral'}" title="${esc(`${x.cat} · ${x.via}查${x.target} | ${x.rule}`)}">${x.name}</span>`;
|
|
8756
10682
|
const line = (label, fn) => `<tr><td class="bz-lbl">${label}</td>${cols.map(([k]) => `<td data-c="${SHORT[k]}">${fn(r.pillars[k], k)}</td>`).join('')}</tr>`;
|
|
8757
10683
|
const head = `<thead><tr><th></th>${cols.map(([k, n]) => `<th${k === 'day' ? ' class="bz-hdr-day"' : ''}>${n}</th>`).join('')}</tr></thead>`;
|
|
@@ -8786,7 +10712,7 @@ return { interpret, DIRECTIONS, DIRECTION_DEFAULT, BRIGHTNESS, flowOf, geocodeAd
|
|
|
8786
10712
|
: '<span class="pp-dim">—</span>')),
|
|
8787
10713
|
wxRow,
|
|
8788
10714
|
].join('');
|
|
8789
|
-
const wx = Object.entries(r.wuxing).map(([k, v]) => `<span class="pp-chip wx-${ELC[k]}">${k} ${v}</span>`).join('');
|
|
10715
|
+
const wx = Object.entries(r.wuxing).map(([k, v]) => `<span class="pp-chip wx-${ELC[k]}">${wxIcon(k)}${k} ${v}</span>`).join('');
|
|
8790
10716
|
|
|
8791
10717
|
const fr = r.frame || {};
|
|
8792
10718
|
const tierCls = (t) => (t === '大吉' || t === '吉' ? 'ok' : t === '大忌' || t === '忌' ? 'warn' : '');
|
|
@@ -8824,7 +10750,7 @@ return { interpret, DIRECTIONS, DIRECTION_DEFAULT, BRIGHTNESS, flowOf, geocodeAd
|
|
|
8824
10750
|
${sect('格局', gj)}
|
|
8825
10751
|
${sect('用神', ys)}
|
|
8826
10752
|
${sect('五行与十神', `<div class="pp-chips">${wx}</div>${gods}`)}
|
|
8827
|
-
${sect('大运',
|
|
10753
|
+
${sect('大运', `${pp.curveHTML(r)}<div class="pp-chips">${dy}</div>`)}
|
|
8828
10754
|
${ln ? sect('流年', ln) : ''}
|
|
8829
10755
|
${r.palaces ? sect('胎元与命身宫', `<div class="pp-chips">
|
|
8830
10756
|
<span class="pp-chip" title="${r.palaces.taiyuan.note}">胎元 ${r.palaces.taiyuan.ganzhi}</span>
|
|
@@ -8925,6 +10851,21 @@ return { interpret, DIRECTIONS, DIRECTION_DEFAULT, BRIGHTNESS, flowOf, geocodeAd
|
|
|
8925
10851
|
return (html || '') + pp.interpretHTML(it, opt);
|
|
8926
10852
|
};
|
|
8927
10853
|
|
|
10854
|
+
pp.quickHTML = function (r) {
|
|
10855
|
+
if (!r) return '';
|
|
10856
|
+
const LABEL = {
|
|
10857
|
+
almanac: '黄历', zeri: '择日', chenggu: '称骨', xiaoliuren: '小六壬',
|
|
10858
|
+
star: '星座', numerology: '生命数字', trend: '人生 K 线', fengshui: '风水', name: '起名',
|
|
10859
|
+
};
|
|
10860
|
+
const title = LABEL[r.system] || '速查';
|
|
10861
|
+
const text = r.summary || '';
|
|
10862
|
+
const body = text ? `<pre class="mp-quick-pre">${esc(text)}</pre>` : '';
|
|
10863
|
+
return `<section class="mp-quick" aria-label="${esc(title)}结果">
|
|
10864
|
+
<div class="mp-quick-h"><b>${esc(title)}</b></div>
|
|
10865
|
+
${body}
|
|
10866
|
+
</section>`;
|
|
10867
|
+
};
|
|
10868
|
+
|
|
8928
10869
|
pp.summaryCardPNG = function (r, it) {
|
|
8929
10870
|
try {
|
|
8930
10871
|
if (typeof document === 'undefined' || !document.createElement) return '';
|
|
@@ -9184,6 +11125,15 @@ const TABS = [
|
|
|
9184
11125
|
{ k: 'qimen', label: '奇门', timeLabel: '起局时刻', hint: '奇门时家转盘:用起局时刻定节气、三元与局数;换局规则决定符头与节气如何对齐。' },
|
|
9185
11126
|
{ k: 'hepan', label: '合盘', timeLabel: '你的出生时刻', hint: '合盘按「日主 + 夫妻宫 + 五行互补」看两个人的相处与互补,需要双方出生信息。' },
|
|
9186
11127
|
{ k: 'tarot', label: '塔罗', timeLabel: '', hint: '塔罗不看时间,只记录抽牌时刻与你的提问。' },
|
|
11128
|
+
{ k: 'almanac', label: '黄历', timeLabel: '', hint: '黄历按公历日期查农历、干支、建除、宜忌与冲煞;只做本地计算。' },
|
|
11129
|
+
{ k: 'zeri', label: '择日', timeLabel: '', hint: '择日把区间内每天的建除宜忌与事项适配打分排序,并按属相排除冲煞日。' },
|
|
11130
|
+
{ k: 'chenggu', label: '称骨', timeLabel: '出生时刻', hint: '称骨按农历年、月、日、时的骨重求和;时辰必填。' },
|
|
11131
|
+
{ k: 'xiaoliuren', label: '小六壬', timeLabel: '起课时刻', hint: '小六壬用农历月/日/时掐指,也可用三个随机数起课。' },
|
|
11132
|
+
{ k: 'star', label: '星座', timeLabel: '', hint: '星座按公历日期给出太阳星座与当日基调;同日同星座结果固定。' },
|
|
11133
|
+
{ k: 'numerology', label: '生命数字', timeLabel: '', hint: '生命数字按公历生日归约出生命路径数、生日数与态度数。' },
|
|
11134
|
+
{ k: 'trend', label: '人生K线', timeLabel: '出生时刻', hint: '人生 K 线按大运与流年十神给逐年运势分,画成走势图。' },
|
|
11135
|
+
{ k: 'fengshui', label: '风水', timeLabel: '', hint: '风水看八宅命卦、大游年四吉四凶与流年九宫飞星;也可按坐向看门主灶倾向。' },
|
|
11136
|
+
{ k: 'name', label: '起名', timeLabel: '', hint: '起名按八字喜用匹配字库五行,可评估现有名字或生成候选名。' },
|
|
9187
11137
|
];
|
|
9188
11138
|
|
|
9189
11139
|
function pad(n) { return String(n).padStart(2, '0'); }
|
|
@@ -9197,6 +11147,9 @@ const TIME_KIND = {
|
|
|
9197
11147
|
ziwei: 'birth', bazi: 'birth', hepan: 'birth',
|
|
9198
11148
|
liuyao: 'cast', meihua: 'cast', daliuren: 'cast', qimen: 'cast',
|
|
9199
11149
|
tarot: 'none',
|
|
11150
|
+
almanac: 'date', zeri: 'date', star: 'date', numerology: 'date',
|
|
11151
|
+
chenggu: 'birth', trend: 'birth', fengshui: 'birth', name: 'birth',
|
|
11152
|
+
xiaoliuren: 'cast',
|
|
9200
11153
|
};
|
|
9201
11154
|
const SAMPLE = { date: '1990-05-12', time: '14:30' };
|
|
9202
11155
|
const kindOf = (t) => TIME_KIND[t] || 'none';
|
|
@@ -9226,6 +11179,19 @@ function App() {
|
|
|
9226
11179
|
const [fit, setFit] = useState(null);
|
|
9227
11180
|
const [rectEvents, setRectEvents] = useState([{ year: '2019', kind: 'career' }, { year: '2022', kind: 'love' }]);
|
|
9228
11181
|
const [rectRes, setRectRes] = useState(null);
|
|
11182
|
+
const [task, setTask] = useState('开业');
|
|
11183
|
+
const [days, setDays] = useState('30');
|
|
11184
|
+
const [shengXiao, setShengXiao] = useState('');
|
|
11185
|
+
const [xlrMode, setXlrMode] = useState('time');
|
|
11186
|
+
const [num1, setNum1] = useState(''); const [num2, setNum2] = useState(''); const [num3, setNum3] = useState('');
|
|
11187
|
+
const [toAge, setToAge] = useState('60');
|
|
11188
|
+
const [fsMode, setFsMode] = useState('natal');
|
|
11189
|
+
const [sitting, setSitting] = useState('子');
|
|
11190
|
+
const [facing, setFacing] = useState('午');
|
|
11191
|
+
const [nnMode, setNnMode] = useState('assess');
|
|
11192
|
+
const [surname, setSurname] = useState('李');
|
|
11193
|
+
const [givenName, setGivenName] = useState('');
|
|
11194
|
+
const [limitN, setLimitN] = useState('6');
|
|
9229
11195
|
const [onlineGeo, setOnlineGeo] = useState(false);
|
|
9230
11196
|
const [lines, setLines] = useState('');
|
|
9231
11197
|
const [result, setResult] = useState(null);
|
|
@@ -9246,10 +11212,13 @@ function App() {
|
|
|
9246
11212
|
const [manual, setManual] = useState({ birth: null, cast: null });
|
|
9247
11213
|
|
|
9248
11214
|
const meta = TABS.find((t) => t.k === tab) || TABS[0];
|
|
9249
|
-
const isBirth = tab === 'bazi' || tab === 'ziwei' || tab === 'hepan'
|
|
11215
|
+
const isBirth = tab === 'bazi' || tab === 'ziwei' || tab === 'hepan'
|
|
11216
|
+
|| tab === 'chenggu' || tab === 'trend' || tab === 'fengshui' || tab === 'name';
|
|
9250
11217
|
const timeKind = kindOf(tab);
|
|
9251
11218
|
const usesTimeInput = !(tab === 'meihua' && mhMode === 'number');
|
|
9252
|
-
const
|
|
11219
|
+
const showDate = tab !== 'tarot' && usesTimeInput;
|
|
11220
|
+
const showTime = showDate && timeKind !== 'date';
|
|
11221
|
+
const showDT = showTime;
|
|
9253
11222
|
|
|
9254
11223
|
const run = async (kind, override, modeOverride, over) => {
|
|
9255
11224
|
const k = kind || tab;
|
|
@@ -9282,6 +11251,42 @@ function App() {
|
|
|
9282
11251
|
else if (parsed.warning) setNotice(parsed.warning);
|
|
9283
11252
|
}
|
|
9284
11253
|
if (k === 'qimen') { args.juMethod = qimenRule; args.lateZiHourMode = qLateZi; }
|
|
11254
|
+
if (k === 'bazi') args.verifyPast = 6;
|
|
11255
|
+
if (k === 'zeri') {
|
|
11256
|
+
args.startDate = dt.date;
|
|
11257
|
+
args.task = task;
|
|
11258
|
+
args.days = Number(days) || 30;
|
|
11259
|
+
if (shengXiao) args.shengXiao = shengXiao;
|
|
11260
|
+
}
|
|
11261
|
+
if (k === 'xiaoliuren') {
|
|
11262
|
+
args.mode = xlrMode;
|
|
11263
|
+
if (xlrMode === 'number') {
|
|
11264
|
+
args.num1 = Number(num1) || undefined;
|
|
11265
|
+
args.num2 = Number(num2) || undefined;
|
|
11266
|
+
args.num3 = Number(num3) || undefined;
|
|
11267
|
+
} else {
|
|
11268
|
+
args.date = dt.date;
|
|
11269
|
+
const hh = Number(String(dt.time).split(':')[0]);
|
|
11270
|
+
args.hour = Number.isFinite(hh) ? hh : 12;
|
|
11271
|
+
}
|
|
11272
|
+
}
|
|
11273
|
+
if (k === 'trend') { args.gender = genderV; args.toAge = Number(toAge) || 60; args.trueSolar = trueSolar; args.lateZi = lateZi; }
|
|
11274
|
+
if (k === 'fengshui') {
|
|
11275
|
+
args.action = fsMode;
|
|
11276
|
+
if (fsMode === 'advise') { args.sitting = sitting; args.facing = facing; delete args.datetime; }
|
|
11277
|
+
else { args.gender = genderV; }
|
|
11278
|
+
}
|
|
11279
|
+
if (k === 'name') {
|
|
11280
|
+
args.action = nnMode;
|
|
11281
|
+
args.surname = surname.trim();
|
|
11282
|
+
if (nnMode === 'generate') {
|
|
11283
|
+
args.gender = genderV; args.limit = Number(limitN) || 6;
|
|
11284
|
+
args.trueSolar = trueSolar; args.lateZi = lateZi;
|
|
11285
|
+
} else {
|
|
11286
|
+
args.givenName = givenName.trim();
|
|
11287
|
+
delete args.datetime;
|
|
11288
|
+
}
|
|
11289
|
+
}
|
|
9285
11290
|
if (k === 'tarot') args.count = Number(count);
|
|
9286
11291
|
if (k === 'hepan') {
|
|
9287
11292
|
args.chart = hpChart;
|
|
@@ -9558,9 +11563,9 @@ function App() {
|
|
|
9558
11563
|
|
|
9559
11564
|
h('div', { key: 'head', className: 'mp-panel' },
|
|
9560
11565
|
h('div', { className: 'mp-row' },
|
|
9561
|
-
|
|
11566
|
+
showDate ? field(isBirth ? '出生日期(公历)' : timeKind === 'date' ? '日期(公历)' : '起卦日期(公历)',
|
|
9562
11567
|
h('input', { className: 'mp-input', type: 'date', value: date, onChange: (e) => editDate(e.target.value) })) : null,
|
|
9563
|
-
|
|
11568
|
+
showTime ? field(isBirth ? '出生时刻' : '起卦时刻',
|
|
9564
11569
|
h('input', { className: 'mp-input', type: 'time', value: time, onChange: (e) => editTime(e.target.value) })) : null,
|
|
9565
11570
|
isBirth ? field('性别', h('select', {
|
|
9566
11571
|
className: 'mp-input', value: gender, onChange: (e) => setGender(e.target.value),
|
|
@@ -9635,6 +11640,63 @@ function App() {
|
|
|
9635
11640
|
className: 'mp-input', value: changing, placeholder: '1-6',
|
|
9636
11641
|
onChange: (e) => setChanging(e.target.value),
|
|
9637
11642
|
})) : null,
|
|
11643
|
+
tab === 'zeri' ? field('所办事项', h('select', {
|
|
11644
|
+
className: 'mp-input', value: task, onChange: (e) => setTask(e.target.value),
|
|
11645
|
+
}, ['嫁娶', '订婚', '开业', '入宅', '搬家', '动土', '装修', '安葬', '出行', '祭祀', '签约', '求医', '纳财']
|
|
11646
|
+
.map((t) => h('option', { key: t, value: t }, t)))) : null,
|
|
11647
|
+
tab === 'zeri' ? field('看多少天', h('input', {
|
|
11648
|
+
className: 'mp-input', type: 'number', min: 1, max: 60, value: days,
|
|
11649
|
+
onChange: (e) => setDays(e.target.value),
|
|
11650
|
+
})) : null,
|
|
11651
|
+
tab === 'zeri' ? field('当事人属相(可空)', h('select', {
|
|
11652
|
+
className: 'mp-input', value: shengXiao, onChange: (e) => setShengXiao(e.target.value),
|
|
11653
|
+
}, h('option', { value: '' }, '不排除'), ['鼠', '牛', '虎', '兔', '龙', '蛇', '马', '羊', '猴', '鸡', '狗', '猪']
|
|
11654
|
+
.map((s) => h('option', { key: s, value: s }, s)))) : null,
|
|
11655
|
+
tab === 'xiaoliuren' ? field('起课方式', h('div', { className: 'mp-seg' },
|
|
11656
|
+
h('button', { className: xlrMode === 'time' ? 'on' : '', onClick: () => setXlrMode('time') }, '时间'),
|
|
11657
|
+
h('button', { className: xlrMode === 'number' ? 'on' : '', onClick: () => setXlrMode('number') }, '报数'))) : null,
|
|
11658
|
+
(tab === 'xiaoliuren' && xlrMode === 'number') ? field('第一个数', h('input', {
|
|
11659
|
+
className: 'mp-input', type: 'number', min: 1, max: 99, value: num1,
|
|
11660
|
+
onChange: (e) => setNum1(e.target.value),
|
|
11661
|
+
})) : null,
|
|
11662
|
+
(tab === 'xiaoliuren' && xlrMode === 'number') ? field('第二个数', h('input', {
|
|
11663
|
+
className: 'mp-input', type: 'number', min: 1, max: 99, value: num2,
|
|
11664
|
+
onChange: (e) => setNum2(e.target.value),
|
|
11665
|
+
})) : null,
|
|
11666
|
+
(tab === 'xiaoliuren' && xlrMode === 'number') ? field('第三个数', h('input', {
|
|
11667
|
+
className: 'mp-input', type: 'number', min: 1, max: 99, value: num3,
|
|
11668
|
+
onChange: (e) => setNum3(e.target.value),
|
|
11669
|
+
})) : null,
|
|
11670
|
+
tab === 'trend' ? field('算到几岁', h('input', {
|
|
11671
|
+
className: 'mp-input', type: 'number', min: 10, max: 100, value: toAge,
|
|
11672
|
+
onChange: (e) => setToAge(e.target.value),
|
|
11673
|
+
})) : null,
|
|
11674
|
+
tab === 'fengshui' ? field('看什么', h('div', { className: 'mp-seg' },
|
|
11675
|
+
h('button', { className: fsMode === 'natal' ? 'on' : '', onClick: () => setFsMode('natal') }, '命卦'),
|
|
11676
|
+
h('button', { className: fsMode === 'advise' ? 'on' : '', onClick: () => setFsMode('advise') }, '门主灶'))) : null,
|
|
11677
|
+
(tab === 'fengshui' && fsMode === 'advise') ? field('坐山(24 山)', h('input', {
|
|
11678
|
+
className: 'mp-input', value: sitting, placeholder: '如 子 / 乾',
|
|
11679
|
+
onChange: (e) => setSitting(e.target.value),
|
|
11680
|
+
})) : null,
|
|
11681
|
+
(tab === 'fengshui' && fsMode === 'advise') ? field('朝向(24 山)', h('input', {
|
|
11682
|
+
className: 'mp-input', value: facing, placeholder: '如 午 / 巽',
|
|
11683
|
+
onChange: (e) => setFacing(e.target.value),
|
|
11684
|
+
})) : null,
|
|
11685
|
+
tab === 'name' ? field('看什么', h('div', { className: 'mp-seg' },
|
|
11686
|
+
h('button', { className: nnMode === 'assess' ? 'on' : '', onClick: () => setNnMode('assess') }, '评名字'),
|
|
11687
|
+
h('button', { className: nnMode === 'generate' ? 'on' : '', onClick: () => setNnMode('generate') }, '生成候选'))) : null,
|
|
11688
|
+
tab === 'name' ? field('姓', h('input', {
|
|
11689
|
+
className: 'mp-input', value: surname, placeholder: '如 李',
|
|
11690
|
+
onChange: (e) => setSurname(e.target.value),
|
|
11691
|
+
})) : null,
|
|
11692
|
+
(tab === 'name' && nnMode === 'assess') ? field('名字', h('input', {
|
|
11693
|
+
className: 'mp-input', value: givenName, placeholder: '如 沐宸',
|
|
11694
|
+
onChange: (e) => setGivenName(e.target.value),
|
|
11695
|
+
})) : null,
|
|
11696
|
+
(tab === 'name' && nnMode === 'generate') ? field('候选个数', h('input', {
|
|
11697
|
+
className: 'mp-input', type: 'number', min: 1, max: 20, value: limitN,
|
|
11698
|
+
onChange: (e) => setLimitN(e.target.value),
|
|
11699
|
+
})) : null,
|
|
9638
11700
|
),
|
|
9639
11701
|
isBirth ? h('div', { className: 'mp-checks' },
|
|
9640
11702
|
h('label', { title: '按出生地经度把钟表时间换算成真太阳时' },
|
|
@@ -9705,7 +11767,8 @@ function App() {
|
|
|
9705
11767
|
: result.kind === 'qimen' ? (PP.qimenHTML(result.r) + PP.qimenFactsHTML(result.r))
|
|
9706
11768
|
: result.kind === 'hepan' ? (result.r.chart === 'ziwei' ? PP.ziweiHepanHTML(result.r) : PP.hepanHTML(result.r))
|
|
9707
11769
|
: result.kind === 'tarot' ? PP.tarotHTML(result.r)
|
|
9708
|
-
: PP.liuyaoHTML(result.r)
|
|
11770
|
+
: result.kind === 'liuyao' ? PP.liuyaoHTML(result.r)
|
|
11771
|
+
: PP.quickHTML(result.r),
|
|
9709
11772
|
result.it,
|
|
9710
11773
|
{ folded: defaultFolded() },
|
|
9711
11774
|
),
|