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 { defineConfig, externalizeDepsPlugin } from 'electron-vite'
import vue from '@vitejs/plugin-vue' import vue from '@vitejs/plugin-vue'
import VueDevTools from 'vite-plugin-vue-devtools'; import VueDevTools from 'vite-plugin-vue-devtools';
import pkg from './package.json'
export default defineConfig({ export default defineConfig({
define: {
'import.meta.env.VITE_APP_VERSION': JSON.stringify(pkg.version)
},
main: { main: {
plugins: [externalizeDepsPlugin()] plugins: [externalizeDepsPlugin()]
}, },

View File

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

View File

@ -1,8 +1,8 @@
@font-face { @font-face {
font-family: "iconfont"; /* Project id 4622943 */ font-family: "iconfont"; /* Project id 4622943 */
src: url('iconfont.woff2?t=1743060096253') format('woff2'), src: url('iconfont.woff2?t=1750322442953') format('woff2'),
url('iconfont.woff?t=1743060096253') format('woff'), url('iconfont.woff?t=1750322442953') format('woff'),
url('iconfont.ttf?t=1743060096253') format('truetype'); url('iconfont.ttf?t=1750322442953') format('truetype');
} }
.iconfont { .iconfont {
@ -13,6 +13,46 @@
-moz-osx-font-smoothing: grayscale; -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 { .icon--_saomaqiang:before {
content: "\e640"; content: "\e640";
} }

View File

@ -2,7 +2,7 @@
<div class="calibration-page"> <div class="calibration-page">
<el-header class="calibration-header"> <el-header class="calibration-header">
<div class="header-left"> <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-option v-for="(item, index) in schemeList" :key="index" :label="item.schemeName" :value="item.id"></el-option>
</el-select> </el-select>
<el-button icon="CaretRight" type="primary" :disabled="playState" @click="startExecution">开始执行</el-button> <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> <el-button icon="Setting" :disabled="playState" @click="openSettings">设置</el-button>
</div> </div>
</el-header> </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"> <el-main class="calibration-main">
<div class="device-grid"> <div class="device-grid">
@ -46,16 +60,42 @@
</div> </div>
</el-main> </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 id="log-box-main" class="log-box-main">
<div class="log-list"> <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">
{{ 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>
</div> </div>
<div class="calibration-footer-btn"> <div class="calibration-footer-btn">
<div class="btn-box"> <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"> <view class="log-box-operate-item" @click="toggleIsScroll">
<el-tooltip v-if="isScroll" class="box-item" content="关闭滚动" effect="dark" placement="bottom"> <el-tooltip v-if="isScroll" class="box-item" content="关闭滚动" effect="dark" placement="bottom">
<span class="iconfont icon-unlock"></span> <span class="iconfont icon-unlock"></span>
@ -64,6 +104,14 @@
<span class="iconfont icon-icon_suoding"></span> <span class="iconfont icon-icon_suoding"></span>
</el-tooltip> </el-tooltip>
</view> </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>
<div class="version-info">{{ version }}</div> <div class="version-info">{{ version }}</div>
</div> </div>
@ -96,14 +144,33 @@
<el-form-item label="方案名称"> <el-form-item label="方案名称">
<el-input v-model="currentScheme.schemeName" /> <el-input v-model="currentScheme.schemeName" />
</el-form-item> </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-form-item label="断路器类型">
<el-select v-model="currentScheme.schemeType"> <el-select v-model="currentScheme.schemeType" placeholder="请选择断路器类型">
<el-option label="1p" value="1p" /> <el-option label="1p" value="1p" />
<el-option label="2p" value="2p" /> <el-option label="2p" value="2p" />
<el-option label="3p" value="3p" /> <el-option label="3p" value="3p" />
<el-option label="4p" value="4p" /> <el-option label="4p" value="4p" />
</el-select> </el-select>
</el-form-item> </el-form-item>
</el-col>
</el-row>
<el-divider>断路器校准参数</el-divider> <el-divider>断路器校准参数</el-divider>
<div class="calibrate-params"> <div class="calibrate-params">
@ -121,7 +188,7 @@
<el-divider>校准精度(%)</el-divider> <el-divider>校准精度(%)</el-divider>
<div class="accuracy-params"> <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-input-number v-model="currentScheme.errorRange[key]" :min="0" :max="100" :precision="2" />
</el-form-item> </el-form-item>
</div> </div>
@ -132,21 +199,26 @@
<el-button type="primary" @click="addCalibrationStep">添加步骤</el-button> <el-button type="primary" @click="addCalibrationStep">添加步骤</el-button>
</div> </div>
<el-table :data="currentScheme.steps" style="width: 100%"> <el-table :data="currentScheme.steps" style="width: 100%">
<el-table-column type="index" width="50" /> <!-- <el-table-column type="index" width="50" />-->
<!-- <el-table-column prop="step" label="步骤" width="80" /> --> <el-table-column prop="id" align="center" label="排序" width="80" />
<el-table-column prop="step" align="center" label="步骤"> <!-- <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"> <template #default="scope">
<el-input-number v-model="scope.row.step" :min="0" /> <el-input-number v-model="scope.row.step" :min="1" />
</template> </template>
</el-table-column> </el-table-column>
<el-table-column prop="voltage" align="center" label="电压(V)"> <el-table-column prop="voltage" align="center" label="电压Un(%)">
<template #default="scope"> <template #default="scope">
<el-input-number v-model="scope.row.voltage" :min="0" :precision="1" /> <el-input-number v-model="scope.row.voltage" :min="0" :precision="1" />
</template> </template>
</el-table-column> </el-table-column>
<el-table-column prop="current" align="center" label="电流(A)"> <el-table-column prop="current" align="center" label="电流Ib(%)">
<template #default="scope"> <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> </template>
</el-table-column> </el-table-column>
<el-table-column prop="powerFactor" align="center" label="功率因数"> <el-table-column prop="powerFactor" align="center" label="功率因数">
@ -160,11 +232,11 @@
</el-select> </el-select>
</template> </template>
</el-table-column> </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"> <template #default="scope">
<el-switch v-model="scope.row.riseFirst" /> <el-switch v-model="scope.row.isILeak" />
</template> </template>
</el-table-column> --> </el-table-column>
<el-table-column label="操作" width="80"> <el-table-column label="操作" width="80">
<template #default="scope"> <template #default="scope">
<el-button type="danger" link @click="deleteCalibrationStep(scope.$index)">删除</el-button> <el-button type="danger" link @click="deleteCalibrationStep(scope.$index)">删除</el-button>
@ -186,24 +258,33 @@
</template> </template>
<script setup> <script setup>
import { ref, onMounted } from 'vue'; import { ref, onMounted, computed, onUnmounted } from 'vue';
import { ElMessage, ElMessageBox } from 'element-plus'; import { ElMessage, ElMessageBox } from 'element-plus';
import dayjs from 'dayjs';
import axios from 'axios'; import axios from 'axios';
import config from '@renderer/util/config.js'; import config from '@renderer/util/config.js';
import { logWebSocketStore } from '@renderer/stores/logWebSocket.js';
const webSocketStore = logWebSocketStore();
const selectedScheme = ref(''); const selectedScheme = ref('');
const playState = ref(false); const playState = ref(false);
const devices = ref([]); const devices = ref([]);
const isScroll = ref(true); 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 version = ref('V1.0.0');
// 使
// const versionTxt = import.meta.env.VITE_APP_VERSION || 'dev';
// version.value = versionTxt;
const loading = ref(true); const loading = ref(true);
const isOpenLog = ref(false);
// //
const schemeDrawerVisible = ref(false); const schemeDrawerVisible = ref(false);
const schemeDetailVisible = ref(false); const schemeDetailVisible = ref(false);
const schemeList = ref([]); const schemeList = ref([]);
const activeScheme = ref();
const accuracyType = ref({ const accuracyType = ref({
voltage: '电压', voltage: '电压',
@ -234,6 +315,8 @@ const defaultPropList = ref([]);
const currentScheme = ref({ const currentScheme = ref({
schemeName: '', schemeName: '',
schemeType: '1p', schemeType: '1p',
series: 'B7',
framework: '100ZS',
configParam: defaultConfList, configParam: defaultConfList,
propertyParam: defaultPropList, propertyParam: defaultPropList,
errorRange: { errorRange: {
@ -265,7 +348,7 @@ const generateMockDevices = () => {
const playCalibration = async () => { const playCalibration = async () => {
try { 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) { if (response.data.code === 0) {
devices.value = response.data.data.result || []; devices.value = response.data.data.result || [];
} else { } 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 = () => { const generateRandomLog = () => {
// HH:MM:SS.ms // HH:MM:SS.ms
const now = new Date(); 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(() => { onMounted(() => {
initSocket();
getSchemeDefaultConf(); getSchemeDefaultConf();
getSchemeDefaultProp(); getSchemeDefaultProp();
generateDeviceList(); generateDeviceList();
loadSchemes(); loadSchemes();
}); });
onUnmounted(() => {
if (webSocketStore) {
webSocketStore.close();
}
});
const toggleIsScroll = () => { const toggleIsScroll = () => {
isScroll.value = !isScroll.value; isScroll.value = !isScroll.value;
}; };
@ -404,6 +541,7 @@ const loadSchemes = async () => {
schemeList.value = response.data.data.schemes || []; schemeList.value = response.data.data.schemes || [];
if (!selectedScheme.value && schemeList.value.length > 0) { if (!selectedScheme.value && schemeList.value.length > 0) {
selectedScheme.value = schemeList.value[0].id; selectedScheme.value = schemeList.value[0].id;
activeScheme.value = schemeList.value[0];
} }
ElMessage.success('获取方案列表成功'); ElMessage.success('获取方案列表成功');
} else { } else {
@ -496,13 +634,15 @@ const addScheme = () => {
currentScheme.value = { currentScheme.value = {
schemeName: `方案${schemeList.value.length + 1}`, schemeName: `方案${schemeList.value.length + 1}`,
schemeType: '1p', schemeType: '1p',
series: 'B7',
framework: '100ZS',
configParam: defaultConfList, configParam: defaultConfList,
propertyParam: defaultPropList, propertyParam: defaultPropList,
errorRange: { errorRange: {
voltage: 0.5, voltage: 0.5,
current: 0.5, current: 0.5,
smallCurrent: 1.0, smallCurrent: 1.0,
powerFactor: "0.5L", powerFactor: '0.5L',
activePower: 0.5, activePower: 0.5,
reactivePower: 0.5, reactivePower: 0.5,
apparentPower: 0.5 apparentPower: 0.5
@ -526,21 +666,25 @@ const viewScheme = scheme => {
// //
const addCalibrationStep = () => { const addCalibrationStep = () => {
currentScheme.value.steps.push({ currentScheme.value.steps.push({
id: currentScheme.value.steps.length + 1,
step: currentScheme.value.steps.length + 1, step: currentScheme.value.steps.length + 1,
voltage: 220, voltage: 100,
current: 10, current: 100,
powerFactor: "0.5L", powerFactor: '0.5L',
// riseFirst: true isILeak: false
}); });
}; };
const toggleOpenLog = () => {
isOpenLog.value = !isOpenLog.value;
};
// //
const deleteCalibrationStep = index => { const deleteCalibrationStep = index => {
currentScheme.value.steps.splice(index, 1); currentScheme.value.steps.splice(index, 1);
// //
// currentScheme.value.steps.forEach((step, idx) => { currentScheme.value.steps.forEach((step, idx) => {
// step.step = idx + 1; step.id = idx + 1;
// }); });
}; };
</script> </script>
@ -562,6 +706,23 @@ const deleteCalibrationStep = index => {
color: white; color: white;
flex-shrink: 0; 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-left,
.header-center, .header-center,
@ -627,6 +788,9 @@ const deleteCalibrationStep = index => {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
flex-shrink: 0; flex-shrink: 0;
&.open-log {
height: 350px;
}
} }
.log-output { .log-output {
@ -680,6 +844,19 @@ const deleteCalibrationStep = index => {
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: space-between; 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 { .version-info {
text-align: right; text-align: right;