From ef440177b302857c7b474d8f4d19d5438634e4d3 Mon Sep 17 00:00:00 2001 From: YueL1331 <358700404@qq.com> Date: Wed, 7 Jan 2026 15:57:34 +0800 Subject: [PATCH] test --- 1.1/test1.py | 116 ++++---- zhandianxinxi/my-vue-app/vite.config.js | 16 -- .../.gitignore | 0 .../.vscode/extensions.json | 0 .../README.md | 0 .../index.html | 0 .../package-lock.json | 0 .../package.json | 0 .../public/vite.svg | 0 .../src/App.vue | 263 +++++++++++------- .../src/assets/vue.svg | 0 .../src/main.js | 0 .../光谱数据监控/src/siderVue.vue | 39 +++ .../光谱数据监控/src/sidevueold.vue | 60 ++++ .../src/style.css | 0 .../光谱数据监控/vite.config.js | 30 ++ 16 files changed, 354 insertions(+), 170 deletions(-) delete mode 100644 zhandianxinxi/my-vue-app/vite.config.js rename zhandianxinxi/{my-vue-app => 光谱数据监控}/.gitignore (100%) rename zhandianxinxi/{my-vue-app => 光谱数据监控}/.vscode/extensions.json (100%) rename zhandianxinxi/{my-vue-app => 光谱数据监控}/README.md (100%) rename zhandianxinxi/{my-vue-app => 光谱数据监控}/index.html (100%) rename zhandianxinxi/{my-vue-app => 光谱数据监控}/package-lock.json (100%) rename zhandianxinxi/{my-vue-app => 光谱数据监控}/package.json (100%) rename zhandianxinxi/{my-vue-app => 光谱数据监控}/public/vite.svg (100%) rename zhandianxinxi/{my-vue-app => 光谱数据监控}/src/App.vue (59%) rename zhandianxinxi/{my-vue-app => 光谱数据监控}/src/assets/vue.svg (100%) rename zhandianxinxi/{my-vue-app => 光谱数据监控}/src/main.js (100%) create mode 100644 zhandianxinxi/光谱数据监控/src/siderVue.vue create mode 100644 zhandianxinxi/光谱数据监控/src/sidevueold.vue rename zhandianxinxi/{my-vue-app => 光谱数据监控}/src/style.css (100%) create mode 100644 zhandianxinxi/光谱数据监控/vite.config.js diff --git a/1.1/test1.py b/1.1/test1.py index 62cfd18..dbed975 100644 --- a/1.1/test1.py +++ b/1.1/test1.py @@ -1,10 +1,11 @@ import os +import sys import json import threading import requests import logging from datetime import datetime -from flask import Flask, jsonify +from flask import Flask, jsonify, send_from_directory from flask_sqlalchemy import SQLAlchemy from flask_cors import CORS from flask_apscheduler import APScheduler @@ -13,35 +14,62 @@ from lxml import etree # --- 配置日志 --- logging.basicConfig(level=logging.INFO, format='%(asctime)s - %(levelname)s - %(message)s') -app = Flask(__name__) + +# --- 关键路径处理函数 (适配 PyInstaller) --- +def get_base_path(): + """获取运行时及其所在目录,适配开发环境和打包后的EXE环境""" + if getattr(sys, 'frozen', False): + # 如果是打包后的 exe,sys.executable 是 exe 的路径 + return os.path.dirname(sys.executable) + # 开发环境下,是当前脚本的路径 + return os.path.dirname(os.path.abspath(__file__)) + + +def get_static_path(): + """获取 Vue 静态资源 dist 的路径""" + if getattr(sys, 'frozen', False): + # PyInstaller 打包时,资源文件会被解压到 sys._MEIPASS 临时目录 + # 我们需要在打包命令中指定 --add-data "dist;dist" + return os.path.join(sys._MEIPASS, 'dist') + # 开发环境 + return os.path.join(os.path.dirname(os.path.abspath(__file__)), 'dist') + + +# --- Flask 初始化 --- +# static_folder 指向 Vue 打包后的 dist 目录 +# static_url_path='' 表示静态文件不需要 /static 前缀 +dist_folder = get_static_path() +app = Flask(__name__, static_folder=dist_folder, static_url_path='') CORS(app) # --- 数据库配置 --- -app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///monitor_data.db' +# 确保数据库生成在 exe 同级目录下,而不是临时文件夹中 +db_path = os.path.join(get_base_path(), 'monitor_data.db') +app.config['SQLALCHEMY_DATABASE_URI'] = f'sqlite:///{db_path}' app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False -app.config['SCHEDULER_API_ENABLED'] = True # 允许通过API查看调度任务 +app.config['SCHEDULER_API_ENABLED'] = True db = SQLAlchemy(app) scheduler = APScheduler() -# --- 模型定义 --- +# --- 模型定义 (保持不变) --- class MonitorRecord(db.Model): id = db.Column(db.Integer, primary_key=True) source = db.Column(db.String(50)) name = db.Column(db.String(100)) - status = db.Column(db.String(50)) # 正常 / 离线 / 异常 + status = db.Column(db.String(50)) reason = db.Column(db.String(255)) offset = db.Column(db.String(50)) - latest_time = db.Column(db.String(50)) # 数据时间 - check_time = db.Column(db.String(50)) # 爬取时间 + latest_time = db.Column(db.String(50)) + check_time = db.Column(db.String(50)) content = db.Column(db.Text, nullable=True) with app.app_context(): db.create_all() -# --- 爬虫配置 --- +# --- 爬虫配置 (保持不变) --- CONFIG = { "106": { "base_url": "http://106.75.72.40:7500/api/proxy/tcp", @@ -57,7 +85,7 @@ CONFIG = { is_running = False -# --- 核心辅助函数 --- +# --- 核心辅助函数 (保持不变) --- def calculate_offset(latest_time_str): if not latest_time_str or latest_time_str == "N/A": return "从未同步" @@ -72,9 +100,6 @@ def calculate_offset(latest_time_str): def save_record(source, name, status, reason, latest_time="N/A", content=None): - """ - Upsert 逻辑: 有则更新,无则插入 - """ record = MonitorRecord.query.filter_by(source=source, name=name).first() now_str = datetime.now().strftime("%Y-%m-%d %H:%M:%S") current_offset = calculate_offset(latest_time) @@ -82,11 +107,9 @@ def save_record(source, name, status, reason, latest_time="N/A", content=None): if record: if content is not None: record.content = content if latest_time != "N/A": record.latest_time = latest_time - record.status = status record.reason = reason record.check_time = now_str - # 使用当前库里的时间重新计算 offset time_base = latest_time if latest_time != "N/A" else record.latest_time record.offset = calculate_offset(time_base) else: @@ -96,17 +119,15 @@ def save_record(source, name, status, reason, latest_time="N/A", content=None): check_time=now_str, content=content ) db.session.add(new_record) - try: db.session.commit() except Exception as e: db.session.rollback() logging.error(f"DB Error: {e}") - return f"{source}_{name}" -# --- 106 逻辑 --- +# --- 业务逻辑函数 (保持不变) --- def get_106_dynamic_token(port): try: login_url = f"http://106.75.72.40:{port}/api/login" @@ -140,6 +161,7 @@ def find_closest_item(items, is_date_level=True): def run_106_logic(active_set): + # (保持原样,省略以节省空间,直接用你原本的逻辑即可) c = CONFIG["106"] today_str = datetime.now().strftime("%Y_%m_%d") main_headers = {"Authorization": c["primary_auth"], "User-Agent": "Mozilla/5.0"} @@ -150,12 +172,10 @@ def run_106_logic(active_set): name = item.get('name', '') if not name.lower().endswith('_data'): continue if "TOWER" not in name.upper(): continue - if str(item.get('status')).lower() != 'online': key = save_record("106网站", name, "离线", f"设备状态: {item.get('status')}") active_set.add(key) continue - try: port = item.get('conf', {}).get('remote_port') token = get_106_dynamic_token(port) @@ -163,35 +183,24 @@ def run_106_logic(active_set): key = save_record("106网站", name, "异常", "Token获取失败") active_set.add(key) continue - headers = {"Authorization": c["primary_auth"], "x-auth": token} api_root = "/api/resources/Data/" if "TOWER_" in name.upper() else "/api/resources/data/" - - # 寻找日期 res1 = requests.get(f"http://106.75.72.40:{port}{api_root}", headers=headers, timeout=10) best_date = find_closest_item(res1.json().get('items', []), True) - if not best_date or best_date[2] != today_str: key = save_record("106网站", name, "正常", "未找到今日文件夹", latest_time=best_date[2] if best_date else "N/A") active_set.add(key) continue - - # 寻找文件 date_path = f"{api_root}{best_date[2]}/" res2 = requests.get(f"http://106.75.72.40:{port}{date_path}", headers=headers, timeout=10) best_file = find_closest_item(res2.json().get('items', []), False) - if not best_file: key = save_record("106网站", name, "正常", "今日文件夹为空", latest_time=today_str) active_set.add(key) continue - - # 获取内容 file_item = best_file[1] full_path = file_item.get('path') or f"{date_path}{file_item.get('name')}" - - # 判断下载方式 is_tower_i = "TOWER" in name.upper() and "TOWER_" not in name.upper() if is_tower_i: download_url = f"http://106.75.72.40:{port}/api/raw{full_path}" @@ -201,10 +210,8 @@ def run_106_logic(active_set): file_api_url = f"http://106.75.72.40:{port}/api/resources{full_path}" res3 = requests.get(file_api_url, headers=headers, timeout=20) final_content = res3.json().get('content', '') - key = save_record("106网站", name, "正常", "同步成功", latest_time=today_str, content=final_content) active_set.add(key) - except Exception as e: key = save_record("106网站", name, "异常", f"采集错误: {str(e)[:50]}") active_set.add(key) @@ -212,15 +219,14 @@ def run_106_logic(active_set): logging.error(f"106 Global Error: {e}") -# --- 82 逻辑 --- def run_82_logic(active_set): + # (保持原样,直接用你原本的逻辑即可) c = CONFIG["82"] session = requests.Session() try: session.post(f"{c['base_url']}/login.php", data=c["login"], timeout=10) resp = session.post(f"{c['base_url']}/GetStationList.php", timeout=10) stations = etree.HTML(resp.content).xpath('//option/@value') - for sid in [s for s in stations if s]: try: r = session.post(f"{c['base_url']}/getLastWeatherData.php", data=str(sid), @@ -242,22 +248,15 @@ def run_82_logic(active_set): logging.error(f"82 Global Error: {e}") -# --- 核心任务逻辑 --- def execute_monitor_task(): global is_running - if is_running: - logging.warning("Task already running, skipping...") - return - + if is_running: return is_running = True logging.info("Starting monitor task...") - with app.app_context(): active_set = set() run_106_logic(active_set) run_82_logic(active_set) - - # 掉线处理 all_records = MonitorRecord.query.all() now_str = datetime.now().strftime("%Y-%m-%d %H:%M:%S") for record in all_records: @@ -266,17 +265,15 @@ def execute_monitor_task(): record.reason = "设备本次未出现" record.check_time = now_str record.offset = calculate_offset(record.latest_time) - try: db.session.commit() except: db.session.rollback() - is_running = False logging.info("Monitor task finished.") -# --- 路由 --- +# --- API 路由 (保持不变) --- @app.route('/api/run', methods=['POST']) def manual_start(): if is_running: return jsonify({"status": "busy"}), 400 @@ -298,18 +295,33 @@ def logs(): } for l in data]) -# --- 调度器配置 --- -# id: 任务ID, func: 任务函数(字符串路径或引用), trigger: cron(定时), hour/minute: 时间 +# --- 新增: 前端页面托管路由 --- +@app.route('/') +def serve_index(): + return send_from_directory(app.static_folder, 'index.html') + + +@app.route('/') +def serve_static_files(path): + # 尝试在 dist 目录寻找文件 (css, js, icons) + file_path = os.path.join(app.static_folder, path) + if os.path.exists(file_path): + return send_from_directory(app.static_folder, path) + # 如果找不到文件(例如刷新页面时的路由),返回index.html让Vue Router处理 + return send_from_directory(app.static_folder, 'index.html') + + +# --- 调度器与启动 --- @scheduler.task('cron', id='daily_job', hour=10, minute=0) def auto_run_task(): with app.app_context(): - logging.info("Auto scheduler triggered.") - # 在新线程中运行,避免阻塞调度器主线程 threading.Thread(target=execute_monitor_task).start() if __name__ == "__main__": scheduler.init_app(app) scheduler.start() - # 注意:debug=True 可能会导致调度器在开发模式下运行两次,生产环境建议关闭 debug - app.run(debug=True, port=5000, use_reloader=False) \ No newline at end of file + # Host='0.0.0.0' 允许外部IP访问 + # Port=5000 (确保 Windows 防火墙开放了此端口) + print("应用正在启动... 请确保 dist 文件夹与脚本/exe 同级或已被打包") + app.run(host='0.0.0.0', port=5000, debug=False, use_reloader=False) diff --git a/zhandianxinxi/my-vue-app/vite.config.js b/zhandianxinxi/my-vue-app/vite.config.js deleted file mode 100644 index ecf6339..0000000 --- a/zhandianxinxi/my-vue-app/vite.config.js +++ /dev/null @@ -1,16 +0,0 @@ -// vite.config.js -import { defineConfig } from 'vite' -import vue from '@vitejs/plugin-vue' - -export default defineConfig({ - plugins: [vue()], - server: { - proxy: { - '/api': { - target: 'http://127.0.0.1:5000', // 必须指向你的 Flask 地址 - changeOrigin: true, - rewrite: (path) => path // 保持路径 /api 不变 - } - } - } -}) diff --git a/zhandianxinxi/my-vue-app/.gitignore b/zhandianxinxi/光谱数据监控/.gitignore similarity index 100% rename from zhandianxinxi/my-vue-app/.gitignore rename to zhandianxinxi/光谱数据监控/.gitignore diff --git a/zhandianxinxi/my-vue-app/.vscode/extensions.json b/zhandianxinxi/光谱数据监控/.vscode/extensions.json similarity index 100% rename from zhandianxinxi/my-vue-app/.vscode/extensions.json rename to zhandianxinxi/光谱数据监控/.vscode/extensions.json diff --git a/zhandianxinxi/my-vue-app/README.md b/zhandianxinxi/光谱数据监控/README.md similarity index 100% rename from zhandianxinxi/my-vue-app/README.md rename to zhandianxinxi/光谱数据监控/README.md diff --git a/zhandianxinxi/my-vue-app/index.html b/zhandianxinxi/光谱数据监控/index.html similarity index 100% rename from zhandianxinxi/my-vue-app/index.html rename to zhandianxinxi/光谱数据监控/index.html diff --git a/zhandianxinxi/my-vue-app/package-lock.json b/zhandianxinxi/光谱数据监控/package-lock.json similarity index 100% rename from zhandianxinxi/my-vue-app/package-lock.json rename to zhandianxinxi/光谱数据监控/package-lock.json diff --git a/zhandianxinxi/my-vue-app/package.json b/zhandianxinxi/光谱数据监控/package.json similarity index 100% rename from zhandianxinxi/my-vue-app/package.json rename to zhandianxinxi/光谱数据监控/package.json diff --git a/zhandianxinxi/my-vue-app/public/vite.svg b/zhandianxinxi/光谱数据监控/public/vite.svg similarity index 100% rename from zhandianxinxi/my-vue-app/public/vite.svg rename to zhandianxinxi/光谱数据监控/public/vite.svg diff --git a/zhandianxinxi/my-vue-app/src/App.vue b/zhandianxinxi/光谱数据监控/src/App.vue similarity index 59% rename from zhandianxinxi/my-vue-app/src/App.vue rename to zhandianxinxi/光谱数据监控/src/App.vue index 158b49c..ce9ad40 100644 --- a/zhandianxinxi/my-vue-app/src/App.vue +++ b/zhandianxinxi/光谱数据监控/src/App.vue @@ -4,39 +4,46 @@
- 红色:已离线 / 异常 / 滞后>7天 - 橘色:滞后 2-7 天 - 黄色:滞后 1-2 天 - 绿色:正常且今日已同步 + 红色:已离线 / 异常 / 滞后>7天 + 橘色:滞后 2-7 天 + 黄色:滞后 1-2 天 + 绿色:正常且今日已同步
-
+
- + 全部 - 106 代理 - 82 气象站 + 106 塔上光谱仪 + 82 高光谱传感器 - +
- - 屏蔽选中 + + 屏蔽选中
@@ -48,32 +55,37 @@ v-loading="isRunning" @selection-change="val => selectedRows = val" :row-class-name="tableRowClassName" + style="width: 100%" > - - + + + + + + - - - - + + - - - + + + + + @@ -81,31 +93,43 @@ - -
-
- - {{ formatDisplayName(activeDevice.name) }} - {{ getStatusLabel(activeDevice) }} - {{ activeDevice.latest_time }} - {{ activeDevice.check_time }} - -
-
-

