refactor(layout): 重构整体布局并移除未使用的组件和页面,调整应用配置,添加断路器标定页面

- 删除了 无用组件和页面
- 调整了 Layout 组件的样式
- 更新了路由配置,重定向到新的 calibration 页面
- 调整应用配置
- 添加断路器标定页面
This commit is contained in:
fhysy 2025-06-06 16:39:43 +08:00
parent e97b8516ae
commit 57cf6523ef
28 changed files with 957 additions and 126995 deletions

View File

@ -1,7 +1,7 @@
# 应用程序的唯一标识符
appId: com.electron.app
appId: gy.calibration.pc
# 应用程序的名称
productName: 谷云工具
productName: 谷云标定工具
# 定义构建资源和输出文件的目录
directories:
buildResources: build
@ -50,7 +50,7 @@ linux:
- AppImage
- snap
- deb
maintainer: electronjs.org # 维护者信息
maintainer: GuyunDevelopment # 维护者信息
category: Utility # 应用程序类别
icon: resources/icon.png # 应用程序图标路径
# 针对AppImage包的配置

View File

@ -1,7 +1,7 @@
{
"name": "gateway-app",
"version": "1.0.9",
"description": "An Electron application with Vue",
"name": "calibration-pc",
"version": "1.0.0",
"description": "谷云开发部开发的断路器标定软件",
"main": "./out/main/index.js",
"author": "example.com",
"homepage": "https://electron-vite.org",
@ -21,6 +21,7 @@
"@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",

View File

@ -2,14 +2,13 @@
<html>
<head>
<meta charset="UTF-8" />
<title>谷云工具</title>
<title>谷云标定工具</title>
<!-- https://developer.mozilla.org/en-US/docs/Web/HTTP/CSP -->
<link rel="stylesheet" href="/src/assets/iconfont/iconfont.css">
<!-- <meta-->
<!-- http-equiv="Content-Security-Policy"-->
<!-- content="default-src 'self'; connect-src 'self' *; script-src 'self'; style-src 'self' 'unsafe-inline'; img-src 'self' data:"-->
<!-- />-->
<script type="text/javascript" src="/videoplayer/EasyPlayer-element.min.js"></script>
</head>
<body>

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

File diff suppressed because one or more lines are too long

View File

@ -1,4 +0,0 @@
<?xml version="1.0" encoding="utf-8"?>
<cross-domain-policy>
<allow-access-from domain="*"/>
</cross-domain-policy>

View File

@ -31,7 +31,8 @@ code {
align-items: center;
justify-content: center;
flex-direction: column;
margin-bottom: 80px;
width: 100%;
height: 100%;
}
.logo {

View File

@ -1,69 +0,0 @@
<script setup>
const settings = defineProps({
collapse: Boolean,
width: String
});
</script>
<template>
<el-aside :width="settings.width">
<el-scrollbar>
<el-menu default-active="/system/switch" :collapse="settings.collapse" :router="true">
<el-menu-item index="/system/switch"> <span class="iconfont icon-icon_duanluqi"></span> 断路器调试</el-menu-item>
<!-- <el-menu-item index="/system/videoplayer"><span class="iconfont icon-icon_jiankong"></span> 视频监控</el-menu-item>-->
<el-menu-item index="/system/serialport"><span class="iconfont icon-chuankou2"></span> 设备参数读写</el-menu-item>
<el-menu-item index="/system/devicestandard"><span class="iconfont icon-qianzhaoyiqibiaoding"></span> 断路器标定</el-menu-item>
<el-menu-item index="/system/scancodewrite"><span class="iconfont icon--_saomaqiang"></span> 扫码录入</el-menu-item>
<!-- <el-menu-item index="/gateway/southdirection"><span class="iconfont icon-icon_chajian"></span> 南向采集配置</el-menu-item>-->
<!-- <el-menu-item index="/gateway/northboundtask"><span class="iconfont icon-icon_chajian"></span> 北向任务配置</el-menu-item>-->
<!-- <el-menu-item index="/gateway/collectdatamonitor"><span class="iconfont icon-icon_chajian"></span> 采集数据监视</el-menu-item>-->
<!-- <el-menu-item index="/system/test"><el-icon> <Menu /> </el-icon></el-menu-item>-->
</el-menu>
</el-scrollbar>
</el-aside>
</template>
<style scoped lang="less">
.el-aside {
height: 100vh;
overflow: hidden;
}
.el-menu {
padding: 0 10px;
background: #fff;
}
.el-menu-item {
border-radius: 10px;
background: #f2f2f2;
margin-bottom: 12px;
align-items: center;
padding: 0;
padding-left: 26px !important;
color: #333;
font-size: 16px;
.iconfont {
margin-right: 6px;
color: #999;
font-size: 22px;
}
}
.el-menu-item.is-active {
background: #4c94db;
border-radius: 10px;
/*margin: 0 10px;*/
color: #fff;
.iconfont {
color: #fff;
}
}
.el-scrollbar {
height: 100vh;
overflow: hidden;
/*background-color: #1a1c1e;*/
}
.el-menu {
border-right: none;
}
</style>

View File

@ -1,160 +0,0 @@
<template>
<div>
<el-header style="display: flex; font-size: 16px">
<div class="header-left">
<img :src="logo" alt="" />
</div>
<div class="haeder-menu" :style="{ width: menuWidth + 'px' }">
<el-menu default-active="/system" mode="horizontal" router="true">
<el-menu-item index="/system">系统模板</el-menu-item>
<!-- <el-menu-item index="/gateway">网关</el-menu-item>-->
</el-menu>
</div>
<div class="haeder-right">
<div class="block" style="margin-right: 25px">
<el-icon size="24" color="#80b3e6"><BellFilled /></el-icon>
</div>
<div class="user-name" style="margin-right: 10px">admin</div>
<el-dropdown trigger="click">
<div class="el-dropdown-link avatar-box" style="cursor: pointer">
<img :src="avatar" alt="" />
</div>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item>admin</el-dropdown-item>
<!-- <el-dropdown-item @click="LogOut">退出登录</el-dropdown-item>-->
</el-dropdown-menu>
</template>
</el-dropdown>
</div>
</el-header>
</div>
</template>
<script setup>
import { ref, watch } from 'vue';
import logo from '@renderer/assets/image/logo.png';
import avatar from '@renderer/assets/image/avatar.png';
const screenWidth = ref(document.documentElement.clientWidth || document.body.clientWidth);
const timer = ref(false);
const domWidth = ref(document.body.clientWidth);
const menuWidth = ref(document.body.clientWidth - 363);
watch(
(screenWidth.value = () => {
// 使
if (!timer.value) {
if (domWidth.value != document.body.clientWidth) {
domWidth.value = document.body.clientWidth;
menuWidth.value = document.body.clientWidth - 363;
}
timer.value = true;
setTimeout(function () {
timer.value = false;
}, 500);
}
})
);
</script>
<style scoped lang="scss">
.el-header {
width: 100%;
background-color: #0066cc;
color: var(--el-text-color-primary);
justify-content: space-between;
padding: 0;
}
.header-left {
display: flex;
justify-content: center;
align-items: center;
width: 180px;
img {
width: 154px;
height: 37px;
}
}
.haeder-menu {
width: calc(100% - 180px - 183px);
overflow: hidden;
:deep(.el-scrollbar__bar.is-vertical) {
display: none;
}
:deep(a) {
width: 100%;
}
.el-menu.el-menu--horizontal {
display: flex;
height: 100%;
width: 100%;
box-sizing: border-box;
background: #0066cc;
border-bottom: none;
.el-menu-item {
color: #fff !important;
width: 124px;
font-size: 16px;
border: none;
text-indent: 10px;
&.is-active {
background: #267dd4;
position: relative;
&:after {
content: '';
position: absolute;
top: 22px;
left: 20px;
width: 5px;
height: 16px;
background: #fff;
border-radius: 2px;
}
}
&:not(.is-disabled):hover {
background: #4c94db;
}
}
}
}
.haeder-right {
display: flex;
align-items: center;
width: 165px;
margin-right: 18px;
.user-name {
font-family: Source Han Sans CN;
font-weight: 400;
font-size: 12px;
color: #ffffff;
}
.avatar-box {
img {
width: 54px;
height: 54px;
margin-bottom: -2px;
}
}
}
.toolbar {
display: inline-flex;
align-items: center;
justify-content: center;
height: 100%;
right: 20px;
text-align: right;
}
.el-dropdown-menu__item {
width: 120px;
}
.icon-color {
color: white;
}
</style>

View File

@ -1,6 +1,5 @@
<template>
<el-container direction="vertical" style="min-height: 100vh; min-width: 100vw; overflow: hidden">
<Header></Header>
<el-container direction="vertical" style="height: 100%; width: 100%; ">
<el-main>
<router-view v-slot="{ Component }">
<keep-alive>
@ -13,7 +12,6 @@
<script setup>
//
import Header from '@renderer/layout/Header.vue';
</script>
<style scoped>
@ -30,6 +28,6 @@ import Header from '@renderer/layout/Header.vue';
/*height: 100vh;*/
overflow-x: hidden;
background: #fff;
padding-right: 10px;
/*padding-right: 10px;*/
}
</style>

View File

@ -1,6 +1,5 @@
import { createRouter, createWebHistory } from 'vue-router';
import Layout from '@renderer/layout/index.vue';
import systenLayout from '@renderer/views/system/index.vue';
const router = createRouter({
history: createWebHistory(),
@ -9,119 +8,22 @@ const router = createRouter({
path: '/',
component: Layout,
meta: { title: '整体页面布局' },
redirect: '/system',
redirect: '/calibration',
children: [
{
path: '/system',
name: 'system',
component: systenLayout,
redirect: { name: 'switch' },
children: [
{
path: 'switch',
name: 'switch',
component: () => import('@renderer/views/system/switch/index.vue'),
meta: {
menu: 'switch',
keepAlive: true
}
},
{
path: 'videoplayer',
name: 'videoplayer',
component: () => import('@renderer/views/system/videoplayer/index.vue'),
meta: {
menu: 'videoplayer',
keepAlive: true
}
},
{
path: 'serialport',
name: 'serialport',
component: () => import('@renderer/views/system/serialport/index.vue'),
meta: {
menu: 'serialport',
keepAlive: true
}
},
{
path: 'scancodewrite',
name: 'scancodewrite',
component: () => import('@renderer/views/system/scancodewrite/index.vue'),
meta: {
menu: 'scancodewrite',
keepAlive: true
}
},
{
path: 'devicestandard',
name: 'devicestandard',
component: () => import('@renderer/views/system/serialport/devicestandard.vue'),
meta: {
menu: 'devicestandard',
keepAlive: true
}
},
{
path: 'about',
name: 'about',
component: () => import('@renderer/views/AboutView.vue'),
meta: {
menu: 'about',
keepAlive: true
}
},
{
path: 'test',
name: 'test',
component: () => import('@renderer/views/test.vue'),
meta: {
menu: 'test',
keepAlive: true
}
}
]
},
{
path: '/gateway',
name: 'gateway',
component: systenLayout,
redirect: { name: 'southdirection' },
children: [
{
path: 'southdirection',
name: 'southdirection',
component: () => import('@renderer/views/gateway/southdirection.vue'),
meta: {
menu: 'southdirection',
keepAlive: true
}
},
{
path: 'northboundtask',
name: 'northboundtask',
component: () => import('@renderer/views/gateway/northboundtask.vue'),
meta: {
menu: 'northboundtask',
keepAlive: true
}
},
{
path: 'collectdatamonitor',
name: 'collectdatamonitor',
component: () => import('@renderer/views/gateway/collectdatamonitor.vue'),
meta: {
menu: 'collectdatamonitor',
keepAlive: true
}
}
]
}
path: 'calibration',
name: 'calibration',
component: () => import('@renderer/views/calibration/index.vue'),
meta: {
menu: 'calibration',
keepAlive: true
}
}
]
},
{
path: '/', // 这个是根路由,现在我们让它重定向到 /system
redirect: '/system'
redirect: '/calibration'
},
{ path: '/:pathMatch(.*)', redirect: '/' }
]

View File

@ -1,15 +0,0 @@
<template>
<div class="about">
<h1>This is an about page</h1>
</div>
</template>
<style>
@media (min-width: 1024px) {
.about {
min-height: 100vh;
display: flex;
align-items: center;
}
}
</style>

View File

@ -0,0 +1,258 @@
<template>
<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-option v-for="item in schemes" :key="item.value" :label="item.label" :value="item.value"></el-option>
</el-select>
<el-button icon="CaretRight" type="primary" :disabled="playState" @click="startExecution">开始执行</el-button>
<el-button icon="RemoveFilled" type="danger" :disabled="!playState" @click="stopExecution">停止</el-button>
</div>
<!-- <div class="header-center">-->
<!-- <el-button type="primary" size="small" @click="startExecution">开始执行</el-button>-->
<!-- <el-button type="danger" size="small" @click="stopExecution">停止</el-button>-->
<!-- </div>-->
<div class="header-right">
<el-button icon="Setting" :disabled="playState" @click="openSettings">设置</el-button>
</div>
</el-header>
<el-main class="calibration-main">
<div class="device-grid">
<el-card v-for="device in devices" :key="device.id" class="device-card">
<template #header>
<div class="device-card-header">
<span>{{ device.code }}</span>
<el-tag :type="device.status === 'connected' ? 'success' : 'info'" size="small">
{{ device.status === 'connected' ? '已连接' : '未连接' }}
</el-tag>
</div>
</template>
<div class="device-card-body">
<div :class="['iconfont', 'icon-icon_duanluqi', 'circuit-breaker', device.result === '成功' ? 'color-green' : device.result === '失败' ? 'color-red' : '']"></div>
<p>设备码: {{ device.deviceCode || '无' }}</p>
<p>设备地址: {{ device.address || '无' }}</p>
<p>
标定结果: <span :class="['device-result', device.result === '成功' ? 'color-green' : device.result === '失败' ? 'color-red' : '']">{{ device.result || '无' }}</span>
</p>
</div>
</el-card>
</div>
</el-main>
<el-footer class="calibration-footer">
<div class="log-output">
<pre v-for="(log, index) in logs" :key="index">{{ log }}</pre>
</div>
<div class="version-info">{{ version }}</div>
</el-footer>
</div>
</template>
<script setup>
import { ref, onMounted } from 'vue';
import { ElMessage } from 'element-plus';
const selectedScheme = ref('scheme1');
const playState = ref(false);
const devices = ref([]);
const schemes = ref([
{
value: 'scheme1',
label: '方案1'
},
{
value: 'scheme2',
label: '方案2'
}
]);
const logs = ref(['14:44:10.482 添加条码 GE 2', '14:44:10.487 连接失败 127.0.0.1:8821 0000 未将对象引用设置到对象的实例。']);
const version = ref('V1.0.0'); // Or dynamically get this
const generateMockDevices = () => {
const mockDevices = [];
for (let i = 1; i <= 16; i++) {
mockDevices.push({
id: i,
code: `设备 ${i}`,
deviceCode: Math.floor(1000000000000 + Math.random() * 9000000000000).toString(),
address: `192.168.1.${i}`,
status: i % 3 === 0 ? 'disconnected' : 'connected', // Mock status
result: i % 3 !== 0 ? (i % 2 === 0 ? '' : '成功') : '失败' // Mock result
});
}
devices.value = mockDevices;
};
onMounted(() => {
generateMockDevices();
});
const startExecution = () => {
ElMessage.info('开始执行');
playState.value = true;
// Add actual start logic here
};
const stopExecution = () => {
ElMessage.info('停止执行');
playState.value = false;
// Add actual stop logic here
};
const openSettings = () => {
ElMessage.info('打开设置');
// Add settings opening logic here
};
</script>
<style scoped lang="scss">
.calibration-page {
display: flex;
flex-direction: column;
height: 100vh;
background-color: #f0f2f5;
}
.calibration-header {
display: flex;
justify-content: space-between;
align-items: center;
padding: 0 20px;
height: 60px;
background-color: #404040; /* Dark grey similar to image */
color: white;
flex-shrink: 0;
}
.header-left,
.header-center,
.header-right {
display: flex;
align-items: center;
}
.header-center .el-button {
margin-left: 10px;
}
.calibration-main {
flex-grow: 1;
padding: 20px;
overflow-y: auto;
}
.device-grid {
display: grid;
grid-template-columns: repeat(8, 1fr);
gap: 15px;
}
.device-card {
/* background-color: #ffffff; */ /* Default card color is fine */
}
.device-card-header {
display: flex;
justify-content: space-between;
align-items: center;
font-size: 14px;
}
.device-card-body {
font-size: 12px;
text-align: center;
.device-result {
font-size: 14px;
font-weight: bold;
}
}
.circuit-breaker {
font-size: 70px;
color: #999;
}
.color-green {
color: #67c23a; /* Green for connected */
}
.color-red {
color: #f56c6c; /* Red for disconnected */
}
.calibration-footer {
height: 200px; /* Fixed height for logs */
background-color: #303030; /* Darker grey for logs */
color: #a7a7a7; /* Light grey text for logs */
padding: 10px 20px;
display: flex;
flex-direction: column;
flex-shrink: 0;
}
.log-output {
flex-grow: 1;
overflow-y: auto;
font-family: 'Courier New', Courier, monospace;
font-size: 12px;
white-space: pre-wrap; /* Ensures logs wrap and preserve formatting */
border: 1px solid #555;
padding: 5px;
background-color: #202020;
}
.log-output pre {
margin: 2px 0;
color: #dcdcdc;
}
.version-info {
text-align: right;
font-size: 12px;
padding-top: 5px;
color: #888;
}
/* Element Plus component overrides if needed */
.el-header {
--el-header-padding: 0 20px; /* Adjust if necessary */
--el-header-height: 60px; /* Ensure this matches fixed height */
}
.el-footer {
--el-footer-padding: 0px; /* Adjust if necessary */
--el-footer-height: 200px; /* Ensure this matches fixed height */
}
.el-main {
--el-main-padding: 20px; /* Adjust if necessary */
}
.el-select .el-input__inner {
background-color: #555; /* Darker input fields */
color: white;
border-color: #666;
}
//.el-button--primary {
// background-color: #007bff; /* Example primary button color */
// border-color: #007bff;
//}
//
//.el-button--danger {
// background-color: #dc3545; /* Example danger button color */
// border-color: #dc3545;
//}
:deep(.device-grid .el-card__header) {
padding: 10px;
}
:deep(.device-grid .el-card__body) {
padding: 10px;
}
/* Ensure select dropdowns are also styled if needed */
</style>

View File

@ -1,240 +0,0 @@
<template>
<div id="southdirection-box">
<div class="device-config">
<h2>设备状态</h2>
<div class="btn-list">
<div class="btn-list-left"></div>
<div class="btn-list-right">
<el-button type="primary" @click="getDeviceList">刷新</el-button>
</div>
</div>
<div class="table-box">
<el-table
ref="deviceConfigTableRef"
highlight-current-row
:data="deviceList"
border
height="187"
:style="{ width: tabelBox + 'px', background: '#f2f2f2' }"
empty-text="配置为空"
@row-click="deviceConfigClick"
>
<el-table-column type="index" label="序号" align="center" width="60" />
<el-table-column prop="equipId" label="设备ID" align="center" />
<el-table-column prop="quality" label="通讯质量" align="center" />
<el-table-column prop="status" label="运行状态" align="center" />
<el-table-column prop="status" label="开启状态" align="center">
<template #default="scope">
<el-switch v-model="scope.row.status" :before-change="handleBeforeChange.bind(this, scope.$index)" />
</template>
</el-table-column>
</el-table>
</div>
</div>
<div class="data-config">
<h2>数据源配置 {{ activeDeviceConfig.equipId || '' }}</h2>
<div class="btn-list">
<div class="btn-list-left"></div>
<div class="btn-list-right">
<el-button type="primary" @click="getDeviceConfig">刷新</el-button>
</div>
</div>
<div class="table-box">
<el-table ref="deviceDataTableRef" :data="activeDeviceConfig.result" border size="small" height="100%" :style="{ width: tabelBox + 'px' }" empty-text="配置为空">
<el-table-column type="index" label="序号" align="center" width="60" fixed />
<el-table-column prop="id" label="设备ID_数据ID" align="center">
<template #default="scope">
{{ activeDeviceConfig.equipId + '_' + scope.row.id }}
</template>
</el-table-column>
<el-table-column prop="dataName" label="数据名" align="center" />
<el-table-column prop="dataValue" label="当前值" align="center" />
<el-table-column prop="dataUnit" label="单位" align="center" />
<el-table-column prop="time" label="时间" align="center" />
<!-- <el-table-column label="操作" width="55" fixed="right" align="center">-->
<!-- <template #default="scope">-->
<!-- <el-button link type="danger" size="small" @click.stop="delDeviceDataConfig(scope.$index)">删除</el-button>-->
<!-- </template>-->
<!-- </el-table-column>-->
</el-table>
</div>
</div>
</div>
</template>
<script setup>
import { ref, onMounted, onUnmounted } from 'vue';
import { ElMessage } from 'element-plus';
import axios from 'axios';
import config from '@renderer/util/config.js';
const deviceConfigTableRef = ref();
const deviceDataTableRef = ref();
const tabelBox = ref(884);
const deviceList = ref([]);
//
const ActiveRowIndex = ref(0);
const activeDeviceConfig = ref({
equipId: '',
status: false,
result: []
});
const getDeviceDataList = id => {
// console.log('')
axios
.get(config.url + '/data/collect?equipId=' + id, {})
.then(response => {
//
if (response.data.code == 0) {
//
activeDeviceConfig.value = {
equipId: response.data.data.equipId || '',
status: response.data.data.status || false,
result: response.data.data.result || []
};
// activeDeviceResult.value = response.data.data.result || []
} else {
ElMessage.error(response.data.message);
}
})
.catch(error => {
//
console.error(error); //
ElMessage.error(error);
});
};
const getDeviceList = () => {
// console.log('')
axios
.get(config.url + '/data/equip/list', {})
.then(response => {
//
if (response.data.code == 0) {
//
deviceList.value = response.data.data.equips || [];
if (deviceList.value.length > ActiveRowIndex.value) {
getDeviceDataList(deviceList.value[ActiveRowIndex.value].equipId);
} else if (deviceList.value.length > 0) {
getDeviceDataList(deviceList.value[0].equipId);
} else {
activeDeviceConfig.value = {
equipId: '',
starus: false,
result: []
};
// activeDeviceResult.value = []
}
} else {
ElMessage.error(response.data.message);
}
})
.catch(error => {
//
console.error(error); //
ElMessage.error(error);
});
};
const getDeviceConfig = () => {
getDeviceDataList(deviceList.value[ActiveRowIndex.value].equipId);
};
const handleBeforeChange = index => {
console.log('e', index);
return new Promise((resolve, reject) => {
let deviceObj = deviceList.value[index];
console.log('deviceObj', deviceObj.status);
let url = config.url + '/data/collect/start?equipId=' + deviceObj.equipId;
if (deviceObj.status) {
url = config.url + '/data/collect/end?taskId=' + deviceObj.equipId;
}
// axios.get
axios
.get(url)
.then(response => {
console.log('response', response);
if (response.data.code === 0) {
// ok
if (deviceObj.status) {
ElMessage.success('关闭成功');
} else {
ElMessage.success('开启成功');
}
deviceList.value[index].status = !deviceList.value[index].status;
getDeviceList();
resolve();
} else {
// ok
ElMessage.error(response.data.message);
reject();
}
})
.catch(error => {
//
ElMessage.error(error.data.message);
reject();
});
});
};
//
const deviceConfigClick = e => {
getDeviceDataList(e.equipId);
deviceConfigTableRef.value.toggleRowSelection(e);
ActiveRowIndex.value = deviceList.value.indexOf(e);
};
//
const handleResize = () => {
deviceDataTableRef.value.doLayout();
tabelBox.value = window.innerWidth - 180 - 10;
};
onMounted(() => {
window.addEventListener('resize', handleResize);
getDeviceList();
handleResize();
});
onUnmounted(() => {
window.removeEventListener('resize', handleResize);
});
</script>
<style scoped lang="scss">
.el-table .double-clicked-row {
background-color: #f0f9eb;
}
:deep(.el-table .el-scrollbar) {
background: #fff;
}
#southdirection-box {
height: 100%;
.btn-list {
display: flex;
align-items: center;
justify-content: space-between;
padding: 10px 0;
}
.device-config {
position: relative;
overflow-y: auto;
}
.data-config {
position: relative;
height: calc(100% - 278px);
.table-box {
position: absolute;
width: 100%;
height: calc(100% - 162px);
}
}
.table-box {
//position: absolute;
//width: 100%;
}
}
</style>

View File

@ -1,656 +0,0 @@
<template>
<div id="southdirection-box">
<div class="plat-config">
<h2>平台配置</h2>
<div class="btn-list">
<div class="btn-list-left">
<el-button type="primary" @click="addDeviceConfig">新增配置</el-button>
<!-- <el-button type="danger" @click="getDeviceConfig">删除</el-button>-->
</div>
<div class="btn-list-right">
<!-- <el-button type="primary" @click="getDeviceConfig">导出</el-button>-->
<!-- <el-button type="primary" @click="getDeviceConfig">导入</el-button>-->
<el-button type="primary" @click="getDeviceConfig">获取配置</el-button>
<el-button type="primary" @click="setDeviceConfig">设置配置</el-button>
</div>
</div>
<div class="table-box">
<el-table
ref="platConfigTableRef"
highlight-current-row
:data="platConfigList"
border
height="187"
:row-class-name="tableRowClassName"
:style="{ width: tabelBox + 'px',background:'#f2f2f2' }"
empty-text="配置为空"
>
<el-table-column type="index" label="序号" align="center" width="60" />
<el-table-column prop="PlatformIp" label="平台地址" >
<template #default="scope">
<el-input v-model="scope.row.PlatformIp" placeholder="请输入平台地址" />
</template>
</el-table-column>
<el-table-column prop="type" label="上报格式" >
<template #default="scope">
<el-select v-model="scope.row.type" placeholder="请选择上报格式">
<el-option
v-for="item in uploadType"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</template>
</el-table-column>
<el-table-column prop="mqtt.user" label="用户名" >
<template #default="scope">
<el-input v-model="scope.row.mqtt.user" placeholder="请输入用户名" />
</template>
</el-table-column>
<el-table-column prop="mqtt.passwd" label="密码" >
<template #default="scope">
<el-input type="password" show-password v-model="scope.row.mqtt.passwd" placeholder="请输入密码" />
</template>
</el-table-column>
<el-table-column prop="mqtt.isUseSsl" label="ssl" >
<template #default="scope">
<el-switch v-model="scope.row.mqtt.isUseSsl" />
</template>
</el-table-column>
<el-table-column prop="mqtt.isUseSslConf" label="ssl文件" >
<template #default="scope">
<el-switch v-model="scope.row.mqtt.isUseSslConf" />
</template>
</el-table-column>
<el-table-column prop="pushConf.isUseResumeBrokenTransfer" label="断点续存" >
<template #default="scope">
<el-switch v-model="scope.row.pushConf.isUseResumeBrokenTransfer" />
</template>
</el-table-column>
<el-table-column prop="tasks" label="任务ID" >
<template #default="scope">
{{scope.row.tasks?scope.row.tasks.join():''}}
</template>
</el-table-column>
<el-table-column label="操作" width="140" align="center">
<template #default="scope">
<el-button link type="primary" size="small" @click="openCollectionModel(scope.row)">加解密配置</el-button>
<el-button link type="danger" size="small" @click="delDeviceConfig(scope.$index)">删除</el-button>
</template>
</el-table-column>
</el-table>
</div>
</div>
<div class="plat-config">
<h2>任务配置</h2>
<div class="table-box">
<el-table
ref="platConfigTableRef"
highlight-current-row
:data="taskList"
border
height="187"
:row-class-name="tableRowClassName"
@row-click="taskConfigClick"
:style="{ width: tabelBox + 'px',background:'#f2f2f2' }"
empty-text="配置为空"
>
<el-table-column type="index" label="序号" align="center" width="60" />
<el-table-column prop="taskId" label="任务ID" align="center">
<template #default="scope">
{{scope.row.taskId}}
<!-- <el-input v-model="scope.row.taskId" placeholder="请输入任务ID" />-->
</template>
</el-table-column>
<el-table-column prop="uploadMode" label="上报策略" >
<template #default="scope">
<el-autocomplete
v-model="scope.row.uploadMode"
:fetch-suggestions="querySearch"
clearable
class="inline-input w-50"
placeholder="请选择上报策略"
/>
</template>
</el-table-column>
</el-table>
</div>
</div>
<div class="data-config">
<h2>上报测点 {{activeDeviceConfig.taskId}}</h2>
<div class="table-box">
<el-table
:data="activeDeviceConfig.dataIdEntire"
border
size="small"
height="100%"
ref="deviceDataTableRef"
:style="{ width: tabelBox + 'px' }"
empty-text="配置为空"
>
<el-table-column type="index" label="序号" align="center" width="60" fixed />
<el-table-column prop="equipId" label="设备ID" align="center"/>
<el-table-column prop="idInfo[0].id" label="数据ID" align="center"/>
<el-table-column prop="idInfo[0].uploadMode" label="上报策略" align="center"/>
</el-table>
</div>
</div>
<el-dialog
v-model="collectionModelShow"
title="加解密配置"
width="500"
:before-close="handleClose"
>
<el-form ref="collectionRef" :model="collectionForm" label-width="auto" status-icon>
<h3>加密算法配置</h3>
<el-form-item label="启动">
<el-switch v-model="collectionForm.encrypt_1.enable" />
</el-form-item>
<el-row>
<el-col :span="12">
<el-form-item label="加密算法">
<el-select v-model="collectionForm.encrypt_1.algorithm" placeholder="选择加密算法">
<el-option label="AES" value="AES" />
<el-option label="DES" value="DES" />
<el-option label="SM4" value="SM4" />
<el-option label="RSA" value="RSA" />
</el-select>
</el-form-item>
</el-col>
<el-col :span="12" class="text-center">
<el-form-item label="加密key值">
<el-input v-model="collectionForm.encrypt_1.param.key" placeholder="请输入加密key值" />
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="12">
<el-form-item label="加密模式">
<el-select v-model="collectionForm.encrypt_1.mode" placeholder="选择加密模式">
<el-option label="ECB" value="ECB" />
<el-option label="CBC" value="CBC" />
<el-option label="CTR" value="CTR" />
<el-option label="CFB" value="CFB" />
<el-option label="CFB1" value="CFB1" />
<el-option label="CFB8" value="CFB8" />
<el-option label="CFB128" value="CFB128" />
<el-option label="OFB" value="OFB" />
<el-option label="OFB128" value="OFB128" />
</el-select>
</el-form-item>
</el-col>
<el-col :span="12" class="text-center">
<el-form-item label="加密向量表">
<el-input v-model="collectionForm.encrypt_1.param.ivec" placeholder="请输入加密向量表" />
</el-form-item>
</el-col>
</el-row>
<el-form-item label="加密位数">
<el-select v-model="collectionForm.encrypt_1.bits" placeholder="选择加密位数">
<el-option label="128" value="128" />
<el-option label="192" value="192" />
<el-option label="256" value="256" />
</el-select>
</el-form-item>
<h3>解密算法配置</h3>
<el-form-item label="启动">
<el-switch v-model="collectionForm.decrypt_1.enable" />
</el-form-item>
<el-row>
<el-col :span="12">
<el-form-item label="加密算法">
<el-select v-model="collectionForm.decrypt_1.algorithm" placeholder="选择加密算法">
<el-option label="AES" value="AES" />
<el-option label="DES" value="DES" />
<el-option label="SM4" value="SM4" />
<el-option label="RSA" value="RSA" />
</el-select>
</el-form-item>
</el-col>
<el-col :span="12" class="text-center">
<el-form-item label="加密key值">
<el-input v-model="collectionForm.decrypt_1.param.key" placeholder="请输入加密key值" />
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="12">
<el-form-item label="加密模式">
<el-select v-model="collectionForm.decrypt_1.mode" placeholder="选择加密模式">
<el-option label="ECB" value="ECB" />
<el-option label="CBC" value="CBC" />
<el-option label="CTR" value="CTR" />
<el-option label="CFB" value="CFB" />
<el-option label="CFB1" value="CFB1" />
<el-option label="CFB8" value="CFB8" />
<el-option label="CFB128" value="CFB128" />
<el-option label="OFB" value="OFB" />
<el-option label="OFB128" value="OFB128" />
</el-select>
</el-form-item>
</el-col>
<el-col :span="12" class="text-center">
<el-form-item label="加密向量表">
<el-input v-model="collectionForm.decrypt_1.param.ivec" placeholder="请输入加密向量表" />
</el-form-item>
</el-col>
</el-row>
<el-form-item label="加密位数">
<el-select v-model="collectionForm.decrypt_1.bits" placeholder="选择加密位数">
<el-option label="128" value="128" />
<el-option label="192" value="192" />
<el-option label="256" value="256" />
</el-select>
</el-form-item>
</el-form>
<template #footer>
<div class="dialog-footer">
<el-button @click="collectionModelShow = false">取消</el-button>
<el-button type="primary" @click="editCollection">
确认
</el-button>
</div>
</template>
</el-dialog>
</div>
</template>
<script setup>
import { reactive, ref, onMounted, onUnmounted, computed, watch } from "vue"
import { ElMessage, ElMessageBox } from "element-plus"
import axios from "axios"
import config from '@renderer/util/config.js';
//
const uploadModeList = ref([
{ value: 'timer 10', link: 'timer 10' },
{ value: 'change 10', link: 'change 10' },
{ value: 'timer 10 || change 10', link: 'timer 10 || change 10' },
{ value: 'label LD200 up', link: 'label LD200 up' },
])
const querySearch = (queryString, cb) => {
const results = queryString
? uploadModeList.value.filter(createFilter(queryString))
: uploadModeList.value
// call callback function to return suggestions
cb(results)
}
const createFilter = (queryString) => {
return (restaurant) => {
return (
restaurant.value.toLowerCase().indexOf(queryString.toLowerCase()) === 0
)
}
}
//
const taskList = ref([
]);
const platConfigTableRef = ref();
const deviceDataTableRef = ref();
const tabelBox = ref(884)
const platConfigList = ref([])
const uploadType = ref([
{ label: 'DT-MQTT-DEFAULT', value: 'DT-MQTT-DEFAULT'},
{ label: 'DT-MQTT-DEBUG', value: 'DT-MQTT-DEBUG'},
{ label: 'HN-MQTT-BJ', value: 'HN-MQTT-BJ'},
{ label: 'SC-MQTT-DEFAULT', value: 'SC-MQTT-DEFAULT'},
{ label: 'WG-MQTT-PuPu', value: 'WG-MQTT-PuPu'},
{ label: 'WG-MQTT-TengSheng', value: 'WG-MQTT-TengSheng'},
])
//
const addDeviceConfig = () => {
let newDeviceConfig = {
"PlatformIp": "",
"tasks": ["T1", "T2", "T3", "T4", "T5"],
"type": "",
"mqtt": {
"user": "",
"passwd": "",
"isUseSsl": false,
"isUseSslConf": false,
"sslConf": {
"localCertificate_file": "/etc/custom/sslConf/pupu_core_3501000001.pem",
"privateKey_file": "/etc/custom/sslConf/pupu_core_3501000001.prv"
}
},
"pushConf": {
"isRun": true,
"isUseResumeBrokenTransfer": false,
"broken": {
"resume": { "intervalTime_ms": 1000 },
"filter": {
"filter_s": 600,
"relevanceTaskId": ["T1", "T2", "T3", "T4", "T5"]
}
}
},
"encrypt_decrypt": {
"encrypt_1": {
"algorithm": "AES",
"param": {
"mode": "CBC",
"key": "0123456789abcdef",
"ivec": "0123456789abcdef",
"bits": 128
},
"enable": false
},
"decrypt_1": {
"algorithm": "AES",
"param": {
"mode": "CBC",
"key": "0123456789abcdef",
"ivec": "0123456789abcdef",
"bits": 128
},
"enable": false
}
}
};
console.log("platConfigList.value",platConfigTableRef.value)
platConfigList.value.push(newDeviceConfig);
}
//
const delDeviceConfig = (index) => {
console.log("当前点击的行号", index);
ElMessageBox.confirm(
'确认要删除该设备配置吗?',
'告警',
{
confirmButtonText: '确认',
cancelButtonText: '取消',
type: 'warning',
}
)
.then(() => {
platConfigList.value.splice(index, 1);
ElMessage({
type: 'success',
message: '删除成功',
})
})
.catch(() => {
ElMessage({
type: 'info',
message: '取消删除',
})
})
}
const getDeviceConfig = () => {
console.log('获取配置')
axios
.get(config.url + '/config/platform/query', {})
.then(response => {
//
console.log(response); //
if (response.data.code == 0) {
//
platConfigList.value = response.data.data.PlatformPar || [];
taskList.value = response.data.data.tasks.map(item=>{
if(item.dataIdEntire!=[]){
let dataIdEntireList = [];
item.dataIdEntire.forEach(val=>{
val.idInfo.forEach(info=>{
dataIdEntireList.push({
equipId: val.equipId,
idInfo:[info]
})
})
})
item.dataIdEntire = dataIdEntireList;
}
return item;
}) || [];
// taskList.value = taskDataList;
console.log("格式化后数据",taskList.value)
if(!platConfigList.value.length){
addDeviceConfig()
}
if(platConfigList.value.length){
if(platConfigList.value.length>ActiveRowIndex.value){
activeDeviceConfig.value = platConfigList.value[ActiveRowIndex.value];
}else{
activeDeviceConfig.value = platConfigList.value[0];
}
}
} else {
ElMessage.error(response.data.message);
}
})
.catch(error => {
//
console.error(error); //
ElMessage.error(error);
});
}
const setDeviceConfig = () => {
console.log('提交配置',platConfigList.value)
axios
.post(config.url + '/config/platform/add', {PlatformPar: platConfigList.value,tasks:taskList.value})
.then(response => {
//
console.log(response); //
if (response.data.code == 0) {
ElMessage.success('保存成功');
} else {
ElMessage.error(response.data.message);
}
})
.catch(error => {
//
console.error(error); //
ElMessage.error(error);
});
}
//
const ActiveRowIndex = ref(0);
const collectionModelShow = ref(false);
const collectionRef = ref();
const collectionForm = ref({
//
"encrypt_1": {
//
"algorithm": "AES",
//
"param": {
"mode": "CBC",
"key": "0123456789abcdef",
"ivec": "0123456789abcdef",
"bits": 128
},
//
"enable": false
},
//
"decrypt_1": {
//
"algorithm": "AES",
//
"param": {
"mode": "CBC",
"key": "0123456789abcdef",
"ivec": "0123456789abcdef",
"bits": 128
},
//
"enable": false
}
})
//
const openCollectionModel = (row) => {
console.log("当前采集",row)
collectionForm.value = {
"encrypt_1": row.encrypt_decrypt.encrypt_1,
"decrypt_1": row.encrypt_decrypt.decrypt_1
};
collectionModelShow.value = true;
}
const editCollection = () => {
platConfigList.value[ActiveRowIndex.value].encrypt_decrypt.encrypt_1 = collectionForm.value.encrypt_1;
platConfigList.value[ActiveRowIndex.value].encrypt_decrypt.decrypt_1 = collectionForm.value.decrypt_1;
collectionModelShow.value = false;
}
const activeDeviceConfig = ref({
"PlatformIp": "",
"tasks": ["T1", "T2", "T3", "T4", "T5"],
"type": "",
"mqtt": {
"user": "",
"passwd": "",
"isUseSsl": false,
"isUseSslConf": false,
"sslConf": {
"localCertificate_file": "/etc/custom/sslConf/pupu_core_3501000001.pem",
"privateKey_file": "/etc/custom/sslConf/pupu_core_3501000001.prv"
}
},
"pushConf": {
"isRun": true,
"isUseResumeBrokenTransfer": false,
"broken": {
"resume": { "intervalTime_ms": 1000 },
"filter": {
"filter_s": 600,
"relevanceTaskId": ["T1", "T2", "T3", "T4", "T5"]
}
}
},
"encrypt_decrypt": {
"encrypt_1": {
"algorithm": "AES",
"param": {
"mode": "CBC",
"key": "0123456789abcdef",
"ivec": "0123456789abcdef",
"bits": 128
},
"enable": false
},
"decrypt_1": {
"algorithm": "AES",
"param": {
"mode": "CBC",
"key": "0123456789abcdef",
"ivec": "0123456789abcdef",
"bits": 128
},
"enable": false
}
}
})
watch(
() => ({ ...activeDeviceConfig.value }),
(newVal) => {
console.log("数据变化",newVal)
platConfigList.value[ActiveRowIndex.value] = newVal;
},
{ deep: true }
);
// const saveDeviceDataConfig = () => {
// platConfigList.value[ActiveRowIndex.value] = activeDeviceConfig.value;
// }
const tableRowClassName = ({ row, rowIndex }) => {
if (rowIndex === ActiveRowIndex.value) {
return 'selected-row';
}
return '';
};
//
const taskConfigClick = (e) => {
console.log(`current page:`,e)
activeDeviceConfig.value = e;
platConfigTableRef.value.toggleRowSelection(e);
ActiveRowIndex.value = platConfigList.value.indexOf(e);
// e.isDoubleClicked = !e.isDoubleClicked;
}
//
const handleResize = () => {
deviceDataTableRef.value.doLayout();
tabelBox.value = window.innerWidth - 180 - 10 ;
}
onMounted(() => {
window.addEventListener('resize', handleResize);
getDeviceConfig()
handleResize()
// if(platConfigList.value[0]){
// activeDeviceConfig.value = platConfigList.value[0];
// platConfigTableRef.value.toggleRowSelection(platConfigList.value[0]);
// ActiveRowIndex.value = 0;
// }
});
onUnmounted(() => {
window.removeEventListener('resize', handleResize);
});
</script>
<style scoped lang="scss">
.el-table .double-clicked-row {
background-color: #f0f9eb;
}
:deep(.el-table .el-scrollbar) {
background: #fff;
}
#southdirection-box{
height: 100%;
.btn-list{
display: flex;
align-items: center;
justify-content: space-between;
padding: 10px 0;
}
.plat-config{
position: relative;
overflow-y: auto;
}
.data-config{
position: relative;
height: calc(100% - 278px - 226px);
.table-box{
position: absolute;
width: 100%;
height: calc(100% - 120px);
}
}
.table-box{
//position: absolute;
//width: 100%;
}
}
</style>
<style>
.selected-row {
background-color: #55e800;
}
</style>

