# -*- coding: utf-8 -*- """版本与安装信息的**唯一真源**(2026-09-17 用户令 1:安装时检查已装版本、提示差异、问是否重装)。 为什么单开一个模块:以前版本号散在三处(`scripts/pack_dist.py` 的 `VERSION`、README 抬头、说明书标题), 改一处忘一处就会出现"包说 0.4.0、安装记录说 0.2.0"这种对不上的事。现在: src/version.py ← 唯一真源(本文件) scripts/pack_dist.py ← 读它写进 dist-manifest.json 与默认包名 install.ps1 / install.sh ← 读它写进 <安装目录>/install-info.json,并与已装的比对 guanlan.py / 各文档 ← 读它(文档里的人工版本号以它为准) `install-info.json` 落在安装根本身(不在 run/ 里):它是**交付物级别的安装记录**, 卸载/重装/拷机都要跟着走,所以和 `configs/`、`dist-manifest.json` 同级。 """ from __future__ import annotations try: from ._root import install_root as _install_root except ImportError: # 直接当脚本跑(python <本文件>)时没有包上下文 from _root import install_root as _install_root import datetime as dt import json import pathlib NAME = '观澜·如东样板 v2' VERSION = '2.66.0' # ★ 版本只改这里 EDITION = 'offline-single-package' PACKAGE_STEM = 'app_guanlang' # 交付包文件名前缀(用户令 2026-09-17) INSTALL_INFO = 'install-info.json' # 相对安装根 SERVICE_NAME = 'guanlan' # Windows 服务名 / systemd 单元名(用户令 1) SERVICE_DISPLAY = '观澜·如东样板 v2 (Guanlan Wind Asset Intelligence)' # ── 版本号规则(用户令 2026-09-17)──────────────────────────────────────────────── # 编号: v<大版本号>.<中版本号>.<小版本号> 例: v2.5.0 # 定义: 大版本号 —— 系统解决方案、架构或核心功能改变 # 中版本号 —— 非核心功能新增、减少、修改 # 小版本号 —— 消缺完善 # 包名: 打包文件名 = app_guanlang_v<版本号>.zip 例: app_guanlang_v2.5.0.zip # 用法: 用户/发布者定"这次算哪一级"→ VERSION 改一行 → 打包器与安装脚本自动跟着变 # (级别判据见 BUMP_RULE;一次发布混了几类就取最高那一级) RULE = 'v<大版本号>.<中版本号>.<小版本号>' LEVEL_MEANING = { 'major': '大版本号 —— 系统解决方案、架构或核心功能改变', 'minor': '中版本号 —— 非核心功能新增、减少、修改', 'patch': '小版本号 —— 消缺完善', } BUMP_RULE = ('改动落在"解决方案/架构/核心功能" → 大 +1(中/小归 0);' '落在"非核心功能的新增/减少/修改" → 中 +1(小归 0);' '只是"消缺完善" → 小 +1;一次发布混了几类,取最高那一级') # 版本记录(人读的那份由 scripts/version_log.py 生成到 docs/版本记录.md;这里只有事实,不重复描述) # level: major/minor/patch 表示这一版**相对上一版**是哪一级变化;legacy 表示该版用的是 # 旧编号体系(0.x,未按本规则),仅作历史对账用。 HISTORY: tuple[dict, ...] = ( dict(version='2.66.0', date='2026-10-03', level='minor', title='门户在 Vue 中严格实现(Vue 装配 + 同源 iframe)+ 静态附加件放置助手', note='① 用户令 2026-10-03:「前端也要严格实现观澜的门户」且前端必须是 Vue。本轮把门户搬进 Vue,做法与门户自己的装配脚本 `portal_build.py` 同口径、只是搬到运行时由 Vue 驱动:(a)门户件取自 v2.9.2:`shell.html`(门户外壳 140,103 B:样式/脚本/导航)+ `templates/` **28 件**内嵌交付文档(19.3 MB)+`governance_sources.json`,落在 `release/web/portal/`(按需取,不塞进 JS 包);(b)新增 `views/PortalView.vue`:取壳 → 逐个替换 ``(28 个标记)→ 注入资料索引 → **同源 iframe(Blob URL)**呈现 ⇒ 门户自身样式与脚本原样运行,观感/行为与 v2.9.2 一致;外层是 Vue 工作台;(c)接线为工作台页签「门户」(`portal_src`)。② 实逮并修:`vite build` 会**清空** `release/web` ⇒ 先放门户件再构建会全被清掉(本轮首次上传 0 件)。新增 `scripts/place_static_extras.py`(构建**之后**放置门户件/favicon/经典页;经典页用 v2.9.2 自带 `build.py` 出页),把"构建 → 放置附加件"固化成流程。③ 远端验证(106.120.102.238):`/web/portal/shell.html` 200 · 140,103 B;`/web/portal/governance_sources.json` 200;`/web/portal/templates/tpl-standard_panel_zh.html.html` 200 · 29,270 B;`/web/detail_v2.html` 200 · 238,644 B;`/web/index.html` 200;`/` 200 ✓。'), dict(version='2.65.0', date='2026-10-03', level='minor', title='图表改为复用 v2.9.2 的 SVG 渲染器(基础设施就位,逐页替换待续)', note='① 承接 2.64.0 的结构对拍结论:差异集中在**图表容器**(旧页内联 SVG vs Vue 侧 `div.echart`/ECharts)。本轮把 v2.9.2 的图表渲染器原样接进 Vue:(a)`app_frontEnd/web/src/lib/legacy/charts.js` = v2.9.2 包内 `src/windscada/ui/charts.js` **原文**(31,083 B,工厂 `window.__charts(ctx)` → `rateChart/quadChart/ramChart/waterfall/attribBar`,均返回**内联 SVG**);(b)`src/lib/legacyCharts.ts` —— 按旧页 `app.js:523` 的**同一份入参**构造上下文 `{ t, fmt, esc, dv, EN:false, displayText, verb }`,导出 `chart(name, …args)` 与旧页同款包裹 `chartPanel(title, svg, note)`(`

