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.
Files changed (157) hide show
  1. package/LICENSE +21 -0
  2. package/README.en.md +202 -0
  3. package/README.md +247 -0
  4. package/dist/assets/index-CsOD5rSo.js +102 -0
  5. package/dist/assets/index-CsOD5rSo.js.map +1 -0
  6. package/dist/assets/index-DQx62TUr.css +1 -0
  7. package/dist/favicon.svg +12 -0
  8. package/dist/index.html +15 -0
  9. package/eslint.config.js +28 -0
  10. package/index.html +25 -0
  11. package/package.json +96 -0
  12. package/postcss.config.js +10 -0
  13. package/public/favicon.svg +12 -0
  14. package/scripts/guanwei +245 -0
  15. package/scripts/install.sh +79 -0
  16. package/scripts/release.sh +42 -0
  17. package/scripts/setup.bat +36 -0
  18. package/scripts/setup.sh +123 -0
  19. package/server/src/data/db.json +12059 -0
  20. package/server/src/data/guanwei.db +0 -0
  21. package/server/src/data/lifeContext.ts +423 -0
  22. package/server/src/data/spreads.ts +255 -0
  23. package/server/src/data/tarotCards.ts +2353 -0
  24. package/server/src/index.ts +71 -0
  25. package/server/src/routes/ai.ts +376 -0
  26. package/server/src/routes/divine.ts +140 -0
  27. package/server/src/routes/hour.ts +40 -0
  28. package/server/src/routes/tarot.ts +129 -0
  29. package/server/src/routes/users.ts +140 -0
  30. package/server/src/services/chartBrief.ts +131 -0
  31. package/server/src/services/divineStore.ts +188 -0
  32. package/server/src/services/hourInference.ts +150 -0
  33. package/server/src/services/llmProvider.ts +181 -0
  34. package/server/src/services/promptBuilder.ts +327 -0
  35. package/server/src/services/relativesCheck.ts +139 -0
  36. package/server/src/services/sixRelatives.ts +70 -0
  37. package/server/src/services/skills.ts +116 -0
  38. package/server/src/types/index.ts +104 -0
  39. package/server/src/utils/semanticAnalyzer.ts +555 -0
  40. package/server/src/utils/tarotEngine.ts +849 -0
  41. package/server/tsconfig.json +18 -0
  42. package/shared/core/data/classics.ts +276 -0
  43. package/shared/core/data/courses.ts +211 -0
  44. package/shared/core/data/duanyu.ts +148 -0
  45. package/shared/core/data/ganzhi.ts +53 -0
  46. package/shared/core/data/gua64.ts +93 -0
  47. package/shared/core/data/liuren.ts +18 -0
  48. package/shared/core/data/liuyao.ts +73 -0
  49. package/shared/core/data/qimen.ts +27 -0
  50. package/shared/core/data/region-full.ts +3 -0
  51. package/shared/core/data/region.ts +310 -0
  52. package/shared/core/data/tarotSpreads.ts +78 -0
  53. package/shared/core/data/xiaoliuren.ts +18 -0
  54. package/shared/core/data/ziwei.ts +97 -0
  55. package/shared/core/data/zodiac.ts +20 -0
  56. package/shared/core/engine/astrology.ts +169 -0
  57. package/shared/core/engine/bazi.ts +306 -0
  58. package/shared/core/engine/calendar.ts +127 -0
  59. package/shared/core/engine/daily.ts +133 -0
  60. package/shared/core/engine/liuren.ts +86 -0
  61. package/shared/core/engine/liuyao.ts +93 -0
  62. package/shared/core/engine/meihua.ts +87 -0
  63. package/shared/core/engine/qimen.ts +78 -0
  64. package/shared/core/engine/questionFit.ts +94 -0
  65. package/shared/core/engine/tarot.ts +69 -0
  66. package/shared/core/engine/trueSolarTime.ts +86 -0
  67. package/shared/core/engine/xiaoliuren.ts +19 -0
  68. package/shared/core/engine/ziwei.ts +217 -0
  69. package/shared/core/engine/ziweiInterpret.ts +161 -0
  70. package/shared/core/types.ts +206 -0
  71. package/src/App.tsx +69 -0
  72. package/src/arts/registry.ts +31 -0
  73. package/src/assets/react.svg +1 -0
  74. package/src/components/Backdrop.tsx +27 -0
  75. package/src/components/BambooArt.tsx +41 -0
  76. package/src/components/CalendarInput.tsx +131 -0
  77. package/src/components/DateInput.tsx +103 -0
  78. package/src/components/Disclaimer.tsx +12 -0
  79. package/src/components/Empty.tsx +8 -0
  80. package/src/components/ErrorBoundary.tsx +38 -0
  81. package/src/components/HomeSideAnchor.tsx +62 -0
  82. package/src/components/InkVein.tsx +10 -0
  83. package/src/components/LifeEventsInput.tsx +47 -0
  84. package/src/components/LocationPicker.tsx +117 -0
  85. package/src/components/Motes.tsx +65 -0
  86. package/src/components/Navigation.tsx +80 -0
  87. package/src/components/ProfileForm.tsx +40 -0
  88. package/src/components/ProfilePicker.tsx +40 -0
  89. package/src/components/QuestionFields.tsx +47 -0
  90. package/src/components/ReportView.tsx +211 -0
  91. package/src/components/ResultCard.tsx +40 -0
  92. package/src/components/SealButton.tsx +18 -0
  93. package/src/components/SiteFooter.tsx +17 -0
  94. package/src/components/SiteNav.tsx +103 -0
  95. package/src/components/SongDialog.tsx +64 -0
  96. package/src/components/SongSearchSelect.tsx +81 -0
  97. package/src/components/SongSelect.tsx +61 -0
  98. package/src/components/StarField.tsx +119 -0
  99. package/src/components/TarotCard.tsx +129 -0
  100. package/src/components/TimeShichenInput.tsx +29 -0
  101. package/src/components/arts/AstrologyArt.tsx +141 -0
  102. package/src/components/arts/BaziArt.tsx +269 -0
  103. package/src/components/arts/LiurenArt.tsx +87 -0
  104. package/src/components/arts/LiuyaoArt.tsx +111 -0
  105. package/src/components/arts/MeihuaArt.tsx +105 -0
  106. package/src/components/arts/QimenArt.tsx +86 -0
  107. package/src/components/arts/TarotArt.tsx +110 -0
  108. package/src/components/arts/XiaoliurenArt.tsx +93 -0
  109. package/src/components/arts/ZiweiArt.tsx +296 -0
  110. package/src/data/arts.ts +84 -0
  111. package/src/data/lifeContext.ts +423 -0
  112. package/src/data/sample-report.json +146 -0
  113. package/src/data/shichen.ts +21 -0
  114. package/src/data/spreads.ts +255 -0
  115. package/src/data/tarotCards.ts +2353 -0
  116. package/src/hooks/useAIInterpret.ts +76 -0
  117. package/src/hooks/useDivine.ts +39 -0
  118. package/src/hooks/useTheme.ts +29 -0
  119. package/src/index.css +190 -0
  120. package/src/lib/utils.ts +6 -0
  121. package/src/main.tsx +12 -0
  122. package/src/pages/AboutPage.tsx +25 -0
  123. package/src/pages/AcademyPage.tsx +128 -0
  124. package/src/pages/AstrologyPage.tsx +33 -0
  125. package/src/pages/AuthPage.tsx +263 -0
  126. package/src/pages/BaZiPage.tsx +33 -0
  127. package/src/pages/ClassicsPage.tsx +120 -0
  128. package/src/pages/DemoPage.tsx +161 -0
  129. package/src/pages/HistoryPage.tsx +133 -0
  130. package/src/pages/Home.tsx +179 -0
  131. package/src/pages/HomePage.tsx +174 -0
  132. package/src/pages/ModulePage.tsx +320 -0
  133. package/src/pages/SpreadEditor.tsx +230 -0
  134. package/src/pages/SpreadEditorPage.tsx +105 -0
  135. package/src/pages/SpreadLibrary.tsx +199 -0
  136. package/src/pages/SpreadLibraryPage.tsx +17 -0
  137. package/src/pages/TarotPage.tsx +738 -0
  138. package/src/pages/ZiWeiPage.tsx +33 -0
  139. package/src/services/api.ts +342 -0
  140. package/src/stores/useDivinationStore.ts +22 -0
  141. package/src/styles/song.css +1066 -0
  142. package/src/styles/tokens.css +60 -0
  143. package/src/types/index.ts +104 -0
  144. package/src/utils/astrologyCalc.ts +61 -0
  145. package/src/utils/comboEngine.ts +134 -0
  146. package/src/utils/dst.ts +24 -0
  147. package/src/utils/panTone.ts +206 -0
  148. package/src/utils/recordStore.ts +60 -0
  149. package/src/utils/semanticAnalyzer.ts +555 -0
  150. package/src/utils/spreadStorage.ts +32 -0
  151. package/src/utils/storage.ts +27 -0
  152. package/src/utils/tarotEngine.ts +849 -0
  153. package/src/utils/userStore.ts +234 -0
  154. package/src/vite-env.d.ts +1 -0
  155. package/tailwind.config.js +52 -0
  156. package/tsconfig.json +40 -0
  157. package/vite.config.ts +34 -0