File diff suppressed because it is too large Load Diff

View File

@ -1,35 +0,0 @@
<template>
<el-container style="padding: 10px 0">
<Aside width="180px"></Aside>
<el-main>
<router-view v-slot="{ Component }">
<keep-alive>
<component :is="Component" />
</keep-alive>
</router-view>
</el-main>
</el-container>
</template>
<script setup>
//
import Aside from '@renderer/layout/Aside.vue';
</script>
<style scoped>
body {
background-color: #eee;
max-width: 100vw;
}
.el-container {
/*padding: 10px 0;*/
}
.el-main {
width: 100%;
padding: 0;
height: 100vh;
overflow-x: hidden;
background: #fff;
padding-right: 10px;
}
</style>

View File

@ -1,796 +0,0 @@
<template>
<div class="scanner-container">
<el-upload
v-model:file-list="fileList"
class="upload-demo"
:action="config.serialPortUrl + '/importDevice'"
multiple
ref="upload"
name="excelFile"
:before-remove="beforeRemove"
:limit="1"
:on-exceed="handleExceed"
:before-upload="beforeAvatarUpload"
>
<el-button type="primary"><el-icon><Upload /></el-icon></el-button>
<!-- <template #tip>-->
<!-- <div class="el-upload__tip">-->
<!-- xls 文件.-->
<!-- </div>-->
<!-- </template>-->
</el-upload>
<!-- Device Code Section -->
<div class="input-section">
<div class="label">设备序列号</div>
<el-input
ref="snInputRef"
v-model="deviceAttrList[1].inputValue"
placeholder="请输入或扫描设备序列号"
size="large"
clearable
class="device-code-input"
@focus="handleSnInputFocus"
@blur="handleSnInputBlur"
/>
</div>
<!-- Main and Sub Model Section -->
<div class="model-section">
<div class="model-inputs">
<div class="model-input-group">
<div class="label">主型号</div>
<el-input v-model="deviceAttrList[2].inputValue" :disabled="inputStatus" size="large" placeholder="请输入主型号" @input="saveCache($event,2)" />
</div>
<div class="model-input-group">
<div class="label">次型号</div>
<el-input v-model="deviceAttrList[3].inputValue" :disabled="inputStatus" size="large" placeholder="请输入次型号" @input="saveCache($event,3)" />
</div>
<el-button size="large" type="primary" @click="toggleInput">{{inputStatus?'解锁':'锁定'}}</el-button>
</div>
</div>
<!-- Information Display Section -->
<div class="info-section">
<span class="info-time">{{infoTime ? '时间:' + $dayjs(infoTime).format('YYYY-MM-DD HH:mm:ss') : ''}}</span>
<el-tag type="info" class="status-tag" v-if="deviceAttrList[1].outputValue==''">-</el-tag>
<el-tag type="success" class="status-tag" v-else-if="getContrastStatus()">比对成功</el-tag>
<el-tag type="danger" class="status-tag" v-else>比对失败</el-tag>
<div class="info-card">
<div class="info-title">设备序列号</div>
<div class="info-content">{{ deviceAttrList[1].outputValue || '-' }}</div>
</div>
<div class="info-card">
<div class="info-title"></div>
<div class="info-content"></div>
</div>
<div class="info-card">
<div class="info-title">主型号</div>
<div class="info-content">{{ deviceAttrList[2].outputValue || '-' }}</div>
</div>
<div class="info-card">
<div class="info-title">次型号</div>
<div class="info-content">{{ deviceAttrList[3].outputValue || '-' }}</div>
</div>
</div>
</div>
<!-- <div id="serialport-box">-->
<!-- <div id="serialport-main" class="serialport-main">-->
<!-- <el-collapse v-model="activeFold" @change="foldChange">-->
<!-- <el-collapse-item name="3" title="设备参数">-->
<!-- <div class="device-attr-list">-->
<!-- <div v-for="(item, index) in deviceAttrList" :key="item.id" class="device-attr-item">-->
<!-- <div class="device-attr-item-name">{{ item.name }}</div>-->
<!-- <div class="device-attr-item-value">-->
<!-- <el-input v-model="item.outputValue" class="device-attr-input" disabled />-->
<!-- <el-button type="primary" :loading="item.outputLoadingStatus" @click="sendDeviceAttr(item, 'output', index)">读取</el-button>-->
<!-- </div>-->
<!-- <div class="device-attr-item-value">-->
<!-- <el-input v-model="item.inputValue"-->
<!-- v-if="item.name === '设备序列号SN'"-->
<!-- ref="snInputRef"-->
<!-- class="device-attr-input"-->
<!-- :disabled="item.inputDisable"-->
<!-- @focus="handleSnInputFocus"-->
<!-- @blur="handleSnInputBlur"-->
<!-- @keyup.enter="handleSnEnter(item, index)"-->
<!-- placeholder="扫码输入"/>-->
<!-- <el-input v-model="item.inputValue" :disabled="item.inputDisable" class="device-attr-input" v-else/>-->
<!-- <el-button type="primary" :disabled="item.inputDisable" :loading="item.inputLoadingStatus" @click="sendDeviceAttr(item, 'input', index)">写入</el-button>-->
<!-- </div>-->
<!-- </div>-->
<!-- <div class="btn-box">-->
<!-- <el-button type="primary" @click="allReadDeviceAttr">一键读取</el-button>-->
<!-- <el-button type="primary" @click="allWriteDeviceAttr">一键写入</el-button>-->
<!-- </div>-->
<!-- </div>-->
<!-- </el-collapse-item>-->
<!-- </el-collapse>-->
<!-- </div>-->
<!-- </div>-->
</template>
<script setup>
import axios from 'axios'; // axios
import config from '@renderer/util/config.js';
import { reactive, ref, onMounted, onUnmounted, watch, computed, nextTick, onDeactivated, onActivated } from 'vue';
import { ElMessage,ElMessageBox,genFileId } from 'element-plus';
import { buildHead, hexToString, hexToVersion, rearrangeHexStr, versionToHex } from '../serialport/js/fun';
import { useSerialPortStore } from '@renderer/stores/seralPort.js';
const useseralPortStore = useSerialPortStore();
const upload = ref()
//
const connectionState = ref(false);
const inputStatus = ref(false);
const snInputRef = ref(null); // SN
const serialportForm = computed(() => {
return useseralPortStore.serialport;
});
const infoTime = ref('');
//
const isScannerInput = ref(false);
const lastScanTime = ref(0);
const scanTimeout = ref(null);
const toggleInput = () => {
inputStatus.value = !inputStatus.value;
ElMessage.success(inputStatus.value?'已锁定':'已解锁');
}
const fileList = ref([])
const handleExceed = () => {
ElMessage.error(
`只能上传一个设备表、请先删除设备表再上传`
);
}
const beforeAvatarUpload = (rawFile) => {
console.log("rawFile",rawFile)
if (rawFile.type !== 'application/vnd.openxmlformats-officedocument.spreadsheetml.sheet' && rawFile.type !== 'application/vnd.ms-excel') {
ElMessage.error('请上传xls或者xlsx格式文件!')
fileList.value = [];
return false
}
return true
}
const beforeRemove = (uploadFile, uploadFiles) => {
return ElMessageBox.confirm(
`确认要删除 ${uploadFile.name} 吗?`, "告警", {
confirmButtonText: "确认",
cancelButtonText: "取消",
type: "warning"
}
).then(
() => {
fileList.value = [];
return true;
},
() => false
)
}
// SN
const handleSnInputFocus = () => {
isScannerInput.value = true;
};
// SN
const handleSnInputBlur = () => {
isScannerInput.value = false;
};
//
const generateDerivedSerialNumbers = serialNumber => {
if (!serialNumber || serialNumber.length === 0) return;
// Get the first digit and the rest of the string
const firstDigit = parseInt(serialNumber.charAt(0));
const restOfString = serialNumber.substring(1);
// Generate motherboard serial number (first digit + 1)
const motherboardFirstDigit = (firstDigit + 1) % 10; // Use modulo to handle 9 -> 0
const motherboardSN = motherboardFirstDigit + restOfString;
// Generate 69 serial number (first digit + 2)
const sn69FirstDigit = (firstDigit + 2) % 10; // Use modulo to handle 8 -> 0, 9 -> 1
const sn69 = sn69FirstDigit + restOfString;
// Find and update the motherboard serial number field
const motherboardIndex = deviceAttrList.value.findIndex(item => item.name === '主板序列号');
if (motherboardIndex !== -1) {
deviceAttrList.value[motherboardIndex].inputValue = motherboardSN;
}
// Find and update the 69 serial number field
const sn69Index = deviceAttrList.value.findIndex(item => item.name === '69序列号');
if (sn69Index !== -1) {
deviceAttrList.value[sn69Index].inputValue = sn69;
}
};
//
const handleSnEnter = (item, index) => {
console.log("item",item.inputValue)
if (item.inputValue && item.inputValue.trim() !== '') {
//
queryProductKey(item.inputValue);
// SN
// sendDeviceAttr(item, 'input', index);
}
};
const JudgeInput = () => {
if(deviceAttrList.value[2].inputValue === '' || deviceAttrList.value[3].inputValue === ''){
ElMessage.error('主型号和次型号不能为空');
}else if(deviceAttrList.value[1].inputValue === ''){
ElMessage.error('设备序列号不能为空');
}else if(deviceAttrList.value[4].inputValue === ''){
ElMessage.error('产品密钥不能为空');
}else if(deviceAttrList.value[5].inputValue === ''){
ElMessage.error('主板序列号不能为空');
}else if(deviceAttrList.value[6].inputValue === ''){
ElMessage.error('69序列号不能为空');
}else if (!serialportForm.value.port) {
ElMessage.error('请先打开串口');
}else{
allWriteDeviceAttr()
}
}
// API
const queryProductKey = async serialNumber => {
try {
//
const keyIndex = deviceAttrList.value.findIndex(item => item.name === '产品密钥');
if (keyIndex !== -1) {
deviceAttrList.value[keyIndex].inputLoadingStatus = true;
}
// API
const response = await axios.get(`${config.serialPortUrl}/importDeviceSearch`, {
params: { devKey: serialNumber }
});
if (response.data.code === 0 && response.data.data.Result) {
//
if (keyIndex !== -1) {
deviceAttrList.value[keyIndex].inputValue = response.data.data.Result.devSecret;
generateDerivedSerialNumbers(serialNumber);
JudgeInput()
// ElMessage.success('');
}
} else {
ElMessage.error('未找到对应密钥,请检查设备序列号和设备表内设备序列号是否正确');
}
} catch (error) {
console.error('查询密钥失败:', error);
ElMessage.error('查询密钥失败');
} finally {
//
const keyIndex = deviceAttrList.value.findIndex(item => item.name === '产品密钥');
if (keyIndex !== -1) {
deviceAttrList.value[keyIndex].inputLoadingStatus = false;
}
}
};
// SN
const focusSnInput = () => {
if (snInputRef.value) {
// SN
const snIndex = deviceAttrList.value.findIndex(item => item.name === '设备序列号SN');
if (snIndex !== -1) {
//
deviceAttrList.value[snIndex].inputValue = '';
//
nextTick(() => {
snInputRef.value.focus();
});
}
}
};
const deviceAttrCommonList = ref({
input: {
//
datalentype: '00',
//ID
remote_id: '0',
//ID
local_id: '0',
//
cmd: '0100',
//
devtype: '00',
//
datatype: '00'
},
output: {
//
datalentype: '00',
//ID
remote_id: '0',
//ID
local_id: '0',
//
cmd: '0011',
//
devtype: '00',
//
datatype: '00'
}
});
const deviceAttrList = ref([
{
id: 1,
name: '芯片ID',
startValue: 0,
lengthValue: 4,
inputValue: '',
inputLoadingStatus: false,
outputValue: '',
outputLoadingStatus: false,
dataType: 'hex',
inputDisable: false
},
{
id: 2,
name: '设备序列号SN',
startValue: 4,
lengthValue: 8,
inputValue: '',
inputLoadingStatus: false,
outputValue: '',
outputLoadingStatus: false,
dataType: 'str',
inputDisable: false
},
{
id: 3,
name: '产品主型号',
startValue: 12,
lengthValue: 4,
inputValue: '',
inputLoadingStatus: false,
outputValue: '',
outputLoadingStatus: false,
dataType: 'str',
inputDisable: false
},
{
id: 4,
name: '产品次型号',
startValue: 16,
lengthValue: 4,
inputValue: '',
inputLoadingStatus: false,
outputValue: '',
outputLoadingStatus: false,
dataType: 'str',
inputDisable: false
},
{
id: 5,
name: '产品密钥',
startValue: 20,
lengthValue: 8,
inputValue: '',
inputLoadingStatus: false,
outputValue: '',
outputLoadingStatus: false,
dataType: 'str',
inputDisable: false
},
{
id: 6,
name: '主板序列号',
startValue: 28,
lengthValue: 8,
inputValue: '',
inputLoadingStatus: false,
outputValue: '',
outputLoadingStatus: false,
dataType: 'str',
inputDisable: false
},
{
id: 7,
name: '69序列号',
startValue: 36,
lengthValue: 8,
inputValue: '',
inputLoadingStatus: false,
outputValue: '',
outputLoadingStatus: false,
dataType: 'str',
inputDisable: false
}
]);
const saveCache = (e,index) => {
console.log("e,index",e,index)
if(index === 2){
localStorage.setItem('mainModel', e);
}else if(index === 3){
localStorage.setItem('secondaryModel', e);
}
}
//
const analysisData = (hexData, data) => {
if (hexData.length < 28) {
console.log('返回hex小于28个字符串', hexData);
return '';
} else {
// let head = hexData.substring(0,8);
// let startAddr = hexData.substring(8,16);
// let len = hexData.substring(16,24);
let hexValue = hexData.substring(24, data.lengthValue * 4 * 2 + 24);
// let crc = hexData.substring(hexData.length - 4);
if (data.dataType === 'str') {
let str = hexToString(hexValue).trim();
return str;
} else if (data.dataType === 'hex') {
let str = rearrangeHexStr(hexValue);
return str;
}
// else if(data.dataType === 'firmware'){
// let str = hexToVersion(hexValue);
// return str;
// }
else {
return hexValue;
}
}
};
const sendDeviceAttr = (data, type, index) => {
if (!serialportForm.value.port) {
ElMessage.error('请先打开串口');
return '';
}
console.log('readDeviceAttr', data);
let head = buildHead(
deviceAttrCommonList.value[type].datalentype,
deviceAttrCommonList.value[type].remote_id,
deviceAttrCommonList.value[type].local_id,
deviceAttrCommonList.value[type].cmd,
deviceAttrCommonList.value[type].devtype,
deviceAttrCommonList.value[type].datatype,
data.startValue,
data.lengthValue,
data.inputValue,
data
);
if (type === 'input') {
deviceAttrList.value[index].inputLoadingStatus = true;
// head = head + 'd240';
} else {
deviceAttrList.value[index].outputLoadingStatus = true;
}
axios
.post(
config.serialPortUrl + '/serial/response',
{
port: serialportForm.value.port,
data: head,
hex: 1,
crc: 1,
flush: 1
},
{
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
}
}
)
.then(response => {
//
console.log('串口请求', response); //
if (response.data.code === 0) {
ElMessage.success('操作成功');
if (type === 'output') {
deviceAttrList.value[index].outputValue = analysisData(response.data.data.hex || '', data);
}
} else {
ElMessage.error(response.data.message);
}
if (type === 'input') {
deviceAttrList.value[index].inputLoadingStatus = false;
} else {
deviceAttrList.value[index].outputLoadingStatus = false;
}
})
.catch(error => {
console.log('error', error);
if (type === 'input') {
deviceAttrList.value[index].inputLoadingStatus = false;
} else {
deviceAttrList.value[index].outputLoadingStatus = false;
}
//
if (error.response.data.message) {
ElMessage.error(error.response.data.message);
} else {
ElMessage.error(error);
}
});
};
const allReadDeviceAttr = () => {
console.log('serialportForm', serialportForm.value);
if (!serialportForm.value.port) {
ElMessage.error('请先打开串口');
return '';
}
let head = buildHead(
deviceAttrCommonList.value['output'].datalentype,
deviceAttrCommonList.value['output'].remote_id,
deviceAttrCommonList.value['output'].local_id,
deviceAttrCommonList.value['output'].cmd,
deviceAttrCommonList.value['output'].devtype,
deviceAttrCommonList.value['output'].datatype,
0,
46,
''
);
axios
.post(
config.serialPortUrl + '/serial/response',
{
port: serialportForm.value.port,
data: head,
hex: 1,
crc: 1,
flush: 1
},
{
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
}
}
)
.then(response => {
//
console.log('串口请求', response); //
if (response.data.code === 0) {
if(response.data.data.hex!==''){
ElMessage.success('操作成功');
let hexValue = response.data.data.hex.substring(24, 392);
let startValue = 0;
deviceAttrList.value.forEach((item, index) => {
if (startValue === 0) {
startValue = item.startValue;
}
let value = hexValue.substring(startValue, item.lengthValue * 4 * 2 + startValue);
startValue = startValue + item.lengthValue * 4 * 2;
console.log('value', value);
infoTime.value = new Date().getTime();
if (item.dataType === 'str') {
deviceAttrList.value[index].outputValue = hexToString(value).trim();
// deviceAttrList.value[index].inputValue = hexToString(value).trim();
} else if (item.dataType === 'hex') {
deviceAttrList.value[index].outputValue = rearrangeHexStr(value);
// deviceAttrList.value[index].inputValue = rearrangeHexStr(value);
} else {
deviceAttrList.value[index].outputValue = value;
// deviceAttrList.value[index].inputValue = value;
}
});
}else{
ElMessage.error('获取数据为空');
}
// deviceAttrList.value[index].outputValue = analysisData(response.data.data.hex || '', data);
} else {
ElMessage.error(response.data.message);
}
})
.catch(error => {
console.log('error', error);
//
if (error.response.data.message) {
ElMessage.error(error.response.data.message);
} else {
ElMessage.error(error);
}
});
};
const allWriteDeviceAttr = () => {
if (!serialportForm.value.port) {
ElMessage.error('请先打开串口');
return '';
}
deviceAttrList.value.forEach((item, index) => {
setTimeout(() => {
if(index === 1 || index === 2 || index === 3 || index === 4 || index === 5 || index === 6 ){
sendDeviceAttr(item, 'input', index);
}
}, 50);
});
setTimeout(() => {
allReadDeviceAttr();
},1000)
};
const getKeyboardWriter = e => {
// SN
if (isScannerInput.value) {
const now = Date.now();
//
if (now - lastScanTime.value > 3000) {
// 500ms
const snIndex = deviceAttrList.value.findIndex(item => item.name === '设备序列号SN');
if (snIndex !== -1) {
deviceAttrList.value[snIndex].inputValue = '';
}
}
lastScanTime.value = now;
//
if (scanTimeout.value) {
clearTimeout(scanTimeout.value);
}
// 50ms
scanTimeout.value = setTimeout(() => {
//
if (e.key === 'Enter') {
const snIndex = deviceAttrList.value.findIndex(item => item.name === '设备序列号SN');
if (snIndex !== -1 && deviceAttrList.value[snIndex].inputValue) {
handleSnEnter(deviceAttrList.value[snIndex], snIndex);
}
}
}, 50);
}
};
const getContrastStatus = () => {
let flag = true;
if(deviceAttrList.value[1].outputValue && deviceAttrList.value[1].outputValue !== deviceAttrList.value[1].inputValue){
flag = false;
}
if(deviceAttrList.value[2].outputValue && deviceAttrList.value[2].outputValue !== deviceAttrList.value[2].inputValue){
flag = false;
}
if(deviceAttrList.value[3].outputValue && deviceAttrList.value[3].outputValue !== deviceAttrList.value[3].inputValue){
flag = false;
}
return flag;
}
//
onMounted(() => {
// getSerialPortList(false);
deviceAttrList.value[2].inputValue = localStorage.getItem('mainModel');
deviceAttrList.value[3].inputValue = localStorage.getItem('secondaryModel');
});
onActivated(() => {
console.log('页面重新打开(从缓存恢复)');
console.log("serialportForm",serialportForm.value)
allReadDeviceAttr();
//
// SN
nextTick(() => {
focusSnInput();
});
//
window.addEventListener('keydown', getKeyboardWriter);
});
//
onDeactivated(() => {
console.log('页面离开(进入缓存)');
//
window.removeEventListener('keydown', getKeyboardWriter);
//
if (scanTimeout.value) {
clearTimeout(scanTimeout.value);
}
});
</script>
<style lang="less" scoped>
.scanner-container {
padding: 20px;
height: calc(100% - 80px);
background: #f2f2f2;
border-radius: 10px;
font-family: Arial, sans-serif;
}
.import-btn {
margin-bottom: 20px;
}
.device-code-input {
margin-bottom: 20px;
}
//.input-section,
.model-section,
.info-section {
margin-bottom: 20px;
}
.label {
font-size: 14px;
margin-bottom: 8px;
font-weight: 500;
}
.model-inputs {
display: flex;
gap: 10px;
align-items: flex-end;
}
.model-input-group {
flex: 1;
}
.info-section {
display: grid;
grid-template-columns: repeat(auto-fill, minmax(280px, 1fr));
gap: 15px;
background-color: #f8f8f8;
padding: 15px;
position: relative;
}
.info-card {
padding: 15px;
}
.info-title {
margin-bottom: 10px;
display: flex;
justify-content: space-between;
align-items: center;
color: #666;
}
.info-time{
position: absolute;
top: 5px;
left: 10px;
color: #999;
font-size: 12px;
}
.status-tag {
position: absolute;
top: 10px;
right: 10px;
font-size: 12px;
font-weight: normal;
}
.info-content {
}
</style>

