2025-07-29 11:07:43 +08:00

792 lines
23 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 v-if="!pending">
<view v-if="isLogin">
<scroll-view
v-if="deviceInfo"
scroll-y="true"
:style="{ height: deviceInfo.windowHeight + 'px' }"
lower-threshold="100"
@refresherrefresh="refresherList"
:refresher-enabled="true"
@scrolltolower="nextPage"
:refresher-triggered="refresherTriggered"
>
<view
class="search"
v-if="!(lockList.length === 0 && lockSearch.searchStr === '' && !focus)"
>
<up-search
searchIconSize="48rpx"
:inputStyle="{ fontSize: '32rpx' }"
@focus="getFocus"
@blur="getBlur"
height="80rpx"
placeholder="搜索"
:clearabled="false"
@change="changeSearch"
v-model="lockSearch.searchStr"
bgColor="#ffffff"
:showAction="false"
maxlength="50"
></up-search>
</view>
<view class="lock-list" v-if="!pending">
<view v-if="lockList.length === 0 && lockSearch.searchStr === '' && !focus">
<image
src="https://cos-lock.skychip.top/mp/icon_add_round.png"
mode="aspectFill"
class="button-add-big"
@click="toSearchDevice"
></image>
<view class="text">添加锁时,手机必须在锁旁边</view>
</view>
<view v-else>
<view class="group" v-for="(group, groupIndex) in lockList" :key="group.groupId">
<view class="group-name">
<view class="group-name-text">{{ group.groupName }}</view>
<view class="group-name-line"></view>
</view>
<up-swipe-action>
<up-swipe-action-item
class="lock"
:ref="'swipeItem' + groupIndex"
:options="options"
v-for="(lock, lockIndex) in group.lockList"
:key="lock.lockId"
:threshold="50"
@click="deleteLock(lock, groupIndex, lockIndex)"
>
<view class="lock" @click="toLockDetail(lock)">
<view class="lock-top">
<image
class="lock-image-lock"
src="https://cos-lock.skychip.top/mp/icon_lock.png"
></image>
<view class="lock-top-right">
<view class="lock-top-right-power">
<image
class="lock-top-right-power-image"
:src="getPowerIcon(lock.electricQuantity)"
></image>
<view class="lock-top-right-power-text"
>{{ lock.electricQuantity }}%</view
>
</view>
<view>{{ getRole(lock.userType, lock.keyRight) }}</view>
</view>
</view>
<view class="lock-name">{{ lock.lockAlias }}</view>
<view style="display: flex; align-items: center">
<view v-if="lock.keyStatus === 110412" class="lock-status">已过期</view>
<view v-if="lock.days" class="lock-status" style="background-color: #63b8af"
>余{{ lock.days }}天</view
>
<view
v-if="lock.keyStatus === 110403"
class="lock-status"
style="background-color: #63b8af"
>未生效</view
>
<view v-if="lock.keyStatus === 110405" class="lock-status">已冻结</view>
<view
v-if="
(lock.remoteEnable === 1 && lock.keyRight === 0) ||
(lock.lockSetting.remoteUnlock === 1 && lock.keyRight === 1)
"
class="lock-status"
style="background-color: #63b8af"
>远程开锁
</view>
</view>
<view
class="lock-time"
:style="{
padding:
lock.keyStatus === 110401 &&
!lock.days &&
!(
(lock.remoteEnable === 1 && lock.keyRight === 0) ||
(lock.lockSetting.remoteUnlock === 1 && lock.keyRight === 1)
) === 0
? '12rpx 24rpx 0 24rpx'
: '6rpx 24rpx 0 24rpx'
}"
>
<view
v-if="lock.keyType === 1 || lock.keyType === 3"
style="font-size: 32rpx"
>{{ getTimeLimit(lock.keyType) }}</view
>
<view v-else>
<view>{{ timeFormat(lock.startDate, 'yyyy-mm-dd h:M') }}</view>
<view>{{
timeFormat(lock.endDate, 'yyyy-mm-dd h:M ') + getTimeLimit(lock.keyType)
}}</view>
</view>
</view>
</view>
</up-swipe-action-item>
</up-swipe-action>
</view>
</view>
</view>
</scroll-view>
<image
v-if="lockList.length !== 0"
src="https://cos-lock.skychip.top/mp/icon_add_round.png"
mode="aspectFill"
class="button-add"
@click="toSearchDevice"
></image>
</view>
<view v-else>
<view class="tips">因智能门锁与账号绑定,登录为手机号登录</view>
<label for="phone">
<view class="button-login">登录</view>
</label>
</view>
</view>
<button
open-type="getPhoneNumber"
style="display: none"
id="phone"
@getphonenumber="getphonenumber"
></button>
<up-modal
:show="showModal"
title="是否删除授权管理员钥匙?"
:showCancelButton="true"
width="600rpx"
@cancel="cancelModal"
@confirm="confirmModal"
>
<view class="slot-content" @click="changeRadio">
<view style="display: flex; align-items: center">
<radio :checked="checked"></radio>
<view>同时删除其发送的所有钥匙钥匙删除后不能恢复</view>
</view>
</view>
</up-modal>
</view>
</template>
<script>
import { timeFormat } from 'uview-plus'
import { mapState, mapActions } from 'pinia'
import { loginRequest } from '@/api/user'
import { useUserStore } from '@/stores/user'
import { useLockStore } from '@/stores/lock'
import { useBluetoothStore } from '@/stores/bluetooth'
import { useBasicStore } from '@/stores/basic'
import { deleteKeyRequest } from '@/api/key'
import { deleteLockRequest } from '@/api/lock'
import { setStorage, getStorage } from '@/utils/storage'
export default {
data() {
return {
refresherTriggered: false,
focus: false,
pending: true,
deviceInfo: null,
options: [
{
text: '删除',
style: {
backgroundColor: '#f56c6c'
}
}
],
showModal: false,
checked: false,
deleteLockInfo: null
}
},
computed: {
...mapState(useUserStore, ['userInfo', 'isLogin']),
...mapState(useLockStore, ['lockList', 'lockTotal', 'lockSearch']),
...mapState(useBluetoothStore, [
'bluetoothStatus',
'isInitBluetooth',
'keyId',
'currentLockInfo'
])
},
async onLoad(data) {
uni.showLoading({
title: '加载中',
mask: true
})
// #ifdef MP-WEIXIN
const accountInfo = uni.getAccountInfoSync()
getApp().globalData.appid = accountInfo.miniProgram.appId
getApp().globalData.envVersion = accountInfo.miniProgram.envVersion
// #endif
this.deviceInfo = await this.getDeviceInfo()
const token = getStorage('token')
if (token) {
await Promise.all([this.getLockList(this.lockSearch), this.getUserInfo()]).then(res => {
this.pending = false
uni.hideLoading()
const list = getStorage('lockList')
const userInfo = getStorage('userInfo')
if (res[0].code === -1 && res[1] === -1 && list && userInfo) {
uni.showToast({
title: '网络访问失败,请检查网络是否正常',
icon: 'none'
})
this.updateLockList(list)
this.updateUserInfo(userInfo)
this.updateLoginStatus(true)
}
})
} else {
await this.homeLogin()
this.pending = false
uni.hideLoading()
console.log('登录成功')
}
// #ifdef MP-WEIXIN
const _data = JSON.parse(JSON.stringify(data))
this.shareJump(_data)
// #endif
},
methods: {
timeFormat,
...mapActions(useUserStore, [
'updateUserInfo',
'updateLoginStatus',
'phoneLogin',
'getUserInfo'
]),
...mapActions(useLockStore, [
'getLockList',
'updateLockList',
'getRole',
'getTimeLimit',
'updateLockSearch',
'getPowerIcon'
]),
...mapActions(useBluetoothStore, [
'getBluetoothStatus',
'initAndListenBluetooth',
'updateCurrentLockInfo',
'checkSetting',
'updateKeyId',
'resetDevice'
]),
...mapActions(useBasicStore, ['routeJump', 'getDeviceInfo', 'getNetworkType', 'shareJump']),
async deleteLock(lock, groupIndex, lockIndex) {
const that = this
const netWork = await this.getNetworkType()
if (!netWork) {
return
}
that.$refs['swipeItem' + groupIndex][lockIndex].closeHandler()
if (lock.userType !== 110301 && lock.keyRight === 1) {
this.deleteLockInfo = lock
this.showModal = true
return
}
const message =
lock.userType === 110301
? '删除锁后,所有信息都会一起删除,确定删除锁吗?'
: '确定删除该钥匙吗?'
const data = {
...lock,
name: lock.bluetooth.bluetoothDeviceName,
deviceId: lock.bluetooth.bluetoothDeviceId,
commKey: lock.bluetooth.privateKey,
signKey: lock.bluetooth.signKey,
publicKey: lock.bluetooth.publicKey
}
this.updateKeyId(lock.keyId.toString())
this.updateCurrentLockInfo(data)
uni.showModal({
title: '提示',
content: message,
async success(res) {
if (res.confirm) {
uni.showLoading({
title: '删除中',
mask: true
})
if (that.currentLockInfo.userType === 110301) {
const { code: resetDeviceCode } = await that.resetDevice({
name: that.currentLockInfo.name,
authUid: that.userInfo.uid.toString(),
keyId: that.keyId.toString()
})
if (resetDeviceCode === 0 || resetDeviceCode === -2) {
const { code, message } = await deleteLockRequest({
lockId: that.currentLockInfo.lockId
})
if (code === 0) {
uni.hideLoading()
that.updateLockSearch({
...that.lockSearch,
pageNo: 1
})
that.getLockList(that.lockSearch)
uni.showToast({
title: '删除成功',
icon: 'none'
})
} else {
uni.hideLoading()
uni.showToast({
title: message,
icon: 'none'
})
}
} else if (resetDeviceCode === -1) {
uni.hideLoading()
uni.showToast({
title: '删除失败,请保持在锁附近',
icon: 'none'
})
}
} else {
const { code, message } = await deleteKeyRequest({
keyId: that.keyId
})
if (code === 0) {
uni.hideLoading()
that.updateLockSearch({
...that.lockSearch,
pageNo: 1
})
that.getLockList(that.lockSearch)
uni.showToast({
title: '删除成功',
icon: 'none'
})
} else {
uni.hideLoading()
uni.showToast({
title: message,
icon: 'none'
})
}
}
}
}
})
},
async confirmModal() {
uni.showLoading({
title: '删除中',
mask: true
})
const that = this
const { code, message } = await deleteKeyRequest({
keyId: that.deleteLockInfo.keyId,
includeUnderlings: that.checked ? 1 : 0
})
that.showModal = false
if (code === 0) {
uni.hideLoading()
that.updateLockSearch({
...that.lockSearch,
pageNo: 1
})
that.getLockList(that.lockSearch)
uni.showToast({
title: '删除成功',
icon: 'none'
})
} else {
uni.hideLoading()
uni.showToast({
title: message,
icon: 'none'
})
}
},
homeLogin() {
const that = this
return new Promise(resolve => {
// #ifdef APP-PLUS
that.routeJump({
name: 'login',
type: 'reLaunch'
})
// #endif
// #ifdef MP-WEIXIN
uni.login({
provider: 'weixin',
async success(loginRes) {
const { code, data } = await loginRequest({
js_code: loginRes.code
})
if (code === 0) {
await setStorage('openid', data.openid)
if (data.accessToken) {
setStorage('token', data.accessToken)
that.getLockList(that.lockSearch)
await that.getUserInfo()
that.updateLoginStatus(true)
resolve(true)
} else {
that.updateLoginStatus(false)
resolve(false)
}
} else {
uni.showToast({
title: '登录失败,请重试',
icon: 'none'
})
resolve(false)
}
},
fail() {
uni.showToast({
title: '登录失败,请重试',
icon: 'none'
})
resolve(false)
}
})
// #endif
resolve(false)
})
},
changeRadio() {
this.checked = !this.checked
},
cancelModal() {
this.showModal = false
this.checked = false
},
async getphonenumber(data) {
if (data.detail.errMsg === 'getPhoneNumber:fail user deny') {
return
}
const that = this
uni.showLoading({
title: '登录中'
})
uni.login({
provider: 'weixin',
async success(loginRes) {
const result = await that.phoneLogin({
encryptedData: data.detail.encryptedData,
iv: data.detail.iv,
code: loginRes.code
})
uni.hideLoading()
if (!result) {
uni.showToast({
title: result.message,
icon: 'none'
})
}
},
fail() {
uni.hideLoading()
uni.showToast({
title: '登录失败,请重试'
})
}
})
},
async nextPage() {
if (this.lockList.length < this.lockTotal) {
const search = {
...this.lockSearch,
pageNo: this.lockSearch.pageNo + 1
}
const { code, message } = await this.getLockList(search)
if (code !== 0) {
uni.showToast({
title: message,
icon: 'none'
})
this.updateLockSearch(search)
}
}
},
async refresherList() {
this.refresherTriggered = true
this.getUserInfo()
this.updateLockSearch({
...this.lockSearch,
pageNo: 1
})
const { code, message } = await this.getLockList(this.lockSearch)
if (code === 0) {
uni.showToast({
title: '刷新成功',
icon: 'none'
})
} else {
uni.showToast({
title: message,
icon: 'none'
})
}
this.refresherTriggered = false
},
async changeSearch(data) {
this.updateLockSearch({
...this.lockSearch,
searchStr: data
})
await this.getLockList(this.lockSearch)
},
getFocus() {
this.focus = true
},
getBlur() {
this.focus = false
},
async toSearchDevice() {
this.routeJump({
name: 'addLockGuid'
})
},
async toLockDetail(lock) {
if (!(this.bluetoothStatus === 0 || this.bluetoothStatus === -1)) {
this.getBluetoothStatus()
return
}
if (lock.keyStatus === 110403) {
uni.showToast({
title: '您的钥匙未生效',
icon: 'none'
})
return
}
if (lock.keyStatus === 110412) {
uni.showToast({
title: '钥匙已过期',
icon: 'none'
})
return
}
if (lock.keyStatus === 110405) {
uni.showToast({
title: '钥匙已冻结',
icon: 'none'
})
return
}
let result = true
if (!this.isInitBluetooth) {
result = await this.initAndListenBluetooth()
}
if (result) {
const data = {
...lock,
name: lock.bluetooth.bluetoothDeviceName,
deviceId: lock.bluetooth.bluetoothDeviceId,
commKey: lock.bluetooth.privateKey,
signKey: lock.bluetooth.signKey,
publicKey: lock.bluetooth.publicKey
}
this.updateKeyId(lock.keyId.toString())
this.updateCurrentLockInfo(data)
this.routeJump({
name: 'lockDetail'
})
} else {
this.checkSetting()
}
}
}
}
</script>
<style lang="scss">
page {
background-color: $uni-bg-color-grey;
}
.u-swipe-action {
display: flex;
flex-wrap: wrap;
justify-content: space-between;
width: 672rpx;
padding-bottom: 32rpx;
overflow: inherit !important;
}
.u-swipe-action-item {
width: 320rpx;
height: 300rpx;
margin-top: 32rpx;
overflow: inherit !important;
border-radius: 32rpx !important;
}
.u-swipe-action-item__right {
border-radius: 32rpx !important;
}
.u-swipe-action-item__content {
border-radius: 32rpx !important;
}
.u-swipe-action-item__right__button {
border-radius: 32rpx !important;
}
</style>
<style scoped lang="scss">
.search {
width: 686rpx !important;
margin-top: 32rpx;
margin-left: 32rpx;
}
.button-login {
width: 650rpx;
height: 120rpx;
margin-left: 50rpx;
font-size: 48rpx;
font-weight: bold;
line-height: 120rpx;
color: #ffffff;
text-align: center;
background: #4777ee;
border-radius: 46rpx;
}
.button-add {
position: fixed;
right: 50rpx;
bottom: 50rpx;
width: 120rpx;
height: 120rpx;
border-radius: 50%;
}
.lock-list {
display: flex;
flex-wrap: wrap;
justify-content: space-between;
width: 672rpx;
padding-bottom: 32rpx;
margin-left: 39rpx;
.lock {
width: 320rpx;
height: 300rpx;
background: #ffffff;
border-radius: 32rpx;
box-shadow: 0 8rpx 36rpx 0 rgba(0, 0, 0, 0.12);
.lock-name {
display: -webkit-box;
padding: 0 24rpx;
overflow: hidden;
text-overflow: ellipsis;
-webkit-line-clamp: 2;
font-size: 32rpx;
font-weight: bold;
line-height: 38rpx;
word-break: break-all;
white-space: normal;
-webkit-box-orient: vertical;
}
.lock-time {
padding: 6rpx 24rpx 0 24rpx;
font-size: 22rpx;
font-weight: bold;
}
.lock-top {
display: flex;
align-items: center;
justify-content: space-between;
padding: 24rpx 24rpx 12rpx 24rpx;
font-size: 22rpx;
.lock-image-lock {
width: 64rpx;
height: 64rpx;
}
.lock-top-right-power {
display: flex;
align-items: center;
.lock-top-right-power-image {
width: 40rpx;
height: 24rpx;
margin-right: 10rpx;
}
}
}
}
.group {
display: flex;
flex-wrap: wrap;
justify-content: space-between;
width: 672rpx;
margin-top: 32rpx;
.group-name {
display: flex;
align-items: center;
width: 672rpx;
font-size: 32rpx;
font-weight: bold;
}
.group-name-text {
padding: 0 32rpx;
word-break: break-all;
}
.group-name-line {
flex-grow: 1;
height: 3rpx;
background: #e5e5e5;
}
}
}
.button-add-big {
width: 400rpx;
height: 400rpx;
margin-top: 300rpx;
margin-left: 136rpx;
}
.text {
width: 672rpx;
margin-top: 32rpx;
font-size: 32rpx;
font-weight: bold;
color: #999999;
text-align: center;
}
.tips {
padding: 32rpx 0;
margin-top: 40vh;
font-size: 28rpx;
color: #999999;
text-align: center;
}
.lock-status {
display: inline-block;
padding: 4rpx 8rpx;
margin-top: 5rpx;
margin-left: 24rpx;
font-size: 22rpx;
font-weight: bold;
color: #ffffff;
text-align: center;
background-color: #ecab1f;
border-radius: 8rpx;
}
</style>