congming_huose-apk/common/components/DeviceTab.vue

838 lines
26 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<view class="device-tab">
<!-- 设备列表 -->
<scroll-view scroll-y @scrolltolower="handqixing" refresher-enabled @refresherrefresh="onRefresh" :refresher-triggered="isRefreshing" refresher-default-style="black" class="device-list">
<view
v-for="(device, index) in deviceList"
:key="index"
class="device-item"
@click="handleDeviceClick(device)">
<!-- 灰色圆角头像 -->
<view class="device-avatar" style="position: relative;">
<image :src="device.productPicture" mode="aspectFit"></image>
<!-- 红色角标 -->
<view class="device-badge" style="background-color: #4cd964;" v-if="device.iotOnlineData.onlineStatus == 1"></view>
<view class="device-badge" v-else></view>
</view>
<!-- 信息区 -->
<view class="device-info">
<text class="device-name">{{ device.productName }}(<text class="device-name" style="font-weight: 400;">{{ device.name}}</text>)</text>
<text class="device-room" v-if="device.roomName">{{ device.roomName || '--'}}</text>
<view class="device-status-bar">
<image
v-if="listRowShowLx(device)"
:src="staticStatusImg.lx"
class="device-sbar-img"
mode="aspectFit"
/>
<view v-else class="device-status-sbar-row">
<image
:src="staticStatusImg.xh"
class="device-sbar-img"
mode="aspectFit"
/>
<image
:src="staticStatusImg.dc"
class="device-sbar-img"
mode="aspectFit"
/>
<image
v-if="listRowShowDoorOpen(device)"
:src="staticStatusImg.door"
class="device-sbar-img"
mode="aspectFit"
/>
<image
v-if="listRowShowFc(device)"
:src="staticStatusImg.fc"
class="device-sbar-img"
mode="aspectFit"
/>
</view>
</view>
</view>
<!-- 右侧箭头 -->
<view class="device-arrow"></view>
</view>
<view v-if="deviceList.length == 0" style="margin: auto;margin-top: 50rpx;width: 272rpx;height: 272rpx;border-radius: 50%;border: 1px solid #ccc;">
<image style="width: 272rpx;height: 272rpx;" src="https://api.ccttiot.com/ad93702b02429b8adf394569460b8b2a-1758263546164.png" mode=""></image>
</view>
<view v-if="deviceList.length == 0" style="width: 600rpx;text-align: center;margin: auto;margin-top: 30rpx;">
{{$i18n.t('hubwz')}}
</view>
</scroll-view>
<!-- 添加设备按钮 -->
<view class="add-device-btn" @click="handleAddDevice">
<text class="add-icon">+</text>
<text>{{ $i18n.t('addDevice') }}</text>
</view>
</view>
</template>
<script>
import { mergeDeviceRoomOverlayIntoRows } from '@/common/utils/deviceRoomOverlay.js'
import { isFxCoverOpen } from '@/common/utils/iotFxCover.js'
import { connectIfLoggedIn, isOpen, sendJson } from '@/common/utils/appUserWs.js'
function normalizeMac(m) {
if (m === undefined || m === null) return ''
return String(m).replace(/:/g, '').trim().toUpperCase()
}
function mergeIotDataPoint(ov, nv) {
if (nv === null) return null
if (typeof nv !== 'object' || Array.isArray(nv)) return nv
if (!ov || typeof ov !== 'object' || Array.isArray(ov)) return { ...nv }
const merged = { ...ov, ...nv }
if (!Object.prototype.hasOwnProperty.call(nv, 'desc') || nv.desc === undefined) {
if (Object.prototype.hasOwnProperty.call(ov, 'desc') && ov.desc !== undefined) {
merged.desc = ov.desc
}
}
return merged
}
export default {
name: 'DeviceTab',
props: {
// 首页当前为设备 Tab 时批量订阅;从列表进详情不改变此值,不会整表退订
subscriptionActive: { type: Boolean, default: false },
},
data() {
return {
deviceList: [],
isRefreshing:false,
kjid:'', // 添加空间ID
pageNum:1,
pageSize:10,
total:0,
hasMore:true,
requestInProgress:false,
// 注意data 键勿用前导 _小程序端不会挂到 this会导致 undefined
// 当前已 subscribe 的 mac 列表(用于离页/换页时顺序退订)
listWsSubscribedMacs: [],
listWsBatchId: 0,
listWsAppendSeq: 0,
listWsSeqTimer: null,
// 为 true 时第 1 页列表拉完必整表重订;下拉/换空间/需刷新 时置 true
listWsForceFullResync: false,
staticStatusImg: {
lx: '/static/lx.png',
fc: '/static/fc.png',
dc: '/static/dc.png',
xh: '/static/xh.png',
/** 门磁门开 */
door: '/static/mc.png',
},
}
},
computed: {
// 计算当前语言,用于触发更新
currentLanguage() {
return this.$i18n.getCurrentLanguage()
}
},
created() {
this.updateDeviceList()
},
watch: {
// 监听语言变化
currentLanguage() {
console.log('设备页面语言变化:', this.currentLanguage)
this.updateDeviceList()
},
subscriptionActive: {
immediate: true,
handler(on) {
if (on) {
this._listSubscriptionTurnOn()
} else {
this._listSubscriptionTurnOff()
}
},
},
},
mounted() {
// 监听语言变化事件
uni.$on('languageChanged', this.handleLanguageChange)
// 监听空间切换事件
uni.$on('spaceChanged', this.handleSpaceChanged)
// 首页 onShow 拉取空间列表并写入 kjid 后广播,避免子页返回后组件内 kjid 仍为旧值
uni.$on('homePageShow', this.syncKjidFromStorage)
uni.$on('deviceListNeedRefresh', this.onDeviceListNeedRefresh)
uni.$on('refreshDeviceData', this.onRefreshDeviceData)
uni.$on('appWs:deviceOnlineStatus', this.onAppWsDeviceListOnline)
uni.$on('appWs:deviceData', this.onAppWsDeviceListData)
// 初始化空间ID并首次拉取列表
if(uni.getStorageSync('kjid')){
this.kjid = uni.getStorageSync('kjid')
this.getDeviceList()
}else{
this.$http.get('/bst/space/list?pageNum=1&pageSize=99').then(res =>{
if(res.code == 200 && res.rows.length > 0){
this.kjid = res.rows[0].id
uni.setStorageSync('kjid',this.kjid)
this.getDeviceList()
}
})
}
},
beforeDestroy() {
uni.$off('appWs:opened', this._onListWsSocketOpened)
this._listSubscriptionTurnOff()
// 移除事件监听
uni.$off('languageChanged', this.handleLanguageChange)
uni.$off('spaceChanged', this.handleSpaceChanged)
uni.$off('homePageShow', this.syncKjidFromStorage)
uni.$off('deviceListNeedRefresh', this.onDeviceListNeedRefresh)
uni.$off('refreshDeviceData', this.onRefreshDeviceData)
uni.$off('appWs:deviceOnlineStatus', this.onAppWsDeviceListOnline)
uni.$off('appWs:deviceData', this.onAppWsDeviceListData)
},
methods: {
_listClearSeqTimer() {
if (this.listWsSeqTimer != null) {
clearTimeout(this.listWsSeqTimer)
this.listWsSeqTimer = null
}
},
_listQueueNext(fn) {
this._listClearSeqTimer()
this.listWsSeqTimer = setTimeout(fn, 0)
},
_macsFromDeviceList(dlist) {
const out = []
const seen = Object.create(null)
const src = dlist != null ? dlist : this.deviceList
for (const d of src || []) {
const m = normalizeMac(d && d.mac)
if (!m || seen[m]) continue
seen[m] = true
out.push(m)
}
return out
},
/** 按首次出现顺序去重,保证一 mac 一退订/一订阅 */
_macsDedupeOrder(arr) {
const out = []
const seen = Object.create(null)
for (const m of arr || []) {
if (!m || seen[m]) continue
seen[m] = true
out.push(m)
}
return out
},
_listMacSetSignature(macs) {
return this._macsDedupeOrder(macs)
.slice()
.sort()
.join('\0')
},
/** 当前 deviceList 与已订阅 mac 集合一致则无需因接口重复拉取而整表重订(含均空) */
_listDeviceListMacsMatchSubscribed() {
const a = this._listMacSetSignature(this._macsFromDeviceList())
const b = this._listMacSetSignature(this.listWsSubscribedMacs)
return a === b
},
/** 在设备列表内:对当前已订阅的 mac 顺序退订,再按当前 deviceList 顺序逐条 subscribe退订完成前不清空避免离页时漏退订 */
_listFullResubscribe() {
if (!this.subscriptionActive) return
connectIfLoggedIn()
this.listWsAppendSeq++
this.listWsBatchId++
const batchId = this.listWsBatchId
const toUnsub = this._macsDedupeOrder(this.listWsSubscribedMacs)
const toSub = this._macsFromDeviceList()
const runUnsub = (i) => {
if (batchId !== this.listWsBatchId) return
if (i >= toUnsub.length) {
this.listWsSubscribedMacs = []
return this._listRunSubscribeChain(batchId, toSub, 0)
}
if (isOpen()) {
sendJson({ action: 'unsubscribe', mac: toUnsub[i] }, '列表退订')
}
this._listQueueNext(() => runUnsub(i + 1))
}
runUnsub(0)
},
_listRunSubscribeChain(batchId, macs, i) {
if (batchId !== this.listWsBatchId) return
if (!this.subscriptionActive) return
if (i >= (macs || []).length) return
if (isOpen()) {
sendJson({ action: 'subscribe', mac: macs[i] }, '列表订阅')
if (!this.listWsSubscribedMacs) {
this.listWsSubscribedMacs = []
}
this.listWsSubscribedMacs.push(macs[i])
}
this._listQueueNext(() => this._listRunSubscribeChain(batchId, macs, i + 1))
},
/** 上拉加载到的新行,逐条 subscribe仅新 mac */
_listAppendSubscribeRows(rows) {
if (!this.subscriptionActive) return
const have = Object.create(null)
for (const m of this.listWsSubscribedMacs || []) {
have[m] = true
}
const toAdd = []
for (const d of rows || []) {
const m = normalizeMac(d && d.mac)
if (!m || have[m]) continue
have[m] = true
toAdd.push(m)
}
if (!toAdd.length) return
this.listWsAppendSeq++
const seq = this.listWsAppendSeq
const run = (j) => {
if (seq !== this.listWsAppendSeq) return
if (!this.subscriptionActive) return
if (j >= toAdd.length) return
if (isOpen()) {
sendJson({ action: 'subscribe', mac: toAdd[j] }, '列表上拉订阅')
if (!this.listWsSubscribedMacs) {
this.listWsSubscribedMacs = []
}
this.listWsSubscribedMacs.push(toAdd[j])
}
this._listQueueNext(() => run(j + 1))
}
run(0)
},
_listSubscriptionTurnOn() {
if (!this.subscriptionActive) return
connectIfLoggedIn()
uni.$off('appWs:opened', this._onListWsSocketOpened)
uni.$on('appWs:opened', this._onListWsSocketOpened)
this._listFullResubscribe()
},
_listSubscriptionTurnOff() {
uni.$off('appWs:opened', this._onListWsSocketOpened)
this._listClearSeqTimer()
this.listWsBatchId++
this.listWsAppendSeq++
const toUnsub = this._macsDedupeOrder(this.listWsSubscribedMacs)
this.listWsSubscribedMacs = []
const run = (i) => {
if (i >= toUnsub.length) return
if (isOpen()) {
sendJson({ action: 'unsubscribe', mac: toUnsub[i] }, '列表离页退订')
}
this._listQueueNext(() => run(i + 1))
}
run(0)
},
_onListWsSocketOpened() {
if (!this.subscriptionActive) return
this._listFullResubscribe()
},
_onDeviceListHttpDone(requestPage, responseRows, loadOk) {
if (!loadOk || !this.subscriptionActive) return
this.$nextTick(() => {
if (!this.subscriptionActive) return
if (requestPage === 1) {
const needFull = this.listWsForceFullResync || !this._listDeviceListMacsMatchSubscribed()
this.listWsForceFullResync = false
if (needFull) {
this._listFullResubscribe()
}
} else {
this._listAppendSubscribeRows(responseRows)
}
})
},
/** 编辑设备名/房间后等需重新对齐 WS 时的列表刷新 */
onDeviceListNeedRefresh() {
this.listWsForceFullResync = true
this.refreshDeviceListFromHome()
},
/** 全局长连接device_online_status 更新列表项在线角标 */
onAppWsDeviceListOnline(msg) {
if (!msg || !msg.data || typeof msg.data !== 'object') return
const outer = msg.data
const inner = outer.data
if (!inner || typeof inner !== 'object') return
const payloadMac = normalizeMac(inner.mac != null ? inner.mac : outer.mac)
if (!payloadMac) return
const target = (this.deviceList || []).find((d) => normalizeMac(d.mac) === payloadMac)
if (!target) return
const statusPayload =
inner.data !== undefined && typeof inner.data === 'object' ? inner.data : inner
const os = statusPayload.onlineStatus
const lot = statusPayload.lastOnlineTime
if (os !== undefined && os !== null && os !== '') {
const n = Number(os)
if (!Number.isNaN(n)) {
this.$set(target, 'onlineStatus', n)
}
}
if (lot !== undefined && lot !== null) {
this.$set(target, 'lastOnlineTime', lot)
}
if (!target.iotOnlineData) {
this.$set(target, 'iotOnlineData', {})
}
if (os !== undefined && os !== null && os !== '') {
this.$set(target.iotOnlineData, 'onlineStatus', String(os))
}
},
/** 长连接 device_data按 mac 合入当前行的 iotData供状态条展示 */
onAppWsDeviceListData(msg) {
if (!msg || msg.event !== 'device_data') return
const outer = msg.data
if (!outer || typeof outer !== 'object') return
const inner = outer.data
if (!inner || typeof inner !== 'object') return
const payloadMac = normalizeMac(inner.mac != null ? inner.mac : outer.mac)
if (!payloadMac) return
const target = (this.deviceList || []).find((d) => normalizeMac(d.mac) === payloadMac)
if (!target) return
const patch = inner.data !== undefined ? inner.data : inner
if (!patch || typeof patch !== 'object') return
if (!target.iotData) {
this.$set(target, 'iotData', {})
}
const keys = Object.keys(patch).filter((k) => k !== 'mac')
for (const k of keys) {
const nv = patch[k]
const ov = target.iotData[k]
const nextVal =
nv !== null && typeof nv === 'object' && !Array.isArray(nv)
? mergeIotDataPoint(ov, nv)
: nv
if (JSON.stringify(ov) === JSON.stringify(nextVal)) continue
this.$set(target.iotData, k, nextVal)
}
},
/** 列表行与头像角标一致onlineStatus 为 1 视为在线(兼容仅有根级 onlineStatus 的列表数据) */
listRowIsOnline(device) {
if (!device) return false
const od = device.iotOnlineData
if (od && od.onlineStatus !== undefined && od.onlineStatus !== null) {
return String(od.onlineStatus) === '1'
}
if (device.onlineStatus !== undefined && device.onlineStatus !== null) {
return String(device.onlineStatus) === '1'
}
return false
},
/** 离线展示 lx无在线信息时不展示避免首屏全灰 */
listRowShowLx(device) {
if (!device) return false
const od = device.iotOnlineData
if (od && od.onlineStatus !== undefined && od.onlineStatus !== null) {
return String(od.onlineStatus) !== '1'
}
if (device.onlineStatus !== undefined && device.onlineStatus !== null) {
return String(device.onlineStatus) !== '1'
}
return false
},
_listCoverOpenFromIot(iot) {
const fx = iot && iot.fx
if (!fx) return false
return isFxCoverOpen(fx)
},
/** 接口 statuses 可能在 device 根上,或随长连接合入 iotData.statuses */
_listGetStatuses(device) {
if (!device) return []
if (Array.isArray(device.statuses)) return device.statuses
const iot = device.iotData
if (iot && Array.isArray(iot.statuses)) return iot.statuses
return []
},
_listStatusKeyMatchesDoorOpen(key) {
if (key === undefined || key === null) return false
// 兼容 door:open、door_open、Door:Open
const k = String(key).trim().toLowerCase().replace(/_/g, ':')
return k === 'door:open'
},
_listStatusHasAntiTheft(device) {
const list = this._listGetStatuses(device)
for (let i = 0; i < list.length; i++) {
const s = list[i]
if (!s || s.key === undefined || s.key === null) continue
const k = String(s.key).trim().toLowerCase().replace(/_/g, ':')
if (k === 'common:anti-theft' || k === 'common:antitheft') return true
}
return false
},
/** 门开statuses 含 door:open兼容 door_open 等) */
listRowShowDoorOpen(device) {
if (!this.listRowIsOnline(device)) return false
const list = this._listGetStatuses(device)
for (let i = 0; i < list.length; i++) {
const s = list[i]
if (this._listStatusKeyMatchesDoorOpen(s && s.key)) return true
}
return false
},
/**
* 防拆/盖子iotData.fx 为开,或 statuses 含 common:anti-theft
*/
listRowShowFc(device) {
if (!this.listRowIsOnline(device)) return false
if (this._listStatusHasAntiTheft(device)) return true
if (!device.iotData) return false
return this._listCoverOpenFromIot(device.iotData)
},
// 请求设备列表force 为 true 时允许与进行中的请求并行(用于首页 onShow 强制刷新 roomName
getDeviceList(force = false){
if(this.requestInProgress && !force) return
this.requestInProgress = true
const requestPage = this.pageNum
let responseRows = []
let loadOk = false
this.$http.get(`/bst/device/list?spaceId=${this.kjid}&pageNum=${this.pageNum}&pageSize=${this.pageSize}`).then((res) => {
if (res.code == 200) {
loadOk = true
this.total = Number(res.total || 0)
const rows = Array.isArray(res.rows) ? res.rows : []
responseRows = rows
mergeDeviceRoomOverlayIntoRows(rows)
if(this.pageNum == 1){
this.deviceList = rows
}else{
this.deviceList = this.deviceList.concat(rows)
mergeDeviceRoomOverlayIntoRows(this.deviceList)
}
this.hasMore = this.deviceList.length < this.total
}
}).catch(() => {
// 如果请求失败,使用模拟数据
this.updateDeviceList()
}).finally(()=>{
this.requestInProgress = false
if(this.isRefreshing){
this.isRefreshing = false
}
this._onDeviceListHttpDone(requestPage, responseRows, loadOk)
})
},
// 处理空间变化
handleSpaceChanged(payload){
try{
this.listWsForceFullResync = true
this.kjid = (payload && payload.kjid) || uni.getStorageSync('kjid')
this.pageNum = 1
this.getDeviceList()
}catch(e){
console.warn('处理空间切换失败:', e)
}
},
// 与本地 storage 中的当前空间 id 对齐,并重新拉设备列表(从设置页改房间返回首页时需刷新 roomName
syncKjidFromStorage() {
const k = uni.getStorageSync('kjid')
if (k !== undefined && k !== null && k !== '') {
this.kjid = k
}
this.refreshDeviceListFromHome()
},
refreshDeviceListFromHome() {
if (!this.kjid) {
const k = uni.getStorageSync('kjid')
if (k !== undefined && k !== null && k !== '') {
this.kjid = k
}
}
if (!this.kjid) return
this.pageNum = 1
this.getDeviceList(true)
},
/** 首页空间切换等场景传入 kjid否则用本地 storage */
applySpaceAndRefresh(kjid) {
const k =
kjid !== undefined && kjid !== null && kjid !== ''
? kjid
: uni.getStorageSync('kjid')
if (k !== undefined && k !== null && k !== '') {
this.kjid = k
}
this.refreshDeviceListFromHome()
},
onRefreshDeviceData(payload) {
const kjid = payload && payload.kjid
this.applySpaceAndRefresh(kjid)
},
// 上拉加载更多
handqixing() {
if(!this.hasMore) return
this.pageNum += 1
this.getDeviceList()
},
// 下拉刷新
onRefresh() {
if(this.isRefreshing || this.requestInProgress) return
this.isRefreshing = true
setTimeout(()=>{
this.listWsForceFullResync = true
this.pageNum = 1
this.getDeviceList()
},1000)
},
handleLanguageChange(lang) {
console.log('设备页面语言切换事件:', lang)
this.updateDeviceList()
},
updateDeviceList() {
console.log('更新设备列表')
// 强制更新组件
this.$forceUpdate()
},
// 点击设备跳转到详情
handleDeviceClick(device) {
console.log(device);
if(device.productType == 'HUB'){ //hub
uni.navigateTo({
url: '/subpackage/device/devicexq?id=' + device.id + '&mac=' + device.mac
})
}else if(device.productType == 'FIRE'){ //烟感
uni.navigateTo({
url: '/pages/device/yangan?id=' + device.id + '&mac=' + device.mac
})
}else if(device.productType == 'DOOR'){ //门磁
uni.navigateTo({
url:'/pages/device/mcgydevice?id=' + device.id
})
}else if(device.productType == 'INFRARED'){//人体红外传感器
uni.navigateTo({
url:'/subpackage/device/rentiredwai/index?id=' + device.id
})
}else if(device.productType == 'CONTROL'){//遥控器
uni.navigateTo({
url:'/subpackage/device/yaokongqi/index?id=' + device.id + '&mac=' + device.mac
})
}else if(device.productType == 'GLASS'){//玻璃破碎传感器
uni.navigateTo({
url:'/subpackage/device/boligan/index?id=' + device.id
})
}else if(device.productType == 'WATER'){//水浸传感器
uni.navigateTo({
url:'/subpackage/device/shuiqinganying/index?id=' + device.id
})
}else if(device.productType == 'RELAY'){//继电器
uni.navigateTo({
url:'/pages/device/jidianqi/index?id=' + device.id
})
}else if(device.productType == 'SIREN'){//警笛
uni.navigateTo({
url:'/pages/device/jingdi/index?id=' + device.id
})
}else if(device.productType == 'KEY_BOARD'){//键盘
uni.navigateTo({
url:'/pages/device/jianpan/index?id=' + device.id
})
}
},
// 点击添加设备:依次校验 空间 → 房间 → 是否已绑定 HUB未满足时用弹窗说明下一步取消 / 确定)
handleAddDevice() {
const t = (k) => (this.$i18n && this.$i18n.t ? this.$i18n.t(k) : k)
const showOnboardingModal = (contentKey, url) => {
uni.showModal({
title: t('onboardingHintTitle'),
content: t(contentKey),
cancelText: t('cancel'),
confirmText: t('confirm'),
success: (res) => {
if (res.confirm) {
uni.navigateTo({ url })
}
}
})
}
const proceedWithSpace = (kjid) => {
this.kjid = kjid
uni.setStorageSync('kjid', kjid)
this.$http.get(`/bst/room/list?spaceId=${kjid}&pageNum=1&pageSize=999`).then((roomRes) => {
// 与全局其它接口一致使用 == 200后端可能返回字符串 "200",用 !== 会误判失败
if (roomRes.code != 200) {
uni.showToast({ title: roomRes.msg || t('error'), icon: 'none' })
return
}
const roomRows = Array.isArray(roomRes.rows)
? roomRes.rows
: (Array.isArray(roomRes.data) ? roomRes.data : (Array.isArray(roomRes.records) ? roomRes.records : []))
const totalNum = roomRes.total !== undefined && roomRes.total !== null && roomRes.total !== ''
? Number(roomRes.total)
: NaN
const hasRooms = roomRows.length > 0 || (!Number.isNaN(totalNum) && totalNum > 0)
if (!hasRooms) {
showOnboardingModal('onboardingNeedRoom', '/pages/room/addroom')
return
}
this.$http.get(`/bst/device/list?spaceId=${kjid}&pageNum=1&pageSize=${this.pageSize}`).then((devRes) => {
if (devRes.code != 200) {
uni.showToast({ title: devRes.msg || t('error'), icon: 'none' })
return
}
const rows = Array.isArray(devRes.rows) ? devRes.rows : []
const hasHub = rows.some((d) => d.productType === 'HUB')
if (!hasHub) {
showOnboardingModal('onboardingNeedHub', '/pages/device/addhub')
return
}
uni.navigateTo({ url: '/subpackage/device/adddevice' })
})
})
}
// 优先使用 storage首页 onShow 会刷新空间并写入 kjid组件内 this.kjid 可能未同步
let kjid = uni.getStorageSync('kjid') || this.kjid
if (!kjid) {
this.$http.get('/bst/space/list?pageNum=1&pageSize=99').then((spaceRes) => {
if (spaceRes.code == 200 && spaceRes.rows && spaceRes.rows.length > 0) {
kjid = spaceRes.rows[0].id
uni.setStorageSync('kjid', kjid)
proceedWithSpace(kjid)
} else {
showOnboardingModal('onboardingNeedSpace', '/pages/kongjian/index')
}
})
return
}
proceedWithSpace(kjid)
}
}
}
</script>
<style lang="scss" scoped>
.container {
padding: 100rpx 30rpx;
}
.device-tab {
// padding: 20rpx;
// background: #f5f5f5;
}
.device-list {
// margin-bottom: 30rpx;
height: 71vh;
overflow: scroll;
}
.device-item {
position: relative;
display: flex;
align-items: center;
margin: auto;
box-sizing: border-box;
margin-top: 20rpx;
background: #fff;
border-radius: 20rpx;
margin-bottom: 20rpx;
padding: 20rpx;
box-shadow: -2rpx 2rpx 6rpx rgba(0, 0, 0, 0.08);
transition: all 0.2s ease;
width: 700rpx;
}
.device-item:active {
transform: scale(0.98);
background: #f8f8f8;
box-shadow: 0 1rpx 5rpx rgba(0,0,0,0.1);
}
.device-badge {
position: absolute;
top: -4rpx;
left: -4rpx;
background: #ff4444;
color: #fff;
font-size: 22rpx;
border-radius:50%;
width: 20rpx;
height: 20rpx;
z-index: 99;
}
.device-avatar {
width: 128rpx;
height: 128rpx;
background: #e0e0e0;
border-radius: 10rpx;
// overflow: hidden;
margin-right: 20rpx;
display: flex;
align-items: center;
justify-content: center;
margin-left: 10rpx;
image{
width: 128rpx;
height: 128rpx;
border-radius: 10rpx;
}
}
.device-icon {
font-size: 40rpx;
}
.device-info {
flex: 1;
display: flex;
flex-direction: column;
justify-content: center;
}
.device-name {
font-size: 30rpx;
color: #333;
font-weight: bold;
margin-bottom: 8rpx;
}
.device-room {
font-size: 26rpx;
color: #999;
margin-bottom: 12rpx;
}
.device-status-bar {
display: flex;
align-items: center;
flex-wrap: wrap;
}
.device-status-sbar-row {
display: flex;
align-items: center;
flex-wrap: wrap;
}
.device-sbar-img {
width: 44rpx;
height: 44rpx;
margin-right: 10rpx;
}
.status-icon {
font-size: 28rpx;
color: #666;
margin-right: 16rpx;
}
.device-arrow {
font-size: 66rpx;
color: #bbb;
margin-left: 20rpx;
}
.add-device-btn {
position: fixed;
bottom: 180rpx;
left: 0;
right: 0;
margin: 40rpx auto 0 auto;
width: 700rpx;
height: 80rpx;
border: 2rpx solid #bbb;
border-radius: 20rpx;
display: flex;
align-items: center;
justify-content: center;
font-size: 30rpx;
color: #333;
background: #fff;
transition: all 0.2s ease;
.add-icon {
font-size: 38rpx;
margin-right: 16rpx;
}
}
.add-device-btn:active {
transform: scale(0.95);
background: #f0f0f0;
border-color: #999;
}
</style>