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 @@ + + +