我们将使用Flask作为Web框架,使用Chart.js来绘制图表,使用模拟数据来展示。由于要求实时展示,我们可以使用AJAX定期从后端获取数据更新前端图表。
步骤:
创建Flask应用。
设计一个路由用于渲染主页面。
设计一个路由用于提供模拟数据(JSON格式)。
前端页面使用HTML和JavaScript,利用Chart.js绘制多个图表,并定时更新。
注意:我们模拟的数据将包括流量、COD、氨氮、总磷、SS、pH这6个指标。我们假设每个指标的数据是一个时间序列,这里我们使用当前时间作为时间点,数据为随机值。
由于是模拟数据,我们将在后端生成随机数,并按照一定的时间间隔(比如每分钟)更新数据,但为了简单,我们每次请求都生成新的随机数(模拟实时变化)。
但是,考虑到趋势图需要历史数据,我们将在后端维护一个固定长度的历史数据队列,每次请求返回最新的数据以及历史数据。
由于有6个指标,我们可以为每个指标维护一个队列。
但是,为了简单起见,我们只模拟最近24小时的数据(每分钟一个点,共1440个点)。不过,我们也可以只返回最近一段时间的数据,比如最近100个点。
我们这里选择返回最近100个点,并且每次请求都会添加一个新的数据点(模拟实时数据)并移除最旧的数据点,以保持数据长度不变。
然而,如果我们希望前端每次请求都获取全部历史数据(比如最近100个点),那么我们就需要在后端存储这些数据。
由于我们要求是实时展示,所以我们每5秒更新一次图表。
我们采用以下结构:
后端:
一个全局变量,存储6个指标各自最近100个点的数据(时间戳和值)。
一个路由,返回JSON格式的数据,包含6个指标的最新数据(包括历史数据,即每个指标的时间序列)。
另一个路由,用于渲染主页面。
前端:
使用6个Chart.js图表,分别展示每个指标的趋势图。
使用定时器,每5秒向后端请求一次数据,并更新图表。
由于我们使用模拟数据,我们将在后端初始化时生成一些历史数据。
我们开始编写代码。
下面是一个完整的水质数据展示网页应用,使用Python Flask框架开发,包含实时数据和趋势图展示功能,大家需要在pycharm开发工具中按如下布局进行


from flask import Flask, render_template, jsonify, requestimport randomimport timefrom datetime import datetime, timedeltaimport jsonfrom collections import dequeimport threadingapp = Flask(__name__)# 模拟水质数据存储water_quality_data = {'流量': deque(maxlen=100),'COD': deque(maxlen=100),'氨氮': deque(maxlen=100),'总磷': deque(maxlen=100),'SS': deque(maxlen=100),'pH': deque(maxlen=100)}# 时间戳存储timestamps = deque(maxlen=100)# 水质指标的正常范围quality_ranges = {'流量': {'min': 10, 'max': 100, 'unit': 'm³/s'},'COD': {'min': 10, 'max': 100, 'unit': 'mg/L'},'氨氮': {'min': 0.1, 'max': 5, 'unit': 'mg/L'},'总磷': {'min': 0.05, 'max': 2, 'unit': 'mg/L'},'SS': {'min': 5, 'max': 100, 'unit': 'mg/L'},'pH': {'min': 6.5, 'max': 8.5, 'unit': ''}}# 初始化一些模拟数据def initialize_data():now = datetime.now()for i in range(100):time_point = now - timedelta(minutes=(100-i))timestamps.append(time_point.strftime('%H:%M'))# 生成模拟数据(添加一些随机变化)water_quality_data['流量'].append(round(random.uniform(20, 80), 2))water_quality_data['COD'].append(round(random.uniform(15, 85), 2))water_quality_data['氨氮'].append(round(random.uniform(0.5, 4), 2))water_quality_data['总磷'].append(round(random.uniform(0.1, 1.8), 2))water_quality_data['SS'].append(round(random.uniform(10, 90), 2))water_quality_data['pH'].append(round(random.uniform(6.8, 8.2), 2))# 更新数据(模拟实时数据)def update_data():while True:time.sleep(2) # 每2秒更新一次数据now = datetime.now()# 更新时间戳if len(timestamps) >= 100:timestamps.popleft()timestamps.append(now.strftime('%H:%M:%S'))# 更新每个指标的数据(添加随机波动)for key in water_quality_data.keys():current_values = list(water_quality_data[key])last_value = current_values[-1] if current_values else quality_ranges[key]['min']# 生成新的数据点,基于前一个值添加随机变化if key == 'pH':# pH值变化较小change = random.uniform(-0.05, 0.05)new_value = last_value + change# 确保pH在合理范围内new_value = max(6.0, min(9.0, new_value))elif key == '流量':change = random.uniform(-3, 3)new_value = last_value + changenew_value = max(5, min(95, new_value))else:change = random.uniform(-2, 2)new_value = last_value + change# 确保数据在合理范围内new_value = max(quality_ranges[key]['min']*0.5,min(quality_ranges[key]['max']*1.5, new_value))if len(water_quality_data[key]) >= 100:water_quality_data[key].popleft()water_quality_data[key].append(round(new_value, 2))@app.route('/')def index():return render_template('index.html',indicators=list(water_quality_data.keys()),ranges=quality_ranges)@app.route('/api/current_data')def get_current_data():"""获取当前最新数据"""current_data = {}for key in water_quality_data.keys():if water_quality_data[key]:current_data[key] = water_quality_data[key][-1]else:current_data[key] = 0return jsonify({'data': current_data,'timestamp': timestamps[-1] if timestamps else datetime.now().strftime('%H:%M:%S')})@app.route('/api/historical_data')def get_historical_data():"""获取历史数据用于图表"""return jsonify({'timestamps': list(timestamps),'data': {key: list(water_quality_data[key]) for key in water_quality_data.keys()}})@app.route('/api/quality_status')def get_quality_status():"""获取水质状态(正常/警告/异常)"""status = {}current_data = {key: water_quality_data[key][-1] for key in water_quality_data.keys()}for key, value in current_data.items():min_val = quality_ranges[key]['min']max_val = quality_ranges[key]['max']if value < min_val * 0.9 or value > max_val * 1.1:status[key] = 'danger' # 异常elif value < min_val or value > max_val:status[key] = 'warning' # 警告else:status[key] = 'normal' # 正常return jsonify(status)@app.route('/api/simulate_anomaly', methods=['POST'])def simulate_anomaly():"""模拟水质异常事件"""data = request.jsonindicator = data.get('indicator', 'COD')if indicator in water_quality_data:# 将当前值设置为异常值current_values = list(water_quality_data[indicator])if current_values:# 设置一个超出正常范围的值if indicator == 'pH':anomaly_value = random.choice([5.5, 9.5]) # pH异常else:anomaly_value = quality_ranges[indicator]['max'] * 1.5current_values[-1] = round(anomaly_value, 2)water_quality_data[indicator].pop()water_quality_data[indicator].append(current_values[-1])return jsonify({'success': True, 'indicator': indicator})if __name__ == '__main__':# 初始化数据initialize_data()# 启动后台数据更新线程update_thread = threading.Thread(target=update_data, daemon=True)update_thread.start()# 启动Flask应用app.run(debug=True, host='0.0.0.0', port=5000)

