v3.6.0 同步发版

- 交互契约改对话式询问(禁点选工具、一次1~2问、先铺垫再问),规则库 3.6
- 盘面图默认生成+chart_shot 截图;品牌区、大运/流年小卡信息补全、交流群二维码卡
- day_boundary 子正换日;memory_save --update-chart 换盘留痕;share_export 事件简介+城市级地域
- share_push 失败诊断+代理引导;_runtime_guard;修 STAR_ORDER 三奇贵人 KeyError
- README 规则计数 541→542;manifest 重建(1275 文件);测试 142 项全绿
This commit is contained in:
2026-10-10 13:13:41 +08:00
parent f725df8ff6
commit 433c9b4a41
32 changed files with 1468 additions and 389 deletions
Binary file not shown.

After

Width:  |  Height:  |  Size: 30 KiB

Binary file not shown.
Binary file not shown.

After

Width:  |  Height:  |  Size: 54 KiB

+71 -117
View File
@@ -2,52 +2,24 @@
"""ChartData → 单文件 HTML 盘面(模板 render/templates/paipan.html)。
与 Markdown 输出同为纯计算渲染:十神/藏干/空亡/纳音/十二长生/月令旺衰
均为固定查表,不含任何命理判断(P1)。
均为固定查表,不含任何命理判断(P1)。岁运干支(大运/流年格)的十神/
星运/空亡/纳音复用 chart/ganzhi.py,神煞复用 chart/shensha.py 的岁运
求值器 evaluate_target(与 query_shensha.py 同一口径),不在渲染层
另造规则。
logo 与品牌字体(render/assets/)以 base64 内嵌,保证 HTML 单文件便携;
神煞条目的查法注释(全角/半角括号)仅在本装配层剔除,Markdown 输出保持原样。
"""
import base64
import re
from datetime import datetime
from pathlib import Path
from chart.luck import luck_label_for_year
from chart.ganzhi import NAYIN, WX_GAN, WX_ZHI, gz_void, ten_god, terrain
from chart.shensha import evaluate_target
GAN = "甲乙丙丁戊己庚辛壬癸"
ZHI = "子丑寅卯辰巳午未申酉戌亥"
_WX_GAN = {"甲": "木", "乙": "木", "丙": "火", "丁": "火", "戊": "土",
"己": "土", "庚": "金", "辛": "金", "壬": "水", "癸": "水"}
_WX_ZHI = {"子": "水", "丑": "土", "寅": "木", "卯": "木", "辰": "土",
"巳": "火", "午": "火", "未": "土", "申": "金", "酉": "金",
"戌": "土", "亥": "水"}
_YY = {"甲": 1, "丙": 1, "戊": 1, "庚": 1, "壬": 1,
"乙": -1, "丁": -1, "己": -1, "辛": -1, "癸": -1}
_SHENG = {"木": "火", "火": "土", "土": "金", "金": "水", "水": "木"}
_KE = {"木": "土", "土": "水", "水": "火", "火": "金", "金": "木"}
_HIDDEN = {"子": ["癸"], "丑": ["己", "癸", "辛"], "寅": ["甲", "丙", "戊"],
"卯": ["乙"], "辰": ["戊", "乙", "癸"], "巳": ["丙", "戊", "庚"],
"午": ["丁", "己"], "未": ["己", "丁", "乙"],
"申": ["庚", "壬", "戊"], "酉": ["辛"], "戌": ["戊", "辛", "丁"],
"亥": ["壬", "甲"]}
_NAYIN = {"甲子": "海中金", "乙丑": "海中金", "丙寅": "炉中火", "丁卯": "炉中火",
"戊辰": "大林木", "己巳": "大林木", "庚午": "路旁土", "辛未": "路旁土",
"壬申": "剑锋金", "癸酉": "剑锋金", "甲戌": "山头火", "乙亥": "山头火",
"丙子": "涧下水", "丁丑": "涧下水", "戊寅": "城头土", "己卯": "城头土",
"庚辰": "白蜡金", "辛巳": "白蜡金", "壬午": "杨柳木", "癸未": "杨柳木",
"甲申": "泉中水", "乙酉": "泉中水", "丙戌": "屋上土", "丁亥": "屋上土",
"戊子": "霹雳火", "己丑": "霹雳火", "庚寅": "松柏木", "辛卯": "松柏木",
"壬辰": "长流水", "癸巳": "长流水", "甲午": "沙中金", "乙未": "沙中金",
"丙申": "山下火", "丁酉": "山下火", "戊戌": "平地木", "己亥": "平地木",
"庚子": "壁上土", "辛丑": "壁上土", "壬寅": "金箔金", "癸卯": "金箔金",
"甲辰": "覆灯火", "乙巳": "覆灯火", "丙午": "天河水", "丁未": "天河水",
"戊申": "大驿土", "己酉": "大驿土", "庚戌": "钗钏金", "辛亥": "钗钏金",
"壬子": "桑柘木", "癸丑": "桑柘木", "甲寅": "大溪水", "乙卯": "大溪水",
"丙辰": "沙中土", "丁巳": "沙中土", "戊午": "天上火", "己未": "天上火",
"庚申": "石榴木", "辛酉": "石榴木", "壬戌": "大海水", "癸亥": "大海水"}
_TERRAIN_ORDER = ["长生", "沐浴", "冠带", "临官", "帝旺", "衰",
"病", "死", "墓", "绝", "胎", "养"]
_TERRAIN_START = {"甲": "亥", "乙": "午", "丙": "寅", "戊": "寅", "庚": "巳",
"壬": "申", "丁": "酉", "己": "酉", "辛": "子", "癸": "卯"}
_WX_CLASS = {"木": "wx-mu", "火": "wx-huo", "土": "wx-tu",
"金": "wx-jin", "水": "wx-shui"}
_SEASON = {"寅": ("木", "火", "水", "金", "土"), "卯": ("木", "火", "水", "金", "土"),
"巳": ("火", "土", "木", "水", "金"), "午": ("火", "土", "木", "水", "金"),
@@ -57,43 +29,22 @@ _SEASON = {"寅": ("木", "火", "水", "金", "土"), "卯": ("木", "火", "
"丑": ("土", "金", "火", "木", "水"), "未": ("土", "金", "火", "木", "水")}
_SEASON_LABEL = ("旺", "相", "休", "囚", "死")
_ASSETS_DIR = Path(__file__).resolve().parent / "assets"
_SHA_NOTE_RE = re.compile(r"[((][^))]*[))]")
def _strip_sha_note(entry: str) -> str:
"""神煞条目剔除查法括号注释:`将星(以年支子查,见于月支子)` → `将星`。"""
return _SHA_NOTE_RE.sub("", entry).strip()
def _asset_b64(name: str) -> str:
return base64.b64encode((_ASSETS_DIR / name).read_bytes()).decode("ascii")
def _wx(ch: str) -> str:
wx = _WX_GAN.get(ch) or _WX_ZHI.get(ch)
return {"木": "wx-mu", "火": "wx-huo", "土": "wx-tu",
"金": "wx-jin", "水": "wx-shui"}.get(wx, "")
def _ten_god(me: str, other: str) -> str:
wx_m, wx_o = _WX_GAN[me], _WX_GAN[other]
same = _YY[me] == _YY[other]
if wx_m == wx_o:
return "比肩" if same else "劫财"
if _SHENG[wx_m] == wx_o:
return "食神" if same else "伤官"
if _KE[wx_m] == wx_o:
return "偏财" if same else "正财"
if _KE[wx_o] == wx_m:
return "七杀" if same else "正官"
return "偏印" if same else "正印"
def _terrain(me: str, zhi: str) -> str:
"""日干对支的十二长生(阳顺阴逆)。"""
start = _ZHI_IDX(_TERRAIN_START[me])
zi = _ZHI_IDX(zhi)
off = (zi - start) % 12 if _YY[me] == 1 else (start - zi) % 12
return _TERRAIN_ORDER[off]
def _ZHI_IDX(z: str) -> int:
return ZHI.index(z)
def _gz_void(gz: str) -> str:
"""一柱干支的旬空两支。"""
g, z = GAN.index(gz[0]), ZHI.index(gz[1])
return ZHI[(z - g + 10) % 12] + ZHI[(z - g + 11) % 12]
wx = WX_GAN.get(ch) or WX_ZHI.get(ch)
return _WX_CLASS.get(wx, "")
def _z(zhi: str, voids: str) -> str:
@@ -111,51 +62,21 @@ def _pillar_col(chart, key: str, label: str, cur: bool = False) -> dict:
"gan": p.gan, "zhi": p.zhi,
"hidden": list(zip(p.hidden_stems, p.hidden_ten_gods)),
"terrain": chart.terrain.get(key, ""),
"void": _gz_void(p.gan + p.zhi),
"void": gz_void(p.gan + p.zhi),
"nayin": p.na_yin or "",
"shensha": chart.shen_sha.get(key, []),
"shensha": [_strip_sha_note(s) for s in chart.shen_sha.get(key, [])],
"voids": voids,
}
def _extra_col(chart, gz: str, label: str, cur: bool) -> dict:
"""流年/大运列(仅干支行星级信息,神煞留空)。"""
me = chart.day_master
voids = chart.void.get("day", "") + chart.void.get("hour", "")
return {
"label": label, "cur": cur,
"god": _ten_god(me, gz[0]),
"gan": gz[0], "zhi": gz[1],
"hidden": [(h, _ten_god(me, h)) for h in _HIDDEN[gz[1]]],
"terrain": _terrain(me, gz[1]),
"void": _gz_void(gz),
"nayin": _NAYIN[gz],
"shensha": [],
"voids": voids,
}
def _main_table(chart) -> str:
this_year = datetime.now().year
ly = next(((y, gz) for y, gz, _ in chart.flow_years if y == this_year), None)
cur_luck = None
for lp in chart.luck_pillars:
if lp.start_year <= this_year:
cur_luck = lp
cols = []
if ly:
cols.append(_extra_col(chart, ly[1], f"流年 {ly[0]}", cur=True))
if cur_luck:
cols.append(_extra_col(chart, cur_luck.gan + cur_luck.zhi, "大运", cur=True))
for key, label in (("year", "年柱"), ("month", "月柱"),
("day", "日柱"), ("hour", "时柱")):
cols.append(_pillar_col(chart, key, label, cur=(key == "day")))
def _render_table(cols: list) -> str:
"""干支列列表 → 排盘表格(主星/天干/地支/藏干/星运/空亡/纳音/神煞)。"""
def th(c):
tag = '<span class="curtag">当前</span>' if c["cur"] else ""
return f'<th class="{"cur" if c["cur"] else ""}">{c["label"]}{tag}</th>'
def row(label, fn, cls=""):
def row(label, fn):
tds = "".join(
f'<td class="{"cur" if c["cur"] else ""}">{fn(c)}</td>' for c in cols)
return f'<tr><td class="rowlabel">{label}</td>{tds}</tr>'
@@ -178,6 +99,34 @@ def _main_table(chart) -> str:
return '<table class="pan">' + "".join(rows) + "</table>"
def _main_table(chart) -> str:
"""排盘卡:仅年月日时四柱。"""
cols = [_pillar_col(chart, key, label, cur=(key == "day"))
for key, label in (("year", "年柱"), ("month", "月柱"),
("day", "日柱"), ("hour", "时柱"))]
return _render_table(cols)
def _target_shensha(chart, gz: str) -> list[str]:
"""岁运干支相对本命锚点所逢的神煞(与 query_shensha.py 同一求值器)。"""
pillars = {k: getattr(chart, k).gan + getattr(chart, k).zhi
for k in ("year", "month", "day", "hour")}
return [star for star, _ in evaluate_target(pillars, chart.gender, gz)["anchored"]]
def _gz_sub(chart, gz: str) -> str:
"""岁运小卡的干支附属行:十神·星运 / 空亡 / 纳音 / 神煞(无标签,值本身说话)。"""
lines = [
f"{ten_god(chart.day_master, gz[0])} · {terrain(chart.day_master, gz[1])}",
gz_void(gz),
NAYIN[gz],
]
sha = _target_shensha(chart, gz)
if sha:
lines.append(" · ".join(sha))
return "".join(f'<div class="cs">{line}</div>' for line in lines)
def _luck_strip(chart) -> str:
this_year = datetime.now().year
cells = []
@@ -189,8 +138,7 @@ def _luck_strip(chart) -> str:
f'<div class="cy">{lp.start_year} · {lp.start_age:.0f} 岁起</div>'
f'<div class="cg"><span class="{_wx(lp.gan)}">{lp.gan}</span>'
f'<span class="{_wx(lp.zhi)}">{lp.zhi}</span></div>'
f'<div class="cy">{_ten_god(chart.day_master, lp.gan)}'
f' · {_terrain(chart.day_master, lp.zhi)}</div></div>')
f'{_gz_sub(chart, gz)}</div>')
return '<div class="strip">' + "".join(cells) + "</div>"
@@ -218,7 +166,8 @@ def _flow_years(chart) -> str:
cls += " jiao"
cells.append(f'<div class="{cls}"><div class="cy">{year}</div>'
f'<div class="cg"><span class="{_wx(gz[0])}">{gz[0]}</span>'
f'<span class="{_wx(gz[1])}">{gz[1]}</span></div></div>')
f'<span class="{_wx(gz[1])}">{gz[1]}</span></div>'
f'{_gz_sub(chart, gz)}</div>')
return '<div class="strip">' + "".join(cells) + "</div>"
@@ -258,14 +207,18 @@ def build_html(chart) -> str:
audit = (f"时区 {chart.tz_used}(夏令时:{'是' if chart.is_dst else '否'})· "
f"经度修正 {chart.lon_correction_min:+.2f} 分 · 均时差 {chart.eot_minutes:+.2f} 分 · "
f"日界 子初换日(23:00)· tyme4py {chart.library_version} · "
f"schema {chart.schema_version}")
f"日界 子初换日(23:00)")
audit_full = (f"{audit}· tyme4py {chart.library_version} · "
f"schema {chart.schema_version}")
mapping = {
"TITLE": f"命盘:{chart.name}",
"LOGO_B64": _asset_b64("lingyao-logo.png"),
"FONT_B64": _asset_b64("logo-font.ttf"),
"QRCODE_B64": _asset_b64("qun-qrcode.jpg"),
"NAME": chart.name,
"GENDER_LABEL": "乾造" if chart.gender == "男" else "坤造",
"DAY_MASTER": f"{chart.day_master}{_WX_GAN[chart.day_master]}",
"DAY_MASTER": f"{chart.day_master}{WX_GAN[chart.day_master]}",
"BIRTHPLACE": chart.birthplace,
"BIRTH_CLOCK": chart.birth_clock,
"TRUE_SOLAR": chart.true_solar_time,
@@ -282,6 +235,7 @@ def build_html(chart) -> str:
"FLOW_MONTHS": _flow_months(chart),
"SEASON_BAR": _season_bar(chart),
"AUDIT": audit,
"AUDIT_FULL": audit_full,
}
out = tpl
for k, v in mapping.items():
+139 -56
View File
@@ -5,24 +5,69 @@
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>{{TITLE}}</title>
<style>
@font-face {
font-family: 'LogoFont';
src: url("data:font/ttf;base64,{{FONT_B64}}") format("truetype");
font-weight: normal; font-style: normal; font-display: swap;
}
:root {
--ink: #2b2b2b; --muted: #8a8577; --line: #e6dfd2; --bg: #f7f4ee;
--card: #fffdf8; --accent: #b08d2f;
/* 灵爻妙解网站浅色主题(root.css :root.light + paipan_base.css) */
--ink: #222831;
--muted: rgba(27, 27, 27, .52);
--soft: rgba(27, 27, 27, .68);
--line: rgba(193, 188, 235, .55);
--card-line: rgba(193, 188, 235, .45);
--accent: #dc7b28; /* 网站浅色 primary-color-1 */
--accent-2: #f97316; /* 网站浅色 primary-color-3 */
--wx-mu: #2e7d32; --wx-huo: #c62828; --wx-tu: #8d6e63;
--wx-jin: #af8c2c; --wx-shui: #1565c0;
}
* { box-sizing: border-box; }
body {
margin: 0; padding: 24px; background: var(--bg); color: var(--ink);
margin: 0; padding: 28px 24px 32px; color: var(--ink);
font-family: -apple-system, "PingFang SC", "Microsoft YaHei", sans-serif;
background:
radial-gradient(ellipse 72% 58% at 78% 20%, rgba(150, 125, 245, .18), transparent 68%),
radial-gradient(ellipse 66% 48% at 20% 18%, rgba(112, 174, 255, .18), transparent 70%),
radial-gradient(ellipse 82% 52% at 52% 92%, rgba(84, 210, 200, .11), transparent 72%),
linear-gradient(135deg, #fbfcff 0%, #f5f8ff 42%, #f2edff 100%);
background-attachment: fixed;
}
.page { max-width: 980px; margin: 0 auto; }
.page { max-width: 1100px; margin: 0 auto; }
/* 顶部品牌区:base.html 抽屉顶部 .drawer-brand 的居中放大版 */
.brand-head {
display: flex; align-items: center; justify-content: center;
gap: 14px; margin: 2px 0 20px;
}
.brand-logo { width: 52px; height: 52px; display: block; }
.brand-name {
font-family: 'LogoFont', 'Kaiti SC', 'STKaiti', 'KaiTi', serif;
font-weight: 500; font-size: 30px; line-height: 1.25;
color: var(--ink); letter-spacing: 1px;
}
.brand-slogan {
margin-top: 3px; font-size: 11px; letter-spacing: 3.5px;
color: var(--muted);
}
/* 大卡片:网站左 panel(.panel)亮色风格 */
.panel {
border: 1px solid var(--line);
border-radius: 12px;
background: linear-gradient(135deg, rgba(255, 255, 255, 1), rgba(250, 252, 255, 1));
box-shadow: 0 16px 38px rgba(48, 56, 96, .12);
padding: 16px;
}
/* 面板内子卡 */
.card {
background: var(--card); border: 1px solid var(--line);
border-radius: 12px; padding: 18px 20px; margin-bottom: 16px;
box-shadow: 0 1px 3px rgba(60, 50, 30, .06);
background: #fff; border: 1px solid var(--card-line);
border-radius: 8px; padding: 16px 18px; margin-bottom: 14px;
box-shadow: 0 2px 8px rgba(48, 56, 96, .05);
}
/* 头部 */
.card:last-child { margin-bottom: 0; }
/* 用户信息 */
.head { display: flex; align-items: center; gap: 16px; flex-wrap: wrap; }
.name { font-size: 28px; font-weight: 700; font-family: "Kaiti SC", "STKaiti", "KaiTi", serif; }
.chip {
@@ -31,90 +76,128 @@
}
.meta { color: var(--muted); font-size: 13px; line-height: 1.9; }
.meta b { color: var(--ink); font-weight: 600; }
.meta.tech {
margin-top: 8px; padding-top: 8px; font-size: 12.5px;
border-top: 1px dashed var(--card-line);
}
.warn {
margin-top: 6px; padding: 6px 10px; border-radius: 8px;
background: #fdf3e3; border: 1px solid #ecd9ae; color: #8a6d1d; font-size: 12.5px;
}
/* 主排盘表 */
table.pan { width: 100%; border-collapse: collapse; table-layout: fixed; }
.pan th, .pan td {
border: 1px solid var(--line); text-align: center;
border: 1px solid var(--card-line); text-align: center;
padding: 6px 4px; vertical-align: middle; font-size: 14px;
}
.pan th { background: #f3eee2; color: #6b6455; font-weight: 600; }
.pan td.rowlabel { background: #f3eee2; color: #6b6455; font-size: 13px; width: 64px; }
.pan th { background: rgba(193, 188, 235, .18); color: var(--soft); font-weight: 600; }
.pan td.rowlabel { background: rgba(193, 188, 235, .14); color: var(--soft); font-size: 13px; width: 64px; }
.gz { font-family: "Kaiti SC", "STKaiti", "KaiTi", serif; font-size: 34px; line-height: 1.25; }
.god { font-size: 12.5px; color: #7a7264; }
.god { font-size: 12.5px; color: var(--muted); }
.god.day { color: var(--accent); font-weight: 600; }
.hidden { font-size: 12.5px; line-height: 1.7; color: #57503f; }
.hidden { font-size: 12.5px; line-height: 1.7; color: var(--soft); }
.hidden .hg { font-weight: 600; }
.small { font-size: 12.5px; color: #57503f; line-height: 1.7; }
.small { font-size: 12.5px; color: var(--soft); line-height: 1.7; }
.voidz { border-bottom: 2px dashed #c0392b; padding-bottom: 1px; }
.cur { background: #fdf6e0; }
.curtag { display:block; font-size: 11px; color: var(--accent); }
.cur { background: rgba(220, 123, 40, .07); }
.curtag { display: block; font-size: 11px; color: var(--accent); }
.wx-mu { color: var(--wx-mu); } .wx-huo { color: var(--wx-huo); }
.wx-tu { color: var(--wx-tu); } .wx-jin { color: var(--wx-jin); }
.wx-shui { color: var(--wx-shui); }
/* 条带 */
.sect { font-size: 15px; font-weight: 700; margin: 2px 0 10px;
font-family: "Kaiti SC", "STKaiti", "KaiTi", serif; }
.sect .note { font-size: 12px; color: var(--muted); font-weight: 400; margin-left: 8px; }
.strip { display: flex; flex-wrap: wrap; gap: 6px; }
.cell {
border: 1px solid var(--line); border-radius: 8px; background: #fff;
padding: 6px 8px; text-align: center; min-width: 64px;
border: 1px solid var(--card-line); border-radius: 8px; background: #fff;
padding: 6px 8px; text-align: center; min-width: 64px; max-width: 96px;
}
.cell .cy { font-size: 11px; color: var(--muted); }
.cell .cg { font-family: "Kaiti SC", "STKaiti", "KaiTi", serif; font-size: 20px; }
.cell.cur { border-color: var(--accent); background: #fdf6e0; box-shadow: 0 0 0 1px var(--accent) inset; }
.cell .cs { font-size: 11px; color: var(--muted); line-height: 1.55; margin-top: 1px; }
.cell.cur { border-color: var(--accent); background: rgba(220, 123, 40, .07); box-shadow: 0 0 0 1px var(--accent) inset; }
.cell.jiao .cy { color: #c0392b; }
/* 尾部交流群卡:左图右文 */
.qrcard { display: flex; align-items: center; gap: 16px; }
.qrimg { width: 108px; height: 108px; border-radius: 6px; display: block; }
.qrtitle { font-size: 15px; font-weight: 700;
font-family: "Kaiti SC", "STKaiti", "KaiTi", serif; }
.qrdesc { margin-top: 4px; font-size: 12.5px; color: var(--soft); line-height: 1.8; }
/* 页脚 */
.foot { color: var(--muted); font-size: 12px; line-height: 1.9; }
.brand { margin-top: 6px; color: #a89e86; }
.foot {
text-align: center; color: var(--muted); font-size: 12px;
line-height: 1.9; margin-top: 16px;
}
</style>
</head>
<body>
<div class="page">
<div class="card">
<div class="head">
<span class="name">{{NAME}}</span>
<span class="chip">{{GENDER_LABEL}}</span>
<span class="chip">日主 {{DAY_MASTER}}</span>
<div class="brand-head">
<img class="brand-logo" src="data:image/png;base64,{{LOGO_B64}}" alt="灵爻妙解">
<div class="brand-text">
<div class="brand-name">灵爻妙解</div>
<div class="brand-slogan">传承易学智慧</div>
</div>
<div class="meta" style="margin-top:8px">
出生地:<b>{{BIRTHPLACE}}</b> 钟表时间:<b>{{BIRTH_CLOCK}}</b> 真太阳时:<b>{{TRUE_SOLAR}}</b><br>
月令:{{MONTH_LING}} 胎元:{{TAI_YUAN}} 命宫:{{MING_GONG}} {{QIYUN}}
</div>
<div class="panel">
<div class="card">
<div class="head">
<span class="name">{{NAME}}</span>
<span class="chip">{{GENDER_LABEL}}</span>
<span class="chip">日主 {{DAY_MASTER}}</span>
</div>
<div class="meta" style="margin-top:8px">
出生地:<b>{{BIRTHPLACE}}</b> 钟表时间:<b>{{BIRTH_CLOCK}}</b> 真太阳时:<b>{{TRUE_SOLAR}}</b><br>
月令:{{MONTH_LING}} 胎元:{{TAI_YUAN}} 命宫:{{MING_GONG}} {{QIYUN}}
</div>
{{WARNINGS}}
<div class="meta tech">
{{SEASON_BAR}}<br>
{{AUDIT}}
</div>
<!-- 排盘审计原文(开发溯源,含版本信息):{{AUDIT_FULL}} -->
</div>
{{WARNINGS}}
<div class="card">
<div class="sect">排盘<span class="note">四柱 · 藏干 · 空亡 · 纳音 · 神煞</span></div>
{{MAIN_TABLE}}
</div>
<div class="card">
<div class="sect">大运<span class="note">{{LUCK_NOTE}}</span></div>
{{LUCK_STRIP}}
{{CHILDHOOD_BLOCK}}
</div>
<div class="card">
<div class="sect">流年<span class="note">出生年至今后十年,今年高亮,红字年份为交运年</span></div>
{{FLOW_YEARS}}
</div>
<div class="card">
<div class="sect">流月<span class="note">本年节气月</span></div>
{{FLOW_MONTHS}}
</div>
<div class="card qrcard">
<img class="qrimg" src="data:image/jpeg;base64,{{QRCODE_B64}}" alt="微信群二维码">
<div class="qrtext">
<div class="qrtitle">灵爻妙解 · 传统文化交流群</div>
<div class="qrdesc">扫码加入微信群:反馈解读偏差、交流文化心得、参与档案共建,版本更新动态第一时间同步。</div>
</div>
</div>
</div>
<div class="card">
<div class="sect">排盘<span class="note">四柱 · 藏干 · 空亡 · 纳音 · 神煞(当前大运与流年并列对照)</span></div>
{{MAIN_TABLE}}
</div>
<div class="card">
<div class="sect">大运<span class="note">{{LUCK_NOTE}}</span></div>
{{LUCK_STRIP}}
{{CHILDHOOD_BLOCK}}
</div>
<div class="card">
<div class="sect">流年<span class="note">出生年至今后十年,今年高亮,红字年份为交运年</span></div>
{{FLOW_YEARS}}
</div>
<div class="card">
<div class="sect">流月<span class="note">本年节气月</span></div>
{{FLOW_MONTHS}}
</div>
<div class="card foot">
{{SEASON_BAR}}<br>
{{AUDIT}}
<div class="brand">灵爻妙解 · 传承易学智慧 · 本盘面由 AI 生成,仅供娱乐参考</div>
</div>
<div class="foot">灵爻妙解 · 传承易学智慧 · 本盘面由 AI 生成,仅供娱乐参考</div>
</div>
</body>