guanwei 1.1.1
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/LICENSE +21 -0
- package/README.en.md +202 -0
- package/README.md +247 -0
- package/dist/assets/index-CsOD5rSo.js +102 -0
- package/dist/assets/index-CsOD5rSo.js.map +1 -0
- package/dist/assets/index-DQx62TUr.css +1 -0
- package/dist/favicon.svg +12 -0
- package/dist/index.html +15 -0
- package/eslint.config.js +28 -0
- package/index.html +25 -0
- package/package.json +96 -0
- package/postcss.config.js +10 -0
- package/public/favicon.svg +12 -0
- package/scripts/guanwei +245 -0
- package/scripts/install.sh +79 -0
- package/scripts/release.sh +42 -0
- package/scripts/setup.bat +36 -0
- package/scripts/setup.sh +123 -0
- package/server/src/data/db.json +12059 -0
- package/server/src/data/guanwei.db +0 -0
- package/server/src/data/lifeContext.ts +423 -0
- package/server/src/data/spreads.ts +255 -0
- package/server/src/data/tarotCards.ts +2353 -0
- package/server/src/index.ts +71 -0
- package/server/src/routes/ai.ts +376 -0
- package/server/src/routes/divine.ts +140 -0
- package/server/src/routes/hour.ts +40 -0
- package/server/src/routes/tarot.ts +129 -0
- package/server/src/routes/users.ts +140 -0
- package/server/src/services/chartBrief.ts +131 -0
- package/server/src/services/divineStore.ts +188 -0
- package/server/src/services/hourInference.ts +150 -0
- package/server/src/services/llmProvider.ts +181 -0
- package/server/src/services/promptBuilder.ts +327 -0
- package/server/src/services/relativesCheck.ts +139 -0
- package/server/src/services/sixRelatives.ts +70 -0
- package/server/src/services/skills.ts +116 -0
- package/server/src/types/index.ts +104 -0
- package/server/src/utils/semanticAnalyzer.ts +555 -0
- package/server/src/utils/tarotEngine.ts +849 -0
- package/server/tsconfig.json +18 -0
- package/shared/core/data/classics.ts +276 -0
- package/shared/core/data/courses.ts +211 -0
- package/shared/core/data/duanyu.ts +148 -0
- package/shared/core/data/ganzhi.ts +53 -0
- package/shared/core/data/gua64.ts +93 -0
- package/shared/core/data/liuren.ts +18 -0
- package/shared/core/data/liuyao.ts +73 -0
- package/shared/core/data/qimen.ts +27 -0
- package/shared/core/data/region-full.ts +3 -0
- package/shared/core/data/region.ts +310 -0
- package/shared/core/data/tarotSpreads.ts +78 -0
- package/shared/core/data/xiaoliuren.ts +18 -0
- package/shared/core/data/ziwei.ts +97 -0
- package/shared/core/data/zodiac.ts +20 -0
- package/shared/core/engine/astrology.ts +169 -0
- package/shared/core/engine/bazi.ts +306 -0
- package/shared/core/engine/calendar.ts +127 -0
- package/shared/core/engine/daily.ts +133 -0
- package/shared/core/engine/liuren.ts +86 -0
- package/shared/core/engine/liuyao.ts +93 -0
- package/shared/core/engine/meihua.ts +87 -0
- package/shared/core/engine/qimen.ts +78 -0
- package/shared/core/engine/questionFit.ts +94 -0
- package/shared/core/engine/tarot.ts +69 -0
- package/shared/core/engine/trueSolarTime.ts +86 -0
- package/shared/core/engine/xiaoliuren.ts +19 -0
- package/shared/core/engine/ziwei.ts +217 -0
- package/shared/core/engine/ziweiInterpret.ts +161 -0
- package/shared/core/types.ts +206 -0
- package/src/App.tsx +69 -0
- package/src/arts/registry.ts +31 -0
- package/src/assets/react.svg +1 -0
- package/src/components/Backdrop.tsx +27 -0
- package/src/components/BambooArt.tsx +41 -0
- package/src/components/CalendarInput.tsx +131 -0
- package/src/components/DateInput.tsx +103 -0
- package/src/components/Disclaimer.tsx +12 -0
- package/src/components/Empty.tsx +8 -0
- package/src/components/ErrorBoundary.tsx +38 -0
- package/src/components/HomeSideAnchor.tsx +62 -0
- package/src/components/InkVein.tsx +10 -0
- package/src/components/LifeEventsInput.tsx +47 -0
- package/src/components/LocationPicker.tsx +117 -0
- package/src/components/Motes.tsx +65 -0
- package/src/components/Navigation.tsx +80 -0
- package/src/components/ProfileForm.tsx +40 -0
- package/src/components/ProfilePicker.tsx +40 -0
- package/src/components/QuestionFields.tsx +47 -0
- package/src/components/ReportView.tsx +211 -0
- package/src/components/ResultCard.tsx +40 -0
- package/src/components/SealButton.tsx +18 -0
- package/src/components/SiteFooter.tsx +17 -0
- package/src/components/SiteNav.tsx +103 -0
- package/src/components/SongDialog.tsx +64 -0
- package/src/components/SongSearchSelect.tsx +81 -0
- package/src/components/SongSelect.tsx +61 -0
- package/src/components/StarField.tsx +119 -0
- package/src/components/TarotCard.tsx +129 -0
- package/src/components/TimeShichenInput.tsx +29 -0
- package/src/components/arts/AstrologyArt.tsx +141 -0
- package/src/components/arts/BaziArt.tsx +269 -0
- package/src/components/arts/LiurenArt.tsx +87 -0
- package/src/components/arts/LiuyaoArt.tsx +111 -0
- package/src/components/arts/MeihuaArt.tsx +105 -0
- package/src/components/arts/QimenArt.tsx +86 -0
- package/src/components/arts/TarotArt.tsx +110 -0
- package/src/components/arts/XiaoliurenArt.tsx +93 -0
- package/src/components/arts/ZiweiArt.tsx +296 -0
- package/src/data/arts.ts +84 -0
- package/src/data/lifeContext.ts +423 -0
- package/src/data/sample-report.json +146 -0
- package/src/data/shichen.ts +21 -0
- package/src/data/spreads.ts +255 -0
- package/src/data/tarotCards.ts +2353 -0
- package/src/hooks/useAIInterpret.ts +76 -0
- package/src/hooks/useDivine.ts +39 -0
- package/src/hooks/useTheme.ts +29 -0
- package/src/index.css +190 -0
- package/src/lib/utils.ts +6 -0
- package/src/main.tsx +12 -0
- package/src/pages/AboutPage.tsx +25 -0
- package/src/pages/AcademyPage.tsx +128 -0
- package/src/pages/AstrologyPage.tsx +33 -0
- package/src/pages/AuthPage.tsx +263 -0
- package/src/pages/BaZiPage.tsx +33 -0
- package/src/pages/ClassicsPage.tsx +120 -0
- package/src/pages/DemoPage.tsx +161 -0
- package/src/pages/HistoryPage.tsx +133 -0
- package/src/pages/Home.tsx +179 -0
- package/src/pages/HomePage.tsx +174 -0
- package/src/pages/ModulePage.tsx +320 -0
- package/src/pages/SpreadEditor.tsx +230 -0
- package/src/pages/SpreadEditorPage.tsx +105 -0
- package/src/pages/SpreadLibrary.tsx +199 -0
- package/src/pages/SpreadLibraryPage.tsx +17 -0
- package/src/pages/TarotPage.tsx +738 -0
- package/src/pages/ZiWeiPage.tsx +33 -0
- package/src/services/api.ts +342 -0
- package/src/stores/useDivinationStore.ts +22 -0
- package/src/styles/song.css +1066 -0
- package/src/styles/tokens.css +60 -0
- package/src/types/index.ts +104 -0
- package/src/utils/astrologyCalc.ts +61 -0
- package/src/utils/comboEngine.ts +134 -0
- package/src/utils/dst.ts +24 -0
- package/src/utils/panTone.ts +206 -0
- package/src/utils/recordStore.ts +60 -0
- package/src/utils/semanticAnalyzer.ts +555 -0
- package/src/utils/spreadStorage.ts +32 -0
- package/src/utils/storage.ts +27 -0
- package/src/utils/tarotEngine.ts +849 -0
- package/src/utils/userStore.ts +234 -0
- package/src/vite-env.d.ts +1 -0
- package/tailwind.config.js +52 -0
- package/tsconfig.json +40 -0
- package/vite.config.ts +34 -0
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
/* ============================================================
|
|
2
|
+
观微 · 宋式美学设计系统 —— 设计令牌
|
|
3
|
+
以宋瓷之釉、宋画之留白、宋字之筋骨为法
|
|
4
|
+
============================================================ */
|
|
5
|
+
@import url('https://fonts.googleapis.com/css2?family=Noto+Serif+SC:wght@300;400;500;600;700&family=Ma+Shan+Zheng&family=Zhi+Mang+Xing&display=swap');
|
|
6
|
+
|
|
7
|
+
:root{
|
|
8
|
+
/* —— 色彩体系:低饱和 · 暖调宣纸底 —— */
|
|
9
|
+
--paper: #F5F1E7; /* 宣纸底 */
|
|
10
|
+
--paper-deep: #EDE8D9; /* 旧纸 */
|
|
11
|
+
--paper-dim: #E4DECC; /* 深纸 */
|
|
12
|
+
--ink: #37342C; /* 浓墨(主文字)*/
|
|
13
|
+
--ink-soft: #6B675C; /* 中墨(次级)*/
|
|
14
|
+
--ink-faint: #97917F; /* 淡墨(弱化)*/
|
|
15
|
+
--line: rgba(55,52,44,.16);
|
|
16
|
+
--line-soft: rgba(55,52,44,.10);
|
|
17
|
+
--celadon: #96A88F; /* 豆青 */
|
|
18
|
+
--celadon-deep: #5F7660; /* 深豆青(可交互强调)*/
|
|
19
|
+
--celadon-tint: #E3E8DC; /* 豆青淡染 */
|
|
20
|
+
--crab: #7C9792; /* 蟹壳青 */
|
|
21
|
+
--crab-tint: #DCE4E0;
|
|
22
|
+
--rice: #D9CDAC; /* 米黄 */
|
|
23
|
+
--rice-tint: #EDE5CE;
|
|
24
|
+
--cinnabar: #A5402D; /* 朱砂(仅印章/卦动/要点)*/
|
|
25
|
+
--cinnabar-deep:#8C3526;
|
|
26
|
+
--cinnabar-tint:#F3E3DC;
|
|
27
|
+
--white-paper: #FBF9F3; /* 新纸(浮层)*/
|
|
28
|
+
|
|
29
|
+
/* —— 字体 —— */
|
|
30
|
+
--font-song: "Noto Serif SC","Songti SC","Source Han Serif SC","SimSun","STSong",serif;
|
|
31
|
+
--font-kai: "Ma Shan Zheng","Kaiti SC","KaiTi","STKaiti","Noto Serif SC",serif;
|
|
32
|
+
--font-zm: "Zhi Mang Xing","Ma Shan Zheng","Kaiti SC",serif;
|
|
33
|
+
|
|
34
|
+
/* —— 字阶:1.25 大三度 —— */
|
|
35
|
+
--fs-xs: 0.75rem;
|
|
36
|
+
--fs-sm: 0.875rem;
|
|
37
|
+
--fs-base:1.05rem;
|
|
38
|
+
--fs-lg: 1.35rem;
|
|
39
|
+
--fs-xl: clamp(1.6rem, 3.2vw, 2.2rem);
|
|
40
|
+
--fs-2xl: clamp(2.2rem, 5.5vw, 3.6rem);
|
|
41
|
+
--fs-3xl: clamp(3rem, 9vw, 6rem);
|
|
42
|
+
--fs-display: clamp(4.2rem, 14vw, 10rem);
|
|
43
|
+
|
|
44
|
+
/* —— 间距:8pt 律 —— */
|
|
45
|
+
--sp-1:.5rem; --sp-2:1rem; --sp-3:1.5rem; --sp-4:2rem;
|
|
46
|
+
--sp-5:3rem; --sp-6:4.5rem; --sp-7:6.5rem; --sp-8:9rem;
|
|
47
|
+
|
|
48
|
+
/* —— 圆角:克制的折角,无大圆角 —— */
|
|
49
|
+
--r-sm:2px; --r-md:4px; --r-lg:8px;
|
|
50
|
+
|
|
51
|
+
/* —— 阴影:纸影,非悬浮 —— */
|
|
52
|
+
--shadow-paper: 0 1px 2px rgba(55,52,44,.06), 0 10px 30px -18px rgba(55,52,44,.18);
|
|
53
|
+
|
|
54
|
+
/* —— 动效 —— */
|
|
55
|
+
--ease-out: cubic-bezier(.22,1,.36,1);
|
|
56
|
+
--ease-ink: cubic-bezier(.5,0,.1,1);
|
|
57
|
+
--dur-fast: 220ms;
|
|
58
|
+
--dur-mid: 600ms;
|
|
59
|
+
--dur-slow: 1200ms;
|
|
60
|
+
}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
export interface TarotCard {
|
|
2
|
+
id: number;
|
|
3
|
+
name: string;
|
|
4
|
+
nameEn: string;
|
|
5
|
+
arcana: 'major' | 'minor';
|
|
6
|
+
suit?: 'wands' | 'cups' | 'swords' | 'pentacles';
|
|
7
|
+
number?: number;
|
|
8
|
+
keywords: string[];
|
|
9
|
+
meanings: {
|
|
10
|
+
upright: string;
|
|
11
|
+
reversed: string;
|
|
12
|
+
};
|
|
13
|
+
deepMeaning: {
|
|
14
|
+
coreTheme: string;
|
|
15
|
+
psychological: string;
|
|
16
|
+
spiritual: string;
|
|
17
|
+
shadow: string;
|
|
18
|
+
lifeAreas: {
|
|
19
|
+
love: string;
|
|
20
|
+
career: string;
|
|
21
|
+
wealth: string;
|
|
22
|
+
health: string;
|
|
23
|
+
growth: string;
|
|
24
|
+
};
|
|
25
|
+
advice: string;
|
|
26
|
+
warning: string;
|
|
27
|
+
};
|
|
28
|
+
imageSymbols: string[];
|
|
29
|
+
story: string;
|
|
30
|
+
astrology: {
|
|
31
|
+
zodiac?: string;
|
|
32
|
+
planet?: string;
|
|
33
|
+
element?: string;
|
|
34
|
+
};
|
|
35
|
+
kabbalah: {
|
|
36
|
+
path?: number;
|
|
37
|
+
sephirah?: string;
|
|
38
|
+
};
|
|
39
|
+
alchemy: {
|
|
40
|
+
stage?: string;
|
|
41
|
+
element?: string;
|
|
42
|
+
};
|
|
43
|
+
numerology: {
|
|
44
|
+
number: number;
|
|
45
|
+
meaning: string;
|
|
46
|
+
};
|
|
47
|
+
image: string;
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export interface SpreadPosition {
|
|
51
|
+
id: number;
|
|
52
|
+
name: string;
|
|
53
|
+
description: string;
|
|
54
|
+
x: number;
|
|
55
|
+
y: number;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export interface Spread {
|
|
59
|
+
id: string;
|
|
60
|
+
name: string;
|
|
61
|
+
description: string;
|
|
62
|
+
positions: SpreadPosition[];
|
|
63
|
+
category: 'basic' | 'advanced' | 'custom';
|
|
64
|
+
scene?: string[];
|
|
65
|
+
isCustom?: boolean;
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
export interface DrawnCard {
|
|
69
|
+
cardId: number;
|
|
70
|
+
isReversed: boolean;
|
|
71
|
+
positionId: number;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
export interface DivinationRecord {
|
|
75
|
+
id: string;
|
|
76
|
+
type: 'tarot' | 'combo' | 'ziwei' | 'bazi' | 'astrology';
|
|
77
|
+
question: string;
|
|
78
|
+
category: string;
|
|
79
|
+
spreadId?: string;
|
|
80
|
+
cards?: DrawnCard[];
|
|
81
|
+
comboMethod?: string;
|
|
82
|
+
comboResult?: string;
|
|
83
|
+
interpretation: string;
|
|
84
|
+
timestamp: number;
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export interface ComboResult {
|
|
88
|
+
method: 'xiaoliuren' | 'daliuren' | 'liuyao' | 'meihua' | 'qimen';
|
|
89
|
+
methodName: string;
|
|
90
|
+
result: string;
|
|
91
|
+
detail: string;
|
|
92
|
+
relationToTarot: string;
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
export type QuestionCategory = 'love' | 'career' | 'wealth' | 'health' | 'study' | 'family' | 'friendship' | 'spiritual' | 'travel' | 'general';
|
|
96
|
+
|
|
97
|
+
export interface TarotReading {
|
|
98
|
+
question: string;
|
|
99
|
+
category: QuestionCategory;
|
|
100
|
+
spreadId: string;
|
|
101
|
+
cards: DrawnCard[];
|
|
102
|
+
interpretation: string;
|
|
103
|
+
comboResult?: ComboResult;
|
|
104
|
+
}
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
export function getCurrentZodiac(): string {
|
|
2
|
+
const now = new Date();
|
|
3
|
+
const month = now.getMonth() + 1;
|
|
4
|
+
const day = now.getDate();
|
|
5
|
+
|
|
6
|
+
if ((month === 3 && day >= 21) || (month === 4 && day <= 19)) return '白羊座';
|
|
7
|
+
if ((month === 4 && day >= 20) || (month === 5 && day <= 20)) return '金牛座';
|
|
8
|
+
if ((month === 5 && day >= 21) || (month === 6 && day <= 21)) return '双子座';
|
|
9
|
+
if ((month === 6 && day >= 22) || (month === 7 && day <= 22)) return '巨蟹座';
|
|
10
|
+
if ((month === 7 && day >= 23) || (month === 8 && day <= 22)) return '狮子座';
|
|
11
|
+
if ((month === 8 && day >= 23) || (month === 9 && day <= 22)) return '处女座';
|
|
12
|
+
if ((month === 9 && day >= 23) || (month === 10 && day <= 23)) return '天秤座';
|
|
13
|
+
if ((month === 10 && day >= 24) || (month === 11 && day <= 22)) return '天蝎座';
|
|
14
|
+
if ((month === 11 && day >= 23) || (month === 12 && day <= 21)) return '射手座';
|
|
15
|
+
if ((month === 12 && day >= 22) || (month === 1 && day <= 19)) return '摩羯座';
|
|
16
|
+
if ((month === 1 && day >= 20) || (month === 2 && day <= 18)) return '水瓶座';
|
|
17
|
+
return '双鱼座';
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export function getMoonPhase(): string {
|
|
21
|
+
const now = new Date();
|
|
22
|
+
let year = now.getFullYear();
|
|
23
|
+
let month = now.getMonth() + 1;
|
|
24
|
+
const day = now.getDate();
|
|
25
|
+
|
|
26
|
+
let c, e, jd, b;
|
|
27
|
+
if (month < 3) {
|
|
28
|
+
year--;
|
|
29
|
+
month += 12;
|
|
30
|
+
}
|
|
31
|
+
c = 365.25 * year;
|
|
32
|
+
e = 30.6 * month;
|
|
33
|
+
jd = c + e + day - 694039.09;
|
|
34
|
+
jd /= 29.5305882;
|
|
35
|
+
b = parseInt(jd.toString());
|
|
36
|
+
jd -= b;
|
|
37
|
+
b = Math.round(jd * 8);
|
|
38
|
+
|
|
39
|
+
const phases = ['新月', '峨眉月', '上弦月', '盈凸月', '满月', '亏凸月', '下弦月', '残月'];
|
|
40
|
+
return phases[b] || '新月';
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
export function getDailyFortune(): string {
|
|
44
|
+
const fortunes = [
|
|
45
|
+
'今日星辰排列显示,你的直觉格外敏锐。信任内心的声音,它将指引你走向正确的方向。适合进行冥想和内省。',
|
|
46
|
+
'太阳与木星形成吉相位,带来扩张与成长的机会。勇敢地迈出舒适区,宇宙正在支持你冒险。',
|
|
47
|
+
'月亮进入你的情感宫位,情绪波动较大。与亲友共度时光能为你带来安慰,避免做出冲动的决定。',
|
|
48
|
+
'水星逆行结束,沟通与计划开始顺畅。搁置的项目可以重新启动,旧友可能带来好消息。',
|
|
49
|
+
'金星与火星的相位激发你的魅力与行动力。这是追求爱情或事业目标的理想时机,展现你的热情。',
|
|
50
|
+
'土星的影响提醒你脚踏实地。虽然进展缓慢,但每一步都坚实可靠。耐心是你的盟友。',
|
|
51
|
+
'天王星的突变能量可能带来意外的转折。保持开放和灵活,惊喜往往伪装成挑战出现。',
|
|
52
|
+
'海王星的迷雾笼罩,理想与现实之间的界限模糊。用艺术和创造力来表达你难以言说的感受。',
|
|
53
|
+
'冥王星的转化力量正在运作。放下旧有的模式,允许深刻的蜕变发生。死亡与重生是同一枚硬币的两面。',
|
|
54
|
+
'群星汇聚在你的事业宫,职业发展的机会显现。你的努力终于被看见,晋升或认可可能到来。',
|
|
55
|
+
'今日适合学习新知识和技能。心智格外清晰,阅读、写作和研究都能获得丰硕成果。',
|
|
56
|
+
'财务方面需要谨慎。避免冲动消费,回顾你的预算和理财计划。稳健的投资策略胜过冒险投机。',
|
|
57
|
+
];
|
|
58
|
+
|
|
59
|
+
const seed = new Date().getDate() + new Date().getMonth() * 31;
|
|
60
|
+
return fortunes[seed % fortunes.length];
|
|
61
|
+
}
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import type { ComboResult } from '@/types';
|
|
2
|
+
|
|
3
|
+
export function generateXiaoLiuRen(): ComboResult {
|
|
4
|
+
const now = new Date();
|
|
5
|
+
const month = now.getMonth() + 1;
|
|
6
|
+
const day = now.getDate();
|
|
7
|
+
const hour = now.getHours();
|
|
8
|
+
|
|
9
|
+
const liuShen = ['大安', '留连', '速喜', '赤口', '小吉', '空亡'];
|
|
10
|
+
const index = (month + day + hour) % 6;
|
|
11
|
+
const result = liuShen[index];
|
|
12
|
+
|
|
13
|
+
const meanings: Record<string, string> = {
|
|
14
|
+
'大安': '身不动时,五行属木,颜色青色,方位东方。临青龙,谋事主一、五、七。有静止、心安、吉祥之含义。',
|
|
15
|
+
'留连': '卒未归时,五行属水,颜色黑色,方位北方。临玄武,谋事主二、八、十。有暗昧不明、延迟、纠缠之含义。',
|
|
16
|
+
'速喜': '人即至时,五行属火,颜色红色,方位南方。临朱雀,谋事主三、六、九。有快速、喜庆、吉利之含义。',
|
|
17
|
+
'赤口': '官事凶时,五行属金,颜色白色,方位西方。临白虎,谋事主四、七、十。有不吉、惊恐、凶险之含义。',
|
|
18
|
+
'小吉': '人来喜时,五行属木,颜色绿色,方位东方。临六合,谋事主一、五、七。有和合、吉利之含义。',
|
|
19
|
+
'空亡': '音信稀时,五行属土,颜色黄色,方位中央。临勾陈,谋事主三、六、九。有虚无、失望、不利之含义。',
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
return {
|
|
23
|
+
method: 'xiaoliuren',
|
|
24
|
+
methodName: '小六壬',
|
|
25
|
+
result,
|
|
26
|
+
detail: meanings[result],
|
|
27
|
+
relationToTarot: `小六壬${result}与当前塔罗能量呼应。${result === '大安' || result === '速喜' || result === '小吉' ? '东方术数显示吉兆,与塔罗中正位牌的能量共振,宜积极行动。' : '术数提示需要谨慎,与塔罗中逆位牌的警示一致,建议内观等待时机。'}`,
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
export function generateLiuYao(): ComboResult {
|
|
32
|
+
const now = new Date();
|
|
33
|
+
const yaoResults = [];
|
|
34
|
+
|
|
35
|
+
// 简化六爻起卦:基于时间随机生成六爻
|
|
36
|
+
const seed = now.getTime();
|
|
37
|
+
for (let i = 0; i < 6; i++) {
|
|
38
|
+
const rand = (seed + i * 997) % 100;
|
|
39
|
+
if (rand < 25) yaoResults.push('老阴');
|
|
40
|
+
else if (rand < 50) yaoResults.push('少阴');
|
|
41
|
+
else if (rand < 75) yaoResults.push('少阳');
|
|
42
|
+
else yaoResults.push('老阳');
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
const hexagramNames: Record<string, string> = {
|
|
46
|
+
'666666': '坤为地', '666665': '地雷复', '666656': '地水师', '666655': '地泽临',
|
|
47
|
+
'665666': '雷地豫', '665665': '震为雷', '665656': '雷水解', '665655': '雷泽归妹',
|
|
48
|
+
'656666': '水地比', '656665': '水雷屯', '656656': '坎为水', '656655': '水泽节',
|
|
49
|
+
'655666': '泽地萃', '655665': '泽雷随', '655656': '泽水困', '655655': '兑为泽',
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
const guaKey = yaoResults.map(y => y.includes('阴') ? '6' : '5').join('');
|
|
53
|
+
const hexagram = hexagramNames[guaKey] || `卦象${guaKey}`;
|
|
54
|
+
|
|
55
|
+
return {
|
|
56
|
+
method: 'liuyao',
|
|
57
|
+
methodName: '六爻',
|
|
58
|
+
result: hexagram,
|
|
59
|
+
detail: `六爻卦象:${yaoResults.join('、')}。${hexagram}卦象揭示了当前事态的深层变化。动爻显示变化的契机,静爻代表稳定的基础。`,
|
|
60
|
+
relationToTarot: '六爻卦象与塔罗牌阵形成阴阳互补。卦中的动爻对应塔罗中的转变牌,提示变革的时机已经成熟。',
|
|
61
|
+
};
|
|
62
|
+
}
|
|
63
|
+
|
|
64
|
+
export function generateMeiHua(): ComboResult {
|
|
65
|
+
const now = new Date();
|
|
66
|
+
const hour = now.getHours();
|
|
67
|
+
const minute = now.getMinutes();
|
|
68
|
+
|
|
69
|
+
const shangGua = (hour + 1) % 8;
|
|
70
|
+
const xiaGua = (minute + 1) % 8;
|
|
71
|
+
|
|
72
|
+
const trigrams = ['乾', '兑', '离', '震', '巽', '坎', '艮', '坤'];
|
|
73
|
+
const shang = trigrams[shangGua];
|
|
74
|
+
const xia = trigrams[xiaGua];
|
|
75
|
+
|
|
76
|
+
const hexagramName = `${shang}${xia}`;
|
|
77
|
+
|
|
78
|
+
return {
|
|
79
|
+
method: 'meihua',
|
|
80
|
+
methodName: '梅花易数',
|
|
81
|
+
result: hexagramName,
|
|
82
|
+
detail: `以当前时间${hour}时${minute}分起卦,得上卦${shang}、下卦${xia},合成${hexagramName}卦。体用之分揭示了主客关系,卦气旺衰指示时机吉凶。`,
|
|
83
|
+
relationToTarot: '梅花易数的体用关系与塔罗牌阵的主牌-辅牌结构相呼应。体卦代表问卜者自身状态,用卦对应塔罗中代表外部环境的牌位。',
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
export function generateDaLiuRen(): ComboResult {
|
|
88
|
+
const now = new Date();
|
|
89
|
+
const hour = now.getHours();
|
|
90
|
+
|
|
91
|
+
const tianPan = ['子', '丑', '寅', '卯', '辰', '巳', '午', '未', '申', '酉', '戌', '亥'];
|
|
92
|
+
const diPan = ['贵', '蛇', '雀', '合', '勾', '龙', '空', '虎', '常', '玄', '阴', '后'];
|
|
93
|
+
|
|
94
|
+
const tian = tianPan[hour % 12];
|
|
95
|
+
const di = diPan[hour % 12];
|
|
96
|
+
|
|
97
|
+
return {
|
|
98
|
+
method: 'daliuren',
|
|
99
|
+
methodName: '大六壬',
|
|
100
|
+
result: `${tian}加${di}`,
|
|
101
|
+
detail: `大六壬以当前时辰起课,天盘${tian}加地盘${di}。四课三传揭示了事件的来龙去脉,三传(初传、中传、末传)指示事态发展的始、中、终三个阶段。`,
|
|
102
|
+
relationToTarot: '大六壬的四课三传与塔罗牌阵的时间线(过去-现在-未来)形成精密对应。初传对应过去牌位,末传呼应未来趋势。',
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
export function generateQiMen(): ComboResult {
|
|
107
|
+
const now = new Date();
|
|
108
|
+
const hour = now.getHours();
|
|
109
|
+
|
|
110
|
+
const baMen = ['休门', '生门', '伤门', '杜门', '景门', '死门', '惊门', '开门'];
|
|
111
|
+
const jiuXing = ['天蓬', '天任', '天冲', '天辅', '天英', '天芮', '天柱', '天心'];
|
|
112
|
+
|
|
113
|
+
const men = baMen[hour % 8];
|
|
114
|
+
const xing = jiuXing[hour % 9];
|
|
115
|
+
|
|
116
|
+
return {
|
|
117
|
+
method: 'qimen',
|
|
118
|
+
methodName: '奇门遁甲',
|
|
119
|
+
result: `${xing}星临${men}`,
|
|
120
|
+
detail: `奇门遁甲以当前时辰定局,${xing}星临${men}。奇门盘中的天盘九星、地盘八卦、人盘八门与神盘八神共同构建了时空模型,揭示了趋吉避凶的最佳时机与方位。`,
|
|
121
|
+
relationToTarot: '奇门遁甲的时空模型为塔罗解读增添了方位与时间的维度。塔罗揭示心灵的图景,奇门提供行动的指南,两者结合形成完整的决策系统。',
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
export function generateComboResult(method: string): ComboResult {
|
|
126
|
+
switch (method) {
|
|
127
|
+
case 'xiaoliuren': return generateXiaoLiuRen();
|
|
128
|
+
case 'liuyao': return generateLiuYao();
|
|
129
|
+
case 'meihua': return generateMeiHua();
|
|
130
|
+
case 'daliuren': return generateDaLiuRen();
|
|
131
|
+
case 'qimen': return generateQiMen();
|
|
132
|
+
default: return generateXiaoLiuRen();
|
|
133
|
+
}
|
|
134
|
+
}
|
package/src/utils/dst.ts
ADDED
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
// 中国夏令时(1986-1991,北京时间 02:00 起止)——排盘时提示用户按钟表时间填写
|
|
2
|
+
const DST_PERIODS: [number, number, number, number, number, number][] = [
|
|
3
|
+
[1986, 5, 4, 1986, 9, 14],
|
|
4
|
+
[1987, 4, 12, 1987, 9, 13],
|
|
5
|
+
[1988, 4, 10, 1988, 9, 11],
|
|
6
|
+
[1989, 4, 16, 1989, 9, 17],
|
|
7
|
+
[1990, 4, 15, 1990, 9, 16],
|
|
8
|
+
[1991, 4, 14, 1991, 9, 15],
|
|
9
|
+
];
|
|
10
|
+
|
|
11
|
+
/** 判断公历日期是否处于中国夏令时期间(此时钟表拨快 1 小时,排盘需按钟表时间填写) */
|
|
12
|
+
export function isChinaDSTDate(dateStr: string): boolean {
|
|
13
|
+
const m = /^(\d{4})-(\d{1,2})-(\d{1,2})$/.exec(dateStr || '');
|
|
14
|
+
if (!m) return false;
|
|
15
|
+
const y = Number(m[1]), mo = Number(m[2]), d = Number(m[3]);
|
|
16
|
+
if (y < 1986 || y > 1991) return false;
|
|
17
|
+
const t = new Date(y, mo - 1, d, 12, 0, 0).getTime();
|
|
18
|
+
for (const [sy, sm, sd, ey, em, ed] of DST_PERIODS) {
|
|
19
|
+
const start = new Date(sy, sm - 1, sd, 2, 0, 0).getTime();
|
|
20
|
+
const end = new Date(ey, em - 1, ed, 2, 0, 0).getTime();
|
|
21
|
+
if (t >= start && t <= end) return true;
|
|
22
|
+
}
|
|
23
|
+
return false;
|
|
24
|
+
}
|
|
@@ -0,0 +1,206 @@
|
|
|
1
|
+
// 盘面话术生成器:按排盘数据(五行/旺衰/十神/用神/大运/纳音/季节)组合出每盘不同的解读话术
|
|
2
|
+
// 确定性输出:同一盘面每次生成一致(不引入随机),不同盘面因数据不同而话术不同
|
|
3
|
+
import type { BaziResult, ZiweiResult } from '@core/types';
|
|
4
|
+
|
|
5
|
+
// ─── 五行意象 ───
|
|
6
|
+
const WX_IMAGE: Record<string, { img: string; nature: string; realm: string }> = {
|
|
7
|
+
木: { img: '参天之木', nature: '向上伸展、生生不息', realm: '东方 · 青绿 · 肝胆' },
|
|
8
|
+
火: { img: '燎原之火', nature: '热烈明亮、光明磊落', realm: '南方 · 赤红 · 心肠' },
|
|
9
|
+
土: { img: '厚重之土', nature: '包容承载、踏实守信', realm: '中央 · 黄褐 · 脾胃' },
|
|
10
|
+
金: { img: '百炼之金', nature: '坚刚果断、肃杀分明', realm: '西方 · 素白 · 肺腑' },
|
|
11
|
+
水: { img: '奔流之水', nature: '灵动善变、智谋深远', realm: '北方 · 玄黑 · 肾水' },
|
|
12
|
+
};
|
|
13
|
+
const WX_ORDER = ['木', '火', '土', '金', '水'];
|
|
14
|
+
|
|
15
|
+
// ─── 月支 × 季节 ───
|
|
16
|
+
const MONTH_SEASON: Record<string, { season: string; note: string }> = {
|
|
17
|
+
寅: { season: '孟春', note: '木气初生,万物发陈' },
|
|
18
|
+
卯: { season: '仲春', note: '木气正盛,花木繁茂' },
|
|
19
|
+
辰: { season: '季春', note: '土气渐旺,湿土培木' },
|
|
20
|
+
巳: { season: '孟夏', note: '火气初升,炎气渐盛' },
|
|
21
|
+
午: { season: '仲夏', note: '火气当令,烈日当空' },
|
|
22
|
+
未: { season: '季夏', note: '土气主事,火土相生' },
|
|
23
|
+
申: { season: '孟秋', note: '金气初肃,万物收敛' },
|
|
24
|
+
酉: { season: '仲秋', note: '金气正锐,白露为霜' },
|
|
25
|
+
戌: { season: '季秋', note: '土气回旺,金火入库' },
|
|
26
|
+
亥: { season: '孟冬', note: '水气初生,寒流暗涌' },
|
|
27
|
+
子: { season: '仲冬', note: '水气当令,寒凝之季' },
|
|
28
|
+
丑: { season: '季冬', note: '土气主事,腊月土冻' },
|
|
29
|
+
};
|
|
30
|
+
|
|
31
|
+
const STRENGTH_NOTE: Record<string, string> = {
|
|
32
|
+
身强: '根基厚实、气机充盈',
|
|
33
|
+
身弱: '气机偏弱、喜生扶帮身',
|
|
34
|
+
中和: '五行流转、不偏不倚',
|
|
35
|
+
};
|
|
36
|
+
const STRENGTH_ADVICE: Record<string, string> = {
|
|
37
|
+
身强: '宜泄宜克,向外施展方能舒展',
|
|
38
|
+
身弱: '宜生宜扶,蓄力而后发',
|
|
39
|
+
中和: '顺势而为,动静皆宜',
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
// ─── 十神 → 性格倾向(含藏干加权)───
|
|
43
|
+
const TENDENCY_POOL: { keys: string[]; trait: string; descs: string[] }[] = [
|
|
44
|
+
{
|
|
45
|
+
keys: ['伤官', '食神'],
|
|
46
|
+
trait: '才情外放',
|
|
47
|
+
descs: [
|
|
48
|
+
'食伤泄秀之人,点子多、手脚快,靠本事吃饭,最受不了按部就班的死规矩——适合做需要创造力的行当,把才华落到作品上。',
|
|
49
|
+
'食伤旺的人表达欲强,学东西快,但也容易不耐烦、眼高手低,须防话比事多,把聪明用在刀刃上。',
|
|
50
|
+
],
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
keys: ['正印', '偏印'],
|
|
54
|
+
trait: '内秀深思',
|
|
55
|
+
descs: [
|
|
56
|
+
'印星贴身之人,心思细、记性好,喜欢想明白了再动,重学识涵养,适合深耕专业或与文化、文书相关之事。',
|
|
57
|
+
'印旺的人安全感来自内在储备,遇事习惯先查资料、问老规矩,节奏偏稳,但也要防想太多而行动迟缓。',
|
|
58
|
+
],
|
|
59
|
+
},
|
|
60
|
+
{
|
|
61
|
+
keys: ['正官', '七杀'],
|
|
62
|
+
trait: '自律担当',
|
|
63
|
+
descs: [
|
|
64
|
+
'官杀在命之人,自我要求高、守规矩、有担当,遇事敢扛,适合有章法有目标的赛道,把约束变成铠甲。',
|
|
65
|
+
'官杀旺的人骨子里要强,见不得自己掉队,压力越大越能顶——但弦绷太紧易生内耗,须学会放自己一马。',
|
|
66
|
+
],
|
|
67
|
+
},
|
|
68
|
+
{
|
|
69
|
+
keys: ['正财', '偏财'],
|
|
70
|
+
trait: '务实经营',
|
|
71
|
+
descs: [
|
|
72
|
+
'财星入命之人,对现实账目门儿清,务实不空谈,擅长把资源盘活,做生意、管项目都有天然的手感。',
|
|
73
|
+
'财旺的人重实际、讲回报,得失心也重些,钱要赚得稳,更要赚得安心,勿为一时之利透支长远。',
|
|
74
|
+
],
|
|
75
|
+
},
|
|
76
|
+
{
|
|
77
|
+
keys: ['比肩', '劫财'],
|
|
78
|
+
trait: '自立要强',
|
|
79
|
+
descs: [
|
|
80
|
+
'比劫同气之人,凡事靠自己,不轻易开口求人,性子直、讲义气,适合独当一面的角色,但也易因硬扛而辛苦。',
|
|
81
|
+
'比肩旺的人主见强、不服输,和人共事容易起摩擦,若能学会借力与分工,路会宽很多。',
|
|
82
|
+
],
|
|
83
|
+
},
|
|
84
|
+
];
|
|
85
|
+
|
|
86
|
+
// ─── 五行旺缺补益(修身养性取向)───
|
|
87
|
+
const WX_BENEFIT: Record<string, string> = {
|
|
88
|
+
木: '东方、青绿色系、晨间运动与肝胆养护',
|
|
89
|
+
火: '南方、赤红色系、日间活动与心气舒展',
|
|
90
|
+
土: '居所稳固、黄褐色系、脾胃调理与规律作息',
|
|
91
|
+
金: '西方、素白色系、呼吸吐纳与肺腑清净',
|
|
92
|
+
水: '北方、玄黑色系、静坐冥思与肾水滋养',
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
function nayinWx(nayin: string): string | null {
|
|
96
|
+
const m = /[金木水火土]$/.exec(nayin || '');
|
|
97
|
+
return m ? m[0] : null;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// 具体十神名 → 类名(与引擎喜忌列表对齐:印/比劫/食伤/财/官杀)
|
|
101
|
+
function tenClass(name: string): string {
|
|
102
|
+
if (['正印', '偏印'].includes(name)) return '印';
|
|
103
|
+
if (['比肩', '劫财'].includes(name)) return '比劫';
|
|
104
|
+
if (['食神', '伤官'].includes(name)) return '食伤';
|
|
105
|
+
if (['正财', '偏财'].includes(name)) return '财';
|
|
106
|
+
if (['正官', '七杀'].includes(name)) return '官杀';
|
|
107
|
+
return name;
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
// ─── 八字话术 ───
|
|
111
|
+
export function baziTone(r: BaziResult): { headline: string; overview: string; character: string[]; wuxingNote: string; dayunNote: string; nayinNote: string } {
|
|
112
|
+
const dayWx = r.dayGanWx;
|
|
113
|
+
const img = WX_IMAGE[dayWx];
|
|
114
|
+
const month = MONTH_SEASON[r.monthGZ[1]] || { season: '', note: '' };
|
|
115
|
+
|
|
116
|
+
// 1. 命局气质标题句:日主意象 × 季节处境 × 旺衰定调
|
|
117
|
+
const headline = `日主${r.dayGan}${dayWx},如${img.img}——${img.nature};生于${month.season}(${r.monthGZ[1]}月),${month.note},${r.strength === '身强' ? '得势而旺' : r.strength === '身弱' ? '气机有待生扶' : '气机中和'}。`;
|
|
118
|
+
|
|
119
|
+
// 2. 总述:旺衰 + 用神 + 调候
|
|
120
|
+
const overview = `${STRENGTH_NOTE[r.strength]},${STRENGTH_ADVICE[r.strength]}。以${r.yongshen.wx}(${r.yongshen.shishen})为用神,喜${r.yongshen.xi.join('、')},忌${r.yongshen.ji.join('、')};${r.yongshen.tiaohou}。`;
|
|
121
|
+
|
|
122
|
+
// 3. 性格倾向:统计天干+藏干十神权重,取前 2-3
|
|
123
|
+
const w: Record<string, number> = {};
|
|
124
|
+
r.shishen.forEach(s => { w[s.name] = (w[s.name] || 0) + 1; });
|
|
125
|
+
r.canggan.forEach(cg => cg.gans.forEach(g => { w[g.shishen] = (w[g.shishen] || 0) + (g.qi === '本气' ? 2 : 1); }));
|
|
126
|
+
const picked = TENDENCY_POOL
|
|
127
|
+
.map(pool => ({ pool, weight: pool.keys.reduce((s, k) => s + (w[k] || 0), 0) }))
|
|
128
|
+
.filter(x => x.weight > 0)
|
|
129
|
+
.sort((a, b) => b.weight - a.weight)
|
|
130
|
+
.slice(0, 3);
|
|
131
|
+
// 句式轮换:按日主五行索引挑 desc 变体,避免同类型盘面话术完全一致
|
|
132
|
+
const wxIdx = WX_ORDER.indexOf(dayWx);
|
|
133
|
+
const character = picked.map((x, i) => `${x.pool.trait}——${x.pool.descs[(wxIdx + i) % x.pool.descs.length]}`);
|
|
134
|
+
|
|
135
|
+
// 4. 五行旺缺
|
|
136
|
+
const ranked = [...WX_ORDER].sort((a, b) => (r.wxWeighted[b] || 0) - (r.wxWeighted[a] || 0));
|
|
137
|
+
const most = ranked[0], least = ranked[ranked.length - 1];
|
|
138
|
+
const mostV = r.wxWeighted[most] || 0, leastV = r.wxWeighted[least] || 0;
|
|
139
|
+
let wuxingNote = '';
|
|
140
|
+
if (mostV - leastV >= 3) {
|
|
141
|
+
const ke = WX_ORDER[(WX_ORDER.indexOf(most) + 2) % 5]; // 我克者(制衡)
|
|
142
|
+
const xie = WX_ORDER[(WX_ORDER.indexOf(most) + 1) % 5]; // 我生者(泄秀)
|
|
143
|
+
wuxingNote = `盘中${most}气独旺(${mostV}),旺则须疏——宜以${ke}制衡、或以${xie}泄秀,方得流通;${least}气偏弱(${leastV}),可于${WX_BENEFIT[least]}中缓缓补益,修身养性、仅供参考。`;
|
|
144
|
+
} else if (mostV - leastV >= 1) {
|
|
145
|
+
wuxingNote = `盘中${most}气稍盛、${least}气稍弱,五行大体流通,宜顺其势:近${WX_BENEFIT[least]},远${WX_BENEFIT[most]}之过亢,动静有度。`;
|
|
146
|
+
} else {
|
|
147
|
+
wuxingNote = '五行分布均衡,气机流通顺畅,无大偏颇——顺势而为即是上策。';
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
// 5. 行运指引:当前大运 × 喜忌(十神类名对齐)
|
|
151
|
+
const now = new Date().getFullYear();
|
|
152
|
+
const cur = r.dayun.find(d => now >= d.startYear && now <= d.endYear);
|
|
153
|
+
let dayunNote = '';
|
|
154
|
+
if (cur) {
|
|
155
|
+
const cls = tenClass(cur.ganShishen);
|
|
156
|
+
const isXi = r.yongshen.xi.includes(cls);
|
|
157
|
+
const isJi = r.yongshen.ji.includes(cls);
|
|
158
|
+
if (isJi) {
|
|
159
|
+
dayunNote = `今行${cur.gz}${cur.ganShishen}运(${cur.startYear}-${cur.endYear}),为忌神之运——宜守成、忌冒进,防破耗与无谓竞争,把精力留给真正重要之事。`;
|
|
160
|
+
} else if (isXi) {
|
|
161
|
+
dayunNote = `今行${cur.gz}${cur.ganShishen}运(${cur.startYear}-${cur.endYear}),喜神当令——正是进取之时,可放心把计划往前推,善用贵人与资源。`;
|
|
162
|
+
} else {
|
|
163
|
+
dayunNote = `今行${cur.gz}${cur.ganShishen}运(${cur.startYear}-${cur.endYear}),吉凶相参——稳中求进,进可攻退可守,最忌临事犹豫、两头落空。`;
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// 6. 纳音意象
|
|
168
|
+
const nw = nayinWx(r.nayin);
|
|
169
|
+
const nayinNote = nw && WX_IMAGE[nw]
|
|
170
|
+
? `年命纳音${r.nayin},其性属${nw},如${WX_IMAGE[nw].img}——${WX_IMAGE[nw].nature},暗合一生之底色。`
|
|
171
|
+
: `年命纳音${r.nayin},如琴有调,暗合一生之音律。`;
|
|
172
|
+
|
|
173
|
+
return { headline, overview, character, wuxingNote, dayunNote, nayinNote };
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
// ─── 紫微命宫点睛 ───
|
|
177
|
+
export function ziweiTone(r: ZiweiResult): { mingNote: string; liunianNote: string } {
|
|
178
|
+
const DIZHI = ['寅', '卯', '辰', '巳', '午', '未', '申', '酉', '戌', '亥', '子', '丑'];
|
|
179
|
+
// 命宫主星:zwStars 中落命宫位的星(值=ming 的星)
|
|
180
|
+
const mingStars = Object.entries(r.zwStars).filter(([, p]) => p === r.ming).map(([s]) => s);
|
|
181
|
+
const mingNote = mingStars.length
|
|
182
|
+
? `命宫安于${DIZHI[r.ming]},主星${mingStars.join('、')}坐守——${mingStars.map(s => starBrief(s)).join(';')}为一生行运之枢。`
|
|
183
|
+
: `命宫安于${DIZHI[r.ming]}(主星未临,借对宫之星论之)——为一生行运之枢。`;
|
|
184
|
+
const liunianNote = `${r.nominalAge ?? ''}虚岁流年命宫落${r.liunianPalaceName || DIZHI[r.liunianIdx ?? 0]},流年主星${r.liunianStars?.join('、') || '未临'}——流年之机,观此宫动静。`;
|
|
185
|
+
return { mingNote, liunianNote };
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
const STAR_BRIEF: Record<string, string> = {
|
|
189
|
+
紫微: '帝星入命,气度格局不凡,好揽事、有担当',
|
|
190
|
+
天机: '机变聪明,心思活络,善谋不善断',
|
|
191
|
+
太阳: '光明磊落,热心助人,贵在坦荡',
|
|
192
|
+
武曲: '刚毅务实,财星得用,宜事不宜闲',
|
|
193
|
+
天同: '福星随和,与世无争,贵人缘佳',
|
|
194
|
+
廉贞: '次桃花主事,才情与锋芒并具',
|
|
195
|
+
天府: '库星坐命,稳重守成,善于经营',
|
|
196
|
+
太阴: '月华照命,细腻内敛,重情重家',
|
|
197
|
+
贪狼: '欲望与才艺之星,圆融善交际',
|
|
198
|
+
巨门: '口舌与思辨之星,能言善断亦招是非',
|
|
199
|
+
天相: '辅弼之星,识大体、善协调',
|
|
200
|
+
天梁: '荫星照命,逢凶化吉,长辈缘深',
|
|
201
|
+
七杀: '将星入命,杀伐果断,先苦后成',
|
|
202
|
+
破军: '破旧立新之星,变动中见机遇',
|
|
203
|
+
};
|
|
204
|
+
function starBrief(s: string): string {
|
|
205
|
+
return STAR_BRIEF[s] || `${s}主事`;
|
|
206
|
+
}
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
// 占问记录存储(localStorage,上限 200 条 LRU 淘汰,按用户隔离)
|
|
2
|
+
import { recordsKey, currentUser, pushRecordsToServer } from './userStore';
|
|
3
|
+
|
|
4
|
+
export interface DivinationRecord {
|
|
5
|
+
id: string;
|
|
6
|
+
artId: string;
|
|
7
|
+
createdAt: number;
|
|
8
|
+
result: unknown;
|
|
9
|
+
location?: { lng: number; lat: number; province?: string; city?: string; district?: string };
|
|
10
|
+
profileId?: string; // 档案标识(main / 示例档案 id)
|
|
11
|
+
profile?: { birthDate?: string; birthTime?: string; gender?: string; location?: unknown } | null;
|
|
12
|
+
inputs?: unknown;
|
|
13
|
+
question?: string;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const MAX = 200;
|
|
17
|
+
|
|
18
|
+
export function getRecords(): DivinationRecord[] {
|
|
19
|
+
try {
|
|
20
|
+
const data = localStorage.getItem(recordsKey());
|
|
21
|
+
return data ? JSON.parse(data) : [];
|
|
22
|
+
} catch {
|
|
23
|
+
return [];
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export function saveRecord(rec: Omit<DivinationRecord, 'id'>): DivinationRecord {
|
|
28
|
+
const records = getRecords();
|
|
29
|
+
const full: DivinationRecord = { ...rec, id: rec.artId + '-' + rec.createdAt };
|
|
30
|
+
records.unshift(full);
|
|
31
|
+
localStorage.setItem(recordsKey(), JSON.stringify(records.slice(0, MAX)));
|
|
32
|
+
// 云同步(登录用户)
|
|
33
|
+
const u = currentUser();
|
|
34
|
+
if (u) { pushRecordsToServer(u.username, records.slice(0, MAX)).catch(() => {}); }
|
|
35
|
+
return full;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
export function deleteRecord(id: string): void {
|
|
39
|
+
localStorage.setItem(recordsKey(), JSON.stringify(getRecords().filter(r => r.id !== id)));
|
|
40
|
+
}
|
|
41
|
+
|
|
42
|
+
export function clearRecords(): void {
|
|
43
|
+
localStorage.removeItem(recordsKey());
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export function getRecord(id: string): DivinationRecord | undefined {
|
|
47
|
+
return getRecords().find(r => r.id === id);
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
export async function syncRecordsFromServer(): Promise<void> {
|
|
51
|
+
const u = currentUser();
|
|
52
|
+
if (!u) return;
|
|
53
|
+
try {
|
|
54
|
+
const { pullRecordsFromServer } = await import('./userStore');
|
|
55
|
+
const remote = await pullRecordsFromServer(u.username);
|
|
56
|
+
if (remote && Array.isArray(remote) && remote.length > 0) {
|
|
57
|
+
localStorage.setItem(recordsKey(), JSON.stringify(remote.slice(0, MAX)));
|
|
58
|
+
}
|
|
59
|
+
} catch { /* ignore */ }
|
|
60
|
+
}
|