微调
This commit is contained in:
9
frontend/src/api/system/sntp.ts
Normal file
9
frontend/src/api/system/sntp.ts
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
import http from '@/api'
|
||||||
|
|
||||||
|
export const startSntpService = () => {
|
||||||
|
return http.post('/sntp/start', {})
|
||||||
|
}
|
||||||
|
|
||||||
|
export const stopSntpService = () => {
|
||||||
|
return http.post('/sntp/stop', {})
|
||||||
|
}
|
||||||
@@ -18,7 +18,7 @@
|
|||||||
<template #operation='scope'>
|
<template #operation='scope'>
|
||||||
<el-button v-auth.role="'edit'" type='primary' link :icon='EditPen' @click="openDrawer('edit', scope.row)" :disabled="scope.row.code == 'root'">编辑</el-button>
|
<el-button v-auth.role="'edit'" type='primary' link :icon='EditPen' @click="openDrawer('edit', scope.row)" :disabled="scope.row.code == 'root'">编辑</el-button>
|
||||||
<el-button v-auth.role="'delete'" type='primary' link :icon='Delete' @click='deleteAccount(scope.row)' :disabled="scope.row.code == 'root'">删除</el-button>
|
<el-button v-auth.role="'delete'" type='primary' link :icon='Delete' @click='deleteAccount(scope.row)' :disabled="scope.row.code == 'root'">删除</el-button>
|
||||||
<el-button v-auth.role="'SetPermissions'" type='primary' link :icon='Share' @click="openDrawer('设置权限', scope.row)" :disabled="scope.row.code == 'root'">设置权限</el-button>
|
<el-button v-auth.role="'SetPermissions'" type='primary' link :icon='Share' @click="openDrawer('设置权限', scope.row)" >设置权限</el-button>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
</ProTable>
|
</ProTable>
|
||||||
|
|||||||
325
frontend/src/views/machine/sntp/index.vue
Normal file
325
frontend/src/views/machine/sntp/index.vue
Normal file
@@ -0,0 +1,325 @@
|
|||||||
|
<template>
|
||||||
|
<div class="sntp-page">
|
||||||
|
<section class="sntp-panel">
|
||||||
|
<el-row :gutter="16" class="time-list">
|
||||||
|
<el-col :xs="24" :md="12">
|
||||||
|
<div class="time-item">
|
||||||
|
<div class="time-label">当前电脑时间</div>
|
||||||
|
<div class="time-content">{{ computerTime }}</div>
|
||||||
|
</div>
|
||||||
|
</el-col>
|
||||||
|
<el-col :xs="24" :md="12">
|
||||||
|
<div class="time-item">
|
||||||
|
<div class="time-label">装置返回时间</div>
|
||||||
|
<div class="time-content">{{ deviceTime }}</div>
|
||||||
|
</div>
|
||||||
|
</el-col>
|
||||||
|
</el-row>
|
||||||
|
|
||||||
|
<div class="action-row">
|
||||||
|
<el-button type="primary" :loading="starting" :disabled="running || stopping" @click="handleStart">
|
||||||
|
启动SNTP对时服务
|
||||||
|
</el-button>
|
||||||
|
<el-button type="danger" plain :loading="stopping" :disabled="!running || starting" @click="handleStop">
|
||||||
|
停止SNTP对时服务
|
||||||
|
</el-button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div class="history-section">
|
||||||
|
<div class="history-header">
|
||||||
|
<span class="history-title">历史记录</span>
|
||||||
|
<el-button link :disabled="historyList.length === 0" @click="clearHistory">清空</el-button>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-if="historyList.length === 0" class="history-empty">
|
||||||
|
<span>暂无数据</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-else class="history-table">
|
||||||
|
<div class="history-table__head history-row">
|
||||||
|
<div class="col-order">序号</div>
|
||||||
|
<div>当前电脑时间</div>
|
||||||
|
<div>装置返回时间</div>
|
||||||
|
</div>
|
||||||
|
<div class="history-table__body">
|
||||||
|
<div v-for="(item, index) in historyList" :key="item.id" class="history-row">
|
||||||
|
<div class="col-order">{{ index + 1 }}</div>
|
||||||
|
<div>{{ item.computerTime }}</div>
|
||||||
|
<div>{{ item.deviceTime }}</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts" name="sntp">
|
||||||
|
import { computed, onBeforeUnmount, onMounted, ref } from 'vue'
|
||||||
|
import socketClient from '@/utils/webSocketClient'
|
||||||
|
import { startSntpService, stopSntpService } from '@/api/system/sntp'
|
||||||
|
|
||||||
|
interface SntpTimeMessage {
|
||||||
|
type: string
|
||||||
|
computerTime?: string
|
||||||
|
deviceTime?: string
|
||||||
|
}
|
||||||
|
|
||||||
|
interface SntpHistoryItem {
|
||||||
|
id: string
|
||||||
|
computerTime: string
|
||||||
|
deviceTime: string
|
||||||
|
}
|
||||||
|
|
||||||
|
defineOptions({
|
||||||
|
name: 'sntp'
|
||||||
|
})
|
||||||
|
|
||||||
|
const messageType = 'sntp_time_update'
|
||||||
|
const maxHistoryCount = 50
|
||||||
|
|
||||||
|
const running = ref(false)
|
||||||
|
const starting = ref(false)
|
||||||
|
const stopping = ref(false)
|
||||||
|
|
||||||
|
const computerTimeValue = ref('--')
|
||||||
|
const deviceTimeValue = ref('--')
|
||||||
|
const historyList = ref<SntpHistoryItem[]>([])
|
||||||
|
|
||||||
|
const computerTime = computed(() => computerTimeValue.value)
|
||||||
|
const deviceTime = computed(() => deviceTimeValue.value)
|
||||||
|
|
||||||
|
const appendHistory = (computerTimeText: string, deviceTimeText: string) => {
|
||||||
|
const nextItem: SntpHistoryItem = {
|
||||||
|
id: `${Date.now()}_${Math.random().toString(16).slice(2, 8)}`,
|
||||||
|
computerTime: computerTimeText,
|
||||||
|
deviceTime: deviceTimeText
|
||||||
|
}
|
||||||
|
historyList.value = [nextItem, ...historyList.value].slice(0, maxHistoryCount)
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleTimeUpdate = (message: SntpTimeMessage) => {
|
||||||
|
const nextComputerTime = message.computerTime || '--'
|
||||||
|
const nextDeviceTime = message.deviceTime || '--'
|
||||||
|
computerTimeValue.value = nextComputerTime
|
||||||
|
deviceTimeValue.value = nextDeviceTime
|
||||||
|
appendHistory(nextComputerTime, nextDeviceTime)
|
||||||
|
}
|
||||||
|
|
||||||
|
const ensureSocketConnection = () => {
|
||||||
|
socketClient.Instance.connect()
|
||||||
|
socketClient.Instance.registerCallBack(messageType, (message: SntpTimeMessage) => {
|
||||||
|
handleTimeUpdate(message)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const clearHistory = () => {
|
||||||
|
historyList.value = []
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleStart = async () => {
|
||||||
|
starting.value = true
|
||||||
|
try {
|
||||||
|
await startSntpService()
|
||||||
|
running.value = true
|
||||||
|
} finally {
|
||||||
|
starting.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleStop = async () => {
|
||||||
|
stopping.value = true
|
||||||
|
try {
|
||||||
|
await stopSntpService()
|
||||||
|
running.value = false
|
||||||
|
} finally {
|
||||||
|
stopping.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
onMounted(() => {
|
||||||
|
ensureSocketConnection()
|
||||||
|
})
|
||||||
|
|
||||||
|
onBeforeUnmount(() => {
|
||||||
|
socketClient.Instance.unRegisterCallBack(messageType)
|
||||||
|
})
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style scoped lang="scss">
|
||||||
|
.sntp-page {
|
||||||
|
height: 100%;
|
||||||
|
min-height: 100%;
|
||||||
|
padding: 16px;
|
||||||
|
background: #f5f7fa;
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sntp-panel {
|
||||||
|
flex: 1;
|
||||||
|
min-height: 0;
|
||||||
|
background: #ffffff;
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 20px;
|
||||||
|
box-shadow: 0 8px 24px rgba(15, 23, 42, 0.06);
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.time-list {
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.time-item {
|
||||||
|
min-height: 168px;
|
||||||
|
padding: 18px 20px;
|
||||||
|
border: 1px solid #e5e7eb;
|
||||||
|
border-radius: 8px;
|
||||||
|
background: #ffffff;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
|
||||||
|
.time-label {
|
||||||
|
font-size: 14px;
|
||||||
|
color: #606266;
|
||||||
|
}
|
||||||
|
|
||||||
|
.time-content {
|
||||||
|
font-size: 26px;
|
||||||
|
line-height: 1.35;
|
||||||
|
color: #303133;
|
||||||
|
word-break: break-word;
|
||||||
|
}
|
||||||
|
|
||||||
|
.action-row {
|
||||||
|
display: flex;
|
||||||
|
gap: 12px;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.history-section {
|
||||||
|
flex: 1;
|
||||||
|
min-height: 0;
|
||||||
|
border: 1px solid #e5e7eb;
|
||||||
|
border-radius: 8px;
|
||||||
|
overflow: hidden;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
background: #ffffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.history-header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
padding: 14px 16px;
|
||||||
|
border-bottom: 1px solid #e5e7eb;
|
||||||
|
background: #fafafa;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.history-title {
|
||||||
|
font-size: 14px;
|
||||||
|
color: #303133;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.history-empty {
|
||||||
|
flex: 1;
|
||||||
|
min-height: 0;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
color: #909399;
|
||||||
|
font-size: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.history-table {
|
||||||
|
flex: 1;
|
||||||
|
min-height: 0;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.history-table__head {
|
||||||
|
background: #fafafa;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.history-table__body {
|
||||||
|
flex: 1;
|
||||||
|
min-height: 0;
|
||||||
|
overflow: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.history-row {
|
||||||
|
display: grid;
|
||||||
|
grid-template-columns: 88px minmax(0, 1fr) minmax(0, 1fr);
|
||||||
|
}
|
||||||
|
|
||||||
|
.history-row > div {
|
||||||
|
padding: 14px 16px;
|
||||||
|
border-bottom: 1px solid #f0f2f5;
|
||||||
|
color: #303133;
|
||||||
|
word-break: break-word;
|
||||||
|
}
|
||||||
|
|
||||||
|
.history-row > div + div {
|
||||||
|
border-left: 1px solid #f0f2f5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.history-table__head > div {
|
||||||
|
font-size: 13px;
|
||||||
|
color: #606266;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.history-table__body .history-row:last-child > div {
|
||||||
|
border-bottom: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.col-order {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 900px) {
|
||||||
|
.sntp-page {
|
||||||
|
padding: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sntp-panel {
|
||||||
|
padding: 16px;
|
||||||
|
gap: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.time-item {
|
||||||
|
min-height: 132px;
|
||||||
|
margin-bottom: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.time-content {
|
||||||
|
font-size: 22px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.action-row {
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: stretch;
|
||||||
|
}
|
||||||
|
|
||||||
|
.history-row {
|
||||||
|
grid-template-columns: 1fr;
|
||||||
|
}
|
||||||
|
|
||||||
|
.history-row > div + div {
|
||||||
|
border-left: none;
|
||||||
|
border-top: 1px solid #f0f2f5;
|
||||||
|
}
|
||||||
|
|
||||||
|
.col-order {
|
||||||
|
text-align: left;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
Reference in New Issue
Block a user