entry-server.js 98 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725
  1. import { defineComponent, computed, unref, useSSRContext, ref, onMounted, watch, onBeforeUnmount, mergeProps, createSSRApp } from "vue";
  2. import { ssrRenderAttrs, ssrInterpolate, ssrRenderStyle, ssrRenderList, ssrRenderAttr, ssrRenderClass, ssrRenderComponent, renderToString } from "vue/server-renderer";
  3. const ZH = {
  4. "app.brand": "观澜",
  5. "app.build": "最新完成构建",
  6. "app.lang_en": "英文",
  7. "app.lang_zh": "中文",
  8. "app.subtitle": "海上风电场",
  9. "app.title": "观澜 · 风电数据分析与决策支持",
  10. "ask.common_q": "{cat} · 常用问题 (点即问, 或下方自由输入)",
  11. "ask.done_meta": "模型 {m} · 用时 {s} 秒 · {n} 步",
  12. "ask.elapsed": "{s} 秒",
  13. "ask.empty": "问题为空",
  14. "ask.en_qa_failed": "英文质检未通过: 已拦截这次模型回答, 请重新提问。",
  15. "ask.error": "出错: {e}",
  16. "ask.go": "提问",
  17. "ask.grounding": "答案句句带 [对象id] 溯源 + 数据回抓校验核验; 判级只转述本体原文带审级, 模型零判级权。",
  18. "ask.idle": "空闲 — 点常用问题或输入后提问。串行一次一问。",
  19. "ask.local_down": "本机模型未启动 (Ollama 127.0.0.1:11434 未响应); 离线版不转云",
  20. "ask.model": "模型",
  21. "ask.no_models": "无可用模型",
  22. "ask.offline": "离线版: 模型问答需连接系统。",
  23. "ask.partial": "初答 · 27B 复核中",
  24. "ask.placeholder": "自由提问 ({cat})",
  25. "ask.question": "问题: {q}",
  26. "ask.refined": "已复核 (默认档 {m} 未过成文校验, 由更强模型重答)",
  27. "ask.refs.none": "无匹配契约条目 (契约 {sha}): 本回答未被结构化事实契约背书, 按模型输出对待",
  28. "ask.refs.title": "契约引用 ({n} 条, 契约 {sha}) — 按问题/回答匹配的可交付结论, 引文来自 qa_refs",
  29. "ask.review": "交叉审核 · {m}",
  30. "ask.review_err": "审核未完成: {e}",
  31. "ask.review_note": "由另一模型基于同一份证据复核上述答案。溯源核验仅校验数字与机组号是否越界, 本项校验结论是否有证据支撑。",
  32. "ask.running": "推理中…",
  33. "ask.verify_bad": "溯源核验未全过 (引用 {n})",
  34. "ask.verify_ok": "溯源核验 {n}/{n} 全真",
  35. "ask.xrev": "交叉审核",
  36. "ask.xrev_note": "(DeepSeek 14B 互审, +约 1 分钟)",
  37. "ask.xrev_title": "由另一模型基于同一份证据复核; 同一模型自审无法构成独立校验",
  38. "asst.alarm_type": "告警类型 {t}",
  39. "asst.cause_counts": "手册 {r} 次 · 工单 {w} 次 · 涉码 {c}",
  40. "asst.causes": "← 可能原因 (手册排查方向 + 工单实证)",
  41. "asst.check": "查什么",
  42. "asst.code_placeholder": "报警码, 如 3225 / 3130 / 13902",
  43. "asst.common": "常见",
  44. "asst.corrective": "抢修",
  45. "asst.corrective_flow": "报警码 → 失效模式 → 手册分步处置 → 历史工单",
  46. "asst.drawings": "对应图纸",
  47. "asst.failmodes": "指向的失效模式",
  48. "asst.faulttree": "故障树",
  49. "asst.faulttree_flow": "按证据量排序的系统失效模式 + 部件级成因",
  50. "asst.fm_counts": "该类本场报警 {a} 条 / 工单 {w} 条",
  51. "asst.head": "检修助手 — 确定性本体查询",
  52. "asst.head_note": "结果逐字取自手册与工单, 不经模型",
  53. "asst.identity": "系统 {sys} · 本场 {n} 条",
  54. "asst.interval": "周期 {i}",
  55. "asst.known_tags": "该系统手册出现过的位号 (对图纸时参考):",
  56. "asst.no_causes": "(手册无该类排查条目 — 原因层待补)",
  57. "asst.no_name": "(码表未收录)",
  58. "asst.no_steps": "本码无手册处置条目 (手册覆盖本场 161/322 码) — 需查图纸或联系厂商支持。",
  59. "asst.node": "节点",
  60. "asst.nodes": "应检节点 · {c}",
  61. "asst.nodes_note": "手册此步未给位号 → 按回路构成展开, 逐点查",
  62. "asst.prev_empty": "(该系统无匹配的年度维护条目 — 维护作业指导书按位置代号编排, 未必逐系统对齐)",
  63. "asst.prev_fm": "失效模式 (按本场报警量)",
  64. "asst.prev_head": "{sys} 预防链 · 失效模式 {f} 类 · 预防性任务 {n} 项",
  65. "asst.prev_tasks": "年度任务",
  66. "asst.preventive": "预防性维护",
  67. "asst.preventive_flow": "系统 → 失效模式 → 年度任务 → 作业指导步骤",
  68. "asst.qty": "量: {q}",
  69. "asst.query_failed": "查询失败: {e}",
  70. "asst.querying": "查询中…",
  71. "asst.spec_name": "名称",
  72. "asst.spec_no": "参数号",
  73. "asst.spec_range": "允许范围",
  74. "asst.spec_val": "整定值",
  75. "asst.specs": "判定规范 (实机参数表)",
  76. "asst.step_n": "第 {n} 步",
  77. "asst.steps": "现场处置 {n} 步",
  78. "asst.steps_note": "节点部件 = 手册原文元件位号 (确定); 判定规范 = 实机参数表按物理量匹配的候选判据, 非厂商绑定 — 现场以铭牌与图纸复核。",
  79. "asst.tags": "节点部件",
  80. "asst.tree_codes": "征兆码 {c}",
  81. "asst.tree_counts": "本场报警 {a} 条 · 工单 {w} 条",
  82. "asst.tree_empty": "该系统无失效模式记录",
  83. "asst.tree_head": "{sys} 故障树 · {n} 个失效模式",
  84. "asst.tree_note": "一层 = 系统 × 失效类 (归纳自本场码表与工单) · 二层 = 手册排查方向归一 + 工单原因计数; 条长按实证量 (报警条数 + 工单 × 10), 因果来自手册与工单原文非推断, 树深两层不外推。",
  85. "asst.unassigned": "未归属",
  86. "asst.wi_steps": "WI {n} 步",
  87. "asst.wo_action": "处置",
  88. "asst.wo_fault": "故障",
  89. "asst.wo_head": "现场实际怎么修的 ({sys} 历史工单 {n} 条, 台账 2020~2024)",
  90. "asst.wo_note": '工单时间窗止 2024-11-21; 2025–26 现场未提供 ⇒ "0 条" 是记录缺失非未修过。',
  91. "attr.变桨与液压": "变桨与液压",
  92. "attr.塔架/结构": "塔架/结构",
  93. "attr.无记录停机": "无记录停机",
  94. "attr.未归类": "未归类",
  95. "attr.电网/供电(外部)": "电网/供电(外部)",
  96. "attr.轮毂/叶片": "轮毂/叶片",
  97. "attr.运维操作": "运维操作",
  98. "attr.风况(外部)": "风况(外部)",
  99. "cat.equip": "设备类",
  100. "cat.equip.hint": "设备状态 / 机制 / 判级 — 从机组档案、对象查询与机制检索取证",
  101. "cat.equip.q1": "29号机组现在什么状态、为什么、该怎么办?",
  102. "cat.equip.q2": "全场哪些机组有报警级或定论级判级?逐台列出并注明审级",
  103. "cat.equip.q3": "失效机制库里有哪些机制族?各涉及哪些机组、状态如何?",
  104. "cat.loss": "损失分析",
  105. "cat.loss.hint": "当前 3 个月损失 / 未来损失 — ont_loss / ont_scenario 取证, 金额带电价区间统计方法",
  106. "cat.loss.q1": "近3个月全场损失结构如何?分状态和分机组说, 电量与金额都给",
  107. "cat.loss.q2": "29号如果拖到主轴承失效, 未来损失量级是多少?与现在修对比",
  108. "cat.loss.q3": "限电常态化对检修机会成本有什么影响?",
  109. "cat.risk": "安全风险预测",
  110. "cat.risk.hint": "风险敞口 / 劣化趋势 / 盲区 — ont_risk / ont_mechanism_search 取证, 未列 ≠ 正常",
  111. "cat.risk.q1": "当前哪些机组风险在上升需要重点盯?联动监视读数如何?",
  112. "cat.risk.q2": '监测有哪些盲区?哪些"没报警"不等于正常?',
  113. "cat.risk.q3": "29号轴承正在承受金属接触, 风险敞口怎么评估?",
  114. "cat.sched": "检修排程决策",
  115. "cat.sched.hint": "排期沙盘 / 天气窗 / 验收树 — ont_scenario / ont_get 取证",
  116. "cat.sched.q1": "29号修泵什么时候做最合适?对比各情景的损失与风险",
  117. "cat.sched.q2": "未来几个月哪些月份适合安排CTV检修和吊装作业?依据历史可作业窗",
  118. "cat.sched.q3": "29号修好之后按什么判据验收?三分支分别是什么条件?",
  119. "ch.attrib.none": "无记录 {h}h ({p}%)",
  120. "ch.attrib.ok": "已归因到部件 {h}h ({p}%)",
  121. "ch.attrib.uncls": "未归类 {h}h",
  122. "ch.beta.median": "全场中位 {v}°",
  123. "ch.blame.d1": "限电与调度令, 不进设备责任",
  124. "ch.blame.d2": "故障与检修停机, 唯一进设备考核",
  125. "ch.blame.d3": "正常发电欠发与低风待机",
  126. "ch.blame.n1": "交易与调度侧",
  127. "ch.blame.n2": "设备侧",
  128. "ch.blame.n3": "运行侧",
  129. "ch.blame.title": "损失 {tot} MWh 按责任归属",
  130. "ch.cap.legend": "圆内数字 = 台数, 圆面积 ∝ 台数",
  131. "ch.cap.median": "中位 {v}",
  132. "ch.cap.p": "满发封顶",
  133. "ch.cap.w": "转速封顶",
  134. "ch.ml.legend": "— {s}",
  135. "ch.pareto.cum80": "累计 80%",
  136. "ch.pareto.cumtip": "{k} · 累计 {p}%",
  137. "ch.pareto.key": "关键少数: 前 {k} 项占 80%",
  138. "ch.pareto.none": "时间窗内无数据",
  139. "ch.pareto.top3": "前三: ",
  140. "ch.pareto.total": "共 {n} 项, 合计 {tot}{unit}",
  141. "ch.pareto.xlab": "报警码 (悬停看全名)",
  142. "ch.quad.few": "时间窗内码数不足, 不作四象限",
  143. "ch.quad.q1": "低频长时 · 硬故障",
  144. "ch.quad.q1s": "备件与检修窗",
  145. "ch.quad.q2": "高频长时 · 重点治理",
  146. "ch.quad.q2s": "优先派工",
  147. "ch.quad.q3": "低频短时 · 本底",
  148. "ch.quad.q3s": "不单独处置",
  149. "ch.quad.q4": "高频短时 · 重复触发",
  150. "ch.quad.q4s": "调整参数或屏蔽, 非硬件故障",
  151. "ch.quad.tip": "{n} 条 · 单次中位 {med}s · 累计 {h}h · {nt} 台",
  152. "ch.quad.xlab": "发生条数 (对数)",
  153. "ch.quad.ylab": "单次中位时长 (对数)",
  154. "ch.ram.area": "圆面积 = 累计停机时长",
  155. "ch.ram.equip": "设备类",
  156. "ch.ram.ext": "外部类",
  157. "ch.ram.few": "部件数不足, 不作象限",
  158. "ch.ram.median": "虚线 = 本场中位分界",
  159. "ch.ram.q1": "常坏且难修",
  160. "ch.ram.q1s": "最高优先级",
  161. "ch.ram.q2": "罕发难修",
  162. "ch.ram.q2s": "备件与作业窗",
  163. "ch.ram.q3": "频发易修",
  164. "ch.ram.q3s": "查根因, 换件无用",
  165. "ch.ram.q4": "少发好修",
  166. "ch.ram.q4s": "维持",
  167. "ch.ram.tip": "{p}: 平均无故障间隔(MTBF){mtbf}h · 单次停机时长(MDT){mdt}h · 停机 {h}h · {n}起 · {u}台",
  168. "ch.ram.tip_ext": " · 外部类不计设备可靠性",
  169. "ch.ram.xlab": "平均无故障间隔(MTBF,h,对数)→ 越右越可靠",
  170. "ch.ram.ylab": "单次停机时长(MDT,h)→ 越上越难修",
  171. "ch.rate.days": "仅{d}天",
  172. "ch.rate.month": "{m}月",
  173. "ch.rate.n": "{n}条",
  174. "ch.rate.tip": "{m}: {v} 条/日 (总 {n} 条, 数据覆盖 {cov}%)",
  175. "ch.rate.ylab": "条/日",
  176. "ch.ruler.norm": "正态期望 3.7σ",
  177. "ch.ruler.row": "{r}σ σ = {s}",
  178. "ch.wf.act": "实际上网",
  179. "ch.wf.theo": "理论可发",
  180. "ch.wf.tip": "{nm}: 损失 {loss} MWh, 占理论 {pct}%, 时长 {h}h",
  181. "chain.gb": "齿轮箱",
  182. "chain.gde": "发电机DE",
  183. "chain.gen": "发电机",
  184. "chain.gnde": "发电机NDE",
  185. "chain.hss": "高速",
  186. "chain.ims": "中间",
  187. "chain.mba": "主轴承后",
  188. "chain.mbf": "主轴承前",
  189. "chain.ps": "行星",
  190. "cmp.alarm_n": "报警 {a}",
  191. "cmp.alarm_units": "报警机组",
  192. "cmp.basis": "依据",
  193. "cmp.by_system": "按系统查看",
  194. "cmp.cards_note": "点系统卡 → 该系统全场机组与依据; 九系统 = 交付九分册一一对应; 叶片与叶根 / 塔架与基础无 SCADA 轴 (分册蓝本取 CMS / 巡检)",
  195. "cmp.counts": "报警 {a} · 不可判 {u} · 良好 {g} · 优秀 {e}",
  196. "cmp.cross_eyebrow": "多系统交叉机组",
  197. "cmp.detail": "详情",
  198. "cmp.detail_head": "{sys} · 全场 {n} 台",
  199. "cmp.external": "外部",
  200. "cmp.fleet_n": " · 全场 {n} 台",
  201. "cmp.fusion_moved": "振动线结论与四源融合已移至独立板块 →",
  202. "cmp.fusion_n": "({n} 台次收录)",
  203. "cmp.good_units": "良好 {n} 台 (轻微偏离, 周期观察 — 点开看逐台依据)",
  204. "cmp.grading_pending": "正在按所选时间窗重算判级…",
  205. "cmp.grading_pending_why": "时间窗 {span}:变桨/偏航/蓄能/温度四个判级面正按该时间窗重算(首次约 25 秒),下面的系统卡暂时还是上一份统计方法 —— 算完会自动刷新,不会静默混用。",
  206. "cmp.hit_sys": "命中系统",
  207. "cmp.n_sys": "系统数",
  208. "cmp.n_units": "({n} 台)",
  209. "cmp.none": "本系统全场无非常态。",
  210. "cmp.problem_page": "该问题页 →",
  211. "cmp.rel_ev": "事件",
  212. "cmp.rel_eyebrow": "部件可靠性 · 时间窗 {win}",
  213. "cmp.rel_fm": "典型失效模式 (按时长)",
  214. "cmp.rel_h": "停机时长",
  215. "cmp.rel_head": "部件停机贡献与可靠性",
  216. "cmp.rel_mdt": "单次停机时长(MDT,h)",
  217. "cmp.rel_meta": "总停机 {t}h = 设备类 {a} + 外部类 {b}",
  218. "cmp.rel_mtbo": "平均停机间隔(MTBO,h)",
  219. "cmp.rel_note": '统计方法 ≥1h 停机段, 不含调度令 — 与"电量算账"里的停机时长{h} 不同, 差额是短于 1h 的停机段 (本页按段做可靠性指标须成段, 能量账按逐条工况态算电量): 两个数都对, 不可互相相减。',
  220. "cmp.rel_part": "部件",
  221. "cmp.rel_units": "影响机组",
  222. "cmp.unk_n": " · 不可判 {u}",
  223. "cmp.unk_units": "不可判 (监测/数据不足, ≠ 正常)",
  224. "cmp.watch_head": "关注机组 (≥2 系统交叉, {n} 台; 点机组号 → 该台独立页)",
  225. "dec.classic": "维护标准 / 维护框架 / 数据质量三块尚未迁入新版 —",
  226. "dec.head": "检修决策 · 本体问答 (推理无界, 结论须过校验)",
  227. "en.balance": "电量平衡",
  228. "en.blame": "损失责任分账",
  229. "en.blame_note": '最大一笔是限电不是故障。进设备考核的只有"故障与检修停机" {n} MWh; 限电属交易与调度侧, 2026 起交易策略主导, 少发未必是弃风。',
  230. "en.cap_fail": "不通过",
  231. "en.cap_note": "能量守恒核 · 理论 {op} 全额定上限 {cap}",
  232. "en.cap_pass": "通过",
  233. "en.kpi_act": "实际上网 · 等效满发 {h} h",
  234. "en.kpi_loss": "损失率 · {v} MWh",
  235. "en.kpi_theo": "理论可发 (实际+损失)",
  236. "en.loss_mwh": "损失 MWh",
  237. "en.share": "时长占比",
  238. "en.state": "工况态",
  239. "en.states": "工况态明细",
  240. "en.states_note": "控制参数一致性与曲线欠发筛查在固定干净窗判, 已迁入「发电性能」页 — 本页只出当期电量账。",
  241. "en.win": "窗 {win}",
  242. "fig.fleet_median": "全场中位",
  243. "fig.point": "测点",
  244. "fig.quartile": "四分位",
  245. "fig.this_unit": "本台",
  246. "fig.trend": "趋势",
  247. "fig.vibbar_note": "条为本台与全场中位之比; 色按 z (稳健离群度): 绿 <2 · 橙 2~3 · 红 ≥3",
  248. "fig.x_median": "×中位",
  249. "flt.alarm_face": "报警面",
  250. "flt.alarm_meta": "{n} 条 · {c} 个码 · 时间窗 {win}",
  251. "flt.attrib_note": '归因覆盖: "无记录停机" {h}h 是全场最大单项, 因不给 MTBF 而在上图中不出现 — 它不是"没有故障", 是"没归上因"。统计方法为段起点 ±30min 首发码。缺口成因待定: 此前写的"98% 的段在前 72h 内有报警 ⇒ 长停远窗"不成立 — 前 72h 有报警不等于该报警与本段停机有因果 (双模审 2026-08-28 逮)。要定成因须改判"段起止是否落在某条报警的持续区间内", 未做之前只报缺口不报原因。',
  252. "flt.block_err": "本块未算出 ({k}): {e}",
  253. "flt.eyebrow": "故障统计与可靠性",
  254. "flt.pareto_d": "帕累托 · 按持续时长",
  255. "flt.pareto_d_note": "时长 = 闩锁统计方法 (复位 − 激活), 只作排序参考不作停机时长",
  256. "flt.pareto_n": "帕累托 · 按发生条数",
  257. "flt.pareto_n_note": "条数 = 发生频次, 不反映影响程度 — 高频短时报警码 (信号抖动或重复触发) 排序靠前",
  258. "flt.per_t": "台级分布 · 报警条数 top{n}",
  259. "flt.per_t_note": "全场合计 {n} 条 · 单台占比高须先核查是否为单一报警码重复触发",
  260. "flt.per_t_share": "占全场",
  261. "flt.quad": "故障模式四象限 · 条数 × 单次时长",
  262. "flt.quad_note": "时间窗内共 {n} 个码, 图上为条数前 {m} 个。",
  263. "flt.quad_note2": "点面积表示影响机组数, 大点为批次共性、小点为单台; 竖分界取条数中位, 横分界取 10min (一个 SCADA 采样格)。右下角的码不要按条数排优先级 — 属重复触发, 非硬件故障。上面两张帕累托对照看即此图的两条投影。",
  264. "flt.quad_partial": "窗含不满月 {m} (覆盖 {c}%), 条数为原始累计未按天归一。",
  265. "flt.ram": "可靠性 × 可维修性 · 部件象限",
  266. "flt.ram_note": '按停机时长排序的表看不出这层结构: 同样 500h 的停机, "频发易修"要查根因、"罕发难修"要备件, 处置相反。设备类停机 {a}h / 外部类 {b}h (外部 = 电网·风况·运维操作, 不计设备可靠性)。海上机组 MDT 普遍偏高是交通窗口决定的, 不等于维修本身慢 — 跨场比 MDT 须同为海上。',
  267. "flt.rate": "月度故障率",
  268. "flt.rate_example": "实测: {m} 总数最低但日均 {v} 条/日 = 全时间窗前列。",
  269. "flt.rate_full": "各月覆盖完整。",
  270. "flt.rate_note": "柱高 = 日均条数, 不是月总数",
  271. "flt.rate_partial": "本时间窗 {m} 数据不满月 (灰虚线柱), 直接比月总数会读反。",
  272. "flt.seasonal": "故障季节性 · top 报警族 × 月",
  273. "flt.seasonal_note": "全程 19 个月 (跨 2 冬 1 夏); 未做 χ² 显著性检验前为【参考级】, 只看形态不作季节归因",
  274. "flt.seasonal_unit": "条/月",
  275. "flt.seasonal_x": "月",
  276. "flt.stop_face": "停机面",
  277. "flt.stop_h": "停机时长",
  278. "flt.stop_meta": "{h}h · {n} 起 · 统计方法 ≥1h 段, 不含调度令",
  279. "flt.stop_n": "次数",
  280. "flt.stop_pareto": "停机归因帕累托",
  281. "flt.stop_pareto_note": "按停机时长; 事件次数见悬停",
  282. "flt.stop_units": "台级停机 top{n}",
  283. "flt.stop_units_note": "停机时长高 ≠ 设备差 — 须先看归因里外部类占比 (调度令已剔除)",
  284. "fus.alarm_n": "{n} 条",
  285. "fus.alarm_rank": " · 第 {r}/{t}",
  286. "fus.burst": "⚠ {p}% 是码 {c} 抖动",
  287. "fus.burst_tip": "⚠ 单码 {c} 占 {p}%, 中位 {m} 秒/次 — 条数被抖动灌满",
  288. "fus.clear": "× 清除",
  289. "fus.cms_act": "行动",
  290. "fus.cms_action": "需行动机组 (报警 / 不可判)",
  291. "fus.cms_basis": "统计方法说明",
  292. "fus.cms_basis_note": "本表 = windcms 状态管理判级 (含标量趋势与数据可判性); 融合矩阵 / 逐台结论 = 振动线确诊链转录 (候选以上)。两统计方法差异台: 仅本表列 {a} (多为标量发作 / 数据不可判, 未到确诊链); 仅确诊链列 {b} (预警级在察, 状态管理未升)。",
  293. "fus.cms_chain": "部件状态链",
  294. "fus.cms_chain_note": "(主承前 · 主承后 · 齿轮箱 · 发电机; 悬停看逐件等级)",
  295. "fus.cms_evidence": "证据强度",
  296. "fus.cms_focus": "状态焦点",
  297. "fus.cms_focus_note": "(传动链: 主承前 · 主承后 · 齿轮箱 · 发电机两端)",
  298. "fus.cms_grades": "38 台部件等级一览",
  299. "fus.cms_head": "振动状态评估 (windcms 报告 {d} 结果转录)",
  300. "fus.cms_link": "谱图 · 波形 · 判据 · 工作台等分析功能在独立 windcms 系统 →",
  301. "fus.cms_loading": "载入振动评估结果…",
  302. "fus.cms_note": "状态管理判级, 不代表确诊数量; 判据/谱线/计算过程不在本页 — 深潜走底部独立系统。",
  303. "fus.cms_open": "打开 windcms ↗",
  304. "fus.cms_overall": "综合",
  305. "fus.cms_static": "(未启动时用静态版)",
  306. "fus.cms_window": '数据窗 {r} —— 本时间窗**实算**覆盖区间 (窗名只是起始日代号); 页面上的图谱与"末窗"状态取自时间窗内最新记录, 故多显示末期日期。',
  307. "fus.col.alarm": "报警",
  308. "fus.col.claim": "证据时间窗",
  309. "fus.col.claim_axis": "26-01 → 09",
  310. "fus.col.display_only": " · 只展示",
  311. "fus.col.fixed": " · 不随本时间窗",
  312. "fus.col.lub": "润滑柱塞",
  313. "fus.col.oil": "油样",
  314. "fus.col.part": "部件",
  315. "fus.col.signal": "振动信号",
  316. "fus.col.state_concl": "设备状态 / 振动结论",
  317. "fus.col.temp": "温度",
  318. "fus.col.tempaxis": "温度轴",
  319. "fus.col.tempaxis_note": "±6 K",
  320. "fus.col.vib": "振动",
  321. "fus.col.wo": "工单",
  322. "fus.ev_code": "码",
  323. "fus.ev_dur": "累计时长 h",
  324. "fus.ev_head": "{c} — 报警 {an} 条 / {au} 台 · 工单 {wn} 条 / {wu} 台 (时间窗内)",
  325. "fus.ev_min": "时长 min",
  326. "fus.ev_no_alarm": "时间窗内无该部件报警",
  327. "fus.ev_no_wo": "时间窗内无该部件工单 (工单数据止 2024-11, 2026 类窗必空 = 数据边界)",
  328. "fus.ev_recent": "最近 {n} 条明细",
  329. "fus.ev_text": "报警内容",
  330. "fus.ev_time": "时间",
  331. "fus.ev_top": "台分布 top: ",
  332. "fus.ev_units": "台数",
  333. "fus.ev_win": "时间窗 {win} · 顶部选择器可换",
  334. "fus.ev_wo_act": "处理",
  335. "fus.ev_wo_cause": "原因",
  336. "fus.ev_wo_name": "故障名称",
  337. "fus.eval_head": "评价体系 (规范化定义 — 两轴并存, 不可互相替代)",
  338. "fus.eval_meaning": "含义",
  339. "fus.eval_note": '设备状态答"这台设备现在怎么样" (危险 > 报警 > 良好 > 优秀 > 不可判); 证据状态答"这个判断有多可信" (定论 → INSUFFICIENT)。设备状态一律取自振动线的判级栏这一个来源。本页判级为转录, 不产新判级; 标注为参照同型机组/频谱信号级 (未定位到具体零件)/测量链/未对应到具体部件的行不作部件级结论消费; claim_window = 该结论的证据时间窗, 窗外状态该结论不背书。',
  340. "fus.eval_state": "设备状态 (业主统计方法)",
  341. "fus.eval_tier": "证据状态 (可信度)",
  342. "fus.eyebrow": "振动与 SCADA 证据视图 · 展示振动结论、SCADA 指标、报警与工单线索",
  343. "fus.fam_tip": "{n} 条 · 全场最多 {m} 条",
  344. "fus.legend_note": '两轴不可互相替代: "候选"说的是可信度不是设备好坏; "良好"说的是设备状态不是证据强度。设备状态只取自振动线的判级栏这一个来源。',
  345. "fus.legend_state": "格子颜色 = 设备状态 (业主统计方法, 与 windcms 报告同一套):",
  346. "fus.legend_tier": "徽章 = 证据状态 (可信度, windcms 九枚举):",
  347. "fus.loop_ba": "换前 → 换后 (同标尺)",
  348. "fus.loop_bound": "数据边界: {b}",
  349. "fus.loop_closed": "已闭环 · 换件后恢复 ({n} 例)",
  350. "fus.loop_date": "换件日",
  351. "fus.loop_el": "早期 → 近期 (同标尺)",
  352. "fus.loop_head": "维修后验证案例 (检出 → 换件 → 恢复)",
  353. "fus.loop_item": "事项",
  354. "fus.loop_neg": "负面对照: 工单后持平 {n} 例 (换件 ≠ 必然回落, 说明回落非巧合)。",
  355. "fus.loop_none": "暂无端到端闭环证据:现场提交的振动数据包里没有「报警→检修→复测」的成对记录",
  356. "fus.loop_note": "有换件记录且同一统计方法指标回落, 可作为检测链已闭合的案例; 指标仍在上升但缺少可匹配工单的机组, 需现场或台账确认后再关闭。",
  357. "fus.loop_ratio": "恢复比",
  358. "fus.loop_rising": "未闭环 · 指标上升待现场/工单确认 ({n} 台)",
  359. "fus.loop_state": "状态",
  360. "fus.loop_verdict": "判",
  361. "fus.loop_x": "倍比",
  362. "fus.lub_now": "润滑柱塞现状",
  363. "fus.lv.bad": "定论/报警级",
  364. "fus.lv.note": "候选/监视/观察级",
  365. "fus.lv.ok": "销案/无新证",
  366. "fus.lv.warn": "预警级",
  367. "fus.m.bad": "异常严重, 建议尽快停机检查",
  368. "fus.m.bad_e": "证据: 已定案",
  369. "fus.m.ex": "未见异常, 保持常规监视",
  370. "fus.m.ex_e": "证据: 仅记录 / 已取消",
  371. "fus.m.ok": "轻微偏离, 记录观察",
  372. "fus.m.ok_e": "证据: 待现场核实 / 已记为基线下窗观察",
  373. "fus.m.unk": "数据不足或监测中断 — 不等于正常",
  374. "fus.m.unk_e": "证据: 数据不足",
  375. "fus.m.warn": "存在需现场核实或处置的异常",
  376. "fus.m.warn_e": "证据: 基本确认 / 新出现待核实",
  377. "fus.matrix_foot": '润滑柱塞各台设定本就不同 (全场单次循环 22~961 秒), 所以此列只展示不判级, 只有已定案的机制链才标色; 油液多数台末次取样距今超 12 个月, 此列看的是"样够不够新", 不代表当前状态。',
  378. "fus.matrix_head": "全场融合矩阵 (38 台 × 6 个证据面)",
  379. "fus.matrix_note1": '各列取数窗口不同 (见列头) — 不同窗的两格同时发红不等于"同时发生", 不可当共时印证。',
  380. "fus.matrix_note2": '每格右侧曲线 = 逐月记忆轨迹 ({n} 个月, {m0} 起; 实心点 = 现窗) — 现窗快照会丢形态, 轨迹才看得见"塌→回升→翻转"类机制签名。',
  381. "fus.no_alarm": "时间窗内无报警",
  382. "fus.none_word": "无",
  383. "fus.oil_fresh": "近共时",
  384. "fus.oil_none": "无样",
  385. "fus.oil_stale": "stale > 12 个月",
  386. "fus.rule1": "① 同一失效机制的多个观测面 ≠ 多源印证 — 融合禁按源数加权 (29# 柱塞/温度/振动稳态/振动瞬态 = 四个观测面一条机制链)。",
  387. "fus.rule2": '② "没报警" ≠ "正常" — 在已知盲区上, 缺报警只说明监测面无效, 不说明设备无恙。',
  388. "fus.rules_head": "两条判读纪律 (随板块常显)",
  389. "fus.short.nodata": "数据不足",
  390. "fus.short.normal": "正常",
  391. "fus.short.nosample": "未取样",
  392. "fus.short.stale": "样龄过期",
  393. "fus.short.uneval": "未评估",
  394. "fus.short.unlisted": "未收录",
  395. "fus.sub.cms": "振动分析",
  396. "fus.sub.events": "报警工单",
  397. "fus.sub.loop": "端到端闭环",
  398. "fus.sub.matrix": "融合矩阵",
  399. "fus.sub.units": "逐台情况",
  400. "fus.temp_now": "温度轴现状",
  401. "fus.tip_state": " — 设备状态 {s}",
  402. "fus.tip_tier": " / 证据状态 {e}",
  403. "fus.unavailable": "融合面数据不可用: {e}",
  404. "fus.units_filter": " · 筛选: {l}",
  405. "fus.units_head": '逐台情况 (全场 {u} 台 / {n} 台次{f}; 深入谱图/波形 → "振动分析")',
  406. "fus.unlisted_head": "未列机组 {n} 台 (振动线未收录 — 未列 ≠ 正常)",
  407. "fus.unlisted_link": "未列 {n} 台",
  408. "fus.win_n": "{n} 个窗段",
  409. "fus.win_none": "未标窗",
  410. "fus.wo_n": "{n} 单",
  411. "fus.wo_none": "时间窗内无传动链工单",
  412. "gen.beta": "桨距调度同档差",
  413. "gen.beta_note": "同一功率档下的桨角与全场中位之差。{units} 偏离达中位的 {k} 倍 → {v}",
  414. "gen.cands": "曲线欠发筛查",
  415. "gen.cands_meta": "{note} · σ = {s}%",
  416. "gen.cap": "封顶参数落位",
  417. "gen.cap_note": "台数落在几个离散位置上, 不是连成一片 — 这是配置/版本差异的形状; 磨损或工况引起的会是连续散布。两轴各自成组且分组不同台 ⇒ 参数级差异, 成因须配置台账。",
  418. "gen.ctrl": "控制参数一致性",
  419. "gen.ctrl_win": "窗 {win}",
  420. "gen.eyebrow": "特性曲线与控制参数",
  421. "gen.figs": "七个镜头 · 逐档中位与同型机群残差",
  422. "gen.fixed_win": "随上方时间窗 · 按时间窗重算",
  423. "gen.loading": "正在计算七个镜头 (剥限电 · 逐档中位 · 同型机群残差)…",
  424. "gen.no_cands": "无候选",
  425. "gen.recomputing": "正在按所选时间窗重算七镜头…",
  426. "gen.recomputing_short": "正在按窗重算…",
  427. "gen.ruler": "尺子巡检 · 极差 ÷ σ",
  428. "gen.ruler_note": "判据自身的分辨力。38 台正态抽样的极差期望约 3.7σ, 明显超出即分布非单峰 — 与左图的分组互为印证; 落在期望附近的轴 (额定桨角) 则是连续分布。",
  429. "gen.win_range": "时间窗 {span}",
  430. "gen.win_why": '七镜头与月度时序都按所选时间窗重算(含自定义起止日期)。提示:2025-07 ~ 2025-12 是"限电前干净判别窗"——2026 起交易策略主导,曲线含策略性少发,作设备判据时须注意;选到含 2026 的窗时该影响存在但不再遮掩。',
  431. "kpi.alarm_units": "九系统有报警的台数 / 全场",
  432. "kpi.alarm_units.hint": "统计方法: 九系统 (叶片与叶根/变桨/偏航/齿轮箱/发电机/主轴承/变流器/塔架与基础/主控与传感网) 中任一被判「报警」即计一台。与下方「全场状态」格的统计方法不同 —— 那里按每台 振动/温度/润滑/油液/报警/工单 六列取最严一级, 所以两处数字可以不同, 不是矛盾。",
  433. "kpi.avail": "时间可用率 (不含调度令) · {win}",
  434. "kpi.curtail": "限电·绑定 电量损失",
  435. "kpi.downtime": "停机时长 · {n} 起",
  436. "kpi.loss": "停机电量损失 · {win}",
  437. "kpi.loss_dispatch": "另调度令 {n}",
  438. "kpi.mtbo": "平均停机间隔(MTBO;非行业 MTBF)",
  439. "loss.curtail_bound": "限电 (命令与实际功率对得上)",
  440. "loss.curtail_unbound": "限电命令期 · 功率未被压制",
  441. "loss.dispatch": "调度令停机",
  442. "loss.lowwind": "低风待机",
  443. "loss.normal": "正常发电时的欠发",
  444. "loss.stop": "故障与检修停机",
  445. "mdl.cloud": "云端",
  446. "mdl.deepseek-chat.nm": "DeepSeek",
  447. "mdl.deepseek-chat.t": "约 3 到 10 秒",
  448. "mdl.deepseek-r1:14b.nm": "本机 DeepSeek 14B · 审核档",
  449. "mdl.deepseek-r1:14b.t": "约 1 到 3 分钟",
  450. "mdl.deepseek-r1:32b.nm": "本机 DeepSeek 32B",
  451. "mdl.deepseek-r1:32b.t": "约 3 到 8 分钟",
  452. "mdl.deepseek-v4-pro.nm": "云端 DS",
  453. "mdl.deepseek-v4-pro.t": "约 2 到 6 分钟",
  454. "mdl.kimi-fast.nm": "Kimi",
  455. "mdl.kimi-fast.t": "约 5 到 20 秒",
  456. "mdl.local": "本地",
  457. "mdl.outbound_warn": "内容会外发到模型厂商, 场名与台号已去标识化",
  458. "mdl.qwen-plus.nm": "通义千问",
  459. "mdl.qwen-plus.t": "约 5 到 20 秒",
  460. "mdl.qwen3.8:27b.nm": "标准 27B · 复杂题",
  461. "mdl.qwen3.8:27b.t": "约 2 到 4 分钟",
  462. "mdl.qwen3:30b-a3b.nm": "快 30B (a3b)",
  463. "mdl.qwen3:30b-a3b.t": "约 20 到 60 秒",
  464. "mdl.qwen3:8b.nm": "轻量 8B · 默认",
  465. "mdl.qwen3:8b.t": "约 30 到 60 秒",
  466. "mdl.unknown_warn": "未知档位, 按内容会外发处理",
  467. "mdl.zero_outbound": "全程零外发",
  468. "mt.cov": "当前覆盖",
  469. "mt.eyebrow": "系统维护 · 数据输入与更新端口",
  470. "mt.fail": "盘点失败: {e}",
  471. "mt.freq": "频率",
  472. "mt.head": "数据维护面 · {n} 个源",
  473. "mt.how": "补数据怎么做",
  474. "mt.last": "末次更新",
  475. "mt.layer_data": "数据层",
  476. "mt.layer_data_note": "随运营滚动 — 新数据到位后跑摄入, 本体与判级自动刷新",
  477. "mt.layer_mech": "机理层",
  478. "mt.layer_mech_note": "相对稳定 — 换厂商资料或现场反馈修正时才动; 它是判据的地基, 变更须过审",
  479. "mt.note": '每个源给出: 当前覆盖 / 末次更新 / 约定位置 / 摄入命令 / 更新频率 / 责任方。要补数据就把新文件放到"约定位置"再跑"摄入命令"。',
  480. "mt.put": "放到:",
  481. "mt.resp": "责任",
  482. "mt.rows_n": " ({n} 条)",
  483. "mt.run": "跑:",
  484. "mt.src": "源",
  485. "mt.surveying": "盘点中…",
  486. "ov.all_steps_done": "已走完(待验收)",
  487. "ov.attention": "需要关注",
  488. "ov.attention_n": "{n} 台",
  489. "ov.basis_fusion": "本台依据来自融合面(九系统判级无「报警」行):部件 {part} · 链路走到第 {done}/{n} 步 · 卡在「{stuck}」 · 证据源 {srcs}",
  490. "ov.caveat": "限定",
  491. "ov.evidence": "查看完整依据",
  492. "ov.fus_gap_hint": "这一面(需要关注 / 全场状态)取数来自融合面;缺件时如实为空,不从交付包补齐。在本机放好原始件后跑一次重算即会由观澜自己的生成端补上(scripts/rudong_fusion_handoff.py,见 docs 的「源代码化清单」)。",
  493. "ov.illustrative": "示意数据",
  494. "ov.no_srcs": "无(该台无外部证据源)",
  495. "ov.quiet": "其余 {n} 台未进入优先关注清单;设备状态请查看全场状态评估。",
  496. "ov.quiet_all": "当前优先关注清单为空;{n} 台机组的设备状态请查看全场状态评估。",
  497. "ov.ranked_by": "按 设备状态 × 证据量 排序",
  498. "ov.strip": "全场状态",
  499. "ov.strip_hint": "一格一台 · 点格定位",
  500. "ov.systems": "九系统",
  501. "ov.systems_hint": "点系统看逐台依据",
  502. "pop.acts": "动作统计",
  503. "pop.close": "关闭",
  504. "pop.loading": "载入中…",
  505. "pop.none": "本时间窗无记录",
  506. "pop.o_act": "处置",
  507. "pop.o_cause": "原因",
  508. "pop.o_check": "排查",
  509. "pop.o_date": "日期",
  510. "pop.o_fault": "故障",
  511. "pop.o_kind": "类别",
  512. "pop.orders": "工单 {n} 单",
  513. "pop.orders_none": "时间窗内无工单",
  514. "pop.orders_one": "工单 1 单",
  515. "pop.problem": "{u} · {sys}",
  516. "pop.unit": "{u} · 全部问题",
  517. "rpt.attrib.note": '"无记录停机"不是没有故障, 是没归上因。',
  518. "rpt.attrib.title": "归因覆盖",
  519. "rpt.aud_in": "场站内部 (全量)",
  520. "rpt.aud_up": "上报集团 (脱敏)",
  521. "rpt.b.attrib.d": "已归因 vs 无记录停机的占比 — 无记录不等于没有故障",
  522. "rpt.b.attrib.nm": "归因覆盖",
  523. "rpt.b.blame.d": '交易调度侧 / 设备侧 / 运行侧 三段占比 — 回答"谁的责任"',
  524. "rpt.b.blame.nm": "损失责任分账",
  525. "rpt.b.caliber.d": "统计期 / 残月 / 工单时间窗 / 判级性质 — 建议常驻, 没有边界的数字堆不能作汇报依据",
  526. "rpt.b.caliber.nm": "统计方法与适用边界",
  527. "rpt.b.chain.d": "证据→机制→判级→排期→动作→验收 六步的瓶颈分布",
  528. "rpt.b.chain.nm": "决策链进度",
  529. "rpt.b.emon.d": "逐月上网 / 损失 / 损失率 / 等效满发, 含数据覆盖率",
  530. "rpt.b.emon.nm": "逐月发电表",
  531. "rpt.b.frate.d": "按天归一的日均条数, 数据不满月单独标注",
  532. "rpt.b.frate.nm": "月度故障率",
  533. "rpt.b.kpi.d": "六个核心数: 上网电量 / 等效满发 / 可用率 / 损失率 / 停机间隔 / 需行动台",
  534. "rpt.b.kpi.nm": "一级指标",
  535. "rpt.b.loop.d": "检出 → 换件 → 复测回落的历史例证, 证明这条链是通的",
  536. "rpt.b.loop.nm": "闭环实证",
  537. "rpt.b.narr.d": "模型基于实测数据生成的运行总结 — 为图表补充文字结论, 供汇报使用",
  538. "rpt.b.narr.nm": "运行总结",
  539. "rpt.b.pareto.d": "按条数降序 + 累计 80% 线, 找关键少数",
  540. "rpt.b.pareto.nm": "故障帕累托",
  541. "rpt.b.parts.d": "逐部件停机时长 / 事件数 / 平均无故障间隔(MTBF)/ 单次停机时长(MDT)/ 影响机组数",
  542. "rpt.b.parts.nm": "部件停机表",
  543. "rpt.b.plan.d": "由数据推出的行动项, 不是套话",
  544. "rpt.b.plan.nm": "下期建议",
  545. "rpt.b.quad.d": "条数 × 单次时长, 区分重复触发与实际停机 — 按条数排序会误定优先级",
  546. "rpt.b.quad.nm": "故障四象限",
  547. "rpt.b.ram.d": '平均无故障间隔(MTBF)× 单次停机时长(MDT)部件象限, 分出"频发易修"与"罕发难修", 处置方向相反',
  548. "rpt.b.ram.nm": "可靠性象限",
  549. "rpt.b.units.d": "需跟踪台的部件 / 证据源 / 六步进度 / 卡在哪一步",
  550. "rpt.b.units.nm": "重点机组",
  551. "rpt.b.water.d": "理论可发 → 逐段扣损 → 实际上网, 用于定位电量流向",
  552. "rpt.b.water.nm": "能量瀑布",
  553. "rpt.blame.cal": "工况态分解 + 限电命令面绑定判别; 平均出力=实发÷台时, 用作风资源代理量",
  554. "rpt.blame.note": '进设备考核的只有"故障与检修停机"; 限电属交易与调度侧。',
  555. "rpt.blame.note2": '限电不是"低风时段收到的空令": 限电·绑定时段平均出力 1.435 MW, 正常发电时段 1.412 MW, 比值 1.02 — 风况相同而出力被压, 说明限电是真压制; 但"限电命令期·功率未被压制"那一档平均出力仅 0.724 MW (18% 额定), 确属低风时段收令。',
  556. "rpt.blame.title": "损失责任分账",
  557. "rpt.cal.deid": "脱敏",
  558. "rpt.cal.deid_d": "上报版隐去判据门槛与特征频率",
  559. "rpt.cal.denom": "可用率与损失率分母不同",
  560. "rpt.cal.denom_d": '可用率分母剔除低风待机与调度令(问"该发时能不能发"), 损失率分母含全部时段(问"理论能发多少"); 同一低风时段在可用率里不计时间、在损失率里计入基数 — 这是两个指标的设计差异不是错, 但两者不可直接相乘或互推。低风待机对损失的贡献仅 118 MWh(0.5%)',
  561. "rpt.cal.partial": " — {m} 数据不满月(覆盖 {c}%), 月度已按天归一",
  562. "rpt.cal.period": "统计期",
  563. "rpt.cal.rating": "判级",
  564. "rpt.cal.rating_d": "由振动/温度/润滑/油液四源融合得出; 行动等级由综合报告出具",
  565. "rpt.cal.stop": "停机",
  566. "rpt.cal.stop_d": "可靠性统计方法 ≥1h 段, 电量账为逐条工况态 — 两个数都对, 不可相减",
  567. "rpt.cal.title": "统计方法与数据边界",
  568. "rpt.cal.wo": "工单",
  569. "rpt.cal.wo_d": "台账 2020~2024-11, 本报告不对本期检修执行下结论",
  570. "rpt.caliber_lbl": "统计方法: ",
  571. "rpt.chain.cal": "「动作」一列不可判 — {b}",
  572. "rpt.chain.note": "六步: 证据 → 机制 → 判级 → 排期 → 动作 → 验收。当前 {s}。",
  573. "rpt.chain.title": "决策链进度",
  574. "rpt.chapters": "{n} 个章节",
  575. "rpt.cloud_note": "; 内容会外发到模型厂商",
  576. "rpt.compose": "生成",
  577. "rpt.compose_fail": "生成失败: {e}",
  578. "rpt.composed": "已按需求编排 {n} 块{narr} · {m} 用时 {s}s",
  579. "rpt.composing": "模型编排中… 挑组件 + 写总结",
  580. "rpt.emon.act": "上网 MWh",
  581. "rpt.emon.cal": "覆盖率<50% 的月不参与环比",
  582. "rpt.emon.cov": "覆盖",
  583. "rpt.emon.eflh": "等效满发 h",
  584. "rpt.emon.loss": "损失 MWh",
  585. "rpt.emon.m": "月",
  586. "rpt.emon.partial": " 不满月",
  587. "rpt.emon.pct": "损失率",
  588. "rpt.emon.title": "逐月发电",
  589. "rpt.empty_canvas": "画布是空的 — 先从组件库选内容",
  590. "rpt.export_docx": "导出 Word",
  591. "rpt.export_xlsx": "导出 Excel",
  592. "rpt.eyebrow": "汇报定制 · 画布",
  593. "rpt.facts.agg": "聚合层",
  594. "rpt.facts.sub": "{n} 条, 契约 {sha}; 数据/时间窗/结论只经契约更新",
  595. "rpt.facts.sys": "系统",
  596. "rpt.facts.text": "结论",
  597. "rpt.facts.title": "可交付级结论 · 契约生成",
  598. "rpt.facts.verdict": "裁决",
  599. "rpt.foot_asof": "出件 {d}",
  600. "rpt.foot_deid": "判据门槛与特征频率已脱敏",
  601. "rpt.foot_src": "数据源 SCADA 10min · 报警事件 · 工单台账 · CMS 振动 · 油液化验",
  602. "rpt.foot_trace": "结论可回溯至本体对象",
  603. "rpt.frate.cal": "不满月按天归一",
  604. "rpt.frate.note": "柱高=日均条数, 不是月总数。",
  605. "rpt.frate.title": "月度故障率",
  606. "rpt.g.end": "收尾",
  607. "rpt.g.flt": "故障",
  608. "rpt.g.gen": "发电",
  609. "rpt.g.rel": "可靠性",
  610. "rpt.g.unit": "机组",
  611. "rpt.grounded": "数字全部接地",
  612. "rpt.hint_idle": "悬停查看组件说明 · 已选组件再次点击移出 · 右侧 ↑↓ 调整顺序",
  613. "rpt.kpi.act": "上网电量",
  614. "rpt.kpi.action": "需行动机组",
  615. "rpt.kpi.action_n": "定论 {a} · 预警 {b} · 候选 {c}",
  616. "rpt.kpi.annual": "年化约 {v} h",
  617. "rpt.kpi.avail": "时间可用率",
  618. "rpt.kpi.avail_n": "不含调度令与低风待机",
  619. "rpt.kpi.eflh": "等效满发小时",
  620. "rpt.kpi.loss": "损失率",
  621. "rpt.kpi.mom": "环比 {v}%",
  622. "rpt.kpi.mom_na": "环比不可比(末月不满)",
  623. "rpt.kpi.mtbo": "平均停机间隔",
  624. "rpt.kpi.mtbo_n": "停机 {n} 起 · 单次均 {h}h",
  625. "rpt.kpi.theo": "理论可发 {v}",
  626. "rpt.library": "组件库",
  627. "rpt.library_note": "点击加入报告, 再次点击移出 · 画布里已有 {n} 块",
  628. "rpt.local_note": "; 数据不出本地",
  629. "rpt.loop.cal": "振动标量前后对比",
  630. "rpt.loop.comp": "部件",
  631. "rpt.loop.date": "换件日期",
  632. "rpt.loop.note": "这几台正是算法当初的检出台 ⇒ 故障前能看见、换件后能看见恢复。",
  633. "rpt.loop.post": "换后",
  634. "rpt.loop.pre": "换前",
  635. "rpt.loop.ratio": "比值",
  636. "rpt.loop.title": "端到端闭环实证",
  637. "rpt.narr.cal": "模型 {m} 生成 · 数字取自系统实测摘要 · 数据回抓校验{g}",
  638. "rpt.narr.empty": "还没有生成 — 在上面的交互窗输入报告要求。",
  639. "rpt.narr.g_n": "报 {n} 处待核",
  640. "rpt.narr.g_ok": "全通过",
  641. "rpt.narr.title": "运行总结",
  642. "rpt.narr.ungr": "⚠ 以下数值由模型自行推算, 不在原始数据摘要中, 引用前须核对: {v}",
  643. "rpt.narr_plus": " + 一段运行总结",
  644. "rpt.need_empty": "先输入报告要求",
  645. "rpt.need_head": "输入报告要求",
  646. "rpt.need_hint": "模型依据需求选取组件, 并基于实测数据生成运行总结",
  647. "rpt.need_ph": "例如: 上报集团月报, 侧重设备可靠性与重点机组",
  648. "rpt.no_compose": "本部署未启用编排接口, 画布仍可手动拼装",
  649. "rpt.no_models": "本部署未配置任何模型密钥, 编排不可用",
  650. "rpt.paper_sub": "统计期 {win} | {n} 台",
  651. "rpt.paper_title": "运行分析报告",
  652. "rpt.pareto.cal": "报警事件表",
  653. "rpt.pareto.title": "故障帕累托",
  654. "rpt.parts.cal": "归因=段起点±30min 首发码",
  655. "rpt.parts.ev": "事件",
  656. "rpt.parts.ext": " (外部)",
  657. "rpt.parts.h": "停机h",
  658. "rpt.parts.mdt": "单次停机时长(MDT,h)",
  659. "rpt.parts.mtbf": "平均无故障间隔(MTBF,h)",
  660. "rpt.parts.part": "部件",
  661. "rpt.parts.title": "部件停机",
  662. "rpt.parts.units": "影响机组",
  663. "rpt.plan.1": '补机制定性 — {n} 台停在"判了级但根因未定", 是当前最大单一瓶颈',
  664. "rpt.plan.2": "回收 25~26 工单 — 台账止 2024-11, 本期检修执行与闭环无法自动判定",
  665. "rpt.plan.3": "油液采样节奏 — 四源里油液长期 stale, 只能作回测锚",
  666. "rpt.plan.4": "限电侧 — 损失六成在交易与调度侧, 设备侧改善空间有限",
  667. "rpt.plan.cal": "建议级 — 排期与动作须经审批",
  668. "rpt.plan.title": "下期计划与建议",
  669. "rpt.preset.blank": "空白画布",
  670. "rpt.preset.month": "月度运行分析",
  671. "rpt.preset.topic": "设备专题",
  672. "rpt.preset.week": "周报",
  673. "rpt.preset.year": "年度总结",
  674. "rpt.presets": "预设",
  675. "rpt.preview_empty": "文档预览窗 — 从上面的组件库点选内容, 此处显示报告预览",
  676. "rpt.print": "打印 / 存 PDF",
  677. "rpt.quad.cal": "纵界=10min(一个SCADA采样格)",
  678. "rpt.quad.note": "右下角报警码属重复触发, 不应按条数排定优先级。",
  679. "rpt.quad.title": "故障模式四象限",
  680. "rpt.ram.cal": "SCADA ≥1h 停机段, 不含调度令; 海上 MDT 含交通窗口",
  681. "rpt.ram.note": "设备类 {a}h / 外部类 {b}h (电网·风况·运维操作不计设备可靠性)",
  682. "rpt.ram.title": "可靠性 × 可维修性",
  683. "rpt.ungrounded_n": "{n} 个数字待核",
  684. "rpt.units.at": "卡在",
  685. "rpt.units.cal": "四源融合判级",
  686. "rpt.units.clear": "全通",
  687. "rpt.units.ev": "部件与证据",
  688. "rpt.units.head": "需跟踪 {n} 台 / {tot} | 瓶颈 ",
  689. "rpt.units.prog": "进度",
  690. "rpt.units.rising": "在升 {r}×",
  691. "rpt.units.state": "状态",
  692. "rpt.units.stuck": "{n} 台卡在「{s}」",
  693. "rpt.units.title": "重点机组与处置",
  694. "rpt.up_note": "上报版 — 判据门槛与特征频率已脱敏",
  695. "rpt.up_tag": "上报版",
  696. "rpt.water.cal": "SCADA 10min 工况态分解; 损失=同工况理论出力与实测之差。量级自检为防虚高的下限校验, 不是风资源判据",
  697. "rpt.water.cap_bad": "量级自检: 理论 ≥ 全额定上限 {cap} MWh ✗ 数字不可信",
  698. "rpt.water.cap_ok": "量级自检: 理论低于全额定上限 {cap} MWh ✔",
  699. "rpt.water.note": "理论可发 {theo} = 上网 {act} + 损失 {loss} MWh ({pct}%)",
  700. "rpt.water.title": "发电完成情况",
  701. "rv.advci": "AdvCI",
  702. "rv.dual": "双模型审",
  703. "rv.lite": "lite",
  704. "rv.none": "未审",
  705. "st.bad": "危险",
  706. "st.excellent": "优秀",
  707. "st.ok": "良好",
  708. "st.ok_excellent": "良好 / 优秀",
  709. "st.unk": "不可判",
  710. "st.warn": "报警",
  711. "step.acceptance": "验收",
  712. "step.action": "动作",
  713. "step.evidence": "证据",
  714. "step.mechanism": "机制",
  715. "step.rating": "判级",
  716. "step.schedule": "排期",
  717. "step.site_records": "现场记录",
  718. "step.stuck_at": "卡在「{step}」",
  719. "sys.blade": "叶片与叶根",
  720. "sys.control": "主控与传感网",
  721. "sys.converter": "变流器",
  722. "sys.gearbox": "齿轮箱",
  723. "sys.generator": "发电机",
  724. "sys.main_bearing": "主轴承",
  725. "sys.other": "其他",
  726. "sys.pitch": "变桨",
  727. "sys.tower": "塔架与基础",
  728. "sys.yaw": "偏航",
  729. "tab.assistant": "检修助手",
  730. "tab.component": "部件问题",
  731. "tab.decision": "检修决策",
  732. "tab.energy": "电量算账",
  733. "tab.fault": "故障统计",
  734. "tab.generation": "发电性能",
  735. "tab.ontology": "本体",
  736. "tab.overview": "总览",
  737. "tab.report": "汇报定制",
  738. "tab.system": "系统维护",
  739. "tab.vibration": "振动融合分析",
  740. "tier.candidate": "候选",
  741. "tier.candidate.s": "候选",
  742. "tier.candidate_baseline": "候选·记基线",
  743. "tier.candidate_baseline.s": "候选·记基线",
  744. "tier.candidate_new": "候选·新发",
  745. "tier.candidate_new.s": "候选·新发",
  746. "tier.confirmed": "定论",
  747. "tier.confirmed.s": "定论",
  748. "tier.detail.不可判": "不可判",
  749. "tier.detail.仲裁输入": "仲裁输入",
  750. "tier.detail.优": "优",
  751. "tier.detail.优秀": "优秀",
  752. "tier.detail.传感器项": "传感器项",
  753. "tier.detail.候选": "候选",
  754. "tier.detail.候选·监视(复发)": "候选·监视(复发)",
  755. "tier.detail.候选·跟踪": "候选·跟踪",
  756. "tier.detail.危险": "危险",
  757. "tier.detail.可降": "可降",
  758. "tier.detail.复发候选(两轴合并)": "复发候选(两轴合并)",
  759. "tier.detail.定论": "定论",
  760. "tier.detail.弱同向新证": "弱同向新证",
  761. "tier.detail.报": "报",
  762. "tier.detail.报警": "报警",
  763. "tier.detail.本体无新证": "本体无新证",
  764. "tier.detail.正常": "正常",
  765. "tier.detail.正样本": "正样本",
  766. "tier.detail.温度单轴保留": "温度单轴保留",
  767. "tier.detail.监视": "监视",
  768. "tier.detail.监视(不开箱)": "监视(不开箱)",
  769. "tier.detail.良": "良",
  770. "tier.detail.良好": "良好",
  771. "tier.detail.蓄能失效候选(定向查)": "蓄能失效候选(定向查)",
  772. "tier.detail.观察": "观察",
  773. "tier.detail.观察(发电机侧)": "观察(发电机侧)",
  774. "tier.detail.记基线观察": "记基线观察",
  775. "tier.detail.销案(定论)": "销案(定论)",
  776. "tier.detail.降观察": "降观察",
  777. "tier.detail.预警": "预警",
  778. "tier.detail.预警·处置升急": "预警·处置升急",
  779. "tier.detail.预警·处置规划": "预警·处置规划",
  780. "tier.insufficient": "不足",
  781. "tier.insufficient.s": "不足",
  782. "tier.near": "准定论·预警",
  783. "tier.near.s": "准定论",
  784. "tier.normal": "正常",
  785. "tier.normal.s": "正常",
  786. "tier.reference": "参考",
  787. "tier.reference.s": "参考",
  788. "tier.withdrawn": "撤回",
  789. "tier.withdrawn.s": "撤回",
  790. "ui.all": "全部",
  791. "ui.back_portal": "← 返回门户",
  792. "ui.back_to": "← 返回{tab}",
  793. "ui.collapse": "收起",
  794. "ui.count": "条数",
  795. "ui.data_hidden": "({n} 处数据文本尚无英文,需复核)",
  796. "ui.date": "日期",
  797. "ui.empty": "无数据",
  798. "ui.error": "出错了",
  799. "ui.expand": "展开",
  800. "ui.loading": "载入中…",
  801. "ui.not_ported": "本页尚未迁入新版。",
  802. "ui.not_recorded": "台账未填",
  803. "ui.open": "查看 →",
  804. "ui.open_classic": "在经典版打开 →",
  805. "ui.query": "查询",
  806. "ui.reselect": "重新选择机组 / 测点",
  807. "ui.retry": "重试",
  808. "ui.system": "系统",
  809. "ui.unit": "机组",
  810. "ui.units": "机组",
  811. "ui.verbatim": "原始记录 (中文)",
  812. "ui.verbatim_note": "{n} 处为手册/工单原文, 无英文译本, 按原文转录。",
  813. "unit.h": "h",
  814. "unit.n": " 条",
  815. "unit.units": "台",
  816. "vp.(振动线未收录)": "(振动线未收录)",
  817. "vp.主轴承(后)+集中润滑泵": "主轴承(后)+集中润滑泵",
  818. "vp.发电机(引用同族, 见族主台)": "发电机(无本台独立证据, 参照同型机组)",
  819. "vp.发电机(未映射)": "发电机(未对应到具体部件)",
  820. "vp.发电机DE": "发电机DE",
  821. "vp.发电机DE+NDE(两端)": "发电机DE+NDE(两端)",
  822. "vp.发电机NDE": "发电机NDE",
  823. "vp.齿轮箱(未映射)": "齿轮箱(未对应到具体部件)",
  824. "vp.齿轮箱(测量链问题, 非部件)": "齿轮箱(测量链问题, 非部件)",
  825. "vp.齿轮箱(谱线级, 未映射到部件)": "齿轮箱(频谱信号级 (未定位到具体零件), 未对应到具体部件)",
  826. "vp.齿轮箱中间轴": "齿轮箱中间轴",
  827. "vp.齿轮箱二级行星内齿圈": "齿轮箱二级行星内齿圈",
  828. "vp.齿轮箱高速轴": "齿轮箱高速轴",
  829. "win.2025H2": "2025H2",
  830. "win.2026": "2026年",
  831. "win.2026H1": "2026H1",
  832. "win.30d": "近30日",
  833. "win.90d": "近90日",
  834. "win.all": "全程",
  835. "win.apply": "应用",
  836. "win.bad_order": "开始日期不能晚于结束日期",
  837. "win.banner": "时间窗 {win} · 本页全部数字随上方时间窗切换",
  838. "win.custom": "自定义",
  839. "win.custom_hint": "自定义起止日期(含两端)。日粒度件(判级/停机台账/曲线)按日精确;月度聚合件按所跨月份取整,图上会写明。",
  840. "win.custom_sel": "自定义 {a} ~ {b}",
  841. "win.from": "开始日期(含)",
  842. "win.label": "时间窗",
  843. "win.months": "按月",
  844. "win.need_both": "请把开始与结束日期都选上",
  845. "win.partial": "时间窗内 {m} 数据不满月 (覆盖 {c}%), 月度图已按天归一",
  846. "win.presets": "预设时间窗",
  847. "win.through": "至{date}",
  848. "win.to": "结束日期(含)"
  849. };
  850. function t(key, vars) {
  851. let s = ZH[key] ?? key;
  852. if (vars) {
  853. for (const [k, v] of Object.entries(vars)) s = s.split(`{${k}}`).join(String(v));
  854. }
  855. return s;
  856. }
  857. const TABS = [
  858. "overview",
  859. "energy",
  860. "component",
  861. "vibration",
  862. "generation",
  863. "fault",
  864. "decision",
  865. "assistant",
  866. "report",
  867. "system"
  868. ];
  869. const DONE = ["overview", "energy", "component", "fault"];
  870. const ST = { 危险: "bad", 报警: "warn", 不可判: "unk", 良好: "ok", 优秀: "ex" };
  871. const ST_ALIAS = {
  872. critical: "bad",
  873. alarm: "warn",
  874. undeterminable: "unk",
  875. indeterminate: "unk",
  876. "not evaluable": "unk",
  877. good: "ok",
  878. excellent: "ex",
  879. warn: "warn",
  880. bad: "bad",
  881. ok: "ok",
  882. unk: "unk",
  883. ex: "ex"
  884. };
  885. const stateKey = (value) => ST[String(value)] || ST_ALIAS[String(value)];
  886. const STEP = {
  887. 证据: "evidence",
  888. 机制: "mechanism",
  889. 判级: "rating",
  890. 排期: "schedule",
  891. 动作: "action",
  892. 验收: "acceptance"
  893. };
  894. const stKey = (k) => "st." + (k === "ex" ? "excellent" : k);
  895. const unitNo = (wtg) => String(wtg).replace("WTG", "").replace(/^0/, "") + "#";
  896. const fmt = (n, d = 0) => {
  897. if (n == null || typeof n === "number" && isNaN(n)) return "—";
  898. const num = Number(n);
  899. if (isNaN(num)) return "—";
  900. return num.toLocaleString("zh-CN", { maximumFractionDigits: d, minimumFractionDigits: d });
  901. };
  902. const stWord = (zh) => {
  903. const k = stateKey(zh);
  904. return k ? t(stKey(k)) : String(zh ?? "");
  905. };
  906. const COLS = ["振动", "温度", "润滑", "油液", "报警", "工单"];
  907. function unitState(row) {
  908. const ks = COLS.map((c) => stateKey((row?.[c] || {}).ostate)).filter(Boolean);
  909. if (!ks.length) return "unk";
  910. if (ks.includes("bad")) return "bad";
  911. if (ks.includes("warn")) return "warn";
  912. const seen = ks.filter((k) => k !== "unk");
  913. if (!seen.length) return "unk";
  914. return seen.includes("ok") ? "ok" : "ex";
  915. }
  916. const SEV = {
  917. bad: "critical",
  918. warn: "warning",
  919. unk: "unknown",
  920. ok: "good",
  921. ex: "excellent"
  922. };
  923. const winName = (w) => String(w ?? "");
  924. const SYS9 = ["叶片与叶根", "变桨", "偏航", "齿轮箱", "发电机", "主轴承", "变流器", "塔架与基础", "主控与传感网"];
  925. const SYS_KEY = {
  926. 叶片与叶根: "blade",
  927. 变桨: "pitch",
  928. 偏航: "yaw",
  929. 齿轮箱: "gearbox",
  930. 发电机: "generator",
  931. 主轴承: "main_bearing",
  932. 变流器: "converter",
  933. 塔架与基础: "tower",
  934. 主控与传感网: "control",
  935. 主轴: "main_bearing",
  936. 其他: "other"
  937. };
  938. const SYS_ZH = (() => {
  939. const out = {};
  940. for (const zh of SYS9) out[SYS_KEY[zh]] = zh;
  941. for (const [zh, k] of Object.entries(SYS_KEY)) if (!(k in out)) out[k] = zh;
  942. return out;
  943. })();
  944. const sysSlug = (value) => SYS_KEY[String(value)] || String(value);
  945. const sysFromSlug = (value) => SYS_ZH[String(value)] || String(value);
  946. const sysName = (value) => {
  947. const key = SYS_KEY[String(value)] || String(value);
  948. return SYS_ZH[key] ? t("sys." + key) : String(value ?? "");
  949. };
  950. const partName = (p) => sysName(p);
  951. const _sfc_main$5 = /* @__PURE__ */ defineComponent({
  952. __name: "Component",
  953. __ssrInlineRender: true,
  954. props: {
  955. d: {},
  956. sys: {}
  957. },
  958. setup(__props) {
  959. const props = __props;
  960. const SD = computed(() => props.d?.sysdist || {});
  961. const rel = computed(() => props.d?.rel && !props.d.rel.err ? props.d.rel : null);
  962. const sysv = computed(() => props.sys ? sysFromSlug(props.sys) : null);
  963. const cards = computed(() => SYS9.filter((s) => SD.value[s]).map((s) => {
  964. const dist = SD.value[s];
  965. const a = dist["报警"] || 0;
  966. const u = dist["不可判"] || 0;
  967. const N = Object.values(dist).reduce((x, y) => x + y, 0) || 1;
  968. const col = a >= 8 ? "var(--critical)" : a >= 3 ? "var(--warning)" : a ? "var(--accent)" : "var(--good)";
  969. return { s, dist, a, u, N, col };
  970. }));
  971. function hitHtml(w) {
  972. return (w.syss || []).map((x) => `<a href="/problem/${w.t}/${encodeURIComponent(sysFromSlug(x) || x)}">${sysName(x)}</a>`).join(" · ");
  973. }
  974. const relEh = computed(() => {
  975. const items = ((props.d?.control || {}).energy || {}).items || [];
  976. const hit = items.filter((x) => x.k === "停机").map((x) => x.h)[0];
  977. return hit == null ? null : hit;
  978. });
  979. const relRows = computed(() => {
  980. const tb = rel.value?.表 || [];
  981. const maxH = Math.max(1, ...tb.map((r) => r.停机时长h));
  982. return tb.map((r) => ({
  983. r,
  984. maxH,
  985. sev: r.部件 === "无记录停机" ? "var(--unknown)" : r.外部 ? "var(--accent)" : r.占停机 >= 0.06 ? "var(--critical)" : r.占停机 >= 0.03 ? "var(--warning)" : "var(--good)"
  986. }));
  987. });
  988. const detail = computed(() => {
  989. const s = sysv.value;
  990. if (!s) return null;
  991. const rows = (props.d?.systems || {})[s] || [];
  992. const dist = SD.value[s] || {};
  993. const gd = (props.d?.systems || {})[s + "·良好"] || [];
  994. const cnt = (k) => dist[k] || 0;
  995. const N = Object.values(dist).reduce((a, b) => a + b, 0) || 1;
  996. return {
  997. s,
  998. N,
  999. gd,
  1000. cnt,
  1001. al: rows.filter((r) => r.st === "报警"),
  1002. un: rows.filter((r) => r.st === "不可判"),
  1003. dist
  1004. };
  1005. });
  1006. return (_ctx, _push, _parent, _attrs) => {
  1007. if (detail.value) {
  1008. _push(`<section${ssrRenderAttrs(_attrs)}><p class="small"><a href="#tab=component" data-back="1">${ssrInterpolate(unref(t)("ui.back_to", { tab: unref(t)("tab.component") }))}</a></p><div class="panel mb"><h3>${ssrInterpolate(unref(t)("cmp.detail_head", { sys: unref(sysName)(detail.value.s), n: detail.value.N }))}</h3><div style="${ssrRenderStyle({ "display": "flex", "height": "12px", "border-radius": "4px", "overflow": "hidden", "margin": "8px 0 4px" })}"><!--[-->`);
  1009. ssrRenderList([["报警", "critical"], ["不可判", "unknown"], ["良好", "good"], ["优秀", "excellent"]], ([k, c]) => {
  1010. _push(`<i style="${ssrRenderStyle(`width:${(detail.value.cnt(k) / detail.value.N * 100).toFixed(1)}%;background:var(--${c})`)}"></i>`);
  1011. });
  1012. _push(`<!--]--></div><div class="small muted">${ssrInterpolate(unref(t)("cmp.counts", { a: detail.value.cnt("报警"), u: detail.value.cnt("不可判"), g: detail.value.cnt("良好"), e: detail.value.cnt("优秀") }))}</div></div><!--[-->`);
  1013. ssrRenderList([detail.value.al, detail.value.un], (list, kind) => {
  1014. _push(`<div class="panel mb" style="${ssrRenderStyle(list.length ? null : { display: "none" })}"><h3>${ssrInterpolate(kind === 0 ? unref(t)("cmp.alarm_units") : unref(t)("cmp.unk_units"))} ${ssrInterpolate(unref(t)("cmp.n_units", { n: list.length }))}</h3><div class="tblwrap"><table><tr><th style="${ssrRenderStyle({ "width": "74px" })}">${ssrInterpolate(unref(t)("ui.unit"))}</th><th>${ssrInterpolate(unref(t)("cmp.why"))}</th><th class="r" style="${ssrRenderStyle({ "width": "96px" })}">${ssrInterpolate(unref(t)("cmp.detail"))}</th></tr><!--[-->`);
  1015. ssrRenderList(list, (r) => {
  1016. _push(`<tr><td><a${ssrRenderAttr("href", `/turbine/${r.t}`)}><b>${ssrInterpolate(unref(unitNo)(r.t))}</b></a></td><td class="small">${ssrInterpolate(r.why)}</td><td class="r"><a class="small"${ssrRenderAttr("href", `/problem/${r.t}/${encodeURIComponent(detail.value.s)}`)}>${ssrInterpolate(unref(t)("cmp.detail_link"))}</a></td></tr>`);
  1017. });
  1018. _push(`<!--]--></table></div></div>`);
  1019. });
  1020. _push(`<!--]-->`);
  1021. if (detail.value.gd.length) {
  1022. _push(`<details class="panel mb"><summary style="${ssrRenderStyle({ "cursor": "pointer", "font-weight": "600" })}">${ssrInterpolate(unref(t)("cmp.good_units", { n: detail.value.gd.length }))}</summary><div class="tblwrap"><table style="${ssrRenderStyle({ "margin-top": "10px" })}"><tr><th style="${ssrRenderStyle({ "width": "74px" })}">${ssrInterpolate(unref(t)("ui.unit"))}</th><th>${ssrInterpolate(unref(t)("cmp.why"))}</th></tr><!--[-->`);
  1023. ssrRenderList(detail.value.gd, (r) => {
  1024. _push(`<tr><td><b>${ssrInterpolate(unref(unitNo)(r.t))}</b></td><td class="small">${ssrInterpolate(r.why)}</td></tr>`);
  1025. });
  1026. _push(`<!--]--></table></div></details>`);
  1027. } else {
  1028. _push(`<!---->`);
  1029. }
  1030. if (!detail.value.al.length && !detail.value.un.length && !detail.value.gd.length) {
  1031. _push(`<div class="panel"><p class="small">${ssrInterpolate(unref(t)("cmp.none"))}</p></div>`);
  1032. } else {
  1033. _push(`<!---->`);
  1034. }
  1035. _push(`</section>`);
  1036. } else {
  1037. _push(`<!--[-->`);
  1038. if (__props.d.sysmx_pending) {
  1039. _push(`<section><div class="panel mb small" style="${ssrRenderStyle({ "border-left": "3px solid var(--accent)" })}"><b>${ssrInterpolate(unref(t)("cmp.grading_pending"))}</b> ${ssrInterpolate(unref(t)("cmp.grading_pending_why", { span: __props.d.sysmx_span || "" }))}</div></section>`);
  1040. } else {
  1041. _push(`<!---->`);
  1042. }
  1043. _push(`<section><div class="eyebrow">${ssrInterpolate(unref(t)("cmp.by_system"))}</div><div class="panel mb"><div style="${ssrRenderStyle({ "display": "grid", "grid-template-columns": "repeat(auto-fit,minmax(170px,1fr))", "gap": "10px" })}"><!--[-->`);
  1044. ssrRenderList(cards.value, (c) => {
  1045. _push(`<div class="sysc" role="button" tabindex="0"${ssrRenderAttr("data-sys", unref(sysSlug)(c.s))} style="${ssrRenderStyle(`cursor:pointer;border-left:3px solid ${c.col}`)}"><div class="t"><b>${ssrInterpolate(unref(sysName)(c.s))}</b></div><div class="bar"><!--[-->`);
  1046. ssrRenderList([["报警", "critical"], ["不可判", "unknown"], ["良好", "good"], ["优秀", "excellent"]], ([k, col]) => {
  1047. _push(`<i style="${ssrRenderStyle([
  1048. `width:${(c.dist[k] / c.N * 100).toFixed(1)}%;background:var(--${col})`,
  1049. c.dist[k] ? null : { display: "none" }
  1050. ])}"></i>`);
  1051. });
  1052. _push(`<!--]--></div><div class="small muted"><span>${unref(t)("cmp.alarm_n", { a: `<b style="color:${c.col}">${c.a}</b>` }) ?? ""}</span>${ssrInterpolate((c.u ? unref(t)("cmp.unk_n", { u: c.u }) : "") + unref(t)("cmp.fleet_n", { n: c.N }))}</div></div>`);
  1053. });
  1054. _push(`<!--]--></div><p class="small muted" style="${ssrRenderStyle({ "margin-top": "8px" })}">${ssrInterpolate(unref(t)("cmp.cards_note"))}</p></div></section>`);
  1055. if (__props.d.rel && __props.d.rel.err) {
  1056. _push(`<section><div class="panel mb" style="${ssrRenderStyle({ "border-left": "3px solid var(--critical)" })}"><p class="small">${ssrInterpolate(unref(t)("flt.block_err", { k: "rel", e: __props.d.rel.err }))}</p></div></section>`);
  1057. } else if (rel.value) {
  1058. _push(`<section><div class="eyebrow">${ssrInterpolate(unref(t)("cmp.rel_eyebrow", { win: rel.value.窗 }))}</div><div class="panel mb"><h3>${ssrInterpolate(unref(t)("cmp.rel_head"))} <span class="small muted" style="${ssrRenderStyle({ "font-weight": "400" })}">${ssrInterpolate(unref(t)("cmp.rel_meta", { t: rel.value.总停机h, a: rel.value.设备类停机h, b: rel.value.外部类停机h }))}</span></h3><p class="small muted" style="${ssrRenderStyle({ "margin": "-2px 0 8px" })}">${ssrInterpolate(unref(t)("cmp.rel_note", { h: relEh.value == null ? "" : " " + unref(fmt)(relEh.value) + "h" }))}</p><div class="tblwrap"><table><tr><th>${ssrInterpolate(unref(t)("cmp.rel_part"))}</th><th>${ssrInterpolate(unref(t)("cmp.rel_h"))}</th><th class="r">${ssrInterpolate(unref(t)("cmp.rel_ev"))}</th><th class="r">${ssrInterpolate(unref(t)("cmp.rel_mtbo"))}</th><th class="r">${ssrInterpolate(unref(t)("cmp.rel_mdt"))}</th><th class="r">${ssrInterpolate(unref(t)("cmp.rel_units"))}</th><th>${ssrInterpolate(unref(t)("cmp.rel_fm"))}</th></tr><!--[-->`);
  1059. ssrRenderList(relRows.value, (x) => {
  1060. _push(`<tr><td><b>${ssrInterpolate(unref(partName)(x.r.部件))}</b>`);
  1061. if (x.r.外部) {
  1062. _push(`<span>${ssrInterpolate(" ")}<span class="badge">${ssrInterpolate(unref(t)("cmp.external"))}</span></span>`);
  1063. } else {
  1064. _push(`<!---->`);
  1065. }
  1066. _push(`</td><td><i class="bar" style="${ssrRenderStyle(`width:${Math.round(x.r.停机时长h / x.maxH * 72)}px;background:${x.sev}`)}"></i><span class="num small">${ssrInterpolate(x.r.停机时长h)}h · ${ssrInterpolate((x.r["占停机"] * 100).toFixed(1))}%</span></td><td class="r">${ssrInterpolate(x.r.事件数)}</td><td class="r">${ssrInterpolate(x.r.外部 ? "—" : x.r.MTBF_h || "—")}</td><td class="r">${ssrInterpolate(x.r.MDT_h)}</td><td class="r">${ssrInterpolate(x.r.影响台数)}</td><td class="small">${ssrInterpolate(x.r.典型失效模式 || "—")}</td></tr>`);
  1067. });
  1068. _push(`<!--]--></table></div><p class="small muted">${ssrInterpolate(rel.value.口径)}</p></div></section>`);
  1069. } else {
  1070. _push(`<!---->`);
  1071. }
  1072. if (__props.d.fus && !__props.d.fus.err) {
  1073. _push(`<section><div class="panel mb"><p class="small">${ssrInterpolate(unref(t)("cmp.fusion_moved"))} <a href="#tab=vibration"><b>${ssrInterpolate(unref(t)("tab.vibration"))}</b></a> ${ssrInterpolate(unref(t)("cmp.fusion_n", { n: (__props.d.fus.表 || []).length }))}</p></div></section>`);
  1074. } else {
  1075. _push(`<!---->`);
  1076. }
  1077. _push(`<section><div class="eyebrow">${ssrInterpolate(unref(t)("cmp.cross_eyebrow"))}</div><div class="panel mb"><h3>${ssrInterpolate(unref(t)("cmp.watch_head", { n: (__props.d.watch || []).length }))}</h3><div class="tblwrap"><table><tr><th>${ssrInterpolate(unref(t)("ui.unit"))}</th><th class="r">${ssrInterpolate(unref(t)("cmp.n_sys"))}</th><th>${ssrInterpolate(unref(t)("cmp.hit_sys"))}</th><th>${ssrInterpolate(unref(t)("cmp.basis"))}</th></tr><!--[-->`);
  1078. ssrRenderList(__props.d.watch || [], (w) => {
  1079. _push(`<tr><td><a${ssrRenderAttr("href", `/turbine/${w.t}`)}><b>${ssrInterpolate(unref(unitNo)(w.t))}</b></a></td><td class="r">${ssrInterpolate(w.n)}</td><td class="small">${hitHtml(w) ?? ""}</td><td class="small">${ssrInterpolate(w.why)}</td></tr>`);
  1080. });
  1081. _push(`<!--]--></table></div></div></section><!--]-->`);
  1082. }
  1083. };
  1084. }
  1085. });
  1086. const _sfc_setup$5 = _sfc_main$5.setup;
  1087. _sfc_main$5.setup = (props, ctx) => {
  1088. const ssrContext = useSSRContext();
  1089. (ssrContext.modules || (ssrContext.modules = /* @__PURE__ */ new Set())).add("src/views/Component.vue");
  1090. return _sfc_setup$5 ? _sfc_setup$5(props, ctx) : void 0;
  1091. };
  1092. const _sfc_main$4 = /* @__PURE__ */ defineComponent({
  1093. __name: "EChart",
  1094. __ssrInlineRender: true,
  1095. props: {
  1096. option: {},
  1097. height: {}
  1098. },
  1099. setup(__props) {
  1100. const props = __props;
  1101. const el = ref(null);
  1102. let chart = null;
  1103. async function draw() {
  1104. if (!el.value) return;
  1105. const echarts = await import("echarts");
  1106. if (!chart) chart = echarts.init(el.value, void 0, { renderer: "canvas" });
  1107. chart.setOption(props.option, true);
  1108. }
  1109. onMounted(draw);
  1110. watch(() => props.option, draw, { deep: true });
  1111. onBeforeUnmount(() => {
  1112. if (chart) {
  1113. chart.dispose();
  1114. chart = null;
  1115. }
  1116. });
  1117. return (_ctx, _push, _parent, _attrs) => {
  1118. _push(`<div${ssrRenderAttrs(mergeProps({
  1119. class: "echart",
  1120. "data-chart": JSON.stringify(__props.option),
  1121. style: `height:${__props.height || "260px"}`,
  1122. ref_key: "el",
  1123. ref: el
  1124. }, _attrs))} data-v-999bee15></div>`);
  1125. };
  1126. }
  1127. });
  1128. const _export_sfc = (sfc, props) => {
  1129. const target = sfc.__vccOpts || sfc;
  1130. for (const [key, val] of props) {
  1131. target[key] = val;
  1132. }
  1133. return target;
  1134. };
  1135. const _sfc_setup$4 = _sfc_main$4.setup;
  1136. _sfc_main$4.setup = (props, ctx) => {
  1137. const ssrContext = useSSRContext();
  1138. (ssrContext.modules || (ssrContext.modules = /* @__PURE__ */ new Set())).add("src/components/EChart.vue");
  1139. return _sfc_setup$4 ? _sfc_setup$4(props, ctx) : void 0;
  1140. };
  1141. const EChart = /* @__PURE__ */ _export_sfc(_sfc_main$4, [["__scopeId", "data-v-999bee15"]]);
  1142. const _sfc_main$3 = /* @__PURE__ */ defineComponent({
  1143. __name: "Energy",
  1144. __ssrInlineRender: true,
  1145. props: {
  1146. d: {}
  1147. },
  1148. setup(__props) {
  1149. const props = __props;
  1150. const E = computed(() => (props.d?.control || {}).energy);
  1151. const S = computed(() => (props.d?.control || {}).states || []);
  1152. const LOSS = {
  1153. "限电·绑定": "loss.curtail_bound",
  1154. "限电命令·未绑定": "loss.curtail_unbound",
  1155. "停机(调度令)": "loss.dispatch",
  1156. 停机: "loss.stop",
  1157. 正常发电: "loss.normal",
  1158. 低风待机: "loss.lowwind"
  1159. };
  1160. const lossName = (k) => LOSS[k] ? t(LOSS[k]) : k;
  1161. const kpi = computed(() => {
  1162. const e = E.value || {};
  1163. return [
  1164. { v: fmt(e.theo), u: "MWh", l: t("en.kpi_theo"), k: "" },
  1165. { v: fmt(e.act), u: "MWh", l: t("en.kpi_act", { h: fmt(e.eflh) }), k: "ok" },
  1166. { v: fmt(e.loss_pct, 1), u: "%", l: t("en.kpi_loss", { v: fmt(e.loss) }), k: e.loss_pct >= 10 ? "warn" : "" },
  1167. {
  1168. v: e.cap_ok ? t("en.cap_pass") : t("en.cap_fail"),
  1169. u: "",
  1170. l: t("en.cap_note", { op: e.cap_ok ? "<" : "≥", cap: fmt(e.cap) }),
  1171. k: e.cap_ok ? "ok" : "bad"
  1172. }
  1173. ];
  1174. });
  1175. const blameCat = (k) => k.indexOf("限电") >= 0 || k.indexOf("调度") >= 0 ? 0 : k === "停机" ? 1 : 2;
  1176. const blameGroups = computed(() => {
  1177. const g = [0, 0, 0];
  1178. for (const x of E.value?.items || []) g[blameCat(x.k)] += x.loss;
  1179. return g;
  1180. });
  1181. const waterfallOption = computed(() => {
  1182. const e = E.value || {};
  1183. const items = (e.items || []).filter((x) => x.loss > 0).map((x) => ({ name: x.k, value: Math.round(x.loss) }));
  1184. const groups = blameGroups.value;
  1185. const tot = groups.reduce((a, b) => a + b, 0) || 1;
  1186. const pct = groups.map((v) => Math.round(v / tot * 100)).filter((p, i) => groups[i] / tot * 500 > 46);
  1187. return {
  1188. title: { text: t("en.balance"), left: "center", textStyle: { fontSize: 13 } },
  1189. tooltip: { trigger: "axis" },
  1190. grid: { left: 70, right: 20, top: 44, bottom: 40 },
  1191. xAxis: { type: "category", data: items.map((x) => x.name) },
  1192. yAxis: { type: "value", name: "MWh" },
  1193. series: [{ type: "bar", data: items.map((x) => x.value), itemStyle: { color: "#0b5fa5" } }],
  1194. // 与旧图数字集合同源:theo · act · Σitems · 各 item · 组占比%
  1195. __data__: [
  1196. Math.round(e.theo),
  1197. Math.round(e.act),
  1198. Math.round(items.reduce((a, x) => a + x.value, 0)),
  1199. ...items.map((x) => x.value),
  1200. ...pct
  1201. ]
  1202. };
  1203. });
  1204. const blameOption = computed(() => {
  1205. const groups = blameGroups.value;
  1206. return {
  1207. grid: { left: 90, right: 24, top: 20, bottom: 30 },
  1208. xAxis: { type: "value", name: "MWh" },
  1209. yAxis: { type: "category", data: [t("ch.blame.n1"), t("ch.blame.n2"), t("ch.blame.n3")] },
  1210. series: [{ type: "bar", data: groups.map((v) => Math.round(v)), itemStyle: { color: "#2f7dbf" } }],
  1211. __data__: []
  1212. };
  1213. });
  1214. const mxp = computed(() => Math.max(1, ...S.value.map((x) => x.pct)));
  1215. return (_ctx, _push, _parent, _attrs) => {
  1216. if (!E.value) {
  1217. _push(`<section${ssrRenderAttrs(_attrs)}><div class="panel"><p class="small muted">${ssrInterpolate(unref(t)("ui.empty"))}</p></div></section>`);
  1218. } else {
  1219. _push(`<section${ssrRenderAttrs(_attrs)}><div class="kpis" style="${ssrRenderStyle({ "margin-bottom": "12px" })}"><!--[-->`);
  1220. ssrRenderList(kpi.value, (k, i) => {
  1221. _push(`<div class="${ssrRenderClass([k.k, "kpi"])}"><b>${ssrInterpolate(k.v)}`);
  1222. if (k.u) {
  1223. _push(`<small>${ssrInterpolate(k.u)}</small>`);
  1224. } else {
  1225. _push(`<!---->`);
  1226. }
  1227. _push(`</b><span>${ssrInterpolate(k.l)}</span></div>`);
  1228. });
  1229. _push(`<!--]--></div><div class="grid2"><div class="panel span2"><h3>${ssrInterpolate(unref(t)("en.balance"))} <span class="small muted" style="${ssrRenderStyle({ "font-weight": "400" })}">${ssrInterpolate(unref(t)("en.win", { win: unref(winName)(__props.d.win) }))}</span></h3>`);
  1230. _push(ssrRenderComponent(EChart, {
  1231. option: waterfallOption.value,
  1232. height: "280px"
  1233. }, null, _parent));
  1234. _push(`</div><div class="panel"><h3>${ssrInterpolate(unref(t)("en.blame"))}</h3>`);
  1235. _push(ssrRenderComponent(EChart, {
  1236. option: blameOption.value,
  1237. height: "240px"
  1238. }, null, _parent));
  1239. _push(`<!--[-->`);
  1240. ssrRenderList(blameGroups.value, (v, i) => {
  1241. _push(`<div style="${ssrRenderStyle({ "display": "flex", "align-items": "baseline", "gap": "7px", "margin-top": "5px" })}"><i style="${ssrRenderStyle(`flex:none;width:9px;height:9px;border-radius:2px;background:${["var(--accent)", "var(--critical)", "var(--muted)"][i]};opacity:.55`)}"></i><span style="${ssrRenderStyle({ "font-weight": "600" })}">${ssrInterpolate(unref(t)(["ch.blame.n1", "ch.blame.n2", "ch.blame.n3"][i]))}</span><span class="num">${ssrInterpolate(unref(fmt)(Math.round(v)))} MWh</span><span class="small muted">${ssrInterpolate(unref(t)(["ch.blame.d1", "ch.blame.d2", "ch.blame.d3"][i]))}</span></div>`);
  1242. });
  1243. _push(`<!--]--><p class="small muted" style="${ssrRenderStyle({ "margin-top": "6px" })}">${ssrInterpolate(unref(t)("en.blame_note", { n: unref(fmt)(Math.round(((E.value.items || []).find((x) => x.k === "停机") || {}).loss || 0)) }))}</p></div><div class="panel"><h3>${ssrInterpolate(unref(t)("en.states"))} <span class="small muted" style="${ssrRenderStyle({ "font-weight": "400" })}">${ssrInterpolate(unref(t)("en.win", { win: unref(winName)(__props.d.win) }))}</span></h3><div class="tblwrap"><table><tr><th>${ssrInterpolate(unref(t)("en.state"))}</th><th>${ssrInterpolate(unref(t)("en.share"))}</th><th class="r" style="${ssrRenderStyle({ "width": "90px" })}">${ssrInterpolate(unref(t)("en.loss_mwh"))}</th></tr><!--[-->`);
  1244. ssrRenderList(S.value, (x) => {
  1245. _push(`<tr><td class="small">${ssrInterpolate(lossName(x.k))}</td><td><i class="bar" style="${ssrRenderStyle(`width:${Math.round(x.pct / mxp.value * 84)}px;background:${x.k.indexOf("停机") >= 0 && x.k.indexOf("调度") < 0 ? "var(--critical)" : "var(--accent)"}`)}"></i><span class="num small">${ssrInterpolate(x.pct)}% · ${ssrInterpolate(unref(fmt)(Math.round(x.h)))}h</span></td><td class="r small">${ssrInterpolate(unref(fmt)(Math.round(x.loss)))}</td></tr>`);
  1246. });
  1247. _push(`<!--]--></table></div><p class="small muted" style="${ssrRenderStyle({ "margin-top": "6px" })}">${ssrInterpolate(__props.d.control.anchors || "")}<br>${ssrInterpolate(unref(t)("en.states_note"))}</p></div></div></section>`);
  1248. }
  1249. };
  1250. }
  1251. });
  1252. const _sfc_setup$3 = _sfc_main$3.setup;
  1253. _sfc_main$3.setup = (props, ctx) => {
  1254. const ssrContext = useSSRContext();
  1255. (ssrContext.modules || (ssrContext.modules = /* @__PURE__ */ new Set())).add("src/views/Energy.vue");
  1256. return _sfc_setup$3 ? _sfc_setup$3(props, ctx) : void 0;
  1257. };
  1258. const _sfc_main$2 = /* @__PURE__ */ defineComponent({
  1259. __name: "Fault",
  1260. __ssrInlineRender: true,
  1261. props: {
  1262. d: {}
  1263. },
  1264. setup(__props) {
  1265. const props = __props;
  1266. const F = computed(() => props.d?.faults || {});
  1267. const pm = computed(() => F.value.partial || []);
  1268. const M = computed(() => props.d?.m8 && !props.d.m8.err ? props.d.m8.mtbf : null);
  1269. const mo = computed(() => F.value.monthly || { months: [], vals: [] });
  1270. const perDay = computed(() => mo.value.per_day || []);
  1271. const exampleIdx = computed(() => pm.value.length ? (mo.value.months || []).indexOf(pm.value[0]) : -1);
  1272. const rateTail = computed(() => {
  1273. const head = pm.value.length ? t("flt.rate_partial", { m: pm.value.join("/") }) : t("flt.rate_full");
  1274. const hasExample = exampleIdx.value >= 0 && !!mo.value.per_day;
  1275. return head + (hasExample ? " " + t("flt.rate_example", { m: pm.value[0], v: exampleVal.value }) : "");
  1276. });
  1277. const exampleVal = computed(() => {
  1278. const i = exampleIdx.value;
  1279. return i >= 0 && mo.value.per_day ? mo.value.per_day[i] : null;
  1280. });
  1281. const partialWarnTxt = computed(() => pm.value.length ? t("win.partial", { m: pm.value.join("/"), c: Math.round((F.value.cov_min || 0) * 100) }) : "");
  1282. const basic = {
  1283. grid: { left: 60, right: 20, top: 24, bottom: 46 },
  1284. tooltip: { trigger: "axis" },
  1285. yAxis: { type: "value" }
  1286. };
  1287. const rateOption = computed(() => ({
  1288. ...basic,
  1289. xAxis: { type: "category", data: mo.value.months || [] },
  1290. series: [{ type: "bar", data: perDay.value.map((v) => v == null ? null : Math.round(v * 100) / 100), itemStyle: { color: "#0b5fa5" } }],
  1291. __data__: perDay.value.filter((v) => v != null)
  1292. }));
  1293. function pareto(items) {
  1294. const it = (items || []).filter((x) => x && x.v > 0);
  1295. return {
  1296. ...basic,
  1297. xAxis: { type: "category", data: it.map((x) => x.k) },
  1298. series: [{ type: "bar", data: it.map((x) => x.v), itemStyle: { color: "#2f7dbf" } }],
  1299. __data__: []
  1300. // 旧 Pareto 只把坐标刻度写成 SVG 文字(数据靠柱高/悬停),故不给数
  1301. };
  1302. }
  1303. const shortK = (k) => {
  1304. const m = String(k).match(/^([0-9]+)/);
  1305. return m ? m[1] : String(k).slice(0, 4);
  1306. };
  1307. const esc = (s) => String(s ?? "").replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;");
  1308. function paretoSummaryHtml(items, unit, note) {
  1309. const it = (items || []).filter((x) => x && x.v > 0);
  1310. if (!it.length) return esc(t("ch.pareto.none"));
  1311. const tot = it.reduce((a, x) => a + x.v, 0);
  1312. let cum = 0;
  1313. let k80 = null;
  1314. for (let i = 0; i < it.length; i++) {
  1315. cum += it[i].v;
  1316. if (k80 === null && tot && cum / tot >= 0.8) k80 = i + 1;
  1317. }
  1318. const top3 = it.slice(0, 3).map((x) => `<b>${esc(shortK(x.k))}</b> ${esc(String(x.k).replace(/^[0-9]+ */, "").slice(0, 18))}`).join(" · ");
  1319. const total = t("ch.pareto.total", { n: it.length, tot: fmt(Math.round(tot)), unit });
  1320. const head = k80 ? `<b>${esc(t("ch.pareto.key", { k: k80 }))}</b> (${esc(total)})` : esc(total);
  1321. return head + "<br>" + esc(t("ch.pareto.top3")) + top3 + (note ? "<br>" + esc(note) : "");
  1322. }
  1323. const paretoNSummary = computed(() => paretoSummaryHtml(F.value.pareto_n || [], t("unit.n"), t("flt.pareto_n_note")));
  1324. const paretoDSummary = computed(() => paretoSummaryHtml(F.value.pareto_d || [], " h", t("flt.pareto_d_note")));
  1325. const stopParetoSummary = computed(() => paretoSummaryHtml(stopPareto.value, " h", t("flt.stop_pareto_note")));
  1326. const paretoNOption = computed(() => pareto(F.value.pareto_n || []));
  1327. const paretoDOption = computed(() => pareto(F.value.pareto_d || []));
  1328. function k80Of(items) {
  1329. const it = (items || []).filter((x) => x && x.v > 0);
  1330. if (!it.length) return null;
  1331. const tot = it.reduce((a, x) => a + x.v, 0);
  1332. let cum = 0;
  1333. for (const x of it) {
  1334. cum += x.v;
  1335. if (tot && cum / tot >= 0.8) return { k: String(x.k), n: it.length, tot };
  1336. }
  1337. return { k: String(it[it.length - 1].k), n: it.length, tot };
  1338. }
  1339. computed(() => k80Of(F.value.pareto_n || []));
  1340. computed(() => k80Of(F.value.pareto_d || []));
  1341. const quadOption = computed(() => {
  1342. const it = (F.value.quad || []).filter((x) => x && x.n > 0 && x.med > 0);
  1343. return {
  1344. grid: { left: 60, right: 24, top: 24, bottom: 46 },
  1345. xAxis: { type: "log", name: "MTBF(h)" },
  1346. yAxis: { type: "value", name: "MDT(h)" },
  1347. series: [{
  1348. type: "scatter",
  1349. data: it.map((x) => [x.mtbf ?? x.n, x.med]),
  1350. symbolSize: (v) => Math.min(28, 6 + Math.sqrt(Math.max(v[0] || 1, 1))),
  1351. itemStyle: { color: "#b42318", opacity: 0.6 }
  1352. }],
  1353. __data__: []
  1354. // 象限图是散点:坐标即数据,旧图不把数值写成文字
  1355. };
  1356. });
  1357. const pt = computed(() => F.value.per_t || []);
  1358. const maxT = computed(() => Math.max(1, ...pt.value.map((x) => x.v)));
  1359. const seasonal = computed(() => M.value && props.d?.m8?.seasonal ? props.d.m8.seasonal : []);
  1360. const ML_COLS = ["var(--accent)", "#D48A1E", "#2E8B6A", "#7C5295"];
  1361. const seasonalNames = computed(() => seasonal.value.slice(0, 4).map((x) => String(x.name).slice(0, 16)));
  1362. const seasonalOption = computed(() => {
  1363. const s = seasonal.value.slice(0, 4);
  1364. const months = s.length ? s[0].months : [];
  1365. return {
  1366. ...basic,
  1367. xAxis: { type: "category", data: months },
  1368. legend: { top: 0, textStyle: { fontSize: 10 } },
  1369. series: s.map((x) => ({ name: String(x.name).slice(0, 16), type: "line", data: x.vals, connectNulls: false })),
  1370. __data__: []
  1371. // 旧 multiline 只标刻度;系列值不写成文字
  1372. };
  1373. });
  1374. const RT = computed(() => props.d?.rel && !props.d.rel.err ? props.d.rel : null);
  1375. const ramOption = computed(() => {
  1376. const rows = RT.value?.表 || [];
  1377. return {
  1378. grid: { left: 60, right: 24, top: 24, bottom: 40 },
  1379. xAxis: { type: "value", name: "MTBF(h)" },
  1380. yAxis: { type: "value", name: "MDT(h)" },
  1381. series: [{
  1382. type: "scatter",
  1383. data: rows.filter((r) => !r.外部 && r.MTBF_h).map((r) => [r.MTBF_h, r.MDT_h]),
  1384. symbolSize: 8,
  1385. itemStyle: { color: "#2f7dbf", opacity: 0.5 }
  1386. }],
  1387. __data__: []
  1388. // 旧 ramChart 也是散点(MTBF×MDT),数值不写成文字
  1389. };
  1390. });
  1391. const attribGroups = computed(() => {
  1392. const rows = RT.value?.表 || [];
  1393. const unk = (rows.find((r) => r.部件 === "无记录停机") || {}).停机时长h || 0;
  1394. const un2 = rows.filter((r) => r.部件 === "未归类").reduce((a, r) => a + r.停机时长h, 0);
  1395. const tot = RT.value?.总停机h || 0;
  1396. const ok = tot - unk - un2;
  1397. return { ok, unk, un2 };
  1398. });
  1399. const attribOption = computed(() => {
  1400. const g = attribGroups.value;
  1401. return {
  1402. xAxis: { type: "value" },
  1403. yAxis: { type: "category", data: [t("ch.attrib.ok", { h: Math.round(g.ok), p: "" }), t("ch.attrib.none", { h: Math.round(g.unk), p: "" }), t("ch.attrib.uncls", { h: Math.round(g.un2) })] },
  1404. series: [{ type: "bar", data: [Math.round(g.ok), Math.round(g.unk), Math.round(g.un2)], itemStyle: { color: "#0b5fa5" } }],
  1405. __data__: [Math.round(g.ok), Math.round(g.unk), Math.round(g.un2)]
  1406. };
  1407. });
  1408. const stopPareto = computed(() => (props.d?.m8?.stop_pareto || []).map((x) => ({
  1409. k: String(x.k).split(";")[0].slice(0, 26),
  1410. v: x.h
  1411. })));
  1412. const stopParetoOption = computed(() => pareto(stopPareto.value));
  1413. const top = computed(() => M.value?.台级top || []);
  1414. const mxTop = computed(() => Math.max(1, ...top.value.map((x) => x.时长h)));
  1415. return (_ctx, _push, _parent, _attrs) => {
  1416. _push(`<section${ssrRenderAttrs(_attrs)}><div class="eyebrow">${ssrInterpolate(unref(t)("flt.eyebrow"))}</div><div class="panel mb small" style="${ssrRenderStyle({ "border-left": "3px solid var(--accent)" })}"><b>${ssrInterpolate(unref(t)("win.banner", { win: unref(winName)(__props.d.win) }))}</b>`);
  1417. if (partialWarnTxt.value) {
  1418. _push(`<!--[--> | <span style="${ssrRenderStyle({ "color": "var(--warning)" })}">${ssrInterpolate(partialWarnTxt.value)}</span><!--]-->`);
  1419. } else {
  1420. _push(`<!---->`);
  1421. }
  1422. _push(`</div><div class="grid2"><div class="eyebrow" style="${ssrRenderStyle({ "grid-column": "1/-1", "margin-top": "4px" })}">${ssrInterpolate(unref(t)("flt.alarm_face"))} <span style="${ssrRenderStyle({ "text-transform": "none", "letter-spacing": "0", "color": "var(--muted)" })}">${ssrInterpolate(unref(t)("flt.alarm_meta", { n: unref(fmt)(F.value.total), c: F.value.n_codes, win: unref(winName)(__props.d.win) }))}</span></div><div class="panel span2"><h3>${ssrInterpolate(unref(t)("flt.rate"))}</h3>`);
  1423. _push(ssrRenderComponent(EChart, {
  1424. option: rateOption.value,
  1425. height: "250px"
  1426. }, null, _parent));
  1427. _push(`<p class="small muted" style="${ssrRenderStyle({ "margin-top": "6px" })}"><b>${ssrInterpolate(unref(t)("flt.rate_note"))}</b> — ${ssrInterpolate(rateTail.value)}</p></div><div class="panel"><h3>${ssrInterpolate(unref(t)("flt.pareto_n"))}</h3>`);
  1428. _push(ssrRenderComponent(EChart, {
  1429. option: paretoNOption.value,
  1430. height: "240px"
  1431. }, null, _parent));
  1432. _push(`<p class="small muted" style="${ssrRenderStyle({ "margin-top": "5px", "line-height": "1.6" })}">${paretoNSummary.value ?? ""}</p></div><div class="panel"><h3>${ssrInterpolate(unref(t)("flt.pareto_d"))}</h3>`);
  1433. _push(ssrRenderComponent(EChart, {
  1434. option: paretoDOption.value,
  1435. height: "240px"
  1436. }, null, _parent));
  1437. _push(`<p class="small muted" style="${ssrRenderStyle({ "margin-top": "5px", "line-height": "1.6" })}">${paretoDSummary.value ?? ""}</p></div><div class="panel span2"><h3>${ssrInterpolate(unref(t)("flt.quad"))}</h3>`);
  1438. _push(ssrRenderComponent(EChart, {
  1439. option: quadOption.value,
  1440. height: "300px"
  1441. }, null, _parent));
  1442. _push(`<p class="small muted" style="${ssrRenderStyle({ "margin-top": "6px" })}">${ssrInterpolate(unref(t)("flt.quad_note", { n: F.value.n_codes, m: (F.value.quad || []).length }))} `);
  1443. if (pm.value.length) {
  1444. _push(`<span style="${ssrRenderStyle({ "color": "var(--warning)" })}">${ssrInterpolate(unref(t)("flt.quad_partial", { m: pm.value.join("/"), c: Math.round((F.value.cov_min || 0) * 100) }))}</span>`);
  1445. } else {
  1446. _push(`<!---->`);
  1447. }
  1448. _push(` ${ssrInterpolate(unref(t)("flt.quad_note2"))}</p></div><div class="panel"><h3>${ssrInterpolate(unref(t)("flt.per_t", { n: pt.value.length }))}</h3><div class="tblwrap"><table><tr><th style="${ssrRenderStyle({ "width": "70px" })}">${ssrInterpolate(unref(t)("ui.unit"))}</th><th>${ssrInterpolate(unref(t)("ui.count"))}</th><th class="r" style="${ssrRenderStyle({ "width": "80px" })}">${ssrInterpolate(unref(t)("flt.per_t_share"))}</th></tr><!--[-->`);
  1449. ssrRenderList(pt.value, (x) => {
  1450. _push(`<tr><td><b>${ssrInterpolate(unref(unitNo)(x.k))}</b></td><td><i class="bar" style="${ssrRenderStyle(`width:${Math.round(x.v / maxT.value * 90)}px;background:${x.v / maxT.value > 0.6 ? "var(--critical)" : "var(--accent)"}`)}"></i><span class="num small">${ssrInterpolate(unref(fmt)(x.v))}</span></td><td class="r small">${ssrInterpolate((x.v / F.value.total * 100).toFixed(1))}%</td></tr>`);
  1451. });
  1452. _push(`<!--]--></table></div><p class="small muted" style="${ssrRenderStyle({ "margin-top": "6px" })}">${ssrInterpolate(unref(t)("flt.per_t_note", { n: unref(fmt)(F.value.total) }))}</p></div>`);
  1453. if (__props.d.m8 && __props.d.m8.err) {
  1454. _push(`<div class="panel" style="${ssrRenderStyle({ "border-left": "3px solid var(--critical)" })}"><p class="small">${ssrInterpolate(unref(t)("flt.block_err", { k: "m8", e: __props.d.m8.err }))}</p></div>`);
  1455. } else {
  1456. _push(`<!---->`);
  1457. }
  1458. if (M.value && seasonal.value.length) {
  1459. _push(`<div class="panel"><h3>${ssrInterpolate(unref(t)("flt.seasonal"))} <span class="small muted" style="${ssrRenderStyle({ "font-weight": "400" })}">(${ssrInterpolate(unref(t)("flt.seasonal_unit"))})</span></h3>`);
  1460. _push(ssrRenderComponent(EChart, {
  1461. option: seasonalOption.value,
  1462. height: "260px"
  1463. }, null, _parent));
  1464. _push(`<div class="legend" style="${ssrRenderStyle({ "margin-top": "6px", "flex-wrap": "wrap" })}"><!--[-->`);
  1465. ssrRenderList(seasonalNames.value, (nm, i) => {
  1466. _push(`<span><i style="${ssrRenderStyle(`background:${ML_COLS[i % 4]}`)}"></i>${ssrInterpolate(nm)}</span>`);
  1467. });
  1468. _push(`<!--]--></div><p class="small muted" style="${ssrRenderStyle({ "margin-top": "6px" })}">${ssrInterpolate(unref(t)("flt.seasonal_note"))}</p></div>`);
  1469. } else {
  1470. _push(`<!---->`);
  1471. }
  1472. if (M.value) {
  1473. _push(`<!--[--><div class="eyebrow" style="${ssrRenderStyle({ "grid-column": "1/-1", "margin-top": "4px" })}">${ssrInterpolate(unref(t)("flt.stop_face"))} <span style="${ssrRenderStyle({ "text-transform": "none", "letter-spacing": "0", "color": "var(--muted)" })}">${ssrInterpolate(unref(t)("flt.stop_meta", { h: unref(fmt)(M.value.停机总时长h), n: unref(fmt)(M.value.停机事件数) }))}</span></div>`);
  1474. if (RT.value) {
  1475. _push(`<div class="panel span2"><h3>${ssrInterpolate(unref(t)("flt.ram"))}</h3>`);
  1476. _push(ssrRenderComponent(EChart, {
  1477. option: ramOption.value,
  1478. height: "300px"
  1479. }, null, _parent));
  1480. _push(`<p class="small muted" style="${ssrRenderStyle({ "margin-top": "6px" })}">${ssrInterpolate(unref(t)("flt.ram_note", { a: RT.value.设备类停机h, b: RT.value.外部类停机h }))}</p><div style="${ssrRenderStyle({ "margin-top": "12px" })}">`);
  1481. _push(ssrRenderComponent(EChart, {
  1482. option: attribOption.value,
  1483. height: "160px"
  1484. }, null, _parent));
  1485. _push(`</div><p class="small muted" style="${ssrRenderStyle({ "margin-top": "2px" })}">${ssrInterpolate(unref(t)("flt.attrib_note", { h: ((RT.value.表 || []).find((x) => x.部件 === "无记录停机") || {}).停机时长h || 0 }))}</p></div>`);
  1486. } else {
  1487. _push(`<!---->`);
  1488. }
  1489. _push(`<div class="panel"><h3>${ssrInterpolate(unref(t)("flt.stop_pareto"))}</h3>`);
  1490. _push(ssrRenderComponent(EChart, {
  1491. option: stopParetoOption.value,
  1492. height: "240px"
  1493. }, null, _parent));
  1494. _push(`<p class="small muted" style="${ssrRenderStyle({ "margin-top": "5px", "line-height": "1.6" })}">${stopParetoSummary.value ?? ""}</p><p class="small muted" style="${ssrRenderStyle({ "margin-top": "4px" })}">${ssrInterpolate(M.value.口径)}</p></div><div class="panel"><h3>${ssrInterpolate(unref(t)("flt.stop_units", { n: top.value.length }))}</h3><div class="tblwrap"><table><tr><th style="${ssrRenderStyle({ "width": "64px" })}">${ssrInterpolate(unref(t)("ui.unit"))}</th><th>${ssrInterpolate(unref(t)("flt.stop_h"))}</th><th class="r" style="${ssrRenderStyle({ "width": "56px" })}">${ssrInterpolate(unref(t)("flt.stop_n"))}</th></tr><!--[-->`);
  1495. ssrRenderList(top.value, (x) => {
  1496. _push(`<tr><td><b>${ssrInterpolate(unref(unitNo)(x.t))}</b></td><td><i class="bar" style="${ssrRenderStyle(`width:${Math.round(x.时长h / mxTop.value * 90)}px;background:${x.时长h / mxTop.value > 0.6 ? "var(--critical)" : "var(--accent)"}`)}"></i><span class="num small">${ssrInterpolate(unref(fmt)(x.时长h))}</span>h</td><td class="r small">${ssrInterpolate(x.次)}</td></tr>`);
  1497. });
  1498. _push(`<!--]--></table></div><p class="small muted" style="${ssrRenderStyle({ "margin-top": "6px" })}">${ssrInterpolate(unref(t)("flt.stop_units_note"))}</p></div><!--]-->`);
  1499. } else {
  1500. _push(`<!---->`);
  1501. }
  1502. _push(`</div></section>`);
  1503. };
  1504. }
  1505. });
  1506. const _sfc_setup$2 = _sfc_main$2.setup;
  1507. _sfc_main$2.setup = (props, ctx) => {
  1508. const ssrContext = useSSRContext();
  1509. (ssrContext.modules || (ssrContext.modules = /* @__PURE__ */ new Set())).add("src/views/Fault.vue");
  1510. return _sfc_setup$2 ? _sfc_setup$2(props, ctx) : void 0;
  1511. };
  1512. const _sfc_main$1 = /* @__PURE__ */ defineComponent({
  1513. __name: "Overview",
  1514. __ssrInlineRender: true,
  1515. props: {
  1516. d: {}
  1517. },
  1518. setup(__props) {
  1519. const props = __props;
  1520. const F = computed(() => props.d?.fus || {});
  1521. const M = computed(() => props.d?.m8?.mtbf || {});
  1522. const C = computed(() => props.d?.control || {});
  1523. const LS = computed(() => {
  1524. const out = {};
  1525. for (const x of C.value.states || []) out[x.k] = x.loss;
  1526. return out;
  1527. });
  1528. const kpi = computed(() => {
  1529. const c = C.value;
  1530. const lossLabel = t("kpi.loss", { win: winName(props.d?.win) }) + (LS.value["停机(调度令)"] != null ? ` · ${t("kpi.loss_dispatch", { n: fmt(LS.value["停机(调度令)"]) })}` : "");
  1531. return [
  1532. { v: fmt(c.avail, 1), u: "%", l: t("kpi.avail", { win: winName(props.d?.win) }), k: c.avail >= 95 ? "ok" : "warn", ti: "" },
  1533. { v: fmt(M.value.停机总时长h), u: "h", l: t("kpi.downtime", { n: fmt(M.value.停机事件数) }), k: "warn", ti: "" },
  1534. { v: fmt(M.value.MTBF_h), u: "h", l: t("kpi.mtbo"), k: "", ti: "" },
  1535. { v: fmt(LS.value["停机"]), u: "MWh", l: lossLabel, k: "", ti: "" },
  1536. { v: fmt(LS.value["限电·绑定"]), u: "MWh", l: t("kpi.curtail"), k: "", ti: "" },
  1537. {
  1538. // 旧模板 `<b>${fmt(报警台)} <small>/ ${fmt(全场)}</small></b>` —— `vHtml` 保留 <small> 元素与节点边界
  1539. vRaw: `${fmt(props.d?.kpi?.报警台)} <small>/ ${fmt(props.d?.kpi?.全场)}</small>`,
  1540. u: "",
  1541. l: t("kpi.alarm_units"),
  1542. k: props.d?.kpi?.报警台 ? "warn" : "ok",
  1543. ti: t("kpi.alarm_units.hint"),
  1544. vHtml: true
  1545. }
  1546. ];
  1547. });
  1548. const why = computed(() => {
  1549. const out = {};
  1550. for (const w of props.d?.watch || []) out[w.t] = w.why;
  1551. return out;
  1552. });
  1553. const rows = computed(() => ((F.value.链盘 || {}).rows || []).filter((r) => r.lvl === "bad" || r.lvl === "warn"));
  1554. function basis(r) {
  1555. const own = (why.value[r.t] || "").trim();
  1556. if (own) return own;
  1557. const stuckKey = STEP[r.stuck] || String(r.stuck || "").toLowerCase();
  1558. const stuckStep = stuckKey === "evidence" ? t("step.site_records") : t("step." + (stuckKey || "evidence"));
  1559. return t("ov.basis_fusion", {
  1560. part: r.part || "—",
  1561. done: r.done == null ? "—" : r.done,
  1562. n: (r.steps || []).length || "—",
  1563. stuck: r.stuck ? stuckStep : t("ov.all_steps_done"),
  1564. srcs: (r.srcs || []).join(" / ") || t("ov.no_srcs")
  1565. });
  1566. }
  1567. function stuckText(r) {
  1568. if (!r.stuck) return "";
  1569. const stuckKey = STEP[r.stuck] || String(r.stuck || "").toLowerCase();
  1570. const stuckStep = stuckKey === "evidence" ? t("step.site_records") : t("step." + (stuckKey || "evidence"));
  1571. return t("step.stuck_at", { step: stuckStep });
  1572. }
  1573. const SYS_SEG = [["危险", "critical"], ["报警", "warning"], ["不可判", "unknown"], ["良好", "good"], ["优秀", "excellent"]];
  1574. const systems = computed(() => {
  1575. const dist = props.d?.sysdist || {};
  1576. return SYS9.map((s) => {
  1577. const c = dist[s] || {};
  1578. const n = Object.values(c).reduce((a, b) => a + b, 0) || 1;
  1579. return { name: sysName(s), slug: sysSlug(s), dist: c, n, seg: SYS_SEG.filter(([k]) => c[k]).map(([k, v]) => ({ k, v })) };
  1580. });
  1581. });
  1582. const mat = computed(() => F.value.矩阵 || []);
  1583. const hot = computed(() => new Set(rows.value.map((r) => r.t)));
  1584. return (_ctx, _push, _parent, _attrs) => {
  1585. _push(`<!--[--><section><div class="kpis"><!--[-->`);
  1586. ssrRenderList(kpi.value, (k, i) => {
  1587. _push(`<div class="${ssrRenderClass([k.k, "kpi"])}"${ssrRenderAttr("title", k.ti || void 0)}>`);
  1588. if (k.vHtml) {
  1589. _push(`<b>${k.vRaw ?? ""}</b>`);
  1590. } else {
  1591. _push(`<b>${ssrInterpolate(k.v)}`);
  1592. if (k.u) {
  1593. _push(`<small>${ssrInterpolate(k.u)}</small>`);
  1594. } else {
  1595. _push(`<!---->`);
  1596. }
  1597. _push(`</b>`);
  1598. }
  1599. _push(`<span>${ssrInterpolate(k.l)}</span></div>`);
  1600. });
  1601. _push(`<!--]--></div></section><section><div class="sechead"><h2>${ssrInterpolate(unref(t)("ov.attention"))}</h2><span class="meta">${ssrInterpolate(unref(t)("ov.attention_n", { n: rows.value.length }))}</span><span class="meta">${ssrInterpolate(unref(t)("ov.ranked_by"))}</span></div>`);
  1602. if (F.value.gap) {
  1603. _push(`<div class="card mb" style="${ssrRenderStyle({ "margin-bottom": "10px" })}"><p class="small" style="${ssrRenderStyle({ "color": "var(--warning)" })}">${ssrInterpolate(F.value.gap)}</p><p class="small muted">${ssrInterpolate(unref(t)("ov.fus_gap_hint"))}</p></div>`);
  1604. } else {
  1605. _push(`<!---->`);
  1606. }
  1607. _push(`<div class="cards"><!--[-->`);
  1608. ssrRenderList(rows.value, (r) => {
  1609. _push(`<div class="card"${ssrRenderAttr("id", `u-${r.t}`)} style="${ssrRenderStyle(`--sev:var(--${unref(SEV)[unref(stateKey)(r.ostate) || (r.lvl === "bad" ? "bad" : "warn")]})`)}"><div class="head"><a class="unit"${ssrRenderAttr("href", `/turbine/${r.t}`)}>${ssrInterpolate(unref(unitNo)(r.t))}</a><span class="sys">${ssrInterpolate(r.part || "")}</span><span class="${ssrRenderClass([unref(stateKey)(r.ostate) || (r.lvl === "bad" ? "bad" : "warn"), "chip"])}">${ssrInterpolate(unref(stWord)(r.ostate || (r.lvl === "bad" ? "危险" : "报警")))}</span></div><details class="evidence-detail"><summary>${ssrInterpolate(unref(t)("ov.evidence"))}</summary><div class="why">${ssrInterpolate(basis(r))}</div></details><div class="evid">`);
  1610. if (r.estate) {
  1611. _push(`<span class="${ssrRenderClass([r.estate, "tier"])}">${ssrInterpolate(unref(stWord)(r.estate))}</span>`);
  1612. } else {
  1613. _push(`<!---->`);
  1614. }
  1615. if (r.srcs && r.srcs.length) {
  1616. _push(`<span class="rv">${ssrInterpolate(r.srcs.length)} sources</span>`);
  1617. } else {
  1618. _push(`<!---->`);
  1619. }
  1620. _push(`</div>`);
  1621. if (stuckText(r) || r.next) {
  1622. _push(`<div class="caveat"><b>${ssrInterpolate(unref(t)("ov.caveat"))}</b>${ssrInterpolate(stuckText(r))}${ssrInterpolate(stuckText(r) && r.next ? " — " : "")}${ssrInterpolate(r.next || "")}</div>`);
  1623. } else {
  1624. _push(`<!---->`);
  1625. }
  1626. _push(`</div>`);
  1627. });
  1628. _push(`<!--]--></div></section><section><div class="sechead"><h2>${ssrInterpolate(unref(t)("ov.strip"))}</h2><span class="meta">${ssrInterpolate(unref(t)("ov.strip_hint"))}</span><div class="legend"><!--[-->`);
  1629. ssrRenderList(["bad", "warn", "unk", "ok", "ex"], (k) => {
  1630. _push(`<span><i style="${ssrRenderStyle(`background:var(--${unref(SEV)[k]})`)}"></i>${ssrInterpolate(unref(t)(unref(stKey)(k)))}</span>`);
  1631. });
  1632. _push(`<!--]--></div></div><div class="strip" style="${ssrRenderStyle(`--n:${mat.value.length || 38}`)}"><div class="cells">`);
  1633. if (mat.value.length) {
  1634. _push(`<!--[-->`);
  1635. ssrRenderList(mat.value, (r) => {
  1636. _push(`<div class="${ssrRenderClass([[unref(unitState)(r), hot.value.has(r.turbine) ? "hot" : ""], "cell"])}"${ssrRenderAttr("data-u", r.turbine)}${ssrRenderAttr("title", `${unref(unitNo)(r.turbine)} ${unref(t)(unref(stKey)(unref(unitState)(r)))}`)}></div>`);
  1637. });
  1638. _push(`<!--]-->`);
  1639. } else {
  1640. _push(`<div class="quiet" style="${ssrRenderStyle({ "padding": "10px" })}">${ssrInterpolate(F.value.gap || unref(t)("ov.fus_gap_hint"))}</div>`);
  1641. }
  1642. _push(`</div><div class="nums"><!--[-->`);
  1643. ssrRenderList(mat.value, (r) => {
  1644. _push(`<span class="${ssrRenderClass(hot.value.has(r.turbine) ? "hot" : "")}">${ssrInterpolate(unref(unitNo)(r.turbine).replace("#", ""))}</span>`);
  1645. });
  1646. _push(`<!--]--></div><div class="quiet">${ssrInterpolate(rows.value.length ? unref(t)("ov.quiet", { n: Math.max(0, (mat.value.length || 38) - rows.value.length) }) : unref(t)("ov.quiet_all", { n: mat.value.length || 38 }))}</div></div></section><section><div class="sechead"><h2>${ssrInterpolate(unref(t)("ov.systems"))}</h2><span class="meta">${ssrInterpolate(unref(t)("ov.systems_hint"))}</span></div><div class="systems"><!--[-->`);
  1647. ssrRenderList(systems.value, (s) => {
  1648. _push(`<div class="sysc"><div class="t"><b>${ssrInterpolate(s.name)}</b><a${ssrRenderAttr("href", `#tab=component&sys=${encodeURIComponent(s.slug)}`)}>${ssrInterpolate(unref(t)("ui.open"))}</a></div><div class="bar"><!--[-->`);
  1649. ssrRenderList(s.seg, (k) => {
  1650. _push(`<i style="${ssrRenderStyle(`width:${(s.dist[k.k] / s.n * 100).toFixed(1)}%;background:var(--${k.v})`)}"></i>`);
  1651. });
  1652. _push(`<!--]--></div><div class="cnt"><!--[-->`);
  1653. ssrRenderList(s.seg, (k) => {
  1654. _push(`<span><i style="${ssrRenderStyle(`background:var(--${k.v})`)}"></i>${ssrInterpolate(s.dist[k.k])}</span>`);
  1655. });
  1656. _push(`<!--]--></div></div>`);
  1657. });
  1658. _push(`<!--]--></div></section><!--]-->`);
  1659. };
  1660. }
  1661. });
  1662. const _sfc_setup$1 = _sfc_main$1.setup;
  1663. _sfc_main$1.setup = (props, ctx) => {
  1664. const ssrContext = useSSRContext();
  1665. (ssrContext.modules || (ssrContext.modules = /* @__PURE__ */ new Set())).add("src/views/Overview.vue");
  1666. return _sfc_setup$1 ? _sfc_setup$1(props, ctx) : void 0;
  1667. };
  1668. const _sfc_main = /* @__PURE__ */ defineComponent({
  1669. __name: "Workbench",
  1670. __ssrInlineRender: true,
  1671. props: {
  1672. d: {},
  1673. tab: {},
  1674. sys: {}
  1675. },
  1676. emits: ["tab"],
  1677. setup(__props, { emit: __emit }) {
  1678. const props = __props;
  1679. const pending = computed(() => !DONE.includes(props.tab));
  1680. return (_ctx, _push, _parent, _attrs) => {
  1681. _push(`<!--[--><nav class="wb-tabs" data-v-d9042022><!--[-->`);
  1682. ssrRenderList(unref(TABS), (id) => {
  1683. _push(`<button class="${ssrRenderClass({ on: id === __props.tab })}" data-v-d9042022>${ssrInterpolate(unref(t)("tab." + id))}`);
  1684. if (!unref(DONE).includes(id)) {
  1685. _push(`<sup class="todo" data-v-d9042022>迁移中</sup>`);
  1686. } else {
  1687. _push(`<!---->`);
  1688. }
  1689. _push(`</button>`);
  1690. });
  1691. _push(`<!--]--></nav><div class="page-head" data-v-d9042022><h1 data-v-d9042022>${ssrInterpolate(unref(t)("tab." + __props.tab))}</h1><span class="window-badge" data-v-d9042022>${ssrInterpolate(unref(t)("win.label"))} · ${ssrInterpolate(unref(winName)(__props.d?.win))}</span></div>`);
  1692. if (__props.tab === "overview") {
  1693. _push(ssrRenderComponent(_sfc_main$1, { d: __props.d }, null, _parent));
  1694. } else if (__props.tab === "energy") {
  1695. _push(ssrRenderComponent(_sfc_main$3, { d: __props.d }, null, _parent));
  1696. } else if (__props.tab === "component") {
  1697. _push(ssrRenderComponent(_sfc_main$5, {
  1698. d: __props.d,
  1699. sys: __props.sys
  1700. }, null, _parent));
  1701. } else if (__props.tab === "fault") {
  1702. _push(ssrRenderComponent(_sfc_main$2, { d: __props.d }, null, _parent));
  1703. } else if (pending.value) {
  1704. _push(`<section class="card" data-v-d9042022><h2 data-v-d9042022>${ssrInterpolate(unref(t)("tab." + __props.tab))} <small data-v-d9042022>(本页签尚未迁移到 Vue)</small></h2><p class="hint" data-v-d9042022> 迁移纪律:页签逐个搬,每搬一个就用 <code data-v-d9042022>tools/render_parity.mjs</code> 与重构前页面 (现网 <code data-v-d9042022>/detail/v2</code>)对拍文案/数值/类名序列,**通过后**才删对应的 Python 前端源码。 当前已迁移:${ssrInterpolate(unref(DONE).join("、"))}。 </p></section>`);
  1705. } else {
  1706. _push(`<!---->`);
  1707. }
  1708. _push(`<!--]-->`);
  1709. };
  1710. }
  1711. });
  1712. const _sfc_setup = _sfc_main.setup;
  1713. _sfc_main.setup = (props, ctx) => {
  1714. const ssrContext = useSSRContext();
  1715. (ssrContext.modules || (ssrContext.modules = /* @__PURE__ */ new Set())).add("src/views/Workbench.vue");
  1716. return _sfc_setup ? _sfc_setup(props, ctx) : void 0;
  1717. };
  1718. const Workbench = /* @__PURE__ */ _export_sfc(_sfc_main, [["__scopeId", "data-v-d9042022"]]);
  1719. async function renderTab(d, tab = "overview") {
  1720. const app = createSSRApp(Workbench, { d, tab });
  1721. return await renderToString(app);
  1722. }
  1723. export {
  1724. renderTab
  1725. };