feat(renderer): 优化日志功能和界面布局

- 更新 iconfont 文件,添加新的图标
- 重构日志显示逻辑,支持不同类型的消息
- 添加方案详情展示区域
- 优化方案编辑界面布局
- 实现日志滚动和展开/收缩功能
- 添加版本号动态显示
- 优化 WebSocket 连接管理
This commit is contained in:
fhysy 2025-06-20 15:24:40 +08:00
parent 2764645590
commit 82a578fc5c
7 changed files with 258 additions and 39 deletions

View File

@ -2,8 +2,12 @@ import { resolve } from 'path'
import { defineConfig, externalizeDepsPlugin } from 'electron-vite'
import vue from '@vitejs/plugin-vue'
import VueDevTools from 'vite-plugin-vue-devtools';
import pkg from './package.json'
export default defineConfig({
define: {
'import.meta.env.VITE_APP_VERSION': JSON.stringify(pkg.version)
},
main: {
plugins: [externalizeDepsPlugin()]
},

View File

@ -1,6 +1,6 @@
{
"name": "calibration-pc",
"version": "1.0.0",
"version": "1.0.9",
"description": "谷云开发部开发的断路器标定软件",
"main": "./out/main/index.js",
"author": "example.com",
@ -21,12 +21,10 @@
"@electron-toolkit/preload": "^3.0.1",
"@electron-toolkit/utils": "^3.0.0",
"axios": "^1.7.2",
"calibration-pc": "file:",
"dayjs": "^1.11.13",
"electron-updater": "^6.1.7",
"element-plus": "^2.7.6",
"file-saver": "^2.0.5",
"gateway-app": "file:",
"pinia": "^2.1.7",
"vue-router": "^4.4.0",
"xlsx": "^0.18.5"

View File

@ -1,8 +1,8 @@
@font-face {
font-family: "iconfont"; /* Project id 4622943 */
src: url('iconfont.woff2?t=1743060096253') format('woff2'),
url('iconfont.woff?t=1743060096253') format('woff'),
url('iconfont.ttf?t=1743060096253') format('truetype');
src: url('iconfont.woff2?t=1750322442953') format('woff2'),
url('iconfont.woff?t=1750322442953') format('woff'),
url('iconfont.ttf?t=1750322442953') format('truetype');
}
.iconfont {
@ -13,6 +13,46 @@
-moz-osx-font-smoothing: grayscale;
}
.icon-rizhi:before {
content: "\e61c";
}
.icon-bug:before {
content: "\e725";
}
.icon-caozuorizhi:before {
content: "\e610";
}
.icon-rizhishenji:before {
content: "\e666";
}
.icon-chenggong:before {
content: "\e615";
}
.icon-rizhi1:before {
content: "\e613";
}
.icon-chenggong1:before {
content: "\e616";
}
.icon-bug-fill:before {
content: "\e773";
}
.icon-bug-line:before {
content: "\e774";
}
.icon-kongtiaoguizhuduanluqiAA:before {
content: "\e68e";
}
.icon--_saomaqiang:before {
content: "\e640";
}

View File

@ -2,7 +2,7 @@
<div class="calibration-page">
<el-header class="calibration-header">
<div class="header-left">
<el-select v-model="selectedScheme" placeholder="选择方案" style="width: 200px; margin-right: 15px">
<el-select v-model="selectedScheme" placeholder="选择方案" style="width: 200px; margin-right: 15px" @change="schemeChange">
<el-option v-for="(item, index) in schemeList" :key="index" :label="item.schemeName" :value="item.id"></el-option>
</el-select>
<el-button icon="CaretRight" type="primary" :disabled="playState" @click="startExecution">开始执行</el-button>
@ -13,6 +13,20 @@
<el-button icon="Setting" :disabled="playState" @click="openSettings">设置</el-button>
</div>
</el-header>
<div class="scheme-list" v-if="activeScheme && activeScheme.series">
<span class="scheme-item"
>系列: <span class="scheme-value">{{ activeScheme.series }}</span></span
>
<span class="scheme-item"
>框架: <span class="scheme-value">{{ activeScheme.framework }}</span></span
>
<span class="scheme-item"
>类型: <span class="scheme-value">{{ activeScheme.schemeType }}</span></span
>
<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
>
</div>
<el-main class="calibration-main">
<div class="device-grid">
@ -46,16 +60,42 @@
</div>
</el-main>
<el-footer class="calibration-footer">
<el-footer class="calibration-footer" :class="isOpenLog ? 'open-log' : ''">
<div id="log-box-main" class="log-box-main">
<div class="log-list">
<div v-for="(item, index) in logs" :key="index" class="log-item">
{{ item }}
<el-tooltip v-if="item.type == 'subscribe'" effect="light" content="订阅" placement="top">
<span class="iconfont icon-icon_shanghang" style="color: #00a73c"></span>
</el-tooltip>
<el-tooltip v-else-if="item.type == 'calibrate_error'" effect="light" content="错误信息" placement="top">
<span class="iconfont icon-bug-fill" style="color: #ff3b2b"></span>
</el-tooltip>
<!-- <el-tooltip-->
<!-- effect="light"-->
<!-- content="消息"-->
<!-- placement="top"-->
<!-- v-else-if="item.type == 'calibrate_info'"-->
<!-- >-->
<!-- <span class="iconfont icon-rizhi1" style="color: #0066cc"></span>-->
<!-- </el-tooltip>-->
<el-tooltip v-else effect="light" content="系统" placement="top">
<span class="iconfont icon-rizhi1"></span>
</el-tooltip>
{{ item.time }} {{ item.msg }}
</div>
</div>
</div>
<div class="calibration-footer-btn">
<div class="btn-box">
<view class="log-box-operate-item">
<div>日志状态</div>
<div v-if="socketStatus" style="color: #00a73c; display: flex; align-items: center">连接</div>
<div v-else style="color: #ff3b2b; display: flex; align-items: center" @click="initSocket">
<el-tooltip class="box-item" content="重新连接" effect="dark" placement="bottom">
<div style="display: flex; align-items: center">断连<span class="iconfont icon-icon_gengxin"></span></div>
</el-tooltip>
</div>
</view>
<view class="log-box-operate-item" @click="toggleIsScroll">
<el-tooltip v-if="isScroll" class="box-item" content="关闭滚动" effect="dark" placement="bottom">
<span class="iconfont icon-unlock"></span>
@ -64,6 +104,14 @@
<span class="iconfont icon-icon_suoding"></span>
</el-tooltip>
</view>
<view class="log-box-operate-item" @click="toggleOpenLog">
<el-tooltip v-if="isOpenLog" class="box-item" content="收缩日志" effect="dark" placement="bottom">
<span class="iconfont icon-up-arrow"></span>
</el-tooltip>
<el-tooltip v-else class="box-item" content="展开日志" effect="dark" placement="bottom">
<span class="iconfont icon-shousuoshangjiantou"></span>
</el-tooltip>
</view>
</div>
<div class="version-info">{{ version }}</div>
</div>
@ -96,14 +144,33 @@
<el-form-item label="方案名称">
<el-input v-model="currentScheme.schemeName" />
</el-form-item>
<el-form-item label="断路器类型">
<el-select v-model="currentScheme.schemeType">
<el-option label="1p" value="1p" />
<el-option label="2p" value="2p" />
<el-option label="3p" value="3p" />
<el-option label="4p" value="4p" />
</el-select>
</el-form-item>
<el-row>
<el-col :span="8">
<el-form-item label="系列">
<el-select v-model="currentScheme.series" placeholder="请选择系列">
<el-option label="B7" value="B7" />
<el-option label="B7L" value="B7L" />
</el-select>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="框架">
<el-select v-model="currentScheme.framework" placeholder="请选择框架">
<el-option label="100ZS" value="100ZS" />
</el-select>
</el-form-item>
</el-col>
<el-col :span="8">
<el-form-item label="断路器类型">
<el-select v-model="currentScheme.schemeType" placeholder="请选择断路器类型">
<el-option label="1p" value="1p" />
<el-option label="2p" value="2p" />
<el-option label="3p" value="3p" />
<el-option label="4p" value="4p" />
</el-select>
</el-form-item>
</el-col>
</el-row>
<el-divider>断路器校准参数</el-divider>
<div class="calibrate-params">
@ -121,7 +188,7 @@
<el-divider>校准精度(%)</el-divider>
<div class="accuracy-params">
<el-form-item label-width="80px" v-for="(value, key) in currentScheme.errorRange" :key="key" :label="accuracyType[key]">
<el-form-item v-for="(value, key) in currentScheme.errorRange" :key="key" label-width="80px" :label="accuracyType[key]">
<el-input-number v-model="currentScheme.errorRange[key]" :min="0" :max="100" :precision="2" />
</el-form-item>
</div>
@ -132,21 +199,26 @@
<el-button type="primary" @click="addCalibrationStep">添加步骤</el-button>
</div>
<el-table :data="currentScheme.steps" style="width: 100%">
<el-table-column type="index" width="50" />
<!-- <el-table-column prop="step" label="步骤" width="80" /> -->
<el-table-column prop="step" align="center" label="步骤">
<!-- <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="步骤值">-->
<!-- <template #default="scope">-->
<!-- <el-input-number v-model="scope.row.id" :min="0" />-->
<!-- </template>-->
<!-- </el-table-column>-->
<el-table-column prop="step" align="center" label="步骤值">
<template #default="scope">
<el-input-number v-model="scope.row.step" :min="0" />
<el-input-number v-model="scope.row.step" :min="1" />
</template>
</el-table-column>
<el-table-column prop="voltage" align="center" label="电压(V)">
<el-table-column prop="voltage" align="center" label="电压Un(%)">
<template #default="scope">
<el-input-number v-model="scope.row.voltage" :min="0" :precision="1" />
</template>
</el-table-column>
<el-table-column prop="current" align="center" label="电流(A)">
<el-table-column prop="current" align="center" label="电流Ib(%)">
<template #default="scope">
<el-input-number v-model="scope.row.current" :min="0" :precision="3" />
<el-input-number v-model="scope.row.current" :min="0" :precision="1" />
</template>
</el-table-column>
<el-table-column prop="powerFactor" align="center" label="功率因数">
@ -160,11 +232,11 @@
</el-select>
</template>
</el-table-column>
<!-- <el-table-column prop="riseFirst" align="center" label="先升后标" width="120">
<el-table-column prop="isILeak" align="center" label="检测漏电" width="120">
<template #default="scope">
<el-switch v-model="scope.row.riseFirst" />
<el-switch v-model="scope.row.isILeak" />
</template>
</el-table-column> -->
</el-table-column>
<el-table-column label="操作" width="80">
<template #default="scope">
<el-button type="danger" link @click="deleteCalibrationStep(scope.$index)">删除</el-button>
@ -186,24 +258,33 @@
</template>
<script setup>
import { ref, onMounted } from 'vue';
import { ref, onMounted, computed, onUnmounted } from 'vue';
import { ElMessage, ElMessageBox } from 'element-plus';
import dayjs from 'dayjs';
import axios from 'axios';
import config from '@renderer/util/config.js';
import { logWebSocketStore } from '@renderer/stores/logWebSocket.js';
const webSocketStore = logWebSocketStore();
const selectedScheme = ref('');
const playState = ref(false);
const devices = ref([]);
const isScroll = ref(true);
const logs = ref(['14:44:10.482 添加条码 GE 2', '14:44:10.487 连接失败 127.0.0.1:8821 0000 未将对象引用设置到对象的实例。']);
const logs = ref([]);
const version = ref('V1.0.0');
// 使
// const versionTxt = import.meta.env.VITE_APP_VERSION || 'dev';
// version.value = versionTxt;
const loading = ref(true);
const isOpenLog = ref(false);
//
const schemeDrawerVisible = ref(false);
const schemeDetailVisible = ref(false);
const schemeList = ref([]);
const activeScheme = ref();
const accuracyType = ref({
voltage: '电压',
@ -234,6 +315,8 @@ const defaultPropList = ref([]);
const currentScheme = ref({
schemeName: '',
schemeType: '1p',
series: 'B7',
framework: '100ZS',
configParam: defaultConfList,
propertyParam: defaultPropList,
errorRange: {
@ -265,7 +348,7 @@ const generateMockDevices = () => {
const playCalibration = async () => {
try {
const response = await axios.get(config.url + '/master/scheme/start?schemeId='+ selectedScheme.value);
const response = await axios.get(config.url + '/master/scheme/start?schemeId=' + selectedScheme.value);
if (response.data.code === 0) {
devices.value = response.data.data.result || [];
} else {
@ -291,6 +374,15 @@ const generateDeviceList = async () => {
}
};
const schemeChange = e => {
if (e) {
const scheme = schemeList.value.filter(item => item.id === e);
activeScheme.value = scheme[0];
} else {
activeScheme.value = {};
}
};
const generateRandomLog = () => {
// HH:MM:SS.ms
const now = new Date();
@ -330,13 +422,58 @@ const generateLogs = () => {
}
};
const getSocketMeassage = message => {
let msg = JSON.parse(message.data);
if (msg.msgType !== undefined) {
if (logs.value.length > 400) {
logs.value.shift();
}
logs.value.push({
time: dayjs().format('YYYY-MM-DD HH:mm:ss'),
msg: msg.data,
type: msg.msgType
});
} else {
if (logs.value.length > 400) {
logs.value.shift();
}
logs.value.push({
time: dayjs().format('YYYY-MM-DD HH:mm:ss'),
msg,
type: null
});
}
if (isScroll.value) {
setTimeout(() => {
let div = document.querySelector('#log-box-main');
div.scrollTop = div.scrollHeight;
}, 200);
}
};
const initSocket = () => {
webSocketStore.init(getSocketMeassage);
};
// socket
const socketStatus = computed(() => {
return webSocketStore.socket_open;
});
onMounted(() => {
initSocket();
getSchemeDefaultConf();
getSchemeDefaultProp();
generateDeviceList();
loadSchemes();
});
onUnmounted(() => {
if (webSocketStore) {
webSocketStore.close();
}
});
const toggleIsScroll = () => {
isScroll.value = !isScroll.value;
};
@ -404,6 +541,7 @@ const loadSchemes = async () => {
schemeList.value = response.data.data.schemes || [];
if (!selectedScheme.value && schemeList.value.length > 0) {
selectedScheme.value = schemeList.value[0].id;
activeScheme.value = schemeList.value[0];
}
ElMessage.success('获取方案列表成功');
} else {
@ -496,13 +634,15 @@ const addScheme = () => {
currentScheme.value = {
schemeName: `方案${schemeList.value.length + 1}`,
schemeType: '1p',
series: 'B7',
framework: '100ZS',
configParam: defaultConfList,
propertyParam: defaultPropList,
errorRange: {
voltage: 0.5,
current: 0.5,
smallCurrent: 1.0,
powerFactor: "0.5L",
powerFactor: '0.5L',
activePower: 0.5,
reactivePower: 0.5,
apparentPower: 0.5
@ -526,21 +666,25 @@ const viewScheme = scheme => {
//
const addCalibrationStep = () => {
currentScheme.value.steps.push({
id: currentScheme.value.steps.length + 1,
step: currentScheme.value.steps.length + 1,
voltage: 220,
current: 10,
powerFactor: "0.5L",
// riseFirst: true
voltage: 100,
current: 100,
powerFactor: '0.5L',
isILeak: false
});
};
const toggleOpenLog = () => {
isOpenLog.value = !isOpenLog.value;
};
//
const deleteCalibrationStep = index => {
currentScheme.value.steps.splice(index, 1);
//
// currentScheme.value.steps.forEach((step, idx) => {
// step.step = idx + 1;
// });
currentScheme.value.steps.forEach((step, idx) => {
step.id = idx + 1;
});
};
</script>
@ -562,6 +706,23 @@ const deleteCalibrationStep = index => {
color: white;
flex-shrink: 0;
}
.scheme-list {
background: #ccc;
padding: 10px;
font-size: 13px;
display: flex;
align-items: center;
justify-content: space-between;
.scheme-item {
margin-left: 10px;
&:first-child {
margin-left: 0;
}
.scheme-value {
font-weight: bold;
}
}
}
.header-left,
.header-center,
@ -627,6 +788,9 @@ const deleteCalibrationStep = index => {
display: flex;
flex-direction: column;
flex-shrink: 0;
&.open-log {
height: 350px;
}
}
.log-output {
@ -680,6 +844,19 @@ const deleteCalibrationStep = index => {
display: flex;
align-items: center;
justify-content: space-between;
.btn-box {
display: flex;
align-items: center;
.log-box-operate-item {
display: flex;
align-items: center;
margin-right: 10px;
cursor: pointer;
.icon-icon_gengxin {
margin-left: 2px;
}
}
}
}
.version-info {
text-align: right;