File diff suppressed because it is too large Load Diff

View File

@ -1,956 +0,0 @@
<template>
<div id="serialport-box">
<div id="serialport-main" class="serialport-main">
<el-collapse v-model="activeFold" @change="foldChange">
<el-collapse-item name="1" title="串口设置">
<el-form ref="serialportFormRef" :model="serialportForm" :rules="serialportRules" class="demo-serialportForm" label-width="auto" status-icon>
<el-form-item label="端口" prop="port" required>
<el-select v-model="serialportForm.port" :disabled="connectionState" placeholder="选择端口" style="flex: 1">
<el-option v-for="(item, index) in serialportList" :key="index" :label="item" :value="item" />
</el-select>
<el-button style="margin-left: 10px" type="primary" @click="getSerialPortList">获取端口列表</el-button>
</el-form-item>
<el-form-item label="波特率" prop="baudrate" required>
<el-select v-model="serialportForm.baudrate" :disabled="connectionState" placeholder="选择波特率">
<el-option label="4800" :value="4800" />
<el-option label="9600" :value="9600" />
<el-option label="19200" :value="19200" />
<el-option label="43000" :value="43000" />
<el-option label="56000" :value="56000" />
<el-option label="115200" :value="115200" />
</el-select>
</el-form-item>
<el-form-item label="数据位" prop="databits" required>
<el-select v-model="serialportForm.databits" :disabled="connectionState" placeholder="选择数据位">
<el-option label="5" :value="5" />
<el-option label="6" :value="6" />
<el-option label="7" :value="7" />
<el-option label="8" :value="8" />
</el-select>
</el-form-item>
<el-form-item label="校验位" prop="parity" required>
<el-select v-model="serialportForm.parity" :disabled="connectionState" placeholder="选择校验位">
<el-option label="None" :value="0" />
<el-option label="Odd" :value="1" />
<el-option label="Even" :value="2" />
<el-option label="Mark" :value="3" />
<el-option label="Space" :value="4" />
</el-select>
</el-form-item>
<el-form-item label="停止位" prop="stopbits" required>
<el-select v-model="serialportForm.stopbits" :disabled="connectionState" placeholder="选择停止位">
<el-option label="1" :value="0" />
<el-option label="1.5" :value="1" />
<el-option label="2" :value="2" />
</el-select>
</el-form-item>
<el-form-item class="btn-box">
<el-button type="danger" @click="disconnSerialPort">关闭 </el-button>
<el-button type="primary" @click="connectSerialPort(serialportFormRef)">{{ connectionState ? '已打开' : '打开' }}</el-button>
</el-form-item>
</el-form>
</el-collapse-item>
<el-collapse-item name="2" title="通用调试">
<el-form ref="commonDebuggerFormRef" :model="commonDebuggerForm" :rules="commonDebuggerRules" class="demo-serialportForm" label-width="auto" status-icon>
<el-row>
<el-col :span="12">
<el-form-item label="本地ID" prop="localId">
<el-input v-model="commonDebuggerForm.localId" placeholder="请输入本地ID" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="目标ID" prop="remoteId">
<el-input v-model="commonDebuggerForm.remoteId" placeholder="请输入目标ID" />
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="12">
<el-form-item label="设备类型" prop="devType" required>
<el-select v-model="commonDebuggerForm.devType" placeholder="选择设备类型" style="flex: 1">
<el-option v-for="(item, index) in devTypeList" :key="index" :label="item.label" :value="item.value" />
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="数据类型" prop="dataType" required>
<el-select v-model="commonDebuggerForm.dataType" placeholder="选择数据类型" style="flex: 1">
<el-option v-for="(item, index) in dataTypeList" :key="index" :label="item.label" :value="item.value" />
</el-select>
</el-form-item>
</el-col>
</el-row>
<el-row>
<el-col :span="12">
<el-form-item label="指令码" prop="cmd" required>
<el-select v-model="commonDebuggerForm.cmd" placeholder="选择指令码" style="flex: 1">
<el-option v-for="(item, index) in cmdList" :key="index" :label="item.label" :value="item.value" />
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="数据长度类型" prop="dataLenType" required>
<el-select v-model="commonDebuggerForm.dataLenType" placeholder="选择数据长度类型" style="flex: 1">
<el-option v-for="(item, index) in dataLenTypeList" :key="index" :label="item.label" :value="item.value" />
</el-select>
</el-form-item>
</el-col>
</el-row>
<el-row v-if="commonDebuggerForm.cmd === '0100' || commonDebuggerForm.cmd === '0011'">
<el-col :span="12">
<el-form-item label="起始地址" prop="writeStart" required>
<el-input v-model="commonDebuggerForm.writeStart" type="number" placeholder="请输入起始地址" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="长度" prop="writeLength" required>
<el-input v-model="commonDebuggerForm.writeLength" type="number" placeholder="请输入长度" />
</el-form-item>
</el-col>
</el-row>
<el-form-item v-if="commonDebuggerForm.cmd === '0100'" label="数据" prop="writeData">
<el-input v-model="commonDebuggerForm.writeData" type="textarea" :rows="2" placeholder="请输入数据" />
</el-form-item>
<el-form-item label="发送包" prop="dataStr">
<el-input v-model="commonDebuggerForm.dataStr" disabled />
</el-form-item>
<el-form-item label="返回包" prop="hexStr">
<el-input v-model="commonDebuggerForm.hexStr" disabled />
</el-form-item>
<el-form-item class="btn-box">
<el-button type="primary" :loading="sendCommonDebuggerState" @click="sendCommonDebugger(commonDebuggerFormRef)">发送</el-button>
</el-form-item>
</el-form>
</el-collapse-item>
<el-collapse-item name="3" title="设备基础参数">
<div class="device-attr-list">
<div v-for="(item, index) in deviceAttrList" :key="item.id" class="device-attr-item">
<div class="device-attr-item-name">{{ item.name }}</div>
<div class="device-attr-item-value">
<el-input v-model="item.outputValue" class="device-attr-input" disabled />
<el-button type="primary" :loading="item.outputLoadingStatus" @click="sendDeviceAttr(item, 'output', index)">读取</el-button>
</div>
<div class="device-attr-item-value">
<el-input v-model="item.inputValue" :disabled="item.inputDisable" class="device-attr-input" />
<el-button type="primary" :disabled="item.inputDisable" :loading="item.inputLoadingStatus" @click="sendDeviceAttr(item, 'input', index)">写入</el-button>
</div>
</div>
<div class="btn-box">
<el-button type="primary" @click="allReadDeviceAttr">一键读取</el-button>
<el-button type="primary" @click="allWriteDeviceAttr">一键写入</el-button>
</div>
</div>
</el-collapse-item>
</el-collapse>
</div>
</div>
</template>
<script setup>
import axios from 'axios'; // axios
import config from '@renderer/util/config.js';
import { reactive, ref, onMounted, onUnmounted, watch } from 'vue';
import { ElMessage } from 'element-plus';
import { buildHead, hexToString, hexToVersion, rearrangeHexStr, versionToHex } from './js/fun';
import { useSerialPortStore } from '@renderer/stores/seralPort.js';
const useseralPortStore = useSerialPortStore();
const activeFold = ref(['1', '2', '3']);
//
const connectionState = ref(false);
const serialportFormRef = ref();
const serialportList = ref([]);
const serialportForm = reactive({
port: '',
baudrate: 115200,
databits: 8,
parity: 0,
stopbits: 0
});
// serialportForm
watch(
() => ({ ...serialportForm }),
newVal => {
// localStorage
newVal.port = newVal.port.toLowerCase();
useseralPortStore.setSerialport(newVal);
// localStorage.setItem('serialportForm', newVal);
},
{ deep: true }
);
const serialportRules = reactive({
port: [{ required: true, message: '请选择端口', trigger: 'change' }],
baudrate: [{ required: true, message: '请选择波特率', trigger: 'change' }],
databits: [{ required: true, message: '请选择port', trigger: 'change' }],
parity: [{ required: true, message: '请选择userName', trigger: 'change' }],
stopbits: [{ required: true, message: '请选择passWord', trigger: 'change' }]
});
//
const getSerialPortList = (msgShow = true) => {
axios
.get(config.serialPortUrl + '/serial/list', {})
.then(response => {
//
if (response.data.code === 0) {
serialportList.value = response.data.data.List || [];
if (msgShow) {
ElMessage.success('获取端口列表成功');
}
} else {
ElMessage.error(response.data.message);
}
})
.catch(error => {
//
ElMessage.error(error);
});
};
//
const connectSerialPort = formEl => {
if (!formEl) return;
formEl.validate(valid => {
if (valid) {
serialportForm.port = serialportForm.port.toLowerCase();
axios
.post(config.serialPortUrl + '/serial/open', serialportForm)
.then(response => {
//
console.log(response); //
if (response.data.code === 0) {
ElMessage.success('打开成功');
connectionState.value = true;
} else {
if (response.data.code === 60) {
connectionState.value = false;
}
ElMessage.error(response.data.message);
}
})
.catch(error => {
//
if (error.response.data.message) {
ElMessage.error(error.response.data.message);
} else {
ElMessage.error(error);
}
});
}
});
};
//
const disconnSerialPort = () => {
serialportForm.port = serialportForm.port.toLowerCase();
axios
.get(config.serialPortUrl + '/serial/close', {
params: {
port: serialportForm.port
}
})
.then(response => {
//
if (response.data.code === 0) {
connectionState.value = false;
ElMessage.success('关闭成功');
} else {
ElMessage.error(response.data.message);
}
})
.catch(error => {
//
ElMessage.error(error);
});
};
const deviceAttrCommonList = ref({
input: {
//
datalentype: '00',
//ID
remote_id: '0',
//ID
local_id: '0',
//
cmd: '0100',
//
devtype: '00',
//
datatype: '00'
},
output: {
//
datalentype: '00',
//ID
remote_id: '0',
//ID
local_id: '0',
//
cmd: '0011',
//
devtype: '00',
//
datatype: '00'
}
});
const deviceAttrList = ref([
{
id: 1,
name: '芯片ID',
startValue: 0,
lengthValue: 4,
inputValue: '',
inputLoadingStatus: false,
outputValue: '',
outputLoadingStatus: false,
dataType: 'hex',
inputDisable: false
},
{
id: 2,
name: '设备序列号SN',
startValue: 4,
lengthValue: 8,
inputValue: '',
inputLoadingStatus: false,
outputValue: '',
outputLoadingStatus: false,
dataType: 'str',
inputDisable: false
},
{
id: 3,
name: '产品主型号',
startValue: 12,
lengthValue: 4,
inputValue: '',
inputLoadingStatus: false,
outputValue: '',
outputLoadingStatus: false,
dataType: 'str',
inputDisable: false
},
{
id: 4,
name: '产品次型号',
startValue: 16,
lengthValue: 4,
inputValue: '',
inputLoadingStatus: false,
outputValue: '',
outputLoadingStatus: false,
dataType: 'str',
inputDisable: false
},
{
id: 5,
name: '产品密钥',
startValue: 20,
lengthValue: 8,
inputValue: '',
inputLoadingStatus: false,
outputValue: '',
outputLoadingStatus: false,
dataType: 'str',
inputDisable: false
},
{
id: 6,
name: '主板序列号',
startValue: 28,
lengthValue: 8,
inputValue: '',
inputLoadingStatus: false,
outputValue: '',
outputLoadingStatus: false,
dataType: 'str',
inputDisable: false
},
{
id: 7,
name: '69序列号',
startValue: 36,
lengthValue: 8,
inputValue: '',
inputLoadingStatus: false,
outputValue: '',
outputLoadingStatus: false,
dataType: 'str',
inputDisable: false
},
{
id: 8,
name: '通信版本号',
startValue: 44,
lengthValue: 1,
inputValue: '',
inputLoadingStatus: false,
outputValue: '',
outputLoadingStatus: false,
dataType: 'firmware',
inputDisable: true
},
{
id: 9,
name: '软件固件号',
startValue: 45,
lengthValue: 1,
inputValue: '',
inputLoadingStatus: false,
outputValue: '',
outputLoadingStatus: false,
dataType: 'firmware',
inputDisable: true
}
]);
//
const analysisData = (hexData, data) => {
if (hexData.length < 28) {
console.log('返回hex小于28个字符串', hexData);
return '';
} else {
// let head = hexData.substring(0,8);
// let startAddr = hexData.substring(8,16);
// let len = hexData.substring(16,24);
let hexValue = hexData.substring(24, data.lengthValue * 4 * 2 + 24);
// let crc = hexData.substring(hexData.length - 4);
if (data.dataType === 'str') {
let str = hexToString(hexValue).trim();
return str;
} else if (data.dataType === 'hex') {
let str = rearrangeHexStr(hexValue);
return str;
}
// else if(data.dataType === 'firmware'){
// let str = hexToVersion(hexValue);
// return str;
// }
else {
return hexValue;
}
}
};
const sendDeviceAttr = (data, type, index) => {
if (!serialportForm.port) {
ElMessage.error('请先打开串口');
return '';
}
console.log('readDeviceAttr', data);
let head = buildHead(
deviceAttrCommonList.value[type].datalentype,
deviceAttrCommonList.value[type].remote_id,
deviceAttrCommonList.value[type].local_id,
deviceAttrCommonList.value[type].cmd,
deviceAttrCommonList.value[type].devtype,
deviceAttrCommonList.value[type].datatype,
data.startValue,
data.lengthValue,
data.inputValue,
data
);
if (type === 'input') {
deviceAttrList.value[index].inputLoadingStatus = true;
// head = head + 'd240';
} else {
deviceAttrList.value[index].outputLoadingStatus = true;
}
axios
.post(
config.serialPortUrl + '/serial/response',
{
port: serialportForm.port,
data: head,
hex: 1,
crc: 1,
flush: 1
},
{
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
}
}
)
.then(response => {
//
console.log('串口请求', response); //
if (response.data.code === 0) {
ElMessage.success('操作成功');
if (type === 'output') {
deviceAttrList.value[index].outputValue = analysisData(response.data.data.hex || '', data);
}
} else {
ElMessage.error(response.data.message);
}
if (type === 'input') {
deviceAttrList.value[index].inputLoadingStatus = false;
} else {
deviceAttrList.value[index].outputLoadingStatus = false;
}
})
.catch(error => {
console.log('error', error);
if (type === 'input') {
deviceAttrList.value[index].inputLoadingStatus = false;
} else {
deviceAttrList.value[index].outputLoadingStatus = false;
}
//
if (error.response.data.message) {
ElMessage.error(error.response.data.message);
} else {
ElMessage.error(error);
}
});
};
const allReadDeviceAttr = () => {
if (!serialportForm.port) {
ElMessage.error('请先打开串口');
return '';
}
let head = buildHead(
deviceAttrCommonList.value['output'].datalentype,
deviceAttrCommonList.value['output'].remote_id,
deviceAttrCommonList.value['output'].local_id,
deviceAttrCommonList.value['output'].cmd,
deviceAttrCommonList.value['output'].devtype,
deviceAttrCommonList.value['output'].datatype,
0,
46,
''
);
axios
.post(
config.serialPortUrl + '/serial/response',
{
port: serialportForm.port,
data: head,
hex: 1,
crc: 1,
flush: 1
},
{
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
}
}
)
.then(response => {
//
console.log('串口请求', response); //
if (response.data.code === 0) {
if (response.data.data.hex !== '') {
ElMessage.success('操作成功');
let hexValue = response.data.data.hex.substring(24, 392);
let startValue = 0;
deviceAttrList.value.forEach((item, index) => {
if (startValue === 0) {
startValue = item.startValue;
}
let value = hexValue.substring(startValue, item.lengthValue * 4 * 2 + startValue);
startValue = startValue + item.lengthValue * 4 * 2;
console.log('value', value);
if (item.dataType === 'str') {
deviceAttrList.value[index].outputValue = hexToString(value).trim();
deviceAttrList.value[index].inputValue = hexToString(value).trim();
} else if (item.dataType === 'hex') {
deviceAttrList.value[index].outputValue = rearrangeHexStr(value);
deviceAttrList.value[index].inputValue = rearrangeHexStr(value);
} else {
deviceAttrList.value[index].outputValue = value;
deviceAttrList.value[index].inputValue = value;
}
});
} else {
ElMessage.error('获取数据为空');
}
// deviceAttrList.value[index].outputValue = analysisData(response.data.data.hex || '', data);
} else {
ElMessage.error(response.data.message);
}
})
.catch(error => {
console.log('error', error);
//
if (error.response.data.message) {
ElMessage.error(error.response.data.message);
} else {
ElMessage.error(error);
}
});
};
const allWriteDeviceAttr = () => {
if (!serialportForm.port) {
ElMessage.error('请先打开串口');
return '';
}
deviceAttrList.value.forEach((item, index) => {
setTimeout(() => {
sendDeviceAttr(item, 'input', index);
}, 50);
});
// let head = buildHead(
// deviceAttrCommonList.value['input'].datalentype,
// deviceAttrCommonList.value['input'].remote_id,
// deviceAttrCommonList.value['input'].local_id,
// deviceAttrCommonList.value['input'].cmd,
// deviceAttrCommonList.value['input'].devtype,
// deviceAttrCommonList.value['input'].datatype,
// 0,
// 46,
// ''
// );
//
// axios
// .post(
// config.serialPortUrl + '/serial/response',
// {
// port: serialportForm.port,
// data: head,
// hex: 1,
// crc: 1
// },
// {
// headers: {
// 'Content-Type': 'application/x-www-form-urlencoded'
// }
// }
// )
// .then(response => {
// //
// console.log('', response); //
// if (response.data.code === 0) {
// ElMessage.success('');
// let hexValue = response.data.data.hex.substring(24, 392);
// let startValue = 0;
// deviceAttrList.value.forEach((item, index) => {
// if(startValue === 0){
// startValue = item.startValue;
// }
// let value = hexValue.substring(startValue, item.lengthValue * 4 * 2 + startValue);
// startValue = startValue + item.lengthValue * 4 * 2;
// console.log("value",value)
// if (item.dataType === 'str') {
// deviceAttrList.value[index].outputValue = hexToString(value).trim();
// deviceAttrList.value[index].inputValue = hexToString(value).trim();
// }else{
// deviceAttrList.value[index].outputValue = value;
// deviceAttrList.value[index].inputValue = value;
// }
// })
// // deviceAttrList.value[index].outputValue = analysisData(response.data.data.hex || '', data);
// } else {
// ElMessage.error(response.data.message);
// }
// })
// .catch(error => {
// console.log('error', error);
// //
// if (error.response.data.message) {
// ElMessage.error(error.response.data.message);
// } else {
// ElMessage.error(error);
// }
// });
};
const commonDebuggerFormRef = ref();
const sendCommonDebuggerState = ref(false);
const commonDebuggerForm = ref({
localId: '',
remoteId: '',
devType: '00',
dataType: '00',
cmd: '0011',
dataLenType: '00',
writeStart: 0,
writeLength: 0,
writeData: '',
dataStr: '',
hexStr: ''
});
const devTypeList = ref([
{
label: '配套通信工具',
value: '00'
},
{
label: '网关',
value: '01'
},
{
label: '设备',
value: '10'
},
{
label: '其他',
value: '11'
}
]);
const dataTypeList = ref([
{
label: '紧急',
value: '00'
},
{
label: '应答',
value: '01'
},
{
label: '发送',
value: '10'
},
{
label: '广播',
value: '11'
}
]);
const cmdList = ref([
{
label: '特殊指令',
value: '0000'
},
{
label: '事件指令',
value: '0001'
},
{
label: '连接指令',
value: '0010'
},
{
label: '地址读指令',
value: '0011'
},
{
label: '地址写指令',
value: '0100'
},
{
label: '文件读指令',
value: '0101'
},
{
label: '文件写指令',
value: '0110'
},
{
label: '文件控制指令',
value: '0111'
},
{
label: 'OTA升级指令',
value: '1000'
},
{
label: '控制台通道',
value: '1001'
}
]);
const dataLenTypeList = ref([
{
label: '单帧数据',
value: '00'
},
{
label: '多帧数据起始帧',
value: '01'
},
{
label: '多帧数据中间帧',
value: '10'
},
{
label: '多帧数据结束帧',
value: '11'
}
]);
const commonDebuggerRules = reactive({
devType: [{ required: true, message: '请选择设备类型', trigger: 'change' }],
dataType: [{ required: true, message: '请选择数据类型', trigger: 'change' }],
cmd: [{ required: true, message: '请选择指令码', trigger: 'change' }],
dataLenType: [{ required: true, message: '请选择数据长度类型', trigger: 'change' }],
writeStart: [{ required: true, message: '请输入起始地址', trigger: 'blur' }],
writeLength: [{ required: true, message: '请输入长度', trigger: 'blur' }]
});
//
const sendCommonDebugger = formEl => {
if (!formEl) return;
formEl.validate(valid => {
if (valid) {
if (!serialportForm.port) {
ElMessage.error('请先打开串口');
return '';
}
let head = buildHead(
commonDebuggerForm.value.dataLenType,
commonDebuggerForm.value.remoteId,
commonDebuggerForm.value.localId,
commonDebuggerForm.value.cmd,
commonDebuggerForm.value.devType,
commonDebuggerForm.value.dataType,
commonDebuggerForm.value.writeStart,
commonDebuggerForm.value.writeLength,
commonDebuggerForm.value.writeData,
commonDebuggerForm.value
);
sendCommonDebuggerState.value = true;
axios
.post(
config.serialPortUrl + '/serial/response',
{
port: serialportForm.port,
data: head,
hex: 1,
crc: 1,
flush: 1
},
{
headers: {
'Content-Type': 'application/x-www-form-urlencoded'
}
}
)
.then(response => {
//
console.log('串口请求', response); //
sendCommonDebuggerState.value = false;
if (response.data.code === 0) {
ElMessage.success('请求成功');
commonDebuggerForm.value.dataStr = head;
commonDebuggerForm.value.hexStr = response.data.data.hex;
} else {
ElMessage.error(response.data.message);
}
})
.catch(error => {
sendCommonDebuggerState.value = false;
console.log('error', error);
//
if (error.response.data.message) {
ElMessage.error(error.response.data.message);
} else {
ElMessage.error(error);
}
});
}
});
};
//
onMounted(() => {
getSerialPortList(false);
});
onUnmounted(() => {});
</script>
<style lang="less" scoped>
/* 样式可以在这里定义 */
#serialport-box {
height: 100%;
position: relative;
padding-bottom: 5px;
.serialport-main {
height: calc(100% - 60px);
overflow-y: auto;
border-radius: 10px;
background: #f2f2f2;
&.open-log {
height: calc(50% - 42px);
}
}
.device-attr-list {
padding: 20px 10px;
.device-attr-item {
display: flex;
.device-attr-item-name {
width: 120px;
}
.device-attr-item-value {
flex: 1;
display: flex;
align-items: center;
justify-content: space-between;
padding: 5px;
.device-attr-input {
margin-right: 10px;
}
}
}
.btn-box {
display: flex;
justify-content: center;
padding: 20px;
}
}
:deep(.el-form) {
padding: 20px 20px 0;
}
:deep(.el-tabs--border-card > .el-tabs__content) {
padding: 0;
}
:deep(.el-collapse-item__header) {
//height: 42px;
//line-height: 42px;
}
:deep(.el-collapse-item__content) {
padding-bottom: 0;
}
:deep(.el-tabs__content) {
background: #f2f2f2;
}
:deep(.el-tabs__item) {
background: #e6e6e6;
&.is-active {
background: #f2f2f2;
}
}
:deep(.log-box-search .el-input__wrapper) {
background: #fff;
}
:deep(.log-box-search .el-select__wrapper) {
background: #fff;
}
.btn-box {
:deep(.el-form-item__content) {
justify-content: end;
width: 100%;
padding: 0;
}
}
}
</style>

