wx-starlock/pages/lockDetail/lockDetail.vue
2025-02-07 16:28:45 +08:00

518 lines
14 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>
<view class="days" v-if="currentLockInfo.days">钥匙将在{{ currentLockInfo.days }}天后失效</view>
<view class="lock-name">{{ currentLockInfo.lockAlias }}</view>
<view class="top">
<image
class="top-background"
src="/static/images/background_main.jpg"
mode="aspectFill"
></image>
<view style="width: 100%; height: 50rpx">
<view class="power" @click="powerTip">
<image
class="power-icon"
:src="getPowerIcon(currentLockInfo.electricQuantity)"
mode="aspectFill"
></image>
<view class="power-text">{{ currentLockInfo.electricQuantity }}%</view>
<image class="power-tips" src="/static/images/icon_tips.png" mode="aspectFill"></image>
</view>
</view>
<view class="switch" @click="openDoorOperate('open')" @longpress="openDoorOperate('close')">
<SwitchLoading :size="220" ref="loading"></SwitchLoading>
</view>
<view class="switch-text">点击开锁长按闭锁</view>
<view class="bottom">
<view class="bottom-side">
<image class="bottom-icon" src="/static/images/icon_role.png" mode="aspectFill"></image>
<view>{{ getRole(currentLockInfo.userType, currentLockInfo.keyRight) }}</view>
</view>
<view class="bottom-side">
<image
class="bottom-icon"
:src="
currentLockInfo.lockSetting.appUnlockOnline
? '/static/images/icon_cloud_active.png'
: '/static/images/icon_cloud.png'
"
mode="aspectFill"
style="width: 40rpx; height: 40rpx"
></image>
<view
:style="{ color: currentLockInfo.lockSetting.appUnlockOnline ? '#63b8af' : '#a3a3a3' }"
>手机需联网</view
>
</view>
</view>
</view>
<view class="menu" v-if="currentLockInfo.keyRight === 1">
<view class="menu-title">
<image class="menu-image" src="/static/images/icon_menu.png"></image>
<view>功能</view>
</view>
<view class="menu-main">
<view
v-if="currentLockInfo.keyRight === 1"
class="menu-main-view"
@click="routeJump({ name: 'keyList' })"
>
<image class="menu-main-image" src="/static/images/tabbar_key_select.png"></image>
<view>电子钥匙</view>
</view>
<view
v-if="currentLockInfo.lockFeature.password || currentLockInfo.keyRight === 1"
class="menu-main-view"
@click="routeJump({ name: 'passwordList' })"
>
<image class="menu-main-image" src="/static/images/icon_lock_transparent.png"></image>
<view>密码</view>
</view>
<view class="menu-main-view" @click="routeJump({ name: 'cardList' })">
<image class="menu-main-image" src="/static/images/icon_card.png"></image>
<view></view>
</view>
<view class="menu-main-view" @click="routeJump({ name: 'fingerprintList' })">
<image class="menu-main-image" src="/static/images/icon_fingerprint.png"></image>
<view>指纹</view>
</view>
<view class="menu-main-view" @click="routeJump({ name: 'keyList' })">
<image class="menu-main-image" src="/static/images/icon_admin_black.png"></image>
<view>授权管理员</view>
</view>
<view class="menu-main-view" @click="routeJump({ name: 'keyList' })">
<image class="menu-main-image" src="/static/images/icon_record.png"></image>
<view>操作记录</view>
</view>
</view>
</view>
<view class="setting" @click="routeJump({ name: 'setting' })">
<image class="setting-image" src="/static/images/icon_setting.png"></image>
<view class="setting-text">设置</view>
<image class="setting-arrow" mode="aspectFill" src="/static/images/icon_arrow.png"></image>
</view>
<up-popup
:show="show"
@close="closePopup"
mode="center"
:closeOnClickOverlay="true"
bgColor="transparent"
>
<view class="popup" @click="closePopup">
<image
class="popup-background"
:src="
type === 'close'
? '/static/images/background_close_door.png'
: '/static/images/background_open_door.png'
"
mode="aspectFill"
/>
<view>
<view class="popup-name">{{ currentLockInfo.lockAlias }}</view>
<view class="popup-time">{{ timeFormat('', 'mm/dd h:M') }}</view>
</view>
</view>
</up-popup>
</view>
</template>
<script>
import { mapState, mapActions } from 'pinia'
import { timeFormat } from 'uview-plus'
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 { getLockNetTokenRequest } from '@/api/lock'
import { deleteKeyRequest } from '@/api/key'
export default {
data() {
return {
time: 0,
onlineToken: '0',
pending: false,
show: false,
type: ''
}
},
computed: {
...mapState(useBluetoothStore, ['currentLockInfo', 'keyId']),
...mapState(useUserStore, ['userInfo']),
...mapState(useLockStore, ['lockSearch'])
},
components: {
SwitchLoading
},
onLoad() {
this.getServeTime()
},
methods: {
timeFormat,
...mapActions(useLockStore, ['getRole', 'updateLockSearch', 'getLockList', 'getPowerIcon']),
...mapActions(useBluetoothStore, [
'openDoor',
'updateServerTimestamp',
'closeBluetoothConnection'
]),
...mapActions(useBasicStore, ['routeJump', 'backAndToast', 'getNetworkType']),
closePopup() {
this.show = false
},
powerTip() {
const that = this
const time = timeFormat(that.currentLockInfo.electricQuantityDate, 'yyyy-mm-dd h:M')
console.log('更新时间', that.currentLockInfo.electricQuantityDate, time)
uni.showModal({
title: '锁电量更新时间',
content: time,
showCancel: false
})
},
async getNetToken() {
const { code, data, message } = await getLockNetTokenRequest({
lockId: this.currentLockInfo.lockId
})
if (code === 0) {
this.onlineToken = data.token
return true
}
uni.showToast({
title: message,
icon: 'none'
})
return false
},
async getServeTime() {
const { code, data } = await this.updateServerTimestamp()
if (code === 0) {
this.time = parseInt((data.date - new Date().getTime()) / 1000, 10)
}
},
async openDoorOperate(type) {
const timestamp = new Date().getTime()
if (
this.currentLockInfo.faceAuthentication === 1 &&
this.currentLockInfo.nextFaceValidateTime <= new Date().getTime() + this.time * 1000 &&
this.currentLockInfo.nextFaceValidateTime !== 0
) {
uni.showModal({
title: '提示',
content: '开门前需进行实名认证小程序暂不支持请使用APP认证开门',
showCancel: false
})
return
}
if (this.pending) {
return
}
if (this.currentLockInfo.lockSetting.appUnlockOnline) {
const netWork = await this.getNetworkType()
if (!netWork) {
return
}
}
uni.vibrateLong()
this.pending = true
this.$refs.loading.open()
if (type === 'close') {
uni.showToast({
title: `正在尝试闭锁……`,
icon: 'none'
})
}
if (this.currentLockInfo.lockSetting.appUnlockOnline) {
const result = await this.getNetToken()
if (!result) {
this.$refs.loading.close()
this.pending = false
return
}
}
let openMode
if (type === 'close') {
openMode = this.currentLockInfo.lockSetting.appUnlockOnline ? 33 : 32
} else {
openMode = this.currentLockInfo.lockSetting.appUnlockOnline ? 1 : 0
}
const { code } = await this.openDoor({
name: this.currentLockInfo.name,
uid: this.userInfo.uid.toString(),
openMode,
openTime: parseInt(new Date().getTime() / 1000, 10) + this.time,
onlineToken: this.onlineToken
})
this.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) {
this.show = true
this.type = type
setTimeout(() => {
this.show = false
}, 3000)
if (this.currentLockInfo.keyType === 3) {
const { code: deleteKeyCode } = await deleteKeyRequest({
keyId: this.keyId
})
if (deleteKeyCode === 0) {
this.updateLockSearch({
...this.lockSearch,
pageNo: 1
})
this.getLockList(this.lockSearch)
this.backAndToast('单次钥匙已在被使用后删除', 1)
}
}
} else if (code === 13) {
uni.showToast({
title: `只能在循环时间内操作门锁`,
icon: 'none'
})
} else if (code === -1) {
uni.showToast({
title: `${type === 'close' ? '关' : '开'}锁失败,请保证在锁附近`,
icon: 'none'
})
}
this.$refs.loading.close()
this.pending = false
}
}
}
</script>
<style lang="scss" scoped>
.popup {
display: flex;
position: relative;
width: 400rpx;
height: 389rpx;
text-align: center;
flex-wrap: wrap;
.popup-background {
z-index: -1;
width: 400rpx;
height: 389rpx;
position: absolute;
}
.popup-name {
margin-left: 30rpx;
z-index: 9;
margin-top: 180rpx;
color: #676b6d;
width: 340rpx;
max-height: 80rpx;
line-height: 40rpx;
word-break: break-all;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 2;
overflow: hidden;
text-overflow: ellipsis;
white-space: normal;
}
.popup-time {
margin-top: 10rpx;
width: 400rpx;
}
}
.days {
line-height: 60rpx;
background: #faecc9;
color: #bc9839;
text-align: center;
font-size: 32rpx;
width: 750rpx;
height: 60rpx;
}
.lock-name {
text-align: center;
font-size: 32rpx;
padding: 32rpx 32rpx 0 32rpx;
font-weight: bold;
word-break: break-all;
}
.top {
margin-top: 32rpx;
margin-left: 32rpx;
width: 686rpx;
height: 464rpx;
border-radius: 32rpx;
position: relative;
.top-background {
z-index: -1;
position: absolute;
width: 686rpx;
height: 464rpx;
border-radius: 32rpx;
}
.switch {
margin-top: 20rpx;
margin-left: 218rpx;
display: flex;
justify-content: center;
align-items: center;
width: 250rpx;
height: 250rpx;
background: #ffffff;
border-radius: 50%;
box-shadow: 0 8rpx 36rpx 0 rgba(0, 0, 0, 0.12);
}
.power {
float: right;
padding-top: 18rpx;
//width: 100%;
display: flex;
align-items: center;
height: 50rpx;
justify-content: flex-end;
.power-icon {
width: 50rpx;
margin-right: 10rpx;
height: 30rpx;
}
.power-text {
font-size: 32rpx;
font-weight: bold;
margin-right: 10rpx;
line-height: 50rpx;
}
.power-tips {
margin-right: 32rpx;
width: 40rpx;
height: 40rpx;
}
}
.switch-text {
margin-top: 10rpx;
text-align: center;
}
}
.bottom {
width: 686rpx;
position: absolute;
bottom: 0;
display: flex;
align-items: center;
background-color: rgba(0, 0, 0, 0.1);
height: 48rpx;
line-height: 48rpx;
font-size: 32rpx;
color: #63b8af;
border-radius: 0 0 32rpx 32rpx;
justify-content: space-around;
.bottom-side {
display: flex;
align-items: center;
}
.bottom-icon {
width: 32rpx;
height: 32rpx;
margin-right: 10rpx;
}
}
.menu {
margin-top: 32rpx;
margin-left: 32rpx;
width: 686rpx;
background-color: #ffffff;
border-radius: 32rpx;
box-shadow: 0 8rpx 36rpx 0 rgba(0, 0, 0, 0.12);
font-size: 40rpx;
.menu-title {
padding: 24rpx 32rpx;
display: flex;
align-items: center;
}
.menu-image {
margin-right: 40rpx;
width: 40rpx;
height: 40rpx;
}
.menu-main {
padding-top: 32rpx;
display: flex;
align-items: center;
font-size: 28rpx;
flex-wrap: wrap;
text-align: center;
margin-left: 43rpx;
.menu-main-view {
width: 150rpx;
margin-bottom: 48rpx;
.menu-main-image {
filter: sepia(100%) saturate(10000%) hue-rotate(180deg) brightness(0.1);
margin-bottom: 10rpx;
width: 40rpx;
height: 40rpx;
}
}
}
}
.setting {
padding: 24rpx 0;
margin-top: 32rpx;
margin-left: 32rpx;
display: flex;
align-items: center;
width: 686rpx;
background-color: #ffffff;
border-radius: 32rpx;
box-shadow: 0 8rpx 36rpx 0 rgba(0, 0, 0, 0.12);
font-size: 40rpx;
.setting-text {
margin-left: 32rpx;
}
.setting-arrow {
margin-right: 32rpx;
width: 48rpx;
height: 48rpx;
margin-left: auto;
}
.setting-image {
margin-left: 32rpx;
width: 48rpx;
height: 48rpx;
}
}
</style>