import { defineComponent, ref, onMounted, unref, useSSRContext, onUnmounted, computed, watch, onBeforeUnmount, mergeProps, reactive, createSSRApp } from "vue";
import { ssrRenderAttrs, ssrInterpolate, ssrRenderStyle, ssrRenderList, ssrRenderAttr, ssrIncludeBooleanAttr, ssrRenderClass, ssrRenderComponent, renderToString } from "vue/server-renderer";
const ZH = {
"app.brand": "观澜",
"app.build": "最新完成构建",
"app.lang_en": "英文",
"app.lang_zh": "中文",
"app.subtitle": "海上风电场",
"app.title": "观澜 · 风电数据分析与决策支持",
"ask.common_q": "{cat} · 常用问题 (点即问, 或下方自由输入)",
"ask.done_meta": "模型 {m} · 用时 {s} 秒 · {n} 步",
"ask.elapsed": "{s} 秒",
"ask.empty": "问题为空",
"ask.en_qa_failed": "英文质检未通过: 已拦截这次模型回答, 请重新提问。",
"ask.error": "出错: {e}",
"ask.go": "提问",
"ask.grounding": "答案句句带 [对象id] 溯源 + 数据回抓校验核验; 判级只转述本体原文带审级, 模型零判级权。",
"ask.idle": "空闲 — 点常用问题或输入后提问。串行一次一问。",
"ask.local_down": "本机模型未启动 (Ollama 127.0.0.1:11434 未响应); 离线版不转云",
"ask.model": "模型",
"ask.no_models": "无可用模型",
"ask.offline": "离线版: 模型问答需连接系统。",
"ask.partial": "初答 · 27B 复核中",
"ask.placeholder": "自由提问 ({cat})",
"ask.question": "问题: {q}",
"ask.refined": "已复核 (默认档 {m} 未过成文校验, 由更强模型重答)",
"ask.refs.none": "无匹配契约条目 (契约 {sha}): 本回答未被结构化事实契约背书, 按模型输出对待",
"ask.refs.title": "契约引用 ({n} 条, 契约 {sha}) — 按问题/回答匹配的可交付结论, 引文来自 qa_refs",
"ask.review": "交叉审核 · {m}",
"ask.review_err": "审核未完成: {e}",
"ask.review_note": "由另一模型基于同一份证据复核上述答案。溯源核验仅校验数字与机组号是否越界, 本项校验结论是否有证据支撑。",
"ask.running": "推理中…",
"ask.verify_bad": "溯源核验未全过 (引用 {n})",
"ask.verify_ok": "溯源核验 {n}/{n} 全真",
"ask.xrev": "交叉审核",
"ask.xrev_note": "(DeepSeek 14B 互审, +约 1 分钟)",
"ask.xrev_title": "由另一模型基于同一份证据复核; 同一模型自审无法构成独立校验",
"asst.alarm_type": "告警类型 {t}",
"asst.cause_counts": "手册 {r} 次 · 工单 {w} 次 · 涉码 {c}",
"asst.causes": "← 可能原因 (手册排查方向 + 工单实证)",
"asst.check": "查什么",
"asst.code_placeholder": "报警码, 如 3225 / 3130 / 13902",
"asst.common": "常见",
"asst.corrective": "抢修",
"asst.corrective_flow": "报警码 → 失效模式 → 手册分步处置 → 历史工单",
"asst.drawings": "对应图纸",
"asst.failmodes": "指向的失效模式",
"asst.faulttree": "故障树",
"asst.faulttree_flow": "按证据量排序的系统失效模式 + 部件级成因",
"asst.fm_counts": "该类本场报警 {a} 条 / 工单 {w} 条",
"asst.head": "检修助手 — 确定性本体查询",
"asst.head_note": "结果逐字取自手册与工单, 不经模型",
"asst.identity": "系统 {sys} · 本场 {n} 条",
"asst.interval": "周期 {i}",
"asst.known_tags": "该系统手册出现过的位号 (对图纸时参考):",
"asst.no_causes": "(手册无该类排查条目 — 原因层待补)",
"asst.no_name": "(码表未收录)",
"asst.no_steps": "本码无手册处置条目 (手册覆盖本场 161/322 码) — 需查图纸或联系厂商支持。",
"asst.node": "节点",
"asst.nodes": "应检节点 · {c}",
"asst.nodes_note": "手册此步未给位号 → 按回路构成展开, 逐点查",
"asst.prev_empty": "(该系统无匹配的年度维护条目 — 维护作业指导书按位置代号编排, 未必逐系统对齐)",
"asst.prev_fm": "失效模式 (按本场报警量)",
"asst.prev_head": "{sys} 预防链 · 失效模式 {f} 类 · 预防性任务 {n} 项",
"asst.prev_tasks": "年度任务",
"asst.preventive": "预防性维护",
"asst.preventive_flow": "系统 → 失效模式 → 年度任务 → 作业指导步骤",
"asst.qty": "量: {q}",
"asst.query_failed": "查询失败: {e}",
"asst.querying": "查询中…",
"asst.spec_name": "名称",
"asst.spec_no": "参数号",
"asst.spec_range": "允许范围",
"asst.spec_val": "整定值",
"asst.specs": "判定规范 (实机参数表)",
"asst.step_n": "第 {n} 步",
"asst.steps": "现场处置 {n} 步",
"asst.steps_note": "节点部件 = 手册原文元件位号 (确定); 判定规范 = 实机参数表按物理量匹配的候选判据, 非厂商绑定 — 现场以铭牌与图纸复核。",
"asst.tags": "节点部件",
"asst.tree_codes": "征兆码 {c}",
"asst.tree_counts": "本场报警 {a} 条 · 工单 {w} 条",
"asst.tree_empty": "该系统无失效模式记录",
"asst.tree_head": "{sys} 故障树 · {n} 个失效模式",
"asst.tree_note": "一层 = 系统 × 失效类 (归纳自本场码表与工单) · 二层 = 手册排查方向归一 + 工单原因计数; 条长按实证量 (报警条数 + 工单 × 10), 因果来自手册与工单原文非推断, 树深两层不外推。",
"asst.unassigned": "未归属",
"asst.wi_steps": "WI {n} 步",
"asst.wo_action": "处置",
"asst.wo_fault": "故障",
"asst.wo_head": "现场实际怎么修的 ({sys} 历史工单 {n} 条, 台账 2020~2024)",
"asst.wo_note": '工单时间窗止 2024-11-21; 2025–26 现场未提供 ⇒ "0 条" 是记录缺失非未修过。',
"attr.变桨与液压": "变桨与液压",
"attr.塔架/结构": "塔架/结构",
"attr.无记录停机": "无记录停机",
"attr.未归类": "未归类",
"attr.电网/供电(外部)": "电网/供电(外部)",
"attr.轮毂/叶片": "轮毂/叶片",
"attr.运维操作": "运维操作",
"attr.风况(外部)": "风况(外部)",
"cat.equip": "设备类",
"cat.equip.hint": "设备状态 / 机制 / 判级 — 从机组档案、对象查询与机制检索取证",
"cat.equip.q1": "29号机组现在什么状态、为什么、该怎么办?",
"cat.equip.q2": "全场哪些机组有报警级或定论级判级?逐台列出并注明审级",
"cat.equip.q3": "失效机制库里有哪些机制族?各涉及哪些机组、状态如何?",
"cat.loss": "损失分析",
"cat.loss.hint": "当前 3 个月损失 / 未来损失 — ont_loss / ont_scenario 取证, 金额带电价区间统计方法",
"cat.loss.q1": "近3个月全场损失结构如何?分状态和分机组说, 电量与金额都给",
"cat.loss.q2": "29号如果拖到主轴承失效, 未来损失量级是多少?与现在修对比",
"cat.loss.q3": "限电常态化对检修机会成本有什么影响?",
"cat.risk": "安全风险预测",
"cat.risk.hint": "风险敞口 / 劣化趋势 / 盲区 — ont_risk / ont_mechanism_search 取证, 未列 ≠ 正常",
"cat.risk.q1": "当前哪些机组风险在上升需要重点盯?联动监视读数如何?",
"cat.risk.q2": '监测有哪些盲区?哪些"没报警"不等于正常?',
"cat.risk.q3": "29号轴承正在承受金属接触, 风险敞口怎么评估?",
"cat.sched": "检修排程决策",
"cat.sched.hint": "排期沙盘 / 天气窗 / 验收树 — ont_scenario / ont_get 取证",
"cat.sched.q1": "29号修泵什么时候做最合适?对比各情景的损失与风险",
"cat.sched.q2": "未来几个月哪些月份适合安排CTV检修和吊装作业?依据历史可作业窗",
"cat.sched.q3": "29号修好之后按什么判据验收?三分支分别是什么条件?",
"ch.attrib.none": "无记录 {h}h ({p}%)",
"ch.attrib.ok": "已归因到部件 {h}h ({p}%)",
"ch.attrib.uncls": "未归类 {h}h",
"ch.beta.median": "全场中位 {v}°",
"ch.blame.d1": "限电与调度令, 不进设备责任",
"ch.blame.d2": "故障与检修停机, 唯一进设备考核",
"ch.blame.d3": "正常发电欠发与低风待机",
"ch.blame.n1": "交易与调度侧",
"ch.blame.n2": "设备侧",
"ch.blame.n3": "运行侧",
"ch.blame.title": "损失 {tot} MWh 按责任归属",
"ch.cap.legend": "圆内数字 = 台数, 圆面积 ∝ 台数",
"ch.cap.median": "中位 {v}",
"ch.cap.p": "满发封顶",
"ch.cap.w": "转速封顶",
"ch.ml.legend": "— {s}",
"ch.pareto.cum80": "累计 80%",
"ch.pareto.cumtip": "{k} · 累计 {p}%",
"ch.pareto.key": "关键少数: 前 {k} 项占 80%",
"ch.pareto.none": "时间窗内无数据",
"ch.pareto.top3": "前三: ",
"ch.pareto.total": "共 {n} 项, 合计 {tot}{unit}",
"ch.pareto.xlab": "报警码 (悬停看全名)",
"ch.quad.few": "时间窗内码数不足, 不作四象限",
"ch.quad.q1": "低频长时 · 硬故障",
"ch.quad.q1s": "备件与检修窗",
"ch.quad.q2": "高频长时 · 重点治理",
"ch.quad.q2s": "优先派工",
"ch.quad.q3": "低频短时 · 本底",
"ch.quad.q3s": "不单独处置",
"ch.quad.q4": "高频短时 · 重复触发",
"ch.quad.q4s": "调整参数或屏蔽, 非硬件故障",
"ch.quad.tip": "{n} 条 · 单次中位 {med}s · 累计 {h}h · {nt} 台",
"ch.quad.xlab": "发生条数 (对数)",
"ch.quad.ylab": "单次中位时长 (对数)",
"ch.ram.area": "圆面积 = 累计停机时长",
"ch.ram.equip": "设备类",
"ch.ram.ext": "外部类",
"ch.ram.few": "部件数不足, 不作象限",
"ch.ram.median": "虚线 = 本场中位分界",
"ch.ram.q1": "常坏且难修",
"ch.ram.q1s": "最高优先级",
"ch.ram.q2": "罕发难修",
"ch.ram.q2s": "备件与作业窗",
"ch.ram.q3": "频发易修",
"ch.ram.q3s": "查根因, 换件无用",
"ch.ram.q4": "少发好修",
"ch.ram.q4s": "维持",
"ch.ram.tip": "{p}: 平均无故障间隔(MTBF){mtbf}h · 单次停机时长(MDT){mdt}h · 停机 {h}h · {n}起 · {u}台",
"ch.ram.tip_ext": " · 外部类不计设备可靠性",
"ch.ram.xlab": "平均无故障间隔(MTBF,h,对数)→ 越右越可靠",
"ch.ram.ylab": "单次停机时长(MDT,h)→ 越上越难修",
"ch.rate.days": "仅{d}天",
"ch.rate.month": "{m}月",
"ch.rate.n": "{n}条",
"ch.rate.tip": "{m}: {v} 条/日 (总 {n} 条, 数据覆盖 {cov}%)",
"ch.rate.ylab": "条/日",
"ch.ruler.norm": "正态期望 3.7σ",
"ch.ruler.row": "{r}σ σ = {s}",
"ch.wf.act": "实际上网",
"ch.wf.theo": "理论可发",
"ch.wf.tip": "{nm}: 损失 {loss} MWh, 占理论 {pct}%, 时长 {h}h",
"chain.gb": "齿轮箱",
"chain.gde": "发电机DE",
"chain.gen": "发电机",
"chain.gnde": "发电机NDE",
"chain.hss": "高速",
"chain.ims": "中间",
"chain.mba": "主轴承后",
"chain.mbf": "主轴承前",
"chain.ps": "行星",
"cmp.alarm_n": "报警 {a}",
"cmp.alarm_units": "报警机组",
"cmp.basis": "依据",
"cmp.by_system": "按系统查看",
"cmp.cards_note": "点系统卡 → 该系统全场机组与依据; 九系统 = 交付九分册一一对应; 叶片与叶根 / 塔架与基础无 SCADA 轴 (分册蓝本取 CMS / 巡检)",
"cmp.counts": "报警 {a} · 不可判 {u} · 良好 {g} · 优秀 {e}",
"cmp.cross_eyebrow": "多系统交叉机组",
"cmp.detail": "详情",
"cmp.detail_head": "{sys} · 全场 {n} 台",
"cmp.external": "外部",
"cmp.fleet_n": " · 全场 {n} 台",
"cmp.fusion_moved": "振动线结论与四源融合已移至独立板块 →",
"cmp.fusion_n": "({n} 台次收录)",
"cmp.good_units": "良好 {n} 台 (轻微偏离, 周期观察 — 点开看逐台依据)",
"cmp.grading_pending": "正在按所选时间窗重算判级…",
"cmp.grading_pending_why": "时间窗 {span}:变桨/偏航/蓄能/温度四个判级面正按该时间窗重算(首次约 25 秒),下面的系统卡暂时还是上一份统计方法 —— 算完会自动刷新,不会静默混用。",
"cmp.hit_sys": "命中系统",
"cmp.n_sys": "系统数",
"cmp.n_units": "({n} 台)",
"cmp.none": "本系统全场无非常态。",
"cmp.problem_page": "该问题页 →",
"cmp.rel_ev": "事件",
"cmp.rel_eyebrow": "部件可靠性 · 时间窗 {win}",
"cmp.rel_fm": "典型失效模式 (按时长)",
"cmp.rel_h": "停机时长",
"cmp.rel_head": "部件停机贡献与可靠性",
"cmp.rel_mdt": "单次停机时长(MDT,h)",
"cmp.rel_meta": "总停机 {t}h = 设备类 {a} + 外部类 {b}",
"cmp.rel_mtbo": "平均停机间隔(MTBO,h)",
"cmp.rel_note": '统计方法 ≥1h 停机段, 不含调度令 — 与"电量算账"里的停机时长{h} 不同, 差额是短于 1h 的停机段 (本页按段做可靠性指标须成段, 能量账按逐条工况态算电量): 两个数都对, 不可互相相减。',
"cmp.rel_part": "部件",
"cmp.rel_units": "影响机组",
"cmp.unk_n": " · 不可判 {u}",
"cmp.unk_units": "不可判 (监测/数据不足, ≠ 正常)",
"cmp.watch_head": "关注机组 (≥2 系统交叉, {n} 台; 点机组号 → 该台独立页)",
"dec.classic": "维护标准 / 维护框架 / 数据质量三块尚未迁入新版 —",
"dec.head": "检修决策 · 本体问答 (推理无界, 结论须过校验)",
"en.balance": "电量平衡",
"en.blame": "损失责任分账",
"en.blame_note": '最大一笔是限电不是故障。进设备考核的只有"故障与检修停机" {n} MWh; 限电属交易与调度侧, 2026 起交易策略主导, 少发未必是弃风。',
"en.cap_fail": "不通过",
"en.cap_note": "能量守恒核 · 理论 {op} 全额定上限 {cap}",
"en.cap_pass": "通过",
"en.kpi_act": "实际上网 · 等效满发 {h} h",
"en.kpi_loss": "损失率 · {v} MWh",
"en.kpi_theo": "理论可发 (实际+损失)",
"en.loss_mwh": "损失 MWh",
"en.share": "时长占比",
"en.state": "工况态",
"en.states": "工况态明细",
"en.states_note": "控制参数一致性与曲线欠发筛查在固定干净窗判, 已迁入「发电性能」页 — 本页只出当期电量账。",
"en.win": "窗 {win}",
"fig.fleet_median": "全场中位",
"fig.point": "测点",
"fig.quartile": "四分位",
"fig.this_unit": "本台",
"fig.trend": "趋势",
"fig.vibbar_note": "条为本台与全场中位之比; 色按 z (稳健离群度): 绿 <2 · 橙 2~3 · 红 ≥3",
"fig.x_median": "×中位",
"flt.alarm_face": "报警面",
"flt.alarm_meta": "{n} 条 · {c} 个码 · 时间窗 {win}",
"flt.attrib_note": '归因覆盖: "无记录停机" {h}h 是全场最大单项, 因不给 MTBF 而在上图中不出现 — 它不是"没有故障", 是"没归上因"。统计方法为段起点 ±30min 首发码。缺口成因待定: 此前写的"98% 的段在前 72h 内有报警 ⇒ 长停远窗"不成立 — 前 72h 有报警不等于该报警与本段停机有因果 (双模审 2026-08-28 逮)。要定成因须改判"段起止是否落在某条报警的持续区间内", 未做之前只报缺口不报原因。',
"flt.block_err": "本块未算出 ({k}): {e}",
"flt.eyebrow": "故障统计与可靠性",
"flt.pareto_d": "帕累托 · 按持续时长",
"flt.pareto_d_note": "时长 = 闩锁统计方法 (复位 − 激活), 只作排序参考不作停机时长",
"flt.pareto_n": "帕累托 · 按发生条数",
"flt.pareto_n_note": "条数 = 发生频次, 不反映影响程度 — 高频短时报警码 (信号抖动或重复触发) 排序靠前",
"flt.per_t": "台级分布 · 报警条数 top{n}",
"flt.per_t_note": "全场合计 {n} 条 · 单台占比高须先核查是否为单一报警码重复触发",
"flt.per_t_share": "占全场",
"flt.quad": "故障模式四象限 · 条数 × 单次时长",
"flt.quad_note": "时间窗内共 {n} 个码, 图上为条数前 {m} 个。",
"flt.quad_note2": "点面积表示影响机组数, 大点为批次共性、小点为单台; 竖分界取条数中位, 横分界取 10min (一个 SCADA 采样格)。右下角的码不要按条数排优先级 — 属重复触发, 非硬件故障。上面两张帕累托对照看即此图的两条投影。",
"flt.quad_partial": "窗含不满月 {m} (覆盖 {c}%), 条数为原始累计未按天归一。",
"flt.ram": "可靠性 × 可维修性 · 部件象限",
"flt.ram_note": '按停机时长排序的表看不出这层结构: 同样 500h 的停机, "频发易修"要查根因、"罕发难修"要备件, 处置相反。设备类停机 {a}h / 外部类 {b}h (外部 = 电网·风况·运维操作, 不计设备可靠性)。海上机组 MDT 普遍偏高是交通窗口决定的, 不等于维修本身慢 — 跨场比 MDT 须同为海上。',
"flt.rate": "月度故障率",
"flt.rate_example": "实测: {m} 总数最低但日均 {v} 条/日 = 全时间窗前列。",
"flt.rate_full": "各月覆盖完整。",
"flt.rate_note": "柱高 = 日均条数, 不是月总数",
"flt.rate_partial": "本时间窗 {m} 数据不满月 (灰虚线柱), 直接比月总数会读反。",
"flt.seasonal": "故障季节性 · top 报警族 × 月",
"flt.seasonal_note": "全程 19 个月 (跨 2 冬 1 夏); 未做 χ² 显著性检验前为【参考级】, 只看形态不作季节归因",
"flt.seasonal_unit": "条/月",
"flt.seasonal_x": "月",
"flt.stop_face": "停机面",
"flt.stop_h": "停机时长",
"flt.stop_meta": "{h}h · {n} 起 · 统计方法 ≥1h 段, 不含调度令",
"flt.stop_n": "次数",
"flt.stop_pareto": "停机归因帕累托",
"flt.stop_pareto_note": "按停机时长; 事件次数见悬停",
"flt.stop_units": "台级停机 top{n}",
"flt.stop_units_note": "停机时长高 ≠ 设备差 — 须先看归因里外部类占比 (调度令已剔除)",
"fus.alarm_n": "{n} 条",
"fus.alarm_rank": " · 第 {r}/{t}",
"fus.burst": "⚠ {p}% 是码 {c} 抖动",
"fus.burst_tip": "⚠ 单码 {c} 占 {p}%, 中位 {m} 秒/次 — 条数被抖动灌满",
"fus.clear": "× 清除",
"fus.cms_act": "行动",
"fus.cms_action": "需行动机组 (报警 / 不可判)",
"fus.cms_basis": "统计方法说明",
"fus.cms_basis_note": "本表 = windcms 状态管理判级 (含标量趋势与数据可判性); 融合矩阵 / 逐台结论 = 振动线确诊链转录 (候选以上)。两统计方法差异台: 仅本表列 {a} (多为标量发作 / 数据不可判, 未到确诊链); 仅确诊链列 {b} (预警级在察, 状态管理未升)。",
"fus.cms_chain": "部件状态链",
"fus.cms_chain_note": "(主承前 · 主承后 · 齿轮箱 · 发电机; 悬停看逐件等级)",
"fus.cms_evidence": "证据强度",
"fus.cms_focus": "状态焦点",
"fus.cms_focus_note": "(传动链: 主承前 · 主承后 · 齿轮箱 · 发电机两端)",
"fus.cms_grades": "38 台部件等级一览",
"fus.cms_head": "振动状态评估 (windcms 报告 {d} 结果转录)",
"fus.cms_link": "谱图 · 波形 · 判据 · 工作台等分析功能在独立 windcms 系统 →",
"fus.cms_loading": "载入振动评估结果…",
"fus.cms_note": "状态管理判级, 不代表确诊数量; 判据/谱线/计算过程不在本页 — 深潜走底部独立系统。",
"fus.cms_open": "打开 windcms ↗",
"fus.cms_overall": "综合",
"fus.cms_static": "(未启动时用静态版)",
"fus.cms_window": '数据窗 {r} —— 本时间窗**实算**覆盖区间 (窗名只是起始日代号); 页面上的图谱与"末窗"状态取自时间窗内最新记录, 故多显示末期日期。',
"fus.col.alarm": "报警",
"fus.col.claim": "证据时间窗",
"fus.col.claim_axis": "26-01 → 09",
"fus.col.display_only": " · 只展示",
"fus.col.fixed": " · 不随本时间窗",
"fus.col.lub": "润滑柱塞",
"fus.col.oil": "油样",
"fus.col.part": "部件",
"fus.col.signal": "振动信号",
"fus.col.state_concl": "设备状态 / 振动结论",
"fus.col.temp": "温度",
"fus.col.tempaxis": "温度轴",
"fus.col.tempaxis_note": "±6 K",
"fus.col.vib": "振动",
"fus.col.wo": "工单",
"fus.ev_code": "码",
"fus.ev_dur": "累计时长 h",
"fus.ev_head": "{c} — 报警 {an} 条 / {au} 台 · 工单 {wn} 条 / {wu} 台 (时间窗内)",
"fus.ev_min": "时长 min",
"fus.ev_no_alarm": "时间窗内无该部件报警",
"fus.ev_no_wo": "时间窗内无该部件工单 (工单数据止 2024-11, 2026 类窗必空 = 数据边界)",
"fus.ev_recent": "最近 {n} 条明细",
"fus.ev_text": "报警内容",
"fus.ev_time": "时间",
"fus.ev_top": "台分布 top: ",
"fus.ev_units": "台数",
"fus.ev_win": "时间窗 {win} · 顶部选择器可换",
"fus.ev_wo_act": "处理",
"fus.ev_wo_cause": "原因",
"fus.ev_wo_name": "故障名称",
"fus.eval_head": "评价体系 (规范化定义 — 两轴并存, 不可互相替代)",
"fus.eval_meaning": "含义",
"fus.eval_note": '设备状态答"这台设备现在怎么样" (危险 > 报警 > 良好 > 优秀 > 不可判); 证据状态答"这个判断有多可信" (定论 → INSUFFICIENT)。设备状态一律取自振动线的判级栏这一个来源。本页判级为转录, 不产新判级; 标注为参照同型机组/频谱信号级 (未定位到具体零件)/测量链/未对应到具体部件的行不作部件级结论消费; claim_window = 该结论的证据时间窗, 窗外状态该结论不背书。',
"fus.eval_state": "设备状态 (业主统计方法)",
"fus.eval_tier": "证据状态 (可信度)",
"fus.eyebrow": "振动与 SCADA 证据视图 · 展示振动结论、SCADA 指标、报警与工单线索",
"fus.fam_tip": "{n} 条 · 全场最多 {m} 条",
"fus.legend_note": '两轴不可互相替代: "候选"说的是可信度不是设备好坏; "良好"说的是设备状态不是证据强度。设备状态只取自振动线的判级栏这一个来源。',
"fus.legend_state": "格子颜色 = 设备状态 (业主统计方法, 与 windcms 报告同一套):",
"fus.legend_tier": "徽章 = 证据状态 (可信度, windcms 九枚举):",
"fus.loop_ba": "换前 → 换后 (同标尺)",
"fus.loop_bound": "数据边界: {b}",
"fus.loop_closed": "已闭环 · 换件后恢复 ({n} 例)",
"fus.loop_date": "换件日",
"fus.loop_el": "早期 → 近期 (同标尺)",
"fus.loop_head": "维修后验证案例 (检出 → 换件 → 恢复)",
"fus.loop_item": "事项",
"fus.loop_neg": "负面对照: 工单后持平 {n} 例 (换件 ≠ 必然回落, 说明回落非巧合)。",
"fus.loop_none": "暂无端到端闭环证据:现场提交的振动数据包里没有「报警→检修→复测」的成对记录",
"fus.loop_note": "有换件记录且同一统计方法指标回落, 可作为检测链已闭合的案例; 指标仍在上升但缺少可匹配工单的机组, 需现场或台账确认后再关闭。",
"fus.loop_ratio": "恢复比",
"fus.loop_rising": "未闭环 · 指标上升待现场/工单确认 ({n} 台)",
"fus.loop_state": "状态",
"fus.loop_verdict": "判",
"fus.loop_x": "倍比",
"fus.lub_now": "润滑柱塞现状",
"fus.lv.bad": "定论/报警级",
"fus.lv.note": "候选/监视/观察级",
"fus.lv.ok": "销案/无新证",
"fus.lv.warn": "预警级",
"fus.m.bad": "异常严重, 建议尽快停机检查",
"fus.m.bad_e": "证据: 已定案",
"fus.m.ex": "未见异常, 保持常规监视",
"fus.m.ex_e": "证据: 仅记录 / 已取消",
"fus.m.ok": "轻微偏离, 记录观察",
"fus.m.ok_e": "证据: 待现场核实 / 已记为基线下窗观察",
"fus.m.unk": "数据不足或监测中断 — 不等于正常",
"fus.m.unk_e": "证据: 数据不足",
"fus.m.warn": "存在需现场核实或处置的异常",
"fus.m.warn_e": "证据: 基本确认 / 新出现待核实",
"fus.matrix_foot": '润滑柱塞各台设定本就不同 (全场单次循环 22~961 秒), 所以此列只展示不判级, 只有已定案的机制链才标色; 油液多数台末次取样距今超 12 个月, 此列看的是"样够不够新", 不代表当前状态。',
"fus.matrix_head": "全场融合矩阵 (38 台 × 6 个证据面)",
"fus.matrix_note1": '各列取数窗口不同 (见列头) — 不同窗的两格同时发红不等于"同时发生", 不可当共时印证。',
"fus.matrix_note2": '每格右侧曲线 = 逐月记忆轨迹 ({n} 个月, {m0} 起; 实心点 = 现窗) — 现窗快照会丢形态, 轨迹才看得见"塌→回升→翻转"类机制签名。',
"fus.no_alarm": "时间窗内无报警",
"fus.none_word": "无",
"fus.oil_fresh": "近共时",
"fus.oil_none": "无样",
"fus.oil_stale": "stale > 12 个月",
"fus.rule1": "① 同一失效机制的多个观测面 ≠ 多源印证 — 融合禁按源数加权 (29# 柱塞/温度/振动稳态/振动瞬态 = 四个观测面一条机制链)。",
"fus.rule2": '② "没报警" ≠ "正常" — 在已知盲区上, 缺报警只说明监测面无效, 不说明设备无恙。',
"fus.rules_head": "两条判读纪律 (随板块常显)",
"fus.short.nodata": "数据不足",
"fus.short.normal": "正常",
"fus.short.nosample": "未取样",
"fus.short.stale": "样龄过期",
"fus.short.uneval": "未评估",
"fus.short.unlisted": "未收录",
"fus.sub.cms": "振动分析",
"fus.sub.events": "报警工单",
"fus.sub.loop": "端到端闭环",
"fus.sub.matrix": "融合矩阵",
"fus.sub.units": "逐台情况",
"fus.temp_now": "温度轴现状",
"fus.tip_state": " — 设备状态 {s}",
"fus.tip_tier": " / 证据状态 {e}",
"fus.unavailable": "融合面数据不可用: {e}",
"fus.units_filter": " · 筛选: {l}",
"fus.units_head": '逐台情况 (全场 {u} 台 / {n} 台次{f}; 深入谱图/波形 → "振动分析")',
"fus.unlisted_head": "未列机组 {n} 台 (振动线未收录 — 未列 ≠ 正常)",
"fus.unlisted_link": "未列 {n} 台",
"fus.win_n": "{n} 个窗段",
"fus.win_none": "未标窗",
"fus.wo_n": "{n} 单",
"fus.wo_none": "时间窗内无传动链工单",
"gen.beta": "桨距调度同档差",
"gen.beta_note": "同一功率档下的桨角与全场中位之差。{units} 偏离达中位的 {k} 倍 → {v}",
"gen.cands": "曲线欠发筛查",
"gen.cands_meta": "{note} · σ = {s}%",
"gen.cap": "封顶参数落位",
"gen.cap_note": "台数落在几个离散位置上, 不是连成一片 — 这是配置/版本差异的形状; 磨损或工况引起的会是连续散布。两轴各自成组且分组不同台 ⇒ 参数级差异, 成因须配置台账。",
"gen.ctrl": "控制参数一致性",
"gen.ctrl_win": "窗 {win}",
"gen.eyebrow": "特性曲线与控制参数",
"gen.figs": "七个镜头 · 逐档中位与同型机群残差",
"gen.fixed_win": "随上方时间窗 · 按时间窗重算",
"gen.loading": "正在计算七个镜头 (剥限电 · 逐档中位 · 同型机群残差)…",
"gen.no_cands": "无候选",
"gen.recomputing": "正在按所选时间窗重算七镜头…",
"gen.recomputing_short": "正在按窗重算…",
"gen.ruler": "尺子巡检 · 极差 ÷ σ",
"gen.ruler_note": "判据自身的分辨力。38 台正态抽样的极差期望约 3.7σ, 明显超出即分布非单峰 — 与左图的分组互为印证; 落在期望附近的轴 (额定桨角) 则是连续分布。",
"gen.win_range": "时间窗 {span}",
"gen.win_why": '七镜头与月度时序都按所选时间窗重算(含自定义起止日期)。提示:2025-07 ~ 2025-12 是"限电前干净判别窗"——2026 起交易策略主导,曲线含策略性少发,作设备判据时须注意;选到含 2026 的窗时该影响存在但不再遮掩。',
"kpi.alarm_units": "九系统有报警的台数 / 全场",
"kpi.alarm_units.hint": "统计方法: 九系统 (叶片与叶根/变桨/偏航/齿轮箱/发电机/主轴承/变流器/塔架与基础/主控与传感网) 中任一被判「报警」即计一台。与下方「全场状态」格的统计方法不同 —— 那里按每台 振动/温度/润滑/油液/报警/工单 六列取最严一级, 所以两处数字可以不同, 不是矛盾。",
"kpi.avail": "时间可用率 (不含调度令) · {win}",
"kpi.curtail": "限电·绑定 电量损失",
"kpi.downtime": "停机时长 · {n} 起",
"kpi.loss": "停机电量损失 · {win}",
"kpi.loss_dispatch": "另调度令 {n}",
"kpi.mtbo": "平均停机间隔(MTBO;非行业 MTBF)",
"loss.curtail_bound": "限电 (命令与实际功率对得上)",
"loss.curtail_unbound": "限电命令期 · 功率未被压制",
"loss.dispatch": "调度令停机",
"loss.lowwind": "低风待机",
"loss.normal": "正常发电时的欠发",
"loss.stop": "故障与检修停机",
"mdl.cloud": "云端",
"mdl.deepseek-chat.nm": "DeepSeek",
"mdl.deepseek-chat.t": "约 3 到 10 秒",
"mdl.deepseek-r1:14b.nm": "本机 DeepSeek 14B · 审核档",
"mdl.deepseek-r1:14b.t": "约 1 到 3 分钟",
"mdl.deepseek-r1:32b.nm": "本机 DeepSeek 32B",
"mdl.deepseek-r1:32b.t": "约 3 到 8 分钟",
"mdl.deepseek-v4-pro.nm": "云端 DS",
"mdl.deepseek-v4-pro.t": "约 2 到 6 分钟",
"mdl.kimi-fast.nm": "Kimi",
"mdl.kimi-fast.t": "约 5 到 20 秒",
"mdl.local": "本地",
"mdl.outbound_warn": "内容会外发到模型厂商, 场名与台号已去标识化",
"mdl.qwen-plus.nm": "通义千问",
"mdl.qwen-plus.t": "约 5 到 20 秒",
"mdl.qwen3.8:27b.nm": "标准 27B · 复杂题",
"mdl.qwen3.8:27b.t": "约 2 到 4 分钟",
"mdl.qwen3:30b-a3b.nm": "快 30B (a3b)",
"mdl.qwen3:30b-a3b.t": "约 20 到 60 秒",
"mdl.qwen3:8b.nm": "轻量 8B · 默认",
"mdl.qwen3:8b.t": "约 30 到 60 秒",
"mdl.unknown_warn": "未知档位, 按内容会外发处理",
"mdl.zero_outbound": "全程零外发",
"mt.cov": "当前覆盖",
"mt.eyebrow": "系统维护 · 数据输入与更新端口",
"mt.fail": "盘点失败: {e}",
"mt.freq": "频率",
"mt.head": "数据维护面 · {n} 个源",
"mt.how": "补数据怎么做",
"mt.last": "末次更新",
"mt.layer_data": "数据层",
"mt.layer_data_note": "随运营滚动 — 新数据到位后跑摄入, 本体与判级自动刷新",
"mt.layer_mech": "机理层",
"mt.layer_mech_note": "相对稳定 — 换厂商资料或现场反馈修正时才动; 它是判据的地基, 变更须过审",
"mt.note": '每个源给出: 当前覆盖 / 末次更新 / 约定位置 / 摄入命令 / 更新频率 / 责任方。要补数据就把新文件放到"约定位置"再跑"摄入命令"。',
"mt.put": "放到:",
"mt.resp": "责任",
"mt.rows_n": " ({n} 条)",
"mt.run": "跑:",
"mt.src": "源",
"mt.surveying": "盘点中…",
"ov.all_steps_done": "已走完(待验收)",
"ov.attention": "需要关注",
"ov.attention_n": "{n} 台",
"ov.basis_fusion": "本台依据来自融合面(九系统判级无「报警」行):部件 {part} · 链路走到第 {done}/{n} 步 · 卡在「{stuck}」 · 证据源 {srcs}",
"ov.caveat": "限定",
"ov.evidence": "查看完整依据",
"ov.fus_gap_hint": "这一面(需要关注 / 全场状态)取数来自融合面;缺件时如实为空,不从交付包补齐。在本机放好原始件后跑一次重算即会由观澜自己的生成端补上(scripts/rudong_fusion_handoff.py,见 docs 的「源代码化清单」)。",
"ov.illustrative": "示意数据",
"ov.no_srcs": "无(该台无外部证据源)",
"ov.quiet": "其余 {n} 台未进入优先关注清单;设备状态请查看全场状态评估。",
"ov.quiet_all": "当前优先关注清单为空;{n} 台机组的设备状态请查看全场状态评估。",
"ov.ranked_by": "按 设备状态 × 证据量 排序",
"ov.strip": "全场状态",
"ov.strip_hint": "一格一台 · 点格定位",
"ov.systems": "九系统",
"ov.systems_hint": "点系统看逐台依据",
"pop.acts": "动作统计",
"pop.close": "关闭",
"pop.loading": "载入中…",
"pop.none": "本时间窗无记录",
"pop.o_act": "处置",
"pop.o_cause": "原因",
"pop.o_check": "排查",
"pop.o_date": "日期",
"pop.o_fault": "故障",
"pop.o_kind": "类别",
"pop.orders": "工单 {n} 单",
"pop.orders_none": "时间窗内无工单",
"pop.orders_one": "工单 1 单",
"pop.problem": "{u} · {sys}",
"pop.unit": "{u} · 全部问题",
"rpt.attrib.note": '"无记录停机"不是没有故障, 是没归上因。',
"rpt.attrib.title": "归因覆盖",
"rpt.aud_in": "场站内部 (全量)",
"rpt.aud_up": "上报集团 (脱敏)",
"rpt.b.attrib.d": "已归因 vs 无记录停机的占比 — 无记录不等于没有故障",
"rpt.b.attrib.nm": "归因覆盖",
"rpt.b.blame.d": '交易调度侧 / 设备侧 / 运行侧 三段占比 — 回答"谁的责任"',
"rpt.b.blame.nm": "损失责任分账",
"rpt.b.caliber.d": "统计期 / 残月 / 工单时间窗 / 判级性质 — 建议常驻, 没有边界的数字堆不能作汇报依据",
"rpt.b.caliber.nm": "统计方法与适用边界",
"rpt.b.chain.d": "证据→机制→判级→排期→动作→验收 六步的瓶颈分布",
"rpt.b.chain.nm": "决策链进度",
"rpt.b.emon.d": "逐月上网 / 损失 / 损失率 / 等效满发, 含数据覆盖率",
"rpt.b.emon.nm": "逐月发电表",
"rpt.b.frate.d": "按天归一的日均条数, 数据不满月单独标注",
"rpt.b.frate.nm": "月度故障率",
"rpt.b.kpi.d": "六个核心数: 上网电量 / 等效满发 / 可用率 / 损失率 / 停机间隔 / 需行动台",
"rpt.b.kpi.nm": "一级指标",
"rpt.b.loop.d": "检出 → 换件 → 复测回落的历史例证, 证明这条链是通的",
"rpt.b.loop.nm": "闭环实证",
"rpt.b.narr.d": "模型基于实测数据生成的运行总结 — 为图表补充文字结论, 供汇报使用",
"rpt.b.narr.nm": "运行总结",
"rpt.b.pareto.d": "按条数降序 + 累计 80% 线, 找关键少数",
"rpt.b.pareto.nm": "故障帕累托",
"rpt.b.parts.d": "逐部件停机时长 / 事件数 / 平均无故障间隔(MTBF)/ 单次停机时长(MDT)/ 影响机组数",
"rpt.b.parts.nm": "部件停机表",
"rpt.b.plan.d": "由数据推出的行动项, 不是套话",
"rpt.b.plan.nm": "下期建议",
"rpt.b.quad.d": "条数 × 单次时长, 区分重复触发与实际停机 — 按条数排序会误定优先级",
"rpt.b.quad.nm": "故障四象限",
"rpt.b.ram.d": '平均无故障间隔(MTBF)× 单次停机时长(MDT)部件象限, 分出"频发易修"与"罕发难修", 处置方向相反',
"rpt.b.ram.nm": "可靠性象限",
"rpt.b.units.d": "需跟踪台的部件 / 证据源 / 六步进度 / 卡在哪一步",
"rpt.b.units.nm": "重点机组",
"rpt.b.water.d": "理论可发 → 逐段扣损 → 实际上网, 用于定位电量流向",
"rpt.b.water.nm": "能量瀑布",
"rpt.blame.cal": "工况态分解 + 限电命令面绑定判别; 平均出力=实发÷台时, 用作风资源代理量",
"rpt.blame.note": '进设备考核的只有"故障与检修停机"; 限电属交易与调度侧。',
"rpt.blame.note2": '限电不是"低风时段收到的空令": 限电·绑定时段平均出力 1.435 MW, 正常发电时段 1.412 MW, 比值 1.02 — 风况相同而出力被压, 说明限电是真压制; 但"限电命令期·功率未被压制"那一档平均出力仅 0.724 MW (18% 额定), 确属低风时段收令。',
"rpt.blame.title": "损失责任分账",
"rpt.cal.deid": "脱敏",
"rpt.cal.deid_d": "上报版隐去判据门槛与特征频率",
"rpt.cal.denom": "可用率与损失率分母不同",
"rpt.cal.denom_d": '可用率分母剔除低风待机与调度令(问"该发时能不能发"), 损失率分母含全部时段(问"理论能发多少"); 同一低风时段在可用率里不计时间、在损失率里计入基数 — 这是两个指标的设计差异不是错, 但两者不可直接相乘或互推。低风待机对损失的贡献仅 118 MWh(0.5%)',
"rpt.cal.partial": " — {m} 数据不满月(覆盖 {c}%), 月度已按天归一",
"rpt.cal.period": "统计期",
"rpt.cal.rating": "判级",
"rpt.cal.rating_d": "由振动/温度/润滑/油液四源融合得出; 行动等级由综合报告出具",
"rpt.cal.stop": "停机",
"rpt.cal.stop_d": "可靠性统计方法 ≥1h 段, 电量账为逐条工况态 — 两个数都对, 不可相减",
"rpt.cal.title": "统计方法与数据边界",
"rpt.cal.wo": "工单",
"rpt.cal.wo_d": "台账 2020~2024-11, 本报告不对本期检修执行下结论",
"rpt.caliber_lbl": "统计方法: ",
"rpt.chain.cal": "「动作」一列不可判 — {b}",
"rpt.chain.note": "六步: 证据 → 机制 → 判级 → 排期 → 动作 → 验收。当前 {s}。",
"rpt.chain.title": "决策链进度",
"rpt.chapters": "{n} 个章节",
"rpt.cloud_note": "; 内容会外发到模型厂商",
"rpt.compose": "生成",
"rpt.compose_fail": "生成失败: {e}",
"rpt.composed": "已按需求编排 {n} 块{narr} · {m} 用时 {s}s",
"rpt.composing": "模型编排中… 挑组件 + 写总结",
"rpt.emon.act": "上网 MWh",
"rpt.emon.cal": "覆盖率<50% 的月不参与环比",
"rpt.emon.cov": "覆盖",
"rpt.emon.eflh": "等效满发 h",
"rpt.emon.loss": "损失 MWh",
"rpt.emon.m": "月",
"rpt.emon.partial": " 不满月",
"rpt.emon.pct": "损失率",
"rpt.emon.title": "逐月发电",
"rpt.empty_canvas": "画布是空的 — 先从组件库选内容",
"rpt.export_docx": "导出 Word",
"rpt.export_xlsx": "导出 Excel",
"rpt.eyebrow": "汇报定制 · 画布",
"rpt.facts.agg": "聚合层",
"rpt.facts.sub": "{n} 条, 契约 {sha}; 数据/时间窗/结论只经契约更新",
"rpt.facts.sys": "系统",
"rpt.facts.text": "结论",
"rpt.facts.title": "可交付级结论 · 契约生成",
"rpt.facts.verdict": "裁决",
"rpt.foot_asof": "出件 {d}",
"rpt.foot_deid": "判据门槛与特征频率已脱敏",
"rpt.foot_src": "数据源 SCADA 10min · 报警事件 · 工单台账 · CMS 振动 · 油液化验",
"rpt.foot_trace": "结论可回溯至本体对象",
"rpt.frate.cal": "不满月按天归一",
"rpt.frate.note": "柱高=日均条数, 不是月总数。",
"rpt.frate.title": "月度故障率",
"rpt.g.end": "收尾",
"rpt.g.flt": "故障",
"rpt.g.gen": "发电",
"rpt.g.rel": "可靠性",
"rpt.g.unit": "机组",
"rpt.grounded": "数字全部接地",
"rpt.hint_idle": "悬停查看组件说明 · 已选组件再次点击移出 · 右侧 ↑↓ 调整顺序",
"rpt.kpi.act": "上网电量",
"rpt.kpi.action": "需行动机组",
"rpt.kpi.action_n": "定论 {a} · 预警 {b} · 候选 {c}",
"rpt.kpi.annual": "年化约 {v} h",
"rpt.kpi.avail": "时间可用率",
"rpt.kpi.avail_n": "不含调度令与低风待机",
"rpt.kpi.eflh": "等效满发小时",
"rpt.kpi.loss": "损失率",
"rpt.kpi.mom": "环比 {v}%",
"rpt.kpi.mom_na": "环比不可比(末月不满)",
"rpt.kpi.mtbo": "平均停机间隔",
"rpt.kpi.mtbo_n": "停机 {n} 起 · 单次均 {h}h",
"rpt.kpi.theo": "理论可发 {v}",
"rpt.library": "组件库",
"rpt.library_note": "点击加入报告, 再次点击移出 · 画布里已有 {n} 块",
"rpt.local_note": "; 数据不出本地",
"rpt.loop.cal": "振动标量前后对比",
"rpt.loop.comp": "部件",
"rpt.loop.date": "换件日期",
"rpt.loop.note": "这几台正是算法当初的检出台 ⇒ 故障前能看见、换件后能看见恢复。",
"rpt.loop.post": "换后",
"rpt.loop.pre": "换前",
"rpt.loop.ratio": "比值",
"rpt.loop.title": "端到端闭环实证",
"rpt.narr.cal": "模型 {m} 生成 · 数字取自系统实测摘要 · 数据回抓校验{g}",
"rpt.narr.empty": "还没有生成 — 在上面的交互窗输入报告要求。",
"rpt.narr.g_n": "报 {n} 处待核",
"rpt.narr.g_ok": "全通过",
"rpt.narr.title": "运行总结",
"rpt.narr.ungr": "⚠ 以下数值由模型自行推算, 不在原始数据摘要中, 引用前须核对: {v}",
"rpt.narr_plus": " + 一段运行总结",
"rpt.need_empty": "先输入报告要求",
"rpt.need_head": "输入报告要求",
"rpt.need_hint": "模型依据需求选取组件, 并基于实测数据生成运行总结",
"rpt.need_ph": "例如: 上报集团月报, 侧重设备可靠性与重点机组",
"rpt.no_compose": "本部署未启用编排接口, 画布仍可手动拼装",
"rpt.no_models": "本部署未配置任何模型密钥, 编排不可用",
"rpt.paper_sub": "统计期 {win} | {n} 台",
"rpt.paper_title": "运行分析报告",
"rpt.pareto.cal": "报警事件表",
"rpt.pareto.title": "故障帕累托",
"rpt.parts.cal": "归因=段起点±30min 首发码",
"rpt.parts.ev": "事件",
"rpt.parts.ext": " (外部)",
"rpt.parts.h": "停机h",
"rpt.parts.mdt": "单次停机时长(MDT,h)",
"rpt.parts.mtbf": "平均无故障间隔(MTBF,h)",
"rpt.parts.part": "部件",
"rpt.parts.title": "部件停机",
"rpt.parts.units": "影响机组",
"rpt.plan.1": '补机制定性 — {n} 台停在"判了级但根因未定", 是当前最大单一瓶颈',
"rpt.plan.2": "回收 25~26 工单 — 台账止 2024-11, 本期检修执行与闭环无法自动判定",
"rpt.plan.3": "油液采样节奏 — 四源里油液长期 stale, 只能作回测锚",
"rpt.plan.4": "限电侧 — 损失六成在交易与调度侧, 设备侧改善空间有限",
"rpt.plan.cal": "建议级 — 排期与动作须经审批",
"rpt.plan.title": "下期计划与建议",
"rpt.preset.blank": "空白画布",
"rpt.preset.month": "月度运行分析",
"rpt.preset.topic": "设备专题",
"rpt.preset.week": "周报",
"rpt.preset.year": "年度总结",
"rpt.presets": "预设",
"rpt.preview_empty": "文档预览窗 — 从上面的组件库点选内容, 此处显示报告预览",
"rpt.print": "打印 / 存 PDF",
"rpt.quad.cal": "纵界=10min(一个SCADA采样格)",
"rpt.quad.note": "右下角报警码属重复触发, 不应按条数排定优先级。",
"rpt.quad.title": "故障模式四象限",
"rpt.ram.cal": "SCADA ≥1h 停机段, 不含调度令; 海上 MDT 含交通窗口",
"rpt.ram.note": "设备类 {a}h / 外部类 {b}h (电网·风况·运维操作不计设备可靠性)",
"rpt.ram.title": "可靠性 × 可维修性",
"rpt.ungrounded_n": "{n} 个数字待核",
"rpt.units.at": "卡在",
"rpt.units.cal": "四源融合判级",
"rpt.units.clear": "全通",
"rpt.units.ev": "部件与证据",
"rpt.units.head": "需跟踪 {n} 台 / {tot} | 瓶颈 ",
"rpt.units.prog": "进度",
"rpt.units.rising": "在升 {r}×",
"rpt.units.state": "状态",
"rpt.units.stuck": "{n} 台卡在「{s}」",
"rpt.units.title": "重点机组与处置",
"rpt.up_note": "上报版 — 判据门槛与特征频率已脱敏",
"rpt.up_tag": "上报版",
"rpt.water.cal": "SCADA 10min 工况态分解; 损失=同工况理论出力与实测之差。量级自检为防虚高的下限校验, 不是风资源判据",
"rpt.water.cap_bad": "量级自检: 理论 ≥ 全额定上限 {cap} MWh ✗ 数字不可信",
"rpt.water.cap_ok": "量级自检: 理论低于全额定上限 {cap} MWh ✔",
"rpt.water.note": "理论可发 {theo} = 上网 {act} + 损失 {loss} MWh ({pct}%)",
"rpt.water.title": "发电完成情况",
"rv.advci": "AdvCI",
"rv.dual": "双模型审",
"rv.lite": "lite",
"rv.none": "未审",
"st.bad": "危险",
"st.excellent": "优秀",
"st.ok": "良好",
"st.ok_excellent": "良好 / 优秀",
"st.unk": "不可判",
"st.warn": "报警",
"step.acceptance": "验收",
"step.action": "动作",
"step.evidence": "证据",
"step.mechanism": "机制",
"step.rating": "判级",
"step.schedule": "排期",
"step.site_records": "现场记录",
"step.stuck_at": "卡在「{step}」",
"sys.blade": "叶片与叶根",
"sys.control": "主控与传感网",
"sys.converter": "变流器",
"sys.gearbox": "齿轮箱",
"sys.generator": "发电机",
"sys.main_bearing": "主轴承",
"sys.other": "其他",
"sys.pitch": "变桨",
"sys.tower": "塔架与基础",
"sys.yaw": "偏航",
"tab.assistant": "检修助手",
"tab.component": "部件问题",
"tab.decision": "检修决策",
"tab.energy": "电量算账",
"tab.fault": "故障统计",
"tab.generation": "发电性能",
"tab.ontology": "本体",
"tab.overview": "总览",
"tab.report": "汇报定制",
"tab.system": "系统维护",
"tab.vibration": "振动融合分析",
"tier.candidate": "候选",
"tier.candidate.s": "候选",
"tier.candidate_baseline": "候选·记基线",
"tier.candidate_baseline.s": "候选·记基线",
"tier.candidate_new": "候选·新发",
"tier.candidate_new.s": "候选·新发",
"tier.confirmed": "定论",
"tier.confirmed.s": "定论",
"tier.detail.不可判": "不可判",
"tier.detail.仲裁输入": "仲裁输入",
"tier.detail.优": "优",
"tier.detail.优秀": "优秀",
"tier.detail.传感器项": "传感器项",
"tier.detail.候选": "候选",
"tier.detail.候选·监视(复发)": "候选·监视(复发)",
"tier.detail.候选·跟踪": "候选·跟踪",
"tier.detail.危险": "危险",
"tier.detail.可降": "可降",
"tier.detail.复发候选(两轴合并)": "复发候选(两轴合并)",
"tier.detail.定论": "定论",
"tier.detail.弱同向新证": "弱同向新证",
"tier.detail.报": "报",
"tier.detail.报警": "报警",
"tier.detail.本体无新证": "本体无新证",
"tier.detail.正常": "正常",
"tier.detail.正样本": "正样本",
"tier.detail.温度单轴保留": "温度单轴保留",
"tier.detail.监视": "监视",
"tier.detail.监视(不开箱)": "监视(不开箱)",
"tier.detail.良": "良",
"tier.detail.良好": "良好",
"tier.detail.蓄能失效候选(定向查)": "蓄能失效候选(定向查)",
"tier.detail.观察": "观察",
"tier.detail.观察(发电机侧)": "观察(发电机侧)",
"tier.detail.记基线观察": "记基线观察",
"tier.detail.销案(定论)": "销案(定论)",
"tier.detail.降观察": "降观察",
"tier.detail.预警": "预警",
"tier.detail.预警·处置升急": "预警·处置升急",
"tier.detail.预警·处置规划": "预警·处置规划",
"tier.insufficient": "不足",
"tier.insufficient.s": "不足",
"tier.near": "准定论·预警",
"tier.near.s": "准定论",
"tier.normal": "正常",
"tier.normal.s": "正常",
"tier.reference": "参考",
"tier.reference.s": "参考",
"tier.withdrawn": "撤回",
"tier.withdrawn.s": "撤回",
"ui.all": "全部",
"ui.back_portal": "← 返回门户",
"ui.back_to": "← 返回{tab}",
"ui.collapse": "收起",
"ui.count": "条数",
"ui.data_hidden": "({n} 处数据文本尚无英文,需复核)",
"ui.date": "日期",
"ui.empty": "无数据",
"ui.error": "出错了",
"ui.expand": "展开",
"ui.loading": "载入中…",
"ui.not_ported": "本页尚未迁入新版。",
"ui.not_recorded": "台账未填",
"ui.open": "查看 →",
"ui.open_classic": "在经典版打开 →",
"ui.query": "查询",
"ui.reselect": "重新选择机组 / 测点",
"ui.retry": "重试",
"ui.system": "系统",
"ui.unit": "机组",
"ui.units": "机组",
"ui.verbatim": "原始记录 (中文)",
"ui.verbatim_note": "{n} 处为手册/工单原文, 无英文译本, 按原文转录。",
"unit.h": "h",
"unit.n": " 条",
"unit.units": "台",
"vp.(振动线未收录)": "(振动线未收录)",
"vp.主轴承(后)+集中润滑泵": "主轴承(后)+集中润滑泵",
"vp.发电机(引用同族, 见族主台)": "发电机(无本台独立证据, 参照同型机组)",
"vp.发电机(未映射)": "发电机(未对应到具体部件)",
"vp.发电机DE": "发电机DE",
"vp.发电机DE+NDE(两端)": "发电机DE+NDE(两端)",
"vp.发电机NDE": "发电机NDE",
"vp.齿轮箱(未映射)": "齿轮箱(未对应到具体部件)",
"vp.齿轮箱(测量链问题, 非部件)": "齿轮箱(测量链问题, 非部件)",
"vp.齿轮箱(谱线级, 未映射到部件)": "齿轮箱(频谱信号级 (未定位到具体零件), 未对应到具体部件)",
"vp.齿轮箱中间轴": "齿轮箱中间轴",
"vp.齿轮箱二级行星内齿圈": "齿轮箱二级行星内齿圈",
"vp.齿轮箱高速轴": "齿轮箱高速轴",
"win.2025H2": "2025H2",
"win.2026": "2026年",
"win.2026H1": "2026H1",
"win.30d": "近30日",
"win.90d": "近90日",
"win.all": "全程",
"win.apply": "应用",
"win.bad_order": "开始日期不能晚于结束日期",
"win.banner": "时间窗 {win} · 本页全部数字随上方时间窗切换",
"win.custom": "自定义",
"win.custom_hint": "自定义起止日期(含两端)。日粒度件(判级/停机台账/曲线)按日精确;月度聚合件按所跨月份取整,图上会写明。",
"win.custom_sel": "自定义 {a} ~ {b}",
"win.from": "开始日期(含)",
"win.label": "时间窗",
"win.months": "按月",
"win.need_both": "请把开始与结束日期都选上",
"win.partial": "时间窗内 {m} 数据不满月 (覆盖 {c}%), 月度图已按天归一",
"win.presets": "预设时间窗",
"win.through": "至{date}",
"win.to": "结束日期(含)",
"tab.documents": "交付文档",
"tab.recalc": "数据重算",
"doc.eyebrow": "交付文档",
"doc.loading": "正在读取交付文档…",
"doc.head": "交付文档 ({n})",
"doc.note": "交付文档随包提供;下载后可离线查阅。",
"doc.name": "文档",
"doc.size": "大小",
"doc.get": "获取",
"doc.download": "下载",
"rc.eyebrow": "数据重算",
"rc.loading": "正在读取重算状态…",
"rc.running": "重算进行中",
"rc.idle": "当前空闲",
"rc.note": "重算链按步执行;本页每 5 秒刷新状态与日志尾部。",
"rc.since": "开始于",
"rc.start": "开始重算",
"rc.log": "重算日志(尾部)",
"rc.empty": "(暂无日志)",
"tab.portal": "门户栏目",
"pt.eyebrow": "门户栏目",
"pt.head": "重构前门户上的栏目(已迁移 / 可达)",
"pt.note": "总览、振动·CMS、交付文档、数据重算已迁为页签;系统架构/方法/经验发现/案例·液压 见「架构与资料」页签;下列两项为在线服务入口。",
"pt.col": "栏目",
"pt.what": "说明",
"pt.open": "打开",
"pt.kind_portal": "门户存档页(release/portal.html 经网关 /portal 伺服)",
"pt.kind_svc": "在线服务(经网关)",
"tab.portal_sec": "架构与资料",
"ps.eyebrow": "系统架构 · 方法 · 经验发现 · 案例·液压",
"ps.missing": "该节内容未抽取到(请检查 v2.9.2 门户原文)。"
};
function t(key, vars) {
let s = ZH[key] ?? key;
if (vars) {
for (const [k, v] of Object.entries(vars)) s = s.split(`{${k}}`).join(String(v));
}
return s;
}
const TABS = [
"overview",
"energy",
"component",
"vibration",
"generation",
"fault",
"decision",
"assistant",
"report",
"system",
"documents",
"recalc",
"portal_sec"
];
const DONE = ["overview", "energy", "component", "fault", "vibration", "generation", "decision", "assistant", "system", "report", "documents", "recalc", "portal_sec"];
const _sfc_main$f = /* @__PURE__ */ defineComponent({
__name: "Documents",
__ssrInlineRender: true,
setup(__props) {
const docs = ref(null);
const err = ref("");
const size = (n) => n >= 1048576 ? (n / 1048576).toFixed(1) + " MB" : Math.max(1, Math.round(n / 1024)) + " KB";
onMounted(async () => {
try {
const r = await fetch("/api/delivery_docs");
const j = await r.json();
if (j && j.error) err.value = String(j.error);
else docs.value = j && j.docs || [];
} catch (e) {
err.value = String(e && e.message || e);
}
});
return (_ctx, _push, _parent, _attrs) => {
_push(`${ssrInterpolate(unref(t)("doc.eyebrow"))}
`);
if (err.value) {
_push(`${ssrInterpolate(err.value)}
`);
} else if (!docs.value) {
_push(`${ssrInterpolate(unref(t)("doc.loading"))}
`);
} else {
_push(`${ssrInterpolate(unref(t)("doc.head", { n: docs.value.length }))}
${ssrInterpolate(unref(t)("doc.note"))}
| ${ssrInterpolate(unref(t)("doc.name"))} | ${ssrInterpolate(unref(t)("doc.size"))} | ${ssrInterpolate(unref(t)("doc.get"))} |
`);
ssrRenderList(docs.value, (d) => {
_push(`| ${ssrInterpolate(d.name)} | ${ssrInterpolate(size(d.size || 0))} | ${ssrInterpolate(unref(t)("doc.download"))} |
`);
});
_push(`
`);
}
_push(``);
};
}
});
const _sfc_setup$f = _sfc_main$f.setup;
_sfc_main$f.setup = (props, ctx) => {
const ssrContext = useSSRContext();
(ssrContext.modules || (ssrContext.modules = /* @__PURE__ */ new Set())).add("src/views/Documents.vue");
return _sfc_setup$f ? _sfc_setup$f(props, ctx) : void 0;
};
const _sfc_main$e = /* @__PURE__ */ defineComponent({
__name: "Recalc",
__ssrInlineRender: true,
setup(__props) {
const st = ref(null);
const err = ref("");
const busy = ref(false);
let timer = null;
const load = async () => {
try {
const r = await fetch("/api/recalc");
st.value = await r.json();
} catch (e) {
err.value = String(e && e.message || e);
}
};
onMounted(() => {
load();
timer = setInterval(load, 5e3);
});
onUnmounted(() => {
if (timer) clearInterval(timer);
});
return (_ctx, _push, _parent, _attrs) => {
_push(`${ssrInterpolate(unref(t)("rc.eyebrow"))}
`);
if (err.value) {
_push(`${ssrInterpolate(err.value)}
`);
} else if (!st.value) {
_push(`${ssrInterpolate(unref(t)("rc.loading"))}
`);
} else {
_push(`${ssrInterpolate(st.value.running ? unref(t)("rc.running") : unref(t)("rc.idle"))}
${ssrInterpolate(unref(t)("rc.note"))}
`);
if (st.value.started_at) {
_push(`
${ssrInterpolate(unref(t)("rc.since"))} ${ssrInterpolate(st.value.started_at)}
`);
} else {
_push(``);
}
_push(`
${ssrInterpolate(unref(t)("rc.log"))}
${ssrInterpolate(st.value.log || unref(t)("rc.empty"))} `);
}
_push(``);
};
}
});
const _sfc_setup$e = _sfc_main$e.setup;
_sfc_main$e.setup = (props, ctx) => {
const ssrContext = useSSRContext();
(ssrContext.modules || (ssrContext.modules = /* @__PURE__ */ new Set())).add("src/views/Recalc.vue");
return _sfc_setup$e ? _sfc_setup$e(props, ctx) : void 0;
};
const _sfc_main$d = /* @__PURE__ */ defineComponent({
__name: "Portal",
__ssrInlineRender: true,
setup(__props) {
const ENTRIES = [
{ id: "sim", label: "仿真", href: "/sim/", note: "仿真与回放(仿真台 18791)", kind: "svc" },
{ id: "admin", label: "状态", href: "/ops", note: "系统状态(运维台)", kind: "svc" },
{
id: "classic",
label: "经典",
href: "/web/detail_v2.html",
note: "经典版页面(v2.9.2 原文原样:布局/样式/字体/图文表一致)",
kind: "svc"
}
];
return (_ctx, _push, _parent, _attrs) => {
_push(`${ssrInterpolate(unref(t)("pt.eyebrow"))}
${ssrInterpolate(unref(t)("pt.head"))}
${ssrInterpolate(unref(t)("pt.note"))}
| ${ssrInterpolate(unref(t)("pt.col"))} | ${ssrInterpolate(unref(t)("pt.what"))} | ${ssrInterpolate(unref(t)("pt.open"))} |
`);
ssrRenderList(ENTRIES, (e) => {
_push(`| ${ssrInterpolate(e.note)} | ${ssrInterpolate(e.kind === "portal" ? unref(t)("pt.kind_portal") : unref(t)("pt.kind_svc"))} | ${ssrInterpolate(unref(t)("pt.open"))} |
`);
});
_push(`
`);
};
}
});
const _sfc_setup$d = _sfc_main$d.setup;
_sfc_main$d.setup = (props, ctx) => {
const ssrContext = useSSRContext();
(ssrContext.modules || (ssrContext.modules = /* @__PURE__ */ new Set())).add("src/views/Portal.vue");
return _sfc_setup$d ? _sfc_setup$d(props, ctx) : void 0;
};
const SEC_ARCHITECTURE = '
技术白皮书 · 系统架构
从证据分析到处置建议
五个相连的系统层:保留来源身份、落位设备上下文、检验机制、准备可问责的决定,并从检修结果中学习。
\n
十分钟 SCADA秒级数据CMS报警工单油样与检查技术文档
\n
01数据地基每条记录能被辨识和解释吗?
02设备本体每条记录指向哪个物理对象与配置?
03证据引擎证据支持某个具体机制吗?
04决策服务现在有什么动作是有依据的?
05检修学习动作改变了预期指标吗?
\n
验证过的现场结果 → 本体 + 证据史 → 下一轮分析
必需证据缺失 → 点名采集任务 → 停止升级
\n
身份、来源完整性或必需证据未解决时,系统失败关闭;验证过的检修结果成为可复用的证据。
\n
01 · 数据地基 · 登记与契约
每条记录能被辨识和解释吗?
保留原始证据,定义每个通道:登记来源位置、指纹、时间覆盖与格式,不改原文件;十分钟与秒级数据核单位、采样间隔、时间戳、哨兵值、通道活性与运行状态。
02 · 设备本体 · 落位
每条记录指向哪个物理对象与配置?
通过受控的工程图谱连接证据:资产、系统、总成、部件、测点与通道、告警、规程、工单、文档相连;机型、配置、有效期与来源约束每一条映射,含糊的对象停在未分配。
03 · 证据引擎 · 检验机制
证据支持某个具体机制吗?
先验测量链与物理界,再比较绝对水平、同工况机群残差、变化、分布形状与合格谱证据;证据族分开,相互矛盾的记录保持可见。
输出
有界的发现、疑似机制、支持证据、矛盾项,或「需更多数据」。
04 · 决策服务 · 限定动作
现在有什么动作是有依据的?
写一个可问责的决定对象:状态、机制、置信、后果、选项、责任人、到期日与闭合证据放在一起;系统准备监视/计划/动作三种选项,授权工程师批准现场与高成本动作。
05 · 检修学习 · 验证与复用
动作改变了预期指标吗?
工作包写明部件、规程、检查点与必需记录;检修后在同工况下比较同一指标,改善支持闭合,无改善则重开机制;验证过的结果挂回本体,供下一个相似案例。
\n
受保护的内容
资产专属参考保持在受保护工作区内
特征频率、业主与场站标识、私有技术文档与台级证据留在受保护工作区;公开图只展示架构。
\n
';
const SEC_METHOD = '工程方法
六步分析与决策依据
观澜把运行数据、设备上下文与检修历史连在一起,不隐藏不确定性。每个结果保留来源、时段与下一步验证。
\n
输入
十分钟 SCADA + 秒级数据
十分钟 SCADA
运行状态、热行为、负荷与机群比较
\n
单位、采样间隔、时间戳、哨兵值与通道活性在使用前核验。有这一列不等于有活信号。
\n
执行路径
六步
01登记来源:指纹与覆盖
02落位身份:机型与配置
03验证测量:单位、时间与活性
04比较状态:绝对与同工况对照
05检验机制:支持与矛盾
06发布决定:动作、责任人与闭合
\n
四道控制
诊断结论的验证要求
01
先验测量链
算特征之前先查静态偏置、缺失或默认值、时间戳对齐与真实采样行为。
02
匹配状态与时段
同工况、同证据窗比较;检修与配置变更开启新时段。
03
证据保持独立
同一传感器的多个特征仍是一族证据;共享预处理或共同测量链不构成互证。
04
缺必需证据就停
缺必需证据只产出"需更多数据",点名采集任务,禁止升级到更强的结论。
\n
决定与检修
从发现到验证过的现场结果
发现
陈述状态
部件、疑似机制、幅度、证据、矛盾与置信。
决定
选择 监视 / 计划 / 动作
给出后果、选项、责任人、到期日、批准与闭合证据。
现场结果
看变了什么
同工况下比较同一指标;闭合、继续或重开机制。
系统准备可审阅的工作范围与闭合所需证据;不签发工作许可,授权工程师批准现场与高成本动作。
\n
边界保持可见
记录决定边界
十分钟数据
分不开秒级作用的机制
秒级数据改善时序证据,但本身不建立因果。
相对偏差
只是筛查结果
直到有绝对物理量与适用边界支持为止。
语言模型
解释受控对象并引用记录
不能发明关系、补缺输入或批准检修。
\n
';
const SEC_FINDINGS = '现场经验
现场经验与工程判断
在 1.5 到 6 MW 机群上的工作,形成了一套鉴定证据、检验机制、支持检修决定的实用方法。
59 项内部检查 → 8 条公开教训
\n
项目身份、业主、场站、机组标识与资产专属诊断参数不公开。
\n
八条反复出现的教训
会改变工程结论的检查
A · 采样
让样本匹配问题
比较机组或机群之前,先声明独立单位、聚合层级、对照组、分母与时间窗。
B · 归因
要机制,也要时间顺序
共现与相关只能筛查假设;因果归因还需要物理机制、预期方向与事件级先后。
C · 测量
先验测量链
有值的字段、固定偏置或安静的通道未必是活信号;先查单位、时间戳、采样、哨兵、偏置与传感器状态。
D · 聚合
先保留事件再汇总
平均会抹掉极端、持续时长与受影响比例;出机群统计前先看点级形状、缺口与边界。
E · 判据
写明每条判据的适用域
单位、阈值、方差、运行状态、控制时长、必需输入与边界附近的情形都随结果一起给出。
F · 守护
验证守护真的在跑
文档里的检查在触发条件与生产调用路径确认前不提供任何保护,且失败必须可见。
G · 管线
让检查器失败来测它
向隔离的样例注入已知错误,证明真实入口拒绝它,再恢复有效输入证明通过。
H · 决定
筛查与动作分开
技术发现只有在范围、不确定性、后果、选项、责任人与批准明确后,才成为检修决定。
\n
经验怎么用
从异常发现到检修建议
01鉴定证据现有数据能支持什么、不能支持什么、哪项检查补上缺口
02落位设备上下文通过本体映射到正确的机型、配置、部件、测点与疑似机制
03准备决定给出监视/计划/动作选项、后果、现场范围、必需记录、批准人与闭合证据
\n
经验边界。经验横跨陆上与海上;换环境或换不熟悉的配置,复用任何判据或结论前都要重新验证控制、测量链、部件结构与运行制度。
\n
本场结论 · 契约生成
定论与准定论·预警 (0 条)
由结构化事实契约 362a00a7649889ea 渲染 (共 6 条); 数据、时间窗、结论只经契约更新, 本段禁手改 (release-manifest.content_contract)。
候选 · 参考 · 证据不足 (6 条, 按系统)
报警面 · 1 条 (候选 1)
| claim | 裁决 | 结论 | 缺项 |
|---|
RD-LEDGER-ALM-01 | 候选 | 报警集中: WTG08 5148 条(机群中位 616 条的 8.4 倍) | 报警条数不等于缺陷严重度: 单码抖动(同一码反复报)会抬高条数; 未做同码基线归一。 |
传动链 · 1 条 (候选 1)
| claim | 裁决 | 结论 | 缺项 |
|---|
RD-LEDGER-WO-01 | 候选 | 重复检修: WTG33 的「偏航单元」累计 81 次(窗内) | 检修台账记录的是"做过什么", 不等于"坏过几次": 同一部件的例行检查也会计次; 未做计划性/故障性停机区分(缺 work_type 字段)。 |
发电量 · 1 条 (参考 1)
| claim | 裁决 | 结论 | 缺项 |
|---|
RD-LEDGER-LOSS-01 | 参考 | 停机损失集中: WTG05 占全场停机损失 10%(2,874 MWh, 全场停机 29,368 MWh) | 损失是"损失了多少电", 不是"为什么损失"; 含调度令停机(非缺陷)与限电, 需与故障类分开看。 |
温度面 · 1 条 (候选 1)
| claim | 裁决 | 结论 | 缺项 |
|---|
RD-LEDGER-TEMP-01 | 候选 | 温度相对离群: WTG01 的 tmp_wtc_TrafoTMa_mean 在 3750.0 功率档偏离机群中位 18.0K(工况窗 ref) | 同工况中位比对的是"机群", 若全机群共因升温和(环境/冷却水温)则判据失效; 未接绝对限值(无 ISO/厂商温度锚)。 |
发电量, 功率曲线 · 1 条 (参考 1)
| claim | 裁决 | 结论 | 缺项 |
|---|
RD-LEDGER-PC-01 | 参考 | 功率曲线偏离: WTG03 同风速下功率偏差 -147 kW(判别: 风速计偏高候选(A类)); 全场判别分布 {'欠发候选(筛查级)': 3, '风速计偏高候选(A类)': 1} | 同风速残差受机位风资源(尾流/扇区)影响; 未做机位修正, 故只作"待核"不作缺陷结论。 |
振动面 · 1 条 (候选 1)
| claim | 裁决 | 结论 | 缺项 |
|---|
RD-LEDGER-VIB-01 | 候选 | 振动面过闸线: 3 条特征线达「候选」及以上(13#·hs_rotor·BPFO(外圈), 35#·hs_rotor·BPFO(外圈), 6#·hs_rotor·BPFO(外圈)) | 机制未定 ⇒ **不命名部件**(滚道/圈侧须实物或换件闭环); 峰值拾取为本器口径(目标频率 ±2 bin 取最大), 无正样本锚 ⇒ 封顶「候选」, 不出「 |
已撤回 (0 条)
';
const SEC_CASE_HYDRAULIC = '发现案例 · 系统① 液压 · 变桨回路与高速轴制动回路
八台机组变桨蓄能器失效
如东海上 38 台 SWT-4.0-130。通过泵加载特征评估 70 号蓄能器储能能力,形成逐台复核与处置建议。证据状态以各项记录为准。
\n
健康 26 台气容丧失 8 台边界 / 参数侧 5 台证据窗 运行 → 2026-07-07已处置 1 台 (4#) · 恢复基准待核对
\n
01 · 问
评估对象、监测量与适用条件
系统
液压 · 变桨回路
一个油箱、两条回路:变桨大站 400 L / 245 bar,HS 制动器由大站经 P BRAKE 分出。
监测量
泵单次加载输出量 V_cyc
加载时长 × 排量 36.8 L/min,来自 10-min 与秒级 SCADA 的泵启停信号。
方向
V_cyc 降低:单次进油量减少
蓄能器气容丧失后,每次加载只能推进很少的油。
评估限制
缺 Δp 或未逐台落值
横坐标缺失的台不外推;档案给组区间的台按区间画,不当单点。
缺失资料
轮毂 106 号无连续通道
只能靠机组自检容量检查记录(235 bar 起测)。
\n
02 · 证
两组蓄能器,分工完全不同,判据只对 70 号有效
70 号 · 泵站近端缓冲
10 L / 180 bar
活塞式,经 ø7 节流直连泵压主管,全额参与蓄能循环;A 类失效全在此件。系统总容变按仿真标定式分解:C = C管路 + C70 + 0.095·C106;预充 180 bar 由 38 台实测容变反算证实 (0.0839 ≈ 实测 0.087 L/bar)。
106 号 · 轮毂顺桨主源
6 × 30 L / 125 bar
无连续 SCADA 通道,经长管 + 旋转接头 + 118 单向阀,对蓄能循环仅弱耦合 (系数 0.095)。有自检容量检查可查;最近记录 140 bar @20 ℃ = 正常。
\n
健康 · 26 台
3.1 – 7.2 L
全部机群带中位 4.4 L。独立核验:按变桨活动带标定的仿真健康点 4.9 L 落在带内,与中位差 12%,仿真结果与运行统计对照;独立性需另行核查。同期泵周期 96 s、加载 6 次/10 min、带宽 50 bar、油温 38 ℃、超压 0%。
气容丧失 · 8 台
0.52 L
A 类中位 0.85 秒,为健康中位的 1/8.5。本质是系统总容变 C 由 0.087 塌到 0.0037 L/bar = 23 倍(剩下的只有纯管路柔度)。8# 实测同期:周期 2.6 s、加载 228 次/10 min、带宽 135、油温 51 ℃、超压占时 98.3%。
\n
03 · 裁
失效机理与鉴别依据
阶跃变化的机理解释
循环尖峰 → 铁片抬升 → 118 单向阀闭 → 轮毂 106 退出循环
C 直接塌到纯管路 ≈0.003(对齐 8# 实测 0.0037)。一旦越过这个连锁点,指标阶跃下落,没有中间态。
为什么不是漏油
外漏只增每循环消耗,不改系统容变 C
25# 泵输出 1.46× 而容变 C 仍在健康区 0.62 ⇒ 主导因素在消耗侧,先查漏点。但 C 已非满值,不能断言蓄能器完好,该台已补"同次测预充"。
为什么不是泵磨损
排量下降会拉长单次加载而不改频次
实测 8# 是频次由 6 升到 228 次/10 min(38×)且单次时长反缩短,该特征不支持单独以泵磨损解释,仍需结合现场检查。
口径边界:反算出的 C 绝对值依赖假定排量 36.8 L/min;分型与健康带用的是实测 V_cyc,不受此影响。
\n
04 · 分型
V_cyc × Δp 二维判别:分型与处置参考
右下
换 70 号蓄能器
压力波动范围扩大,单次进油量降低。7#、10#、8#、3#、20#、2#、35#。
右上
不换蓄能器
压力波动范围扩大,但单次进油量保持 ⟹ 漏油或参数。25#。
左上
正常 · 30 台
Δp 锁定 52,进油量满。
左下 / 未落值
参数侧 · 先测
Δp 过窄属参数侧;Δp 未落值的台横坐标无处安放,未测 ≠ 正常。7#、10# 档案无 Δp,不因缺少坐标而取消现场复核。
\n
05 · 改
逐台处置建议
| 台 | V_cyc (L/次) 原 → 现 | Δp | 判别 | 处置 |
|---|
| 7# | 9.61 → 1.8 | — | 气容丧失 · 全场衰减最快 | 换 70 号蓄能器 |
| 10# | 2.9 → 0.47 | — | 气容丧失 · 最新一起阶跃下降 | 换 70 号蓄能器 |
| 3# / 20# / 2# / 35# | 0.47–0.63 (组区间) | 宽 | 气容丧失 · 档案未逐台落值 | 换 70 号蓄能器 (逐台落值后下单) |
| 8# | 1.45 → 0.5 | 宽 | 气容丧失 · 频次 6 → 228 次/10 min | 换 70 号蓄能器 |
| 21# / 28# / 19# | 0.98 / 1.15 / 0.52 | 宽 | 边界台 | 先测预充定型, 再定 |
| 25# | 4.33 | 宽 (泵输出 1.46×) | 漏油或参数, 不是蓄能器 | 先现场核, 不换蓄能器 |
| 14# / 32# | 3.4–4.4 (健康带内) | 窄 | 压力带宽问题 | 参数侧核 |
| 4# | 0.53 → 4.84 | — | 已处置 · 恢复基准待核对 | 验收:核对基准后同工况复核 |
备件:70 号蓄能器 物资编码 A9B10051506(10 L / 180 bar 活塞式),编码取自备件系统「物资编码」字段;库存列表无导出日期,下单前必核 ERP 现存量,禁按本表采购。凡标「件号待确认」的条目,采购前联系现场核实。
\n
06 · 顺桨安全
顺桨储能与自检证据
70 号失效
SCADA 循环特征变化明显
失电顺桨主源是轮毂 106(103 → 121 路),70 号不在此路。数据窗内 4 次全场停电全部安全顺桨、零超速。
106 失氮(假设情形)
SCADA 循环指标识别能力有限
循环侧只有中度变化(吞吐约 2 L),与个体基线难分辨;能看见它的只有机组自检容量检查(UP03284 组,从轮毂 235 bar 起测)。后果最重:失电顺桨失去能量源。
两者同时失效
SCADA 上与单独 70 号失效不可分辨
而实际后果是顺桨失败 ⇒ 不能仅凭 SCADA 排除液压失效。两套监测互补、同时关注。
将现有自检记录按月归档,用于跟踪 106 号蓄能器的容量变化。
\n
07 · 量
修后验证
同工况复核
同一指标 V_cyc,同一变桨活动带
4# 已处置:0.53 → 4.84 L。4.84 L 低于此处所列 5.0–11.7 L 下限,与页面另一处 3.1–7.2 L 基准不一致。须核对工况与基准来源,当前不能据此确认修后恢复。
阴性对照
换蓄能器不该改变能量产出
4# 换件在 E1 阴性对照池中(优化窗 22 天 < 28 → INSUFFICIENT,未出数),不夸大收益。
记录归档
工单 → 复核 → 台账
每台的处置进检修任务,复核结果挂回本体;106 号自检记录进月度台账。
依据:U6_变桨液压仿真台(SWT-4.0-130,图纸 1034384 / 1034386;参数标定自本场 38 台实测与原厂图纸,2026-08-13 现场证实)。本页数字全部来自《如东_液压系统治理清单_v1.2》,未重跑。仿真台在"仿真与回放"页可交互 →
\n
';
const _sfc_main$c = /* @__PURE__ */ defineComponent({
__name: "PortalSections",
__ssrInlineRender: true,
setup(__props) {
const SUBS = [
{ id: "architecture", label: "系统架构", html: SEC_ARCHITECTURE },
{ id: "method", label: "方法", html: SEC_METHOD },
{ id: "findings", label: "经验发现", html: SEC_FINDINGS },
{ id: "case_hydraulic", label: "案例·液压", html: SEC_CASE_HYDRAULIC }
];
const cur = ref("architecture");
const body = computed(() => (SUBS.find((s) => s.id === cur.value) || SUBS[0]).html);
const missing = computed(() => !body.value);
return (_ctx, _push, _parent, _attrs) => {
_push(`${ssrInterpolate(unref(t)("ps.eyebrow"))}
`);
ssrRenderList(SUBS, (s) => {
_push(``);
});
_push(`
`);
if (missing.value) {
_push(`${ssrInterpolate(unref(t)("ps.missing"))}
`);
} else {
_push(`${body.value ?? ""}
`);
}
_push(``);
};
}
});
const _sfc_setup$c = _sfc_main$c.setup;
_sfc_main$c.setup = (props, ctx) => {
const ssrContext = useSSRContext();
(ssrContext.modules || (ssrContext.modules = /* @__PURE__ */ new Set())).add("src/views/PortalSections.vue");
return _sfc_setup$c ? _sfc_setup$c(props, ctx) : void 0;
};
const ST$1 = { 危险: "bad", 报警: "warn", 不可判: "unk", 良好: "ok", 优秀: "ex" };
const ST_ALIAS$1 = {
critical: "bad",
alarm: "warn",
undeterminable: "unk",
indeterminate: "unk",
"not evaluable": "unk",
good: "ok",
excellent: "ex",
warn: "warn",
bad: "bad",
ok: "ok",
unk: "unk",
ex: "ex"
};
const stateKey$1 = (value) => ST$1[String(value)] || ST_ALIAS$1[String(value)];
const STEP = {
证据: "evidence",
机制: "mechanism",
判级: "rating",
排期: "schedule",
动作: "action",
验收: "acceptance"
};
const stKey$1 = (k) => "st." + (k === "ex" ? "excellent" : k);
const unitNo = (wtg) => String(wtg).replace("WTG", "").replace(/^0/, "") + "#";
const fmt = (n, d = 0) => {
if (n == null || typeof n === "number" && isNaN(n)) return "—";
const num = Number(n);
if (isNaN(num)) return "—";
return num.toLocaleString("zh-CN", { maximumFractionDigits: d, minimumFractionDigits: d });
};
const stWord$1 = (zh) => {
const k = stateKey$1(zh);
return k ? t(stKey$1(k)) : String(zh ?? "");
};
const COLS = ["振动", "温度", "润滑", "油液", "报警", "工单"];
function unitState(row2) {
const ks = COLS.map((c) => stateKey$1((row2?.[c] || {}).ostate)).filter(Boolean);
if (!ks.length) return "unk";
if (ks.includes("bad")) return "bad";
if (ks.includes("warn")) return "warn";
const seen = ks.filter((k) => k !== "unk");
if (!seen.length) return "unk";
return seen.includes("ok") ? "ok" : "ex";
}
const SEV = {
bad: "critical",
warn: "warning",
unk: "unknown",
ok: "good",
ex: "excellent"
};
const winName = (w) => String(w ?? "");
const SYS9 = ["叶片与叶根", "变桨", "偏航", "齿轮箱", "发电机", "主轴承", "变流器", "塔架与基础", "主控与传感网"];
const SYS_KEY = {
叶片与叶根: "blade",
变桨: "pitch",
偏航: "yaw",
齿轮箱: "gearbox",
发电机: "generator",
主轴承: "main_bearing",
变流器: "converter",
塔架与基础: "tower",
主控与传感网: "control",
主轴: "main_bearing",
其他: "other"
};
const SYS_ZH = (() => {
const out = {};
for (const zh of SYS9) out[SYS_KEY[zh]] = zh;
for (const [zh, k] of Object.entries(SYS_KEY)) if (!(k in out)) out[k] = zh;
return out;
})();
const sysSlug = (value) => SYS_KEY[String(value)] || String(value);
const sysFromSlug = (value) => SYS_ZH[String(value)] || String(value);
const sysName = (value) => {
const key = SYS_KEY[String(value)] || String(value);
return SYS_ZH[key] ? t("sys." + key) : String(value ?? "");
};
const partName = (p) => sysName(p);
const _sfc_main$b = /* @__PURE__ */ defineComponent({
__name: "Component",
__ssrInlineRender: true,
props: {
d: {},
sys: {}
},
setup(__props) {
const props = __props;
const SD = computed(() => props.d?.sysdist || {});
const rel = computed(() => props.d?.rel && !props.d.rel.err ? props.d.rel : null);
const sysv = computed(() => props.sys ? sysFromSlug(props.sys) : null);
const cards = computed(() => SYS9.filter((s) => SD.value[s]).map((s) => {
const dist = SD.value[s];
const a = dist["报警"] || 0;
const u = dist["不可判"] || 0;
const N = Object.values(dist).reduce((x, y) => x + y, 0) || 1;
const col = a >= 8 ? "var(--critical)" : a >= 3 ? "var(--warning)" : a ? "var(--accent)" : "var(--good)";
return { s, dist, a, u, N, col };
}));
function hitHtml(w) {
return (w.syss || []).map((x) => `${sysName(x)}`).join(" · ");
}
const relEh = computed(() => {
const items = ((props.d?.control || {}).energy || {}).items || [];
const hit = items.filter((x) => x.k === "停机").map((x) => x.h)[0];
return hit == null ? null : hit;
});
const relRows = computed(() => {
const tb = rel.value?.表 || [];
const maxH = Math.max(1, ...tb.map((r) => r.停机时长h));
return tb.map((r) => ({
r,
maxH,
sev: r.部件 === "无记录停机" ? "var(--unknown)" : r.外部 ? "var(--accent)" : r.占停机 >= 0.06 ? "var(--critical)" : r.占停机 >= 0.03 ? "var(--warning)" : "var(--good)"
}));
});
const detail = computed(() => {
const s = sysv.value;
if (!s) return null;
const rows = (props.d?.systems || {})[s] || [];
const dist = SD.value[s] || {};
const gd = (props.d?.systems || {})[s + "·良好"] || [];
const cnt = (k) => dist[k] || 0;
const N = Object.values(dist).reduce((a, b) => a + b, 0) || 1;
return {
s,
N,
gd,
cnt,
al: rows.filter((r) => r.st === "报警"),
un: rows.filter((r) => r.st === "不可判"),
dist
};
});
return (_ctx, _push, _parent, _attrs) => {
if (detail.value) {
_push(`${ssrInterpolate(unref(t)("ui.back_to", { tab: unref(t)("tab.component") }))}
${ssrInterpolate(unref(t)("cmp.detail_head", { sys: unref(sysName)(detail.value.s), n: detail.value.N }))}
`);
ssrRenderList([["报警", "critical"], ["不可判", "unknown"], ["良好", "good"], ["优秀", "excellent"]], ([k, c]) => {
_push(``);
});
_push(`
${ssrInterpolate(unref(t)("cmp.counts", { a: detail.value.cnt("报警"), u: detail.value.cnt("不可判"), g: detail.value.cnt("良好"), e: detail.value.cnt("优秀") }))}
`);
ssrRenderList([detail.value.al, detail.value.un], (list, kind) => {
_push(`${ssrInterpolate(kind === 0 ? unref(t)("cmp.alarm_units") : unref(t)("cmp.unk_units"))} ${ssrInterpolate(unref(t)("cmp.n_units", { n: list.length }))}
`);
});
_push(``);
if (detail.value.gd.length) {
_push(`${ssrInterpolate(unref(t)("cmp.good_units", { n: detail.value.gd.length }))}
| ${ssrInterpolate(unref(t)("ui.unit"))} | ${ssrInterpolate(unref(t)("cmp.why"))} |
`);
ssrRenderList(detail.value.gd, (r) => {
_push(`| ${ssrInterpolate(unref(unitNo)(r.t))} | ${ssrInterpolate(r.why)} |
`);
});
_push(`
`);
} else {
_push(``);
}
if (!detail.value.al.length && !detail.value.un.length && !detail.value.gd.length) {
_push(`${ssrInterpolate(unref(t)("cmp.none"))}
`);
} else {
_push(``);
}
_push(``);
} else {
_push(``);
if (__props.d.sysmx_pending) {
_push(`${ssrInterpolate(unref(t)("cmp.grading_pending"))} ${ssrInterpolate(unref(t)("cmp.grading_pending_why", { span: __props.d.sysmx_span || "" }))}
`);
} else {
_push(``);
}
_push(`${ssrInterpolate(unref(t)("cmp.by_system"))}
`);
ssrRenderList(cards.value, (c) => {
_push(`
${ssrInterpolate(unref(sysName)(c.s))}
`);
ssrRenderList([["报警", "critical"], ["不可判", "unknown"], ["良好", "good"], ["优秀", "excellent"]], ([k, col]) => {
_push(``);
});
_push(`
${unref(t)("cmp.alarm_n", { a: `${c.a}` }) ?? ""}${ssrInterpolate((c.u ? unref(t)("cmp.unk_n", { u: c.u }) : "") + unref(t)("cmp.fleet_n", { n: c.N }))}
`);
});
_push(`
${ssrInterpolate(unref(t)("cmp.cards_note"))}
`);
if (__props.d.rel && __props.d.rel.err) {
_push(`${ssrInterpolate(unref(t)("flt.block_err", { k: "rel", e: __props.d.rel.err }))}
`);
} else if (rel.value) {
_push(`${ssrInterpolate(unref(t)("cmp.rel_eyebrow", { win: rel.value.窗 }))}
${ssrInterpolate(unref(t)("cmp.rel_head"))} ${ssrInterpolate(unref(t)("cmp.rel_meta", { t: rel.value.总停机h, a: rel.value.设备类停机h, b: rel.value.外部类停机h }))}
${ssrInterpolate(unref(t)("cmp.rel_note", { h: relEh.value == null ? "" : " " + unref(fmt)(relEh.value) + "h" }))}
| ${ssrInterpolate(unref(t)("cmp.rel_part"))} | ${ssrInterpolate(unref(t)("cmp.rel_h"))} | ${ssrInterpolate(unref(t)("cmp.rel_ev"))} | ${ssrInterpolate(unref(t)("cmp.rel_mtbo"))} | ${ssrInterpolate(unref(t)("cmp.rel_mdt"))} | ${ssrInterpolate(unref(t)("cmp.rel_units"))} | ${ssrInterpolate(unref(t)("cmp.rel_fm"))} |
`);
ssrRenderList(relRows.value, (x) => {
_push(`| ${ssrInterpolate(unref(partName)(x.r.部件))}`);
if (x.r.外部) {
_push(`${ssrInterpolate(" ")}${ssrInterpolate(unref(t)("cmp.external"))}`);
} else {
_push(``);
}
_push(` | ${ssrInterpolate(x.r.停机时长h)}h · ${ssrInterpolate((x.r["占停机"] * 100).toFixed(1))}% | ${ssrInterpolate(x.r.事件数)} | ${ssrInterpolate(x.r.外部 ? "—" : x.r.MTBF_h || "—")} | ${ssrInterpolate(x.r.MDT_h)} | ${ssrInterpolate(x.r.影响台数)} | ${ssrInterpolate(x.r.典型失效模式 || "—")} |
`);
});
_push(`
${ssrInterpolate(rel.value.口径)}
`);
} else {
_push(``);
}
if (__props.d.fus && !__props.d.fus.err) {
_push(``);
} else {
_push(``);
}
_push(`${ssrInterpolate(unref(t)("cmp.cross_eyebrow"))}
${ssrInterpolate(unref(t)("cmp.watch_head", { n: (__props.d.watch || []).length }))}
| ${ssrInterpolate(unref(t)("ui.unit"))} | ${ssrInterpolate(unref(t)("cmp.n_sys"))} | ${ssrInterpolate(unref(t)("cmp.hit_sys"))} | ${ssrInterpolate(unref(t)("cmp.basis"))} |
`);
ssrRenderList(__props.d.watch || [], (w) => {
_push(`| ${ssrInterpolate(unref(unitNo)(w.t))} | ${ssrInterpolate(w.n)} | ${hitHtml(w) ?? ""} | ${ssrInterpolate(w.why)} |
`);
});
_push(`
`);
}
};
}
});
const _sfc_setup$b = _sfc_main$b.setup;
_sfc_main$b.setup = (props, ctx) => {
const ssrContext = useSSRContext();
(ssrContext.modules || (ssrContext.modules = /* @__PURE__ */ new Set())).add("src/views/Component.vue");
return _sfc_setup$b ? _sfc_setup$b(props, ctx) : void 0;
};
const _sfc_main$a = /* @__PURE__ */ defineComponent({
__name: "EChart",
__ssrInlineRender: true,
props: {
option: {},
height: {}
},
setup(__props) {
const props = __props;
const el = ref(null);
let chart = null;
async function draw() {
if (!el.value) return;
const echarts = await import("echarts");
if (!chart) chart = echarts.init(el.value, void 0, { renderer: "canvas" });
chart.setOption(props.option, true);
}
onMounted(draw);
watch(() => props.option, draw, { deep: true });
onBeforeUnmount(() => {
if (chart) {
chart.dispose();
chart = null;
}
});
return (_ctx, _push, _parent, _attrs) => {
_push(``);
};
}
});
const _export_sfc = (sfc, props) => {
const target = sfc.__vccOpts || sfc;
for (const [key, val] of props) {
target[key] = val;
}
return target;
};
const _sfc_setup$a = _sfc_main$a.setup;
_sfc_main$a.setup = (props, ctx) => {
const ssrContext = useSSRContext();
(ssrContext.modules || (ssrContext.modules = /* @__PURE__ */ new Set())).add("src/components/EChart.vue");
return _sfc_setup$a ? _sfc_setup$a(props, ctx) : void 0;
};
const EChart = /* @__PURE__ */ _export_sfc(_sfc_main$a, [["__scopeId", "data-v-999bee15"]]);
const _sfc_main$9 = /* @__PURE__ */ defineComponent({
__name: "Energy",
__ssrInlineRender: true,
props: {
d: {}
},
setup(__props) {
const props = __props;
const E = computed(() => (props.d?.control || {}).energy);
const S = computed(() => (props.d?.control || {}).states || []);
const LOSS = {
"限电·绑定": "loss.curtail_bound",
"限电命令·未绑定": "loss.curtail_unbound",
"停机(调度令)": "loss.dispatch",
停机: "loss.stop",
正常发电: "loss.normal",
低风待机: "loss.lowwind"
};
const lossName = (k) => LOSS[k] ? t(LOSS[k]) : k;
const kpi = computed(() => {
const e = E.value || {};
return [
{ v: fmt(e.theo), u: "MWh", l: t("en.kpi_theo"), k: "" },
{ v: fmt(e.act), u: "MWh", l: t("en.kpi_act", { h: fmt(e.eflh) }), k: "ok" },
{ v: fmt(e.loss_pct, 1), u: "%", l: t("en.kpi_loss", { v: fmt(e.loss) }), k: e.loss_pct >= 10 ? "warn" : "" },
{
v: e.cap_ok ? t("en.cap_pass") : t("en.cap_fail"),
u: "",
l: t("en.cap_note", { op: e.cap_ok ? "<" : "≥", cap: fmt(e.cap) }),
k: e.cap_ok ? "ok" : "bad"
}
];
});
const blameCat = (k) => k.indexOf("限电") >= 0 || k.indexOf("调度") >= 0 ? 0 : k === "停机" ? 1 : 2;
const blameGroups = computed(() => {
const g = [0, 0, 0];
for (const x of E.value?.items || []) g[blameCat(x.k)] += x.loss;
return g;
});
const waterfallOption = computed(() => {
const e = E.value || {};
const items = (e.items || []).filter((x) => x.loss > 0).map((x) => ({ name: x.k, value: Math.round(x.loss) }));
const groups = blameGroups.value;
const tot = groups.reduce((a, b) => a + b, 0) || 1;
const pct = groups.map((v) => Math.round(v / tot * 100)).filter((p, i) => groups[i] / tot * 500 > 46);
return {
title: { text: t("en.balance"), left: "center", textStyle: { fontSize: 13 } },
tooltip: { trigger: "axis" },
grid: { left: 70, right: 20, top: 44, bottom: 40 },
xAxis: { type: "category", data: items.map((x) => x.name) },
yAxis: { type: "value", name: "MWh" },
series: [{ type: "bar", data: items.map((x) => x.value), itemStyle: { color: "#0b5fa5" } }],
// 与旧图数字集合同源:theo · act · Σitems · 各 item · 组占比%
__data__: [
Math.round(e.theo),
Math.round(e.act),
Math.round(items.reduce((a, x) => a + x.value, 0)),
...items.map((x) => x.value),
...pct
]
};
});
const blameOption = computed(() => {
const groups = blameGroups.value;
return {
grid: { left: 90, right: 24, top: 20, bottom: 30 },
xAxis: { type: "value", name: "MWh" },
yAxis: { type: "category", data: [t("ch.blame.n1"), t("ch.blame.n2"), t("ch.blame.n3")] },
series: [{ type: "bar", data: groups.map((v) => Math.round(v)), itemStyle: { color: "#2f7dbf" } }],
__data__: []
};
});
const mxp = computed(() => Math.max(1, ...S.value.map((x) => x.pct)));
return (_ctx, _push, _parent, _attrs) => {
if (!E.value) {
_push(`${ssrInterpolate(unref(t)("ui.empty"))}
`);
} else {
_push(``);
ssrRenderList(kpi.value, (k, i) => {
_push(`
${ssrInterpolate(k.v)}`);
if (k.u) {
_push(`${ssrInterpolate(k.u)}`);
} else {
_push(``);
}
_push(`${ssrInterpolate(k.l)}
`);
});
_push(`
${ssrInterpolate(unref(t)("en.balance"))} ${ssrInterpolate(unref(t)("en.win", { win: unref(winName)(__props.d.win) }))}
`);
_push(ssrRenderComponent(EChart, {
option: waterfallOption.value,
height: "280px"
}, null, _parent));
_push(`${ssrInterpolate(unref(t)("en.blame"))}
`);
_push(ssrRenderComponent(EChart, {
option: blameOption.value,
height: "240px"
}, null, _parent));
_push(``);
ssrRenderList(blameGroups.value, (v, i) => {
_push(`
${ssrInterpolate(unref(t)(["ch.blame.n1", "ch.blame.n2", "ch.blame.n3"][i]))}${ssrInterpolate(unref(fmt)(Math.round(v)))} MWh${ssrInterpolate(unref(t)(["ch.blame.d1", "ch.blame.d2", "ch.blame.d3"][i]))}
`);
});
_push(`
${ssrInterpolate(unref(t)("en.blame_note", { n: unref(fmt)(Math.round(((E.value.items || []).find((x) => x.k === "停机") || {}).loss || 0)) }))}
${ssrInterpolate(unref(t)("en.states"))} ${ssrInterpolate(unref(t)("en.win", { win: unref(winName)(__props.d.win) }))}
| ${ssrInterpolate(unref(t)("en.state"))} | ${ssrInterpolate(unref(t)("en.share"))} | ${ssrInterpolate(unref(t)("en.loss_mwh"))} |
`);
ssrRenderList(S.value, (x) => {
_push(`| ${ssrInterpolate(lossName(x.k))} | = 0 && x.k.indexOf("调度") < 0 ? "var(--critical)" : "var(--accent)"}`)}">${ssrInterpolate(x.pct)}% · ${ssrInterpolate(unref(fmt)(Math.round(x.h)))}h | ${ssrInterpolate(unref(fmt)(Math.round(x.loss)))} |
`);
});
_push(`
${ssrInterpolate(__props.d.control.anchors || "")}
${ssrInterpolate(unref(t)("en.states_note"))}
`);
}
};
}
});
const _sfc_setup$9 = _sfc_main$9.setup;
_sfc_main$9.setup = (props, ctx) => {
const ssrContext = useSSRContext();
(ssrContext.modules || (ssrContext.modules = /* @__PURE__ */ new Set())).add("src/views/Energy.vue");
return _sfc_setup$9 ? _sfc_setup$9(props, ctx) : void 0;
};
const _sfc_main$8 = /* @__PURE__ */ defineComponent({
__name: "Fault",
__ssrInlineRender: true,
props: {
d: {}
},
setup(__props) {
const props = __props;
const F = computed(() => props.d?.faults || {});
const pm = computed(() => F.value.partial || []);
const M = computed(() => props.d?.m8 && !props.d.m8.err ? props.d.m8.mtbf : null);
const mo = computed(() => F.value.monthly || { months: [], vals: [] });
const perDay = computed(() => mo.value.per_day || []);
const exampleIdx = computed(() => pm.value.length ? (mo.value.months || []).indexOf(pm.value[0]) : -1);
const rateTail = computed(() => {
const head = pm.value.length ? t("flt.rate_partial", { m: pm.value.join("/") }) : t("flt.rate_full");
const hasExample = exampleIdx.value >= 0 && !!mo.value.per_day;
return head + (hasExample ? " " + t("flt.rate_example", { m: pm.value[0], v: exampleVal.value }) : "");
});
const exampleVal = computed(() => {
const i = exampleIdx.value;
return i >= 0 && mo.value.per_day ? mo.value.per_day[i] : null;
});
const partialWarnTxt = computed(() => pm.value.length ? t("win.partial", { m: pm.value.join("/"), c: Math.round((F.value.cov_min || 0) * 100) }) : "");
const basic = {
grid: { left: 60, right: 20, top: 24, bottom: 46 },
tooltip: { trigger: "axis" },
yAxis: { type: "value" }
};
const rateOption = computed(() => ({
...basic,
xAxis: { type: "category", data: mo.value.months || [] },
series: [{ type: "bar", data: perDay.value.map((v) => v == null ? null : Math.round(v * 100) / 100), itemStyle: { color: "#0b5fa5" } }],
__data__: perDay.value.filter((v) => v != null)
}));
function pareto(items) {
const it = (items || []).filter((x) => x && x.v > 0);
return {
...basic,
xAxis: { type: "category", data: it.map((x) => x.k) },
series: [{ type: "bar", data: it.map((x) => x.v), itemStyle: { color: "#2f7dbf" } }],
__data__: []
// 旧 Pareto 只把坐标刻度写成 SVG 文字(数据靠柱高/悬停),故不给数
};
}
const shortK = (k) => {
const m = String(k).match(/^([0-9]+)/);
return m ? m[1] : String(k).slice(0, 4);
};
const esc2 = (s) => String(s ?? "").replace(/&/g, "&").replace(//g, ">").replace(/"/g, """);
function paretoSummaryHtml(items, unit, note) {
const it = (items || []).filter((x) => x && x.v > 0);
if (!it.length) return esc2(t("ch.pareto.none"));
const tot = it.reduce((a, x) => a + x.v, 0);
let cum = 0;
let k80 = null;
for (let i = 0; i < it.length; i++) {
cum += it[i].v;
if (k80 === null && tot && cum / tot >= 0.8) k80 = i + 1;
}
const top3 = it.slice(0, 3).map((x) => `${esc2(shortK(x.k))} ${esc2(String(x.k).replace(/^[0-9]+ */, "").slice(0, 18))}`).join(" · ");
const total = t("ch.pareto.total", { n: it.length, tot: fmt(Math.round(tot)), unit });
const head = k80 ? `${esc2(t("ch.pareto.key", { k: k80 }))} (${esc2(total)})` : esc2(total);
return head + "
" + esc2(t("ch.pareto.top3")) + top3 + (note ? "
" + esc2(note) : "");
}
const paretoNSummary = computed(() => paretoSummaryHtml(F.value.pareto_n || [], t("unit.n"), t("flt.pareto_n_note")));
const paretoDSummary = computed(() => paretoSummaryHtml(F.value.pareto_d || [], " h", t("flt.pareto_d_note")));
const stopParetoSummary = computed(() => paretoSummaryHtml(stopPareto.value, " h", t("flt.stop_pareto_note")));
const paretoNOption = computed(() => pareto(F.value.pareto_n || []));
const paretoDOption = computed(() => pareto(F.value.pareto_d || []));
function k80Of(items) {
const it = (items || []).filter((x) => x && x.v > 0);
if (!it.length) return null;
const tot = it.reduce((a, x) => a + x.v, 0);
let cum = 0;
for (const x of it) {
cum += x.v;
if (tot && cum / tot >= 0.8) return { k: String(x.k), n: it.length, tot };
}
return { k: String(it[it.length - 1].k), n: it.length, tot };
}
computed(() => k80Of(F.value.pareto_n || []));
computed(() => k80Of(F.value.pareto_d || []));
const quadOption = computed(() => {
const it = (F.value.quad || []).filter((x) => x && x.n > 0 && x.med > 0);
return {
grid: { left: 60, right: 24, top: 24, bottom: 46 },
xAxis: { type: "log", name: "MTBF(h)" },
yAxis: { type: "value", name: "MDT(h)" },
series: [{
type: "scatter",
data: it.map((x) => [x.mtbf ?? x.n, x.med]),
symbolSize: (v) => Math.min(28, 6 + Math.sqrt(Math.max(v[0] || 1, 1))),
itemStyle: { color: "#b42318", opacity: 0.6 }
}],
__data__: []
// 象限图是散点:坐标即数据,旧图不把数值写成文字
};
});
const pt = computed(() => F.value.per_t || []);
const maxT = computed(() => Math.max(1, ...pt.value.map((x) => x.v)));
const seasonal = computed(() => M.value && props.d?.m8?.seasonal ? props.d.m8.seasonal : []);
const ML_COLS = ["var(--accent)", "#D48A1E", "#2E8B6A", "#7C5295"];
const seasonalNames = computed(() => seasonal.value.slice(0, 4).map((x) => String(x.name).slice(0, 16)));
const seasonalOption = computed(() => {
const s = seasonal.value.slice(0, 4);
const months = s.length ? s[0].months : [];
return {
...basic,
xAxis: { type: "category", data: months },
legend: { top: 0, textStyle: { fontSize: 10 } },
series: s.map((x) => ({ name: String(x.name).slice(0, 16), type: "line", data: x.vals, connectNulls: false })),
__data__: []
// 旧 multiline 只标刻度;系列值不写成文字
};
});
const RT = computed(() => props.d?.rel && !props.d.rel.err ? props.d.rel : null);
const ramOption = computed(() => {
const rows = RT.value?.表 || [];
return {
grid: { left: 60, right: 24, top: 24, bottom: 40 },
xAxis: { type: "value", name: "MTBF(h)" },
yAxis: { type: "value", name: "MDT(h)" },
series: [{
type: "scatter",
data: rows.filter((r) => !r.外部 && r.MTBF_h).map((r) => [r.MTBF_h, r.MDT_h]),
symbolSize: 8,
itemStyle: { color: "#2f7dbf", opacity: 0.5 }
}],
__data__: []
// 旧 ramChart 也是散点(MTBF×MDT),数值不写成文字
};
});
const attribGroups = computed(() => {
const rows = RT.value?.表 || [];
const unk = (rows.find((r) => r.部件 === "无记录停机") || {}).停机时长h || 0;
const un2 = rows.filter((r) => r.部件 === "未归类").reduce((a, r) => a + r.停机时长h, 0);
const tot = RT.value?.总停机h || 0;
const ok = tot - unk - un2;
return { ok, unk, un2 };
});
const attribOption = computed(() => {
const g = attribGroups.value;
return {
xAxis: { type: "value" },
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) })] },
series: [{ type: "bar", data: [Math.round(g.ok), Math.round(g.unk), Math.round(g.un2)], itemStyle: { color: "#0b5fa5" } }],
__data__: [Math.round(g.ok), Math.round(g.unk), Math.round(g.un2)]
};
});
const stopPareto = computed(() => (props.d?.m8?.stop_pareto || []).map((x) => ({
k: String(x.k).split(";")[0].slice(0, 26),
v: x.h
})));
const stopParetoOption = computed(() => pareto(stopPareto.value));
const top = computed(() => M.value?.台级top || []);
const mxTop = computed(() => Math.max(1, ...top.value.map((x) => x.时长h)));
return (_ctx, _push, _parent, _attrs) => {
_push(`${ssrInterpolate(unref(t)("flt.eyebrow"))}
${ssrInterpolate(unref(t)("win.banner", { win: unref(winName)(__props.d.win) }))}`);
if (partialWarnTxt.value) {
_push(` | ${ssrInterpolate(partialWarnTxt.value)}`);
} else {
_push(``);
}
_push(`
${ssrInterpolate(unref(t)("flt.alarm_face"))} ${ssrInterpolate(unref(t)("flt.alarm_meta", { n: unref(fmt)(F.value.total), c: F.value.n_codes, win: unref(winName)(__props.d.win) }))}
${ssrInterpolate(unref(t)("flt.rate"))}
`);
_push(ssrRenderComponent(EChart, {
option: rateOption.value,
height: "250px"
}, null, _parent));
_push(`
${ssrInterpolate(unref(t)("flt.rate_note"))} — ${ssrInterpolate(rateTail.value)}
${ssrInterpolate(unref(t)("flt.pareto_n"))}
`);
_push(ssrRenderComponent(EChart, {
option: paretoNOption.value,
height: "240px"
}, null, _parent));
_push(`
${paretoNSummary.value ?? ""}
${ssrInterpolate(unref(t)("flt.pareto_d"))}
`);
_push(ssrRenderComponent(EChart, {
option: paretoDOption.value,
height: "240px"
}, null, _parent));
_push(`
${paretoDSummary.value ?? ""}
${ssrInterpolate(unref(t)("flt.quad"))}
`);
_push(ssrRenderComponent(EChart, {
option: quadOption.value,
height: "300px"
}, null, _parent));
_push(`
${ssrInterpolate(unref(t)("flt.quad_note", { n: F.value.n_codes, m: (F.value.quad || []).length }))} `);
if (pm.value.length) {
_push(`${ssrInterpolate(unref(t)("flt.quad_partial", { m: pm.value.join("/"), c: Math.round((F.value.cov_min || 0) * 100) }))}`);
} else {
_push(``);
}
_push(` ${ssrInterpolate(unref(t)("flt.quad_note2"))}
${ssrInterpolate(unref(t)("flt.per_t", { n: pt.value.length }))}
| ${ssrInterpolate(unref(t)("ui.unit"))} | ${ssrInterpolate(unref(t)("ui.count"))} | ${ssrInterpolate(unref(t)("flt.per_t_share"))} |
`);
ssrRenderList(pt.value, (x) => {
_push(`| ${ssrInterpolate(unref(unitNo)(x.k))} | ${ssrInterpolate(unref(fmt)(x.v))} | ${ssrInterpolate((x.v / F.value.total * 100).toFixed(1))}% |
`);
});
_push(`
${ssrInterpolate(unref(t)("flt.per_t_note", { n: unref(fmt)(F.value.total) }))}
`);
if (__props.d.m8 && __props.d.m8.err) {
_push(`
${ssrInterpolate(unref(t)("flt.block_err", { k: "m8", e: __props.d.m8.err }))}
`);
} else {
_push(``);
}
if (M.value && seasonal.value.length) {
_push(`
${ssrInterpolate(unref(t)("flt.seasonal"))} (${ssrInterpolate(unref(t)("flt.seasonal_unit"))})
`);
_push(ssrRenderComponent(EChart, {
option: seasonalOption.value,
height: "260px"
}, null, _parent));
_push(`
`);
ssrRenderList(seasonalNames.value, (nm, i) => {
_push(`${ssrInterpolate(nm)}`);
});
_push(`
${ssrInterpolate(unref(t)("flt.seasonal_note"))}
`);
} else {
_push(``);
}
if (M.value) {
_push(`
${ssrInterpolate(unref(t)("flt.stop_face"))} ${ssrInterpolate(unref(t)("flt.stop_meta", { h: unref(fmt)(M.value.停机总时长h), n: unref(fmt)(M.value.停机事件数) }))}
`);
if (RT.value) {
_push(`
${ssrInterpolate(unref(t)("flt.ram"))}
`);
_push(ssrRenderComponent(EChart, {
option: ramOption.value,
height: "300px"
}, null, _parent));
_push(`
${ssrInterpolate(unref(t)("flt.ram_note", { a: RT.value.设备类停机h, b: RT.value.外部类停机h }))}
`);
_push(ssrRenderComponent(EChart, {
option: attribOption.value,
height: "160px"
}, null, _parent));
_push(`
${ssrInterpolate(unref(t)("flt.attrib_note", { h: ((RT.value.表 || []).find((x) => x.部件 === "无记录停机") || {}).停机时长h || 0 }))}
`);
} else {
_push(``);
}
_push(`
${ssrInterpolate(unref(t)("flt.stop_pareto"))}
`);
_push(ssrRenderComponent(EChart, {
option: stopParetoOption.value,
height: "240px"
}, null, _parent));
_push(`
${stopParetoSummary.value ?? ""}
${ssrInterpolate(M.value.口径)}
${ssrInterpolate(unref(t)("flt.stop_units", { n: top.value.length }))}
| ${ssrInterpolate(unref(t)("ui.unit"))} | ${ssrInterpolate(unref(t)("flt.stop_h"))} | ${ssrInterpolate(unref(t)("flt.stop_n"))} |
`);
ssrRenderList(top.value, (x) => {
_push(`| ${ssrInterpolate(unref(unitNo)(x.t))} | ${ssrInterpolate(unref(fmt)(x.时长h))}h | ${ssrInterpolate(x.次)} |
`);
});
_push(`
${ssrInterpolate(unref(t)("flt.stop_units_note"))}
`);
} else {
_push(``);
}
_push(`
`);
};
}
});
const _sfc_setup$8 = _sfc_main$8.setup;
_sfc_main$8.setup = (props, ctx) => {
const ssrContext = useSSRContext();
(ssrContext.modules || (ssrContext.modules = /* @__PURE__ */ new Set())).add("src/views/Fault.vue");
return _sfc_setup$8 ? _sfc_setup$8(props, ctx) : void 0;
};
const VIBJARGON = [
[/DE[/]NDE/g, "驱动端与非驱动端"],
[/GenDE/g, "发电机驱动端"],
[/GenNDE/g, "发电机非驱动端"],
[/HS_gen2?/g, "高速轴发电机侧"],
[/HS_rot/g, "高速轴转子侧"],
[/IMS/g, "中间轴"],
[/(? String(s == null ? "" : s).replace(/&/g, "&").replace(//g, ">").replace(/"/g, """);
const displayText = (s) => String(s == null ? "" : s);
const dv = (s) => esc(s);
const ST = { 危险: "bad", 报警: "warn", 不可判: "unk", 良好: "ok", 优秀: "ex" };
const ST_ALIAS = {
critical: "bad",
alarm: "warn",
undeterminable: "unk",
indeterminate: "unk",
"not evaluable": "unk",
good: "ok",
excellent: "ex",
warn: "warn",
bad: "bad",
ok: "ok",
unk: "unk",
ex: "ex"
};
const stateKey = (v) => ST[String(v)] || ST_ALIAS[String(v)];
const STC = { bad: "var(--critical)", warn: "var(--warning)", unk: "var(--unknown)", ok: "var(--good)", ex: "var(--excellent)" };
const CST = { 优秀: "var(--excellent)", 良好: "var(--good)", 报警: "var(--warning)", 危险: "var(--critical)", 不可判: "var(--unknown)", 监测不可用: "var(--unknown)", 无监测: "var(--unknown)" };
const CLV = { bad: "var(--critical)", warn: "var(--warning)", note: "var(--muted)", ok: "var(--good)", unlisted: "var(--muted)", stale: "var(--muted)", meta: "var(--muted)" };
const TIER = {
定论: "confirmed",
"准定论·预警": "near",
"候选·新发": "candidate_new",
候选: "candidate",
"候选·记基线": "candidate_baseline",
参考: "reference",
INSUFFICIENT: "insufficient",
正常: "normal",
撤回: "withdrawn"
};
const stKey = (k) => "st." + (k === "ex" ? "excellent" : k);
const stWord = (zh) => {
const k = stateKey(zh);
return k ? t(stKey(k)) : dv(zh);
};
const tierWord = (zh) => {
const k = TIER[String(zh)];
const key = "tier." + k;
return k ? t(key) : String(zh) === "—" ? "—" : dv(zh);
};
const duo = (st, ev, col) => `${st}${ev}`;
const vibPlain = (x) => {
let s = String(x == null ? "" : x);
for (const [a, b] of VIBJARGON) s = s.replace(a, b);
return s;
};
const attrPlain = (x) => vibPlain(x);
const spark = (hist, color) => {
if (!hist || hist.filter((x) => x != null).length < 3) return "";
const vs = hist.filter((x) => x != null);
const mn = Math.min(...vs), mx = Math.max(...vs), sp = mx - mn || 1;
const W = 84, H = 22, n = hist.length;
let d = "", last = null;
hist.forEach((v, i) => {
if (v == null) return;
const x = i / (n - 1) * W, y = H - 2 - (v - mn) / sp * (H - 4);
d += (d ? "L" : "M") + x.toFixed(1) + " " + y.toFixed(1);
last = [x, y];
});
return ``;
};
const FAMCO = { 齿轮箱: "#8A6D3B", 发电机: "#4A6FA5", 主轴承: "#2E7D6B", 变桨叶片: "#4A6FA5", 偏航: "#2E7D6B", 测风: "#7C5295", 其他: "#5A6470" };
const famBar = (fam, n, maxN) => {
if (!n) return `${t("fus.no_alarm")}`;
const W = 118, w = Math.max(6, Math.round(n / Math.max(maxN || n, 1) * W));
return `${(fam || []).map((f) => ``).join("")}
${(fam || []).slice(0, 2).map((f) => sysName(f.k) + " " + f.n).join(" · ")}
`;
};
function matrixHtml(F) {
const months = (F.列窗 || {}).months || [];
let h = `${t("fus.rules_head")}${t("fus.rule1")}
${t("fus.rule2")}
`;
h += `${t("fus.matrix_head")}
${t("fus.matrix_note1")}
${t("fus.matrix_note2", { n: months.length, m0: months[0] || "?" })}
${t("fus.legend_state")} ${["bad", "warn", "ok", "ex", "unk"].map((k) => `${t(stKey(k))}`).join("")}
${t("fus.legend_tier")} ${["定论", "准定论·预警", "候选·新发", "候选", "候选·记基线", "参考", "INSUFFICIENT"].map((k) => `${tierWord(k)}`).join("")}
${t("fus.legend_note")}
`;
const shortWin = (w) => {
const s = String(w || "");
const m = s.match(/20[0-9][0-9]-[0-9][0-9](?:~20[0-9][0-9]-[0-9][0-9])?|至[ ]*20[0-9][0-9]-[0-9][0-9]|20[0-9][0-9]~20[0-9][0-9]/);
return m ? m[0].startsWith("至") ? t("win.through", { date: m[0].replace(/^至\s*/, "") }) : m[0] : s.slice(0, 14);
};
const th = (name, win, extra) => `
${name} ${dv(shortWin(win))}${extra || ""} | `;
const LW = F.列窗 || {};
h += `
${''.repeat(6)}| ${t("ui.unit")} | ${th(t("fus.col.vib"), LW.振动, t("fus.col.fixed"))}${th(t("fus.col.temp"), LW.温度)}${th(t("fus.col.lub"), LW.润滑, t("fus.col.display_only"))}${th(t("fus.col.oil"), LW.油液)}${th(t("fus.col.alarm"), LW.报警)}${th(t("fus.col.wo"), LW.工单)}
`;
const mat = F.矩阵 || [];
const MAXAL = Math.max(1, ...mat.map((r) => (r.报警 || {}).n || 0));
const MAXWO = Math.max(1, ...mat.map((r) => (r.工单 || {}).n || 0));
const WC = F.色标 || {};
const shortOf = (c) => {
if (c.ostate && c.ostate !== "不可判") return stWord(c.ostate);
const n = String(c.note || "");
if (/数据不足|不可判|nsufficient/.test(n)) return t("fus.short.nodata");
if (/未评估|ot assessed/.test(n)) return t("fus.short.uneval");
if (/无样|未取样|o sample/.test(n)) return t("fus.short.nosample");
if (/样龄|stale/.test(n)) return t("fus.short.stale");
const m = { ok: t("fus.short.normal"), unlisted: t("fus.short.unlisted"), stale: t("fus.short.stale") };
return m[c.level] || "—";
};
const cell = (c0) => {
const c = c0 || {};
const col = CST[c.ostate] || WC[c.wlevel] || CLV[c.level] || "var(--muted)";
const full = vibPlain(c.note);
const isProb = ["bad", "warn", "note"].includes(c.level);
const tip = esc(full) + t("fus.tip_state", { s: stWord(c.ostate || "?") });
if (!isProb) return `${shortOf(c)} | `;
const parts = full.split(/[::]/);
const brief = (parts.length > 1 ? parts.slice(1).join(":") : full).trim();
const cleanBrief = /^[ ]*(评估报告|两口径差异|⚠|ssessment)/.test(brief) || brief.length > 16 ? "" : brief;
const shortCut = cleanBrief ? dv(cleanBrief) : c.ostate && c.ostate !== "不可判" ? "" : shortOf(c);
const ev = c.wlevel && c.wlevel !== "—" && c.wlevel !== "正常" && c.ostate ? duo(stWord(c.ostate), tierWord(c.wlevel), col) : "";
return `${spark(c.hist, c.level === "bad" ? "var(--critical)" : c.level === "warn" ? "var(--warning)" : "var(--muted)")} | `;
};
mat.forEach((r) => {
const al = r.报警 || { n: 0, fam: [] }, wo = r.工单 || { n: 0, acts: [] };
const alc = al.rank && al.rank <= 5 ? "var(--warning)" : "var(--line)";
const bu = al.burst;
const altip = esc((al.items || []).map((x) => `${x.code} ${vibPlain(x.text)} — ${x.n} (${x.first}~${x.last})`).join("\n")) + (bu ? "\n" + esc(t("fus.burst_tip", { c: bu.code, p: (bu.share * 100).toFixed(0), m: bu.med_s })) : "");
h += `| ${unitNo(r.turbine)} | ${cell(r.振动)}${cell(r.温度)}${cell(r.润滑)}${cell(r.油液)}
${t("fus.alarm_n", { n: al.n })}${al.rank ? `${t("fus.alarm_rank", { r: al.rank, t: al.tot })}` : ""}${bu ? ` ${t("fus.burst", { p: (bu.share * 100).toFixed(0), c: bu.code })} ` : ""}${al.rank && al.rank <= 10 || bu ? famBar(al.fam, al.n, MAXAL) : ""} |
${wo.n ? `${t("fus.wo_n", { n: wo.n })}${wo.last ? ` · ${esc(wo.last)}` : ""} ${(wo.acts || []).slice(0, 2).map((a) => dv(a.k) + "×" + a.n).join(" · ")} ` : `—`} |
`;
});
return h + `
${t("fus.matrix_foot")}
`;
}
const baBar = (pre, post, col) => {
const M = Math.max(pre, post, 7), W = 120, sc = (v) => Math.max(2, v / M * (W - 4));
return ``;
};
const chainMini = (comp, col) => {
const c = String(comp || "").replace(/main bearing[, ]*[(]?(?:rear|back)[)]?/ig, "主轴承后").replace(/main bearing[, ]*[(]?(?:front|forward)[)]?/ig, "主轴承前").replace(/main bearing/ig, "主轴承").replace(/generator[, ]*(?:NDE|non-drive[- ]end)/ig, "发电机NDE").replace(/generator[, ]*(?:DE|drive[- ]end)/ig, "发电机DE").replace(/generator/ig, "发电机").replace(/gearbox/ig, "齿轮箱").replace(/planetary/ig, "行星").replace(/intermediate[- ](?:speed[- ])?shaft/ig, "中间轴").replace(/high[- ]speed shaft/ig, "高速轴"), gball = /齿轮箱/.test(c) && !/行星|中间轴|中速|高速轴/.test(c);
return `${CHAIN7.map(([k, re]) => {
const on = re.test(c) || gball && ["ps", "ims", "hss"].includes(k);
return ``;
}).join("")}`;
};
function loopHtml(F) {
const LP = F.能力 && F.能力.loop && F.能力.loop["换件闭环 (检出→换件→恢复)"] ? F.能力.loop : null;
if (!LP) return ``;
const cl = LP["换件闭环 (检出→换件→恢复)"] || [];
const up = LP["当前在升 (无工单可对)"] || [];
const neg = LP["负面对照 (工单后持平)"];
return `${t("fus.loop_head")}
${t("fus.loop_note")}
${t("fus.loop_closed", { n: cl.length })}
| ${t("ui.unit")} | ${t("fus.col.part")} | ${t("fus.loop_item")} | ${t("fus.loop_date")} | ${t("fus.loop_ba")} | ${t("fus.loop_ratio")} | ${t("fus.loop_verdict")} |
${cl.map((r) => `| ${unitNo(r.turbine)} | ${chainMini(r.component + (r.title || ""), "var(--good)")} ${partName(r.component)} | ${dv(r.title)} | ${esc(r.replace_date)} | ${baBar(r.pre, r.post, "var(--good)")}${r.pre} → ${r.post} | ${r.ratio}× | ${dv(r.verdict)} |
`).join("")}
${t("fus.loop_neg", { n: neg })}
${t("fus.loop_rising", { n: up.length })}
| ${t("ui.unit")} | ${t("fus.col.part")} | ${t("fus.loop_el")} | ${t("fus.loop_x")} | ${t("fus.loop_state")} |
${up.map((r) => `| ${unitNo(r.turbine)} | ${chainMini(r.component, "var(--warning)")} ${partName(r.component)} | ${baBar(r.early, r.late, "var(--warning)")}${r.early} → ${r.late} | ${r.ratio}× | ${t("rpt.units.rising", { r: r.ratio })} |
`).join("")}
${t("fus.loop_bound", { b: dv(String(LP["★数据边界"] || "").split("**").join("")) })}
`;
}
const devBar = (ch, dev, vd, raw) => {
if (dev == null) return `${dv(displayText(raw).slice(0, 30))}`;
const M = 6, W = 104, mid = W / 2, k = Math.max(-1, Math.min(1, dev / M)), bw = Math.abs(k) * (mid - 8), strong = Math.abs(dev) >= 3;
const col = !strong ? "var(--muted)" : /候选|andidate/.test(vd || "") ? "var(--warning)" : /记基线|aseline/.test(vd || "") ? "var(--accent)" : /不可判|返服|ndeterm/.test(vd || "") ? "var(--unknown)" : "var(--muted)", over = Math.abs(dev) > M;
return `${dev > 0 ? "+" : ""}${dev}K ${esc(ch || "")}
`;
};
const winBar = (spans, raw) => {
if (!spans || !spans.length) return `${t("fus.win_none")}`;
const T0 = Date.parse("2026-01-01"), T1 = Date.parse("2026-09-01"), W = 118, px = (d) => 3 + Math.max(0, Math.min(1, (Date.parse(d) - T0) / (T1 - T0))) * (W - 6);
return `${t("fus.win_n", { n: spans.length })}
`;
};
const oilPill = (last, fresh, raw) => `${esc(last || t("fus.oil_none"))}
${last ? fresh ? t("fus.oil_fresh") : t("fus.oil_stale") : "—"}
`;
const stateChip = (zh, cls) => {
const k = stateKey(zh);
return k ? `${t(stKey(k))}` : zh ? `${esc(zh)}` : "";
};
function unitsHtml(F, filter) {
const tb = F.表 || [], mat = F.矩阵 || [];
let rows = tb;
const filt = "";
let h = `${t("fus.units_head", { u: new Set(tb.map((x) => x.turbine)).size, n: rows.length + "", f: filt })}
${["bad", "warn", "note", "ok"].map((k) => ``).join("")}
${t("fus.eval_head")}| ${t("fus.eval_state")} | ${t("fus.eval_tier")} | ${t("fus.eval_meaning")} |
| ${stateChip("危险")} | ${t("tier.confirmed.s")} | ${t("fus.m.bad")} (${t("fus.m.bad_e")}) |
| ${stateChip("报警")} | ${t("tier.near")} / ${t("tier.candidate_new")} | ${t("fus.m.warn")} (${t("fus.m.warn_e")}) |
| ${stateChip("良好")} | ${t("tier.candidate")} / ${t("tier.candidate_baseline")} | ${t("fus.m.ok")} (${t("fus.m.ok_e")}) |
| ${stateChip("优秀")} | ${t("tier.reference")} / ${t("tier.normal")} / ${t("tier.withdrawn")} | ${t("fus.m.ex")} (${t("fus.m.ex_e")}) |
| ${stateChip("不可判")} | ${t("tier.insufficient")} | ${t("fus.m.unk")} (${t("fus.m.unk_e")}) |
${t("fus.eval_note")}
| ${t("ui.unit")} | ${t("fus.col.part")} | ${t("fus.col.state_concl")} | ${t("fus.col.claim")} ${t("fus.col.claim_axis")} | ${t("fus.col.signal")} | ${t("fus.col.tempaxis")} ${t("fus.col.tempaxis_note")} | ${t("fus.col.oil")} |
`;
rows.forEach((r) => {
const sev = CLV[r.级] || "var(--accent)", col = CST[r.设备状态] || "var(--muted)";
h += `| ${unitNo(r.turbine)} | ${chainMini(r.部件, sev)} ${partName(r.部件)} |
${duo(stWord(r.设备状态 || "—"), dv(r.振动结论), col)} | ${winBar(r.窗区间, r.claim_window)} |
${dv(vibPlain(r.振动信号))} | ${devBar(r.温度通道, r.温度dev, r.温度判, r.温度轴)} | ${oilPill(r.油末样, r.油新鲜, r.油样轴)} |
`;
});
return h + `
`;
}
const CHAIN7 = [["mbf", /主轴承[((]前|主轴承前|主轴承[++]|主轴承(?![((前后])/], ["mba", /主轴承[((]后|主轴承后/], ["ps", /行星|二级行星/], ["ims", /中间轴|中速/], ["hss", /高速轴|高速(?!.*发电)/], ["gde", /发电机DE|发电机[((]|发电机(?!NDE)/], ["gnde", /发电机NDE|NDE/]];
const gc = (st) => STC[stateKey(st)] || null;
const chainStates = (map) => `${Object.entries(map).map(([k, col]) => ``).join("")}`;
const evLadder = (x) => {
const T = String(x || ""), n = /准定论|near[- ](?:conf|establish)/i.test(T) ? 3 : /确诊|onfirmed/.test(T) ? 4 : /定论|ettled|stablished/.test(T) ? 3 : /候选|andidate/.test(T) ? 2 : /参考|eference/.test(T) ? 1 : 0, col = n >= 4 ? "var(--critical)" : n === 3 ? "var(--warning)" : n === 2 ? "var(--accent)" : n === 1 ? "var(--muted)" : "var(--unknown)";
return `${dv(T)}
`;
};
function eventsHtml(F, d) {
if (!F.事件) return ``;
let h = `${t("fus.ev_win", { win: winName(d.win) })}
`;
for (const [comp, E0] of Object.entries(F.事件)) {
const E = E0;
const A = E.报警 || {}, W = E.工单 || {};
h += `${t("fus.ev_head", { c: sysName(comp), an: A.总条数 || 0, au: A.台数 || 0, wn: W.总条数 || 0, wu: W.台数 || 0 })}
`;
if (A.总条数 > 0) {
h += `
| ${t("fus.ev_code")} | ${t("fus.ev_text")} | ${t("ui.count")} | ${t("fus.ev_units")} | ${t("fus.ev_dur")} |
${(A.码统计 || []).map((c) => `| ${esc(c.code)} | ${dv(vibPlain(c.text))} | ${c.条数} | ${c.台数} | ${c.时长h} |
`).join("")}
${t("fus.ev_top")}${Object.entries(A.top台 || {}).map(([u, n]) => `${unitNo(u)}×${n}`).join(" · ")}
${t("fus.ev_recent", { n: (A.近况 || []).length })}
| ${t("ui.unit")} | ${t("fus.ev_time")} | ${t("fus.ev_code")} | ${t("fus.ev_text")} | ${t("fus.ev_min")} |
${(A.近况 || []).map((x) => `| ${unitNo(x.t)} | ${esc(x.on)} | ${esc(x.code)} | ${dv(vibPlain(x.text))} | ${x.dur_min ?? "—"} |
`).join("")}
`;
} else h += `
${t("fus.ev_no_alarm")}
`;
if (W.总条数 > 0) h += `
| ${t("ui.unit")} | ${t("ui.date")} | ${t("fus.ev_wo_name")} | ${t("fus.ev_wo_act")} | ${t("fus.ev_wo_cause")} |
${(W.明细 || []).map((x) => `| ${unitNo(x.t)} | ${esc(x.date)} | ${dv(x.name)} | ${dv(x.act)} | ${dv(x.cause)} |
`).join("")}
`;
else h += `
${t("fus.ev_no_wo")}
`;
h += `
`;
}
return h;
}
function cmsHtml(V, err) {
if (!V) return ``;
if (V.error) return ``;
const CK = {
"主轴承前": "mbf",
"主轴承后": "mba",
"齿轮箱": "gb",
"发电机": "gen",
"发电机DE": "gde",
"发电机NDE": "gnde",
"Main bearing, front": "mbf",
"Main bearing, rear": "mba",
"Gearbox": "gb",
"Generator": "gen",
"Generator DE": "gde",
"Generator NDE": "gnde"
};
return `${t("fus.cms_head", { d: esc(V.date || "") })}
${V["时间范围"] ? `
${t("fus.cms_window", { r: esc(V["时间范围"]) })}
` : ""}
${dv(V.overall || "")} ${t("fus.cms_note")}
${t("fus.cms_action")}
| ${t("ui.unit")} | ${t("fus.loop_state")} | ${t("fus.cms_focus")} ${t("fus.cms_focus_note")} | ${t("fus.cms_evidence")} | ${t("fus.cms_act")} |
${(V.action || []).map((r) => {
const cm = {};
String(r[2]).split(/[;;]/).forEach((seg) => {
const m = seg.split(/[::]/);
if (m.length > 1) {
const k = CK[m[0].trim()] || null;
if (k) cm[k] = gc(m[1].trim());
}
});
return `| ${unitNo(r[0])} | ${stateChip(r[1])} | ${chainStates(cm)} ${dv(r[2])} | ${evLadder(r[3])} | ${/P0/.test(r[4]) ? `${esc(r[4])}` : esc(r[4])} |
`;
}).join("")}
${t("fus.cms_grades")}
| ${t("ui.unit")} | ${t("fus.cms_chain")} ${t("fus.cms_chain_note")} | ${t("fus.cms_overall")} | ${t("fus.cms_act")} |
${(V.grades || []).map((g) => `| ${unitNo(g.t)} | ${chainStates({ mbf: gc(g.主轴承前), mba: gc(g.主轴承后), gb: gc(g.齿轮箱), gen: gc(g.发电机) })} | ${stateChip(g.综合)} | ${esc(g.行动)} |
`).join("")}
${t("fus.cms_basis")} ${t("fus.cms_basis_note", { a: ((V.diff || {}).only_cms || []).map(unitNo).join(", ") || t("fus.none_word"), b: ((V.diff || {}).only_vibration_package || (V.diff || {})["only_handoff"] || []).map(unitNo).join(", ") || t("fus.none_word") })}
${t("fus.cms_link")} ${t("fus.cms_open")} ${t("fus.cms_static")}
`;
}
const FUS_SUBS = ["matrix", "loop", "units", "events", "cms"];
const _sfc_main$7 = /* @__PURE__ */ defineComponent({
__name: "Vibration",
__ssrInlineRender: true,
props: {
d: {},
sub: {},
cms: {}
},
emits: ["sub"],
setup(__props, { emit: __emit }) {
const props = __props;
const sub = computed(() => props.sub || "matrix");
const F = computed(() => props.d?.fus);
const bodyHtml = computed(() => {
const f = F.value;
if (!f || f.err) return `${t("fus.unavailable", { e: String(f && f.err || "") })}
`;
if (sub.value === "matrix") return matrixHtml(f);
if (sub.value === "loop") return loopHtml(f);
if (sub.value === "units") return unitsHtml(f, null);
if (sub.value === "events") return eventsHtml(f, props.d);
if (sub.value === "cms") return cmsHtml(props.cms ?? null);
return `子视图「${t("fus.sub." + sub.value)}」尚未迁移到 Vue(迁移中)。
`;
});
return (_ctx, _push, _parent, _attrs) => {
_push(`${ssrInterpolate(unref(t)("fus.eyebrow"))}
`);
ssrRenderList(unref(FUS_SUBS), (s) => {
_push(``);
});
_push(`
${bodyHtml.value ?? ""}
`);
};
}
});
const _sfc_setup$7 = _sfc_main$7.setup;
_sfc_main$7.setup = (props, ctx) => {
const ssrContext = useSSRContext();
(ssrContext.modules || (ssrContext.modules = /* @__PURE__ */ new Set())).add("src/views/Vibration.vue");
return _sfc_setup$7 ? _sfc_setup$7(props, ctx) : void 0;
};
const _sfc_main$6 = /* @__PURE__ */ defineComponent({
__name: "Generation",
__ssrInlineRender: true,
props: {
d: {},
curves: {},
pending: { type: Boolean }
},
setup(__props) {
const props = __props;
const M9 = computed(() => props.d?.m9 && !props.d.m9.err ? props.d.m9 : null);
const F = computed(() => props.d?.control || null);
const GEN = computed(() => props.curves || null);
const sp = computed(() => (props.d?.sysmx_span || []).join(" ~ ") || "");
const stats = (spec) => {
const gs = Object.entries(spec?.分组 || {}).map(([k, v]) => ({ v: parseFloat(k), n: v, raw: k }));
if (!gs.length) return null;
const vals = gs.map((g) => g.v).sort((a, b) => a - b);
const med = vals.length % 2 ? vals[(vals.length - 1) / 2] : (vals[vals.length / 2 - 1] + vals[vals.length / 2]) / 2;
return { gs, med };
};
const capStats = computed(() => ({ p: stats(M9.value?.P封顶), w: stats(M9.value?.ω封顶) }));
const capOption = computed(() => {
const P = capStats.value.p, W9 = capStats.value.w;
const series = [];
if (P) series.push({ name: t("ch.cap.p"), type: "scatter", data: P.gs.map((g) => [g.v, 1]), symbolSize: 10, itemStyle: { color: "#0b5fa5", opacity: 0.6 } });
if (W9) series.push({ name: t("ch.cap.w"), type: "scatter", data: W9.gs.map((g) => [g.v, 0]), symbolSize: 10, itemStyle: { color: "#b45309", opacity: 0.6 } });
return {
grid: { left: 56, right: 36, top: 20, bottom: 24 },
xAxis: { type: "value" },
yAxis: { type: "value", min: -0.5, max: 1.5, show: false },
series,
// 旧 capAxis 只把刻度与中位注解写成 SVG 文字(那是图自身算的注解)⇒ 不计入我画的数据
__data__: []
};
});
const betaOuts = computed(() => M9.value?.β_schedule?.离群 || []);
const betaMed = computed(() => M9.value?.β_schedule?.["全场同档差中位"] || 0);
const betaOption = computed(() => ({
grid: { left: 56, right: 60, top: 20, bottom: 24 },
xAxis: { type: "value" },
yAxis: { type: "category", data: betaOuts.value.map((o) => unitNo(o.t)) },
series: [{ type: "bar", data: betaOuts.value.map((o) => o.平均偏), itemStyle: { color: "#0b5fa5" } }],
__data__: []
// 同上:β 图中位是注解
}));
const candsHtml = computed(() => {
const cs = F.value?.curve?.cands || [];
return cs.length ? cs.map((c) => `${unitNo(c.t)} ${c.dev}% ${dv(c.判)}`).join(" | ") : t("gen.no_cands");
});
const betaNoteHtml = computed(() => t("gen.beta_note", {
units: betaOuts.value.map((x) => `${unitNo(x.t)}`).join(" / "),
k: betaOuts.value.length ? Math.round(Math.max(...betaOuts.value.map((x) => x.平均偏)) / Math.max(betaMed.value || 0, 0.01)) : "—",
v: dv(M9.value?.β_schedule?.判)
}));
const rulerRows = computed(() => Object.entries(M9.value?.尺子 || {}).map(([k, v]) => ({ k, r: v.极差 / v.σ })));
const rulerOption = computed(() => ({
grid: { left: 100, right: 80, top: 20, bottom: 24 },
xAxis: { type: "value" },
yAxis: { type: "category", data: rulerRows.value.map((r) => r.k) },
series: [{ type: "bar", data: rulerRows.value.map((r) => r.r), itemStyle: { color: "#0b5fa5" } }],
__data__: []
// 同上:规范线是注解
}));
const figs = computed(() => GEN.value?.figs || []);
function figOption(f) {
const months = f.months || f.x || [];
return {
grid: { left: 56, right: 20, top: 28, bottom: 40 },
legend: { top: 0, textStyle: { fontSize: 10 } },
xAxis: { type: "category", data: months },
yAxis: { type: "value", scale: true },
series: (f.series || []).map((s) => ({ name: String(s.name), type: "line", data: s.vals, connectNulls: false })),
__data__: []
};
}
return (_ctx, _push, _parent, _attrs) => {
_push(`${ssrInterpolate(unref(t)("gen.eyebrow"))}
${ssrInterpolate(unref(t)("gen.fixed_win"))} ${ssrInterpolate(sp.value ? unref(t)("gen.win_range", { span: sp.value }) : "")}
${ssrInterpolate(unref(t)("gen.win_why"))}
`);
if (__props.d.m9 && __props.d.m9.err) {
_push(`${ssrInterpolate(unref(t)("flt.block_err", { k: "m9", e: unref(dv)(__props.d.m9.err) }))}
`);
} else {
_push(``);
}
if (M9.value) {
_push(`${ssrInterpolate(unref(t)("gen.ctrl"))} ${ssrInterpolate(unref(t)("gen.ctrl_win", { win: unref(dv)(M9.value.窗) }))}`);
if (__props.pending) {
_push(`${ssrInterpolate(unref(t)("gen.recomputing_short"))}`);
} else {
_push(``);
}
_push(`
${ssrInterpolate(unref(t)("gen.cap"))}
`);
_push(ssrRenderComponent(EChart, {
option: capOption.value,
height: "170px"
}, null, _parent));
_push(`
${ssrInterpolate(unref(t)("gen.cap_note"))}
${ssrInterpolate(unref(t)("gen.beta"))}
`);
_push(ssrRenderComponent(EChart, {
option: betaOption.value,
height: "170px"
}, null, _parent));
_push(`
${betaNoteHtml.value ?? ""}
${ssrInterpolate(unref(t)("gen.ruler"))}
`);
_push(ssrRenderComponent(EChart, {
option: rulerOption.value,
height: "170px"
}, null, _parent));
_push(`
${ssrInterpolate(unref(t)("gen.ruler_note"))}
`);
} else {
_push(``);
}
if (F.value && F.value.curve) {
_push(`${ssrInterpolate(unref(t)("gen.cands"))} ${ssrInterpolate(unref(t)("gen.cands_meta", { note: unref(dv)(F.value.curve.note || ""), s: __props.d.sigma || F.value.curve.sigma }))}
${candsHtml.value ?? ""}
`);
} else {
_push(``);
}
_push(`${ssrInterpolate(unref(t)("gen.figs"))}
`);
if (GEN.value && GEN.value.err) {
_push(`${ssrInterpolate(unref(esc)(GEN.value.err))}
`);
} else if (GEN.value && GEN.value.building) {
_push(`${ssrInterpolate(unref(t)("gen.recomputing"))} ${ssrInterpolate(unref(dv)(GEN.value.curves?.note || ""))}
`);
} else if (!GEN.value) {
_push(`${ssrInterpolate(unref(t)("gen.loading"))}
`);
} else {
_push(``);
ssrRenderList(figs.value, (f, i) => {
_push(`
${ssrInterpolate(unref(dv)(f.title || ""))}`);
if (f.unit) {
_push(`(${ssrInterpolate(unref(dv)(f.unit))})`);
} else {
_push(``);
}
_push(`
`);
_push(ssrRenderComponent(EChart, {
option: figOption(f),
height: "260px"
}, null, _parent));
_push(`
`);
ssrRenderList(f.series || [], (s, k) => {
_push(`${ssrInterpolate(String(s.name))}`);
});
_push(`
`);
if (f.note) {
_push(`
${ssrInterpolate(unref(dv)(f.note))}
`);
} else {
_push(``);
}
_push(`
`);
});
_push(`
`);
}
_push(``);
};
}
});
const _sfc_setup$6 = _sfc_main$6.setup;
_sfc_main$6.setup = (props, ctx) => {
const ssrContext = useSSRContext();
(ssrContext.modules || (ssrContext.modules = /* @__PURE__ */ new Set())).add("src/views/Generation.vue");
return _sfc_setup$6 ? _sfc_setup$6(props, ctx) : void 0;
};
const _sfc_main$5 = /* @__PURE__ */ defineComponent({
__name: "Decision",
__ssrInlineRender: true,
props: {
d: {},
apis: {}
},
setup(__props) {
const props = __props;
const CATS = [
["equip", "设备类"],
["sched", "检修排程决策"],
["loss", "损失分析"],
["risk", "安全风险预测"]
];
const MDL_LOC = { "qwen3:8b": "local", "qwen3.8:27b": "local", "qwen3:30b-a3b": "local", "deepseek-r1:14b": "local", "deepseek-r1:32b": "local", "deepseek-chat": "cloud", "kimi-fast": "cloud", "qwen-plus": "cloud", "deepseek-v4-pro": "cloud" };
const cat = ref("equip");
const q = ref("");
const model = ref("");
const out = ref("");
const askModels = computed(() => props.apis?.["/detail/api/ask_models"] ?? props.apis?.["/api/ask_models"] ?? null);
const models = computed(() => askModels.value ? askModels.value.models || {} : null);
const localDown = computed(() => !!askModels.value?.offline || !!askModels.value?.LOCAL_AI_DOWN);
const catName = computed(() => t("cat." + cat.value));
const pills = computed(() => CATS.map(([id]) => ``).join(""));
const qs = computed(() => [1, 2, 3].map((i) => `${t("cat." + cat.value + ".q" + i)}
`).join(""));
const mdlKey = (id, f) => t("mdl." + id + "." + f);
const mdlLabel = (id) => (mdlKey(id, "nm") || id) + (MDL_LOC[id] ? " · " + t("mdl." + MDL_LOC[id]) : "") + (mdlKey(id, "t") ? " · " + mdlKey(id, "t") : "");
const mdlNote = (id) => !MDL_LOC[id] ? `${t("mdl.unknown_warn")}` : MDL_LOC[id] === "local" ? t("mdl.zero_outbound") : `${t("mdl.outbound_warn")}`;
const effectiveModel = computed(() => {
const m = models.value;
if (m === null) return model.value;
const ks = Object.keys(m);
return !model.value || !ks.includes(model.value) ? ks[0] || "" : model.value;
});
const opts = computed(() => {
const m = models.value;
if (m === null) return '';
return Object.keys(m).length ? Object.keys(m).map((k) => ``).join("") : ``;
});
const classicHref = computed(() => `/?lang=zh&win=${encodeURIComponent(String(props.d?.win || ""))}`);
const mdlNoteHtml = computed(() => effectiveModel.value ? mdlNote(effectiveModel.value) : "");
return (_ctx, _push, _parent, _attrs) => {
_push(``);
};
}
});
const _sfc_setup$5 = _sfc_main$5.setup;
_sfc_main$5.setup = (props, ctx) => {
const ssrContext = useSSRContext();
(ssrContext.modules || (ssrContext.modules = /* @__PURE__ */ new Set())).add("src/views/Decision.vue");
return _sfc_setup$5 ? _sfc_setup$5(props, ctx) : void 0;
};
const row = "display:flex;gap:8px;align-items:center;flex-wrap:wrap;margin-top:8px";
const _sfc_main$4 = /* @__PURE__ */ defineComponent({
__name: "Assistant",
__ssrInlineRender: true,
setup(__props) {
const SYSOPT = ["变桨", "偏航", "主轴承", "齿轮箱", "发电机", "变流器", "塔架与基础", "主控与传感网", "叶片与叶根"];
const HOT = ["3225", "3130", "13902", "10105", "3224"];
const ASST = reactive({ fault: "", prev: "pitch", tree: "pitch", out: { fault: "", prev: "", tree: "" } });
const selOptions = (id) => SYSOPT.map((s) => ``).join("");
const hotHtml = computed(() => HOT.map((c) => `${c}`).join(" · "));
return (_ctx, _push, _parent, _attrs) => {
_push(`${ssrInterpolate(unref(t)("asst.head"))} — ${ssrInterpolate(unref(t)("asst.head_note"))}
${ssrInterpolate(unref(t)("asst.preventive"))} ${ssrInterpolate(unref(t)("asst.preventive_flow"))}
${ssrInterpolate(ASST.out.prev)}
${ssrInterpolate(unref(t)("asst.faulttree"))} ${ssrInterpolate(unref(t)("asst.faulttree_flow"))}
${ssrInterpolate(ASST.out.tree)}
`);
};
}
});
const _sfc_setup$4 = _sfc_main$4.setup;
_sfc_main$4.setup = (props, ctx) => {
const ssrContext = useSSRContext();
(ssrContext.modules || (ssrContext.modules = /* @__PURE__ */ new Set())).add("src/views/Assistant.vue");
return _sfc_setup$4 ? _sfc_setup$4(props, ctx) : void 0;
};
const _sfc_main$3 = /* @__PURE__ */ defineComponent({
__name: "System",
__ssrInlineRender: true,
props: {
apis: {}
},
setup(__props) {
const props = __props;
const publicMeta = (x) => String(x == null ? "" : x);
const md = computed(() => props.apis?.["/detail/api/maint_survey"] ?? props.apis?.["/api/maint_survey"] ?? null);
const LAYERS = [
[["数据", "Data"], "mt.layer_data", "mt.layer_data_note", "var(--accent)"],
[["机理", "Domain model", "Mechanism"], "mt.layer_mech", "mt.layer_mech_note", "var(--warning)"]
];
const rows = computed(() => md.value?.rows || []);
const groups = computed(() => LAYERS.map(([lvs, kn, kd, col]) => ({ kn, kd, col, rs: rows.value.filter((r) => lvs.includes(String(r.层))) })).filter((g) => g.rs.length));
return (_ctx, _push, _parent, _attrs) => {
_push(`${ssrInterpolate(unref(t)("mt.eyebrow"))}
`);
if (!md.value) {
_push(`${ssrInterpolate(unref(t)("mt.surveying"))}
`);
} else if (md.value.error) {
_push(`${ssrInterpolate(unref(dv)(md.value.error))}
`);
} else {
_push(`${ssrInterpolate(unref(t)("mt.head", { n: rows.value.length }))}
${ssrInterpolate(unref(t)("mt.note"))}
`);
ssrRenderList(groups.value, (g) => {
_push(`${ssrInterpolate(unref(t)(g.kn))} (${ssrInterpolate(g.rs.length)})
${ssrInterpolate(unref(t)(g.kd))}
| ${ssrInterpolate(unref(t)("mt.src"))} | ${ssrInterpolate(unref(t)("mt.cov"))} | ${ssrInterpolate(unref(t)("mt.last"))} | ${ssrInterpolate(unref(t)("mt.freq"))} | ${ssrInterpolate(unref(t)("mt.resp"))} | ${ssrInterpolate(unref(t)("mt.how"))} |
`);
ssrRenderList(g.rs, (r, i) => {
_push(`| ${ssrInterpolate(r.存在 ? "" : "✗ ")}${ssrInterpolate(publicMeta(r.名))} | ${ssrInterpolate(publicMeta(r.覆盖))}`);
if (r.条数) {
_push(`${ssrInterpolate(unref(t)("mt.rows_n", { n: unref(fmt)(r.条数) }))}`);
} else {
_push(``);
}
_push(` | ${ssrInterpolate(unref(esc)(r.末次更新 || "—"))} | ${ssrInterpolate(publicMeta(r.频率))} | ${ssrInterpolate(publicMeta(r.责任))} | ${ssrInterpolate(unref(t)("mt.put"))} ${ssrInterpolate(publicMeta(r.位置))} ${ssrInterpolate(unref(t)("mt.run"))} ${ssrInterpolate(publicMeta(r.摄入))} ${ssrInterpolate(publicMeta(r.说明))} |
`);
});
_push(`
`);
});
_push(``);
}
_push(``);
};
}
});
const _sfc_setup$3 = _sfc_main$3.setup;
_sfc_main$3.setup = (props, ctx) => {
const ssrContext = useSSRContext();
(ssrContext.modules || (ssrContext.modules = /* @__PURE__ */ new Set())).add("src/views/System.vue");
return _sfc_setup$3 ? _sfc_setup$3(props, ctx) : void 0;
};
const _sfc_main$2 = /* @__PURE__ */ defineComponent({
__name: "Report",
__ssrInlineRender: true,
props: {
d: {},
apis: {}
},
setup(__props) {
const props = __props;
const RPT_PRESETS = { month: ["kpi", "water", "emon", "blame", "ram", "pareto", "units", "plan", "caliber"], week: ["kpi", "frate", "units", "plan", "caliber"], topic: ["units", "chain", "ram", "loop", "caliber"], year: ["kpi", "water", "emon", "ram", "loop", "caliber"], blank: [] };
const RPT_BLOCKS = { kpi: "gen", water: "gen", emon: "gen", blame: "gen", ram: "rel", parts: "rel", attrib: "rel", pareto: "flt", quad: "flt", frate: "flt", units: "unit", chain: "unit", loop: "unit", plan: "end", caliber: "end", narr: "end" };
const RPT_ICON = { kpi: "▦", water: "▤", emon: "▥", blame: "◧", ram: "◈", parts: "▤", attrib: "▬", pareto: "▮", quad: "◇", frate: "▪", units: "☰", chain: "⛓", loop: "✓", plan: "✎", caliber: "⚑", narr: "✍" };
const MDL_LOC = { "qwen3:8b": "local", "qwen3.8:27b": "local", "qwen3:30b-a3b": "local", "deepseek-r1:14b": "local", "deepseek-r1:32b": "local", "deepseek-chat": "cloud", "kimi-fast": "cloud", "qwen-plus": "cloud", "deepseek-v4-pro": "cloud" };
const RPT_GROUPS = ["gen", "rel", "flt", "unit", "end"];
const RPT = reactive({
canvas: [],
aud: "in",
need: "",
model: "",
models: null,
compose: null,
narr: null,
out: ""
});
const askModels = computed(() => props.apis?.["/detail/api/ask_models"] ?? props.apis?.["/api/ask_models"] ?? null);
const models = computed(() => askModels.value ? askModels.value.models || {} : RPT.models);
const localDown = computed(() => !!askModels.value?.offline);
const mdlKey = (id, f) => t("mdl." + id + "." + f);
const mdlLabel = (id) => (mdlKey(id, "nm") || id) + (MDL_LOC[id] ? " · " + t("mdl." + MDL_LOC[id]) : "") + (mdlKey(id, "t") ? " · " + mdlKey(id, "t") : "");
const on = (k) => RPT.canvas.includes(k);
const canCompose = computed(() => RPT.compose === true && !!models.value && Object.keys(models.value).length > 0);
const opts = computed(() => {
const m = models.value;
if (m === null) return '';
return Object.keys(m).length ? Object.keys(m).map((k) => ``).join("") : ``;
});
const libraryHtml = computed(() => RPT_GROUPS.map((g) => `${t("rpt.g." + g)}
` + Object.keys(RPT_BLOCKS).filter((k) => RPT_BLOCKS[k] === g).map((k) => ``).join("") + `
`).join(""));
const factsHtml = computed(() => "");
const blockHtml = (_k) => "";
const effectiveModel = computed(() => {
const m = models.value;
if (m === null) return RPT.model;
const ks = Object.keys(m);
return !RPT.model || !ks.includes(RPT.model) ? ks[0] || "" : RPT.model;
});
const needHint = computed(() => t("rpt.need_hint") + (effectiveModel.value ? MDL_LOC[effectiveModel.value] === "local" ? t("rpt.local_note") : `${t("rpt.cloud_note")}` : ""));
const outText = computed(() => {
if (RPT.out) return RPT.out;
if (RPT.compose === false) return t("rpt.no_compose");
if (models.value && !Object.keys(models.value).length) return localDown.value ? t("ask.local_down") : t("rpt.no_models");
return "";
});
const asof = computed(() => (String(props.d?.fus?.数据时点 || "").match(/\d{4}-\d{2}-\d{2}/) || [""])[0]);
return (_ctx, _push, _parent, _attrs) => {
_push(`${ssrInterpolate(unref(t)("rpt.eyebrow"))}
${ssrInterpolate(unref(t)("rpt.presets"))}`);
ssrRenderList(Object.keys(RPT_PRESETS), (k) => {
_push(``);
});
_push(``);
if (RPT.aud === "up") {
_push(`${ssrInterpolate(unref(t)("rpt.up_note"))}`);
} else {
_push(``);
}
_push(`
${ssrInterpolate(unref(t)("rpt.library"))} ${ssrInterpolate(unref(t)("rpt.library_note", { n: RPT.canvas.length }))}
${libraryHtml.value ?? ""}
${ssrInterpolate(unref(t)("rpt.hint_idle"))}
${ssrInterpolate(unref(t)("rpt.need_head"))} ${needHint.value ?? ""}
${ssrInterpolate(outText.value)}
`);
if (!RPT.canvas.length) {
_push(`${ssrInterpolate(unref(t)("rpt.preview_empty"))}
`);
} else {
_push(`${ssrInterpolate(unref(t)("app.brand"))} · ${ssrInterpolate(unref(t)("rpt.paper_title"))}
${ssrInterpolate(unref(t)("rpt.paper_sub", { win: unref(winName)(__props.d.win), n: (__props.d.kpi || {}).全场 || 38 }))}${ssrInterpolate(RPT.aud === "up" ? " | " + unref(t)("rpt.up_tag") : "")}
${ssrInterpolate(unref(t)("rpt.chapters", { n: RPT.canvas.length }))}
`);
ssrRenderList(RPT.canvas, (k, i) => {
_push(``);
if (RPT_BLOCKS[k]) {
_push(`
`);
} else {
_push(``);
}
if (RPT_BLOCKS[k]) {
_push(`
${blockHtml()}
`);
} else {
_push(``);
}
_push(``);
});
_push(`
${factsHtml.value ?? ""}
${ssrInterpolate(unref(t)("rpt.foot_src"))}${ssrInterpolate(asof.value ? " | " + unref(t)("rpt.foot_asof", { d: asof.value }) : "")}${ssrInterpolate(unref(t)("rpt.foot_trace"))}${ssrInterpolate(RPT.aud === "up" ? " | " + unref(t)("rpt.foot_deid") : "")}
`);
}
_push(``);
};
}
});
const _sfc_setup$2 = _sfc_main$2.setup;
_sfc_main$2.setup = (props, ctx) => {
const ssrContext = useSSRContext();
(ssrContext.modules || (ssrContext.modules = /* @__PURE__ */ new Set())).add("src/views/Report.vue");
return _sfc_setup$2 ? _sfc_setup$2(props, ctx) : void 0;
};
const _sfc_main$1 = /* @__PURE__ */ defineComponent({
__name: "Overview",
__ssrInlineRender: true,
props: {
d: {}
},
setup(__props) {
const props = __props;
const F = computed(() => props.d?.fus || {});
const M = computed(() => props.d?.m8?.mtbf || {});
const C = computed(() => props.d?.control || {});
const LS = computed(() => {
const out = {};
for (const x of C.value.states || []) out[x.k] = x.loss;
return out;
});
const kpi = computed(() => {
const c = C.value;
const lossLabel = t("kpi.loss", { win: winName(props.d?.win) }) + (LS.value["停机(调度令)"] != null ? ` · ${t("kpi.loss_dispatch", { n: fmt(LS.value["停机(调度令)"]) })}` : "");
return [
{ v: fmt(c.avail, 1), u: "%", l: t("kpi.avail", { win: winName(props.d?.win) }), k: c.avail >= 95 ? "ok" : "warn", ti: "" },
{ v: fmt(M.value.停机总时长h), u: "h", l: t("kpi.downtime", { n: fmt(M.value.停机事件数) }), k: "warn", ti: "" },
{ v: fmt(M.value.MTBF_h), u: "h", l: t("kpi.mtbo"), k: "", ti: "" },
{ v: fmt(LS.value["停机"]), u: "MWh", l: lossLabel, k: "", ti: "" },
{ v: fmt(LS.value["限电·绑定"]), u: "MWh", l: t("kpi.curtail"), k: "", ti: "" },
{
// 旧模板 `${fmt(报警台)} / ${fmt(全场)}` —— `vHtml` 保留 元素与节点边界
vRaw: `${fmt(props.d?.kpi?.报警台)} / ${fmt(props.d?.kpi?.全场)}`,
u: "",
l: t("kpi.alarm_units"),
k: props.d?.kpi?.报警台 ? "warn" : "ok",
ti: t("kpi.alarm_units.hint"),
vHtml: true
}
];
});
const why = computed(() => {
const out = {};
for (const w of props.d?.watch || []) out[w.t] = w.why;
return out;
});
const rows = computed(() => ((F.value.链盘 || {}).rows || []).filter((r) => r.lvl === "bad" || r.lvl === "warn"));
function basis(r) {
const own = (why.value[r.t] || "").trim();
if (own) return own;
const stuckKey = STEP[r.stuck] || String(r.stuck || "").toLowerCase();
const stuckStep = stuckKey === "evidence" ? t("step.site_records") : t("step." + (stuckKey || "evidence"));
return t("ov.basis_fusion", {
part: r.part || "—",
done: r.done == null ? "—" : r.done,
n: (r.steps || []).length || "—",
stuck: r.stuck ? stuckStep : t("ov.all_steps_done"),
srcs: (r.srcs || []).join(" / ") || t("ov.no_srcs")
});
}
function stuckText(r) {
if (!r.stuck) return "";
const stuckKey = STEP[r.stuck] || String(r.stuck || "").toLowerCase();
const stuckStep = stuckKey === "evidence" ? t("step.site_records") : t("step." + (stuckKey || "evidence"));
return t("step.stuck_at", { step: stuckStep });
}
const SYS_SEG = [["危险", "critical"], ["报警", "warning"], ["不可判", "unknown"], ["良好", "good"], ["优秀", "excellent"]];
const systems = computed(() => {
const dist = props.d?.sysdist || {};
return SYS9.map((s) => {
const c = dist[s] || {};
const n = Object.values(c).reduce((a, b) => a + b, 0) || 1;
return { name: sysName(s), slug: sysSlug(s), dist: c, n, seg: SYS_SEG.filter(([k]) => c[k]).map(([k, v]) => ({ k, v })) };
});
});
const mat = computed(() => F.value.矩阵 || []);
const hot = computed(() => new Set(rows.value.map((r) => r.t)));
return (_ctx, _push, _parent, _attrs) => {
_push(``);
ssrRenderList(kpi.value, (k, i) => {
_push(`
`);
if (k.vHtml) {
_push(`${k.vRaw ?? ""}`);
} else {
_push(`${ssrInterpolate(k.v)}`);
if (k.u) {
_push(`${ssrInterpolate(k.u)}`);
} else {
_push(``);
}
_push(``);
}
_push(`${ssrInterpolate(k.l)}
`);
});
_push(`
${ssrInterpolate(unref(t)("ov.attention"))}
${ssrInterpolate(unref(t)("ov.attention_n", { n: rows.value.length }))}${ssrInterpolate(unref(t)("ov.ranked_by"))}`);
if (F.value.gap) {
_push(`${ssrInterpolate(F.value.gap)}
${ssrInterpolate(unref(t)("ov.fus_gap_hint"))}
`);
} else {
_push(``);
}
_push(``);
ssrRenderList(rows.value, (r) => {
_push(`
${ssrInterpolate(unref(t)("ov.evidence"))}
${ssrInterpolate(basis(r))}
`);
if (r.estate) {
_push(`${ssrInterpolate(unref(stWord$1)(r.estate))}`);
} else {
_push(``);
}
if (r.srcs && r.srcs.length) {
_push(`${ssrInterpolate(r.srcs.length)} sources`);
} else {
_push(``);
}
_push(`
`);
if (stuckText(r) || r.next) {
_push(`
${ssrInterpolate(unref(t)("ov.caveat"))}${ssrInterpolate(stuckText(r))}${ssrInterpolate(stuckText(r) && r.next ? " — " : "")}${ssrInterpolate(r.next || "")}
`);
} else {
_push(``);
}
_push(`
`);
});
_push(`
${ssrInterpolate(unref(t)("ov.strip"))}
${ssrInterpolate(unref(t)("ov.strip_hint"))}`);
ssrRenderList(["bad", "warn", "unk", "ok", "ex"], (k) => {
_push(`${ssrInterpolate(unref(t)(unref(stKey$1)(k)))}`);
});
_push(`
`);
if (mat.value.length) {
_push(``);
ssrRenderList(mat.value, (r) => {
_push(`
`);
});
_push(``);
} else {
_push(`
${ssrInterpolate(F.value.gap || unref(t)("ov.fus_gap_hint"))}
`);
}
_push(`
`);
ssrRenderList(mat.value, (r) => {
_push(`${ssrInterpolate(unref(unitNo)(r.turbine).replace("#", ""))}`);
});
_push(`
${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 }))}
${ssrInterpolate(unref(t)("ov.systems"))}
${ssrInterpolate(unref(t)("ov.systems_hint"))}`);
ssrRenderList(systems.value, (s) => {
_push(`
`);
ssrRenderList(s.seg, (k) => {
_push(``);
});
_push(`
`);
ssrRenderList(s.seg, (k) => {
_push(`${ssrInterpolate(s.dist[k.k])}`);
});
_push(`
`);
});
_push(`
`);
};
}
});
const _sfc_setup$1 = _sfc_main$1.setup;
_sfc_main$1.setup = (props, ctx) => {
const ssrContext = useSSRContext();
(ssrContext.modules || (ssrContext.modules = /* @__PURE__ */ new Set())).add("src/views/Overview.vue");
return _sfc_setup$1 ? _sfc_setup$1(props, ctx) : void 0;
};
const _sfc_main = /* @__PURE__ */ defineComponent({
__name: "Workbench",
__ssrInlineRender: true,
props: {
d: {},
tab: {},
sys: {},
sub: {},
cms: {},
curves: {},
apis: {}
},
emits: ["tab", "sub"],
setup(__props, { emit: __emit }) {
const props = __props;
const emit = __emit;
const pending = computed(() => !DONE.includes(props.tab));
return (_ctx, _push, _parent, _attrs) => {
_push(`${ssrInterpolate(unref(t)("tab." + __props.tab))}
${ssrInterpolate(unref(t)("win.label"))} · ${ssrInterpolate(unref(winName)(__props.d?.win))}`);
if (__props.tab === "overview") {
_push(ssrRenderComponent(_sfc_main$1, { d: __props.d }, null, _parent));
} else if (__props.tab === "energy") {
_push(ssrRenderComponent(_sfc_main$9, { d: __props.d }, null, _parent));
} else if (__props.tab === "component") {
_push(ssrRenderComponent(_sfc_main$b, {
d: __props.d,
sys: __props.sys
}, null, _parent));
} else if (__props.tab === "fault") {
_push(ssrRenderComponent(_sfc_main$8, { d: __props.d }, null, _parent));
} else if (__props.tab === "vibration") {
_push(ssrRenderComponent(_sfc_main$7, {
d: __props.d,
sub: __props.sub,
cms: __props.cms,
onSub: ($event) => emit("sub", $event)
}, null, _parent));
} else if (__props.tab === "generation") {
_push(ssrRenderComponent(_sfc_main$6, {
d: __props.d,
curves: __props.curves,
pending: !!__props.d.m9_pending
}, null, _parent));
} else if (__props.tab === "decision") {
_push(ssrRenderComponent(_sfc_main$5, {
d: __props.d,
apis: __props.apis
}, null, _parent));
} else if (__props.tab === "assistant") {
_push(ssrRenderComponent(_sfc_main$4, { d: __props.d }, null, _parent));
} else if (__props.tab === "system") {
_push(ssrRenderComponent(_sfc_main$3, { apis: __props.apis }, null, _parent));
} else if (__props.tab === "report") {
_push(ssrRenderComponent(_sfc_main$2, {
d: __props.d,
apis: __props.apis
}, null, _parent));
} else if (__props.tab === "documents") {
_push(ssrRenderComponent(_sfc_main$f, null, null, _parent));
} else if (__props.tab === "recalc") {
_push(ssrRenderComponent(_sfc_main$e, null, null, _parent));
} else if (__props.tab === "portal") {
_push(ssrRenderComponent(_sfc_main$d, null, null, _parent));
} else if (__props.tab === "portal_sec") {
_push(ssrRenderComponent(_sfc_main$c, null, null, _parent));
} else if (pending.value) {
_push(`${ssrInterpolate(unref(t)("tab." + __props.tab))} (本页签尚未迁移到 Vue)
迁移纪律:页签逐个搬,每搬一个就用 tools/render_parity.mjs 与重构前页面 (现网 /detail/v2)对拍文案/数值/类名序列,**通过后**才删对应的 Python 前端源码。 当前已迁移:${ssrInterpolate(unref(DONE).join("、"))}。
`);
} else {
_push(``);
}
_push(``);
};
}
});
const _sfc_setup = _sfc_main.setup;
_sfc_main.setup = (props, ctx) => {
const ssrContext = useSSRContext();
(ssrContext.modules || (ssrContext.modules = /* @__PURE__ */ new Set())).add("src/views/Workbench.vue");
return _sfc_setup ? _sfc_setup(props, ctx) : void 0;
};
const Workbench = /* @__PURE__ */ _export_sfc(_sfc_main, [["__scopeId", "data-v-b73286b2"]]);
async function renderTab(d, tab = "overview", sub, cms, curves, apis) {
const app = createSSRApp(Workbench, { d, tab, sub: sub ?? null, cms: cms ?? null, curves: curves ?? null, apis: apis ?? null });
return await renderToString(app);
}
export {
renderTab
};