diff --git a/DESIGN.md b/DESIGN.md index a3e6280..66a092c 100644 --- a/DESIGN.md +++ b/DESIGN.md @@ -32,7 +32,7 @@ Use `Segoe UI`, `Microsoft YaHei`, then system sans-serif. Numeric log paths and - Desktop application: fixed top operation bar, source-context switch, contextual left navigation, sticky filter row, and one active analysis module in the main work area. The source switch offers only the detected modes in the order 综合, 监控log, 工控log. Combined and work-control contexts each expose two modules; segmentation remains an internal calculation rather than a navigation destination. - The primary desktop viewport is 1440×900 with a 960×640 minimum. Below 900px, module navigation becomes horizontal. -- Public site: generous editorial sections with a split hero, an interactive three-scope explanation, and a full-width five-module monitor-log guide. On desktop the guide uses a client view beside its explanation panel; at ≤1050px the explanation moves below and focuses/reveals after a hotspot is selected. Privacy and installation sections follow. +- Public site: generous editorial sections with a split hero followed by one interactive workbench that recreates the desktop operation bar, source switch, module navigation, filters and active analysis view. Combined, monitor-log and work-control scopes share this workbench instead of appearing as separate explanations. On desktop the client view sits beside its explanation panel; at ≤1050px the explanation moves below and focuses/reveals after a hotspot is selected. Privacy and installation sections follow. - Use spacing in a 4px-derived rhythm. Dense controls may use 8–12px gaps; sections use 24–40px; public-site sections use 64px or more. ## Components diff --git a/site/demo.js b/site/demo.js index 5b3faed..f701eb8 100644 --- a/site/demo.js +++ b/site/demo.js @@ -7,14 +7,21 @@ const hotspots = [...document.querySelectorAll("[data-guide-hotspot]")]; const scopeTabs = [...document.querySelectorAll("[data-site-scope]")]; const moduleFirstHotspot = { - overview: "overview-review", - theory: "theory-band", - optical: "optical-endpoint", - device: "device-metric", - anomaly: "anomaly-metric" + combined: { overview: "combined-alignment", device: "combined-device" }, + monitor: { overview: "overview-review", theory: "theory-band", optical: "optical-endpoint", device: "device-metric", anomaly: "anomaly-metric" }, + machine: { overview: "machine-samples", device: "machine-device" } }; const details = { + "scope-combined": ["综合", "双源关联", "自动匹配工控工作时段与监控镀膜层,并校正时钟偏移。", "需要在同一时间或层号范围内核对两类日志时使用。", "选择综合,再通过总览确认关联状态并进入设备模块。", "关联失败时不会强制合并,两类日志仍可独立分析。"], + "scope-monitor": ["监控log", "逐层分析", "关联理论、光学、设备记录和统计复核提示。", "需要按镀膜层复核理论设定、光学曲线或设备变化时使用。", "选择监控log,再从左侧进入五个逐层分析模块。", "监控log以层为核心,不包含连续工控时间轴。"], + "scope-machine": ["工控log", "连续设备记录", "合并连续日期文件并展示设备的实际采样点。", "需要核对功率、真空、温度、气体或转速的连续变化时使用。", "选择工控log,再按材料、区段和设备指标筛选。", "仅有工控log时不推测镀膜层;双源关联后可显示对应层。"], + "combined-alignment": ["综合总览", "双源关联", "汇总两类日志的完整性、镀膜层匹配和时钟偏移。", "开始跨源设备对照前确认关联结果时使用。", "查看关联状态、匹配层数和时钟偏移,再进入设备模块。", "关联状态描述数据匹配结果,不代表工艺质量。"], + "combined-motor": ["综合总览", "转速来源", "综合模式以工控log的 Motor Speed 作为逐层转速。", "核对监控log与工控log转速差异时使用。", "查看工控log权威值,并保留监控log参考值用于审计。", "其他设备字段不互相覆盖,仍按数据源分别展示。"], + "combined-device": ["综合设备", "双源设备对照", "按镀膜层或时间对照工控log与监控log设备数据。", "排查同一层或同一时刻的跨源差异时使用。", "选择横坐标与设备指标,再使用层范围、材料和监控方式筛选。", "时间图展示日志中的实际采样点,不计算插值。"], + "machine-samples": ["工控log总览", "实际采样点", "统计当前筛选范围内的连续设备记录。", "确认日期文件覆盖范围和可用数据量时使用。", "选择材料或镀膜区段后查看采样点与工作时间变化。", "采样点来自原始日志;趋势展示会优先保留真实边界与极值。"], + "machine-quality": ["工控log总览", "数据质量", "检查日期连续性、重复时间、时间缺口和设备逻辑。", "解释连续趋势前确认工控数据是否完整时使用。", "在总览中展开整炉工控log结构与时间质量。", "质量提示用于数据核验,不输出产品合格结论。"], + "machine-device": ["工控log设备", "连续设备趋势", "按时间显示功率、电流、电压、真空、温度、气体和转速。", "需要查看设备在镀膜区段内的连续变化时使用。", "选择材料、区段和设备指标,查看对应实际采样点。", "单选 H 或 L 时只显示该材料参与工作的时间。"], "overview-review": ["总览", "重点复核层", "汇总达到重点复核阈值的层。", "完成分析后确定优先检查顺序。", "进入总览,选择重点复核层查看对应记录。", "统计偏离仅提供复核线索,不代表质量结论。"], "overview-quality": ["总览", "数据质量", "汇总缺失文件、字段异常和时间逻辑问题。", "解读趋势和复核提示前确认数据可用性。", "展开数据质量记录,按错误、警告和提示逐项核验。", "数据质量问题独立于统计复核提示。"], "theory-band": ["理论趋势", "材料与监控方式", "按层显示 H/L 材料及 OMS/Timer 监控方式。", "核对材料切换和监控方式分布时使用。", "选择该区域,再结合层号检查连续性和切换位置。", "材料与监控方式用于分组解释,不直接表示异常。"], @@ -27,21 +34,23 @@ "anomaly-ranking": ["异常复核", "异常排名", "按等级和偏离程度排列需复核层。", "需要确定单层核验顺序时使用。", "选择排名记录,再进入对应层的光学或设备详情。", "排名只反映统计偏离程度,不代表质量判定。"] }; - const scopeContent = { - combined: ["综合分析", "自动校正双源时间偏移,按层数或时间对照设备状态。", "已关联", "工控log + 监控log", "总览、设备", "双源时间对齐、层数/时间横坐标、转速来源核验", "层范围、材料、监控方式、区段"], - monitor: ["监控log分析", "按镀膜层汇总理论、光学、设备统计与复核提示。", "逐层分析", "理论文件、MeasPower、MachineStatus、CoatingLog", "总览、理论趋势、光学、设备、异常复核", "单层曲线、理论曲线、原始截图、H/L 分组统计", "层范围、材料、监控方式"], - machine: ["工控log分析", "合并连续日期日志,查看设备在真实时间轴上的变化。", "连续数据", "YYYY-MM-DD.csv", "总览、设备", "工作区段识别、8,000 点趋势、O₂/Ar 独立通道", "材料、区段;关联后增加层范围与监控方式"] + const scopeConfig = { + combined: { note: "已关联 180 个镀膜层", filters: ["起始层 1", "结束层 180", "材料 H / L", "监控方式 OMS / Timer", "镀膜区段 1"] }, + monitor: { note: "当前包含 180 个监控层", filters: ["起始层 1", "结束层 180", "材料 H / L", "监控方式 OMS / Timer", "异常阈值 3.5"] }, + machine: { note: "当前包含 2 个日期文件", filters: ["起始层 1", "结束层 180", "材料 H / L", "监控方式 OMS / Timer", "镀膜区段 1"] } }; + let activeScope = "combined"; let activeModule = "overview"; - let activeHotspot = "overview-review"; + let activeHotspot = "combined-alignment"; let selectedLayer = 96; let deviceMetric = "power"; let anomalyMetric = "residual"; function setScope(name, focusTab = false) { - const content = scopeContent[name]; - if (!content) return; + const config = scopeConfig[name]; + if (!config) return; + activeScope = name; scopeTabs.forEach(tab => { const active = tab.dataset.siteScope === name; tab.classList.toggle("active", active); @@ -49,8 +58,14 @@ tab.tabIndex = active ? 0 : -1; if (active && focusTab) tab.focus(); }); - $("scope-panel").setAttribute("aria-labelledby", `scope-tab-${name}`); - ["scopeTitle", "scopeSummary", "scopeStatus", "scopeData", "scopeModules", "scopeFeature", "scopeFilters"].forEach((id, index) => { $(id).textContent = content[index]; }); + moduleTabs.forEach(tab => { tab.hidden = !tab.dataset.guideScopes.split(" ").includes(name); }); + document.querySelectorAll("[data-guide-scope-panel]").forEach(panel => { panel.hidden = panel.dataset.guideScopePanel !== name; }); + $("guideFilterbar").innerHTML = config.filters.map(item => `${item}`).join(""); + $("guideSourceNote").textContent = config.note; + const currentTab = moduleTabs.find(tab => tab.dataset.guideModule === activeModule && !tab.hidden); + setModule(currentTab ? activeModule : "overview"); + showDetail(`scope-${name}`); + requestAnimationFrame(drawAll); } scopeTabs.forEach((tab, index) => { @@ -96,18 +111,20 @@ panel.classList.toggle("active", active); panel.hidden = !active; }); - showDetail(moduleFirstHotspot[name]); + showDetail(moduleFirstHotspot[activeScope][name] || `scope-${activeScope}`); requestAnimationFrame(drawAll); } - moduleTabs.forEach((tab, index) => { + moduleTabs.forEach(tab => { tab.addEventListener("click", () => setModule(tab.dataset.guideModule)); tab.addEventListener("keydown", event => { if (!["ArrowLeft", "ArrowRight", "ArrowUp", "ArrowDown", "Home", "End"].includes(event.key)) return; event.preventDefault(); + const availableTabs = moduleTabs.filter(item => !item.hidden); + const index = availableTabs.indexOf(tab); const previous = event.key === "ArrowLeft" || event.key === "ArrowUp"; - const nextIndex = event.key === "Home" ? 0 : event.key === "End" ? moduleTabs.length - 1 : (index + (previous ? moduleTabs.length - 1 : 1)) % moduleTabs.length; - setModule(moduleTabs[nextIndex].dataset.guideModule, true); + const nextIndex = event.key === "Home" ? 0 : event.key === "End" ? availableTabs.length - 1 : (index + (previous ? availableTabs.length - 1 : 1)) % availableTabs.length; + setModule(availableTabs[nextIndex].dataset.guideModule, true); }); }); @@ -164,16 +181,20 @@ const points = item.values.map((value, index) => point(value, index, item.values.length)); context.strokeStyle = item.color; context.lineWidth = item.width || 2; - context.beginPath(); - points.forEach((current, index) => { - if (!index) context.moveTo(current.x, current.y); - else if (options.smooth) { - const previous = points[index - 1]; - context.quadraticCurveTo(previous.x, previous.y, (previous.x + current.x) / 2, (previous.y + current.y) / 2); - if (index === points.length - 1) context.lineTo(current.x, current.y); - } else context.lineTo(current.x, current.y); - }); - context.stroke(); + context.setLineDash(item.dash || []); + if (options.lines !== false) { + context.beginPath(); + points.forEach((current, index) => { + if (!index) context.moveTo(current.x, current.y); + else if (options.smooth) { + const previous = points[index - 1]; + context.quadraticCurveTo(previous.x, previous.y, (previous.x + current.x) / 2, (previous.y + current.y) / 2); + if (index === points.length - 1) context.lineTo(current.x, current.y); + } else context.lineTo(current.x, current.y); + }); + context.stroke(); + } + context.setLineDash([]); if (item.points) { context.fillStyle = item.color; points.forEach(current => { context.beginPath(); context.arc(current.x, current.y, 2.4, 0, Math.PI * 2); context.fill(); }); @@ -200,6 +221,10 @@ const opticalMeas = [0.43, 0.46, 0.44, 0.49, 0.47, 0.52, 0.48, 0.54, 0.50, 0.53, 0.49, 0.55]; const opticalCalc = [0.42, 0.45, 0.45, 0.48, 0.48, 0.50, 0.49, 0.52, 0.51, 0.52, 0.50, 0.53]; const opticalTheory = [0.44, 0.44, 0.46, 0.46, 0.48, 0.48, 0.50, 0.50, 0.51, 0.51, 0.52, 0.52]; + const combinedActualTime = [398, 286, 421, 274, 405, 291, 418, 281, 399, 296, 426, 278]; + const combinedTargetTime = [392, 281, 415, 270, 401, 286, 411, 276, 395, 291, 419, 274]; + const machinePowerH = [5.92, 6.08, 5.87, 6.21, 6.03, 6.17, 5.95, 6.26, 6.11, 5.99, 6.22, 6.06, 6.18, 5.91, 6.24, 6.09, 6.15, 6.01]; + const machinePowerL = [4.08, 4.22, 4.15, 4.31, 4.18, 4.27, 4.12, 4.36, 4.21, 4.16, 4.33, 4.19, 4.29, 4.11, 4.35, 4.23, 4.26, 4.17]; const deviceData = { power: { title: "功率均值逐层趋势", unit: "日志原始值", h: [5.8, 6.0, 5.9, 6.2, 6.1, 6.3, 6.0, 6.2, 6.1, 6.4, 6.2, 6.3], l: [4.1, 4.2, 4.0, 4.3, 4.2, 4.4, 4.1, 4.3, 4.2, 4.5, 4.3, 4.4] }, @@ -230,6 +255,38 @@ $("guideDeviceStats").innerHTML = row("H 材料", item.h) + row("L 材料", item.l); } + function drawCombinedOverview() { + lineChart("guideCombinedOverviewCanvas", [ + { name: "监控实际", values: combinedActualTime, color: "#075e91", points: true }, + { name: "工控工作", values: combinedTargetTime, color: "#c5691a", points: true } + ], { min: 250, max: 450, smooth: true }); + } + + function drawMachineOverview() { + lineChart("guideMachineOverviewCanvas", [ + { name: "H 材料", values: machinePowerH, color: "#075e91", points: true }, + { name: "L 材料", values: machinePowerL, color: "#c5691a", points: true } + ], { min: 3.8, max: 6.5, lines: false }); + } + + function drawCombinedDevice() { + const monitorH = deviceData.power.h; + const monitorL = deviceData.power.l; + lineChart("guideCombinedDeviceCanvas", [ + { name: "H 工控", values: machinePowerH.slice(0, 12), color: "#075e91", points: true }, + { name: "L 工控", values: machinePowerL.slice(0, 12), color: "#c5691a", points: true }, + { name: "H 监控", values: monitorH, color: "#075e91", points: true, dash: [3, 3] }, + { name: "L 监控", values: monitorL, color: "#c5691a", points: true, dash: [3, 3] } + ], { min: 3.8, max: 6.6, smooth: true }); + } + + function drawMachineDevice() { + lineChart("guideMachineDeviceCanvas", [ + { name: "H 材料", values: machinePowerH, color: "#075e91", points: true }, + { name: "L 材料", values: machinePowerL, color: "#c5691a", points: true } + ], { min: 3.8, max: 6.5, lines: false }); + } + function drawAnomaly() { const item = anomalyData[anomalyMetric]; $("guideAnomalyTitle").textContent = item.title; @@ -237,12 +294,16 @@ } function drawAll() { + drawCombinedOverview(); + drawMachineOverview(); lineChart("guideOverviewCanvas", [{ name: "H 材料", values: overviewH, color: "#075e91" }, { name: "L 材料", values: overviewL, color: "#c5691a" }], { min: -0.6, max: 0.7, alerts: [5, 10] }); lineChart("guideThicknessCanvas", [{ name: "物理厚度", values: thickness, color: "#075e91", points: true }], { min: 60, max: 135, smooth: true }); lineChart("guideRateCanvas", [{ name: "理论速率", values: rateTheory, color: "#075e91", points: true }, { name: "实际速率", values: rateActual, color: "#c5691a", points: true }], { min: 0.4, max: 0.7, smooth: true }); lineChart("guideOpticalCanvas", [{ name: "Meas", values: opticalMeas, color: "#075e91" }, { name: "动态 Calc", values: opticalCalc, color: "#c5691a" }, { name: "理论 End T", values: opticalTheory, color: "#6d68a8" }], { min: 0.38, max: 0.58, smooth: true }); drawLayer(); + drawCombinedDevice(); drawDevice(); + drawMachineDevice(); drawAnomaly(); } @@ -281,5 +342,4 @@ new ResizeObserver(drawAll).observe(document.querySelector(".guide-client")); setScope("combined"); - setModule(activeModule); })(); diff --git a/site/index.html b/site/index.html index 9ac0ac8..b90c973 100644 --- a/site/index.html +++ b/site/index.html @@ -25,8 +25,7 @@ Log Analysis镀膜日志分析工作台 - 分析范围 - 功能说明 + 客户端说明 本地处理 安装 源码 @@ -74,68 +73,83 @@ 单炉次镀膜日志分析 - - - 一个文件夹,三种分析范围 - 程序自动识别可用日志。选择综合、监控log或工控log,界面只显示当前数据源支持的模块。 - - - - 综合双源关联与设备对照 - 监控log逐层理论、光学与复核 - 工控log最多 8,000 点连续设备趋势 - - - 综合分析自动校正双源时间偏移,按层数或时间对照设备状态。已关联 - - - 数据工控log + 监控log - 模块总览、设备 - 重点功能双源时间对齐、层数/时间横坐标、转速来源核验 - 筛选层范围、材料、监控方式、区段 - - - - - + - 监控log五大模块 - 切换模块并选择高亮区域,查看逐层分析的功能、适用场景、操作方法和结果说明。页面使用合成数据。 + 三种分析范围,同一工作台 + 程序自动识别工控log与监控log。切换分析范围后,模块、筛选条件和结果在同一界面内更新。演示使用合成数据。 LA - Log Analysis - 监控log - D:\Process\Demo-Lot - 分析完成 + Log Analysis镀膜日志分析工作台 + 日志文件夹D:\Process\Demo-Lot + 选择文件夹 + 开始分析 + 服务可用分析完成 - + + 分析范围 + + 综合 + 监控log + 工控log + + 已关联 180 个镀膜层 + + - 总览 - 理论趋势 - 光学 - 设备 - 异常复核 + 分析模块 + 总览 + 理论趋势 + 光学 + 设备 + 异常复核 - 层号 1-180材料 H / L监控方式 OMS / Timer + 起始层 1结束层 180材料 H / L监控方式 OMS / Timer镀膜区段 1 - 总览完成状态、累计时间与优先复核入口合成数据 - - 炉号DEMO-180 - 层数180H 96 / L 84 - 完成状态已完成 - 累计镀膜20 h 18 m理论 19 h 52 m - 重点复核层2选择查看说明 - 数据质量0 错误3 项提示 + + 综合总览双源完整性、关联状态与当前筛选统计合成数据 + 已关联工控log与监控log已匹配 180 个镀膜层 + + 监控层数180H 96 / L 84 + 镀膜层数180关联结果 + 实际镀膜时间20 h 18 m监控log + 靶材工作时间19 h 52 m工控log + 时钟偏移+207.4 s统一到监控log时间 + 转速来源工控log保留监控log参考值 + + 实际镀膜时间与靶材工作时间按镀膜层对照层号 + + + 监控log总览完成状态、累计时间与优先复核入口合成数据 + + 炉号DEMO-180 + 层数180H 96 / L 84 + 完成状态已完成 + 累计镀膜20 h 18 m理论 19 h 52 m + 重点复核层2选择查看说明 + 数据质量0 错误3 项提示 + + 理论终点偏移概览Meas - 理论 End T(%)层号 + + + 工控log总览连续日期、工作时间与整炉数据质量合成数据 + + 日期文件2连续日期 + 当前工作段180H 96 / L 84 + 实际采样点54,286按时间连续记录 + 当前时间范围20 h 26 m首个至末个工作段 + 靶材工作时间19 h 52 mH/L 有效工作数据 + 数据质量0 错误整炉时间检查 + + H/L 功率时间趋势实际采样点时间 - 理论终点偏移概览Meas - 理论 End T(%)层号 @@ -158,10 +172,30 @@ 监控log五大模块 - 设备分析按材料比较逐层均值与波动合成数据 - 指标功率温度真空 - 功率均值逐层趋势日志原始值层号 - H/L 分组统计材料样本数均值范围 + + 综合设备按镀膜层或时间对照双源设备数据合成数据 + 横坐标层数时间 + + + 功率双源逐层对照工控log / 监控log层号 + + H/L 分组对照材料监控log均值工控log均值差值H6.186.14-0.04L4.274.22-0.05 + + + 监控log设备按材料比较逐层均值与波动合成数据 + + + 功率均值逐层趋势日志原始值层号 + + H/L 分组统计材料样本数均值范围 + + + 工控log设备查看连续设备记录中的实际采样点合成数据 + + + 功率值时间趋势实际采样点时间 + + @@ -178,13 +212,13 @@ 监控log五大模块 @@ -200,7 +234,7 @@ 重点复核层 - 安装 Windows 测试版v0.1.0-beta.5 新增工控log、双源关联与高密度设备趋势。当前版本未进行代码签名。已知问题:部分环境下单层详情图表或原始截图可能不显示。 + 安装 Windows 测试版v0.1.0-beta.5 新增工控log、双源关联与实际采样点设备趋势。当前版本未进行代码签名。已知问题:部分环境下单层详情图表或原始截图可能不显示。 1关闭旧版本升级前先退出所有正在运行的 Log Analysis 窗口。 diff --git a/site/styles.css b/site/styles.css index 497bfaa..a8ebf27 100644 --- a/site/styles.css +++ b/site/styles.css @@ -82,46 +82,33 @@ h1 { max-width: 12ch; margin: 0; font-size: clamp(3.3rem, 6vw, 6rem); line-heigh .demo-readout b { margin-left: auto; color: var(--red); } @keyframes draw-signal { to { stroke-dashoffset: 0; } } -.scope-section { padding: clamp(88px, 9vw, 138px) clamp(20px, 7vw, 120px); border-bottom: 1px solid var(--line); background: var(--bg); } -.scope-intro { max-width: 1220px; margin: 0 auto 42px; } -.scope-intro h2 { max-width: 15ch; margin: 0; font-size: clamp(2.4rem, 4.5vw, 4.5rem); line-height: 1.04; letter-spacing: -.035em; text-wrap: balance; } -.scope-intro p { max-width: 46rem; margin: 22px 0 0; color: var(--muted); font-size: 18px; } -.scope-workbench { display: grid; grid-template-columns: minmax(230px, .48fr) minmax(0, 1.52fr); max-width: 1220px; margin: auto; border-top: 1px solid var(--line); border-bottom: 1px solid var(--line); } -.scope-tabs { display: grid; align-content: start; border-right: 1px solid var(--line); } -.scope-tabs button { display: grid; gap: 3px; min-height: 94px; padding: 20px 22px; border: 0; border-bottom: 1px solid var(--line); background: transparent; color: var(--text); font: inherit; text-align: left; cursor: pointer; } -.scope-tabs button:last-child { border-bottom: 0; } -.scope-tabs button:hover { background: rgba(255, 255, 255, .55); } -.scope-tabs button.active { background: var(--surface); box-shadow: inset 3px 0 var(--blue); } -.scope-tabs button strong { color: var(--blue-dark); font-size: 17px; } -.scope-tabs button span { color: var(--muted); font-size: 12px; } -.scope-panel { min-width: 0; min-height: 282px; padding: clamp(26px, 4vw, 52px); background: var(--surface); } -.scope-panel-head { display: flex; align-items: flex-start; justify-content: space-between; gap: 24px; } -.scope-panel-head h3 { margin: 0; font-size: clamp(1.7rem, 3vw, 2.5rem); line-height: 1.1; letter-spacing: -.025em; } -.scope-panel-head p { max-width: 52ch; margin: 10px 0 0; color: var(--muted); } -.scope-panel-head > span { padding: 6px 10px; border: 1px solid #9bc4b0; border-radius: 6px; color: var(--green); font-size: 12px; font-weight: 700; white-space: nowrap; } -.scope-timeline { display: grid; grid-template-columns: 8px 1fr 8px 1fr 8px 1fr 8px; align-items: center; margin: 34px 0 28px; } -.scope-timeline span { width: 8px; height: 8px; border-radius: 50%; background: var(--blue); } -.scope-timeline i { height: 2px; background: #9fc4d9; } -.scope-facts { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); margin: 0; border-top: 1px solid var(--line); } -.scope-facts div { padding: 16px 18px 16px 0; border-bottom: 1px solid var(--line); } -.scope-facts div:nth-child(odd) { border-right: 1px solid var(--line); } -.scope-facts div:nth-child(even) { padding-left: 18px; } -.scope-facts dt { color: var(--muted); font-size: 11px; font-weight: 700; } -.scope-facts dd { margin: 5px 0 0; font-size: 14px; } - .client-guide { padding: clamp(88px, 9vw, 138px) clamp(20px, 4vw, 70px); background: var(--surface); } +.section-anchor { position: absolute; transform: translateY(-72px); } .guide-intro { max-width: 1220px; margin: 0 auto 46px; } .guide-intro h2, .privacy h2, .install h2 { max-width: 15ch; margin: 0; font-size: clamp(2.4rem, 4.5vw, 4.5rem); line-height: 1.04; letter-spacing: -.035em; text-wrap: balance; } .guide-intro p { max-width: 46rem; margin: 22px 0 0; color: var(--muted); font-size: 18px; } .guide-layout { display: grid; grid-template-columns: minmax(0, 1fr) 290px; gap: 18px; max-width: 1380px; margin: auto; align-items: start; } .guide-client { min-width: 0; overflow: hidden; border: 1px solid #b9c7d2; border-radius: var(--radius); background: #f7f9fb; box-shadow: 0 24px 58px rgba(18, 43, 57, .14); } -.guide-windowbar { min-height: 58px; display: flex; align-items: center; gap: 10px; padding: 10px 14px; border-bottom: 1px solid var(--line); background: #fff; } -.guide-windowbar strong { white-space: nowrap; } -.guide-context { padding: 4px 7px; border: 1px solid #9dbfd2; border-radius: 5px; color: var(--blue); font-size: 10px; font-weight: 700; white-space: nowrap; } -.guide-path { flex: 1; min-width: 0; margin-left: 12px; padding: 7px 10px; overflow: hidden; border: 1px solid var(--line); border-radius: 6px; color: var(--muted); font: 11px/1.2 Consolas, monospace; text-overflow: ellipsis; white-space: nowrap; } -.guide-ready { color: var(--green); font-size: 11px; font-weight: 700; white-space: nowrap; } -.guide-app { display: grid; grid-template-columns: 142px minmax(0, 1fr); min-height: 610px; } +.guide-windowbar { min-height: 68px; display: flex; align-items: center; gap: 10px; padding: 10px 14px; border-bottom: 1px solid var(--line); background: #fff; } +.guide-brand { display: grid; min-width: 116px; line-height: 1.15; } +.guide-brand strong { white-space: nowrap; } +.guide-brand small { margin-top: 3px; color: var(--muted); font-size: 9px; } +.guide-path { flex: 1; min-width: 150px; display: grid; gap: 2px; padding: 7px 10px; overflow: hidden; border: 1px solid var(--line); border-radius: 6px; color: var(--text); font: 11px/1.2 Consolas, monospace; text-overflow: ellipsis; white-space: nowrap; } +.guide-path small { color: var(--muted); font: 9px/1.1 "Segoe UI", sans-serif; } +.guide-toolbar-button { min-height: 34px; padding: 6px 10px; border: 1px solid var(--line); border-radius: 6px; background: #fff; color: #405463; font: inherit; font-size: 10px; cursor: pointer; } +.guide-toolbar-button:hover { border-color: #8bb8d2; color: var(--blue); } +.guide-toolbar-button.primary { border-color: var(--blue); background: var(--blue); color: #fff; } +.guide-ready { color: var(--green); font-size: 10px; font-weight: 700; line-height: 1.35; white-space: nowrap; } +.guide-sourcebar { min-height: 48px; display: flex; align-items: center; gap: 12px; padding: 6px 14px; border-bottom: 1px solid var(--line); background: #e9eef2; } +.guide-sourcebar > strong { color: #405463; font-size: 10px; } +.guide-source-options { display: flex; gap: 2px; padding: 2px; border: 1px solid #b9c7d2; border-radius: 7px; background: #fff; } +.guide-source-options button { min-height: 30px; padding: 5px 15px; border: 0; border-radius: 5px; background: transparent; color: #4b6070; font: inherit; font-size: 10px; cursor: pointer; } +.guide-source-options button:hover { background: #e6f1f7; color: var(--blue); } +.guide-source-options button.active { background: var(--blue); color: #fff; font-weight: 700; } +.guide-sourcebar > span { margin-left: auto; color: var(--muted); font-size: 10px; } +.guide-app { display: grid; grid-template-columns: 142px minmax(0, 1fr); min-height: 650px; } .guide-module-nav { display: grid; align-content: start; gap: 4px; padding: 16px 10px; border-right: 1px solid var(--line); background: var(--soft); } +.guide-nav-label { padding: 0 10px 7px; color: #718290; font-size: 9px; font-weight: 700; } .guide-module-nav button { min-height: 38px; padding: 8px 11px; border: 1px solid transparent; border-radius: 6px; background: transparent; color: #405463; font: inherit; font-size: 12px; font-weight: 650; text-align: left; cursor: pointer; } .guide-module-nav button:hover { background: rgba(255, 255, 255, .7); color: var(--blue); } .guide-module-nav button.active { border-color: var(--line); background: #fff; color: var(--blue); box-shadow: inset 3px 0 var(--blue); } @@ -130,11 +117,14 @@ h1 { max-width: 12ch; margin: 0; font-size: clamp(3.3rem, 6vw, 6rem); line-heigh .guide-filterbar span { padding: 4px 8px; border: 1px solid var(--line); border-radius: 6px; } .guide-screen { min-width: 0; padding: 18px; animation: guide-in .18s ease-out; } .guide-screen[hidden] { display: none; } +[data-guide-scope-panel][hidden] { display: none; } @keyframes guide-in { from { opacity: .55; } to { opacity: 1; } } .guide-page-title { display: flex; align-items: flex-start; justify-content: space-between; gap: 16px; margin-bottom: 14px; } .guide-page-title h3 { margin: 0; font-size: 20px; line-height: 1.2; } .guide-page-title p { margin: 3px 0 0; color: var(--muted); font-size: 11px; } .guide-page-title > span { color: var(--muted); font-size: 10px; } +.guide-status-line { display: flex; align-items: center; gap: 9px; margin-bottom: 10px; color: var(--muted); font-size: 10px; } +.guide-status-line b { padding: 4px 8px; border: 1px solid #9bc4b0; border-radius: 5px; background: #edf8f2; color: var(--green); } .guide-kpis { display: grid; grid-template-columns: repeat(3, minmax(0, 1fr)); gap: 8px; } .guide-kpis > div, .guide-kpis > button { min-width: 0; min-height: 80px; display: grid; align-content: start; gap: 2px; padding: 10px; border: 1px solid var(--line); border-radius: 8px; background: #fff; color: var(--text); font: inherit; text-align: left; } .guide-kpis > button { cursor: pointer; } @@ -178,6 +168,20 @@ h1 { max-width: 12ch; margin: 0; font-size: clamp(3.3rem, 6vw, 6rem); line-heigh .guide-metric-select { display: flex; align-items: center; gap: 7px; margin-bottom: 2px; } .guide-metric-select strong { margin-right: 4px; font-size: 11px; } .guide-metric-select button.active { border-color: var(--blue); background: #e9f3f8; color: var(--blue); font-weight: 700; } +.guide-axis-toggle { display: flex; align-items: center; gap: 4px; margin-bottom: 8px; } +.guide-axis-toggle strong { margin-right: 5px; font-size: 10px; } +.guide-axis-toggle button { min-height: 28px; padding: 4px 10px; border: 1px solid var(--line); background: #fff; color: #405463; font: inherit; font-size: 9px; } +.guide-axis-toggle button:first-of-type { border-radius: 6px 0 0 6px; } +.guide-axis-toggle button:last-child { margin-left: -5px; border-radius: 0 6px 6px 0; } +.guide-axis-toggle button.active { border-color: var(--blue); background: #e9f3f8; color: var(--blue); font-weight: 700; } +.guide-device-workbench { display: grid; grid-template-columns: 118px minmax(0, 1fr); gap: 10px; align-items: stretch; } +.guide-metric-rail { min-height: 278px; padding: 10px 8px; border: 1px solid var(--line); border-radius: 8px; background: #fff; } +.guide-metric-rail strong { display: block; margin: 3px 6px 5px; color: #718290; font-size: 9px; } +.guide-metric-rail strong:not(:first-child) { margin-top: 12px; } +.guide-metric-rail button { width: 100%; min-height: 30px; padding: 5px 7px; border: 0; border-radius: 5px; background: transparent; color: #405463; font: inherit; font-size: 10px; text-align: left; cursor: pointer; } +.guide-metric-rail button:hover { background: #e9f3f8; color: var(--blue); } +.guide-metric-rail button.active { background: var(--blue); color: #fff; font-weight: 700; } +.guide-device-workbench .guide-chart { height: 100%; margin-top: 0; } .device-chart canvas { height: 220px; } .guide-stats { margin-top: 10px; padding: 12px; border: 1px solid var(--line); border-radius: 8px; background: #fff; cursor: pointer; } .guide-stats > strong { font-size: 12px; } @@ -224,7 +228,6 @@ footer strong { color: var(--text); } .guide-layout { grid-template-columns: 1fr; } .guide-explanation { position: static; min-height: 0; } .guide-explanation dl { display: grid; grid-template-columns: repeat(2, 1fr); gap: 0 24px; } - .scope-workbench { grid-template-columns: 210px minmax(0, 1fr); } } @media (max-width: 900px) { @@ -232,6 +235,7 @@ footer strong { color: var(--text); } .guide-module-nav { display: flex; overflow-x: auto; padding: 8px 10px; border-right: 0; border-bottom: 1px solid var(--line); } .guide-module-nav button { flex: 0 0 auto; min-width: 92px; text-align: center; } .guide-module-nav button.active { box-shadow: inset 0 -3px var(--blue); } + .guide-nav-label { display: none; } } @media (max-width: 720px) { @@ -252,12 +256,20 @@ footer strong { color: var(--text); } .demo-sources { overflow-x: auto; } .demo-readout { gap: 9px; } .demo-readout b { display: none; } - .guide-windowbar strong, .guide-ready { display: none; } - .guide-context { margin-left: 0; } - .guide-path { margin-left: 0; } + .guide-brand small, .guide-ready { display: none; } + .guide-windowbar { flex-wrap: wrap; } + .guide-path { order: 2; flex-basis: 100%; } + .guide-toolbar-button { margin-left: auto; } + .guide-toolbar-button + .guide-toolbar-button { margin-left: 0; } + .guide-sourcebar { overflow-x: auto; } + .guide-sourcebar > span { display: none; } + .guide-source-options { flex: 0 0 auto; } .guide-screen { padding: 12px; } .guide-kpis { grid-template-columns: repeat(2, minmax(0, 1fr)); } - .guide-chart-grid, .guide-detail-grid { grid-template-columns: 1fr; } + .guide-chart-grid, .guide-detail-grid, .guide-device-workbench { grid-template-columns: 1fr; } + .guide-metric-rail { min-height: 0; display: flex; gap: 4px; overflow-x: auto; } + .guide-metric-rail strong { display: none; } + .guide-metric-rail button { width: auto; min-width: 76px; text-align: center; } .material-band { grid-template-columns: 1fr 1fr; } .material-band > span { grid-column: 1 / -1; } .guide-time-summary { grid-template-columns: 1fr; } @@ -265,16 +277,6 @@ footer strong { color: var(--text); } .guide-metric-select { align-items: stretch; flex-wrap: wrap; } .guide-review-list { flex-direction: column; } .privacy li { grid-template-columns: 76px 1fr; } - .scope-workbench { display: block; } - .scope-tabs { display: flex; overflow-x: auto; border-right: 0; border-bottom: 1px solid var(--line); } - .scope-tabs button { flex: 0 0 150px; min-height: 78px; padding: 14px; border-right: 1px solid var(--line); border-bottom: 0; } - .scope-tabs button.active { box-shadow: inset 0 -3px var(--blue); } - .scope-panel { min-height: 0; padding: 24px 18px; } - .scope-panel-head { display: grid; } - .scope-panel-head > span { justify-self: start; } - .scope-facts { grid-template-columns: 1fr; } - .scope-facts div:nth-child(odd) { border-right: 0; } - .scope-facts div:nth-child(even) { padding-left: 0; } footer, footer div { align-items: flex-start; flex-direction: column; } } diff --git a/test_analyzer.py b/test_analyzer.py index b96dc5d..fc67cf4 100644 --- a/test_analyzer.py +++ b/test_analyzer.py @@ -894,9 +894,13 @@ def test_public_site_is_local_only_and_links_to_releases(self): self.assertIn("Get-FileHash -Algorithm SHA256", readme) for scope in ("combined", "monitor", "machine"): self.assertIn(f'data-site-scope="{scope}"', html) - self.assertIn("监控log五大模块", html) + self.assertIn("三种分析范围,同一工作台", html) + self.assertIn("guide-sourcebar", html) + self.assertIn("guide-module-nav", html) self.assertIn("异常复核", html) - self.assertIn("8,000", html) + self.assertIn("实际采样点", html + demo_js) + self.assertNotIn("8,000", html + demo_js) + self.assertNotIn("高密度设备趋势", html + demo_js) self.assertNotIn("Spearman", html + demo_js) for content in (html, readme, release_notes): self.assertIn("单层详情图表或原始截图可能不显示", content)
程序自动识别可用日志。选择综合、监控log或工控log,界面只显示当前数据源支持的模块。
自动校正双源时间偏移,按层数或时间对照设备状态。
切换模块并选择高亮区域,查看逐层分析的功能、适用场景、操作方法和结果说明。页面使用合成数据。
程序自动识别工控log与监控log。切换分析范围后,模块、筛选条件和结果在同一界面内更新。演示使用合成数据。
完成状态、累计时间与优先复核入口
双源完整性、关联状态与当前筛选统计
连续日期、工作时间与整炉数据质量
按材料比较逐层均值与波动
按镀膜层或时间对照双源设备数据
查看连续设备记录中的实际采样点
v0.1.0-beta.5 新增工控log、双源关联与高密度设备趋势。当前版本未进行代码签名。已知问题:部分环境下单层详情图表或原始截图可能不显示。
v0.1.0-beta.5 新增工控log、双源关联与实际采样点设备趋势。当前版本未进行代码签名。已知问题:部分环境下单层详情图表或原始截图可能不显示。
升级前先退出所有正在运行的 Log Analysis 窗口。