View File

@ -1,149 +0,0 @@
<template>
<el-form ref="formRef" :model="form" class="demo-form" :rules="formRules" label-width="auto" status-icon>
<el-row>
<el-col :span="12">
<el-form-item label="设备号" prop="deviceSN" required>
<el-input v-model="form.deviceSN" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="设备类型" prop="deviceType" required>
<el-select v-model="form.deviceType" placeholder="选择设备类型">
<el-option v-for="(item, index) in deviceTypeList" :key="index" :label="item.label" :value="item.value" />
</el-select>
</el-form-item>
</el-col>
</el-row>
<el-form-item label="升级包地址" prop="url" required>
<el-input v-model="form.url" />
</el-form-item>
<el-row>
<el-col :span="12">
<el-form-item label="版本号" prop="codeVer" required>
<el-input v-model="form.codeVer" />
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="包大小(字节)" prop="size" required>
<el-input v-model="form.size" />
</el-form-item>
</el-col>
</el-row>
<el-form-item label="签名类型" prop="signType" required>
<el-select v-model="form.signType" placeholder="选择签名类型">
<el-option v-for="(item, index) in signTypeList" :key="index" :label="item.label" :value="item.value" />
</el-select>
</el-form-item>
<el-form-item label="签名" prop="sign" required>
<el-input v-model="form.sign" />
</el-form-item>
<el-row>
<el-col :span="12">
<el-form-item label="包加密类型" prop="scType" required>
<el-select v-model="form.scType" placeholder="选择包加密类型">
<el-option v-for="(item, index) in scTypeList" :key="index" :label="item.label" :value="item.value" />
</el-select>
</el-form-item>
</el-col>
<el-col :span="12">
<el-form-item label="设备型号" prop="productKey" required>
<el-input v-model="form.productKey" />
</el-form-item>
</el-col>
</el-row>
<el-form-item class="btn-box">
<el-button type="primary" @click="updateForm(formRef)">更新</el-button>
</el-form-item>
</el-form>
</template>
<script setup>
import { ref, reactive } from 'vue';
import { ElMessage } from 'element-plus';
import axios from 'axios';
import config from '@renderer/util/config.js';
const formRef = ref();
const form = reactive({
deviceSN: '',
deviceType: 'device',
url: '', //
codeVer: '', //
size: '', //1024", //10
signType: 'md5',
sign: '', //md5
scType: 'crc32', //
productKey: '' //
});
// const form = reactive({
// "deviceSN": "D000000000000001",
// "deviceType": "device",
// "url": "http://111.bin",
// "size": 1024,
// "codeVer": "1.1.1.1.1.1.1.1",
// "signType": "md5",
// "sign": "sa51ear16a1er",
// "scType": "crc32",
// "productKey": "B7L-NET-G1(4G)"
// });
const deviceTypeList = [
{ value: 'device', label: '设备' },
{ value: 'gateway', label: '网关' }
];
const signTypeList = [{ value: 'md5', label: 'md5' }];
const scTypeList = [{ value: 'crc32', label: 'crc32' }];
const formRules = reactive({
deviceSN: [{ required: true, message: '设备编号不能为空', trigger: 'blur' }],
deviceType: [{ required: true, message: '设备类型不能为空', trigger: 'blur' }],
url: [{ required: true, message: '升级包路径不能为空', trigger: 'blur' }],
codeVer: [{ required: true, message: '升级包版本号不能为空', trigger: 'blur' }],
size: [{ required: true, message: '升级包大小不能为空', trigger: 'blur' }],
signType: [{ required: true, message: '签名类型不能为空', trigger: 'blur' }],
sign: [{ required: true, message: '签名不能为空', trigger: 'blur' }],
scType: [{ required: true, message: '包加密类型不能为空', trigger: 'blur' }],
productKey: [{ required: true, message: '设备型号不能为空', trigger: 'blur' }]
});
const updateForm = formEl => {
if (!formEl) return;
formEl.validate(valid => {
if (valid) {
axios
.post(config.url + '/otaUpgrade',form)
.then(response => {
//
console.log(response); //
if (response.data.code == 0) {
//
ElMessage.success('固件更新成功');
} else {
if (response.data.code == 60) {
}
ElMessage.error(response.data.message);
}
})
.catch(error => {
//
console.error(error); //
ElMessage.error(error);
});
} else {
console.log('error submit!');
return false;
}
});
};
</script>
<style scoped lang="less">
.btn-box {
:deep(.el-form-item__content) {
justify-content: end;
width: 100%;
padding: 0;
}
}
</style>

