docs(配图): 菜单树总览改由生成器画,登记钩子扫不到的图

- 新增 tools/arch_diagram/gen_menu_tree_svg.py → docs/images/菜单树总览
  .svg:43 节点 = 9 菜单 + 34 叶,30 已绑页 / 4 未绑 / 16 叶带密码门,
  最深 4 级;读数由脚本现算,不在文档里另抄一份
- 生成器内嵌 NODE_SRC(= ui_menu_tree.txt 的 43 行节点):在仓库里跑会
  与真值逐行互验、不一致 exit 1;generated-fresh 在临时目录重跑读不到
  真值 → 用内嵌表,输出逐字节一致(实测临时目录产物同 sha256)
- 产品需求 §8.2 原先挂的是级联框显示几何图(画的是示例级联,从来不含
  整棵树的节点),换成这张树图,并删掉「该图仍是 42 节点时代所画」的
  补丁注
- 图形绘制流程新增 §2.1:登记 generated-fresh 扫不到的四张图 —— 两张
  tools/refgraph 产物(引用关系图 / _模块,不在 gen_*.py 命名面里)+
  两张手画稿(级联框总览、级联框几何计算)。钩子只扫
  tools/arch_diagram/gen_*.py,这些图改了不会有人报警;再多一张先说清
  为什么不能用生成器
