From 877770aeab78322dcc9814dff36d410d91a710f3 Mon Sep 17 00:00:00 2001 From: wangfq Date: Fri, 5 Jun 2026 11:49:35 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=B5=8B=E8=AF=95=E4=BF=A1=E6=81=AF?= =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E5=9B=BE=E8=A1=A8=E8=A7=86=E5=9B=BE=EF=BC=88?= =?UTF-8?q?ECharts=EF=BC=89=EF=BC=8C=E8=A1=A8=E6=A0=BC/=E5=9B=BE=E8=A1=A8?= =?UTF-8?q?=E4=B8=80=E9=94=AE=E5=88=87=E6=8D=A2?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - B2视图:峰峰值/频率/距离/速度 趋势折线图,三Y轴 - B4视图:工作频率/距离/速度 趋势折线图,三Y轴 - dataZoom时间范围缩放,图例可切换系列显隐 - 新增 /api/test-data/chart 接口返回全量数据 --- edc-web/app/routes/test_data.py | 13 +++ edc-web/app/static/js/test_data.js | 151 +++++++++++++++++++++++++++ edc-web/app/templates/test_data.html | 15 +++ 3 files changed, 179 insertions(+) diff --git a/edc-web/app/routes/test_data.py b/edc-web/app/routes/test_data.py index fbe2b27..72c551d 100644 --- a/edc-web/app/routes/test_data.py +++ b/edc-web/app/routes/test_data.py @@ -37,6 +37,19 @@ def api_test_data(): }) +@bp.route("/api/test-data/chart") +def api_chart_data(): + """返回图表所需全部数据(不分页)""" + serial = request.args.get("serial", "", type=str) + date_from = request.args.get("date_from", "", type=str) + date_to = request.args.get("date_to", "", type=str) + test_mode = request.args.get("test_mode", "", type=str) + data_source = request.args.get("data_source", "", type=str) + + records = get_all_test_data_for_export(serial, date_from, date_to, + test_mode, data_source) + return jsonify({"records": records, "total": len(records)}) + @bp.route("/api/test-data/export") def api_export(): """导出测试数据为 CSV""" diff --git a/edc-web/app/static/js/test_data.js b/edc-web/app/static/js/test_data.js index a5c2089..67cd7bd 100644 --- a/edc-web/app/static/js/test_data.js +++ b/edc-web/app/static/js/test_data.js @@ -199,6 +199,157 @@ function exportCSV() { window.location.href = `/api/test-data/export?${params}`; } +// ─── 图表 ──────────────────────────────────────── + +let chartMode = false; +let chartInstance = null; + +// 图表系列定义 +const CHART_SERIES = { + b2: [ + { key: 'ppvalue', name: '峰峰值', unit: 'V', yAxisIndex: 0 }, + { key: 'idle_freq', name: '开始频率', unit: 'Hz', yAxisIndex: 0 }, + { key: 'enter_freq', name: '进入频率', unit: 'Hz', yAxisIndex: 0 }, + { key: 'exit_freq', name: '离开频率', unit: 'Hz', yAxisIndex: 0 }, + { key: 'enter_dist', name: '进入距离', unit: 'mm', yAxisIndex: 1 }, + { key: 'exit_dist', name: '离开距离', unit: 'mm', yAxisIndex: 1 }, + { key: 'enter_speed', name: '进入速度', unit: 'dm/s',yAxisIndex: 2 }, + { key: 'exit_speed', name: '离开速度', unit: 'dm/s',yAxisIndex: 2 }, + ], + b4: [ + { key: 'work_freq', name: '工作频率', unit: 'Hz', yAxisIndex: 0 }, + { key: 'curr_dist', name: '当前距离', unit: 'mm', yAxisIndex: 1 }, + { key: 'speed', name: '速度', unit: 'dm/s',yAxisIndex: 2 }, + { key: 'near_dist', name: '最近距离', unit: 'mm', yAxisIndex: 1 }, + { key: 'far_dist', name: '最远距离', unit: 'mm', yAxisIndex: 1 }, + { key: 'b4_enter_dist', name: '进入高度', unit: 'mm', yAxisIndex: 1 }, + { key: 'b4_leave_dist', name: '离开高度', unit: 'mm', yAxisIndex: 1 }, + ], +}; + +function toggleChart() { + const container = document.getElementById('chart-container'); + const btn = document.getElementById('btn-chart'); + const table = document.getElementById('test-data-table'); + const pagination = document.getElementById('pagination'); + + chartMode = !chartMode; + if (chartMode) { + container.style.display = 'block'; + table.style.display = 'none'; + pagination.style.display = 'none'; + btn.textContent = '📋 表格'; + btn.classList.add('active'); + // 只对 B2/B4 视图显示图表 + if (currentView === 'all') switchView('b2'); + loadChart(); + } else { + container.style.display = 'none'; + table.style.display = ''; + pagination.style.display = ''; + btn.textContent = '📈 图表'; + btn.classList.remove('active'); + if (chartInstance) { chartInstance.dispose(); chartInstance = null; } + } +} + +async function loadChart() { + const container = document.getElementById('chart-container'); + if (!container || container.style.display === 'none') return; + + const serial = document.getElementById('search-serial').value; + const dateFrom = document.getElementById('search-date-from').value; + const dateTo = document.getElementById('search-date-to').value; + const v = VIEWS[currentView]; + + // 全部视图不适用,用 B2 或 B4 + const ds = v.data_source || (currentView === 'all' ? 'B2' : v.data_source); + + const params = new URLSearchParams(); + if (serial) params.set('serial', serial); + if (dateFrom) params.set('date_from', dateFrom); + if (dateTo) params.set('date_to', dateTo); + if (ds) params.set('data_source', ds); + + let resp, data; + try { + resp = await fetch(`/api/test-data/chart?${params}`); + data = await resp.json(); + } catch (e) { + console.error('加载图表数据失败:', e); + return; + } + + const records = data.records || []; + if (!records.length) { + container.innerHTML = '