File diff suppressed because it is too large Load Diff

View File

@ -1,62 +0,0 @@
<template>
<div id="video-player-box">
<easy-player id="EasyPlayer" ref="vVideoPlayerRef" live muted autoplay :has-audio="videoOption.hasAudio" :video-url="videoOption.videoUrl"></easy-player>
<div class="operation-box">
<div>
<el-checkbox v-model="videoOption.hasAudio" label="音频(音频有问题,请设置成false仅支持flv" size="large" />
</div>
<div>
<span>视频地址</span>
<el-input v-model="videoOption.videoUrl" style="width: 500px; margin-right: 10px" placeholder="请输入视频地址" />
<el-button type="primary" @click="initPlay">初始化</el-button>
<el-button v-if="videoOption.status" type="primary" @click="switchVideo">播放</el-button>
<el-button v-else @click="switchVideo">暂停</el-button>
</div>
</div>
</div>
</template>
<script setup>
import { ref } from 'vue';
const videoOption = ref({
//
videoUrl: '',
//
hasAudio: false,
status: true
});
const vVideoPlayerRef = ref(null);
const initPlay = () => {
let player = vVideoPlayerRef.value.getVueInstance();
player.destroyPlayer();
player.initPlayer();
};
const switchVideo = () => {
let player = vVideoPlayerRef.value.getVueInstance();
player.switchVideo();
videoOption.value.status = !player.pause;
};
</script>
<style lang="less" scoped>
#video-player-box {
height: 100%;
//position: relative;
//padding-bottom: 5px;
}
#EasyPlayer {
display: inline-block;
width: 100% !important;
height: 500px !important;
margin: 0 auto;
}
:deep(.easy-player-loading .logo) {
display: none;
}
:deep(.easy-player-right-menu) {
display: none;
}
</style>

View File

@ -1,13 +0,0 @@
<template>
<div>测试test</div>
</template>
<script>
export default {
name: 'test'
}
</script>
<style scoped>
</style>

859
yarn.lock

File diff suppressed because it is too large Load Diff