wx-starlock/pages/main/lockDetail.vue
2025-10-27 09:58:13 +08:00

1097 lines
30 KiB
Vue
Raw Permalink 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>
<scroll-view
v-if="deviceInfo"
scroll-y="true"
:style="{
height: deviceInfo.windowHeight - deviceInfo.safeArea.top + 'px'
}"
@refresherrefresh="refresher"
:refresher-enabled="true"
:refresher-triggered="refresherTriggered"
>
<view class="select-lock-row">
<image
src="https://cos-lock.skychip.top/mp/icon_add_round.png"
mode="aspectFill"
style="width: 48rpx; height: 48rpx"
@click="toSearchDevice"
></image>
<!-- 普通 picker -->
<picker :range="pickerOptions" @change="onPickerChange">
<view class="lock-name">
<text>
{{ $bluetooth.currentLockInfo.lockAlias }}
</text>
<image
class="icon-down"
src="https://cos-lock.skychip.top/mp/icon_down.png"
mode="aspectFill"
>
</image>
</view>
</picker>
<view class="power" @click="powerTip">
<image
class="power-icon"
:src="$lock.getPowerIcon($bluetooth.currentLockInfo.electricQuantity)"
mode="aspectFill"
></image>
<view class="power-text">{{ $bluetooth.currentLockInfo.electricQuantity }}%</view>
<image
class="power-tips"
src="https://cos-lock.skychip.top/mp/icon_tips.png"
mode="aspectFill"
>
</image>
</view>
</view>
<view
class="open-lock-btn"
@click="openDoorOperate('open')"
@longpress="openDoorOperate('close')"
>
<image
class="open-lock-btn-image-1"
ref="sLoading"
src="https://cos-lock.skychip.top/mp/icon_main_openLockBtn_center.png"
></image>
<image
class="open-lock-btn-image-2"
:class="{ rotate: isRotating }"
src="https://cos-lock.skychip.top/mp/icon_main_openLockBtn_circle.png"
></image>
</view>
<view class="days" v-if="$bluetooth.currentLockInfo.days"
>钥匙将在{{ $bluetooth.currentLockInfo.days }}天后失效
</view>
<view class="switch-text">点击开锁长按闭锁</view>
<view class="other-text">
<view class="bottom-side">
<image
class="bottom-icon"
style="width: 32rpx; height: 32rpx"
src="https://cos-lock.skychip.top/mp/icon_role.png"
mode="aspectFill"
></image>
<view>{{
$lock.getRole($bluetooth.currentLockInfo.userType, $bluetooth.currentLockInfo.keyRight)
}}</view>
</view>
<view class="bottom-side">
<image
class="bottom-icon"
:src="
$bluetooth.currentLockInfo.appUnlockOnline
? 'https://cos-lock.skychip.top/mp/icon_cloud_active.png'
: 'https://cos-lock.skychip.top/mp/icon_cloud.png'
"
mode="aspectFill"
style="width: 40rpx; height: 40rpx"
></image>
<view
:style="{
color: $bluetooth.currentLockInfo.appUnlockOnline ? '#4777ee' : '#a3a3a3'
}"
>手机需联网</view
>
</view>
</view>
<view class="menu">
<view class="menu-main">
<view
v-if="
$bluetooth.currentLockInfo.keyRight === 1 &&
$bluetooth.currentLockInfo.transportType !== transportType.TRANSPORT_TENCENT_YUN
"
class="menu-main-view"
@click="$basic.routeJump({ name: 'keyList' })"
>
<image
class="menu-main-image"
src="https://cos-lock.skychip.top/mp/icon_main_electronicKey.png"
></image>
<view>电子钥匙</view>
</view>
<view
v-if="
$bluetooth.currentLockInfo.lockFeature.password &&
$bluetooth.currentLockInfo.keyRight === 1 &&
$bluetooth.currentLockInfo.transportType !== transportType.TRANSPORT_TENCENT_YUN
"
class="menu-main-view"
@click="$basic.routeJump({ name: 'passwordList' })"
>
<image
class="menu-main-image"
src="https://cos-lock.skychip.top/mp/icon_main_password.png"
>
</image>
<view>密码</view>
</view>
<view
v-if="
$bluetooth.currentLockInfo.lockFeature.icCard &&
$bluetooth.currentLockInfo.keyRight === 1 &&
$bluetooth.currentLockInfo.transportType !== transportType.TRANSPORT_TENCENT_YUN
"
class="menu-main-view"
@click="$basic.routeJump({ name: 'cardList' })"
>
<image
class="menu-main-image transform-scale-110"
src="https://cos-lock.skychip.top/mp/icon_main_icCard.png"
></image>
<view>卡</view>
</view>
<view
v-if="
$bluetooth.currentLockInfo.lockFeature.fingerprint &&
$bluetooth.currentLockInfo.keyRight === 1 &&
$bluetooth.currentLockInfo.transportType !== transportType.TRANSPORT_TENCENT_YUN
"
class="menu-main-view"
@click="$basic.routeJump({ name: 'fingerprintList' })"
>
<image
class="menu-main-image"
src="https://cos-lock.skychip.top/mp/icon_main_fingerprint.png"
>
</image>
<view>指纹</view>
</view>
<view
v-if="
$bluetooth.currentLockInfo.lockFeature.bluetoothRemoteControl &&
$bluetooth.currentLockInfo.keyRight === 1 &&
$bluetooth.currentLockInfo.transportType !== transportType.TRANSPORT_TENCENT_YUN
"
class="menu-main-view transform-scale-110"
@click="$basic.routeJump({ name: 'remoteList' })"
>
<image
class="menu-main-image"
src="https://cos-lock.skychip.top/mp/icon_main_remoteControl.png"
></image>
<view>遥控</view>
</view>
<view
v-if="
$bluetooth.currentLockInfo.lockFeature.d3Face &&
$bluetooth.currentLockInfo.keyRight === 1 &&
$bluetooth.currentLockInfo.transportType !== transportType.TRANSPORT_TENCENT_YUN
"
class="menu-main-view"
@click="$basic.routeJump({ name: 'faceList' })"
>
<image
class="menu-main-image"
src="https://cos-lock.skychip.top/mp/icon_face.png"
></image>
<view>人脸</view>
</view>
<view
v-if="
$bluetooth.currentLockInfo.lockFeature.palmVein &&
$bluetooth.currentLockInfo.keyRight === 1 &&
$bluetooth.currentLockInfo.transportType !== transportType.TRANSPORT_TENCENT_YUN
"
class="menu-main-view"
@click="$basic.routeJump({ name: 'palmVeinList' })"
>
<image
class="menu-main-image"
src="https://cos-lock.skychip.top/mp/icon_palm.png"
></image>
<view>掌静脉</view>
</view>
<view
v-if="$bluetooth.currentLockInfo.transportType === transportType.TRANSPORT_TENCENT_YUN"
class="menu-main-view"
@click="jumpToPlayer"
>
<image
class="menu-main-image transform-scale-140"
src="https://oss-lock.xhjcn.ltd/mp/icon_catEyes.png"
></image>
<view>监控</view>
</view>
<view
v-if="
$bluetooth.currentLockInfo.userType === 110301 &&
$bluetooth.currentLockInfo.transportType !== transportType.TRANSPORT_TENCENT_YUN
"
class="menu-main-view"
@click="$basic.routeJump({ name: 'adminList' })"
>
<image
class="menu-main-image transform-scale-140"
src="https://cos-lock.skychip.top/mp/icon_main_authorizedAdmin.png"
></image>
<view>授权管理员</view>
</view>
<view
v-if="$bluetooth.currentLockInfo.transportType !== transportType.TRANSPORT_TENCENT_YUN"
class="menu-main-view transform-scale-105"
@click="$basic.routeJump({ name: 'recordList' })"
>
<image
class="menu-main-image"
src="https://cos-lock.skychip.top/mp/icon_main_operatingRecord.png"
></image>
<view>操作记录</view>
</view>
<view
v-if="$bluetooth.currentLockInfo.transportType === transportType.TRANSPORT_TENCENT_YUN"
class="menu-main-view transform-scale-105"
@click="$basic.routeJump({ name: 'videoLog' })"
>
<image
class="menu-main-image"
src="https://cos-lock.skychip.top/mp/icon_main_operatingRecord.png"
></image>
<view>门锁记录</view>
</view>
<view class="menu-main-view" @click="$basic.routeJump({ name: 'setting' })">
<image
class="menu-main-image transform-scale-110"
src="https://cos-lock.skychip.top/mp/icon_main_set.png"
></image>
<view>设置</view>
</view>
</view>
</view>
</scroll-view>
<view v-else class="not-devices-box">
<image class="menu-main-image" src="https://cos-lock.skychip.top/mp/icon_noData.png"></image>
<text>暂无智能锁</text>
<image
src="https://cos-lock.skychip.top/mp/icon_add_round.png"
mode="aspectFill"
class="button-add-big"
@click="toSearchDevice"
></image>
</view>
<up-popup
:show="show"
@close="closePopup"
mode="center"
:closeOnClickOverlay="true"
bgColor="transparent"
>
<view class="popup" @click="closePopup">
<image
class="popup-background"
:src="
typeValue === 'close'
? 'https://oss-lock.xhjcn.ltd/mp/background_close_door.png'
: 'https://oss-lock.xhjcn.ltd/mp/background_open_door.png'
"
mode="aspectFill"
/>
<view>
<view class="popup-name">{{ $bluetooth.currentLockInfo.lockAlias }}</view>
<view class="popup-time">{{ timeFormat('', 'mm/dd h:M') }}</view>
</view>
</view>
</up-popup>
</view>
</template>
<script setup>
import { timeFormat } from 'uview-plus'
import { onMounted, onUnmounted, ref } from 'vue'
import { useBluetoothStore } from '@/stores/bluetooth'
import { useBasicStore } from '@/stores/basic'
import SwitchLoading from '@/components/SwitchLoading/SwitchLoading.vue'
import { useLockStore } from '@/stores/lock'
import { useUserStore } from '@/stores/user'
import { getLockDetailRequest, getLockNetTokenRequest, deleteLockRequest } from '@/api/lock'
import { deleteKeyRequest } from '@/api/key'
import { transportType } from '@/constant/transportType'
import { loginRequest } from '@/api/user'
import { setStorage, getStorage } from '@/utils/storage'
import { onLoad, onShow } from '@dcloudio/uni-app' // 注意:这里需要正确导入 UniApp 的生命周期钩子!
// #ifdef APP-PLUS
import { requestPermission } from '@/uni_modules/xhj-record'
// #endif
const $bluetooth = useBluetoothStore()
const $basic = useBasicStore()
const $lock = useLockStore()
const $user = useUserStore()
// const sLoading = ref(null)
const time = ref(0)
const onlineToken = ref('0')
const pending = ref(false)
const show = ref(false)
const typeValue = ref('')
const refresherTriggered = ref(false)
const deviceInfo = ref(null)
// 控制是否应用旋转动画
const isRotating = ref(false)
// 选择器数据源(比如一个字符串数组)
const pickerOptions = ref([])
// 当前选中项的索引picker 内部使用)
const selectedIndex = ref(0)
// 选中后展示的文本
const selectedText = ref(pickerOptions.value[0])
onMounted(async () => {
try {
// #ifdef MP-WEIXIN
const accountInfo = uni.getAccountInfoSync()
getApp().globalData.appid = accountInfo.miniProgram.appId
getApp().globalData.envVersion = accountInfo.miniProgram.envVersion
// #endif
uni.showLoading({
title: '加载中',
mask: true
})
const token = getStorage('token')
const list = getStorage('lockList')
const userInfo = getStorage('userInfo')
if (token) {
await Promise.all([$lock.getLockList($lock.lockSearch), $user.getUserInfo()]).then(res => {
pending.value = false
uni.hideLoading()
if (res[0].code === -1 && res[1] === -1 && list && userInfo) {
uni.showToast({
title: '网络访问失败,请检查网络是否正常',
icon: 'none'
})
$lock.updateLockList(list)
$user.updateUserInfo(userInfo)
$user.updateLoginStatus(true)
}
})
} else {
// 封装 uni.login() 为 Promise
await new Promise((resolve, reject) => {
uni.login({
provider: 'weixin',
success: async loginRes => {
try {
const { code, data } = await loginRequest({
js_code: loginRes.code
})
if (code === 0) {
await setStorage('openid', data.openid)
if (data.accessToken) {
setStorage('token', data.accessToken)
const list = getStorage('lockList')
const userInfo = getStorage('userInfo')
if (!list && !userInfo) {
await $lock.getLockList($lock.lockSearch)
await $user.getUserInfo()
await $user.updateLoginStatus(true)
}
} else {
$user.updateLoginStatus(false)
}
resolve() // 登录成功resolve Promise
} else {
throw new Error('登录请求失败')
}
} catch (err) {
console.error('登录过程出错:', err)
reject(err) // 登录过程出错reject Promise
}
},
fail: err => {
console.error('微信登录失败:', err)
reject(err) // 微信登录失败reject Promise
}
})
})
}
// 取第一个锁的信息作为首页的展示
const lock = list.flatMap(item => item.lockList)
console.log('lock', lock)
if (lock) {
const defaultShowLockIndex = 0
const data = {
...lock[defaultShowLockIndex],
name: lock[defaultShowLockIndex].bluetooth.bluetoothDeviceName,
deviceId: lock[defaultShowLockIndex].bluetooth.bluetoothDeviceId,
commKey: lock[defaultShowLockIndex].bluetooth.privateKey,
signKey: lock[defaultShowLockIndex].bluetooth.signKey,
publicKey: lock[defaultShowLockIndex].bluetooth.publicKey
}
await $bluetooth.updateKeyId(lock[defaultShowLockIndex].keyId.toString())
await $bluetooth.updateCurrentLockInfo(data)
pickerOptions.value = lock.map(item => item.lockAlias)
}
if (Object.keys($bluetooth.currentLockInfo).length !== 0) {
if (!($bluetooth.bluetoothStatus === 0 || $bluetooth.bluetoothStatus === -1)) {
$bluetooth.getBluetoothStatus()
return
}
let result = true
if (!$bluetooth.isInitBluetooth) {
result = await $bluetooth.initAndListenBluetooth()
}
if (!result) {
$bluetooth.checkSetting()
}
}
// 确保登录完成后再执行后续操作
deviceInfo.value = await $basic.getDeviceInfo()
await getServeTime()
} catch (err) {
console.error('初始化过程出错:', err)
uni.showToast({
title: '登录失败,请重试',
icon: 'none'
})
} finally {
uni.hideLoading() // 无论成功失败,最后都隐藏 loading
}
})
const jumpToPlayer = async () => {
// #ifdef APP-PLUS
// 检查权限
const result = await requestPermission()
if (result.code !== 0) {
uni.showToast({
title: '请在设置中打开应用的麦克风权限',
icon: 'none'
})
return
}
// #endif
$basic.routeJump({
name: 'p2pPlayer'
})
}
const openDoorOperate = async type => {
if (!($bluetooth.bluetoothStatus === 0 || $bluetooth.bluetoothStatus === -1)) {
$bluetooth.getBluetoothStatus()
return
}
let result = true
if (!$bluetooth.isInitBluetooth) {
result = await $bluetooth.initAndListenBluetooth()
}
if (!result) {
$bluetooth.checkSetting()
}
isRotating.value = true
const timestamp = new Date().getTime()
if (
$bluetooth.currentLockInfo.faceAuthentication === 1 &&
$bluetooth.currentLockInfo.nextFaceValidateTime <= new Date().getTime() + time.value * 1000 &&
$bluetooth.currentLockInfo.nextFaceValidateTime !== 0
) {
uni.showModal({
title: '提示',
content: '开门前需进行实名认证小程序暂不支持请使用APP认证开门',
showCancel: false
})
isRotating.value = false
return
}
if (pending.value) {
isRotating.value = false
return
}
if ($bluetooth.currentLockInfo.appUnlockOnline && type !== 'close') {
const netWork = await $basic.getNetworkType()
if (!netWork) {
isRotating.value = false
return
}
}
uni.vibrateLong()
pending.value = true
if (type === 'close') {
uni.showToast({
title: `正在尝试闭锁……`,
icon: 'none'
})
}
// if ($bluetooth.currentLockInfo.appUnlockOnline && type !== 'close') {
if ($bluetooth.currentLockInfo.appUnlockOnline) {
const result = await getNetToken()
if (!result) {
// sLoading.value.close()
pending.value = false
isRotating.value = false
return
}
}
let openMode
if (type === 'close') {
openMode = $bluetooth.currentLockInfo.appUnlockOnline ? 33 : 32
} else {
openMode = $bluetooth.currentLockInfo.appUnlockOnline ? 1 : 0
}
const { code } = await $bluetooth.openDoor({
name: $bluetooth.currentLockInfo.name,
uid: $user.userInfo.uid.toString(),
openMode,
openTime: parseInt(new Date().getTime() / 1000, 10) + time.value,
onlineToken: onlineToken.value
})
if (type === 'open') {
// #ifdef MP-WEIXIN
uni.reportEvent('open_door', {
result: code,
duration: new Date().getTime() - timestamp
})
// #endif
} else if (type === 'close') {
// #ifdef MP-WEIXIN
uni.reportEvent('close_door', {
result: code,
duration: new Date().getTime() - timestamp
})
// #endif
}
if (code === 0) {
await $bluetooth
.syncRecord({
keyId: $bluetooth.currentLockInfo.keyId.toString(),
uid: $user.userInfo.uid.toString()
})
.then(() => {
$bluetooth.closeBluetoothConnection()
})
show.value = true
typeValue.value = type
setTimeout(() => {
show.value = false
}, 3000)
if ($bluetooth.currentLockInfo.keyType === 3) {
const { code: deleteKeyCode } = await deleteKeyRequest({
keyId: $bluetooth.keyId
})
if (deleteKeyCode === 0) {
$lock.updateLockSearch({
...$lock.lockSearch,
pageNo: 1
})
$lock.getLockList($lock.lockSearch)
$basic.backAndToast('单次钥匙已在被使用后删除', 1)
}
}
} else if (code === 7) {
uni.showToast({
title: `钥匙过期`,
icon: 'none'
})
} else if (code === 13) {
uni.showToast({
title: `钥匙当前不可用`,
icon: 'none'
})
} else if (code === -1) {
uni.showToast({
title: `${type === 'close' ? '关' : '开'}锁失败,请保证在锁附近`,
icon: 'none'
})
}
pending.value = false
isRotating.value = false
}
const closePopup = () => {
show.value = false
}
const powerTip = () => {
const time = timeFormat($bluetooth.currentLockInfo.electricQuantityDate, 'yyyy-mm-dd h:M')
uni.showModal({
title: '锁电量更新时间',
content: time,
showCancel: false
})
}
const getNetToken = async () => {
const { code, data, message } = await getLockNetTokenRequest({
lockId: $bluetooth.currentLockInfo.lockId
})
if (code === 0) {
onlineToken.value = data.token
return true
}
uni.showToast({
title: message,
icon: 'none'
})
return false
}
const getServeTime = async () => {
const { code, data } = await $bluetooth.updateServerTimestamp()
if (code === 0) {
time.value = parseInt((data.date - new Date().getTime()) / 1000, 10)
}
}
const refresher = async () => {
refresherTriggered.value = true
const { code, data, message } = await getLockDetailRequest({
lockId: $bluetooth.currentLockInfo.lockId
})
if (code === 0) {
$bluetooth.updateCurrentLockInfo({
...$bluetooth.currentLockInfo,
...data
})
uni.showToast({
title: '刷新成功',
icon: 'none'
})
} else {
uni.showToast({
title: message,
icon: 'none'
})
}
refresherTriggered.value = false
}
const toSearchDevice = async () => {
$basic.routeJump({
name: 'addLockGuid'
})
}
const deleteLock = async () => {
const netWork = await $basic.getNetworkType()
if (!netWork) {
return
}
const message = '删除锁后,所有信息都会一起删除,确定删除锁吗?'
uni.showModal({
title: '提示',
content: message,
async success(res) {
if (res.confirm) {
uni.showLoading({
title: '删除中'
})
const { code: resetDeviceCode } = await $bluetooth.resetDevice({
name: $bluetooth.currentLockInfo.name,
authUid: $user.userInfo.uid.toString(),
keyId: $bluetooth.keyId.toString()
})
if (resetDeviceCode === 0 || resetDeviceCode === -2) {
const { code, message } = await deleteLockRequest({
lockId: $bluetooth.currentLockInfo.lockId
})
if (code === 0) {
uni.hideLoading()
$lock.updateLockSearch({
...$lock.lockSearch,
pageNo: 1
})
$lock.getLockList($lock.lockSearch)
$basic.backAndToast('删除成功', 1)
} else {
uni.hideLoading()
uni.showToast({
title: message,
icon: 'none'
})
}
} else if (resetDeviceCode === -1) {
uni.hideLoading()
uni.showToast({
title: '删除失败,请保持在锁附近',
icon: 'none'
})
}
}
}
})
}
// picker 数据变化时触发
const onPickerChange = async e => {
const list = getStorage('lockList')
// 取第一个锁的信息作为首页的展示
const lock = list.flatMap(item => item.lockList)
if (lock) {
const defaultShowLockIndex = e.detail.value
const data = {
...lock[defaultShowLockIndex],
name: lock[defaultShowLockIndex].bluetooth.bluetoothDeviceName,
deviceId: lock[defaultShowLockIndex].bluetooth.bluetoothDeviceId,
commKey: lock[defaultShowLockIndex].bluetooth.privateKey,
signKey: lock[defaultShowLockIndex].bluetooth.signKey,
publicKey: lock[defaultShowLockIndex].bluetooth.publicKey
}
await $bluetooth.updateKeyId(lock[defaultShowLockIndex].keyId.toString())
await $bluetooth.updateCurrentLockInfo(data)
pickerOptions.value = lock.map(item => item.lockAlias)
}
}
</script>
<style lang="scss" scoped>
.popup {
position: relative;
display: flex;
flex-wrap: wrap;
width: 400rpx;
height: 389rpx;
text-align: center;
.popup-background {
position: absolute;
z-index: -1;
width: 400rpx;
height: 389rpx;
}
.popup-name {
z-index: 9;
display: -webkit-box;
width: 340rpx;
max-height: 80rpx;
margin-top: 180rpx;
margin-left: 30rpx;
overflow: hidden;
text-overflow: ellipsis;
-webkit-line-clamp: 2;
line-height: 40rpx;
color: #676b6d;
word-break: break-all;
white-space: normal;
-webkit-box-orient: vertical;
}
.popup-time {
width: 400rpx;
margin-top: 10rpx;
}
}
.days {
width: 750rpx;
height: 60rpx;
font-size: 32rpx;
line-height: 60rpx;
color: #bc9839;
text-align: center;
background: #faecc9;
}
.top {
position: relative;
width: 686rpx;
height: 464rpx;
margin-top: 32rpx;
margin-left: 32rpx;
border-radius: 32rpx;
.top-background {
position: absolute;
z-index: -1;
width: 686rpx;
height: 464rpx;
border-radius: 32rpx;
}
.switch {
display: flex;
align-items: center;
justify-content: center;
width: 250rpx;
height: 250rpx;
margin-top: 20rpx;
margin-left: 218rpx;
background: #ffffff;
border-radius: 50%;
box-shadow: 0 8rpx 36rpx 0 rgba(0, 0, 0, 0.12);
}
.power {
float: right;
//width: 100%;
display: flex;
align-items: center;
justify-content: flex-end;
height: 50rpx;
.power-icon {
width: 50rpx;
height: 30rpx;
}
.power-text {
font-size: 32rpx;
}
.power-tips {
width: 40rpx;
height: 40rpx;
}
}
}
.bottom {
position: absolute;
bottom: 0;
display: flex;
align-items: center;
justify-content: space-around;
width: 686rpx;
height: 48rpx;
font-size: 32rpx;
line-height: 48rpx;
color: #4777ee;
background-color: rgba(0, 0, 0, 0.1);
border-radius: 0 0 32rpx 32rpx;
.bottom-side {
display: flex;
align-items: center;
}
.bottom-icon {
width: 32rpx;
height: 32rpx;
margin-right: 10rpx;
}
}
.menu {
width: 686rpx;
margin-top: 32rpx;
margin-left: 32rpx;
font-size: 40rpx;
background-color: #ffffff;
border-radius: 32rpx;
box-shadow: 0 8rpx 36rpx 0 rgba(0, 0, 0, 0.12);
.menu-title {
display: flex;
align-items: center;
padding: 24rpx 32rpx;
}
.menu-image {
width: 40rpx;
height: 40rpx;
margin-right: 40rpx;
}
.menu-main {
display: flex;
flex-wrap: wrap;
align-items: center;
padding-top: 32rpx;
margin-left: 43rpx;
font-size: 28rpx;
text-align: center;
.menu-main-view {
width: 150rpx;
margin-bottom: 48rpx;
.menu-main-image {
width: 48rpx;
height: 48rpx;
margin-bottom: 10rpx;
}
}
}
}
.setting {
display: flex;
align-items: center;
width: 686rpx;
padding: 24rpx 0;
margin-top: 32rpx;
margin-left: 32rpx;
font-size: 40rpx;
background-color: #ffffff;
border-radius: 32rpx;
box-shadow: 0 8rpx 36rpx 0 rgba(0, 0, 0, 0.12);
.setting-text {
margin-left: 32rpx;
}
.setting-arrow {
width: 48rpx;
height: 48rpx;
margin-right: 32rpx;
margin-left: auto;
}
.setting-image {
width: 40rpx;
height: 40rpx;
margin-left: 32rpx;
}
}
.not-devices-box {
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
color: gray;
font-size: 24rpx;
width: 100%;
height: 100vh;
gap: 20rpx;
position: relative;
image {
width: 320rpx;
height: 240rpx;
}
.add-new-lock {
background-color: #4777ee;
color: white;
}
.button-add-big {
position: absolute;
width: 88rpx;
height: 88rpx;
right: 55rpx;
bottom: 55rpx;
}
}
.select-lock-row {
display: flex;
align-items: center;
justify-content: space-between;
border-radius: 15rpx;
padding: 8rpx 24rpx;
.lock-name {
display: flex;
align-items: center;
justify-content: center;
gap: 18rpx;
border-radius: 50rpx;
margin-top: 24rpx;
margin-bottom: 32rpx;
font-size: 32rpx;
background-color: #4777ee;
color: white;
padding: 12rpx 32rpx;
.icon-down {
width: 32rpx;
height: 32rpx;
}
}
.power {
display: flex;
align-items: center;
gap: 12rpx;
.power-icon {
width: 50rpx;
height: 30rpx;
}
.power-text {
font-size: 32rpx;
}
.power-tips {
width: 40rpx;
height: 40rpx;
}
}
}
.open-lock-btn {
display: flex;
justify-content: center;
position: relative;
height: 480rpx;
margin-top: 18rpx;
margin-bottom: 18rpx;
.open-lock-btn-image-1 {
width: 480rpx;
height: 480rpx;
position: absolute;
}
.open-lock-btn-image-2 {
width: 480rpx;
height: 480rpx;
position: absolute;
transition: transform 0.6s ease-in-out;
}
}
/* 旋转动画 class */
.rotate {
animation: rotate 2s linear infinite; /* 无限旋转动画 */
}
@keyframes rotate {
from {
transform: rotate(0deg);
}
to {
transform: rotate(360deg);
}
}
.switch-text {
margin-top: 35rpx;
font-size: 32rpx;
color: #414141;
text-align: center;
}
.other-text {
display: flex;
align-items: center;
justify-content: center;
gap: 15rpx;
margin-top: 35rpx;
.bottom-side {
display: flex;
align-items: center;
font-size: 32rpx;
gap: 10rpx;
}
.bottom-icon {
width: 48rpx;
height: 48rpx;
}
}
</style>