- arch_diagram README 脚本一览加行
This commit is contained in:
2026-10-30 00:19:20 +08:00
parent 98d46e626d
commit b49e226815
5 changed files with 712 additions and 3 deletions
+235
View File
@@ -0,0 +1,235 @@
<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" width="990" height="784" viewBox="0 0 990 784" font-family="Microsoft YaHei, Segoe UI, PingFang SC, sans-serif">
<rect width="990" height="784" fill="#ffffff"/>
<text x="28.0" y="40.0" font-size="17" fill="#0f172a" font-weight="bold" text-anchor="start">菜单树总览(层级 · 绑页 · 密码门)</text>
<text x="28.0" y="61.0" font-size="11" fill="#475569" font-weight="normal" text-anchor="start">方框 = 菜单(可展开);细线 = 叶(对应一个页面模块,或一处空缺)</text>
<rect x="138.0" y="87.0" width="486.0" height="18.0" rx="6" fill="#f8fafc" stroke="none" stroke-width="0"/>
<rect x="138.0" y="105.0" width="486.0" height="54.0" rx="6" fill="#ffffff" stroke="none" stroke-width="0"/>
<rect x="138.0" y="159.0" width="486.0" height="144.0" rx="6" fill="#f8fafc" stroke="none" stroke-width="0"/>
<rect x="138.0" y="303.0" width="486.0" height="90.0" rx="6" fill="#ffffff" stroke="none" stroke-width="0"/>
<rect x="138.0" y="393.0" width="486.0" height="72.0" rx="6" fill="#f8fafc" stroke="none" stroke-width="0"/>
<rect x="138.0" y="465.0" width="486.0" height="54.0" rx="6" fill="#ffffff" stroke="none" stroke-width="0"/>
<rect x="138.0" y="519.0" width="486.0" height="180.0" rx="6" fill="#f8fafc" stroke="none" stroke-width="0"/>
<rect x="296.0" y="159.0" width="328.0" height="72.0" rx="5" fill="#f5f3ff" stroke="none" stroke-width="0"/>
<rect x="296.0" y="231.0" width="328.0" height="72.0" rx="5" fill="#f5f3ff" stroke="none" stroke-width="0"/>
<line x1="128.0" y1="96.0" x2="128.0" y2="609.0" stroke="#cbd5e1" stroke-width="1.2"/>
<line x1="85.0" y1="352.5" x2="128.0" y2="352.5" stroke="#cbd5e1" stroke-width="1.2"/>
<line x1="128.0" y1="96.0" x2="153.0" y2="96.0" stroke="#cbd5e1" stroke-width="1.2"/>
<line x1="128.0" y1="132.0" x2="150.0" y2="132.0" stroke="#cbd5e1" stroke-width="1.2"/>
<line x1="128.0" y1="231.0" x2="150.0" y2="231.0" stroke="#cbd5e1" stroke-width="1.2"/>
<line x1="128.0" y1="348.0" x2="150.0" y2="348.0" stroke="#cbd5e1" stroke-width="1.2"/>
<line x1="128.0" y1="429.0" x2="150.0" y2="429.0" stroke="#cbd5e1" stroke-width="1.2"/>
<line x1="128.0" y1="492.0" x2="150.0" y2="492.0" stroke="#cbd5e1" stroke-width="1.2"/>
<line x1="128.0" y1="609.0" x2="150.0" y2="609.0" stroke="#cbd5e1" stroke-width="1.2"/>
<line x1="284.0" y1="114.0" x2="284.0" y2="150.0" stroke="#cbd5e1" stroke-width="1.2"/>
<line x1="216.0" y1="132.0" x2="284.0" y2="132.0" stroke="#cbd5e1" stroke-width="1.2"/>
<line x1="284.0" y1="114.0" x2="309.0" y2="114.0" stroke="#cbd5e1" stroke-width="1.2"/>
<line x1="284.0" y1="132.0" x2="309.0" y2="132.0" stroke="#cbd5e1" stroke-width="1.2"/>
<line x1="284.0" y1="150.0" x2="309.0" y2="150.0" stroke="#cbd5e1" stroke-width="1.2"/>
<line x1="284.0" y1="195.0" x2="284.0" y2="267.0" stroke="#cbd5e1" stroke-width="1.2"/>
<line x1="216.0" y1="231.0" x2="284.0" y2="231.0" stroke="#cbd5e1" stroke-width="1.2"/>
<line x1="284.0" y1="195.0" x2="306.0" y2="195.0" stroke="#cbd5e1" stroke-width="1.2"/>
<line x1="284.0" y1="267.0" x2="306.0" y2="267.0" stroke="#cbd5e1" stroke-width="1.2"/>
<line x1="452.0" y1="168.0" x2="452.0" y2="222.0" stroke="#cbd5e1" stroke-width="1.2"/>
<line x1="348.0" y1="195.0" x2="452.0" y2="195.0" stroke="#cbd5e1" stroke-width="1.2"/>
<line x1="452.0" y1="168.0" x2="477.0" y2="168.0" stroke="#cbd5e1" stroke-width="1.2"/>
<line x1="452.0" y1="186.0" x2="477.0" y2="186.0" stroke="#cbd5e1" stroke-width="1.2"/>
<line x1="452.0" y1="204.0" x2="477.0" y2="204.0" stroke="#cbd5e1" stroke-width="1.2"/>
<line x1="452.0" y1="222.0" x2="477.0" y2="222.0" stroke="#cbd5e1" stroke-width="1.2"/>
<line x1="452.0" y1="240.0" x2="452.0" y2="294.0" stroke="#cbd5e1" stroke-width="1.2"/>
<line x1="348.0" y1="267.0" x2="452.0" y2="267.0" stroke="#cbd5e1" stroke-width="1.2"/>
<line x1="452.0" y1="240.0" x2="477.0" y2="240.0" stroke="#cbd5e1" stroke-width="1.2"/>
<line x1="452.0" y1="258.0" x2="477.0" y2="258.0" stroke="#cbd5e1" stroke-width="1.2"/>
<line x1="452.0" y1="276.0" x2="477.0" y2="276.0" stroke="#cbd5e1" stroke-width="1.2"/>
<line x1="452.0" y1="294.0" x2="477.0" y2="294.0" stroke="#cbd5e1" stroke-width="1.2"/>
<line x1="284.0" y1="312.0" x2="284.0" y2="384.0" stroke="#cbd5e1" stroke-width="1.2"/>
<line x1="216.0" y1="348.0" x2="284.0" y2="348.0" stroke="#cbd5e1" stroke-width="1.2"/>
<line x1="284.0" y1="312.0" x2="309.0" y2="312.0" stroke="#cbd5e1" stroke-width="1.2"/>
<line x1="284.0" y1="330.0" x2="309.0" y2="330.0" stroke="#cbd5e1" stroke-width="1.2"/>
<line x1="284.0" y1="348.0" x2="309.0" y2="348.0" stroke="#cbd5e1" stroke-width="1.2"/>
<line x1="284.0" y1="366.0" x2="309.0" y2="366.0" stroke="#cbd5e1" stroke-width="1.2"/>
<line x1="284.0" y1="384.0" x2="309.0" y2="384.0" stroke="#cbd5e1" stroke-width="1.2"/>
<line x1="284.0" y1="402.0" x2="284.0" y2="456.0" stroke="#cbd5e1" stroke-width="1.2"/>
<line x1="216.0" y1="429.0" x2="284.0" y2="429.0" stroke="#cbd5e1" stroke-width="1.2"/>
<line x1="284.0" y1="402.0" x2="309.0" y2="402.0" stroke="#cbd5e1" stroke-width="1.2"/>
<line x1="284.0" y1="420.0" x2="309.0" y2="420.0" stroke="#cbd5e1" stroke-width="1.2"/>
<line x1="284.0" y1="438.0" x2="309.0" y2="438.0" stroke="#cbd5e1" stroke-width="1.2"/>
<line x1="284.0" y1="456.0" x2="309.0" y2="456.0" stroke="#cbd5e1" stroke-width="1.2"/>
<line x1="284.0" y1="474.0" x2="284.0" y2="510.0" stroke="#cbd5e1" stroke-width="1.2"/>
<line x1="216.0" y1="492.0" x2="284.0" y2="492.0" stroke="#cbd5e1" stroke-width="1.2"/>
<line x1="284.0" y1="474.0" x2="309.0" y2="474.0" stroke="#cbd5e1" stroke-width="1.2"/>
<line x1="284.0" y1="492.0" x2="309.0" y2="492.0" stroke="#cbd5e1" stroke-width="1.2"/>
<line x1="284.0" y1="510.0" x2="309.0" y2="510.0" stroke="#cbd5e1" stroke-width="1.2"/>
<line x1="284.0" y1="528.0" x2="284.0" y2="690.0" stroke="#cbd5e1" stroke-width="1.2"/>
<line x1="216.0" y1="609.0" x2="284.0" y2="609.0" stroke="#cbd5e1" stroke-width="1.2"/>
<line x1="284.0" y1="528.0" x2="309.0" y2="528.0" stroke="#cbd5e1" stroke-width="1.2"/>
<line x1="284.0" y1="546.0" x2="309.0" y2="546.0" stroke="#cbd5e1" stroke-width="1.2"/>
<line x1="284.0" y1="564.0" x2="309.0" y2="564.0" stroke="#cbd5e1" stroke-width="1.2"/>
<line x1="284.0" y1="582.0" x2="309.0" y2="582.0" stroke="#cbd5e1" stroke-width="1.2"/>
<line x1="284.0" y1="600.0" x2="309.0" y2="600.0" stroke="#cbd5e1" stroke-width="1.2"/>
<line x1="284.0" y1="618.0" x2="309.0" y2="618.0" stroke="#cbd5e1" stroke-width="1.2"/>
<line x1="284.0" y1="636.0" x2="309.0" y2="636.0" stroke="#cbd5e1" stroke-width="1.2"/>
<line x1="284.0" y1="654.0" x2="309.0" y2="654.0" stroke="#cbd5e1" stroke-width="1.2"/>
<line x1="284.0" y1="672.0" x2="309.0" y2="672.0" stroke="#cbd5e1" stroke-width="1.2"/>
<line x1="284.0" y1="690.0" x2="309.0" y2="690.0" stroke="#cbd5e1" stroke-width="1.2"/>
<rect x="28.0" y="339.5" width="57.0" height="26.0" rx="5" fill="#eff6ff" stroke="#1d4ed8" stroke-width="1.2"/>
<text x="56.5" y="357.2" font-size="13" fill="#1d4ed8" font-weight="bold" text-anchor="middle">主界面</text>
<rect x="150.0" y="121.0" width="66.0" height="22.0" rx="5" fill="#ffffff" stroke="#334155" stroke-width="1.2"/>
<text x="183.0" y="136.3" font-size="12" fill="#0f172a" font-weight="bold" text-anchor="middle">实时数据</text>
<rect x="150.0" y="220.0" width="66.0" height="22.0" rx="5" fill="#ffffff" stroke="#334155" stroke-width="1.2"/>
<text x="183.0" y="235.3" font-size="12" fill="#0f172a" font-weight="bold" text-anchor="middle">参数定值</text>
<rect x="306.0" y="185.0" width="42.0" height="20.0" rx="5" fill="#ffffff" stroke="#6d28d9" stroke-width="1.2"/>
<text x="327.0" y="199.3" font-size="12" fill="#6d28d9" font-weight="bold" text-anchor="middle">整定</text>
<rect x="306.0" y="257.0" width="42.0" height="20.0" rx="5" fill="#ffffff" stroke="#6d28d9" stroke-width="1.2"/>
<text x="327.0" y="271.3" font-size="12" fill="#6d28d9" font-weight="bold" text-anchor="middle">查看</text>
<rect x="150.0" y="337.0" width="66.0" height="22.0" rx="5" fill="#ffffff" stroke="#334155" stroke-width="1.2"/>
<text x="183.0" y="352.3" font-size="12" fill="#0f172a" font-weight="bold" text-anchor="middle">三遥设置</text>
<rect x="150.0" y="418.0" width="66.0" height="22.0" rx="5" fill="#ffffff" stroke="#334155" stroke-width="1.2"/>
<text x="183.0" y="433.3" font-size="12" fill="#0f172a" font-weight="bold" text-anchor="middle">装置维护</text>
<rect x="150.0" y="481.0" width="66.0" height="22.0" rx="5" fill="#ffffff" stroke="#334155" stroke-width="1.2"/>
<text x="183.0" y="496.3" font-size="12" fill="#0f172a" font-weight="bold" text-anchor="middle">通讯参数</text>
<rect x="150.0" y="598.0" width="66.0" height="22.0" rx="5" fill="#ffffff" stroke="#334155" stroke-width="1.2"/>
<text x="183.0" y="613.3" font-size="12" fill="#0f172a" font-weight="bold" text-anchor="middle">记录查询</text>
<line x1="152.0" y1="96.0" x2="159.0" y2="96.0" stroke="#cbd5e1" stroke-width="1.2"/>
<text x="164.0" y="100.0" font-size="12" fill="#0f172a" font-weight="normal" text-anchor="start">装置信息</text>
<text x="222.0" y="99.5" font-size="9" fill="#94a3b8" font-weight="normal" text-anchor="start">device_info</text>
<line x1="308.0" y1="114.0" x2="315.0" y2="114.0" stroke="#cbd5e1" stroke-width="1.2"/>
<text x="320.0" y="118.0" font-size="12" fill="#0f172a" font-weight="normal" text-anchor="start">交流量</text>
<text x="366.0" y="117.5" font-size="9" fill="#94a3b8" font-weight="normal" text-anchor="start">ac</text>
<line x1="308.0" y1="132.0" x2="315.0" y2="132.0" stroke="#cbd5e1" stroke-width="1.2"/>
<text x="320.0" y="136.0" font-size="12" fill="#0f172a" font-weight="normal" text-anchor="start">直流量</text>
<text x="366.0" y="135.5" font-size="9" fill="#94a3b8" font-weight="normal" text-anchor="start">dc</text>
<line x1="308.0" y1="150.0" x2="315.0" y2="150.0" stroke="#cbd5e1" stroke-width="1.2"/>
<text x="320.0" y="154.0" font-size="12" fill="#0f172a" font-weight="normal" text-anchor="start">遥信量</text>
<text x="366.0" y="153.5" font-size="9" fill="#94a3b8" font-weight="normal" text-anchor="start">yx</text>
<line x1="476.0" y1="168.0" x2="483.0" y2="168.0" stroke="#cbd5e1" stroke-width="1.2"/>
<text x="488.0" y="172.0" font-size="12" fill="#0f172a" font-weight="normal" text-anchor="start">参数</text>
<text x="522.0" y="171.5" font-size="9" fill="#94a3b8" font-weight="normal" text-anchor="start">view_param</text>
<text x="583.2" y="171.5" font-size="9" fill="#b45309" font-weight="bold" text-anchor="start">P</text>
<line x1="476.0" y1="186.0" x2="483.0" y2="186.0" stroke="#cbd5e1" stroke-width="1.2"/>
<text x="488.0" y="190.0" font-size="12" fill="#0f172a" font-weight="normal" text-anchor="start">定值</text>
<text x="522.0" y="189.5" font-size="9" fill="#94a3b8" font-weight="normal" text-anchor="start">view_set</text>
<text x="572.8" y="189.5" font-size="9" fill="#b45309" font-weight="bold" text-anchor="start">P</text>
<line x1="476.0" y1="204.0" x2="483.0" y2="204.0" stroke="#cbd5e1" stroke-width="1.2"/>
<text x="488.0" y="208.0" font-size="12" fill="#0f172a" font-weight="normal" text-anchor="start">控制字</text>
<text x="534.0" y="207.5" font-size="9" fill="#94a3b8" font-weight="normal" text-anchor="start">view_word</text>
<text x="590.0" y="207.5" font-size="9" fill="#b45309" font-weight="bold" text-anchor="start">P</text>
<line x1="476.0" y1="222.0" x2="483.0" y2="222.0" stroke="#cbd5e1" stroke-width="1.2"/>
<text x="488.0" y="226.0" font-size="12" fill="#0f172a" font-weight="normal" text-anchor="start">软压板</text>
<text x="534.0" y="225.5" font-size="9" fill="#94a3b8" font-weight="normal" text-anchor="start">view_plate</text>
<text x="595.2" y="225.5" font-size="9" fill="#b45309" font-weight="bold" text-anchor="start">P</text>
<line x1="476.0" y1="240.0" x2="483.0" y2="240.0" stroke="#cbd5e1" stroke-width="1.2"/>
<text x="488.0" y="244.0" font-size="12" fill="#0f172a" font-weight="normal" text-anchor="start">参数</text>
<text x="522.0" y="243.5" font-size="9" fill="#94a3b8" font-weight="normal" text-anchor="start">view_param</text>
<line x1="476.0" y1="258.0" x2="483.0" y2="258.0" stroke="#cbd5e1" stroke-width="1.2"/>
<text x="488.0" y="262.0" font-size="12" fill="#0f172a" font-weight="normal" text-anchor="start">定值</text>
<text x="522.0" y="261.5" font-size="9" fill="#94a3b8" font-weight="normal" text-anchor="start">view_set</text>
<line x1="476.0" y1="276.0" x2="483.0" y2="276.0" stroke="#cbd5e1" stroke-width="1.2"/>
<text x="488.0" y="280.0" font-size="12" fill="#0f172a" font-weight="normal" text-anchor="start">控制字</text>
<text x="534.0" y="279.5" font-size="9" fill="#94a3b8" font-weight="normal" text-anchor="start">view_word</text>
<line x1="476.0" y1="294.0" x2="483.0" y2="294.0" stroke="#cbd5e1" stroke-width="1.2"/>
<text x="488.0" y="298.0" font-size="12" fill="#0f172a" font-weight="normal" text-anchor="start">软压板</text>
<text x="534.0" y="297.5" font-size="9" fill="#94a3b8" font-weight="normal" text-anchor="start">view_plate</text>
<line x1="308.0" y1="312.0" x2="315.0" y2="312.0" stroke="#cbd5e1" stroke-width="1.2"/>
<text x="320.0" y="316.0" font-size="12" fill="#0f172a" font-weight="normal" text-anchor="start">遥测死区</text>
<text x="378.0" y="315.5" font-size="9" fill="#94a3b8" font-weight="normal" text-anchor="start">deadband</text>
<text x="428.8" y="315.5" font-size="9" fill="#b45309" font-weight="bold" text-anchor="start">P</text>
<line x1="308.0" y1="330.0" x2="315.0" y2="330.0" stroke="#cbd5e1" stroke-width="1.2"/>
<text x="320.0" y="334.0" font-size="12" fill="#0f172a" font-weight="normal" text-anchor="start">遥测系数</text>
<text x="378.0" y="333.5" font-size="9" fill="#94a3b8" font-weight="normal" text-anchor="start">calib</text>
<text x="413.1" y="333.5" font-size="9" fill="#b45309" font-weight="bold" text-anchor="start">P</text>
<line x1="308.0" y1="348.0" x2="315.0" y2="348.0" stroke="#be123c" stroke-width="1.2" stroke-dasharray="3,2"/>
<text x="320.0" y="352.0" font-size="12" fill="#be123c" font-weight="normal" text-anchor="start">遥信类型</text>
<text x="377.0" y="351.5" font-size="9" fill="#b45309" font-weight="bold" text-anchor="start">P</text>
<line x1="308.0" y1="366.0" x2="315.0" y2="366.0" stroke="#be123c" stroke-width="1.2" stroke-dasharray="3,2"/>
<text x="320.0" y="370.0" font-size="12" fill="#be123c" font-weight="normal" text-anchor="start">遥信防抖</text>
<text x="377.0" y="369.5" font-size="9" fill="#b45309" font-weight="bold" text-anchor="start">P</text>
<line x1="308.0" y1="384.0" x2="315.0" y2="384.0" stroke="#be123c" stroke-width="1.2" stroke-dasharray="3,2"/>
<text x="320.0" y="388.0" font-size="12" fill="#be123c" font-weight="normal" text-anchor="start">双点遥信</text>
<text x="377.0" y="387.5" font-size="9" fill="#b45309" font-weight="bold" text-anchor="start">P</text>
<line x1="308.0" y1="402.0" x2="315.0" y2="402.0" stroke="#cbd5e1" stroke-width="1.2"/>
<text x="320.0" y="406.0" font-size="12" fill="#0f172a" font-weight="normal" text-anchor="start">时钟设置</text>
<text x="378.0" y="405.5" font-size="9" fill="#94a3b8" font-weight="normal" text-anchor="start">clock</text>
<text x="413.1" y="405.5" font-size="9" fill="#b45309" font-weight="bold" text-anchor="start">P</text>
<line x1="308.0" y1="420.0" x2="315.0" y2="420.0" stroke="#cbd5e1" stroke-width="1.2"/>
<text x="320.0" y="424.0" font-size="12" fill="#0f172a" font-weight="normal" text-anchor="start">强制复归</text>
<text x="378.0" y="423.5" font-size="9" fill="#94a3b8" font-weight="normal" text-anchor="start">reset</text>
<text x="413.1" y="423.5" font-size="9" fill="#b45309" font-weight="bold" text-anchor="start">P</text>
<rect x="307.0" y="429.5" width="71.0" height="17.0" rx="4" fill="#f0fdf4" stroke="#15803d" stroke-width="0.9"/>
<line x1="308.0" y1="438.0" x2="315.0" y2="438.0" stroke="#cbd5e1" stroke-width="1.2"/>
<text x="320.0" y="442.0" font-size="12" fill="#15803d" font-weight="normal" text-anchor="start">手动录波</text>
<text x="378.0" y="441.5" font-size="9" fill="#94a3b8" font-weight="normal" text-anchor="start">wave_manual</text>
<text x="444.4" y="441.5" font-size="9" fill="#b45309" font-weight="bold" text-anchor="start">P</text>
<line x1="308.0" y1="456.0" x2="315.0" y2="456.0" stroke="#cbd5e1" stroke-width="1.2"/>
<text x="320.0" y="460.0" font-size="12" fill="#0f172a" font-weight="normal" text-anchor="start">清除记录</text>
<text x="378.0" y="459.5" font-size="9" fill="#94a3b8" font-weight="normal" text-anchor="start">clear</text>
<text x="413.1" y="459.5" font-size="9" fill="#b45309" font-weight="bold" text-anchor="start">P</text>
<line x1="308.0" y1="474.0" x2="315.0" y2="474.0" stroke="#cbd5e1" stroke-width="1.2"/>
<text x="320.0" y="478.0" font-size="12" fill="#0f172a" font-weight="normal" text-anchor="start">通讯设置</text>
<text x="378.0" y="477.5" font-size="9" fill="#94a3b8" font-weight="normal" text-anchor="start">comm</text>
<text x="407.9" y="477.5" font-size="9" fill="#b45309" font-weight="bold" text-anchor="start">P</text>
<line x1="308.0" y1="492.0" x2="315.0" y2="492.0" stroke="#cbd5e1" stroke-width="1.2"/>
<text x="320.0" y="496.0" font-size="12" fill="#0f172a" font-weight="normal" text-anchor="start">网口设置</text>
<text x="378.0" y="495.5" font-size="9" fill="#94a3b8" font-weight="normal" text-anchor="start">net_cfg</text>
<text x="423.5" y="495.5" font-size="9" fill="#b45309" font-weight="bold" text-anchor="start">P</text>
<line x1="308.0" y1="510.0" x2="315.0" y2="510.0" stroke="#be123c" stroke-width="1.2" stroke-dasharray="3,2"/>
<text x="320.0" y="514.0" font-size="12" fill="#be123c" font-weight="normal" text-anchor="start">SNTP设置</text>
<text x="380.8" y="513.5" font-size="9" fill="#b45309" font-weight="bold" text-anchor="start">P</text>
<line x1="308.0" y1="528.0" x2="315.0" y2="528.0" stroke="#cbd5e1" stroke-width="1.2"/>
<text x="320.0" y="532.0" font-size="12" fill="#0f172a" font-weight="normal" text-anchor="start">SOE记录</text>
<text x="374.9" y="531.5" font-size="9" fill="#94a3b8" font-weight="normal" text-anchor="start">soe</text>
<line x1="308.0" y1="546.0" x2="315.0" y2="546.0" stroke="#cbd5e1" stroke-width="1.2"/>
<text x="320.0" y="550.0" font-size="12" fill="#0f172a" font-weight="normal" text-anchor="start">事故记录</text>
<text x="378.0" y="549.5" font-size="9" fill="#94a3b8" font-weight="normal" text-anchor="start">accident</text>
<line x1="308.0" y1="564.0" x2="315.0" y2="564.0" stroke="#cbd5e1" stroke-width="1.2"/>
<text x="320.0" y="568.0" font-size="12" fill="#0f172a" font-weight="normal" text-anchor="start">操作记录</text>
<text x="378.0" y="567.5" font-size="9" fill="#94a3b8" font-weight="normal" text-anchor="start">oper</text>
<line x1="308.0" y1="582.0" x2="315.0" y2="582.0" stroke="#cbd5e1" stroke-width="1.2"/>
<text x="320.0" y="586.0" font-size="12" fill="#0f172a" font-weight="normal" text-anchor="start">保护告警</text>
<text x="378.0" y="585.5" font-size="9" fill="#94a3b8" font-weight="normal" text-anchor="start">alarm</text>
<line x1="308.0" y1="600.0" x2="315.0" y2="600.0" stroke="#cbd5e1" stroke-width="1.2"/>
<text x="320.0" y="604.0" font-size="12" fill="#0f172a" font-weight="normal" text-anchor="start">保护启动</text>
<text x="378.0" y="603.5" font-size="9" fill="#94a3b8" font-weight="normal" text-anchor="start">start</text>
<line x1="308.0" y1="618.0" x2="315.0" y2="618.0" stroke="#cbd5e1" stroke-width="1.2"/>
<text x="320.0" y="622.0" font-size="12" fill="#0f172a" font-weight="normal" text-anchor="start">遥控记录</text>
<text x="378.0" y="621.5" font-size="9" fill="#94a3b8" font-weight="normal" text-anchor="start">yk</text>
<line x1="308.0" y1="636.0" x2="315.0" y2="636.0" stroke="#cbd5e1" stroke-width="1.2"/>
<text x="320.0" y="640.0" font-size="12" fill="#0f172a" font-weight="normal" text-anchor="start">自检记录</text>
<text x="378.0" y="639.5" font-size="9" fill="#94a3b8" font-weight="normal" text-anchor="start">selftest</text>
<line x1="308.0" y1="654.0" x2="315.0" y2="654.0" stroke="#cbd5e1" stroke-width="1.2"/>
<text x="320.0" y="658.0" font-size="12" fill="#0f172a" font-weight="normal" text-anchor="start">运行记录</text>
<text x="378.0" y="657.5" font-size="9" fill="#94a3b8" font-weight="normal" text-anchor="start">runlog</text>
<line x1="308.0" y1="672.0" x2="315.0" y2="672.0" stroke="#cbd5e1" stroke-width="1.2"/>
<text x="320.0" y="676.0" font-size="12" fill="#0f172a" font-weight="normal" text-anchor="start">运行报告</text>
<text x="378.0" y="675.5" font-size="9" fill="#94a3b8" font-weight="normal" text-anchor="start">runreport</text>
<rect x="307.0" y="681.5" width="71.0" height="17.0" rx="4" fill="#f0fdf4" stroke="#15803d" stroke-width="0.9"/>
<line x1="308.0" y1="690.0" x2="315.0" y2="690.0" stroke="#cbd5e1" stroke-width="1.2"/>
<text x="320.0" y="694.0" font-size="12" fill="#15803d" font-weight="normal" text-anchor="start">录波回放</text>
<text x="378.0" y="693.5" font-size="9" fill="#94a3b8" font-weight="normal" text-anchor="start">wave_play</text>
<rect x="624.0" y="72.0" width="350.0" height="413.0" rx="8" fill="#ffffff" stroke="#cbd5e1" stroke-width="1.2"/>
<text x="640.0" y="102.0" font-size="12.5" fill="#0f172a" font-weight="bold" text-anchor="start">本图读数</text>
<text x="640.0" y="125.0" font-size="10.5" fill="#475569" font-weight="normal" text-anchor="start">节点 43 = 菜单 9 + 叶 34</text>
<text x="640.0" y="143.0" font-size="10.5" fill="#475569" font-weight="normal" text-anchor="start">已绑页 30 · 未绑页 4 · 密码门 16 叶</text>
<text x="640.0" y="161.0" font-size="10.5" fill="#475569" font-weight="normal" text-anchor="start">最深 4 级(主界面 → 叶)</text>
<text x="640.0" y="193.0" font-size="12.5" fill="#0f172a" font-weight="bold" text-anchor="start">怎么看</text>
<rect x="640.0" y="206.0" width="15.0" height="12.0" rx="3" fill="#eff6ff" stroke="#1d4ed8" stroke-width="1.0"/>
<text x="662.0" y="216.0" font-size="10.5" fill="#475569" font-weight="normal" text-anchor="start">根:主界面本身,不是一个页面</text>
<rect x="640.0" y="224.0" width="15.0" height="12.0" rx="3" fill="#ffffff" stroke="#334155" stroke-width="1.0"/>
<text x="662.0" y="234.0" font-size="10.5" fill="#475569" font-weight="normal" text-anchor="start">白框:一级菜单,可展开、不绑页</text>
<rect x="640.0" y="242.0" width="15.0" height="12.0" rx="3" fill="#f5f3ff" stroke="#6d28d9" stroke-width="1.0"/>
<text x="662.0" y="252.0" font-size="10.5" fill="#475569" font-weight="normal" text-anchor="start">紫底:二级菜单与它那一支的行带</text>
<rect x="640.0" y="260.0" width="15.0" height="12.0" rx="3" fill="#f0fdf4" stroke="#15803d" stroke-width="1.0"/>
<text x="662.0" y="270.0" font-size="10.5" fill="#475569" font-weight="normal" text-anchor="start">绿底:录波两叶,已绑页面模块</text>
<rect x="640.0" y="278.0" width="15.0" height="12.0" rx="3" fill="#ffffff" stroke="#be123c" stroke-width="1.0"/>
<line x1="640.0" y1="284.0" x2="655.0" y2="284.0" stroke="#be123c" stroke-width="1.0" stroke-dasharray="3,2"/>
<text x="662.0" y="288.0" font-size="10.5" fill="#475569" font-weight="normal" text-anchor="start">红字 + 虚线:未绑页的叶,进去只有菜单</text>
<text x="640.0" y="306.0" font-size="10.5" fill="#475569" font-weight="normal" text-anchor="start">灰色小字 = 页面模块名(页面注册表的键)</text>
<text x="640.0" y="324.0" font-size="10.5" fill="#475569" font-weight="normal" text-anchor="start">行末的 P = 进这一叶要先过密码</text>
<text x="640.0" y="342.0" font-size="10.5" fill="#475569" font-weight="normal" text-anchor="start">整定 与 查看 复用同一批页面模块</text>
<text x="640.0" y="374.0" font-size="12.5" fill="#0f172a" font-weight="bold" text-anchor="start">未绑页的 4 叶</text>
<text x="640.0" y="397.0" font-size="10.5" fill="#475569" font-weight="normal" text-anchor="start">· 遥信类型(三遥设置 下)</text>
<text x="640.0" y="415.0" font-size="10.5" fill="#475569" font-weight="normal" text-anchor="start">· 遥信防抖(三遥设置 下)</text>
<text x="640.0" y="433.0" font-size="10.5" fill="#475569" font-weight="normal" text-anchor="start">· 双点遥信(三遥设置 下)</text>
<text x="640.0" y="451.0" font-size="10.5" fill="#475569" font-weight="normal" text-anchor="start">· SNTP设置(通讯参数 下)</text>
<text x="28.0" y="750.0" font-size="9.5" fill="#94a3b8" font-weight="normal" text-anchor="start">真值 = ui_menu_tree.txt(tools/menu_gen/gen_menu.py 的输入);本图由 tools/arch_diagram/gen_menu_tree_svg.py 生成 —— 改菜单树要同步脚本内的 NODE_SRC 重跑,勿手改 SVG</text>
</svg>