…

{SVG}

…

`);(c)`src/components/LegacyChart.vue` —— `v-html` 出 SVG 的薄外壳;(d)`tools/legacy_chart_smoke.mjs` —— 在 Node 里直跑该渲染器:`rateChart` 出 SVG 1,564 B ✓(`quadChart` 冒烟夹具字段不全,模块本身正常)。② 旧页图表调用点已定位(供逐页替换):`app.js:523` 建上下文;`report` 页 550/553/555/557/558(waterfall/ramChart/attribBar/quadChart/rateChart);`fault` 页 655/658/667(rateChart/quadChart/ramChart);`energy` 页 685 为另一处图形。③ 下一步(严格仿照收口):按上述调用点把 Vue 的 `Fault.vue` / `Report.vue` / `Energy.vue` 里的 ECharts 图逐张换成 `LegacyChart`(同一份渲染器 ⇒ 图逐像素同源),再重跑 `tools/struct_diff.mjs` 复核结构差异归零。'), dict(version='2.64.0', date='2026-10-03', level='minor', title='结构级对拍(Vue vs v2.9.2 原文页):工具落地 + 逐页签差距实测', note='① 用户令 2026-10-03:「前端必须使用 Vue.js,逐页面严格仿照 v2.9.2 实现」。文本/表格/图数字此前已 10/10 一致(见 2.63.1),本轮补上**结构级**判据。② 新增 `app_frontEnd/web/tools/struct_diff.mjs`:把 v2.9.2 **原文页**(`release/web/detail_v2.html`,jsdom 真跑)与 **Vue SSR 输出**逐页签比「结构骨架」= `#root` 下每个元素的 `tag.class…` 序列;数据面与真实应用一致(`fleet`/`curves`/`cms`/`maint_survey`/`facts`/`ont_chain`/`ask_*` 等)。口径实逮:不给 `curves`/`apis` 会得出"假差异"(如 `system` 基准 220 元素 vs Vue 7 —— 其实是 Vue 拿不到 `/api/maint_survey` 而渲染占位)⇒ 工具已按真实取数面喂数据。③ **实测(10 个页签)**:`report`(66) 与 `system`(220) **结构完全一致** ✓;`overview` 元素数一致(294=294)但类名口径不同:基准 `span.confirmed.tier` vs Vue `span.tier.定论`;`vibration` 1126 vs 1127、`decision` 34 vs 35、`assistant` 50 vs 51(各差 1);`energy` 90 vs 92、`fault` 213 vs 221(差 8)、`component` 397 vs 426(差 29)、`generation` 166 vs 195(差 29)—— 后四者的差异集中在 **图表容器**:旧页画**内联 SVG**,Vue 侧是 `div.echart`(ECharts,前次用户令引入)。④ 结论与下一步(严格仿照的收口路径):(a)`overview` 类名对齐(`confirmed.tier`,去掉"把定论值当类名"的写法);(b)图表改为**复用 v2.9.2 的 SVG 渲染器**(`src/windscada/ui/charts.js`,29 KB,已在包内)包成 Vue 组件 ⇒ 一并抹平 energy/fault/generation 的容器差异并做到图逐像素同源;(c)`component`/`decision`/`assistant`/`vibration` 的少量包裹/顺序差异按骨架逐条对齐。⑤ 说明:`/web/detail_v2.html`(v2.9.2 原文页)仅作**对照基准与回退视图**,产品入口仍是 Vue(`/`)。'), dict(version='2.63.1', date='2026-10-03', level='patch', title='恢复呈现对拍基线(改用 v2.9.2 原文页):10 个页签实测与重构前一致', note='① 背景:旧页 `/detail/v2` 退役后,`tools/render_parity.mjs` 的基线(线上旧页)消失了,P19 把基线改为**离线可复现**:新增 `tools/render_parity_v292.mjs` —— 基线页读 `release/web/detail_v2.html`(由 `app_guanlang_v2.9.2` 包自带 `build.py` 原样出页),数据面 `/detail/api/*` → `/api/*`(Java 后端承接),默认跑全 10 个页签。② 实测(数据取自远端 106.120.102.238:28084,两侧共用同一份 `fleet`):overview 文本 138 项 · energy 51 项/1 表/图数字 11-11 · component 260 项/2 表 · fault 133 项/2 表 · vibration 561 项/1 表 · generation 122 项/图数字 220 · decision 26 项 · assistant 40 项 · report 48 项 · system 165 项/2 表 —— **全部 [OK]**(另有 3 条已登记例外,均为**后端**在"页面按窗缓存"与"实时 API"两条路径上的措辞差异,非前端偏差)。③ 结论:Vue 前端的**文本序列 / 表格单元格 / 图表数字**与 v2.9.2 原文逐项一致(对拍工具判据),叠加 2.63.0 的"同一份样式表",样式/字体/字号同源;逐像素级视觉差异仍需浏览器视觉回归(本环境无浏览器自动化,故不作结论)。'), dict(version='2.63.0', date='2026-10-03', level='minor', title='前端与 v2.9.2 一致:Vue 采用旧页样式表 + 提供 v2.9.2 原样经典页', note='① 用户令 2026-10-03:「前端必须与 `app_guanlang_v2.9.2` 版本的页面保持一致(布局、样式、字体、字号、图文表)」。两次并行落地,分别解决"像"与"就是":② **A 样式同源(Vue 侧)**:从 v2.9.2 包内 `src/windscada/ui/app.css`(23,199 B)**原样**落盘为 `app_frontEnd/web/src/styles/legacy_app.css` 并在 `main.ts` 末尾引入(作为最终口径)⇒ Vue 产物的 CSS 由 1.9 KB 变为 55.7 KB,工作台各页签继承 v2.9.2 的字体/字号/配色/表格与卡片样式(类名此前已由对拍工具逐字核过)。③ **B 原样经典页(保证逐像素一致)**:把 v2.9.2 包内 `src/` 与 `configs/` 解出,用**它自己的** `src/windscada/ui/build.py` 出页(shell.html + app.css + app.js + charts.js + **语言包**,zh),产物 238,644 B,校验无遗留占位符;放入 `release/web/detail_v2.html`(静态伺服 ⇒ **零路由改动**即可访问),工作台「门户栏目」页签加入口「经典版页面」。数据面复用现有 `/api/*`(网关照旧转 Java 后端),实测 `/api/fleet` 200 ⇒ 经典页可直接工作。④ 远端验证(106.120.102.238):`/web/detail_v2.html` 200 · 238,644 B ✓;`/web/index.html` 200 ✓;`/` 200 ✓;`/api/fleet` 200 ✓。⑤ 说明:Vue 侧为"样式同源 + 类名一致",理论上高度接近;原样经典页则是**同一份源码**,可作逐像素基准与回退视图。'), dict(version='2.62.1', date='2026-10-03', level='patch', title='「门户栏目」收敛为在线服务两项;「架构与资料」内嵌四节整页展开', note='① 收尾两项(承 2.62.0 门户四节内嵌):(a)「门户栏目」页签只保留**在线服务**两项(仿真与回放 → `/sim/`、系统状态 → `/ops`),系统架构/方法/经验发现/案例·液压 已迁为「架构与资料」页签,避免重复入口;`pt.note` 文案同步改口径(中英成对)。(b)「架构与资料」的内嵌区由 `max-height: calc(100vh - 260px); overflow:auto` 改为 `max-height: none; overflow: visible` ⇒ **整页展开**,观感回到门户原样(不再套一层内滚动)。② 远端已验证(106.120.102.238):新产物 `index-CK3ivS0h.js` + `index-97FhsCXN.css` 上线,核验其在远端伺服的压缩内容含 `ps-embed` ✓、`max-height:none` ✓、四节锚点 ✓;探活 `/` 200 · `/web/index.html` 200 · `/api/fleet` 200 · `/api/recalc` 200 ✓。'), dict(version='2.62.0', date='2026-10-03', level='minor', title='门户四节原样内嵌进工作台(样式与 v2.9.2 同源)+ 修 Workbench 模板分支未命中', note='① 用户令 2026-10-03(限当日 17:00 前):把门户的 系统架构/方法/经验发现/案例·液压 四节内容迁进工作台,**页面样式、文图颜色必须与 `app_guanlang_v2.9.2` 一致**。② 做法(保证"同源"而非"近似"):脚本直接从 **`app_guanlang_v2.9.2.zip` 包内的 `release/portal.html`** 抽出· 门户 CSS(33.4 KB,8 个 `