wx-starlock/pages/main/lockDetail.vue
2025-03-28 10:00:27 +08:00

664 lines
18 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>
<scroll-view
v-if="deviceInfo"
scroll-y="true"
:style="{ height: deviceInfo.screenHeight - deviceInfo.safeArea.top + 'px' }"
@refresherrefresh="refresher"
:refresher-enabled="true"
:refresher-triggered="refresherTriggered"
>
<view class="days" v-if="$bluetooth.currentLockInfo.days"
>钥匙将在{{ $bluetooth.currentLockInfo.days }}天后失效</view
>
<view class="lock-name">{{ $bluetooth.currentLockInfo.lockAlias }}</view>
<view class="top">
<image
class="top-background"
src="https://oss-lock.xhjcn.ltd/mp/background_main.jpg"
mode="aspectFill"
></image>
<view style="width: 100%; height: 50rpx">
<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://oss-lock.xhjcn.ltd/mp/icon_tips.png"
mode="aspectFill"
></image>
</view>
</view>
<view class="switch" @click="openDoorOperate('open')" @longpress="openDoorOperate('close')">
<SwitchLoading :size="220" ref="sLoading"></SwitchLoading>
</view>
<view class="switch-text">点击开锁长按闭锁</view>
<view class="bottom">
<view class="bottom-side">
<image
class="bottom-icon"
src="https://oss-lock.xhjcn.ltd/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://oss-lock.xhjcn.ltd/mp/icon_cloud_active.png'
: 'https://oss-lock.xhjcn.ltd/mp/icon_cloud.png'
"
mode="aspectFill"
style="width: 40rpx; height: 40rpx"
></image>
<view
:style="{
color: $bluetooth.currentLockInfo.appUnlockOnline ? '#63b8af' : '#a3a3a3'
}"
>手机需联网</view
>
</view>
</view>
</view>
<view class="menu">
<view class="menu-title">
<image class="menu-image" src="https://oss-lock.xhjcn.ltd/mp/icon_menu.png"></image>
<view>功能</view>
</view>
<view class="menu-main">
<view
v-if="$bluetooth.currentLockInfo.keyRight === 1"
class="menu-main-view"
@click="$basic.routeJump({ name: 'keyList' })"
>
<image
class="menu-main-image transform-scale-90"
src="https://oss-lock.xhjcn.ltd/mp/icon_key.png"
></image>
<view>电子钥匙</view>
</view>
<view
v-if="
$bluetooth.currentLockInfo.lockFeature.password &&
$bluetooth.currentLockInfo.keyRight === 1
"
class="menu-main-view"
@click="$basic.routeJump({ name: 'passwordList' })"
>
<image
class="menu-main-image"
src="https://oss-lock.xhjcn.ltd/mp/icon_password_green.png"
></image>
<view>密码</view>
</view>
<view
v-if="
$bluetooth.currentLockInfo.lockFeature.icCard &&
$bluetooth.currentLockInfo.keyRight === 1
"
class="menu-main-view"
@click="$basic.routeJump({ name: 'cardList' })"
>
<image
class="menu-main-image transform-scale-110"
src="https://oss-lock.xhjcn.ltd/mp/icon_card.png"
></image>
<view>卡</view>
</view>
<view
v-if="
$bluetooth.currentLockInfo.lockFeature.fingerprint &&
$bluetooth.currentLockInfo.keyRight === 1
"
class="menu-main-view"
@click="$basic.routeJump({ name: 'fingerprintList' })"
>
<image
class="menu-main-image"
src="https://oss-lock.xhjcn.ltd/mp/icon_fingerprint.png"
></image>
<view>指纹</view>
</view>
<view
v-if="
$bluetooth.currentLockInfo.lockFeature.bluetoothRemoteControl &&
$bluetooth.currentLockInfo.keyRight === 1
"
class="menu-main-view transform-scale-110"
@click="$basic.routeJump({ name: 'remoteList' })"
>
<image
class="menu-main-image"
src="https://oss-lock.xhjcn.ltd/mp/icon_remote.png"
></image>
<view>遥控</view>
</view>
<view
v-if="
$bluetooth.currentLockInfo.lockFeature.d3Face &&
$bluetooth.currentLockInfo.keyRight === 1
"
class="menu-main-view"
@click="$basic.routeJump({ name: 'faceList' })"
>
<image
class="menu-main-image"
src="https://oss-lock.xhjcn.ltd/mp/icon_face.png"
></image>
<view>人脸</view>
</view>
<view
v-if="
$bluetooth.currentLockInfo.lockFeature.palmVein &&
$bluetooth.currentLockInfo.keyRight === 1
"
class="menu-main-view"
@click="$basic.routeJump({ name: 'palmVeinList' })"
>
<image
class="menu-main-image"
src="https://oss-lock.xhjcn.ltd/mp/icon_palm_vein.png"
></image>
<view>掌静脉</view>
</view>
<view
v-if="$bluetooth.currentLockInfo.userType === 110301"
class="menu-main-view"
@click="$basic.routeJump({ name: 'adminList' })"
>
<image
class="menu-main-image transform-scale-140"
src="https://oss-lock.xhjcn.ltd/mp/icon_admin_black.png"
></image>
<view>授权管理员</view>
</view>
<view
class="menu-main-view transform-scale-105"
@click="$basic.routeJump({ name: 'recordList' })"
>
<image
class="menu-main-image"
src="https://oss-lock.xhjcn.ltd/mp/icon_record.png"
></image>
<view>操作记录</view>
</view>
<view
v-if="$bluetooth.currentLockInfo.keyRight === 1"
class="menu-main-view"
@click="$basic.routeJump({ name: 'messageReminder' })"
>
<image
class="menu-main-image transform-scale-110"
src="https://oss-lock.xhjcn.ltd/mp/icon_remind.png"
></image>
<view>消息提醒</view>
</view>
</view>
</view>
<view class="setting" @click="$basic.routeJump({ name: 'setting' })">
<image class="setting-image" src="https://oss-lock.xhjcn.ltd/mp/icon_setting.png"></image>
<view class="setting-text">设置</view>
<image
class="setting-arrow"
mode="aspectFill"
src="https://oss-lock.xhjcn.ltd/mp/icon_arrow.png"
></image>
</view>
</scroll-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, 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 } from '@/api/lock'
import { deleteKeyRequest } from '@/api/key'
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)
onMounted(async () => {
deviceInfo.value = await $basic.getDeviceInfo()
await getServeTime()
})
const openDoorOperate = async type => {
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
})
return
}
if (pending.value) {
return
}
if ($bluetooth.currentLockInfo.appUnlockOnline && type !== 'close') {
const netWork = await $basic.getNetworkType()
if (!netWork) {
return
}
}
uni.vibrateLong()
pending.value = true
sLoading.value.open()
if (type === 'close') {
uni.showToast({
title: `正在尝试闭锁……`,
icon: 'none'
})
}
if ($bluetooth.currentLockInfo.appUnlockOnline && type !== 'close') {
const result = await getNetToken()
if (!result) {
sLoading.value.close()
pending.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
})
$bluetooth
.syncRecord({
keyId: $bluetooth.currentLockInfo.keyId.toString(),
uid: $user.userInfo.uid.toString()
})
.then(() => {
$bluetooth.closeBluetoothConnection()
})
if (type === 'open') {
uni.reportEvent('open_door', {
result: code,
duration: new Date().getTime() - timestamp
})
} else if (type === 'close') {
uni.reportEvent('close_door', {
result: code,
duration: new Date().getTime() - timestamp
})
}
if (code === 0) {
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'
})
}
sLoading.value.close()
pending.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
}
</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;
}
.lock-name {
padding: 32rpx 32rpx 0 32rpx;
font-size: 32rpx;
font-weight: bold;
text-align: center;
word-break: break-all;
}
.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;
padding-top: 18rpx;
.power-icon {
width: 50rpx;
height: 30rpx;
margin-right: 10rpx;
}
.power-text {
margin-right: 10rpx;
font-size: 32rpx;
font-weight: bold;
line-height: 50rpx;
}
.power-tips {
width: 40rpx;
height: 40rpx;
margin-right: 32rpx;
}
}
.switch-text {
margin-top: 10rpx;
text-align: center;
}
}
.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: #63b8af;
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: 40rpx;
height: 40rpx;
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;
}
}
</style>