通讯设置

This commit is contained in:
jingna 2026-06-26 17:48:00 +08:00
parent 978d5c5378
commit 6937c5bfdd
5 changed files with 334 additions and 173 deletions

View File

@ -9,6 +9,7 @@ import type {
LineAlarmSettingPayload,
RealtimeData,
SystemConfigPayload,
FullDeviceConfig,
} from '../types/platform'
export async function fetchRealtimeData(): Promise<RealtimeData> {
@ -77,10 +78,20 @@ export async function saveDeviceNetConfig(payload: { nic: string; ip: string; ma
const response = await http.post<ApiResponse<{ send_status: string }>>('/config/device/net', payload)
return response.data
}
export async function saveDevicePortConfig(payload: { port: string; ip: string; mask: string; gateway: string; protocol: string }): Promise<ApiResponse<{ send_status: string }>> {
const response = await http.post<ApiResponse<{ send_status: string }>>('/config/device/uart', payload)
export async function saveDevicePortConfig(
payload: FullDeviceConfig
): Promise<ApiResponse<{ send_status: string }>> {
const response = await http.post<ApiResponse<{ send_status: string }>>(
'/config/device',
payload
)
return response.data
}
export async function getDeviceNetConfig(): Promise<DeviceConfigPayload[]> {
const response = await http.get<ApiResponse<DeviceConfigPayload[]>>(`/config/device`)
return response.data.data
}
export async function saveChannelConfig(
payload: ChannelConfigPayload,
): Promise<ApiResponse<{ save_path: string; send_status: string }>> {

View File

@ -1,25 +1,11 @@
<script setup lang="ts">
import { ref, onMounted, onUnmounted } from 'vue'
import { fetchDeviceStatus } from '@/api/platform'
import { connectTimeSync } from '@/websocket/client'
const time = ref('')
const date = ref('')
const day = ref('')
const weekDays = ['星期日', '星期一', '星期二', '星期三', '星期四', '星期五', '星期六']
const updateTime = () => {
const now = new Date()
const hours = now.getHours().toString().padStart(2, '0')
const minutes = now.getMinutes().toString().padStart(2, '0')
const seconds = now.getSeconds().toString().padStart(2, '0')
const year = now.getFullYear()
const month = (now.getMonth() + 1).toString().padStart(2, '0')
const dayOfMonth = now.getDate().toString().padStart(2, '0')
time.value = `${hours}:${minutes}:${seconds}`
date.value = `${year}-${month}-${dayOfMonth}`
day.value = weekDays[now.getDay()]
}
let timer: ReturnType<typeof setInterval>
const centerItems = ref([
{
label: '装置自检状态',
@ -47,6 +33,8 @@ const centerItems = ref([
code: 'serial2'
}
])
// WebSocket
let ws: WebSocket | null = null
async function refreshStatus() {
try {
const status:any = await fetchDeviceStatus()
@ -56,15 +44,69 @@ async function refreshStatus() {
} catch (error) {
}
}
//
const updateTimeFromServer = (serverTime: string) => {
const [dateStr, timeStr] = serverTime.split(' ')
const [year, month, dayOfMonth] = dateStr.split('-').map(Number)
const [hours, minutes, seconds] = timeStr.split(':').map(Number)
time.value = timeStr
date.value = dateStr
day.value = weekDays[new Date(year, month - 1, dayOfMonth).getDay()]
}
// /
let localTimer: ReturnType<typeof setInterval> | null = null
let lastServerTime: Date | null = null
const startLocalTimer = () => {
if (localTimer) clearInterval(localTimer)
localTimer = setInterval(() => {
if (lastServerTime) {
lastServerTime.setSeconds(lastServerTime.getSeconds() + 1)
const now = lastServerTime
const hours = now.getHours().toString().padStart(2, '0')
const minutes = now.getMinutes().toString().padStart(2, '0')
const seconds = now.getSeconds().toString().padStart(2, '0')
const year = now.getFullYear()
const month = (now.getMonth() + 1).toString().padStart(2, '0')
const dayOfMonth = now.getDate().toString().padStart(2, '0')
time.value = `${hours}:${minutes}:${seconds}`
date.value = `${year}-${month}-${dayOfMonth}`
day.value = weekDays[now.getDay()]
}
}, 1000)
}
onMounted(() => {
updateTime()
timer = setInterval(updateTime, 1000)
const now = new Date()
const hours = now.getHours().toString().padStart(2, '0')
const minutes = now.getMinutes().toString().padStart(2, '0')
const seconds = now.getSeconds().toString().padStart(2, '0')
const year = now.getFullYear()
const month = (now.getMonth() + 1).toString().padStart(2, '0')
const dayOfMonth = now.getDate().toString().padStart(2, '0')
time.value = `${hours}:${minutes}:${seconds}`
date.value = `${year}-${month}-${dayOfMonth}`
day.value = weekDays[now.getDay()]
// WebSocket
ws = connectTimeSync((event) => {
const serverTimeStr = event.data.time_sync
lastServerTime = new Date(serverTimeStr.replace(' ', 'T'))
updateTimeFromServer(serverTimeStr)
//
startLocalTimer()
})
refreshStatus()
})
onUnmounted(() => {
clearInterval(timer)
if (localTimer) clearInterval(localTimer)
if (ws) {
ws.close()
ws = null
}
})
</script>

View File

@ -113,7 +113,13 @@ export interface ChannelConfigPayload {
ai_channel: ChannelItem[]
ao_channel: ChannelItem[]
}
export type FullDeviceConfig = {
password: string
hardware_version: HardwareVersion
software_version: SoftwareVersion
net: [NetConfigItem, NetConfigItem]
uart: [UartConfigItem, UartConfigItem]
}
export interface AlarmRule {
category: string
limit: number

View File

@ -1,26 +1,46 @@
<script setup lang="ts">
import { ref, onMounted } from 'vue'
import { ElMessage } from 'element-plus'
import { fetchDeviceNet, fetchDevicePort, saveDeviceNetConfig, saveDevicePortConfig, verifyAccessPassword, fetchChannelConfig } from '@/api/platform'
import { fetchDeviceNet, fetchDevicePort, getDeviceNetConfig, saveDevicePortConfig, fetchChannelConfig } from '@/api/platform'
import NumericKeyboard from '@/components/NumericKeyboard.vue'
import PasswordDialog from '@/components/passwordValie.vue'
//
//
const formData: any = ref({
nic: '',
nic: '网卡一',
ip: '',
mask: '',
gateway: '',
protocol: '',
})
//
const formDataRight: any = ref({
nic: '网卡二',
ip: '',
mask: '',
gateway: '',
protocol: '',
})
// COM1
const formData2: any = ref({
port: '',
port: 'COM1',
baud: null,
parity: '',
data_bits: null,
stop_bits: null,
protocol: ''
})
// COM2
const formData2Right: any = ref({
port: 'COM2',
baud: null,
parity: '',
data_bits: null,
stop_bits: null,
protocol: ''
})
const netConfig: any = ref({})
const networkCardOptions: any = ref([
{ label: '网卡一', value: '网卡一' },
{ label: '网卡二', value: '网卡二' },
@ -72,120 +92,118 @@ const keyboardVisible = ref(false)
const passwordDialogVisible = ref(false)
const currentInputValue = ref('')
const currentInputKey = ref('')
const currentInputIndex = ref(0)
const currentInputType = ref('')
const infoList = ref<any>({})
const openKeyboard = (value: number, key: string, type: string) => {
currentInputType.value = type
currentInputValue.value = value.toString()
// /left / right
const currentInputSide = ref('left')
const openKeyboard = (value: string, key: string, side: 'left' | 'right') => {
currentInputSide.value = side
currentInputValue.value = value
currentInputKey.value = key
keyboardVisible.value = true
}
const handleKeyboardConfirm = (value: string) => {
if (currentInputSide.value === 'left') {
formData.value[currentInputKey.value] = value
} else {
formDataRight.value[currentInputKey.value] = value
}
keyboardVisible.value = false
}
//
//
const handleSave = () => {
passwordDialogVisible.value = true
}
//
const handlePasswordConfirm = () => {
saveDeviceNetConfig(formData.value).then((res: any) => {
if (isswitch.value) {
return
}
const handlePasswordConfirm = async () => {
passwordDialogVisible.value = false
if (isswitch.value) return
isswitch.value = true
if (res.code == 200) {
saveDevicePortConfig(formData2.value).then((res: any) => {
if (res.code == 200) {
try {
const submitData: any = {
hardware_version: netConfig.value.hardware_version,
software_version: netConfig.value.software_version,
password: netConfig.value.password,
net: [formData.value, formDataRight.value],
uart: [formData2.value, formData2Right.value]
}
const res = await saveDevicePortConfig(submitData)
if (res.code === 200) {
ElMessage({
type: 'success',
message: `保存成功`,
})
isswitch.value = false
} else {
ElMessage({
type: 'error',
message: '串口设置保存失败',
})
isswitch.value = false
}
message: '全部配置保存成功',
})
}else{
ElMessage({
type: 'error',
message: '常规配置保存失败',
message: '全部配置保存失败',
})
}
} catch (err: any) {
ElMessage({
type: 'error',
message: err.message || '保存失败',
})
} finally {
isswitch.value = false
}
})
}
//
const handlePasswordCancel = () => {
//
}
const config: any = ref({})
const handleChange = (val: any) => {
fetchDeviceNet(val).then((res: any) => {
formData.value = res
})
}
const handleChangePort = (val: any) => {
fetchDevicePort(val).then((res: any) => {
formData2.value = res
})
//
}
//
function init() {
fetchDeviceNet('网卡一').then((res: any) => {
formData.value = res
})
fetchDeviceNet('网卡二').then((res: any) => {
formDataRight.value = res
})
fetchDevicePort('COM1').then((res: any) => {
formData2.value = res
})
fetchChannelConfig().then((res: any) => {
config.value = res
fetchDevicePort('COM2').then((res: any) => {
formData2Right.value = res
})
getDeviceNetConfig().then((res: any) => {
netConfig.value = res
})
}
onMounted(() => {
init()
})
</script>
<template>
<div class="communication-container">
<el-form :model="formData" label-width="100px" class="form-wrapper">
<el-form label-width="100px" class="form-wrapper">
<!-- 常规配置左右两栏 网卡一 / 网卡二 -->
<div class="section">
<div class="section-title">
<div class="icon"></div>
<div>常规配置</div>
</div>
<div class="form-row">
<el-row :gutter="40">
<el-col :span="12">
<el-form-item label="网卡号">
<el-select v-model="formData.nic" placeholder="请选择" @change="handleChange">
<el-option v-for="item in networkCardOptions" :key="item.value" :label="item.label"
:value="item.label" />
</el-select>
<el-input v-model="formData.nic" disabled placeholder="请输入" />
</el-form-item>
<el-form-item label="IP地址">
<el-input v-model="formData.ip" @focus="openKeyboard(formData.ip, 'ip', 'value')"
style="width: 100%;" />
<el-input v-model="formData.ip" @focus="openKeyboard(formData.ip, 'ip', 'left')" />
</el-form-item>
</div>
<div class="form-row">
<el-form-item label="子网掩码">
<el-input v-model="formData.mask" @focus="openKeyboard(formData.mask, 'mask', 'value')" />
<el-input v-model="formData.mask" @focus="openKeyboard(formData.mask, 'mask', 'left')" />
</el-form-item>
<el-form-item label="默认网关">
<el-input v-model="formData.gateway"
@focus="openKeyboard(formData.gateway, 'gateway', 'value')" />
@focus="openKeyboard(formData.gateway, 'gateway', 'left')" />
</el-form-item>
</div>
<div class="form-row">
<el-form-item label="通讯协议">
<el-select v-model="formData.protocol" placeholder="请选择">
<el-option v-for="item in tcpProtocolOptions" :key="item.value" :label="item.label"
@ -193,19 +211,44 @@ onMounted(() => {
</el-select>
</el-form-item>
<el-form-item />
</div>
</el-col>
<el-col :span="12">
<el-form-item label="网卡号">
<el-input v-model="formDataRight.nic" disabled placeholder="请输入" />
</el-form-item>
<el-form-item label="IP地址">
<el-input v-model="formDataRight.ip"
@focus="openKeyboard(formDataRight.ip, 'ip', 'right')" />
</el-form-item>
<el-form-item label="子网掩码">
<el-input v-model="formDataRight.mask"
@focus="openKeyboard(formDataRight.mask, 'mask', 'right')" />
</el-form-item>
<el-form-item label="默认网关">
<el-input v-model="formDataRight.gateway"
@focus="openKeyboard(formDataRight.gateway, 'gateway', 'right')" />
</el-form-item>
<el-form-item label="通讯协议">
<el-select v-model="formDataRight.protocol" placeholder="请选择">
<el-option v-for="item in tcpProtocolOptions" :key="item.value" :label="item.label"
:value="item.label" />
</el-select>
</el-form-item>
<el-form-item />
</el-col>
</el-row>
</div>
<div class="section">
<div class="section-title">
<div class="icon"></div>
<div>串口设置</div>
</div>
<div class="form-row">
<el-row :gutter="40">
<el-col :span="12">
<el-form-item label="串口号">
<el-select v-model="formData2.port" placeholder="请选择" @change="handleChangePort">
<el-option v-for="item in comPortOptions" :key="item.label" :label="item.label"
:value="item.label" />
</el-select>
<el-input v-model="formData2.port" disabled placeholder="请输入" />
</el-form-item>
<el-form-item label="波特率">
<el-select v-model="formData2.baud" placeholder="请选择">
@ -213,8 +256,7 @@ onMounted(() => {
:value="item.value" />
</el-select>
</el-form-item>
</div>
<div class="form-row">
<el-form-item label="校验位">
<el-select v-model="formData2.parity" placeholder="请选择">
<el-option v-for="item in parityOptions" :key="item.value" :label="item.label"
@ -227,8 +269,6 @@ onMounted(() => {
:value="item.value" />
</el-select>
</el-form-item>
</div>
<div class="form-row">
<el-form-item label="停止位">
<el-select v-model="formData2.stop_bits" placeholder="请选择">
<el-option v-for="item in stopBitsOptions" :key="item.value" :label="item.label"
@ -241,29 +281,65 @@ onMounted(() => {
:value="item.value" />
</el-select>
</el-form-item>
</div>
</div>
</el-col>
<el-col :span="12">
<el-form-item label="串口号">
<el-input v-model="formData2Right.port" disabled placeholder="请输入" />
</el-form-item>
<el-form-item label="波特率">
<el-select v-model="formData2Right.baud" placeholder="请选择">
<el-option v-for="item in baudRateOptions" :key="item.value" :label="item.label"
:value="item.value" />
</el-select>
</el-form-item>
<el-form-item label="校验位">
<el-select v-model="formData2Right.parity" placeholder="请选择">
<el-option v-for="item in parityOptions" :key="item.value" :label="item.label"
:value="item.value" />
</el-select>
</el-form-item>
<el-form-item label="数据位">
<el-select v-model="formData2Right.data_bits" placeholder="请选择">
<el-option v-for="item in dataBitsOptions" :key="item.value" :label="item.label"
:value="item.value" />
</el-select>
</el-form-item>
<el-form-item label="停止位">
<el-select v-model="formData2Right.stop_bits" placeholder="请选择">
<el-option v-for="item in stopBitsOptions" :key="item.value" :label="item.label"
:value="item.value" />
</el-select>
</el-form-item>
<el-form-item label="通讯协议">
<el-select v-model="formData2Right.protocol" placeholder="请选择">
<el-option v-for="item in rtuProtocolOptions" :key="item.value" :label="item.label"
:value="item.value" />
</el-select>
</el-form-item>
</el-col>
</el-row>
</div>
</el-form>
<div class="btn-wrapper">
<el-button type="primary" style="background-color: #0099ff;width: 150px;height: 40px;"
@click="handleSave">保存</el-button>
</div>
<el-dialog v-model="dialogVisible" :close-on-click-modal="false" :show-close="false" title="错误提示" width="300">
<div style="color: #FF4D4F;font-size: 16px;text-align: center;">原密码验证错误</div>
<template #footer>
<el-button type="primary" @click="dialogVisible = false">确定</el-button>
</template>
</el-dialog>
<PasswordDialog
v-model:visible="passwordDialogVisible"
title="密码验证"
@confirm="handlePasswordConfirm"
@cancel="handlePasswordCancel"
/>
<PasswordDialog v-model:visible="passwordDialogVisible" title="密码验证" @confirm="handlePasswordConfirm"
@cancel="handlePasswordCancel" />
<NumericKeyboard v-model:visible="keyboardVisible" :model-value="currentInputValue"
@confirm="handleKeyboardConfirm">
</NumericKeyboard>
@confirm="handleKeyboardConfirm" />
</div>
</template>
@ -275,14 +351,13 @@ onMounted(() => {
.form-wrapper {
background: #fff;
padding: 15px 50px;
padding: 15px 20px;
border-radius: 4px;
box-shadow: 0px 0px 10px rgba(219, 225, 236, 1);
}
.section {
margin-bottom: 10px;
padding-bottom: 10px;
border-bottom: 1px solid #ebeef5;
&:last-child {
@ -294,7 +369,7 @@ onMounted(() => {
.section-title {
font-size: 16px;
font-weight: 500;
margin: 0 0 10px 0;
margin: 0 0 12px 0;
display: flex;
align-items: center;
font-weight: 700;
@ -311,9 +386,20 @@ onMounted(() => {
}
}
.form-row {
// / COM1/COM2
.sub-title {
font-size: 15px;
font-weight: 600;
color: #0099ff;
margin-bottom: 10px;
padding-left: 4px;
border-left: 3px solid #0099ff;
}
//
.form-col-row {
display: flex;
gap: 40px;
gap: 20px;
margin-bottom: 10px;
.el-form-item {
@ -325,14 +411,12 @@ onMounted(() => {
.btn-wrapper {
margin-top: 15px;
}
}
:deep(.el-form-item--label-right .el-form-item__label) {
text-align: left;
justify-content: flex-start;
color: #787878;
}
:deep(.el-select__placeholder) {
@ -341,15 +425,19 @@ onMounted(() => {
:deep(.el-input__inner) {
color: #363636;
min-height: 40px;
min-height: 28px;
}
:deep(.el-select__wrapper) {
min-height: 40px;
min-height: 28px;
}
:deep(.el-form-item__label) {
height: 40px;
line-height: 40px;
height: 28px;
line-height: 28px;
}
:deep(.el-form-item) {
margin-bottom: 3px;
}
</style>

View File

@ -1,11 +1,12 @@
<script setup lang="ts">
import { ref, onMounted } from 'vue'
import { ref, onMounted, onUnmounted } from 'vue'
import type { FormInstance, FormRules } from 'element-plus'
import { fetchSystemConfig, saveSystemConfig } from '@/api/platform'
import { ElMessage } from 'element-plus'
import { connectTimeSync } from '@/websocket/client'
const formRef = ref<FormInstance>()
const isUserEdited = ref(false)
const form = ref({
time_sync: '',
brightness: 0,
@ -38,6 +39,7 @@ const handleSubmit = async () => {
isswitch.value = false
})
}
let ws: WebSocket | null = null
function init() {
fetchSystemConfig().then((res: any) => {
form.value = res
@ -45,6 +47,17 @@ function init() {
}
onMounted(() => {
init()
ws = connectTimeSync((event) => {
if (!isUserEdited.value) {
form.value.time_sync = event.data.time_sync
}
})
})
onUnmounted(() => {
if (ws) {
ws.close()
ws = null
}
})
</script>
@ -60,7 +73,8 @@ onMounted(() => {
<el-form ref="formRef" :model="form" :rules="rules" label-width="90px" class="time-form">
<el-form-item label="选择时间" prop="time_sync">
<el-date-picker v-model="form.time_sync" type="datetime" placeholder="请选择时间"
format="YYYY-MM-DD HH:mm:ss" value-format="YYYY-MM-DD HH:mm:ss" style="width: 100%;" />
@focus="isUserEdited = true" format="YYYY-MM-DD HH:mm:ss" value-format="YYYY-MM-DD HH:mm:ss"
style="width: 100%;" />
</el-form-item>
</el-form>
</div>