After

Width:  |  Height:  |  Size: 24 KiB

@@ -1804,10 +1804,9 @@ pre-commit run --all-files
### 8.2 菜单树(43 节点)
![级联框总览](../images/级联框总览.svg)
![菜单树总览(层级 · 绑页 · 密码门)](../images/菜单树总览.svg)
> 该图仍是 42 节点时代所画(2026-10-29 新增的「录波回放」叶未进图):`tools/arch_diagram/` 里没有这张图的生成器,
> 重画要另开一笔并走 `generated-fresh`;**本节下面的文字与代码生成物是真值**。
> 图里的节点数、绑页与密码门读数由 `tools/arch_diagram/gen_menu_tree_svg.py` 现算自真值 `ui_menu_tree.txt`(改菜单树 → 同步脚本内 `NODE_SRC` 重跑);级联框在屏上怎么摆(几何)→ `docs/UI设计/菜单页面设计.md` §4.1。
**规模**:菜单节点 **9** 个(含根)+ 叶子节点 **34** 个 = **43** 节点;最大深度 **4 级(主界面 / 一级 / 二级 / 三级菜单)**。
**绑定现状**(2026-10-29 复扫):**已绑内容页 30 叶 / 未绑 4 叶**(页面注册表 `UI_PAGE_MAP_COUNT` = 30,
+16
View File
@@ -54,6 +54,22 @@
| `gen_data_trust_ladder_svg.py` | `docs/images/数据可信台阶.svg` | `STEPS` 五级台阶(状态 / 问题 / 依据)+ `BAND_LABEL_1` / `GATE_LABEL` / `FOOTNOTE`(台阶状态变了才重画) |
| `gen_data_trust_dc_ac_svg.py` | `docs/images/ADC直流标定与交流验证.svg` | `BOXES_A` / `BOXES_B` 两条泳道 + `CHIPS` 六件事 + `NOTES` 红线与"证不到"清单 |
| `gen_wave_record_flow_svg.py` | `docs/images/故障录波数据流.svg` | `CHAIN` 主链四段 + `EDGE` 跨边界标签 + `TRAPS` 三条陷阱 + `FORBID` 四条红线(所有权、段形态或红线变了才重画) |
| `gen_menu_tree_svg.py` | `docs/images/菜单树总览.svg` | **内嵌 `NODE_SRC`**(= `ui_menu_tree.txt` 的 43 行节点)+ 版面常量;仓库里跑会与真值逐行互验,不一致 exit 1(临时目录里读不到真值 → 用内嵌表,输出逐字节一致) |
### 2.1 钩子扫不到的图:refgraph 产物与历史手画稿
`generated-fresh` 的 `docs-svg` 条目只把 `tools/arch_diagram/gen_*.py` 拷进临时目录重跑逐字节比对。
上表没列到的 `docs/images/*.svg` 就是下面这几张,**它们全都不在这条钩子的扫描面里**——改了不会有任何
人报警,这就是「42 个节点的旧图挂在 43 个节点的标题下」能一路活到今天的原因。
| 图 | 谁画的 | 用在哪 | 维护口径 |
|---|---|---|---|
| `docs/images/引用关系图.svg`、`docs/images/引用关系图_模块.svg` | `tools/refgraph/gen_refgraph.py`(graphviz,不在 `gen_*.py` 命名面里) | `docs/架构设计/引用关系图说明.md` 的产品 | 改了代码引用就跑 `tools\refgraph\gen_refgraph.bat` 重生成,**svg 与改动同一笔提交**,别只更新 `.dot`;口径 → `docs/架构设计/引用关系图说明.md` |
| `docs/images/级联框总览.svg` | 2026-10-26 手画 | `docs/UI设计/菜单页面设计.md` §4.1(级联框显示几何:根框 + 整定子框) | 只作**显示几何示意**,不承载菜单树事实;树的结构 / 层级 / 绑页 → `docs/images/菜单树总览.svg`(有生成器) |
| `docs/images/级联框几何计算.svg` | 同上 | `docs/UI设计/菜单页面设计.md` §5.3(逐框坐标推导) | 同上;坐标公式变了要重算重画,别在 SVG 上描字 |
新增精确布局图一律走 `tools/arch_diagram/gen_*.py`(`docs-svg` 条目自动发现,**不必登记基线**);上面这几张
是这套纪律的**例外面**,再多一张先说清为什么不能用生成器。
---