- - {{ is106Site ? '光谱能量分布 (已滤除饱和值)' : '气象站光谱数据 (Up/Down Spec)' }} -

-
-
-
- 型号: {{ module.model }} - SN: {{ module.sn }} -
-
-
-
-
+ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
@@ -115,7 +139,18 @@ import { ref, reactive, computed, onMounted, onBeforeUnmount, nextTick } from 'v import axios from 'axios' import * as echarts from 'echarts' import { ElMessage, ElMessageBox } from 'element-plus' -import { Loading, CircleCheck, Refresh, DataLine } from '@element-plus/icons-vue' // 确保引入图标 +import { Loading, CircleCheck, Refresh, DataLine } from '@element-plus/icons-vue' +import sidevueold from "./sidevueold.vue" +// --- 响应式布局状态 --- +const windowWidth = ref(window.innerWidth) +const isMobile = computed(() => windowWidth.value < 768) + +// 窗口大小监听函数 +const handleResize = () => { + windowWidth.value = window.innerWidth + // 触发图表重绘 + chartInstances.forEach(chart => chart && chart.resize()) +} // --- 状态变量 --- const rawData = ref([]) @@ -126,14 +161,23 @@ const showHidden = ref(false) const drawerVisible = ref(false) const activeDevice = ref(null) const filters = reactive({ site: 'all', keyword: '' }) -const multipleTableRef = ref() // 表格引用 +const multipleTableRef = ref() +let chartInstances = [] // 存储图表实例以便resize -// 初始化隐藏列表(从 LocalStorage 读取) +// 初始化隐藏列表 const ignoredList = ref(JSON.parse(localStorage.getItem('hide_list') || '[]')) let autoRefreshTimer = null // --- 工具函数 --- +const formatSource = (source) => { + if (!source) return '' + const s = source.toString() + if (s.includes('106')) return '106 塔上光谱仪' + if (s.includes('82')) return '82 高光谱传感器' + return s +} + const formatDisplayName = (name) => { if (!name) return '' return name.split('_').map(part => part.charAt(0).toUpperCase() + part.slice(1).toLowerCase()).join('_') @@ -173,12 +217,12 @@ const getLevel = (row) => { const getStatusLabel = (row) => { if (row.status === '已离线') return '已离线' - if (row.status === '异常') return '采集异常' + if (row.status === '异常') return '异常' const level = getLevel(row) - if (level === 4) return '数据缺失' - if (level === 3) return '滞后 >2天' - if (level === 2) return '昨日数据' - return '在线(今日)' + if (level === 4) return '缺失' + if (level === 3) return '>2天' + if (level === 2) return '昨日' + return '在线' } const getStatusColor = (row) => { @@ -198,49 +242,35 @@ const formatReason = (row) => { return '✅ 同步正常' } -// --- 隐藏/恢复逻辑 (修复重点) --- - -// 1. 判断是否被隐藏 +// --- 隐藏/恢复逻辑 --- const isHidden = (name) => ignoredList.value.includes(name) -// 2. 恢复设备(从屏蔽列表中移除) const restoreDevice = (name) => { if (!name) return - // 过滤掉要恢复的名字 ignoredList.value = ignoredList.value.filter(item => item !== name) - // 更新本地存储 localStorage.setItem('hide_list', JSON.stringify(ignoredList.value)) ElMessage.success('设备已恢复显示') } -// 3. 屏蔽选中设备 const hideSelected = () => { if (selectedRows.value.length === 0) return - const namesToHide = selectedRows.value.map(row => row.name) let count = 0 - namesToHide.forEach(name => { if (!ignoredList.value.includes(name)) { ignoredList.value.push(name) count++ } }) - if (count > 0) { localStorage.setItem('hide_list', JSON.stringify(ignoredList.value)) ElMessage.warning(`已屏蔽 ${count} 个设备`) - - // 清空表格选中状态 - if (multipleTableRef.value) { - multipleTableRef.value.clearSelection() - } + if (multipleTableRef.value) multipleTableRef.value.clearSelection() } else { ElMessage.info('选中的设备已在屏蔽列表中') } } -// 4. 显示详情 const showDetails = (row) => { activeDevice.value = row drawerVisible.value = true @@ -249,16 +279,10 @@ const showDetails = (row) => { // --- 过滤与排序 --- const sortedData = computed(() => { return rawData.value.filter(d => { - // 基础过滤:站点 + 关键词 const basicMatch = (filters.site === 'all' || d.source.includes(filters.site)) && d.name.toLowerCase().includes(filters.keyword.toLowerCase()) - - // 隐藏逻辑:如果勾选了"显示屏蔽设备",则显示所有;否则过滤掉在 ignoredList 中的 - if (showHidden.value) { - return basicMatch - } else { - return basicMatch && !isHidden(d.name) - } + if (showHidden.value) return basicMatch + else return basicMatch && !isHidden(d.name) }).sort((a, b) => getLevel(b) - getLevel(a)) }) @@ -267,7 +291,6 @@ const tableRowClassName = ({ row }) => row.status === '已离线' ? 'offline-row // --- 刷新逻辑 --- const fetchLogs = async () => { try { - // 此处仅为示例 URL,请确保后端 API 地址正确 const res = await axios.get('/api/logs') rawData.value = res.data if (res.data.length > 0) { @@ -275,7 +298,6 @@ const fetchLogs = async () => { lastCheckTime.value = latest.check_time } } catch (e) { - // 开发环境演示数据,实际生产请删除此块 console.warn("API Error, using mock data for display") } } @@ -297,15 +319,12 @@ const handleManualRefresh = async (force = false) => { `数据更新于 ${hours.toFixed(1)} 小时前。后端每日10点自动更新,通常无需手动操作。\n是否强制重新爬取?`, '数据尚新', { confirmButtonText: '强制爬取', cancelButtonText: '仅加载最新', type: 'warning' } ) - // 如果用户确认强制爬取,继续往下执行 } catch { - // 如果用户取消,只刷新日志 fetchLogs() ElMessage.success('已加载最新数据库记录') return } } - try { isRunning.value = true await axios.post('/api/run') @@ -321,15 +340,11 @@ const handleManualRefresh = async (force = false) => { const is106Site = computed(() => activeDevice.value?.source?.includes('106')) const currentChartModules = computed(() => { if (!activeDevice.value?.content || activeDevice.value.content === '{}') return [] - if (is106Site.value) { const modules = [] - // 简单的正则解析,根据实际数据格式调整 const infoRegex = /FS\d_Info,Model,([^,]+),SN,([^,]+).*?Wavelength,([\d\.,\s]+)/gs let match - // 避免死循环,复制一份字符串操作 const contentStr = activeDevice.value.content - while ((match = infoRegex.exec(contentStr)) !== null) { const wavelengths = match[3].split(',').map(Number).filter(n => !isNaN(n)) const series = [] @@ -337,7 +352,9 @@ const currentChartModules = computed(() => { const dMatch = contentStr.match(new RegExp(`${match[1].trim()}_P${p}[^0-9-]*([\\d\\.,\\s-]+)`, 'i')) if (dMatch) { const vals = dMatch[1].split(',').map(v => { - const n = parseFloat(v); return n > 65500 ? null : n + const n = parseFloat(v); + // 修改点 2:不再判断 n > 65500,直接返回原始值 n + return n; }) if (vals.some(v => v !== null)) series.push({ name: `P${p}`, data: vals, color: ['#5470c6', '#91cc75', '#fac858', '#ee6666'][p-1] }) } @@ -356,16 +373,19 @@ const currentChartModules = computed(() => { }) const initCharts = () => { + chartInstances = [] // 清空旧实例引用 nextTick(() => { currentChartModules.value.forEach((m, i) => { const dom = document.getElementById(`chart-${i}`) if (dom) { if (echarts.getInstanceByDom(dom)) echarts.getInstanceByDom(dom).dispose() const chart = echarts.init(dom) + chartInstances.push(chart) chart.setOption({ - title: { text: is106Site.value ? `SN: ${m.sn}` : m.title, left: 'center', top: 10 }, - tooltip: { trigger: 'axis' }, legend: { top: 35 }, - grid: { top: 70, bottom: 30, right: 30, left: 50 }, + title: { text: is106Site.value ? `SN: ${m.sn}` : m.title, left: 'center', top: 10, textStyle: { fontSize: isMobile.value ? 14 : 18 } }, + tooltip: { trigger: 'axis', confine: true }, + legend: { top: 35, type: 'scroll' }, + grid: { top: 70, bottom: 30, right: isMobile.value ? 10 : 30, left: isMobile.value ? 40 : 50 }, xAxis: { type: 'category', data: m.xAxis, boundaryGap: false }, yAxis: { type: 'value', min: 'dataMin', max: 'dataMax' }, series: m.series.map(s => ({ @@ -386,27 +406,33 @@ const initCharts = () => { // --- 生命周期 --- onMounted(() => { - fetchLogs() // 初次加载 + document.title = "光谱数据监控" + fetchLogs() + window.addEventListener('resize', handleResize) autoRefreshTimer = setInterval(() => { if (!isRunning.value) fetchLogs() }, 300000) }) onBeforeUnmount(() => { + window.removeEventListener('resize', handleResize) if (autoRefreshTimer) clearInterval(autoRefreshTimer) + chartInstances.forEach(c => c && c.dispose()) }) diff --git a/zhandianxinxi/my-vue-app/src/assets/vue.svg b/zhandianxinxi/光谱数据监控/src/assets/vue.svg similarity index 100% rename from zhandianxinxi/my-vue-app/src/assets/vue.svg rename to zhandianxinxi/光谱数据监控/src/assets/vue.svg diff --git a/zhandianxinxi/my-vue-app/src/main.js b/zhandianxinxi/光谱数据监控/src/main.js similarity index 100% rename from zhandianxinxi/my-vue-app/src/main.js rename to zhandianxinxi/光谱数据监控/src/main.js diff --git a/zhandianxinxi/光谱数据监控/src/siderVue.vue b/zhandianxinxi/光谱数据监控/src/siderVue.vue new file mode 100644 index 0000000..5b99c5e --- /dev/null +++ b/zhandianxinxi/光谱数据监控/src/siderVue.vue @@ -0,0 +1,39 @@ + + + + + diff --git a/zhandianxinxi/光谱数据监控/src/sidevueold.vue b/zhandianxinxi/光谱数据监控/src/sidevueold.vue new file mode 100644 index 0000000..0024ee1 --- /dev/null +++ b/zhandianxinxi/光谱数据监控/src/sidevueold.vue @@ -0,0 +1,60 @@ + + + + + diff --git a/zhandianxinxi/my-vue-app/src/style.css b/zhandianxinxi/光谱数据监控/src/style.css similarity index 100% rename from zhandianxinxi/my-vue-app/src/style.css rename to zhandianxinxi/光谱数据监控/src/style.css diff --git a/zhandianxinxi/光谱数据监控/vite.config.js b/zhandianxinxi/光谱数据监控/vite.config.js new file mode 100644 index 0000000..09867e6 --- /dev/null +++ b/zhandianxinxi/光谱数据监控/vite.config.js @@ -0,0 +1,30 @@ +import { fileURLToPath, URL } from 'node:url' +import { defineConfig } from 'vite' +import vue from '@vitejs/plugin-vue' + +export default defineConfig({ + // --- 强烈建议新增这一行 --- + // 这确保 index.html 引用 css/js 时使用相对路径, + // 避免 Flask 托管时出现找不到文件的 404 错误。 + base: './', + + plugins: [vue()], + resolve: { + alias: { + '@': fileURLToPath(new URL('./src', import.meta.url)) + } + }, + + // --- 关于这段 server 配置 --- + // 这里的配置仅在你自己电脑上写代码(npm run dev)时有效。 + // 打包(npm run build)后,前端请求会直接发给同源的 Flask, + // 所以这里填什么 IP 对打包后的程序没有影响,不用改。 + server: { + proxy: { + '/api': { + target: 'http://127.0.0.1:5000', + changeOrigin: true + } + } + } +})