<!DOCTYPE html><htmllang="zh-CN"><head><metacharset="UTF-8"><metaname="viewport"content="width=device-width, initial-scale=1.0"><title>水质数据监测系统</title><linkrel="stylesheet"href="{{ url_for('static', filename='css/style.css') }}"><scriptsrc="https://cdn.jsdelivr.net/npm/chart.js"></script><linkrel="stylesheet"href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"></head><body><divclass="container"><headerclass="header"><h1><iclass="fas fa-tint"></i> 水质数据监测系统</h1><divclass="time-display">最后更新: <spanid="last-update">--:--:--</span><buttonid="refresh-btn"class="btn"><iclass="fas fa-sync-alt"></i> 刷新</button></div></header><divclass="dashboard"><!-- 实时数据展示 --><divclass="section"><h2><iclass="fas fa-chart-line"></i> 实时数据</h2><divclass="real-time-cards">{% for indicator in indicators %}<divclass="card"id="{{ indicator }}-card"><divclass="card-header"><h3>{{ indicator }}</h3><spanclass="status-indicator"id="{{ indicator }}-status"></span></div><divclass="card-body"><divclass="value"id="{{ indicator }}-value">--</div><divclass="unit">{{ ranges[indicator]['unit'] }}</div><divclass="range">正常范围: {{ ranges[indicator]['min'] }} - {{ ranges[indicator]['max'] }}{{ ranges[indicator]['unit'] }}</div></div><divclass="card-footer"><buttonclass="btn-small anomaly-btn"data-indicator="{{ indicator }}"><iclass="fas fa-exclamation-triangle"></i> 模拟异常</button></div></div>{% endfor %}</div></div><!-- 趋势图展示 --><divclass="section"><h2><iclass="fas fa-chart-area"></i> 趋势图</h2><divclass="chart-controls"><labelfor="indicator-select">选择指标:</label><selectid="indicator-select">{% for indicator in indicators %}<optionvalue="{{ indicator }}">{{ indicator }}</option>{% endfor %}</select><labelfor="time-range">时间范围:</label><selectid="time-range"><optionvalue="10">最近10分钟</option><optionvalue="30"selected>最近30分钟</option><optionvalue="60">最近60分钟</option><optionvalue="all">全部数据</option></select></div><divclass="chart-container"><canvasid="trend-chart"></canvas></div></div><!-- 多指标对比图 --><divclass="section"><h2><iclass="fas fa-chart-bar"></i> 多指标对比</h2><divclass="chart-container"><canvasid="comparison-chart"></canvas></div></div></div><footerclass="footer"><p>水质监测系统 | 模拟数据 | 更新时间: <spanid="current-time">--:--:--</span></p><p>数据每2秒自动更新 | 使用Python Flask + Chart.js开发</p></footer></div><scriptsrc="{{ url_for('static', filename='js/charts.js') }}"></script></body></html>

