仓库传感器详情页修改
首页修改
This commit is contained in:
17
src/api/worn/socket.js
Normal file
17
src/api/worn/socket.js
Normal file
@@ -0,0 +1,17 @@
|
|||||||
|
import request from '@/utils/request'
|
||||||
|
|
||||||
|
// 智能排风远程开关
|
||||||
|
export function controlSocket(data) {
|
||||||
|
const payload = {
|
||||||
|
devEui: data.devEui,
|
||||||
|
deviceId: data.deviceId,
|
||||||
|
status: data.status
|
||||||
|
}
|
||||||
|
|
||||||
|
return request({
|
||||||
|
url: '/worn/socket/control',
|
||||||
|
method: 'post',
|
||||||
|
params: payload,
|
||||||
|
data: payload
|
||||||
|
})
|
||||||
|
}
|
||||||
@@ -51,8 +51,25 @@
|
|||||||
<div class="donut-card">
|
<div class="donut-card">
|
||||||
<div class="donut">
|
<div class="donut">
|
||||||
<strong>{{ exceptionStats.total }}</strong>
|
<strong>{{ exceptionStats.total }}</strong>
|
||||||
|
<em class="donut-total-tag">累计告警数</em>
|
||||||
<span>累计告警</span>
|
<span>累计告警</span>
|
||||||
</div>
|
</div>
|
||||||
|
<div class="donut-info">
|
||||||
|
<div class="donut-legend">
|
||||||
|
<div class="legend-item danger">
|
||||||
|
<i></i>
|
||||||
|
<span>红色:告警</span>
|
||||||
|
</div>
|
||||||
|
<div class="legend-item warning">
|
||||||
|
<i></i>
|
||||||
|
<span>橙色:预警</span>
|
||||||
|
</div>
|
||||||
|
<div class="legend-item normal">
|
||||||
|
<i></i>
|
||||||
|
<span>绿色:正常</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -1032,13 +1049,36 @@ onBeforeUnmount(() => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
.donut-card {
|
.donut-card {
|
||||||
display: grid;
|
display: flex;
|
||||||
place-items: center;
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
min-height: 184px;
|
min-height: 184px;
|
||||||
|
padding: 20px 18px;
|
||||||
|
gap: 18px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.donut-head {
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 4px;
|
||||||
|
text-align: left;
|
||||||
|
|
||||||
|
strong {
|
||||||
|
font-size: 14px;
|
||||||
|
color: #0f172a;
|
||||||
|
}
|
||||||
|
|
||||||
|
span {
|
||||||
|
font-size: 12px;
|
||||||
|
color: #64748b;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.donut {
|
.donut {
|
||||||
|
position: relative;
|
||||||
display: grid;
|
display: grid;
|
||||||
|
flex: 0 0 110px;
|
||||||
width: 110px;
|
width: 110px;
|
||||||
height: 110px;
|
height: 110px;
|
||||||
place-items: center;
|
place-items: center;
|
||||||
@@ -1062,17 +1102,90 @@ onBeforeUnmount(() => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
strong {
|
strong {
|
||||||
font-size: 24px;
|
font-size: 28px;
|
||||||
|
font-weight: 900;
|
||||||
color: #0f172a;
|
color: #0f172a;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.donut-total-tag {
|
||||||
|
position: relative;
|
||||||
|
z-index: 1;
|
||||||
|
margin-top: 2px;
|
||||||
|
font-size: 10px;
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 700;
|
||||||
|
color: #94a3b8;
|
||||||
|
}
|
||||||
|
|
||||||
span {
|
span {
|
||||||
margin-top: 24px;
|
margin-top: 4px;
|
||||||
font-size: 11px;
|
font-size: 11px;
|
||||||
|
font-weight: 700;
|
||||||
color: #64748b;
|
color: #64748b;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.donut-info {
|
||||||
|
display: flex;
|
||||||
|
flex: 1;
|
||||||
|
flex-direction: column;
|
||||||
|
justify-content: center;
|
||||||
|
gap: 12px;
|
||||||
|
min-width: 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.donut-legend {
|
||||||
|
display: grid;
|
||||||
|
gap: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.legend-item {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
min-height: 34px;
|
||||||
|
padding: 8px 10px;
|
||||||
|
font-size: 12px;
|
||||||
|
color: #334155;
|
||||||
|
background: rgba(255, 255, 255, 0.72);
|
||||||
|
border: 1px solid rgba(226, 232, 240, 0.82);
|
||||||
|
border-radius: 12px;
|
||||||
|
box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.86);
|
||||||
|
|
||||||
|
i {
|
||||||
|
width: 10px;
|
||||||
|
height: 10px;
|
||||||
|
border-radius: 50%;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.danger i {
|
||||||
|
background: #ef4444;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.warning i {
|
||||||
|
background: #f59e0b;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.normal i {
|
||||||
|
background: #22c55e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@media (max-width: 1200px) {
|
||||||
|
.donut-card {
|
||||||
|
flex-direction: column;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.donut-head {
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.donut-info {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
.alarm-list {
|
.alarm-list {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
min-height: 118px;
|
min-height: 118px;
|
||||||
|
|||||||
@@ -57,6 +57,24 @@
|
|||||||
<p>{{ item.locationName }}</p>
|
<p>{{ item.locationName }}</p>
|
||||||
<h2>{{ item.typeName }}</h2>
|
<h2>{{ item.typeName }}</h2>
|
||||||
<strong>{{ item.summary }}</strong>
|
<strong>{{ item.summary }}</strong>
|
||||||
|
<div v-if="item.type === 'socket'" class="socket-actions">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="control-btn control-on"
|
||||||
|
:disabled="!!controlLoadingMap[item.cardKey]"
|
||||||
|
@click="handleSocketControl(item, 'on')"
|
||||||
|
>
|
||||||
|
{{ controlLoadingMap[item.cardKey] ? '发送中...' : '开启排风' }}
|
||||||
|
</button>
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="control-btn control-off"
|
||||||
|
:disabled="!!controlLoadingMap[item.cardKey]"
|
||||||
|
@click="handleSocketControl(item, 'off')"
|
||||||
|
>
|
||||||
|
{{ controlLoadingMap[item.cardKey] ? '发送中...' : '关闭排风' }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@@ -87,8 +105,11 @@
|
|||||||
<script setup name="WarehouseDashboard">
|
<script setup name="WarehouseDashboard">
|
||||||
import { computed, onBeforeUnmount, onMounted, reactive, ref } from 'vue'
|
import { computed, onBeforeUnmount, onMounted, reactive, ref } from 'vue'
|
||||||
import { useRoute, useRouter } from 'vue-router'
|
import { useRoute, useRouter } from 'vue-router'
|
||||||
|
import { ElMessage } from 'element-plus'
|
||||||
import { listMqttDevice } from '@/api/worn/mqttDevice'
|
import { listMqttDevice } from '@/api/worn/mqttDevice'
|
||||||
import { listMqttData } from '@/api/worn/mqttData'
|
import { listMqttData } from '@/api/worn/mqttData'
|
||||||
|
import { listMqttEvent } from '@/api/worn/mqttEvent'
|
||||||
|
import { controlSocket } from '@/api/worn/socket'
|
||||||
import { closeWs, connectWs } from '@/utils/ws'
|
import { closeWs, connectWs } from '@/utils/ws'
|
||||||
|
|
||||||
const route = useRoute()
|
const route = useRoute()
|
||||||
@@ -97,6 +118,7 @@ const router = useRouter()
|
|||||||
const loading = ref(false)
|
const loading = ref(false)
|
||||||
const devices = ref([])
|
const devices = ref([])
|
||||||
const latestDataMap = reactive({})
|
const latestDataMap = reactive({})
|
||||||
|
const controlLoadingMap = reactive({})
|
||||||
const ENV_CARD_TYPES = ['tempHum', 'toxicGas', 'hydrogenSulfide']
|
const ENV_CARD_TYPES = ['tempHum', 'toxicGas', 'hydrogenSulfide']
|
||||||
|
|
||||||
const deptId = computed(() => route.query.deptId)
|
const deptId = computed(() => route.query.deptId)
|
||||||
@@ -140,16 +162,42 @@ function loadLatestData() {
|
|||||||
return Promise.resolve()
|
return Promise.resolve()
|
||||||
}
|
}
|
||||||
|
|
||||||
return listMqttData({
|
const requestList = [
|
||||||
|
listMqttData({
|
||||||
pageNum: 1,
|
pageNum: 1,
|
||||||
pageSize: 1,
|
pageSize: 1,
|
||||||
deviceId: device.id
|
deviceId: device.id
|
||||||
}).then((res) => {
|
}).then((res) => {
|
||||||
const row = (res.rows || [])[0]
|
const row = (res.rows || [])[0]
|
||||||
if (row) {
|
if (row) {
|
||||||
latestDataMap[getDeviceKey(device)] = normalizePayload(row)
|
latestDataMap[getDeviceKey(device)] = normalizePayload({
|
||||||
|
...(latestDataMap[getDeviceKey(device)] || {}),
|
||||||
|
...row
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}).catch(() => {})
|
}).catch(() => {})
|
||||||
|
]
|
||||||
|
|
||||||
|
if (isStateDevice(device)) {
|
||||||
|
requestList.push(
|
||||||
|
listMqttEvent({
|
||||||
|
pageNum: 1,
|
||||||
|
pageSize: 1,
|
||||||
|
deviceId: device.id
|
||||||
|
}).then((res) => {
|
||||||
|
const row = (res.rows || [])[0]
|
||||||
|
if (row) {
|
||||||
|
latestDataMap[getDeviceKey(device)] = normalizePayload({
|
||||||
|
...(latestDataMap[getDeviceKey(device)] || {}),
|
||||||
|
...row,
|
||||||
|
...deriveStateFromEvent(row)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}).catch(() => {})
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
return Promise.all(requestList)
|
||||||
})
|
})
|
||||||
|
|
||||||
return Promise.all(tasks)
|
return Promise.all(tasks)
|
||||||
@@ -160,20 +208,80 @@ function handleWsMessage(data) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const key = getDeviceKey(data)
|
|
||||||
latestDataMap[key] = normalizePayload(data)
|
|
||||||
|
|
||||||
const index = devices.value.findIndex((device) => {
|
const index = devices.value.findIndex((device) => {
|
||||||
return String(device.id || '') === String(data.deviceId || '') ||
|
return String(device.id || '') === String(data.deviceId || '') ||
|
||||||
String(device.devEui || '').toLowerCase() === String(data.devEUI || data.devEui || '').toLowerCase()
|
String(device.devEui || '').toLowerCase() === String(data.devEUI || data.devEui || '').toLowerCase()
|
||||||
})
|
})
|
||||||
|
|
||||||
if (index > -1) {
|
if (index > -1) {
|
||||||
|
const device = devices.value[index]
|
||||||
|
const key = getDeviceKey(device)
|
||||||
|
const patchedData = patchRealtimeState(latestDataMap[key] || {}, data)
|
||||||
|
latestDataMap[key] = normalizePayload({
|
||||||
|
...(latestDataMap[key] || {}),
|
||||||
|
...patchedData
|
||||||
|
})
|
||||||
|
|
||||||
devices.value[index] = {
|
devices.value[index] = {
|
||||||
...devices.value[index],
|
...device,
|
||||||
status: '0',
|
status: '0',
|
||||||
deviceName: data.deviceName || devices.value[index].deviceName
|
deviceName: data.deviceName || device.deviceName
|
||||||
}
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const key = getDeviceKey(data)
|
||||||
|
const patchedData = patchRealtimeState(latestDataMap[key] || {}, data)
|
||||||
|
latestDataMap[key] = normalizePayload({
|
||||||
|
...(latestDataMap[key] || {}),
|
||||||
|
...patchedData
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function patchRealtimeState(previous, incoming) {
|
||||||
|
const next = { ...incoming }
|
||||||
|
const waterStatus = deriveWaterStatus({ ...previous, ...incoming })
|
||||||
|
if (waterStatus !== undefined) {
|
||||||
|
next.waterStatus = waterStatus
|
||||||
|
next.water_status = waterStatus
|
||||||
|
}
|
||||||
|
|
||||||
|
const doorStatus = deriveDoorStatus({ ...previous, ...incoming })
|
||||||
|
if (doorStatus !== undefined) {
|
||||||
|
next.doorStatus = doorStatus
|
||||||
|
next.door_status = doorStatus
|
||||||
|
}
|
||||||
|
|
||||||
|
const socketStatus = deriveSocketStatus({ ...previous, ...incoming })
|
||||||
|
if (socketStatus !== undefined) {
|
||||||
|
next.switchStatus = socketStatus
|
||||||
|
next.socket_status = socketStatus
|
||||||
|
next.socketStatus = socketStatus
|
||||||
|
}
|
||||||
|
|
||||||
|
return next
|
||||||
|
}
|
||||||
|
|
||||||
|
async function handleSocketControl(item, action) {
|
||||||
|
const deviceId = item.id || item.deviceId
|
||||||
|
const devEui = item.devEui || item.devEUI || item.dev_eui
|
||||||
|
if (!devEui) {
|
||||||
|
ElMessage.error('未找到设备 DevEUI,无法下发指令')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
const loadingKey = item.cardKey || String(deviceId)
|
||||||
|
if (controlLoadingMap[loadingKey]) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
controlLoadingMap[loadingKey] = true
|
||||||
|
try {
|
||||||
|
const status = action === 'on' ? 1 : 0
|
||||||
|
await controlSocket({ devEui, deviceId, status })
|
||||||
|
ElMessage.success(action === 'on' ? '开启排风指令已发送' : '关闭排风指令已发送')
|
||||||
|
} finally {
|
||||||
|
controlLoadingMap[loadingKey] = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -217,11 +325,27 @@ function getDeviceKey(device) {
|
|||||||
|
|
||||||
function normalizePayload(row) {
|
function normalizePayload(row) {
|
||||||
const json = parseJson(row.dataJson) || parseJson(row.payload) || {}
|
const json = parseJson(row.dataJson) || parseJson(row.payload) || {}
|
||||||
|
const nestedData = typeof row.data === 'object'
|
||||||
|
? row.data
|
||||||
|
: (typeof row.data === 'string' ? parseJson(row.data) : null) || {}
|
||||||
|
const inferredWaterStatus = deriveWaterStatus({
|
||||||
|
...json,
|
||||||
|
...nestedData,
|
||||||
|
...row
|
||||||
|
})
|
||||||
|
|
||||||
return {
|
return {
|
||||||
...json,
|
...json,
|
||||||
|
...nestedData,
|
||||||
...row,
|
...row,
|
||||||
devEUI: row.devEUI || row.devEui || json.devEUI || json.devEui,
|
devEUI: row.devEUI || row.devEui || json.devEUI || json.devEui,
|
||||||
deviceName: row.deviceName || json.deviceName
|
deviceName: row.deviceName || json.deviceName || nestedData.deviceName,
|
||||||
|
waterStatus: row.waterStatus ?? row.water_status ?? nestedData.waterStatus ?? nestedData.water_status ?? json.waterStatus ?? json.water_status ?? inferredWaterStatus,
|
||||||
|
water_status: row.water_status ?? row.waterStatus ?? nestedData.water_status ?? nestedData.waterStatus ?? json.water_status ?? json.waterStatus ?? inferredWaterStatus,
|
||||||
|
socket_status: row.socket_status ?? row.socketStatus ?? nestedData.socket_status ?? nestedData.socketStatus ?? json.socket_status ?? json.socketStatus,
|
||||||
|
switchStatus: row.switchStatus ?? row.socket_status ?? row.socketStatus ?? nestedData.switchStatus ?? nestedData.socket_status ?? nestedData.socketStatus ?? json.switchStatus ?? json.socket_status ?? json.socketStatus,
|
||||||
|
doorStatus: row.doorStatus ?? row.door_status ?? nestedData.doorStatus ?? nestedData.door_status ?? json.doorStatus ?? json.door_status,
|
||||||
|
tamperStatus: row.tamperStatus ?? row.tamper_status ?? nestedData.tamperStatus ?? nestedData.tamper_status ?? json.tamperStatus ?? json.tamper_status
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -237,6 +361,98 @@ function parseJson(value) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function deriveStateFromEvent(row) {
|
||||||
|
const eventType = String(row.eventType || row.event_type || row.event || '').trim().toLowerCase()
|
||||||
|
const eventDesc = String(row.eventDesc || row.event_desc || row.statusDesc || row.status_desc || '').trim().toLowerCase()
|
||||||
|
const normalized = {
|
||||||
|
eventType: row.eventType || row.event_type,
|
||||||
|
eventDesc: row.eventDesc || row.event_desc,
|
||||||
|
statusDesc: row.statusDesc || row.status_desc
|
||||||
|
}
|
||||||
|
|
||||||
|
if (eventType === 'door_open' || eventDesc.includes('门已打开')) {
|
||||||
|
normalized.doorStatus = 1
|
||||||
|
} else if (eventType === 'door_close' || eventDesc.includes('门已关闭')) {
|
||||||
|
normalized.doorStatus = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
if (eventType === 'socket_on' || eventDesc.includes('插座通电')) {
|
||||||
|
normalized.socket_status = 1
|
||||||
|
normalized.switchStatus = 1
|
||||||
|
} else if (eventType === 'socket_off' || eventDesc.includes('插座关闭') || eventDesc.includes('插座断电')) {
|
||||||
|
normalized.socket_status = 0
|
||||||
|
normalized.switchStatus = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
const inferredWaterStatus = deriveWaterStatus(row)
|
||||||
|
if (inferredWaterStatus !== undefined) {
|
||||||
|
normalized.waterStatus = inferredWaterStatus
|
||||||
|
normalized.water_status = inferredWaterStatus
|
||||||
|
}
|
||||||
|
|
||||||
|
return normalized
|
||||||
|
}
|
||||||
|
|
||||||
|
function deriveWaterStatus(data) {
|
||||||
|
const eventType = String(data.eventType || data.event_type || data.event || data.status || '').trim().toLowerCase()
|
||||||
|
const eventDesc = String(data.eventDesc || data.event_desc || data.statusDesc || data.status_desc || '').trim().toLowerCase()
|
||||||
|
|
||||||
|
if (eventType === 'alarm' || eventDesc.includes('浸水预警') || eventDesc.includes('水浸预警') || eventDesc.includes('浸水报警') || eventDesc.includes('水浸报警')) {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
if (eventType === 'recovery' || eventType === 'normal' || eventDesc.includes('水浸正常') || eventDesc.includes('浸水正常')) {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
const directValue = data.waterStatus ?? data.water_status ?? data.water
|
||||||
|
if (directValue !== undefined && directValue !== null && directValue !== '') {
|
||||||
|
return directValue
|
||||||
|
}
|
||||||
|
|
||||||
|
return undefined
|
||||||
|
}
|
||||||
|
|
||||||
|
function deriveDoorStatus(data) {
|
||||||
|
const directValue = data.doorStatus ?? data.door_status
|
||||||
|
if (directValue !== undefined && directValue !== null && directValue !== '') {
|
||||||
|
return directValue
|
||||||
|
}
|
||||||
|
|
||||||
|
const eventType = String(data.eventType || data.event_type || data.event || data.status || '').trim().toLowerCase()
|
||||||
|
const eventDesc = String(data.eventDesc || data.event_desc || data.statusDesc || data.status_desc || '').trim().toLowerCase()
|
||||||
|
|
||||||
|
if (eventType === 'door_open' || eventDesc.includes('门已打开')) {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
if (eventType === 'door_close' || eventDesc.includes('门已关闭')) {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
return undefined
|
||||||
|
}
|
||||||
|
|
||||||
|
function deriveSocketStatus(data) {
|
||||||
|
const directValue = data.switchStatus ?? data.socket_status ?? data.socketStatus
|
||||||
|
if (directValue !== undefined && directValue !== null && directValue !== '') {
|
||||||
|
return directValue
|
||||||
|
}
|
||||||
|
|
||||||
|
const eventType = String(data.eventType || data.event_type || data.event || data.status || '').trim().toLowerCase()
|
||||||
|
const eventDesc = String(data.eventDesc || data.event_desc || data.statusDesc || data.status_desc || '').trim().toLowerCase()
|
||||||
|
|
||||||
|
if (eventType === 'socket_on' || eventDesc.includes('插座通电')) {
|
||||||
|
return 1
|
||||||
|
}
|
||||||
|
|
||||||
|
if (eventType === 'socket_off' || eventDesc.includes('插座关闭') || eventDesc.includes('插座断电')) {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
|
||||||
|
return undefined
|
||||||
|
}
|
||||||
|
|
||||||
function getSensorType(device, data) {
|
function getSensorType(device, data) {
|
||||||
const raw = `${device.deviceType || ''} ${device.deviceName || ''} ${data.type || ''}`.toLowerCase()
|
const raw = `${device.deviceType || ''} ${device.deviceName || ''} ${data.type || ''}`.toLowerCase()
|
||||||
if (raw.includes('smoke')) return 'smoke'
|
if (raw.includes('smoke')) return 'smoke'
|
||||||
@@ -247,6 +463,11 @@ function getSensorType(device, data) {
|
|||||||
return 'sensor'
|
return 'sensor'
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function isStateDevice(device) {
|
||||||
|
const raw = `${device.deviceType || ''} ${device.deviceName || ''}`.toLowerCase()
|
||||||
|
return raw.includes('door') || raw.includes('socket')
|
||||||
|
}
|
||||||
|
|
||||||
function getSensorIcon(type) {
|
function getSensorIcon(type) {
|
||||||
const iconMap = {
|
const iconMap = {
|
||||||
smoke: '烟',
|
smoke: '烟',
|
||||||
@@ -295,11 +516,11 @@ function getSensorSummary(data, type) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (type === 'socket') {
|
if (type === 'socket') {
|
||||||
return `插座状态:${translateSwitch(data.switchStatus ?? data.socket_status)}`
|
return `插座状态:${translateSwitch(data.switchStatus ?? data.socket_status ?? data.socketStatus ?? data.status)}`
|
||||||
}
|
}
|
||||||
|
|
||||||
if (type === 'door') {
|
if (type === 'door') {
|
||||||
return `门禁状态:${translateSwitch(data.doorStatus)}`
|
return `门禁状态:${translateSwitch(data.doorStatus ?? data.door_status ?? data.status)}`
|
||||||
}
|
}
|
||||||
|
|
||||||
if (type === 'env') {
|
if (type === 'env') {
|
||||||
@@ -368,8 +589,12 @@ function buildMetrics(data, type) {
|
|||||||
metrics.unshift({ label: '水浸状态', value: translateNormalAlarm(data.waterStatus ?? data.water_status ?? data.water) })
|
metrics.unshift({ label: '水浸状态', value: translateNormalAlarm(data.waterStatus ?? data.water_status ?? data.water) })
|
||||||
}
|
}
|
||||||
|
|
||||||
if (type === 'socket' && (data.switchStatus !== undefined || data.socket_status !== undefined)) {
|
if (type === 'socket' && (data.switchStatus !== undefined || data.socket_status !== undefined || data.socketStatus !== undefined || data.status !== undefined)) {
|
||||||
metrics.unshift({ label: '插座状态', value: translateSwitch(data.switchStatus ?? data.socket_status) })
|
metrics.unshift({ label: '插座状态', value: translateSwitch(data.switchStatus ?? data.socket_status ?? data.socketStatus ?? data.status) })
|
||||||
|
}
|
||||||
|
|
||||||
|
if (type === 'door' && (data.doorStatus !== undefined || data.door_status !== undefined || data.status !== undefined)) {
|
||||||
|
metrics.unshift({ label: '门禁状态', value: translateSwitch(data.doorStatus ?? data.door_status ?? data.status) })
|
||||||
}
|
}
|
||||||
|
|
||||||
return metrics.slice(0, 6)
|
return metrics.slice(0, 6)
|
||||||
@@ -403,9 +628,9 @@ function translateSmokeStatus(value) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
function translateSwitch(value) {
|
function translateSwitch(value) {
|
||||||
const normalized = String(value).toLowerCase()
|
const normalized = String(value ?? '').trim().toLowerCase()
|
||||||
if (normalized === '0' || normalized === 'off' || normalized === 'false') return '关闭'
|
if (normalized === '0' || normalized === 'off' || normalized === 'false' || normalized === 'close' || normalized === 'closed') return '关闭'
|
||||||
if (normalized === '1' || normalized === 'on' || normalized === 'true') return '开启'
|
if (normalized === '1' || normalized === 'on' || normalized === 'true' || normalized === 'open' || normalized === 'opened') return '开启'
|
||||||
return value || '--'
|
return value || '--'
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -777,6 +1002,47 @@ onBeforeUnmount(() => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.socket-actions {
|
||||||
|
display: flex;
|
||||||
|
gap: 10px;
|
||||||
|
margin-top: 14px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.control-btn {
|
||||||
|
min-width: 94px;
|
||||||
|
padding: 9px 14px;
|
||||||
|
font-size: 13px;
|
||||||
|
font-weight: 800;
|
||||||
|
line-height: 1;
|
||||||
|
cursor: pointer;
|
||||||
|
border: 1px solid transparent;
|
||||||
|
border-radius: 999px;
|
||||||
|
transition: transform 0.18s ease, box-shadow 0.18s ease, opacity 0.18s ease;
|
||||||
|
|
||||||
|
&:hover:not(:disabled) {
|
||||||
|
transform: translateY(-1px);
|
||||||
|
}
|
||||||
|
|
||||||
|
&:disabled {
|
||||||
|
cursor: not-allowed;
|
||||||
|
opacity: 0.68;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.control-on {
|
||||||
|
color: #065f46;
|
||||||
|
background: linear-gradient(135deg, #dcfce7, #f0fdf4);
|
||||||
|
border-color: #86efac;
|
||||||
|
box-shadow: 0 10px 18px rgba(34, 197, 94, 0.12);
|
||||||
|
}
|
||||||
|
|
||||||
|
.control-off {
|
||||||
|
color: #9a3412;
|
||||||
|
background: linear-gradient(135deg, #ffedd5, #fff7ed);
|
||||||
|
border-color: #fdba74;
|
||||||
|
box-shadow: 0 10px 18px rgba(249, 115, 22, 0.12);
|
||||||
|
}
|
||||||
|
|
||||||
.metric-panel {
|
.metric-panel {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-direction: column;
|
flex-direction: column;
|
||||||
|
|||||||
Reference in New Issue
Block a user