@@ -0,0 +1,310 @@
1
+ // 中国省市区县 → 经纬度数据集
2
+ // 全量数据(34 省/391 市/2728 区县)见 region-full.ts(DataV.GeoAtlas 中心点坐标 + 台湾手动补充)
3
+ // 本文件保留精简版 REGIONS(旧结构兼容)+ 全量查找入口
4
+ import { REGION_FULL } from './region-full';
5
+ // 坐标取各市市中心/政府驻地近似值,数据来源:公开地理数据整理
6
+
7
+ export interface District {
8
+ name: string;
9
+ lng?: number;
10
+ lat?: number;
11
+ }
12
+
13
+ export interface City {
14
+ name: string;
15
+ lng: number;
16
+ lat: number;
17
+ districts?: District[];
18
+ }
19
+
20
+ export interface Province {
21
+ name: string;
22
+ cities: City[];
23
+ }
24
+
25
+ export const REGIONS: Province[] = [
26
+ { name: '北京', cities: [
27
+ { name: '北京市', lng: 116.407, lat: 39.904, districts: [{ name: '东城区' }, { name: '西城区' }, { name: '朝阳区' }, { name: '海淀区' }, { name: '丰台区' }, { name: '通州区' }] },
28
+ ] },
29
+ { name: '上海', cities: [
30
+ { name: '上海市', lng: 121.474, lat: 31.230, districts: [{ name: '黄浦区' }, { name: '徐汇区' }, { name: '静安区' }, { name: '浦东新区' }, { name: '闵行区' }, { name: '嘉定区' }] },
31
+ ] },
32
+ { name: '天津', cities: [
33
+ { name: '天津市', lng: 117.201, lat: 39.084, districts: [{ name: '和平区' }, { name: '河西区' }, { name: '南开区' }, { name: '滨海新区' }] },
34
+ ] },
35
+ { name: '重庆', cities: [
36
+ { name: '重庆市', lng: 106.551, lat: 29.563, districts: [{ name: '渝中区' }, { name: '江北区' }, { name: '沙坪坝区' }, { name: '南岸区' }, { name: '九龙坡区' }] },
37
+ ] },
38
+ { name: '河北', cities: [
39
+ { name: '石家庄', lng: 114.515, lat: 38.042 }, { name: '唐山', lng: 118.180, lat: 39.630 },
40
+ { name: '保定', lng: 115.464, lat: 38.874 }, { name: '邯郸', lng: 114.539, lat: 36.625 },
41
+ { name: '秦皇岛', lng: 119.600, lat: 39.935 }, { name: '廊坊', lng: 116.684, lat: 39.538 },
42
+ ] },
43
+ { name: '山西', cities: [
44
+ { name: '太原', lng: 112.549, lat: 37.857 }, { name: '大同', lng: 113.300, lat: 40.076 },
45
+ { name: '临汾', lng: 111.519, lat: 36.088 }, { name: '运城', lng: 111.007, lat: 35.026 },
46
+ { name: '晋中', lng: 112.753, lat: 37.687 },
47
+ ] },
48
+ { name: '内蒙古', cities: [
49
+ { name: '呼和浩特', lng: 111.749, lat: 40.842 }, { name: '包头', lng: 109.840, lat: 40.657 },
50
+ { name: '赤峰', lng: 118.887, lat: 42.257 }, { name: '鄂尔多斯', lng: 109.781, lat: 39.608 },
51
+ { name: '呼伦贝尔', lng: 119.766, lat: 49.212 },
52
+ ] },
53
+ { name: '辽宁', cities: [
54
+ { name: '沈阳', lng: 123.431, lat: 41.805 }, { name: '大连', lng: 121.615, lat: 38.914 },
55
+ { name: '鞍山', lng: 122.994, lat: 41.108 }, { name: '锦州', lng: 121.127, lat: 41.095 },
56
+ { name: '丹东', lng: 124.354, lat: 40.000 },
57
+ ] },
58
+ { name: '吉林', cities: [
59
+ { name: '长春', lng: 125.323, lat: 43.817 }, { name: '吉林', lng: 126.550, lat: 43.838 },
60
+ { name: '延边', lng: 129.509, lat: 42.891 }, { name: '四平', lng: 124.350, lat: 43.167 },
61
+ ] },
62
+ { name: '黑龙江', cities: [
63
+ { name: '哈尔滨', lng: 126.535, lat: 45.803 }, { name: '齐齐哈尔', lng: 123.918, lat: 47.354 },
64
+ { name: '大庆', lng: 125.103, lat: 46.589 }, { name: '牡丹江', lng: 129.633, lat: 44.551 },
65
+ { name: '佳木斯', lng: 130.319, lat: 46.799 },
66
+ ] },
67
+ { name: '江苏', cities: [
68
+ { name: '南京', lng: 118.797, lat: 32.060 }, { name: '苏州', lng: 120.585, lat: 31.299 },
69
+ { name: '无锡', lng: 120.312, lat: 31.491 }, { name: '徐州', lng: 117.284, lat: 34.205 },
70
+ { name: '常州', lng: 119.974, lat: 31.811 }, { name: '南通', lng: 120.895, lat: 31.980 },
71
+ { name: '扬州', lng: 119.413, lat: 32.394 }, { name: '盐城', lng: 120.163, lat: 33.348 },
72
+ ] },
73
+ { name: '浙江', cities: [
74
+ { name: '杭州', lng: 120.155, lat: 30.274 }, { name: '宁波', lng: 121.550, lat: 29.874 },
75
+ { name: '温州', lng: 120.699, lat: 27.994 }, { name: '金华', lng: 119.647, lat: 29.079 },
76
+ { name: '绍兴', lng: 120.580, lat: 30.030 }, { name: '嘉兴', lng: 120.758, lat: 30.746 },
77
+ { name: '台州', lng: 121.421, lat: 28.656 },
78
+ ] },
79
+ { name: '安徽', cities: [
80
+ { name: '合肥', lng: 117.227, lat: 31.821 }, { name: '芜湖', lng: 118.433, lat: 31.353 },
81
+ { name: '安庆', lng: 117.063, lat: 30.543 }, { name: '蚌埠', lng: 117.389, lat: 32.916 },
82
+ { name: '黄山', lng: 118.338, lat: 29.714 },
83
+ ] },
84
+ { name: '福建', cities: [
85
+ { name: '福州', lng: 119.296, lat: 26.074 }, { name: '厦门', lng: 118.089, lat: 24.480 },
86
+ { name: '泉州', lng: 118.676, lat: 24.874 }, { name: '漳州', lng: 117.647, lat: 24.513 },
87
+ { name: '莆田', lng: 119.008, lat: 25.454 },
88
+ ] },
89
+ { name: '江西', cities: [
90
+ { name: '南昌', lng: 115.858, lat: 28.683 }, { name: '赣州', lng: 114.935, lat: 25.831 },
91
+ { name: '九江', lng: 116.002, lat: 29.705 }, { name: '景德镇', lng: 117.178, lat: 29.269 },
92
+ ] },
93
+ { name: '山东', cities: [
94
+ { name: '济南', lng: 117.120, lat: 36.651 }, { name: '青岛', lng: 120.383, lat: 36.067 },
95
+ { name: '烟台', lng: 121.448, lat: 37.464 }, { name: '潍坊', lng: 119.162, lat: 36.717 },
96
+ { name: '临沂', lng: 118.356, lat: 35.105 }, { name: '济宁', lng: 116.587, lat: 35.415 },
97
+ ] },
98
+ { name: '河南', cities: [
99
+ { name: '郑州', lng: 113.625, lat: 34.746 }, { name: '洛阳', lng: 112.454, lat: 34.620 },
100
+ { name: '开封', lng: 114.307, lat: 34.797 }, { name: '南阳', lng: 112.528, lat: 32.990 },
101
+ { name: '安阳', lng: 114.392, lat: 36.098 }, { name: '信阳', lng: 114.091, lat: 32.147 },
102
+ ] },
103
+ { name: '湖北', cities: [
104
+ { name: '武汉', lng: 114.305, lat: 30.593 }, { name: '宜昌', lng: 111.287, lat: 30.692 },
105
+ { name: '襄阳', lng: 112.122, lat: 32.009 }, { name: '荆州', lng: 112.241, lat: 30.325 },
106
+ { name: '黄石', lng: 115.038, lat: 30.201 },
107
+ ] },
108
+ { name: '湖南', cities: [
109
+ { name: '长沙', lng: 112.939, lat: 28.228 }, { name: '株洲', lng: 113.134, lat: 27.828 },
110
+ { name: '衡阳', lng: 112.572, lat: 26.893 }, { name: '岳阳', lng: 113.129, lat: 29.357 },
111
+ { name: '张家界', lng: 110.479, lat: 29.117 },
112
+ ] },
113
+ { name: '广东', cities: [
114
+ { name: '广州', lng: 113.264, lat: 23.129, districts: [{ name: '越秀区' }, { name: '天河区' }, { name: '海珠区' }, { name: '番禺区' }] },
115
+ { name: '深圳', lng: 114.058, lat: 22.543, districts: [{ name: '福田区' }, { name: '南山区' }, { name: '罗湖区' }, { name: '宝安区' }] },
116
+ { name: '佛山', lng: 113.122, lat: 23.021 }, { name: '珠海', lng: 113.577, lat: 22.271 },
117
+ { name: '汕头', lng: 116.682, lat: 23.354 }, { name: '东莞', lng: 113.752, lat: 23.021 },
118
+ { name: '中山', lng: 113.392, lat: 22.517 }, { name: '惠州', lng: 114.416, lat: 23.111 },
119
+ ] },
120
+ { name: '广西', cities: [
121
+ { name: '南宁', lng: 108.366, lat: 22.817 }, { name: '桂林', lng: 110.290, lat: 25.274 },
122
+ { name: '柳州', lng: 109.416, lat: 24.326 }, { name: '北海', lng: 109.120, lat: 21.481 },
123
+ ] },
124
+ { name: '海南', cities: [
125
+ { name: '海口', lng: 110.199, lat: 20.044 }, { name: '三亚', lng: 109.512, lat: 18.253 },
126
+ { name: '儋州', lng: 109.581, lat: 19.521 },
127
+ ] },
128
+ { name: '四川', cities: [
129
+ { name: '成都', lng: 104.066, lat: 30.573, districts: [{ name: '锦江区' }, { name: '青羊区' }, { name: '武侯区' }, { name: '成华区' }] },
130
+ { name: '绵阳', lng: 104.680, lat: 31.467 }, { name: '乐山', lng: 103.765, lat: 29.552 },
131
+ { name: '宜宾', lng: 104.642, lat: 28.752 }, { name: '南充', lng: 106.083, lat: 30.795 },
132
+ { name: '攀枝花', lng: 101.718, lat: 26.582 },
133
+ ] },
134
+ { name: '贵州', cities: [
135
+ { name: '贵阳', lng: 106.630, lat: 26.647 }, { name: '遵义', lng: 106.927, lat: 27.726 },
136
+ { name: '六盘水', lng: 104.830, lat: 26.592 }, { name: '安顺', lng: 105.947, lat: 26.253 },
137
+ ] },
138
+ { name: '云南', cities: [
139
+ { name: '昆明', lng: 102.833, lat: 24.880 }, { name: '大理', lng: 100.267, lat: 25.606 },
140
+ { name: '丽江', lng: 100.230, lat: 26.856 }, { name: '曲靖', lng: 103.796, lat: 25.490 },
141
+ { name: '西双版纳', lng: 100.797, lat: 22.001 },
142
+ ] },
143
+ { name: '西藏', cities: [
144
+ { name: '拉萨', lng: 91.140, lat: 29.646 }, { name: '日喀则', lng: 88.880, lat: 29.267 },
145
+ ] },
146
+ { name: '陕西', cities: [
147
+ { name: '西安', lng: 108.940, lat: 34.341, districts: [{ name: '碑林区' }, { name: '雁塔区' }, { name: '未央区' }, { name: '长安区' }] },
148
+ { name: '宝鸡', lng: 107.238, lat: 34.362 }, { name: '咸阳', lng: 108.709, lat: 34.330 },
149
+ { name: '延安', lng: 109.490, lat: 36.585 }, { name: '汉中', lng: 107.023, lat: 33.067 },
150
+ ] },
151
+ { name: '甘肃', cities: [
152
+ { name: '兰州', lng: 103.834, lat: 36.061 }, { name: '天水', lng: 105.725, lat: 34.581 },
153
+ { name: '酒泉', lng: 98.494, lat: 39.732 }, { name: '敦煌', lng: 94.662, lat: 40.142 },
154
+ ] },
155
+ { name: '青海', cities: [
156
+ { name: '西宁', lng: 101.778, lat: 36.617 }, { name: '格尔木', lng: 94.928, lat: 36.406 },
157
+ ] },
158
+ { name: '宁夏', cities: [
159
+ { name: '银川', lng: 106.231, lat: 38.487 }, { name: '吴忠', lng: 106.198, lat: 37.997 },
160
+ { name: '中卫', lng: 105.190, lat: 37.500 },
161
+ ] },
162
+ { name: '新疆', cities: [
163
+ { name: '乌鲁木齐', lng: 87.617, lat: 43.793 }, { name: '喀什', lng: 75.990, lat: 39.470 },
164
+ { name: '伊犁', lng: 81.324, lat: 43.917 }, { name: '克拉玛依', lng: 84.889, lat: 45.580 },
165
+ { name: '吐鲁番', lng: 89.190, lat: 42.951 },
166
+ ] },
167
+ { name: '香港', cities: [
168
+ { name: '香港', lng: 114.169, lat: 22.320 },
169
+ ] },
170
+ { name: '澳门', cities: [
171
+ { name: '澳门', lng: 113.544, lat: 22.198 },
172
+ ] },
173
+ { name: '台湾', cities: [
174
+ { name: '台北', lng: 121.565, lat: 25.033 }, { name: '高雄', lng: 120.312, lat: 22.621 },
175
+ { name: '台中', lng: 120.684, lat: 24.138 },
176
+ ] },
177
+ ];
178
+
179
+ // 解析选择结果 → GeoLocation(未选时返回 null)
180
+ export interface GeoSelection {
181
+ province: string;
182
+ city: string;
183
+ district: string;
184
+ lng: number;
185
+ lat: number;
186
+ }
187
+
188
+ // 名称归一化:去掉行政区后缀(广东省→广东、锦州市→锦州、北镇市→北镇)
189
+ function stripSuffix(name: string, suffixes: string[]): string {
190
+ for (const s of suffixes) {
191
+ if (name.endsWith(s) && name.length > s.length) return name.slice(0, -s.length);
192
+ }
193
+ return name;
194
+ }
195
+ const PROV_SUFFIX = ['壮族自治区', '回族自治区', '维吾尔自治区', '特别行政区', '自治区', '省', '市'];
196
+ const CITY_SUFFIX = ['地区', '自治州', '盟', '市', '县'];
197
+ const DIST_SUFFIX = ['自治县', '自治旗', '林区', '特区', '市', '县', '区', '旗'];
198
+ const normProv = (n: string) => stripSuffix(n, PROV_SUFFIX);
199
+ const normCity = (n: string) => stripSuffix(n, CITY_SUFFIX);
200
+ const normDist = (n: string) => stripSuffix(n, DIST_SUFFIX);
201
+
202
+ // 全量查询:省/市/区三级(精确或去后缀匹配,任一缺失自动回退上一级坐标)
203
+ export function resolveLocation(province: string, city: string, district: string): GeoSelection | null {
204
+ const provKey = REGION_FULL[province] ? province : Object.keys(REGION_FULL).find(k => normProv(k) === normProv(province));
205
+ if (!provKey) return null;
206
+ const p = REGION_FULL[provKey];
207
+ const cityKey = p.cities[city] ? city : Object.keys(p.cities).find(k => normCity(k) === normCity(city));
208
+ if (!cityKey) return { province: provKey, city: '', district: '', lng: p.lng, lat: p.lat };
209
+ const c = p.cities[cityKey];
210
+ const distKey = c.districts[district] ? district : Object.keys(c.districts).find(k => normDist(k) === normDist(district));
211
+ return {
212
+ province: provKey,
213
+ city: cityKey,
214
+ district: distKey || '城区',
215
+ lng: distKey ? c.districts[distKey][0] : c.lng,
216
+ lat: distKey ? c.districts[distKey][1] : c.lat,
217
+ };
218
+ }
219
+
220
+ // 全量列表 API(LocationPicker 用)
221
+ export function allProvinces(): string[] { return Object.keys(REGION_FULL); }
222
+ export function citiesOf(province: string): { name: string; lng: number; lat: number }[] {
223
+ const provKey = REGION_FULL[province] ? province : Object.keys(REGION_FULL).find(k => normProv(k) === normProv(province));
224
+ const p = provKey ? REGION_FULL[provKey] : null;
225
+ return p ? Object.keys(p.cities).map(name => ({ name, lng: p.cities[name].lng, lat: p.cities[name].lat })) : [];
226
+ }
227
+ export function districtsOf(province: string, city: string): { name: string }[] {
228
+ const provKey = REGION_FULL[province] ? province : Object.keys(REGION_FULL).find(k => normProv(k) === normProv(province));
229
+ const p = provKey ? REGION_FULL[provKey] : null;
230
+ const cityKey = p && (p.cities[city] ? city : Object.keys(p.cities).find(k => normCity(k) === normCity(city)));
231
+ const c = cityKey ? p!.cities[cityKey] : null;
232
+ return c ? Object.keys(c.districts).map(name => ({ name })) : [];
233
+ }
234
+ // ============ 扩充城市(第二批,补全主要地级市) ============
235
+ const EXTRA_CITIES: Record<string, { name: string; lng: number; lat: number }[]> = {
236
+ 河北: [{ name: '邢台', lng: 114.505, lat: 37.070 }, { name: '张家口', lng: 114.886, lat: 40.824 }, { name: '承德', lng: 117.962, lat: 40.951 }, { name: '沧州', lng: 116.838, lat: 38.304 }, { name: '衡水', lng: 115.670, lat: 37.738 }],
237
+ 山西: [{ name: '阳泉', lng: 113.580, lat: 37.856 }, { name: '长治', lng: 113.116, lat: 36.195 }, { name: '晋城', lng: 112.851, lat: 35.491 }, { name: '朔州', lng: 112.433, lat: 39.331 }, { name: '忻州', lng: 112.734, lat: 38.417 }],
238
+ 内蒙古: [{ name: '通辽', lng: 122.244, lat: 43.653 }, { name: '巴彦淖尔', lng: 107.388, lat: 40.743 }, { name: '乌兰察布', lng: 113.132, lat: 40.995 }, { name: '兴安盟', lng: 122.070, lat: 46.082 }],
239
+ 辽宁: [{ name: '抚顺', lng: 123.957, lat: 41.881 }, { name: '本溪', lng: 123.766, lat: 41.294 }, { name: '营口', lng: 122.235, lat: 40.667 }, { name: '辽阳', lng: 123.237, lat: 41.269 }, { name: '盘锦', lng: 122.071, lat: 41.124 }],
240
+ 吉林: [{ name: '辽源', lng: 125.143, lat: 42.887 }, { name: '通化', lng: 125.940, lat: 41.728 }, { name: '白山', lng: 126.424, lat: 41.940 }, { name: '松原', lng: 124.825, lat: 45.142 }],
241
+ 黑龙江: [{ name: '鸡西', lng: 130.969, lat: 45.295 }, { name: '鹤岗', lng: 130.298, lat: 47.349 }, { name: '双鸭山', lng: 131.159, lat: 46.646 }, { name: '伊春', lng: 128.841, lat: 47.727 }, { name: '黑河', lng: 127.528, lat: 50.245 }],
242
+ 江苏: [{ name: '连云港', lng: 119.221, lat: 34.600 }, { name: '淮安', lng: 119.015, lat: 33.610 }, { name: '镇江', lng: 119.425, lat: 32.187 }, { name: '泰州', lng: 119.926, lat: 32.456 }, { name: '宿迁', lng: 118.275, lat: 33.963 }],
243
+ 浙江: [{ name: '湖州', lng: 120.086, lat: 30.893 }, { name: '衢州', lng: 118.874, lat: 28.935 }, { name: '丽水', lng: 119.922, lat: 28.467 }],
244
+ 安徽: [{ name: '淮南', lng: 117.018, lat: 32.647 }, { name: '马鞍山', lng: 118.506, lat: 31.670 }, { name: '淮北', lng: 116.798, lat: 33.955 }, { name: '铜陵', lng: 117.812, lat: 30.945 }, { name: '阜阳', lng: 115.814, lat: 32.890 }],
245
+ 福建: [{ name: '三明', lng: 117.638, lat: 26.263 }, { name: '南平', lng: 118.178, lat: 26.642 }, { name: '龙岩', lng: 117.017, lat: 25.075 }, { name: '宁德', lng: 119.548, lat: 26.666 }],
246
+ 江西: [{ name: '萍乡', lng: 113.854, lat: 27.622 }, { name: '新余', lng: 114.917, lat: 27.817 }, { name: '鹰潭', lng: 117.069, lat: 28.260 }, { name: '吉安', lng: 114.993, lat: 27.114 }, { name: '宜春', lng: 114.416, lat: 27.815 }, { name: '抚州', lng: 116.358, lat: 27.984 }, { name: '上饶', lng: 117.943, lat: 28.455 }],
247
+ 山东: [{ name: '淄博', lng: 118.055, lat: 36.813 }, { name: '枣庄', lng: 117.321, lat: 34.810 }, { name: '东营', lng: 118.675, lat: 37.434 }, { name: '泰安', lng: 117.087, lat: 36.191 }, { name: '威海', lng: 122.120, lat: 37.513 }, { name: '日照', lng: 119.527, lat: 35.416 }, { name: '德州', lng: 116.359, lat: 37.435 }, { name: '聊城', lng: 115.985, lat: 36.456 }, { name: '滨州', lng: 117.971, lat: 37.381 }, { name: '菏泽', lng: 115.480, lat: 35.234 }],
248
+ 河南: [{ name: '平顶山', lng: 113.192, lat: 33.766 }, { name: '鹤壁', lng: 114.297, lat: 35.748 }, { name: '新乡', lng: 113.927, lat: 35.303 }, { name: '焦作', lng: 113.242, lat: 35.216 }, { name: '濮阳', lng: 115.030, lat: 35.762 }, { name: '许昌', lng: 113.852, lat: 34.037 }, { name: '漯河', lng: 114.016, lat: 33.580 }, { name: '三门峡', lng: 111.200, lat: 34.772 }, { name: '商丘', lng: 115.656, lat: 34.414 }, { name: '周口', lng: 114.697, lat: 33.626 }, { name: '驻马店', lng: 114.022, lat: 33.011 }],
249
+ 湖北: [{ name: '十堰', lng: 110.798, lat: 32.629 }, { name: '荆门', lng: 112.199, lat: 31.035 }, { name: '孝感', lng: 113.916, lat: 30.924 }, { name: '黄冈', lng: 114.872, lat: 30.453 }, { name: '咸宁', lng: 114.322, lat: 29.841 }, { name: '随州', lng: 113.383, lat: 31.690 }, { name: '恩施', lng: 109.487, lat: 30.272 }],
250
+ 湖南: [{ name: '湘潭', lng: 112.944, lat: 27.830 }, { name: '邵阳', lng: 111.467, lat: 27.239 }, { name: '常德', lng: 111.698, lat: 29.032 }, { name: '益阳', lng: 112.355, lat: 28.554 }, { name: '郴州', lng: 113.014, lat: 25.771 }, { name: '永州', lng: 111.613, lat: 26.420 }, { name: '怀化', lng: 110.001, lat: 27.570 }, { name: '娄底', lng: 111.994, lat: 27.697 }],
251
+ 广东: [{ name: '韶关', lng: 113.597, lat: 24.810 }, { name: '江门', lng: 113.082, lat: 22.579 }, { name: '湛江', lng: 110.365, lat: 21.271 }, { name: '茂名', lng: 110.925, lat: 21.663 }, { name: '肇庆', lng: 112.473, lat: 23.047 }, { name: '梅州', lng: 116.122, lat: 24.288 }, { name: '汕尾', lng: 115.375, lat: 22.786 }, { name: '河源', lng: 114.700, lat: 23.743 }, { name: '阳江', lng: 111.982, lat: 21.857 }, { name: '清远', lng: 113.056, lat: 23.681 }, { name: '潮州', lng: 116.623, lat: 23.657 }, { name: '揭阳', lng: 116.373, lat: 23.550 }, { name: '云浮', lng: 112.045, lat: 22.915 }],
252
+ 广西: [{ name: '梧州', lng: 111.279, lat: 23.476 }, { name: '防城港', lng: 108.354, lat: 21.687 }, { name: '钦州', lng: 108.654, lat: 21.980 }, { name: '贵港', lng: 109.598, lat: 23.111 }, { name: '玉林', lng: 110.181, lat: 22.654 }, { name: '百色', lng: 106.618, lat: 23.902 }, { name: '贺州', lng: 111.566, lat: 24.403 }, { name: '河池', lng: 108.085, lat: 24.692 }, { name: '来宾', lng: 109.222, lat: 23.751 }, { name: '崇左', lng: 107.365, lat: 22.376 }],
253
+ 四川: [{ name: '自贡', lng: 104.778, lat: 29.339 }, { name: '攀枝花', lng: 101.718, lat: 26.582 }, { name: '泸州', lng: 105.442, lat: 28.872 }, { name: '德阳', lng: 104.398, lat: 31.127 }, { name: '广元', lng: 105.843, lat: 32.435 }, { name: '遂宁', lng: 105.571, lat: 30.513 }, { name: '内江', lng: 105.058, lat: 29.580 }, { name: '眉山', lng: 103.848, lat: 30.075 }, { name: '达州', lng: 107.468, lat: 31.209 }, { name: '雅安', lng: 103.042, lat: 30.010 }, { name: '凉山', lng: 102.267, lat: 27.881 }],
254
+ 贵州: [{ name: '毕节', lng: 105.285, lat: 27.302 }, { name: '铜仁', lng: 109.190, lat: 27.718 }, { name: '黔南', lng: 107.517, lat: 26.258 }, { name: '黔东南', lng: 107.977, lat: 26.583 }, { name: '黔西南', lng: 104.897, lat: 25.088 }],
255
+ 云南: [{ name: '玉溪', lng: 102.546, lat: 24.351 }, { name: '保山', lng: 99.162, lat: 25.112 }, { name: '昭通', lng: 103.717, lat: 27.338 }, { name: '普洱', lng: 100.966, lat: 22.825 }, { name: '临沧', lng: 100.089, lat: 23.878 }, { name: '红河', lng: 103.384, lat: 23.366 }, { name: '楚雄', lng: 101.546, lat: 25.032 }],
256
+ 陕西: [{ name: '铜川', lng: 108.945, lat: 34.897 }, { name: '渭南', lng: 109.510, lat: 34.500 }, { name: '榆林', lng: 109.735, lat: 38.285 }, { name: '安康', lng: 109.029, lat: 32.684 }, { name: '商洛', lng: 109.941, lat: 33.870 }],
257
+ 甘肃: [{ name: '嘉峪关', lng: 98.290, lat: 39.773 }, { name: '金昌', lng: 102.188, lat: 38.514 }, { name: '白银', lng: 104.139, lat: 36.545 }, { name: '武威', lng: 102.638, lat: 37.928 }, { name: '张掖', lng: 100.450, lat: 38.925 }, { name: '平凉', lng: 106.665, lat: 35.542 }, { name: '庆阳', lng: 107.643, lat: 35.710 }],
258
+ 新疆: [{ name: '哈密', lng: 93.515, lat: 42.833 }, { name: '昌吉', lng: 87.308, lat: 44.011 }, { name: '巴音郭楞', lng: 86.150, lat: 41.768 }, { name: '阿克苏', lng: 80.260, lat: 41.171 }, { name: '和田', lng: 79.925, lat: 37.110 }],
259
+ 西藏: [{ name: '林芝', lng: 94.362, lat: 29.649 }, { name: '山南', lng: 91.772, lat: 29.237 }, { name: '那曲', lng: 92.053, lat: 31.476 }],
260
+ };
261
+
262
+ // 合并扩充城市到 REGIONS
263
+ Object.keys(EXTRA_CITIES).forEach(pname => {
264
+ const p = REGIONS.find(r => r.name === pname);
265
+ if (p) {
266
+ EXTRA_CITIES[pname].forEach(c => {
267
+ if (!p.cities.some(ex => ex.name === c.name)) p.cities.push(c);
268
+ });
269
+ }
270
+ });
271
+ // ============ 重点城市区县扩充(第三批:主城区坐标) ============
272
+ const EXTRA_DISTRICTS: Record<string, { name: string; lng: number; lat: number }[]> = {
273
+ 杭州: [{ name: '上城区', lng: 120.170, lat: 30.243 }, { name: '拱墅区', lng: 120.142, lat: 30.319 }, { name: '西湖区', lng: 120.130, lat: 30.259 }, { name: '滨江区', lng: 120.212, lat: 30.209 }, { name: '萧山区', lng: 120.264, lat: 30.183 }],
274
+ 南京: [{ name: '玄武区', lng: 118.798, lat: 32.048 }, { name: '秦淮区', lng: 118.795, lat: 32.011 }, { name: '鼓楼区', lng: 118.770, lat: 32.066 }, { name: '建邺区', lng: 118.732, lat: 32.004 }, { name: '江宁区', lng: 118.840, lat: 31.953 }],
275
+ 武汉: [{ name: '江岸区', lng: 114.309, lat: 30.600 }, { name: '江汉区', lng: 114.271, lat: 30.601 }, { name: '武昌区', lng: 114.316, lat: 30.554 }, { name: '洪山区', lng: 114.344, lat: 30.500 }, { name: '汉阳区', lng: 114.218, lat: 30.554 }],
276
+ 长沙: [{ name: '芙蓉区', lng: 113.032, lat: 28.185 }, { name: '天心区', lng: 112.990, lat: 28.113 }, { name: '岳麓区', lng: 112.931, lat: 28.235 }, { name: '开福区', lng: 112.985, lat: 28.256 }],
277
+ 郑州: [{ name: '中原区', lng: 113.613, lat: 34.748 }, { name: '二七区', lng: 113.640, lat: 34.724 }, { name: '金水区', lng: 113.661, lat: 34.800 }, { name: '管城回族区', lng: 113.677, lat: 34.754 }],
278
+ 青岛: [{ name: '市南区', lng: 120.412, lat: 36.075 }, { name: '市北区', lng: 120.375, lat: 36.087 }, { name: '崂山区', lng: 120.469, lat: 36.107 }, { name: '李沧区', lng: 120.433, lat: 36.145 }],
279
+ 沈阳: [{ name: '和平区', lng: 123.395, lat: 41.790 }, { name: '沈河区', lng: 123.459, lat: 41.796 }, { name: '大东区', lng: 123.470, lat: 41.805 }, { name: '皇姑区', lng: 123.442, lat: 41.820 }],
280
+ 大连: [{ name: '中山区', lng: 121.645, lat: 38.918 }, { name: '西岗区', lng: 121.612, lat: 38.915 }, { name: '沙河口区', lng: 121.594, lat: 38.905 }, { name: '甘井子区', lng: 121.525, lat: 38.953 }],
281
+ 厦门: [{ name: '思明区', lng: 118.082, lat: 24.445 }, { name: '湖里区', lng: 118.147, lat: 24.513 }, { name: '集美区', lng: 118.097, lat: 24.576 }, { name: '海沧区', lng: 118.033, lat: 24.485 }],
282
+ 福州: [{ name: '鼓楼区', lng: 119.304, lat: 26.082 }, { name: '台江区', lng: 119.314, lat: 26.058 }, { name: '仓山区', lng: 119.274, lat: 26.047 }, { name: '晋安区', lng: 119.328, lat: 26.082 }],
283
+ 昆明: [{ name: '五华区', lng: 102.707, lat: 25.044 }, { name: '盘龙区', lng: 102.752, lat: 25.116 }, { name: '官渡区', lng: 102.749, lat: 24.950 }, { name: '西山区', lng: 102.665, lat: 25.038 }],
284
+ 南宁: [{ name: '兴宁区', lng: 108.369, lat: 22.854 }, { name: '青秀区', lng: 108.494, lat: 22.785 }, { name: '江南区', lng: 108.273, lat: 22.781 }, { name: '西乡塘区', lng: 108.313, lat: 22.833 }],
285
+ 哈尔滨: [{ name: '道里区', lng: 126.617, lat: 45.756 }, { name: '南岗区', lng: 126.669, lat: 45.760 }, { name: '道外区', lng: 126.649, lat: 45.791 }, { name: '香坊区', lng: 126.663, lat: 45.708 }],
286
+ 长春: [{ name: '南关区', lng: 125.350, lat: 43.864 }, { name: '宽城区', lng: 125.326, lat: 43.943 }, { name: '朝阳区', lng: 125.288, lat: 43.833 }, { name: '二道区', lng: 125.374, lat: 43.865 }],
287
+ 济南: [{ name: '历下区', lng: 117.077, lat: 36.666 }, { name: '市中区', lng: 116.997, lat: 36.651 }, { name: '槐荫区', lng: 116.901, lat: 36.651 }, { name: '天桥区', lng: 116.987, lat: 36.678 }],
288
+ 合肥: [{ name: '瑶海区', lng: 117.309, lat: 31.858 }, { name: '庐阳区', lng: 117.265, lat: 31.879 }, { name: '蜀山区', lng: 117.261, lat: 31.851 }, { name: '包河区', lng: 117.310, lat: 31.793 }],
289
+ 南昌: [{ name: '东湖区', lng: 115.899, lat: 28.685 }, { name: '西湖区', lng: 115.877, lat: 28.657 }, { name: '青云谱区', lng: 115.926, lat: 28.621 }, { name: '青山湖区', lng: 115.962, lat: 28.682 }],
290
+ 太原: [{ name: '小店区', lng: 112.564, lat: 37.736 }, { name: '迎泽区', lng: 112.563, lat: 37.863 }, { name: '杏花岭区', lng: 112.571, lat: 37.887 }, { name: '尖草坪区', lng: 112.487, lat: 37.940 }],
291
+ 石家庄: [{ name: '长安区', lng: 114.539, lat: 38.047 }, { name: '桥西区', lng: 114.461, lat: 38.004 }, { name: '新华区', lng: 114.463, lat: 38.051 }, { name: '裕华区', lng: 114.531, lat: 38.006 }],
292
+ 兰州: [{ name: '城关区', lng: 103.825, lat: 36.057 }, { name: '七里河区', lng: 103.786, lat: 36.066 }, { name: '西固区', lng: 103.628, lat: 36.088 }, { name: '安宁区', lng: 103.719, lat: 36.104 }],
293
+ 贵阳: [{ name: '南明区', lng: 106.714, lat: 26.568 }, { name: '云岩区', lng: 106.725, lat: 26.604 }, { name: '花溪区', lng: 106.670, lat: 26.410 }, { name: '乌当区', lng: 106.751, lat: 26.630 }],
294
+ 乌鲁木齐: [{ name: '天山区', lng: 87.631, lat: 43.794 }, { name: '沙依巴克区', lng: 87.598, lat: 43.801 }, { name: '新市区', lng: 87.574, lat: 43.843 }, { name: '水磨沟区', lng: 87.642, lat: 43.832 }],
295
+ 海口: [{ name: '秀英区', lng: 110.293, lat: 20.007 }, { name: '龙华区', lng: 110.328, lat: 20.031 }, { name: '琼山区', lng: 110.354, lat: 20.003 }, { name: '美兰区', lng: 110.366, lat: 20.029 }],
296
+ 三亚: [{ name: '吉阳区', lng: 109.578, lat: 18.281 }, { name: '天涯区', lng: 109.452, lat: 18.300 }, { name: '崖州区', lng: 109.172, lat: 18.357 }, { name: '海棠区', lng: 109.753, lat: 18.400 }],
297
+ 银川: [{ name: '兴庆区', lng: 106.289, lat: 38.474 }, { name: '西夏区', lng: 106.162, lat: 38.492 }, { name: '金凤区', lng: 106.243, lat: 38.473 }],
298
+ 西宁: [{ name: '城东区', lng: 101.803, lat: 36.599 }, { name: '城中区', lng: 101.784, lat: 36.621 }, { name: '城西区', lng: 101.766, lat: 36.628 }, { name: '城北区', lng: 101.766, lat: 36.650 }],
299
+ };
300
+
301
+ // 合并到城市 districts
302
+ Object.keys(EXTRA_DISTRICTS).forEach(cname => {
303
+ REGIONS.forEach(p => {
304
+ const c = p.cities.find(x => x.name === cname);
305
+ if (c) {
306
+ const existing = c.districts || [];
307
+ c.districts = [...existing, ...EXTRA_DISTRICTS[cname].map(d => ({ name: d.name, lng: d.lng, lat: d.lat }))];
308
+ }
309
+ });
310
+ });
@@ -0,0 +1,78 @@
1
+ // 塔罗牌阵(预设常用 + 自定义支持)
2
+
3
+ export interface TarotSpreadPos {
4
+ id: number;
5
+ name: string;
6
+ description: string;
7
+ }
8
+
9
+ export interface TarotSpread {
10
+ id: string;
11
+ name: string;
12
+ description: string;
13
+ positions: TarotSpreadPos[];
14
+ isCustom?: boolean;
15
+ }
16
+
17
+ export const TAROT_SPREADS: TarotSpread[] = [
18
+ { id: 'one', name: '单张 · 今日之示', description: '抽取单张,取当下之象', positions: [{ id: 0, name: '今日之示', description: '当下之象' }] },
19
+ { id: 'three', name: '三张 · 过去当下未来', description: '经典时间线', positions: [
20
+ { id: 0, name: '过去之象', description: '事情的根源与铺垫' },
21
+ { id: 1, name: '当下之境', description: '此刻的核心状况' },
22
+ { id: 2, name: '未来之趋', description: '自然趋势与方向' },
23
+ ] },
24
+ { id: 'two-options', name: '二选一', description: '两个选择对比', positions: [
25
+ { id: 0, name: '选项甲', description: '第一个选择的能量' },
26
+ { id: 1, name: '选项乙', description: '第二个选择的能量' },
27
+ ] },
28
+ { id: 'mind-body', name: '身 · 心 · 灵', description: '三个层面观照内在', positions: [
29
+ { id: 0, name: '身体', description: '身体层面之需' },
30
+ { id: 1, name: '心智', description: '思维与情绪之课' },
31
+ { id: 2, name: '灵性', description: '灵魂与高我之引' },
32
+ ] },
33
+ { id: 'problem', name: '问题 · 障碍 · 建议 · 结果', description: '四张看清一件事', positions: [
34
+ { id: 0, name: '问题本质', description: '这件事的核心' },
35
+ { id: 1, name: '主要障碍', description: '阻碍的能量' },
36
+ { id: 2, name: '建议行动', description: '可为之径' },
37
+ { id: 3, name: '可能结果', description: '顺势之归' },
38
+ ] },
39
+ { id: 'celtic', name: '凯尔特十字(十张)', description: '深入全局之阵', positions: [
40
+ { id: 0, name: '现况', description: '当下的中心' },
41
+ { id: 1, name: '助力', description: '当下之助力' },
42
+ { id: 2, name: '根基', description: '潜藏之基' },
43
+ { id: 3, name: '已逝', description: '渐远的过去' },
44
+ { id: 4, name: '将至', description: '渐近的未来' },
45
+ { id: 5, name: '所惧', description: '心中的顾虑' },
46
+ { id: 6, name: '己身', description: '自身之态度' },
47
+ { id: 7, name: '环境', description: '外在之环境' },
48
+ { id: 8, name: '期望', description: '亲友之期望' },
49
+ { id: 9, name: '终局', description: '大势之归' },
50
+ ] },
51
+ ];
52
+
53
+ // 自定义牌阵存储(localStorage)
54
+ const KEY = 'guanwei_custom_tarot_spreads';
55
+
56
+ export function getCustomTarotSpreads(): TarotSpread[] {
57
+ try {
58
+ const data = localStorage.getItem(KEY);
59
+ return data ? JSON.parse(data) : [];
60
+ } catch {
61
+ return [];
62
+ }
63
+ }
64
+
65
+ export function saveCustomTarotSpread(spread: TarotSpread): void {
66
+ const list = getCustomTarotSpreads();
67
+ const idx = list.findIndex(s => s.id === spread.id);
68
+ if (idx >= 0) list[idx] = spread; else list.push(spread);
69
+ localStorage.setItem(KEY, JSON.stringify(list));
70
+ }
71
+
72
+ export function deleteCustomTarotSpread(id: string): void {
73
+ localStorage.setItem(KEY, JSON.stringify(getCustomTarotSpreads().filter(s => s.id !== id)));
74
+ }
75
+
76
+ export function allTarotSpreads(): TarotSpread[] {
77
+ return [...TAROT_SPREADS, ...getCustomTarotSpreads()];
78
+ }
@@ -0,0 +1,18 @@
1
+ // 小六壬掌诀(移植参考项目 data.js)
2
+
3
+ export const XLR: Record<string, { ji: string; wx: string; num: string; dir: string; poem: string; text: string }> = {
4
+ 大安: { ji: '吉', wx: '木', num: '一、五、七', dir: '东方', poem: '身不动时,属木青龙,谋事主一、五、七。',
5
+ text: '大安事事昌,求谋在东方,失物不远去,宅舍保安康。行人身未动,病者主无妨,将军回田野,仔细好推详。' },
6
+ 留连: { ji: '凶', wx: '水', num: '二、八、十', dir: '南方', poem: '卒未归时,属水玄武,谋事主二、八、十。',
7
+ text: '留连事难成,求谋日未明,官事只宜缓,去者未回程。失物南方见,急讨方称心,更须防口舌,人口且平平。' },
8
+ 速喜: { ji: '吉', wx: '火', num: '三、六、九', dir: '南方', poem: '人即至时,属火朱雀,谋事主三、六、九。',
9
+ text: '速喜喜来临,求财向南行,失物申未午,逢人路上寻。官事有福德,病者无祸侵,田宅六畜吉,行人有信音。' },
10
+ 赤口: { ji: '凶', wx: '金', num: '四、七、十', dir: '西方', poem: '官事凶时,属金白虎,谋事主四、七、十。',
11
+ text: '赤口主口舌,官非切要防,失物急去寻,行人有惊慌。鸡犬多作怪,病者出西方,更须防咀咒,恐怕染瘟殃。' },
12
+ 小吉: { ji: '吉', wx: '水', num: '一、五、七', dir: '东北', poem: '人来喜时,属水六合,谋事主一、五、七。',
13
+ text: '小吉最吉昌,路上好商良,阳人来报喜,失物在坤方。行人立便至,交关真是强,凡事皆和合,病者叩穹苍。' },
14
+ 空亡: { ji: '凶', wx: '土', num: '二、六、九', dir: '西北', poem: '音信稀时,属土勾陈,谋事主二、六、九。',
15
+ text: '空亡事不长,阴人多乖张,求财无利益,行人有灾殃。失物寻不见,官事主刑伤,病人逢暗鬼,解禳保安康。' },
16
+ };
17
+
18
+ export const XLR_ORDER = ['大安', '留连', '速喜', '赤口', '小吉', '空亡'];
@@ -0,0 +1,97 @@
1
+ // 紫微斗数数据(移植参考项目 data.js)
2
+ // 补齐层(2026-08-20):生年四化 / 主星庙旺落陷 / 格局库 / 辅星安星起法
3
+
4
+ export const ZW_STARS: Record<string, string> = {
5
+ 紫微: '帝星', 天机: '智星', 太阳: '官禄', 武曲: '财星', 天同: '福星', 廉贞: '囚星',
6
+ 天府: '库星', 太阴: '田宅', 贪狼: '桃花', 巨门: '暗星', 天相: '印星', 天梁: '荫星', 七杀: '将星', 破军: '耗星',
7
+ };
8
+
9
+ export const PALACE_NAMES = ['命宫', '兄弟', '夫妻', '子女', '财帛', '疾厄', '迁移', '仆役', '官禄', '田宅', '福德', '父母'];
10
+
11
+ export const ZW_STAR_MEAN: Record<string, string> = {
12
+ 紫微: '至尊之星,主尊贵、格局、领导力;得辅佐则贵显,孤坐则刚愎。',
13
+ 天机: '善变之星,主智慧、机巧、变动;宜谋略不宜实干。',
14
+ 太阳: '光明之星,主贵气、名声、付出;庙旺则显达,落陷则操劳。',
15
+ 武曲: '刚毅之星,主财富、决断、执行;财帛宫得之尤利。',
16
+ 天同: '福寿之星,主安逸、福气、圆融;与人为善,懒于进取。',
17
+ 廉贞: '次桃花星,主才华、贞操、是非;化气为囚,处世宜慎。',
18
+ 天府: '库府之星,主积蓄、安定、包容;得众星拱卫则富厚。',
19
+ 太阴: '月亮之星,主田宅、财富、内敛;庙旺主富,落陷多劳。',
20
+ 贪狼: '桃花之星,主欲望、才艺、交际;善变多能,戒之在贪。',
21
+ 巨门: '暗曜之星,主口才、是非、思虑;化气为暗,谨言慎行。',
22
+ 天相: '辅佐之星,主印信、协调、仪表;端庄守礼,为人之印。',
23
+ 天梁: '荫庇之星,主清高、寿元、化解;逢凶化吉,孤而不群。',
24
+ 七杀: '肃杀之星,主魄力、变革、冲劲;成败皆烈,宜配吉曜。',
25
+ 破军: '耗损之星,主破旧、开创、动荡;先破后立,劳而有成。',
26
+ };
27
+
28
+ // ─── 辅星名与类 ───
29
+ export const ZW_FU_STARS: Record<string, string> = {
30
+ 左辅: '辅曜', 右弼: '辅曜', 文昌: '文曜', 文曲: '文曜', 天魁: '贵人', 天钺: '贵人',
31
+ 禄存: '财曜', 擎羊: '煞曜', 陀罗: '煞曜', 火星: '煞曜', 铃星: '煞曜', 地空: '煞曜', 地劫: '煞曜', 天马: '动曜',
32
+ };
33
+
34
+ // ─── 生年四化(十天干)───
35
+ export const ZW_SIHUA: Record<string, { lu: string; quan: string; ke: string; ji: string }> = {
36
+ 甲: { lu: '廉贞', quan: '破军', ke: '武曲', ji: '太阳' },
37
+ 乙: { lu: '天机', quan: '天梁', ke: '紫微', ji: '太阴' },
38
+ 丙: { lu: '天同', quan: '天机', ke: '文昌', ji: '廉贞' },
39
+ 丁: { lu: '太阴', quan: '天同', ke: '天机', ji: '巨门' },
40
+ 戊: { lu: '贪狼', quan: '太阴', ke: '右弼', ji: '天机' },
41
+ 己: { lu: '武曲', quan: '贪狼', ke: '天梁', ji: '文曲' },
42
+ 庚: { lu: '太阳', quan: '武曲', ke: '太阴', ji: '天同' },
43
+ 辛: { lu: '巨门', quan: '太阳', ke: '文曲', ji: '文昌' },
44
+ 壬: { lu: '天梁', quan: '紫微', ke: '左辅', ji: '武曲' },
45
+ 癸: { lu: '破军', quan: '巨门', ke: '太阴', ji: '贪狼' },
46
+ };
47
+
48
+ // ─── 十四主星庙旺落陷(12 宫序:寅卯辰巳午未申酉戌亥子丑)───
49
+ // 表源:紫微斗数通行庙旺表(斗数全书系统),待逐字校核(与断语库同 seed 策略)
50
+ export const ZW_BRIGHTNESS: Record<string, string[]> = {
51
+ 紫微: ['庙', '旺', '得', '旺', '庙', '旺', '得', '旺', '庙', '旺', '庙', '旺'],
52
+ 天机: ['得', '庙', '得', '利', '平', '利', '得', '庙', '得', '利', '平', '得'],
53
+ 太阳: ['庙', '庙', '旺', '旺', '旺', '旺', '得', '得', '平', '平', '陷', '陷'],
54
+ 武曲: ['得', '利', '庙', '旺', '旺', '庙', '得', '庙', '庙', '旺', '旺', '得'],
55
+ 天同: ['平', '平', '利', '庙', '庙', '庙', '旺', '陷', '平', '旺', '旺', '平'],
56
+ 廉贞: ['平', '庙', '庙', '庙', '庙', '庙', '平', '平', '平', '利', '利', '平'],
57
+ 天府: ['庙', '庙', '得', '得', '庙', '庙', '得', '得', '庙', '庙', '得', '庙'],
58
+ 太阴: ['平', '平', '利', '陷', '陷', '陷', '利', '庙', '旺', '庙', '庙', '旺'],
59
+ 贪狼: ['平', '庙', '利', '旺', '陷', '平', '平', '庙', '庙', '旺', '平', '利'],
60
+ 巨门: ['庙', '旺', '利', '陷', '旺', '利', '庙', '旺', '平', '陷', '旺', '庙'],
61
+ 天相: ['庙', '得', '庙', '得', '庙', '庙', '得', '得', '庙', '庙', '得', '庙'],
62
+ 天梁: ['庙', '庙', '庙', '得', '得', '庙', '庙', '庙', '庙', '庙', '平', '得'],
63
+ 七杀: ['旺', '旺', '得', '旺', '旺', '旺', '得', '旺', '旺', '旺', '得', '旺'],
64
+ 破军: ['庙', '陷', '庙', '陷', '庙', '陷', '庙', '陷', '庙', '陷', '庙', '陷'],
65
+ };
66
+
67
+ // ─── 辅星安星起法(年干/年支/月/时)───
68
+ // 禄存(年干)
69
+ export const LU_CUN: Record<string, string> = { 甲: '寅', 乙: '卯', 丙: '巳', 丁: '午', 戊: '巳', 己: '午', 庚: '申', 辛: '酉', 壬: '亥', 癸: '子' };
70
+ // 天魁天钺(年干)
71
+ export const KUI_YUE: Record<string, string[]> = { 甲: ['丑', '未'], 戊: ['丑', '未'], 庚: ['丑', '未'], 乙: ['子', '申'], 己: ['子', '申'], 丙: ['亥', '酉'], 丁: ['亥', '酉'], 壬: ['卯', '巳'], 癸: ['卯', '巳'], 辛: ['午', '寅'] };
72
+ // 天马(年支三合驿马)
73
+ export const TIAN_MA: Record<string, string> = { 申: '寅', 子: '寅', 辰: '寅', 寅: '申', 午: '申', 戌: '申', 巳: '亥', 酉: '亥', 丑: '亥', 亥: '巳', 卯: '巳', 未: '巳' };
74
+ // 火星起宫(年支三合 → 起宫 + 顺行)
75
+ export const HUO_START: Record<string, string> = { 寅: '丑', 午: '丑', 戌: '丑', 申: '寅', 子: '寅', 辰: '寅', 巳: '卯', 酉: '卯', 丑: '卯', 亥: '酉', 卯: '酉', 未: '酉' };
76
+ // 铃星起宫(年支三合 → 起宫 + 逆行)
77
+ export const LING_START: Record<string, string> = { 寅: '卯', 午: '卯', 戌: '卯', 申: '戌', 子: '戌', 辰: '戌', 巳: '戌', 酉: '戌', 丑: '戌', 亥: '戌', 卯: '戌', 未: '戌' };
78
+
79
+ // ─── 格局库(识别规则在 engine,此处为描述表)───
80
+ export interface GejuDef { name: string; ji: '吉' | '凶' | '平'; desc: string; }
81
+ export const ZW_GEJU: Record<string, GejuDef> = {
82
+ 杀破狼: { name: '杀破狼', ji: '平', desc: '七杀、破军、贪狼三合会照。一生变动开创,先破后立;多有起伏,亦多机遇,宜从事开创性、变动性事业。' },
83
+ 紫府同宫: { name: '紫府同宫', ji: '吉', desc: '紫微、天府同守一宫(寅、申)。双帝星坐命,稳重掌权,财库厚实,是安定型的贵格。' },
84
+ 紫微朝垣: { name: '紫微朝垣', ji: '吉', desc: '紫微独坐午宫,尊贵显达,有领导之象;格局高者主大贵。' },
85
+ 机月同梁: { name: '机月同梁', ji: '平', desc: '天机、太阴、天同、天梁多星组合照会。善谋略、宜公职或专业技术,为人机敏温和。' },
86
+ 日月同宫: { name: '日月同宫', ji: '吉', desc: '太阳、太阴同守丑、未宫。日月并明,阴阳调和,主贵而富,但丑未为陷地,须看庙陷定论。' },
87
+ 日月反背: { name: '日月反背', ji: '凶', desc: '太阳、太阴皆落陷(如太阳子丑、太阴巳午未),阴阳失序,主劳碌奔波、与父母缘分淡。' },
88
+ 月朗天门: { name: '月朗天门', ji: '吉', desc: '太阴独坐亥宫庙旺。月照天门,清贵之象,主聪明秀气,名利双收。' },
89
+ 火贪格: { name: '火贪格', ji: '平', desc: '火星与贪狼同宫或三合。暴发之格,财富来得快、去得也快,宜见好就收。' },
90
+ 铃贪格: { name: '铃贪格', ji: '平', desc: '铃星与贪狼同宫或三合。横发之格,突发机遇获利,成败皆烈。' },
91
+ 石中隐玉: { name: '石中隐玉', ji: '吉', desc: '巨门坐子、午宫(尤以辛年生人)。口才出众,才华内敛,大器晚成。' },
92
+ 君臣庆会: { name: '君臣庆会', ji: '吉', desc: '紫微得左辅右弼拱照,或昌曲同会。君臣得位,主贵显,得贵人扶持。' },
93
+ 禄马交驰: { name: '禄马交驰', ji: '吉', desc: '禄存与天马同宫或三合。财源流动,动中得财,宜外出发展、经商。' },
94
+ 科权禄拱命: { name: '科权禄拱命', ji: '吉', desc: '命宫三合方有化禄、化权、化科拱照。三奇拱命,富贵双全之格。' },
95
+ 廉贞七杀: { name: '廉贞七杀', ji: '平', desc: '廉贞七杀同守丑、未宫。英雄之格,魄力与是非并存,宜纪律性行业。' },
96
+ 命无正曜: { name: '命无正曜', ji: '平', desc: '命宫无十四主星,借对宫星曜为用。性情受环境牵动,宜借外力补足。' },
97
+ };
@@ -0,0 +1,20 @@
1
+ // 星盘数据(移植参考项目 data.js)
2
+
3
+ export const ZODIAC: [string, string, string][] = [
4
+ ['白羊', 'Aries', '♈'], ['金牛', 'Taurus', '♉'], ['双子', 'Gemini', '♊'], ['巨蟹', 'Cancer', '♋'],
5
+ ['狮子', 'Leo', '♌'], ['处女', 'Virgo', '♍'], ['天秤', 'Libra', '♎'], ['天蝎', 'Scorpio', '♏'],
6
+ ['射手', 'Sagittarius', '♐'], ['摩羯', 'Capricorn', '♑'], ['水瓶', 'Aquarius', '♒'], ['双鱼', 'Pisces', '♓'],
7
+ ];
8
+
9
+ export const PLANETS: [string, string, string][] = [
10
+ ['太阳', '☉', 'sun'], ['月亮', '☽', 'moon'], ['水星', '☿', 'merc'], ['金星', '♀', 'ven'],
11
+ ['火星', '♂', 'mars'], ['木星', '♃', 'jup'], ['土星', '♄', 'sat'],
12
+ ];
13
+
14
+ export const ZODIAC_MEAN: Record<string, string> = {
15
+ 白羊: '开创 · 冲劲 · 自我', 金牛: '安定 · 感官 · 坚持', 双子: '思辨 · 灵动 · 交流', 巨蟹: '情感 · 归属 · 守护',
16
+ 狮子: '表达 · 尊严 · 创造', 处女: '精进 · 服务 · 审慎', 天秤: '平衡 · 关系 · 审美', 天蝎: '深潜 · 转化 · 掌控',
17
+ 射手: '远志 · 探索 · 信念', 摩羯: '务实 · 担当 · 成就', 水瓶: '革新 · 独立 · 洞见', 双鱼: '共情 · 想象 · 融通',
18
+ };
19
+
20
+ export const HOUSES = ['命宫', '财帛', '兄弟', '田宅', '子女', '仆役', '夫妻', '疾厄', '迁移', '官禄', '福德', '相貌'];