* {margin: 0;padding: 0;box-sizing: border-box;font-family: 'Segoe UI', Tahoma, Geneva, Verdana, sans-serif;}body {background-color: #f5f7fa;color: #333;line-height: 1.6;}.container {max-width: 1400px;margin: 0 auto;padding: 20px;}.header {display: flex;justify-content: space-between;align-items: center;background: linear-gradient(135deg, #1a6fc4 0%, #0d4d8c 100%);color: white;padding: 20px 30px;border-radius: 12px;margin-bottom: 25px;box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);}.header h1 {font-size: 28px;font-weight: 600;}.header h1 i {margin-right: 12px;}.time-display {display: flex;align-items: center;gap: 15px;}#last-update {font-weight: 600;background: rgba(255, 255, 255, 0.2);padding: 5px 12px;border-radius: 20px;}.btn {background-color: #2d9cdb;color: white;border: none;padding: 10px 20px;border-radius: 6px;cursor: pointer;font-weight: 600;transition: all 0.3s ease;display: flex;align-items: center;gap: 8px;}.btn:hover {background-color: #1a8bc8;transform: translateY(-2px);}.dashboard {display: flex;flex-direction: column;gap: 30px;}.section {background-color: white;border-radius: 12px;padding: 25px;box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);}.section h2 {font-size: 22px;color: #2c3e50;margin-bottom: 20px;display: flex;align-items: center;gap: 10px;}.real-time-cards {display: grid;grid-template-columns: repeat(auto-fill, minmax(250px, 1fr));gap: 20px;}.card {background-color: #fff;border-radius: 10px;overflow: hidden;box-shadow: 0 3px 10px rgba(0, 0, 0, 0.08);transition: transform 0.3s ease, box-shadow 0.3s ease;border: 1px solid #eaeaea;}.card:hover {transform: translateY(-5px);box-shadow: 0 6px 15px rgba(0, 0, 0, 0.1);}.card-header {background-color: #f8f9fa;padding: 15px 20px;border-bottom: 1px solid #eaeaea;display: flex;justify-content: space-between;align-items: center;}.card-header h3 {font-size: 18px;color: #2c3e50;font-weight: 600;}.status-indicator {width: 12px;height: 12px;border-radius: 50%;background-color: #ccc;}.status-normal {background-color: #27ae60 !important;}.status-warning {background-color: #f39c12 !important;}.status-danger {background-color: #e74c3c !important;}.card-body {padding: 20px;text-align: center;}.value {font-size: 36px;font-weight: 700;color: #2c3e50;margin-bottom: 5px;}.unit {font-size: 16px;color: #7f8c8d;margin-bottom: 10px;}.range {font-size: 14px;color: #95a5a6;}.card-footer {padding: 15px 20px;background-color: #f8f9fa;border-top: 1px solid #eaeaea;text-align: center;}.btn-small {background-color: #e74c3c;color: white;border: none;padding: 8px 15px;border-radius: 4px;cursor: pointer;font-size: 14px;transition: all 0.3s ease;display: inline-flex;align-items: center;gap: 5px;}.btn-small:hover {background-color: #c0392b;}.chart-controls {display: flex;gap: 15px;margin-bottom: 20px;align-items: center;flex-wrap: wrap;}.chart-controls label {font-weight: 600;color: #2c3e50;}.chart-controls select {padding: 8px 15px;border-radius: 6px;border: 1px solid #ddd;background-color: white;color: #333;font-size: 14px;}.chart-container {position: relative;height: 400px;width: 100%;}.footer {margin-top: 40px;text-align: center;color: #7f8c8d;font-size: 14px;padding-top: 20px;border-top: 1px solid #eaeaea;}.footer p {margin-bottom: 8px;}#current-time {font-weight: 600;color: #2c3e50;}@media (max-width: 768px) {.header {flex-direction: column;gap: 15px;text-align: center;}.real-time-cards {grid-template-columns: repeat(auto-fill, minmax(200px, 1fr));}.chart-controls {flex-direction: column;align-items: flex-start;}}

document.addEventListener('DOMContentLoaded', function() {// 图表实例let trendChart = null;let comparisonChart = null;// 获取DOM元素const lastUpdateEl = document.getElementById('last-update');const currentTimeEl = document.getElementById('current-time');const refreshBtn = document.getElementById('refresh-btn');const indicatorSelect = document.getElementById('indicator-select');const timeRangeSelect = document.getElementById('time-range');const anomalyButtons = document.querySelectorAll('.anomaly-btn');// 初始化图表initTrendChart();initComparisonChart();// 获取当前数据functionfetchCurrentData() {fetch('/api/current_data').then(response => response.json()).then(data => {updateCurrentData(data);updateStatusIndicators();}).catch(error => console.error('获取当前数据失败:', error));}// 更新当前数据展示functionupdateCurrentData(data) {// 更新每个指标的值for(const [indicator, value] of Object.entries(data.data)) {const valueEl = document.getElementById(`${indicator}-value`);if(valueEl) {valueEl.textContent = value.toFixed(2);}}// 更新时间戳lastUpdateEl.textContent = data.timestamp;// 更新当前时间const now = new Date();currentTimeEl.textContent = now.toLocaleTimeString();}// 更新状态指示器functionupdateStatusIndicators() {fetch('/api/quality_status').then(response => response.json()).then(statusData => {for(const [indicator, status] of Object.entries(statusData)) {const statusEl = document.getElementById(`${indicator}-status`);if(statusEl) {// 移除现有状态类statusEl.classList.remove('status-normal', 'status-warning', 'status-danger');// 添加新状态类statusEl.classList.add(`status-${status}`);// 根据状态更新卡片边框颜色const card = document.getElementById(`${indicator}-card`);if(card) {card.style.borderColor = getStatusColor(status);}}}}).catch(error => console.error('获取状态数据失败:', error));}// 获取状态对应的颜色functiongetStatusColor(status) {switch(status) {case 'normal': return '#27ae60';case 'warning': return '#f39c12';case 'danger': return '#e74c3c';default: return '#eaeaea';}}// 初始化趋势图functioninitTrendChart() {const ctx = document.getElementById('trend-chart').getContext('2d');trendChart = new Chart(ctx, {type: 'line',data: {labels: [],datasets: [{label: '水质指标',data: [],borderColor: '#3498db',backgroundColor: 'rgba(52, 152, 219, 0.1)',borderWidth: 3,fill: true,tension: 0.3}]},options: {responsive: true,maintainAspectRatio: false,plugins: {legend: {display: true,position: 'top',},tooltip: {mode: 'index',intersect: false}},scales: {x: {title: {display: true,text: '时间'},grid: {display: true,color: 'rgba(0, 0, 0, 0.05)'}},y: {title: {display: true,text: '值'},grid: {display: true,color: 'rgba(0, 0, 0, 0.05)'},beginAtZero: false}}}});// 加载初始数据updateTrendChart();}// 更新趋势图functionupdateTrendChart() {fetch('/api/historical_data').then(response => response.json()).then(data => {const indicator = indicatorSelect.value;const timeRange = parseInt(timeRangeSelect.value);let timestamps = data.timestamps;let values = data.data[indicator];// 如果选择了时间范围,则截取数据if(timeRange !== 'all' && timestamps.length > timeRange) {timestamps = timestamps.slice(-timeRange);values = values.slice(-timeRange);}// 更新图表trendChart.data.labels = timestamps;trendChart.data.datasets[0].data = values;trendChart.data.datasets[0].label = indicator;trendChart.update();}).catch(error => console.error('获取历史数据失败:', error));}// 初始化对比图functioninitComparisonChart() {const ctx = document.getElementById('comparison-chart').getContext('2d');comparisonChart = new Chart(ctx, {type: 'bar',data: {labels: [],datasets: [{label: '当前值',data: [],backgroundColor: ['rgba(52, 152, 219, 0.7)','rgba(46, 204, 113, 0.7)','rgba(155, 89, 182, 0.7)','rgba(241, 196, 15, 0.7)','rgba(230, 126, 34, 0.7)','rgba(231, 76, 60, 0.7)'],borderColor: ['rgb(52, 152, 219)','rgb(46, 204, 113)','rgb(155, 89, 182)','rgb(241, 196, 15)','rgb(230, 126, 34)','rgb(231, 76, 60)'],borderWidth: 1}]},options: {responsive: true,maintainAspectRatio: false,plugins: {legend: {display: true,position: 'top',}},scales: {x: {title: {display: true,text: '水质指标'}},y: {title: {display: true,text: '值'},beginAtZero: true}}}});// 加载初始数据updateComparisonChart();}// 更新对比图functionupdateComparisonChart() {fetch('/api/current_data').then(response => response.json()).then(data => {const indicators = Object.keys(data.data);const values = Object.values(data.data);comparisonChart.data.labels = indicators;comparisonChart.data.datasets[0].data = values;comparisonChart.update();}).catch(error => console.error('获取对比数据失败:', error));}// 事件监听器refreshBtn.addEventListener('click', function() {fetchCurrentData();updateTrendChart();updateComparisonChart();});indicatorSelect.addEventListener('change', updateTrendChart);timeRangeSelect.addEventListener('change', updateTrendChart);// 模拟异常按钮anomalyButtons.forEach(button => {button.addEventListener('click', function() {const indicator = this.getAttribute('data-indicator');fetch('/api/simulate_anomaly', {method: 'POST',headers: {'Content-Type': 'application/json',},body: JSON.stringify({indicator: indicator})}).then(response => response.json()).then(data => {if(data.success) {alert(`已模拟${indicator}指标异常`);// 刷新数据fetchCurrentData();updateTrendChart();updateComparisonChart();}}).catch(error => console.error('模拟异常失败:', error));});});// 定时更新数据setInterval(fetchCurrentData, 2000); // 每2秒更新一次当前数据setInterval(updateTrendChart, 10000); // 每10秒更新一次趋势图setInterval(updateComparisonChart, 10000); // 每10秒更新一次对比图// 初始加载数据fetchCurrentData();});

Flask==2.3.3Werkzeug==2.3.7Jinja2==3.1.2
Flask==2.3.3Werkzeug==2.3.7Jinja2==3.1.2这三个库作用是啥,提供阿里云安装镜像地址把这段话给到deepseek,他会提供阿里云镜像安装地址,加快安装速度
Flask==2.3.3:Flask是一个轻量级的Python Web框架,用于构建Web应用程序。它提供了路由、模板渲染、请求和响应处理等基本功能,是开发这个水质数据展示界面的核心框架。Werkzeug==2.3.7:Werkzeug是一个WSGI工具库,它提供了处理HTTP请求、响应、路由、调试等功能的底层支持。Flask框架就是基于Werkzeug构建的。Jinja2==3.1.2:Jinja2是一个模板引擎,用于在Python Web应用中生成动态HTML。Flask使用Jinja2来渲染模板,将Python变量和逻辑嵌入到HTML中,生成最终的网页内容。
作用:Flask是Python的轻量级Web框架,用于快速开发Web应用程序。
提供路由系统,处理URL到函数的映射
处理HTTP请求和响应
支持模板渲染(Jinja2集成)
提供会话管理、错误处理等Web开发基础功能
在这个项目中,Flask用于创建Web服务器和API接口
作用:Werkzeug是WSGI工具包,是Flask的底层依赖。
提供WSGI(Web Server Gateway Interface)实现
处理HTTP请求解析和响应生成
提供URL路由、调试工具、文件上传处理等
包含开发服务器和调试器
是Flask的基石,Flask是基于Werkzeug构建的
作用:Jinja2是Python的模板引擎。
用于生成动态HTML页面
支持模板继承、变量替换、控制结构等
提供安全的HTML自动转义功能
Flask默认使用Jinja2作为模板引擎
在这个项目中,用于渲染index.html模板
# 单个包安装pip install Flask==2.3.3 -i https://mirrors.aliyun.com/pypi/simple/# 安装多个包pip install Flask==2.3.3 Werkzeug==2.3.7 Jinja2==3.1.2 -i https://mirrors.aliyun.com/pypi/simple/# 使用requirements.txt文件安装pip install -r requirements.txt -i https://mirrors.aliyun.com/pypi/simple/

# 水质数据监测系统一个基于Python Flask的水质数据展示网页应用,用于实时监测和展示水质指标。## 功能特点1. **实时数据展示**:显示流量、COD、氨氮、总磷、SS、pH等指标的实时值2. **趋势图展示**:每个指标都有对应的历史趋势图3. **多指标对比**:直观比较各指标当前值4. **状态指示**:根据预设范围显示正常/警告/异常状态5. **异常模拟**:可以手动触发模拟异常数据6. **自动更新**:数据每2秒自动更新## 安装和运行1. 确保已安装Python 3.7或更高版本2. 安装依赖包:pip install -r requirements.txt
3. 运行应用:python app.py
4. 在浏览器中访问:http://localhost:5000## 项目结构- `app.py` - Flask主应用,包含API接口和数据模拟逻辑- `templates/index.html` - 主页面HTML模板- `static/css/style.css` - 页面样式- `static/js/charts.js` - 图表交互和数据处理JavaScript- `requirements.txt` - Python依赖包列表## 数据模拟系统使用随机数生成模拟水质数据,具有以下特点:- 各指标在合理范围内随机波动- pH值变化幅度较小,其他指标变化幅度较大- 可以模拟异常数据以测试系统响应## 技术栈- 后端:Python Flask- 前端:HTML5, CSS3, JavaScript- 图表:Chart.js- 样式:Font Awesome图标库## 自定义配置可以修改`app.py`中的`quality_ranges`字典来调整各指标的正常范围。
确保已安装Python 3.7+和pip
创建项目文件夹,将上述文件按结构放置
在项目根目录运行:pip install -r requirements.txt
运行:python app.py
打开浏览器访问:http://localhost:5000
实时数据展示:顶部卡片显示各水质指标的实时数值和状态
趋势图展示:可以选择不同指标和时间范围查看历史趋势
多指标对比:使用柱状图对比各指标的当前值
状态指示:圆形指示灯显示水质状态(绿色=正常,黄色=警告,红色=异常)
模拟异常:点击"模拟异常"按钮可以测试系统对异常数据的响应
自动更新:数据每2秒自动更新,无需手动刷新
这个系统完全使用Python技术栈开发,包含完整的模拟数据生成和网页展示功能,可以直接运行使用


对上面html模板进行修改,修改为某公园景观关注的指标,比如空气、出水水质、流量指标,需要是可视化大屏的效果,请你修改html内容,其他代码不需要修改我们要求将原本的水质指标改为公园景观关注的指标,如空气、出水水质、流量指标等。同时,要求大屏效果,因此可能需要调整布局和样式,使其更适合大屏展示。由于其他代码(如app.py)不需要修改,我们仅修改HTML模板部分。但是注意,原app.py中定义了六个指标,现在我们需要修改为公园景观关注的指标。由于原代码中使用了六个指标,且模拟数据部分和API都依赖于这些指标,我们不能随意减少指标数量,但可以改变指标的名称和正常范围。然而,根据问题,只修改HTML内容,但实际指标名称在app.py中定义,如果只改HTML不改app.py,那么指标名称对不上。所以我们需要协调一下:要么修改app.py中的指标名称,要么在HTML中使用相同的指标名称。由于问题要求只修改HTML,但为了匹配新的指标,我们假设app.py中的指标已经更改为公园景观相关的指标。但这里我们只能修改HTML,所以我们需要知道新的指标名称是什么?根据问题,新的指标可能是:空气(可能包含多个指标,但这里我们只能选择几个代表性的)、出水水质、流量指标。我们假设新的六个指标为:PM2.5(空气)温度(空气)湿度(空气)出水COD(出水水质)出水氨氮(出水水质)流量(流量指标)但是原代码中每个指标都有对应的卡片和图表,所以我们这里需要将HTML中的指标名称和单位等进行更改。另外,大屏效果通常意味着更大的字体、更紧凑的布局、更醒目的颜色等。我们将对CSS进行一些调整,使其更符合大屏展示。由于问题要求只修改HTML,但为了达到大屏效果,我们也会修改CSS部分(因为CSS是静态文件,属于HTML模板的一部分,所以我们可以在HTML中内嵌CSS,也可以修改外部CSS文件。这里我们选择修改外部CSS,但为了保持完整性,我们也会在HTML中引入一个内联的样式以作调整。不过,原项目结构中有单独的CSS文件,我们可以修改它。但这里为了简单,我们直接在HTML中添加一些内联样式,同时保留对外部CSS的引用。由于我们不知道app.py中具体改了哪些指标,所以我们假设app.py中的指标已经改为:water_quality_data = {'PM2.5': deque(maxlen=100),'温度': deque(maxlen=100),'湿度': deque(maxlen=100),'出水COD': deque(maxlen=100),'出水氨氮': deque(maxlen=100),'流量': deque(maxlen=100)}并且,quality_ranges也需要相应调整,但这里我们只修改HTML,所以假设这些已经调整。因此,我们修改HTML模板,将指标名称替换为上述六个,并调整页面标题和各个部分的描述。同时,为了达到大屏效果,我们将调整布局,可能使用更宽的布局,并调整字体大小。由于时间关系,我们只修改HTML文件,并对CSS进行一些调整。如果原CSS文件不够,我们将在HTML中增加一些内联样式。注意:原CSS文件是独立的,我们可以在HTML中覆盖一些样式,以达到大屏效果。由于我们无法知道原CSS文件的所有内容,因此我们将以内联样式的方式添加一些大屏样式。
<!DOCTYPE html><htmllang="zh-CN"><head><metacharset="UTF-8"><metaname="viewport"content="width=device-width, initial-scale=1.0"><title>公园景观环境监测大屏</title><linkrel="stylesheet"href="{{ url_for('static', filename='css/style.css') }}"><scriptsrc="https://cdn.jsdelivr.net/npm/chart.js"></script><linkrel="stylesheet"href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.4.0/css/all.min.css"><style>/* 大屏专用样式 */body {background-color: #0a1a2d;color: #ffffff;font-family: 'Microsoft YaHei', 'Segoe UI', Arial, sans-serif;overflow-x: hidden;}.container {max-width: 100%;margin: 0 auto;padding: 15px;}.header {background: linear-gradient(90deg, rgba(16, 36, 64, 0.9) 0%, rgba(22, 50, 92, 0.9) 100%);border-radius: 15px;padding: 20px 30px;margin-bottom: 20px;border: 1px solid rgba(64, 156, 255, 0.3);box-shadow: 0 0 30px rgba(0, 150, 255, 0.1);display: flex;justify-content: space-between;align-items: center;}.header h1 {font-size: 32px;font-weight: 700;background: linear-gradient(90deg, #4facfe 0%, #00f2fe 100%);-webkit-background-clip: text;background-clip: text;color: transparent;margin: 0;}.header h1 i {margin-right: 15px;color: #00f2fe;}.park-info {display: flex;gap: 30px;font-size: 16px;}.info-item {text-align: center;}.info-label {color: #8ba3c3;font-size: 14px;margin-bottom: 5px;}.info-value {font-size: 22px;font-weight: bold;color: #4facfe;}.dashboard {display: grid;grid-template-columns: 1fr 1fr 1fr;grid-template-rows: auto auto;gap: 20px;grid-template-areas:"air air water""flow stats stats";}.section {background: rgba(16, 36, 64, 0.7);border-radius: 15px;padding: 20px;border: 1px solid rgba(64, 156, 255, 0.2);backdrop-filter: blur(10px);box-shadow: 0 5px 15px rgba(0, 0, 0, 0.3);}.section-title {font-size: 20px;color: #4facfe;margin-bottom: 15px;padding-bottom: 10px;border-bottom: 1px solid rgba(64, 156, 255, 0.3);display: flex;align-items: center;}.section-title i {margin-right: 10px;}/* 空气质量模块 */.air-quality-section {grid-area: air;}.air-indicators {display: grid;grid-template-columns: repeat(3, 1fr);gap: 15px;margin-bottom: 20px;}.air-card {background: rgba(22, 42, 75, 0.8);border-radius: 12px;padding: 15px;text-align: center;border: 1px solid rgba(64, 156, 255, 0.2);transition: all 0.3s ease;}.air-card:hover {transform: translateY(-5px);box-shadow: 0 10px 20px rgba(0, 0, 0, 0.2);border-color: rgba(64, 156, 255, 0.5);}.air-card h4 {color: #8ba3c3;font-size: 14px;margin-bottom: 10px;}.air-value {font-size: 28px;font-weight: bold;margin-bottom: 5px;}.air-unit {color: #8ba3c3;font-size: 14px;}.air-status {display: inline-block;padding: 3px 10px;border-radius: 20px;font-size: 12px;margin-top: 8px;}.status-excellent { background-color: rgba(0, 200, 83, 0.2); color: #00c853; }.status-good { background-color: rgba(76, 175, 80, 0.2); color: #4caf50; }.status-moderate { background-color: rgba(255, 193, 7, 0.2); color: #ffc107; }.status-poor { background-color: rgba(255, 87, 34, 0.2); color: #ff5722; }/* 水质模块 */.water-quality-section {grid-area: water;}.water-indicators {display: grid;grid-template-columns: 1fr 1fr;gap: 15px;}.water-card {background: rgba(22, 42, 75, 0.8);border-radius: 12px;padding: 15px;border: 1px solid rgba(64, 156, 255, 0.2);}.water-card h4 {color: #8ba3c3;font-size: 14px;margin-bottom: 10px;}.water-value {font-size: 24px;font-weight: bold;margin-bottom: 5px;}.water-unit {color: #8ba3c3;font-size: 14px;}.water-gauge {height: 8px;background: rgba(255, 255, 255, 0.1);border-radius: 4px;margin-top: 10px;overflow: hidden;}.water-fill {height: 100%;border-radius: 4px;transition: width 0.5s ease;}/* 流量模块 */.flow-section {grid-area: flow;}.flow-display {text-align: center;padding: 20px 0;}.flow-value {font-size: 42px;font-weight: bold;background: linear-gradient(90deg, #4facfe 0%, #00f2fe 100%);-webkit-background-clip: text;background-clip: text;color: transparent;margin-bottom: 10px;}.flow-unit {font-size: 18px;color: #8ba3c3;}.flow-change {font-size: 14px;margin-top: 10px;}.positive { color: #00c853; }.negative { color: #ff5722; }/* 统计图表模块 */.stats-section {grid-area: stats;}.chart-container {height: 300px;margin-top: 15px;}.chart-header {display: flex;justify-content: space-between;margin-bottom: 15px;}.chart-selector {display: flex;gap: 10px;}.chart-btn {background: rgba(22, 42, 75, 0.8);border: 1px solid rgba(64, 156, 255, 0.3);color: #8ba3c3;padding: 5px 15px;border-radius: 20px;cursor: pointer;font-size: 14px;transition: all 0.3s ease;}.chart-btn.active {background: rgba(64, 156, 255, 0.2);color: #4facfe;border-color: #4facfe;}.chart-btn:hover {background: rgba(64, 156, 255, 0.1);}/* 底部信息 */.footer {margin-top: 20px;text-align: center;color: #8ba3c3;font-size: 14px;padding: 15px;border-top: 1px solid rgba(255, 255, 255, 0.1);}.data-refresh {display: inline-flex;align-items: center;gap: 10px;background: rgba(22, 42, 75, 0.8);padding: 8px 20px;border-radius: 20px;margin-top: 10px;}.pulse {display: inline-block;width: 10px;height: 10px;border-radius: 50%;background-color: #00c853;margin-right: 5px;animation: pulse 2s infinite;}@keyframes pulse {0% { opacity: 1; }50% { opacity: 0.5; }100% { opacity: 1; }}/* 响应式调整 */@media (max-width: 1200px) {.dashboard {grid-template-columns: 1fr 1fr;grid-template-areas:"air water""flow stats";}}@media (max-width: 768px) {.dashboard {grid-template-columns: 1fr;grid-template-areas:"air""water""flow""stats";}.air-indicators {grid-template-columns: 1fr;}.header {flex-direction: column;text-align: center;gap: 15px;}}</style></head><body><divclass="container"><headerclass="header"><h1><iclass="fas fa-tree"></i> 公园景观环境监测大屏</h1><divclass="park-info"><divclass="info-item"><divclass="info-label">监测位置</div><divclass="info-value">中央景观湖区</div></div><divclass="info-item"><divclass="info-label">监测时间</div><divclass="info-value"id="current-date">--</div></div><divclass="info-item"><divclass="info-label">系统状态</div><divclass="info-value"><spanclass="pulse"></span> 运行正常</div></div></div></header><divclass="dashboard"><!-- 空气质量监测 --><divclass="section air-quality-section"><divclass="section-title"><iclass="fas fa-wind"></i> 空气质量监测</div><divclass="air-indicators"><divclass="air-card"id="pm25-card"><h4>PM2.5</h4><divclass="air-value"id="pm25-value">--</div><divclass="air-unit">μg/m³</div><divclass="air-status status-excellent"id="pm25-status">优</div></div><divclass="air-card"id="temperature-card"><h4>温度</h4><divclass="air-value"id="temperature-value">--</div><divclass="air-unit">°C</div><divclass="air-status status-good"id="temperature-status">舒适</div></div><divclass="air-card"id="humidity-card"><h4>湿度</h4><divclass="air-value"id="humidity-value">--</div><divclass="air-unit">%</div><divclass="air-status status-good"id="humidity-status">适宜</div></div></div><divclass="chart-container"><canvasid="air-trend-chart"></canvas></div></div><!-- 出水水质监测 --><divclass="section water-quality-section"><divclass="section-title"><iclass="fas fa-tint"></i> 出水水质监测</div><divclass="water-indicators"><divclass="water-card"id="ph-card"><h4>pH值</h4><divclass="water-value"id="ph-value">--</div><divclass="water-unit">pH</div><divclass="water-gauge"><divclass="water-fill"id="ph-fill"style="width: 70%; background-color: #4caf50;"></div></div></div><divclass="water-card"id="cod-card"><h4>COD</h4><divclass="water-value"id="cod-value">--</div><divclass="water-unit">mg/L</div><divclass="water-gauge"><divclass="water-fill"id="cod-fill"style="width: 40%; background-color: #ff9800;"></div></div></div><divclass="water-card"id="turbidity-card"><h4>浊度</h4><divclass="water-value"id="turbidity-value">--</div><divclass="water-unit">NTU</div><divclass="water-gauge"><divclass="water-fill"id="turbidity-fill"style="width: 30%; background-color: #00bcd4;"></div></div></div><divclass="water-card"id="do-card"><h4>溶解氧</h4><divclass="water-value"id="do-value">--</div><divclass="water-unit">mg/L</div><divclass="water-gauge"><divclass="water-fill"id="do-fill"style="width: 85%; background-color: #4caf50;"></div></div></div></div><divclass="chart-container"><canvasid="water-trend-chart"></canvas></div></div><!-- 流量监测 --><divclass="section flow-section"><divclass="section-title"><iclass="fas fa-water"></i> 景观水流监测</div><divclass="flow-display"><divclass="flow-value"id="flow-value">0.00</div><divclass="flow-unit">m³/s</div><divclass="flow-change">日变化: <spanid="flow-change-value"class="positive">+2.5%</span></div></div><divclass="chart-container"><canvasid="flow-trend-chart"></canvas></div></div><!-- 综合统计 --><divclass="section stats-section"><divclass="section-title"><iclass="fas fa-chart-bar"></i> 综合数据统计</div><divclass="chart-header"><divclass="chart-title">多指标对比分析</div><divclass="chart-selector"><buttonclass="chart-btn active"data-chart="day">今日</button><buttonclass="chart-btn"data-chart="week">本周</button><buttonclass="chart-btn"data-chart="month">本月</button></div></div><divclass="chart-container"><canvasid="comparison-chart"></canvas></div></div></div><footerclass="footer"><p>© 2023 智慧公园景观监测系统 | 数据更新: <spanid="last-update">--:--:--</span></p><divclass="data-refresh"><iclass="fas fa-sync-alt"></i><span>数据每2秒自动刷新 | 监测点: 公园东区 | 中央湖区 | 北侧林区</span></div></footer></div><scriptsrc="{{ url_for('static', filename='js/charts.js') }}"></script><script>// 更新当前日期时间function updateDateTime() {const now = new Date();const dateStr = now.toLocaleDateString('zh-CN', {year: 'numeric',month: '2-digit',day: '2-digit'});const timeStr = now.toLocaleTimeString('zh-CN');document.getElementById('current-date').textContent = dateStr + ' ' + timeStr;document.getElementById('last-update').textContent = timeStr;// 每秒钟更新一次时间setTimeout(updateDateTime, 1000);}// 初始化时间显示updateDateTime();// 图表按钮切换document.querySelectorAll('.chart-btn').forEach(btn => {btn.addEventListener('click', function() {document.querySelectorAll('.chart-btn').forEach(b => b.classList.remove('active'));this.classList.add('active');// 这里可以添加切换图表数据的逻辑console.log('切换到:', this.dataset.chart, '数据');});});// 模拟数据更新(实际应用中应由后端API提供)function updateMockData() {// 空气质量数据const pm25 = Math.floor(Math.random() * 60) + 20;const temperature = (Math.random() * 10 + 18).toFixed(1);const humidity = Math.floor(Math.random() * 30) + 50;document.getElementById('pm25-value').textContent = pm25;document.getElementById('temperature-value').textContent = temperature;document.getElementById('humidity-value').textContent = humidity;// 更新PM2.5状态const pm25Status = document.getElementById('pm25-status');pm25Status.className = 'air-status ';if (pm25 <= 35) {pm25Status.classList.add('status-excellent');pm25Status.textContent = '优';} else if (pm25 <= 75) {pm25Status.classList.add('status-good');pm25Status.textContent = '良';} else if (pm25 <= 115) {pm25Status.classList.add('status-moderate');pm25Status.textContent = '轻度';} else {pm25Status.classList.add('status-poor');pm25Status.textContent = '中度';}// 水质数据const ph = (Math.random() * 1.5 + 6.5).toFixed(1);const cod = Math.floor(Math.random() * 30) + 10;const turbidity = (Math.random() * 15 + 5).toFixed(1);const doValue = (Math.random() * 4 + 5).toFixed(1);document.getElementById('ph-value').textContent = ph;document.getElementById('cod-value').textContent = cod;document.getElementById('turbidity-value').textContent = turbidity;document.getElementById('do-value').textContent = doValue;// 更新仪表填充document.getElementById('ph-fill').style.width = ((ph - 6.5) / 1.5 * 100) + '%';document.getElementById('cod-fill').style.width = (cod / 40 * 100) + '%';document.getElementById('turbidity-fill').style.width = (turbidity / 20 * 100) + '%';document.getElementById('do-fill').style.width = ((doValue - 5) / 4 * 100) + '%';// 流量数据const flow = (Math.random() * 2.5 + 0.5).toFixed(2);document.getElementById('flow-value').textContent = flow;// 每2秒更新一次模拟数据setTimeout(updateMockData, 2000);}// 启动模拟数据更新updateMockData();</script></body></html>
@app.route('/')def index():return render_template('default.html',indicators=list(water_quality_data.keys()), ranges=quality_ranges)