暂无数据

'; + return; + } + + // 选系列定义 + const seriesDef = CHART_SERIES[ds === 'B4' ? 'b4' : 'b2'] || CHART_SERIES.b2; + + // 时间轴 + const times = records.map(r => r.create_time); + + // 构建 series + const series = seriesDef.map(def => ({ + name: `${def.name}(${def.unit})`, + type: 'line', + yAxisIndex: def.yAxisIndex, + symbol: 'circle', + symbolSize: 4, + data: records.map(r => r[def.key] ?? null), + connectNulls: false, + })); + + // 渲染 ECharts + if (chartInstance) chartInstance.dispose(); + chartInstance = echarts.init(container); + + const option = { + title: { + text: ds === 'B4' ? '波动测试 (0xB4) 数据趋势' : '灵敏度测试 (0xB2) 数据趋势', + left: 'center', + textStyle: { fontSize: 14 }, + }, + tooltip: { + trigger: 'axis', + }, + legend: { + type: 'scroll', + bottom: 0, + }, + grid: { left: 60, right: 140, top: 60, bottom: 80 }, + xAxis: { + type: 'category', + data: times, + axisLabel: { + formatter: v => fmtTime(v).substring(5, 16), // MM-dd HH:mm + rotate: 30, + }, + }, + yAxis: [ + { type: 'value', name: '频率/电压', nameTextStyle: { fontSize: 11 } }, + { type: 'value', name: '距离(mm)', nameTextStyle: { fontSize: 11 } }, + { type: 'value', name: '速度(dm/s)',nameTextStyle: { fontSize: 11 }, + offset: 80 }, + ], + dataZoom: [ + { type: 'slider', start: 0, end: 100, height: 20, bottom: 30 }, + { type: 'inside' }, + ], + series: series, + }; + + chartInstance.setOption(option); + + // 窗口 resize 时自适应 + window.addEventListener('resize', () => { + if (chartInstance) chartInstance.resize(); + }, { once: false }); +} + // ─── 初始加载 ──────────────────────────────────── renderHead(); diff --git a/edc-web/app/templates/test_data.html b/edc-web/app/templates/test_data.html index 2fd3d31..f42a065 100644 --- a/edc-web/app/templates/test_data.html +++ b/edc-web/app/templates/test_data.html @@ -31,8 +31,11 @@ + + + @@ -59,6 +62,18 @@ border-color: #2c3e50; } .tab-btn:hover:not(.active) { background: #e0e0e0; } +.btn-chart { + margin-left: 16px; + padding: 6px 14px; + border: 1px solid #27ae60; + background: #fff; + color: #27ae60; + cursor: pointer; + border-radius: 4px; + font-size: 13px; +} +.btn-chart.active { background: #27ae60; color: #fff; } + {% endblock %}