1. 设备管理界面完成
2. 用户管理界面完成
3. 会话列表完成
This commit is contained in:
Zy
2026-09-30 17:43:01 +08:00
parent a19777a2b8
commit 23703a66c2
47 changed files with 7451 additions and 190 deletions

View File

@@ -0,0 +1,611 @@
<script lang="ts" setup>
import cardNav from '@/components/cardNav/index.vue';
import {getDevice} from "@/api/system/device";
import {DeviceForm} from "@/api/system/device/types";
const router = useRouter();
function backList() {
router.replace({
path: '/device/deviceList'
})
}
function editDevice() {
router.push({
path: '/device/deviceEdit'
})
}
const route = useRoute();
const initFormData: DeviceForm = {
deviceId: undefined,
deviceNo: undefined,
imei: undefined,
name: undefined,
productId: undefined,
model: undefined,
sn: undefined,
hwVersion: undefined,
swVersion: undefined,
fwVersion: undefined,
pkgVersion: undefined,
ip: undefined,
mac: undefined,
wifiSsid: undefined,
bluetoothConnected: undefined,
status: undefined,
bindUserId: undefined,
bindTime: undefined,
homeId: undefined,
locationId: undefined,
signalLevel: undefined,
lng: undefined,
lat: undefined,
address: undefined,
charging: undefined,
deviceBattery: undefined,
lastReportTime: undefined,
lastOnlineTime: undefined,
remark: undefined,
createDept: undefined,
createBy: undefined,
createTime: undefined,
updateBy: undefined,
updateTime: undefined,
activeDeviceNo: undefined,
activeImei: undefined,
activeMac: undefined,
}
const form = ref();
function init() {
form.value = {...initFormData};
console.log(form.value);
if (route.query.id) {
getDevice(route.query.id as string).then(res => {
Object.assign(form.value, res.data);
});
}
}
init()
</script>
<template>
<div class="Main">
<nav class="flex justify-between">
<Pagecrumb></Pagecrumb>
<div>
<el-button plain type="info" @click="backList">返回设备列表</el-button>
<el-button type="primary" @click="editDevice">编辑设备</el-button>
</div>
</nav>
<div class="infoBox">
<div class="info-item">
<el-image class="info_img"
src="https://ts2.tc.mm.bing.net/th/id/OIP-C.2KXRoOZ1EPBDMf6jrp6xeAHaE7?r=0&rs=1&pid=ImgDetMain&o=7&rm=3"></el-image>
<div class="info">
<div class="nav">
<div class="title">{{ form.name }}</div>
<div class="tag online">
<i class="circle"></i>
<span>在线</span>
</div>
</div>
<div class="info-list">
<div class="info-list-item">
<div class="title">设备ID</div>
<div class="value">{{ form.deviceid }}</div>
</div>
<div class="info-list-item">
<div class="title">MAC地址</div>
<div class="value">{{ form.mac }}</div>
</div>
<div class="info-list-item">
<div class="title">所在房间</div>
<div class="value">{{ form.locationName }}</div>
</div>
<div class="info-list-item">
<div class="title">产品型号</div>
<div class="value">{{ form.model }}</div>
</div>
</div>
</div>
</div>
<div class="timeBox">
<div class="title">最后上报时间</div>
<div class="time">{{ form.lastReportTime }}</div>
<div class="tip">
<i class="circle"></i>
<span>每30s自动刷新</span>
</div>
</div>
</div>
<div class="baseInfo">
<cardNav subTitle="静态档案信息,如需修改请前往「编辑设备」页" title="设备基础档案"></cardNav>
<div class=" gridBox">
<div class="grid-item">
<div class="title">设备ID</div>
<div class="value">{{ form.deviceId }}</div>
</div>
<div class="grid-item">
<div class="title">设备名称</div>
<div class="value">{{ form.name }}</div>
</div>
<div class="grid-item">
<div class="title">MAC地址</div>
<div class="value">{{ form.mac }}</div>
</div>
<div class="grid-item">
<div class="title">产品型号</div>
<div class="value">{{ form.model }}</div>
</div>
<div class="grid-item">
<div class="title">所在房间</div>
<div class="value">{{ form.homeName }}</div>
</div>
<div class="grid-item">
</div>
<div class="grid-item">
<div class="title">档案创建时间</div>
<div class="value">{{ form.createTime }}</div>
</div>
<div class="grid-item">
<div class="title">档案更新时间</div>
<div class="value">{{ form.updateTime }}</div>
</div>
</div>
</div>
<div class="readTimeBox">
<cardNav line-color="#14b8a6" subTitle="设备蓝牙上报 · 只读 · 每 30 秒自动刷新"
title="设备实时运行状态"></cardNav>
<div class="flexBox">
<div class="flex-item">
<div class="flex-item-nav">
<i class="circle"></i>
<span>环境数据</span>
</div>
<div class="numBox">
<div class="num">{{ form.tvoc }}</div>
<div class="tag">
<i class="circle"></i>
<span>在线</span>
</div>
</div>
<div class="subTitle">TVOC 传感器原始读数(未换算为浓度值)</div>
<div class="footer">
<span>数据更新时间</span>
<span style="color: rgba(17, 24, 39, 1);">2026-09-30 12:50:30</span>
</div>
</div>
<div class="flex-item flex flex-col">
<div class="flex-item-nav">
<i class="circle" style="background: #2563eb"></i>
<span>运行数据</span>
</div>
<div class="flex-1 flex flex-col justify-evenly">
<div class="footer" style="margin-top: 0">
<span>运行模式</span>
<div>
<span v-if="form.runMode == '2'" style="color: rgba(17, 24, 39, 1);">桌面模式</span>
<span v-else-if="form.runMode == '1'" style="color: rgba(17, 24, 39, 1);">车载模式</span>
<span v-else style="color: rgba(17, 24, 39, 1);">自动</span>
</div>
</div>
<div class="footer" style="margin-top: 0">
<span>风速挡位</span>
<div>
<span v-if="form.fanLevel == '1'" style="color: rgba(17, 24, 39, 1);">强力</span>
<span v-else-if="form.fanLevel == '2'" style="color: rgba(17, 24, 39, 1);">低档</span>
<span v-else style="color: rgba(17, 24, 39, 1);">标准</span>
</div>
</div>
<div class="footer" style="margin-top: 0">
<span>供电模式</span>
<div>
<span v-if="form.powerMode == '0'" style="color: rgba(17, 24, 39, 1);">USB供电</span>
<span v-else style="color: rgba(17, 24, 39, 1);">电池供电</span>
</div>
</div>
<div class="footer" style="margin-top: 0">
<span>停车关机模式</span>
<el-tag v-if="form.parkingShutdown == '1'" type="success">开启</el-tag>
<el-tag v-else type="danger">关闭</el-tag>
</div>
</div>
</div>
<div class="flex-item flex flex-col ">
<div class="flex-item-nav">
<i class="circle" style="background: #f59e0b"></i>
<span>耗材与电量</span>
</div>
<div class="flex-1 flex justify-evenly">
<div class="ageBox">
<div class="title">滤芯寿命</div>
<div class="processInfo">
<span class="num">76</span>
<span class="unit">%</span>
</div>
<el-progress :percentage="30" :show-text="false"></el-progress>
<div class="tip">
<span>预计剩余可用128天</span>
</div>
</div>
<div class="line"></div>
<div class="ageBox BatteryBox">
<div class="title">滤芯寿命</div>
<div class="processInfo">
<span class="num">76</span>
<span class="unit">%</span>
</div>
<el-progress :percentage="30" :show-text="false" color="#22C55E"></el-progress>
<el-tag type="success">充电中</el-tag>
</div>
</div>
</div>
<div class="flex-item flex flex-col ">
<div class="flex-item-nav">
<i class="circle" style="background: #6366f1"></i>
<span>策略说明 · 停车关机模式</span>
</div>
<ul style="margin-top: 16px;">
<li class="modelLi">
<el-tag type="success">开启</el-tag>
<span class="describe">USB 外部供电断开后,电池供电继续运行 5 分钟消杀再自动关机;低电量 SOC<10% 时关闭等离子消杀;SOC<5% 整机锁机(防止过放)。</span>
</li>
<li class="modelLi">
<el-tag type="info">关闭</el-tag>
<span class="describe">不执行该套低电量策略,仅保留基础净化逻辑。</span>
</li>
</ul>
</div>
</div>
</div>
</div>
</template>
<style lang="scss" scoped>
.Main {
height: 100%;
width: 100%;
overflow: auto;
.infoBox {
padding: 24px;
display: flex;
justify-content: space-between;
align-items: center;
max-height: 177px;
min-width: 750px;
border-radius: 16px 16px 16px 16px;
border: 1px solid rgba(238, 240, 243, 1);
margin-top: 8px;
.info-item {
display: flex;
align-items: center;
.info_img {
width: 97px;
height: 97px;
border-radius: 16px 16px 16px 16px;
}
.info {
display: flex;
flex-direction: column;
justify-content: space-between;
margin-left: 24px;
.nav {
display: flex;
align-items: end;
.title {
height: 28px;
line-height: 28px;
color: rgba(17, 24, 39, 1);
font-size: 22px;
text-align: left;
}
.tag {
margin-left: 5px;
width: 62px;
height: 28px;
border-radius: 14px;
&.online {
background-color: rgba(232, 247, 238, 1);
color: rgba(22, 163, 74, 1);
font-size: 12px;
display: flex;
align-items: center;
justify-content: center;
.circle {
margin-right: 6px;
display: block;
width: 7px;
height: 6px;
border-radius: 3px;
background-color: rgba(22, 163, 74, 1);
}
}
}
}
.info-list {
display: flex;
align-items: center;
gap: 20px;
margin-top: 28px;
.info-list-item {
.title {
height: 16px;
line-height: 16px;
color: rgba(156, 163, 175, 1);
font-size: 12px;
}
.value {
height: 40px;
line-height: 20px;
color: rgba(17, 24, 39, 1);
font-size: 14px;
}
}
}
}
}
.timeBox {
.title {
text-align: right;
line-height: 16px;
color: rgba(156, 163, 175, 1);
font-size: 12px;
}
.time {
line-height: 22px;
background-color: rgba(0, 0, 0, 0);
color: rgba(17, 24, 39, 1);
font-size: 16px;
margin-top: 6px;
margin-bottom: 12px;
}
.tip {
display: flex;
align-items: center;
justify-content: end;
span {
background-color: rgba(0, 0, 0, 0);
color: rgba(37, 99, 235, 1);
font-size: 12px;
text-align: left;
}
.circle {
margin-right: 6px;
display: block;
width: 7px;
height: 6px;
border-radius: 3px;
background-color: #2563eb;
}
}
}
}
.baseInfo {
padding: 25px;
min-height: 248px;
border-radius: 16px 16px 16px 16px;
border: 1px solid rgba(238, 240, 243, 1);
margin: 16px 0;
.gridBox {
margin-top: 44px;
display: grid;
grid-template-columns: repeat(4, minmax(381px, 1fr));
gap: 20px;
@media (max-width: 1659px) {
grid-template-columns: repeat(3, minmax(381px, 1fr));
}
@media (max-width: 1250px) {
grid-template-columns: repeat(2, minmax(381px, 1fr));
}
@media (max-width: 600px) {
grid-template-columns: repeat(1, minmax(381px, 1fr));
}
.grid-item {
max-width: 381px;
.title {
height: 16px;
line-height: 16px;
color: rgba(156, 163, 175, 1);
font-size: 12px;
margin-bottom: 6px;
}
.value {
height: 20px;
line-height: 20px;
color: rgba(17, 24, 39, 1);
font-size: 14px;
text-align: left;
}
}
}
}
.readTimeBox {
padding: 25px;
border-radius: 16px 16px 16px 16px;
border: 1px solid rgba(238, 240, 243, 1);
.flexBox {
margin-top: 44px;
gap: 20px;
display: grid;
grid-template-columns: repeat(2, minmax(381px, 1fr));
@media (max-width: 900px) {
grid-template-columns: repeat(1, minmax(381px, 1fr));
}
.flex-item {
min-height: 201px;
border-radius: 12px 12px 12px 12px;
background-color: rgba(247, 249, 252, 1);
border: 1px solid rgba(238, 240, 243, 1);
padding: 21px;
.flex-item-nav {
display: flex;
align-items: center;
.circle {
margin-right: 6px;
display: block;
width: 7px;
height: 6px;
border-radius: 3px;
background-color: rgba(22, 163, 74, 1);
}
span {
font-size: 12px;
display: flex;
align-items: center;
justify-content: center;
}
}
.numBox {
display: flex;
align-items: end;
margin-top: 17px;
.num {
line-height: 43px;
color: rgba(17, 24, 39, 1);
font-size: 36px;
text-align: left;
}
.tag {
margin-left: 5px;
width: 62px;
height: 28px;
border-radius: 14px;
$backColor: rgba(232, 247, 238, 1);
$textColor: rgba(22, 163, 74, 1);
background-color: $backColor;
color: $textColor;
font-size: 12px;
display: flex;
align-items: center;
justify-content: center;
.circle {
margin-right: 6px;
display: block;
width: 7px;
height: 6px;
border-radius: 3px;
background-color: $textColor;
}
}
}
.subTitle {
margin-top: 12px;
line-height: 16px;
color: rgba(156, 163, 175, 1);
font-size: 12px;
}
.footer {
margin-top: 44px;
display: flex;
align-items: center;
justify-content: space-between;
line-height: 14px;
color: rgba(107, 114, 128, 1);
font-size: 12px;
}
// 滤芯寿命
.ageBox {
flex: 1;
margin-top: 16px;
.title {
line-height: 18px;
color: rgba(107, 114, 128, 1);
font-size: 13px;
}
.processInfo {
margin: 12px 0;
.num {
line-height: 31px;
color: rgba(17, 24, 39, 1);
font-size: 26px;
}
.unit {
line-height: 17px;
color: rgba(107, 114, 128, 1);
font-size: 14px;
margin-left: 4px;
}
}
.tip {
line-height: 16px;
color: rgba(156, 163, 175, 1);
font-size: 12px;
}
}
.line {
width: 2px;
height: 96px;
background-color: rgba(238, 240, 243, 1);
margin: 15px 24px;
}
.modelLi {
margin-bottom: 12px;
span.describe {
margin-left: 12px;
line-height: 16px;
color: rgba(75, 85, 99, 1);
font-size: 13px;
text-align: left;
}
}
}
}
}
}
</style>