feat(calibration): 增加日志搜索功能并优化用户界面

- 添加日志搜索功能,支持快捷键操作
- 优化设备参数显示,隐藏 reserve 字段
- 调整日志高亮显示,增加搜索结果导航
- 重新布局快捷键提示和版本信息
- 优化方案列表获取逻辑,确保数据同步- 校验标定中检测和标定后检测至少选择一个
- 优化断路器校准参数显示样式
- 更新软件版本号至 1.0.6
This commit is contained in:
fhysy 2025-07-11 17:36:20 +08:00
parent 27ce508fc2
commit 52597dde2e
14 changed files with 3870 additions and 2847 deletions

View File

@ -1,7 +1,7 @@
# 应用程序的唯一标识符
appId: gy.calibration.pc
# 应用程序的名称
productName: 谷云标定工具
productName: 谷云校表产测
# 定义构建资源和输出文件的目录
directories:
buildResources: build

View File

@ -1,6 +1,6 @@
{
"name": "calibration-pc",
"version": "1.0.4",
"version": "1.0.6",
"description": "谷云开发部开发的断路器标定软件",
"main": "./out/main/index.js",
"author": "example.com",

View File

@ -1,35 +1,19 @@
server:
address: ":8888"
openapiPath: "/api.json"
swaggerPath: "/swagger"
serverMain:
address: "8000"
serverRoot: "resource/public"
logger:
level : "all"
stdout: true
path: "logs"
level: "info"
# GRPC Server.
grpc:
address: ""
name: "demo"
logPath: ""
logStdout: true
errorStack: true
errorLogEnabled: true
errorLogPattern: "error-{Ymd}.log"
accessLogEnabled: true
accessLogPattern: "access-{Ymd}.log"
# Database.
database:
logger:
level: "all"
stdout: true
default:
link: "mysql:root:12345678@tcp(127.0.0.1:3306)/test"
debug: true
plugin:
path: "./plugins"
#台体编号
equipment: HS-9203M-0001
#设备总数
totalNum: 16
#起始端口号
startAddr: 3
#测试源电压波动比例
fluctuateRatioU: 90
#测试源电压波动比例
fluctuateRatioI: 90

BIN
resources/service/data.db Normal file

Binary file not shown.

BIN
resources/service/hscom.dll Normal file

Binary file not shown.

Binary file not shown.

View File

@ -0,0 +1,713 @@
[
{
"key": "c_id",
"name": "芯片id",
"addr": 0,
"len": 4,
"dataType": "string",
"rwType": "R",
"default": "0",
"precision": "",
"unit": ""
},
{
"id": 0,
"key": "calibration_conf",
"name": "标定参数",
"addr": 30018,
"len": 5,
"dataType": "struct",
"rwType": "R",
"default": "",
"precision": "",
"unit": "",
"format": [
{
"index": 0,
"size": 16,
"key": "ec",
"name": "脉冲常数",
"unit": "",
"value": "3200"
},
{
"index": 16,
"size": 16,
"key": "u",
"name": "电压",
"unit": "V",
"value": "220"
},
{
"index": 32,
"size": 16,
"key": "i",
"name": "电流",
"unit": "A",
"value": "10"
},
{
"index": 48,
"size": 16,
"key": "pt",
"name": "电压比",
"unit": "",
"value": "1981"
},
{
"index": 64,
"size": 16,
"key": "ct",
"name": "电流比",
"unit": "",
"value": "2000"
},
{
"index": 80,
"size": 16,
"key": "ileak",
"name": "漏电流",
"unit": "mA",
"value": "20"
},
{
"index": 96,
"size": 8,
"key": "ig",
"name": "电流通道放大倍数",
"unit": "",
"value": "0"
},
{
"index": 104,
"size": 8,
"key": "vg",
"name": "电压通道放大倍数",
"unit": "",
"value": "0"
},
{
"index": 112,
"size": 8,
"key": "ilg",
"name": "漏电流通道放大倍数",
"unit": "",
"value": "3"
},
{
"index": 120,
"size": 8,
"key": "reserve",
"name": "保留位",
"unit": "",
"value": "0"
},
{
"index": 128,
"size": 32,
"key": "isr",
"name": "电流采样电阻",
"unit": "10mΩ",
"value": "440"
}
]
},
{
"key": "last_event",
"name": "事件",
"addr": 50000,
"len": 1,
"dataType": "string",
"rwType": "R",
"default": "0",
"precision": "",
"unit": ""
},
{
"key": "last_event_time",
"name": "事件发生时间",
"addr": 50001,
"len": 1,
"dataType": "time",
"rwType": "R",
"default": "0",
"precision": "",
"unit": ""
},
{
"key": "update_time",
"name": "时间戳",
"addr": 50002,
"len": 1,
"dataType": "time",
"rwType": "R",
"default": "0",
"precision": "",
"unit": ""
},
{
"key": "switch",
"name": "开关状态",
"addr": 50003,
"len": 1,
"dataType": "uint32",
"rwType": "R",
"default": "",
"precision": "",
"unit": ""
},
{
"key": "Freq",
"name": "电网频率",
"addr": 50004,
"len": 1,
"dataType": "uint32",
"rwType": "R",
"default": "0",
"precision": "0.01",
"unit": "Hz"
},
{
"key": "Leak_I",
"name": "漏电流/N相电流",
"addr": 50005,
"len": 1,
"dataType": "uint32",
"rwType": "R",
"default": "0",
"precision": "1",
"unit": "mA"
},
{
"key": "Tn_1",
"name": "N相进线温度",
"addr": 50006,
"len": 1,
"dataType": "int32",
"rwType": "R",
"default": "0",
"precision": "0.1",
"unit": "℃"
},
{
"key": "Tn_2",
"name": "N相出线温度",
"addr": 50007,
"len": 1,
"dataType": "int32",
"rwType": "R",
"default": "0",
"precision": "0.1",
"unit": "℃"
},
{
"key": "Ua",
"name": "A/L相电压",
"addr": 50008,
"len": 1,
"dataType": "uint32",
"rwType": "R",
"default": "0",
"precision": "0.01",
"unit": "V"
},
{
"key": "Ia",
"name": "A/L相电流",
"addr": 50009,
"len": 1,
"dataType": "uint32",
"rwType": "R",
"default": "0",
"precision": "0.001",
"unit": "A",
"critical": "0.2"
},
{
"key": "Pa",
"name": "A/L相有功功率",
"addr": 50010,
"len": 1,
"dataType": "uint32",
"rwType": "R",
"default": "0",
"precision": "0.001",
"unit": "W"
},
{
"key": "Qa",
"name": "A/L相无功功率",
"addr": 50011,
"len": 1,
"dataType": "uint32",
"rwType": "R",
"default": "0",
"precision": "0.001",
"unit": "var"
},
{
"key": "Sa",
"name": "A/L相视在功率",
"addr": 50012,
"len": 1,
"dataType": "uint32",
"rwType": "R",
"default": "0",
"precision": "0.001",
"unit": "va"
},
{
"key": "Pfa",
"name": "A/L相功率因数",
"addr": 50013,
"len": 1,
"dataType": "uint32",
"rwType": "R",
"default": "0",
"precision": "0.001",
"unit": ""
},
{
"key": "EPa_H",
"name": "A/L相有功电能高32位",
"addr": 50014,
"len": 1,
"dataType": "uint32",
"rwType": "R",
"default": "0",
"precision": "1",
"unit": "Wh"
},
{
"key": "EPa_L",
"name": "A/L相有功电能低32位",
"addr": 50015,
"len": 1,
"dataType": "uint32",
"rwType": "R",
"default": "0",
"precision": "1",
"unit": "Wh"
},
{
"key": "EQa_H",
"name": "A/L相无功电能高32位",
"addr": 50016,
"len": 1,
"dataType": "uint32",
"rwType": "R",
"default": "0",
"precision": "1",
"unit": "varh"
},
{
"key": "EQa_L",
"name": "A/L相无功电能低32位",
"addr": 50017,
"len": 1,
"dataType": "uint32",
"rwType": "R",
"default": "0",
"precision": "1",
"unit": "varh"
},
{
"key": "Ta_1",
"name": "L/A相进线温度",
"addr": 50018,
"len": 1,
"dataType": "int32",
"rwType": "R",
"default": "0",
"precision": "0.1",
"unit": "℃"
},
{
"key": "Ta_2",
"name": "L/A相出线温度",
"addr": 50019,
"len": 1,
"dataType": "int32",
"rwType": "R",
"default": "0",
"precision": "0.1",
"unit": "℃"
},
{
"key": "Ub",
"name": "B相电压",
"addr": 50020,
"len": 1,
"dataType": "uint32",
"rwType": "R",
"default": "0",
"precision": "0.01",
"unit": "V"
},
{
"key": "Ib",
"name": "B相电流",
"addr": 50021,
"len": 1,
"dataType": "uint32",
"rwType": "R",
"default": "0",
"precision": "0.001",
"unit": "A",
"critical": "0.2"
},
{
"key": "Pb",
"name": "B相有功功率 ",
"addr": 50022,
"len": 1,
"dataType": "uint32",
"rwType": "R",
"default": "0",
"precision": "0.001",
"unit": "W"
},
{
"key": "Qb",
"name": "B相无功功率",
"addr": 50023,
"len": 1,
"dataType": "uint32",
"rwType": "R",
"default": "0",
"precision": "0.001",
"unit": "var"
},
{
"key": "Sb",
"name": "B相视在功率",
"addr": 50024,
"len": 1,
"dataType": "uint32",
"rwType": "R",
"default": "0",
"precision": "0.001",
"unit": "va"
},
{
"key": "Pfb",
"name": "B相功率因数",
"addr": 50025,
"len": 1,
"dataType": "uint32",
"rwType": "R",
"default": "0",
"precision": "0.001",
"unit": ""
},
{
"key": "EPb_H",
"name": "B相有功电能高32位",
"addr": 50026,
"len": 1,
"dataType": "uint32",
"rwType": "R",
"default": "0",
"precision": "1",
"unit": "Wh"
},
{
"key": "EPb_L",
"name": "B相有功电能低32位",
"addr": 50027,
"len": 1,
"dataType": "uint32",
"rwType": "R",
"default": "0",
"precision": "1",
"unit": "Wh"
},
{
"key": "EQb_H",
"name": "B相无功电能高32位",
"addr": 50028,
"len": 1,
"dataType": "uint32",
"rwType": "R",
"default": "0",
"precision": "1",
"unit": "varh"
},
{
"key": "EQb_L",
"name": "B相无功电能低32位",
"addr": 50029,
"len": 1,
"dataType": "uint32",
"rwType": "R",
"default": "0",
"precision": "1",
"unit": "varh"
},
{
"key": "Tb_1",
"name": "B相进线温度",
"addr": 50030,
"len": 1,
"dataType": "int32",
"rwType": "R",
"default": "0",
"precision": "0.1",
"unit": "℃"
},
{
"key": "Tb_2",
"name": "B相出线温度",
"addr": 50031,
"len": 1,
"dataType": "int32",
"rwType": "R",
"default": "0",
"precision": "0.1",
"unit": "℃"
},
{
"key": "Uc",
"name": "C相电压",
"addr": 50032,
"len": 1,
"dataType": "uint32",
"rwType": "R",
"default": "0",
"precision": "0.01",
"unit": "V"
},
{
"key": "Ic",
"name": "C相电流",
"addr": 50033,
"len": 1,
"dataType": "uint32",
"rwType": "R",
"default": "0",
"precision": "0.001",
"unit": "A",
"critical": "0.2"
},
{
"key": "Pc",
"name": "C相有功功率 ",
"addr": 50034,
"len": 1,
"dataType": "uint32",
"rwType": "R",
"default": "0",
"precision": "0.001",
"unit": "W"
},
{
"key": "Qc",
"name": "C相无功功率",
"addr": 50035,
"len": 1,
"dataType": "uint32",
"rwType": "R",
"default": "0",
"precision": "0.001",
"unit": "var"
},
{
"key": "Sc",
"name": "C相视在功率",
"addr": 50036,
"len": 1,
"dataType": "uint32",
"rwType": "R",
"default": "0",
"precision": "0.001",
"unit": "va"
},
{
"key": "Pfc",
"name": "C相功率因数",
"addr": 50037,
"len": 1,
"dataType": "uint32",
"rwType": "R",
"default": "0",
"precision": "0.001",
"unit": ""
},
{
"key": "EPc_H",
"name": "C相有功电能高32位",
"addr": 50038,
"len": 1,
"dataType": "uint32",
"rwType": "R",
"default": "0",
"precision": "1",
"unit": "Wh"
},
{
"key": "EPc_L",
"name": "C相有功电能低32位",
"addr": 50039,
"len": 1,
"dataType": "uint32",
"rwType": "R",
"default": "0",
"precision": "1",
"unit": "Wh"
},
{
"key": "EQc_H",
"name": "C相无功电能高32位",
"addr": 50040,
"len": 1,
"dataType": "uint32",
"rwType": "R",
"default": "0",
"precision": "1",
"unit": "varh"
},
{
"key": "EQc_L",
"name": "C相无功电能低32位",
"addr": 50041,
"len": 1,
"dataType": "uint32",
"rwType": "R",
"default": "0",
"precision": "1",
"unit": "varh"
},
{
"key": "Tc_1",
"name": "C相进线温度",
"addr": 50042,
"len": 1,
"dataType": "int32",
"rwType": "R",
"default": "0",
"precision": "0.1",
"unit": "℃"
},
{
"key": "Tc_2",
"name": "C相出线温度",
"addr": 50043,
"len": 1,
"dataType": "int32",
"rwType": "R",
"default": "0",
"precision": "0.1",
"unit": "℃"
},
{
"key": "Usum",
"name": "合相电压",
"addr": 50044,
"len": 1,
"dataType": "uint32",
"rwType": "R",
"default": "0",
"precision": "0.01",
"unit": "V"
},
{
"key": "Isum",
"name": "合相电流/剩余电流",
"addr": 50045,
"len": 1,
"dataType": "uint32",
"rwType": "R",
"default": "0",
"precision": "0.001",
"unit": "A",
"critical": "0.2"
},
{
"key": "Psum",
"name": "合相有功功率",
"addr": 50046,
"len": 1,
"dataType": "uint32",
"rwType": "R",
"default": "0",
"precision": "0.001",
"unit": "W"
},
{
"key": "Qsum",
"name": "合相无功功率",
"addr": 50047,
"len": 1,
"dataType": "uint32",
"rwType": "R",
"default": "0",
"precision": "0.001",
"unit": "var"
},
{
"key": "Ssum",
"name": "RMS 合相视在功率",
"addr": 50048,
"len": 1,
"dataType": "uint32",
"rwType": "R",
"default": "0",
"precision": "0.001",
"unit": "va"
},
{
"key": "Pfsum",
"name": "RMS 合相功率因数",
"addr": 50049,
"len": 1,
"dataType": "uint32",
"rwType": "R",
"default": "0",
"precision": "0.001",
"unit": ""
},
{
"key": "EPsum_H;",
"name": "合相有功电能高32位",
"addr": 50050,
"len": 1,
"dataType": "uint32",
"rwType": "R",
"default": "0",
"precision": "1",
"unit": "Wh"
},
{
"key": "EPsum_L;",
"name": "合相有功电能低32位",
"addr": 50051,
"len": 1,
"dataType": "uint32",
"rwType": "R",
"default": "0",
"precision": "1",
"unit": "Wh"
},
{
"key": "EQsum_H,;",
"name": "合相无功电能高32位",
"addr": 50052,
"len": 1,
"dataType": "uint32",
"rwType": "R",
"default": "0",
"precision": "1",
"unit": "varh"
},
{
"key": "EQsum_L;",
"name": "合相无功电能低32位",
"addr": 50053,
"len": 1,
"dataType": "uint32",
"rwType": "R",
"default": "0",
"precision": "1",
"unit": "varh"
}
]

View File

@ -1,8 +1,8 @@
@font-face {
font-family: "iconfont"; /* Project id 4622943 */
src: url('iconfont.woff2?t=1751007258575') format('woff2'),
url('iconfont.woff?t=1751007258575') format('woff'),
url('iconfont.ttf?t=1751007258575') format('truetype');
src: url('iconfont.woff2?t=1752127229460') format('woff2'),
url('iconfont.woff?t=1752127229460') format('woff'),
url('iconfont.ttf?t=1752127229460') format('truetype');
}
.iconfont {
@ -13,6 +13,14 @@
-moz-osx-font-smoothing: grayscale;
}
.icon-kuaijiejian:before {
content: "\e603";
}
.icon-sousuo:before {
content: "\e60e";
}
.icon-xwtubiaoku-13:before {
content: "\e61a";
}

View File

@ -7,7 +7,8 @@
</el-select>
<el-button icon="CaretRight" type="primary" :disabled="playState" @click="startExecution">开始执行</el-button>
<el-button icon="RemoveFilled" type="danger" @click="stopExecution">停止</el-button>
<el-input v-model="operatorCode" placeholder="请输入操作员编号" style="width: 200px; margin-left: 15px" />
<span style="margin-left: 10px;">操作员编号:</span>
<el-input v-model="operatorCode" placeholder="请输入操作员编号" style="width: 150px; margin-left: 10px" />
</div>
<div class="header-right">
<el-button icon="RefreshRight" type="primary" :disabled="playState" @click="generateDeviceList">刷新列表</el-button>
@ -29,9 +30,12 @@
>
</div>
<div v-if="activeScheme && activeScheme.series" class="scheme-list list-two">
<span v-for="(item, index) in activeScheme.configParam.format" :key="index" class="scheme-item"
>{{ item.unit ? item.name + '(' + item.unit + ')' : item.name }}: <span class="scheme-value">{{ item.value }}</span></span
>
<template v-for="(item, index) in activeScheme.configParam.format">
<span v-if="item.key !== 'reserve'" :key="index" class="scheme-item"
>{{ item.unit ? item.name + '(' + item.unit + ')' : item.name }}: <span class="scheme-value">{{ item.value }}</span></span
>
</template>
</div>
<el-main class="calibration-main">
@ -54,7 +58,7 @@
'iconfont',
'icon-icon_duanluqi',
'circuit-breaker',
device.result === 1 ? 'color-green' : device.result === -1 ? 'color-red' : device.stepStatus === -1 ? 'color-red' : device.stepStatus === 1 ? 'color-green' : 'color-orange'
device.result === 1 ? 'color-green' : device.result === -1 ? 'color-red' : device.result === 2 ? 'color-orange' : ''
]"
></div>
<div v-else :class="['iconfont', 'icon-icon_duanluqi', 'circuit-breaker', device.result === 1 ? 'color-green' : device.result === -1 ? 'color-red' : '']"></div>
@ -69,8 +73,8 @@
<p>设备端口: {{ device.devicePort || '无' }}</p>
<p>
标定结果:
<span :class="['device-result', device.result === 1 ? 'color-green' : device.result === -1 ? 'color-red' : '']">{{
device.result === 1 ? '成功' : device.result === -1 ? '失败' : '未标定' || '无'
<span :class="['device-result', device.result === 1 ? 'color-green' : device.result === -1 ? 'color-red' : device.result === 2 ? 'color-orange' : '']">{{
device.result === 1 ? '成功' : device.result === -1 ? '失败' : device.result === 2 ? '标定中' : '未标定' || '无'
}}</span>
</p>
<p>
@ -89,8 +93,39 @@
<el-footer class="calibration-footer" :class="isOpenLog ? 'open-log' : ''">
<div id="log-box-main" class="log-box-main">
<!-- 浮窗搜索栏 -->
<div v-if="isSearchActive" class="floating-search-bar">
<div class="search-input-wrapper">
<el-input ref="searchInputRef" v-model="searchKeyword" placeholder="搜索日志..." size="small" clearable @keyup.enter="navigateSearch('next')" @keyup.esc="closeSearch" @input="performSearch">
<template #prefix>
<el-icon><Search /></el-icon>
</template>
</el-input>
</div>
<div v-if="searchResults.length > 0" class="search-stats">{{ currentSearchIndex + 1 }}/{{ totalSearchCount }}</div>
<div class="search-navigation">
<el-button v-if="searchResults.length > 0" size="mini" :disabled="currentSearchIndex <= 0" @click="navigateSearch('prev')">
<el-icon><ArrowUp /></el-icon>
</el-button>
<el-button v-if="searchResults.length > 0" size="mini" :disabled="currentSearchIndex >= searchResults.length - 1" @click="navigateSearch('next')">
<el-icon><ArrowDown /></el-icon>
</el-button>
<el-button size="mini" @click="closeSearch">
<el-icon><Close /></el-icon>
</el-button>
</div>
</div>
<div class="log-list">
<div v-for="(item, index) in logs" :key="index" class="log-item">
<div
v-for="(item, index) in logs"
:key="index"
class="log-item"
:class="{
'search-highlight': isSearchActive && searchResults.some(result => result.originalIndex === index),
'search-current': isSearchActive && searchResults[currentSearchIndex] && searchResults[currentSearchIndex].originalIndex === index
}"
>
<el-tooltip v-if="item.type == 'calibrate_error'" effect="light" content="错误信息" placement="top">
<span class="iconfont icon-bug-fill" style="color: #ff3b2b"></span>
</el-tooltip>
@ -103,7 +138,7 @@
<el-tooltip v-else effect="light" content="系统" placement="top">
<span class="iconfont icon-rizhi1"></span>
</el-tooltip>
<span class="log-item-txt">{{ item.time }} {{ item.msg }}</span>
<span class="log-item-txt" v-html="getHighlightedText(item)"></span>
</div>
</div>
</div>
@ -128,6 +163,11 @@
<span class="iconfont icon-icon_suoding"></span>
</el-tooltip>
</view>
<view class="log-box-operate-item" @click="openSearch">
<el-tooltip class="box-item" content="搜索日志" effect="dark" placement="bottom">
<span class="iconfont icon-sousuo"></span>
</el-tooltip>
</view>
<view class="log-box-operate-item" @click="clearLog">
<el-tooltip class="box-item" content="清空日志" effect="dark" placement="bottom">
<span class="iconfont icon-icon_shanchu"></span>
@ -142,7 +182,18 @@
</el-tooltip>
</view>
</div>
<div class="version-info">V{{ version }}</div>
<div class="calibration-footer-btn-right">
<el-tooltip placement="top">
<template #content>
<el-table :data="shortcutKeyList" style="width: 100%" class="dark-table">
<el-table-column prop="action" label="功能" width="160" />
<el-table-column prop="label" label="快捷键" width="100" />
</el-table>
</template>
<span class="iconfont icon-kuaijiejian"></span>
</el-tooltip>
<div class="version-info">V{{ version }}</div>
</div>
</div>
</el-footer>
@ -213,9 +264,11 @@
<el-divider>断路器校准参数</el-divider>
<div class="calibrate-params">
<el-form-item v-for="(item, index) in defaultConfList.format" :key="item.key" :label="item.unit ? item.name + '(' + item.unit + ')' : item.name" :label-width="140">
<el-input v-model="currentScheme.configParam.format[index].value"> </el-input>
</el-form-item>
<template v-for="(item, index) in defaultConfList.format">
<el-form-item v-if="item.key !== 'reserve'" :key="item.key" :label="item.unit ? item.name + '(' + item.unit + ')' : item.name" :label-width="145">
<el-input v-model="currentScheme.configParam.format[index].value"> </el-input>
</el-form-item>
</template>
</div>
<!-- <el-divider>断路器类型参数</el-divider>
@ -244,7 +297,7 @@
<div class="steps-header">
<el-button type="primary" @click="addCalibrationStep">添加步骤</el-button>
</div>
<el-table :data="currentScheme.steps" style="width: 100%">
<el-table :data="currentScheme.steps" size="small" style="width: 100%">
<!-- <el-table-column type="index" width="50" />-->
<el-table-column prop="id" align="center" label="排序" width="80" />
<!-- <el-table-column prop="id" align="center" label="步骤值">-->
@ -380,7 +433,7 @@
? Number(scope.row.paramMap[col.valueKey].actualError.toFixed(6)).toString()
: scope.row.paramMap[col.valueKey]?.actualError
}}<span v-if="scope.row.paramMap[col.valueKey]">%</span>
<template v-if="scope.row.paramMap[col.valueKey]" :style="{ color: scope.row.paramMap[col.valueKey]?.result === 1 ? 'green' : 'red' }">
<template v-if="scope.row.paramMap[col.valueKey]">
<el-icon v-if="scope.row.paramMap[col.valueKey].actualValue > scope.row.paramMap[col.valueKey].outputValue"><Top /></el-icon>
<el-icon v-else-if="scope.row.paramMap[col.valueKey].actualValue < scope.row.paramMap[col.valueKey].outputValue"><Bottom /></el-icon>
</template>
@ -407,6 +460,7 @@
<script setup>
import { ref, onMounted, onUnmounted, computed } from 'vue';
import { ElMessage, ElMessageBox } from 'element-plus';
import { Search, ArrowUp, ArrowDown, Top, Bottom, Close } from '@element-plus/icons-vue';
import { throttle } from 'lodash-es';
import dayjs from 'dayjs';
import axios from 'axios';
@ -468,7 +522,13 @@ const defaultiLeakDetectObj = ref({
const logVisible = ref(false);
const logList = ref([]);
//
const searchKeyword = ref('');
const searchResults = ref([]);
const currentSearchIndex = ref(0);
const totalSearchCount = ref(0);
const isSearchActive = ref(false);
const logLoading = ref(false);
const logBoxRef = ref(null);
@ -490,6 +550,25 @@ const activeDeviceInfo = ref({
const defaultPropList = ref([]);
const shortcutKeyList = ref([
{
label: 'Ctrl+F',
action: '显/隐日志查询框'
},
{
label: '↑',
action: '上一个日志(查询时)'
},
{
label: '↓',
action: '下一个日志(查询时)'
},
{
label: 'Esc',
action: '退出查询'
}
]);
const currentScheme = ref({
schemeName: '',
schemeType: '1p',
@ -655,7 +734,6 @@ const setDeviceInfo = msg => {
};
const getSocketMeassage = message => {
// message = {data:{"msgType":"calibrate_result","data":"{\"id\":1,\"switch\":0,\"result\":-1,\"switch\":0,\"result\":-1,\"switch\":0,\"result\":-1,\"switch\":0,\"result\":-1,\"switch\":0,\"result\":-1,\"switch\":0,\"result\":-1,\"switch\":0,\"result\":-1,\"switch\":0,\"result\":-1,\"switch\":0,\"result\":-1,\"switch\":0,\"result\":-1,\"switch\":0,\"result\":-1,\"switch\":0,\"result\":-1,\"switch\":0,\"result\":-1,\"switch\":0,\"result\":-1,\"switch\":0,\"result\":-1,\"switch\":0,\"result\":-1,\"switch\":0,\"result\":-1,\"switch\":0,\"result\":-1,\"switch\":0,\"result\":-1,\"switch\":0,\"result\":-1,\"switch\":0,\"result\":-1,\"switch\":0,\"result\":-1,\"switch\":0,\"result\":-1,\"switch\":0,\"result\":-1,\"resultTxt\":\"\"}"}};
const msg = tryParseJSON(message.data);
console.log('msg', msg);
const newLog = {
@ -671,6 +749,11 @@ const getSocketMeassage = message => {
}
}
if (msg.msgType === 'calibrate_finish') {
playState.value = false;
ElMessage.success('标定完成');
}
//
logs.value = [...logs.value.slice(-MAX_LOG_LENGTH + 1), newLog];
@ -718,6 +801,7 @@ const socketStatus = computed(() => {
return webSocketStore.socket_open;
});
onMounted(() => {
logBoxRef.value = document.querySelector('#log-box-main');
initSocket();
@ -732,12 +816,47 @@ onMounted(() => {
getSchemeDefaultProp();
generateDeviceList();
loadSchemes();
//
document.addEventListener('keydown', handleKeydown);
});
//
const handleKeydown = event => {
// Ctrl+F:
if (event.ctrlKey && event.key === 'f') {
event.preventDefault();
openSearch();
}
//
if (isSearchActive.value) {
switch (event.key) {
case 'ArrowUp':
// :
event.preventDefault();
navigateSearch('prev');
break;
case 'ArrowDown':
// :
event.preventDefault();
navigateSearch('next');
break;
case 'Escape':
// Esc:
event.preventDefault();
closeSearch();
break;
}
}
};
onUnmounted(() => {
if (webSocketStore) {
webSocketStore.close();
}
//
document.removeEventListener('keydown', handleKeydown);
});
const toggleIsScroll = () => {
@ -824,16 +943,16 @@ const loadSchemes = async () => {
// activeSchemeselectedScheme
let found = null;
if (selectedScheme.value) {
found = schemeList.value.find(item => item.id === selectedScheme.value);
found = schemeList.value.find(item => item.id === selectedScheme.value);
}
if (found) {
activeScheme.value = found;
activeScheme.value = found;
} else if (schemeList.value.length > 0) {
selectedScheme.value = schemeList.value[0].id;
activeScheme.value = schemeList.value[0];
selectedScheme.value = schemeList.value[0].id;
activeScheme.value = schemeList.value[0];
} else {
selectedScheme.value = '';
activeScheme.value = {};
selectedScheme.value = '';
activeScheme.value = {};
}
ElMessage.success('获取方案列表成功');
} else {
@ -849,6 +968,11 @@ const loadSchemes = async () => {
//
const saveScheme = async () => {
// true
if (!currentScheme.value.middleDetect && !currentScheme.value.laterDetect) {
ElMessage.error('“标定中检测”和“标定后检测”必须至少选择一个');
return;
}
try {
//
const scheme = JSON.parse(JSON.stringify(currentScheme.value));
@ -979,6 +1103,108 @@ const clearLog = () => {
logs.value = [];
};
//
const searchInputRef = ref(null);
const openSearch = () => {
if (isSearchActive.value) {
closeSearch();
} else {
isSearchActive.value = true;
searchKeyword.value = '';
searchResults.value = [];
currentSearchIndex.value = 0;
totalSearchCount.value = 0;
//
setTimeout(() => {
if (searchInputRef.value) {
searchInputRef.value.focus();
}
}, 100);
}
};
const closeSearch = () => {
isSearchActive.value = false;
searchKeyword.value = '';
searchResults.value = [];
currentSearchIndex.value = 0;
totalSearchCount.value = 0;
};
const performSearch = () => {
if (!searchKeyword.value.trim()) {
searchResults.value = [];
currentSearchIndex.value = 0;
totalSearchCount.value = 0;
return;
}
const keyword = searchKeyword.value.toLowerCase();
const results = [];
logs.value.forEach((log, index) => {
const logText = `${log.time} ${log.msg}`.toLowerCase();
if (logText.includes(keyword)) {
results.push({
originalIndex: index,
time: log.time,
msg: log.msg,
type: log.type
});
}
});
searchResults.value = results;
currentSearchIndex.value = results.length > 0 ? 0 : -1;
totalSearchCount.value = results.length;
//
if (results.length > 0) {
scrollToSearchResult(results[0].originalIndex);
}
};
const getHighlightedText = item => {
if (!searchKeyword.value.trim()) {
return `${item.time} ${item.msg}`;
}
const text = `${item.time} ${item.msg}`;
const keyword = searchKeyword.value;
const regex = new RegExp(`(${keyword.replace(/[.*+?^${}()|[\]\\]/g, '\\$&')})`, 'gi');
return text.replace(regex, '<span class="search-highlight-text">$1</span>');
};
const navigateSearch = direction => {
if (searchResults.value.length === 0) return;
if (direction === 'next') {
currentSearchIndex.value = (currentSearchIndex.value + 1) % searchResults.value.length;
} else if (direction === 'prev') {
currentSearchIndex.value = currentSearchIndex.value === 0 ? searchResults.value.length - 1 : currentSearchIndex.value - 1;
}
//
const currentResult = searchResults.value[currentSearchIndex.value];
if (currentResult) {
scrollToSearchResult(currentResult.originalIndex);
}
};
const scrollToSearchResult = index => {
setTimeout(() => {
const logElements = document.querySelectorAll('.log-item');
if (logElements[index]) {
logElements[index].scrollIntoView({
behavior: 'smooth',
block: 'center'
});
}
}, 100);
};
//
const deleteCalibrationStep = index => {
currentScheme.value.steps.splice(index, 1);
@ -1026,6 +1252,9 @@ const playCalibration = async () => {
height: 100vh;
background-color: #f0f2f5;
}
:deep(.asterisk-left.el-form-item){
margin-bottom: 10px;
}
.calibration-header {
display: flex;
@ -1167,7 +1396,7 @@ const playCalibration = async () => {
display: flex;
flex-direction: column;
flex-shrink: 0;
position: relative;
&.open-log {
height: 350px;
}
@ -1200,7 +1429,7 @@ const playCalibration = async () => {
background-color: #202020;
color: #dcdcdc;
scroll-behavior: smooth;
.log-list{
.log-list {
// padding: 5px;
.log-item {
padding: 0 5px;
@ -1223,11 +1452,11 @@ const playCalibration = async () => {
}
.log-item-txt {
user-select: auto;
margin-left: 5px;
}
}
}
}
.calibration-footer-btn {
@ -1258,10 +1487,15 @@ const playCalibration = async () => {
}
}
}
.calibration-footer-btn-right {
display: flex;
align-items: center;
/* 表格暗黑样式 */
}
}
.version-info {
text-align: right;
margin-left: 10px;
color: #888;
}
@ -1331,7 +1565,10 @@ const playCalibration = async () => {
.calibrate-params {
display: grid;
grid-template-columns: repeat(5, 1fr);
gap: 5px;
gap: 10px;
:deep(.el-form-item){
margin-bottom: 0;
}
}
.accuracy-title {
@ -1346,13 +1583,16 @@ const playCalibration = async () => {
display: grid;
gap: 10px;
grid-template-columns: repeat(6, 1fr);
:deep(.el-form-item){
margin-bottom: 0;
}
}
.calibration-steps {
margin-top: 20px;
.steps-header {
margin-bottom: 20px;
margin-bottom: 5px;
}
}
@ -1366,6 +1606,11 @@ const playCalibration = async () => {
:deep(.el-dialog__body) {
padding: 20px;
max-height: calc(100vh - 152px);
overflow-y: auto;
}
:deep(.el-divider--horizontal) {
margin: 20px 0;
}
.device-params {
@ -1390,4 +1635,110 @@ const playCalibration = async () => {
}
}
}
//
.floating-search-bar {
position: absolute;
top: 20px;
right: 30px;
z-index: 1000;
display: flex;
align-items: center;
// padding: 8px;
// background-color: #2a2a2a;
// border: 1px solid #444;
border-radius: 6px;
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.3);
gap: 8px;
// min-width: 280px;
.search-input-wrapper {
flex: 1;
width: 150px;
}
.search-stats {
color: #ccc;
font-size: 12px;
// width: 40px;
text-align: center;
white-space: nowrap;
}
.search-navigation {
display: flex;
gap: 8px;
.el-button {
margin: 0;
width: 24px;
height: 24px;
}
}
}
//
.log-item {
&.search-highlight {
background-color: rgba(255, 235, 59, 0.3) !important;
}
&.search-current {
background-color: rgba(255, 235, 59, 0.6) !important;
border-left: 3px solid #ffeb3b;
}
:deep(.search-highlight-text) {
background-color: #ffeb3b !important;
color: #000;
padding: 1px 2px;
border-radius: 2px;
font-weight: bold;
}
}
.dark-table {
background: #303030 !important;
color: #dcdcdc !important;
border: none !important;
font-size: 13px;
:deep(.el-table__header) {
background: #303030 !important;
color: #e0e0e0 !important;
font-weight: bold;
border-bottom: 1px solid #555555 !important;
}
:deep(tr) {
color: #e0e0e0 !important;
}
:deep(.el-table__body) {
background: #303030 !important;
}
:deep(.el-table__row) {
background: #303030 !important;
transition: background 0.2s;
}
:deep(.el-table__row:hover) {
background: #2a2a2a !important;
}
:deep(.el-table__cell) {
background: #303030 !important;
border-color: #555555 !important;
color: #dcdcdc !important;
padding: 6px 10px;
}
:deep(.el-table__empty-block) {
background: #303030 !important;
color: #888 !important;
}
:deep(.el-table__border) {
border-color: #555555 !important;
}
:deep(.el-table__inner-wrapper) {
box-shadow: none !important;
}
:deep(.el-table__inner-wrapper:before) {
background: #303030 !important;
}
}
</style>

5533
yarn.lock

File diff suppressed because it is too large Load Diff