Merge branch 'fanpeng' into 'master'

完成绑定设备全部功能

See merge request StarlockTeam/wx-starlock!2
This commit is contained in:
范鹏 2024-09-02 06:26:31 +00:00
commit 48c785918b
55 changed files with 4393 additions and 583 deletions

21
App.vue
View File

@ -10,6 +10,25 @@
//
updateIsLogin(isLogin) {
useUserStore().updateLoginStatus(isLogin)
},
//
appid: '',
//
envVersion: '',
//
getEnvConfig() {
const envVserionStorage = uni.getStorageSync('envVersion')
if(envVserionStorage) {
return envVserionStorage
} else {
if(this.envVersion === 'develop') {
return 'DEV'
} else if(this.envVersion === 'trial') {
return 'PRE'
} else {
return 'PROD'
}
}
}
},
computed: {
@ -22,7 +41,7 @@
const checkResult = await this.checkSetting()
console.log(checkResult)
if(checkResult === true) {
this.initAndListenBluetooth()
this.initAndListenBluetooth(false)
}
},
onShow() {

12
api/check.js Normal file
View File

@ -0,0 +1,12 @@
import request from '../utils/request'
// check 检查模块
// 获取服务器时间
export function getServerDatetime(data) {
return request({
url: '/check/getServerDatetime',
method: 'POST',
data
})
}

12
api/geocode.js Normal file
View File

@ -0,0 +1,12 @@
import request from '../utils/request'
// geocode 地理编码模块
// 获取地址信息
export function getGeocodeAddress(data) {
return request({
url: '/geocode/getAddress',
method: 'POST',
data
})
}

57
api/key.js Normal file
View File

@ -0,0 +1,57 @@
import request from '../utils/request'
// key 电子钥匙模块
// 获取电子钥匙列表
export function getKeyListRequest(data) {
return request({
url: '/key/listUser',
method: 'POST',
data
})
}
// 重置电子钥匙
export function resetKeyRequest(data) {
return request({
url: '/key/reset',
method: 'POST',
data
})
}
// 创建电子钥匙
export function createKeyRequest(data) {
return request({
url: '/v2/key/send',
method: 'POST',
data
})
}
// 删除电子钥匙
export function deleteKeyRequest(data) {
return request({
url: '/key/delete',
method: 'POST',
data
})
}
// 更新锁用户
export function updateLockUserNoRequest(data) {
return request({
url: '/key/updateLockUserNo',
method: 'POST',
data
})
}
// 获取所有锁用户
export function getUserNoListRequest(data) {
return request({
url: '/key/getUserNoList',
method: 'POST',
data
})
}

39
api/keyboardPwd.js Normal file
View File

@ -0,0 +1,39 @@
import request from '../utils/request'
// keyboardPwd 锁密码模块
// 获取密码列表
export function getPsaawordListRequest(data) {
return request({
url: '/keyboardPwd/listSendRecords',
method: 'POST',
data
})
}
// 重置密码
export function resetPsaawordListRequest(data) {
return request({
url: '/keyboardPwd/reset',
method: 'POST',
data
})
}
// 创建密码
export function createPsaawordRequest(data) {
return request({
url: '/keyboardPwd/get',
method: 'POST',
data
})
}
// 删除密码
export function deletePsaawordRequest(data) {
return request({
url: '/keyboardPwd/delete',
method: 'POST',
data
})
}

View File

@ -10,3 +10,30 @@ export function getLockListRequest(data) {
data
})
}
// 绑定锁管理员
export function bindLockAdmin(data) {
return request({
url: '/v2/lock/bindAdmin',
method: 'POST',
data
})
}
// 获取手机联网token
export function getLockNetTokenRequest(data) {
return request({
url: '/lock/getLockNetToken',
method: 'POST',
data
})
}
// 删除智能门锁
export function deleteLockRequest(data) {
return request({
url: '/lock/delete',
method: 'POST',
data
})
}

12
api/lockRecords.js Normal file
View File

@ -0,0 +1,12 @@
import request from '../utils/request'
// lockRecords 锁记录模块
// 电子钥匙开门上报
export function reportOpenDoorRequest(data) {
return request({
url: '/lockRecords/fromLock',
method: 'POST',
data
})
}

12
api/lockSetting.js Normal file
View File

@ -0,0 +1,12 @@
import request from '../utils/request'
// lockSetting 锁设置模块
// 更新锁设置
export function updateLockSettingRequest(data) {
return request({
url: '/lockSetting/updateLockSetting',
method: 'POST',
data
})
}

12
api/room.js Normal file
View File

@ -0,0 +1,12 @@
import request from '../utils/request'
// room 房间模块
// 更新锁电量
export function updateElectricQuantityRequest(data) {
return request({
url: '/room/uploadElectricQuantity',
method: 'POST',
data
})
}

View File

@ -55,3 +55,49 @@ export function getEmailCodeRequest(data) {
data
})
}
// 登录
export function loginRequest(data) {
return request({
url: '/wechat/mini/login',
method: 'POST',
data
})
}
// 注册
export function phoneLoginRequest(data) {
return request({
url: '/wechat/mini/phone/login',
method: 'POST',
data
})
}
// 更换绑定手机号
export function rebindPhoneRequest(data) {
return request({
url: '/wechat/mini/rebindPhone',
method: 'POST',
data
})
}
// 验证码更换密码
export function changePasswordRequest(data) {
return request({
url: '/wechat/mini/changePassword',
method: 'POST',
data
})
}
// 更新云用户时区偏移
export function updateTimezoneOffsetRequest(data) {
return request({
url: '/cloudUser/updateSettings',
method: 'POST',
data
})
}

View File

@ -0,0 +1,75 @@
<template>
<view>
<view @click="changeShow" class="name">
<view class="name-text">{{ title }}</view>
<view class="picker">
{{ timeFormat(time, 'yyyy-mm-dd h:M') }}
</view>
</view>
<up-datetime-picker itemHeight="60" :minDate="minDate" :title="placeholder" :show="show" v-model="time"
mode="datetime" @confirm="confirm" :closeOnClickOverlay="true"
@close="close"></up-datetime-picker>
</view>
</template>
<script>
import { timeFormat } from 'uview-plus'
export default {
name: 'LockDatetimePicker',
props: {
title: String,
value: Number,
minDate: Number,
placeholder: String
},
data() {
return {
time: 0,
show: false
}
},
created() {
this.time = this.value
},
methods: {
timeFormat,
changeShow() {
this.show = !this.show
},
close() {
this.show = false
},
confirm(e) {
this.show = false
this.$emit('changeTime', e.value)
}
}
}
</script>
<style scoped lang="scss">
.name {
height: 100rpx;
width: 750rpx;
display: flex;
align-items: center;
background-color: #ffffff;
font-weight: bold;
font-size: 32rpx;
.name-text {
width: 168rpx;
margin-left: 32rpx;
line-height: 100rpx;
}
.picker {
margin-right: 32rpx;
text-align: right;
width: 518rpx;
height: 100rpx;
line-height: 100rpx;
}
}
</style>

View File

@ -0,0 +1,60 @@
<template>
<view>
<view class="name">
<view class="name-text">{{ title }}</view>
<input :value="value" class="name-input" :placeholder="placeholder" placeholder-class="placeholder-class"
maxlength="16" @input="changeInput"></input>
</view>
</view>
</template>
<script>
export default {
name: 'LockInput',
props:{
title: String,
placeholder: String,
value: String
},
methods: {
changeInput(e) {
this.$emit('changeInput', e.detail.value)
}
},
}
</script>
<style scoped lang="scss">
.name {
height: 100rpx;
width: 750rpx;
display: flex;
align-items: center;
background-color: #ffffff;
.name-text {
width: 168rpx;
margin-left: 32rpx;
font-weight: bold;
font-size: 32rpx;
line-height: 100rpx;
}
.name-input {
margin-right: 32rpx;
text-align: right;
width: 518rpx;
height: 100rpx;
font-size: 32rpx;
line-height: 100rpx;
border: none;
outline: none;
}
}
.placeholder-class {
text-align: right;
font-size: 32rpx;
line-height: 100rpx;
}
</style>

View File

@ -0,0 +1,95 @@
<template>
<view>
<view class="spinner-box" :style="{width: size + 'rpx', height: size + 'rpx'}">
<view v-if="show" class="circle-border" :style="{width: size * 0.75 + 'rpx', height: size * 0.75 + 'rpx'}">
</view>
<view v-else class="circle-border-stop" :style="{width: size * 0.75 + 'rpx', height: size * 0.75 + 'rpx'}"></view>
<view class="circle-core" :style="{width: size * 0.75 - 2 + 'rpx', height: size * 0.75 - 2 + 'rpx'}">
<image src="/static/images/icon_lock_transparent.png" mode="aspectFill" :style="{width: size * 0.35 + 'rpx',
height: size * 0.35 + 'rpx'}"></image>
</view>
</view>
</view>
</template>
<script>
export default {
name: "SwitchLoading",
props:{
size: Number,
},
data () {
return {
show: false
}
},
methods: {
open () {
this.show = true
},
close () {
this.show = false
}
}
};
</script>
<style>
@keyframes spin {
from {
transform: rotate(0);
}
to {
transform: rotate(359deg);
}
}
.spinner-box {
display: flex;
justify-content: center;
align-items: center;
background-color: transparent;
position: relative;
}
.circle-border {
padding: 3upx;
display: flex;
justify-content: center;
align-items: center;
border-radius: 50%;
background: rgb(99, 184, 175);
background: linear-gradient(
0deg,
rgba(99, 184, 175, 0.1) 33%,
rgba(99, 184, 175, 1) 100%
);
animation: spin 0.8s linear 0s infinite;
position: absolute;
z-index: 0;
}
.circle-border-stop {
padding: 3upx;
display: flex;
justify-content: center;
align-items: center;
border-radius: 50%;
background: rgb(99, 184, 175);
position: absolute;
z-index: 0;
}
.circle-core {
z-index: 1;
width: 100%;
height: 100%;
background-color: #FFFFFF;
border-radius: 50%;
display: flex;
justify-content: center;
align-items: center;
}
</style>

View File

@ -1,25 +1,30 @@
import request from '@/utils/request'
const version = '1.0.0.0'
const version = '1.0.0'
const buildNumber = 0
const DEV = {
name: '开发',
baseUrl: 'https://dev.lock.star-lock.cn/api',
webviewBaseUrl: 'https://dev.lock.star-lock.cn',
version: version
version,
buildNumber
}
const PRE = {
name: '预发',
baseUrl: 'https://pre.lock.star-lock.cn/api',
webviewBaseUrl: 'https://pre.lock.star-lock.cn',
version: version
version,
buildNumber
}
const PROD = {
name: '线上',
baseUrl: '',
version: version
baseUrl: 'https://lock.xhjcn.ltd/api',
webviewBaseUrl: 'https://lock.xhjcn.ltd',
version,
buildNumber
}
// 更换环境的时候 切换导出就行
export default PRE
export default { DEV, PRE, PROD }

View File

@ -5,49 +5,6 @@
"versionName" : "1.0.0",
"versionCode" : "100",
"transformPx" : false,
/* 5+App */
"app-plus" : {
"usingComponents" : true,
"nvueStyleCompiler" : "uni-app",
"compilerVersion" : 3,
"splashscreen" : {
"alwaysShowBeforeRender" : true,
"waiting" : true,
"autoclose" : true,
"delay" : 0
},
/* */
"modules" : {},
/* */
"distribute" : {
/* android */
"android" : {
"permissions" : [
"<uses-permission android:name=\"android.permission.CHANGE_NETWORK_STATE\"/>",
"<uses-permission android:name=\"android.permission.MOUNT_UNMOUNT_FILESYSTEMS\"/>",
"<uses-permission android:name=\"android.permission.VIBRATE\"/>",
"<uses-permission android:name=\"android.permission.READ_LOGS\"/>",
"<uses-permission android:name=\"android.permission.ACCESS_WIFI_STATE\"/>",
"<uses-feature android:name=\"android.hardware.camera.autofocus\"/>",
"<uses-permission android:name=\"android.permission.ACCESS_NETWORK_STATE\"/>",
"<uses-permission android:name=\"android.permission.CAMERA\"/>",
"<uses-permission android:name=\"android.permission.GET_ACCOUNTS\"/>",
"<uses-permission android:name=\"android.permission.READ_PHONE_STATE\"/>",
"<uses-permission android:name=\"android.permission.CHANGE_WIFI_STATE\"/>",
"<uses-permission android:name=\"android.permission.WAKE_LOCK\"/>",
"<uses-permission android:name=\"android.permission.FLASHLIGHT\"/>",
"<uses-feature android:name=\"android.hardware.camera\"/>",
"<uses-permission android:name=\"android.permission.WRITE_SETTINGS\"/>"
]
},
/* ios */
"ios" : {},
/* SDK */
"sdkConfigs" : {}
}
},
/* */
"quickapp" : {},
/* */
"mp-weixin" : {
"appid" : "wx9829a39e65550757",
@ -55,20 +12,17 @@
"urlCheck" : true,
"minified" : true
},
"permission" : {
"scope.bluetooth" : {
"desc" : "蓝牙将用于控制和管理您的智能门锁"
},
"scope.userLocation" : {
"desc" : "获取您的位置信息将用于智能门锁的位置服务"
}
},
"requiredPrivateInfos" : [ "getLocation" ],
"usingComponents" : true,
"lazyCodeLoading" : "requiredComponents"
},
"mp-alipay" : {
"usingComponents" : true
},
"mp-baidu" : {
"usingComponents" : true
},
"mp-toutiao" : {
"usingComponents" : true
},
"uniStatistics" : {
"enable" : false
},
"vueVersion" : "3"
}

View File

@ -9,123 +9,139 @@
},
"pages": [
{
"path": "pages/home/home",
"path": "pages/home/home"
},
{
"path": "pages/selectAddress/selectAddress",
"style": {
"navigationBarTitleText": "星星锁",
"navigationBarTextStyle": "white",
"navigationBarBackgroundColor": "#63b8af",
"navigationStyle": "default"
"navigationBarTitleText": "锁地址",
"disableScroll": true
}
},
{
"path": "pages/mine/mine",
"style": {
"navigationBarTitleText": "星星锁",
"navigationBarTextStyle": "white",
"navigationBarBackgroundColor": "#63b8af",
"navigationStyle": "default"
}
},
{
"path": "pages/index/index"
"path": "pages/mine/mine"
},
{
"path": "pages/userInfo/userInfo",
"style": {
"navigationBarTitleText": "个人信息",
"navigationBarTextStyle": "white",
"navigationBarBackgroundColor": "#63b8af",
"navigationStyle": "default"
"navigationBarTitleText": "个人信息"
}
},
{
"path": "pages/updateName/updateName",
"style": {
"navigationBarTitleText": "修改昵称",
"navigationBarTextStyle": "white",
"navigationBarBackgroundColor": "#63b8af",
"navigationStyle": "default"
"navigationBarTitleText": "修改昵称"
}
},
{
"path": "pages/updatePassword/updatePassword",
"style": {
"navigationBarTitleText": "重置密码",
"navigationBarTextStyle": "white",
"navigationBarBackgroundColor": "#63b8af",
"navigationStyle": "default"
"navigationBarTitleText": "重置密码"
}
},
{
"path": "pages/updateEmail/updateEmail",
"style": {
"navigationBarTitleText": "修改邮箱",
"navigationBarTextStyle": "white",
"navigationBarBackgroundColor": "#63b8af",
"navigationStyle": "default"
"navigationBarTitleText": "修改邮箱"
}
},
{
"path": "pages/verifyEmail/verifyEmail",
"style": {
"navigationBarTitleText": "验证邮箱",
"navigationBarTextStyle": "white",
"navigationBarBackgroundColor": "#63b8af",
"navigationStyle": "default"
"navigationBarTitleText": "验证邮箱"
}
},
{
"path": "pages/safeQuestion/safeQuestion",
"style": {
"navigationBarTitleText": "安全问题",
"navigationBarTextStyle": "white",
"navigationBarBackgroundColor": "#63b8af",
"navigationStyle": "default"
"navigationBarTitleText": "安全问题"
}
},
{
"path": "pages/updateSafeQuestion/updateSafeQuestion",
"style": {
"navigationBarTitleText": "修改安全问题",
"navigationBarTextStyle": "white",
"navigationBarBackgroundColor": "#63b8af",
"navigationStyle": "default"
"navigationBarTitleText": "修改安全问题"
}
},
{
"path": "pages/webview/webview",
"style": {
"navigationBarTitleText": "介绍",
"navigationBarTextStyle": "white",
"navigationBarBackgroundColor": "#63b8af",
"navigationStyle": "default"
"navigationBarTitleText": "介绍"
}
},
{
"path": "pages/lockDetail/lockDetail",
"style": {
"navigationBarTitleText": "星星锁",
"navigationBarTextStyle": "white",
"navigationBarBackgroundColor": "#63b8af",
"navigationStyle": "default"
"navigationBarTitleText": "星星锁"
}
},
{
"path": "pages/bindLock/bindLock",
"style": {
"navigationBarTitleText": "添加锁"
}
},
{
"path": "pages/searchDevice/searchDevice",
"style": {
"navigationBarTitleText": "搜索",
"navigationBarTextStyle": "white",
"navigationBarBackgroundColor": "#63b8af",
"navigationStyle": "default"
"disableScroll": true,
"navigationBarTitleText": "附近设备"
}
},
{
"path": "pages/setting/setting",
"style": {
"navigationBarTitleText": "设置"
}
},
{
"path": "pages/keyList/keyList",
"style": {
"disableScroll": true,
"navigationBarTitleText": "电子钥匙"
}
},
{
"path": "pages/createKey/createKey",
"style": {
"navigationBarTitleText": "发送钥匙"
}
},
{
"path": "pages/passwordList/passwordList",
"style": {
"disableScroll": true,
"navigationBarTitleText": "密码"
}
},
{
"path": "pages/createPassword/createPassword",
"style": {
"disableScroll": true,
"navigationBarTitleText": "获取密码"
}
},
{
"path": "pages/passwordDetail/passwordDetail",
"style": {
"disableScroll": true,
"navigationBarTitleText": "密码详情"
}
},
{
"path": "pages/keyDetail/keyDetail",
"style": {
"disableScroll": true,
"navigationBarTitleText": "钥匙详情"
}
}
],
"globalStyle": {
"navigationBarTextStyle": "black",
"navigationBarTextStyle": "white",
"navigationBarTitleText": "星星锁",
"navigationBarBackgroundColor": "#F3F3F3",
"navigationBarBackgroundColor": "#63b8af",
"backgroundColor": "#F3F3F3",
"navigationStyle": "custom"
"navigationStyle": "default"
},
"tabBar": {
"color": "#a3a3a3",

177
pages/bindLock/bindLock.vue Normal file
View File

@ -0,0 +1,177 @@
<template>
<view>
<view class="text">如需修改名字请重新命名点击确定添加锁</view>
<input class="input" :value="name" maxlength="32" placeholder="请输入名称" placeholder-class="input-placeholder"
@input="uopdateName"
></input>
<view class="button" @click="bindLock">确定</view>
</view>
</template>
<script>
import { useBluetoothStore } from '@/stores/bluetooth'
import { useUserStore } from '@/stores/user'
import { mapActions, mapState } from 'pinia'
import { bindLockAdmin } from '@/api/lock'
import { useLockStore } from '@/stores/lock'
import { useBasicStore } from '@/stores/basic'
export default {
data () {
return {
name: ''
}
},
computed: {
...mapState(useBluetoothStore, ['currentLockInfo', 'keyId']),
...mapState(useUserStore, ['userInfo']),
...mapState(useLockStore, ['lockSearch']),
},
onLoad() {
this.name = this.currentLockInfo.name
console.log(this.currentLockInfo)
console.log(this.name)
},
methods: {
...mapActions(useBluetoothStore, ['addLockUser']),
...mapActions(useLockStore, ['getLockList', 'updateLockSearch']),
...mapActions(useBasicStore, ['backAndToast']),
uopdateName(data) {
this.name = data.detail.value
},
async bindLock() {
if(this.name === '') {
uni.showToast({
title: '请输入名称',
icon: 'none'
})
return
}
uni.showLoading({
title: '添加中',
mask: true
})
const timestamp = parseInt(new Date().getTime() / 1000)
const password = (Math.floor(Math.random() * 900000) + 100000).toString()
const { code: addUserCode } = await this.addLockUser({
name: this.currentLockInfo.name,
keyId: this.keyId,
authUid: this.userInfo.uid.toString(),
uid: this.userInfo.uid.toString(),
openMode: 1,
keyType: 0,
startDate: timestamp,
expireDate: 0xffffffff,
useCountLimit: 0xffff,
isRound: 0,
weekRound: 0,
startHour: 0,
startMin: 0,
endHour: 0,
endMin: 0,
role: 0xff,
password
})
if(addUserCode !== 0) {
uni.hideLoading()
this.backAndToast('添加失败,请重试')
return
}
const params = {
lockAlias: this.name,
lockInfo: {
...this.currentLockInfo.lockConfig,
adminPwd: password
},
bluetooth: {
bluetoothDeviceName: this.currentLockInfo.name,
bluetoothDeviceId: this.currentLockInfo.deviceId,
publicKey: this.currentLockInfo.publicKey,
privateKey: this.currentLockInfo.commKey,
signKey: this.currentLockInfo.signKey,
},
lockUserNo: this.currentLockInfo.lockUserNo,
pwdTimestamp: this.currentLockInfo.pwdTimestamp,
featureValue: this.currentLockInfo.featureValue,
featureSettingValue: this.currentLockInfo.featureSettingValue,
featureSettingParams: this.currentLockInfo.featureSettingParams
}
if(this.currentLockInfo.position) {
params.position = {
latitude: this.currentLockInfo.position.latitude,
longitude: this.currentLockInfo.position.longitude,
province: this.currentLockInfo.position.province,
city: this.currentLockInfo.position.city,
district: this.currentLockInfo.position.district,
country: this.currentLockInfo.position.country,
township: this.currentLockInfo.position.township,
address: this.currentLockInfo.position.address
}
}
const { code, message } = await bindLockAdmin(params)
console.log('添加锁返回', code, message)
if(code === 0) {
this.updateLockSearch({
...this.lockSearch,
pageNo: 1
})
this.getLockList(this.lockSearch)
uni.hideLoading()
this.backAndToast('添加成功')
} else {
uni.hideLoading()
uni.showToast({
title: message,
icon: 'none'
})
}
}
},
}
</script>
<style lang="scss">
page {
background-color: $uni-bg-color-grey;
}
</style>
<style lang="scss" scoped>
.text {
color: #2b2a28;
padding: 100rpx 0;
font-size: 34rpx;
text-align: center;
}
.input {
border-radius: 16rpx;
background: #FFFFFF;
margin-left: 35rpx;
margin-top: 24rpx;
height: 108rpx;
width: 616rpx;
padding-left: 32rpx;
padding-right: 32rpx;
}
.input-placeholder {
height: 108rpx;
font-size: 36rpx;
font-weight: bold;
line-height: 108rpx;
}
.button {
margin-top: 160rpx;
margin-left: 35rpx;
width: 680rpx;
height: 96rpx;
background: #63b8af;
border-radius: 16rpx;
line-height: 96rpx;
text-align: center;
font-size: 32rpx;
color: #FFFFFF;
}
</style>

View File

@ -0,0 +1,237 @@
<template>
<view>
<view class="tabs">
<up-tabs :list="tabs" lineWidth="40rpx" lineHeight="5rpx" :current="currnetIndex" lineColor="#63b8af"
@click="clickTab" :inactiveStyle="{color:'#a3a3a3', fontSize: '32rpx', fontWeight: 'bold'}"
:activeStyle="{color:'#63b8af', fontSize: '32rpx', fontWeight: 'bold'}">
</up-tabs>
</view>
<swiper :style="{height: deviceInfo.screenHeight - deviceInfo.safeArea.top - 44 + 'px'}" v-if="deviceInfo"
:list="tabs" :autoplay="false"
:circular="true" :current="currnetIndex" @change="changeSwiper">
<swiper-item>
<LockInput :value="permanentAccount" title="接收者账号" placeholder="请输入手机号或邮箱"
@changeInput="changePermanentAccountInput"></LockInput>
<LockInput :value="permanentName" title="钥匙名称" placeholder="请输入钥匙名称"
@changeInput="changePermanentNmaeInput"></LockInput>
<view class="text">接收者可使用此小程序开关锁</view>
<view class="button" @click="createKey('permanent')">发送钥匙</view>
</swiper-item>
<swiper-item :style="{height: deviceInfo.windowHeight - 44 + 'px'}">
<LockInput :value="temporaryAccount" title="接收者账号" placeholder="请输入手机号或邮箱"
@changeInput="changeTemporaryAccountInput"></LockInput>
<LockInput :value="temporaryName" title="钥匙名称" placeholder="请输入钥匙名称"
@changeInput="changeTemporaryNameInput"></LockInput>
<view style="margin-top: 20rpx">
<LockDatetimePicker title="生效时间" :value="temporaryValidTime" :minDate="minDate"
placeholder="请选择失效时间" @changeTime="changeTemporaryValidTime"></LockDatetimePicker>
<LockDatetimePicker title="失效时间" :value="temporaryInvalidTime" :minDate="minDate"
placeholder="请选择失效时间" @changeTime="changeTemporaryInvalidTime"></LockDatetimePicker>
</view>
<view class="text">接收者在有效期内可以不限次数使用</view>
<view class="button" @click="createKey('temporary')">发送钥匙</view>
</swiper-item>
</swiper>
</view>
</template>
<script>
import { mapActions, mapState } from 'pinia'
import { useBasicStore } from '@/stores/basic'
import LockInput from '@/components/LockInput/LockInput.vue'
import LockDatetimePicker from '@/components/LockDatetimePicker/LockDatetimePicker.vue'
import { createPsaawordRequest } from '@/api/keyboardPwd'
import { useBluetoothStore } from '@/stores/bluetooth'
import { useLockStore } from '@/stores/lock'
import { test } from 'uview-plus'
import { createKeyRequest } from '@/api/key'
export default {
data () {
return {
tabs: [{
name: '永久'
}, {
name: '限时'
}],
permanentName: '',
permanentAccount: '',
temporaryName: '',
temporaryAccount: '',
temporaryValidTime: Number(new Date()),
temporaryInvalidTime: Number(new Date()),
minDate: Number(new Date()),
currnetIndex: 0,
deviceInfo: null,
pending: false
}
},
components: {
LockInput,
LockDatetimePicker
},
computed: {
...mapState(useBluetoothStore, ['currentLockInfo']),
...mapState(useLockStore, ['keySearch']),
},
async onLoad () {
this.deviceInfo = await this.getDeviceInfo()
this.temporaryInvalidTime = this.setTime()
},
methods: {
...mapActions(useBasicStore, ['getDeviceInfo', 'backAndToast']),
...mapActions(useLockStore, ['getKeyList', 'updateKeySearch']),
setTime () {
const now = new Date()
now.setMinutes(0, 0, 0)
now.setDate(now.getDate() + 3)
return now.getTime()
},
async createKey (type, createUser = false) {
if ((type === 'temporary' && !(test.email(this.temporaryAccount) || test.mobile(this.temporaryAccount))) ||
(type === 'permanent' && !(test.email(this.permanentAccount) || test.mobile(this.permanentAccount)))) {
uni.showToast({
title: '请输入格式正确的手机号或邮箱',
icon: 'none'
})
return
}
if ((type === 'temporary' && this.temporaryName === '') || (type === 'permanent' && this.permanentName === '')) {
uni.showToast({
title: '名称不能为空',
icon: 'none'
})
return
}
if(type === 'temporary' && this.temporaryValidTime >= this.temporaryInvalidTime) {
uni.showToast({
title: '失效时间必须大于生效时间',
icon: 'none'
})
return
}
if (this.pending) {
return
}
this.pending = true
let params = {
faceAuthentication: '2',
isRemoteUnlock: '2',
lockId: this.currentLockInfo.lockId,
keyRight: '0',
remarks: '',
countryCode: '86',
createUser: '0'
}
if(createUser) {
params.createUser = '1'
params.usernameType = test.mobile(this.temporaryAccount) ? '1' : '2'
}
if (type === 'temporary') {
params.keyNameForAdmin = this.temporaryName
params.endDate = this.temporaryInvalidTime.toString()
params.keyType = '2'
params.receiverUsername = this.temporaryAccount
params.startDate = this.temporaryValidTime.toString()
} else {
params.keyNameForAdmin = this.permanentName
params.startDate = new Date().getTime().toString()
params.endDate = '0'
params.keyType = '1'
params.receiverUsername = this.permanentAccount
}
const { code, message } = await createKeyRequest(params)
if (code === 0) {
this.updateKeySearch({
...this.keySearch,
pageNo: 1
})
this.getKeyList(this.keySearch)
this.backAndToast('钥匙已发送')
} else if(code === 425) {
this.pending = false
uni.showModal({
title: '提示',
content: `是否发送电子钥匙给未注册账号\n${ params.receiverUsername }`,
success: async (res) => {
if (res.confirm) {
await this.createKey(type, true)
}
}
})
} else {
uni.showToast({
title: message,
icon: 'none'
})
}
this.pending = false
},
changePermanentAccountInput (e) {
this.permanentAccount = e
},
changePermanentNmaeInput (e) {
this.permanentName = e
},
changeTemporaryNameInput (e) {
this.temporaryName = e
},
changeTemporaryAccountInput (e) {
this.temporaryAccount = e
},
changeTemporaryValidTime (e) {
this.temporaryValidTime = e
},
changeTemporaryInvalidTime (e) {
this.temporaryInvalidTime = e
},
clickTab (data) {
this.currnetIndex = data.index
},
changeSwiper (e) {
this.currnetIndex = e.detail.current
}
}
}
</script>
<style lang="scss">
page {
background-color: $uni-bg-color-grey;
}
</style>
<style lang="scss" scoped>
.tabs {
display: flex;
justify-content: center;
}
.text {
margin-top: 40rpx;
margin-bottom: 50rpx;
color: #262626;
font-size: 26rpx;
padding: 0 32rpx;
}
.button {
border-radius: 64rpx;
width: 686rpx;
margin-left: 32rpx;
height: 100rpx;
line-height: 100rpx;
text-align: center;
background-color: #63b8af;
color: #fff;
font-size: 32rpx;
font-weight: bold;
}
</style>

View File

@ -0,0 +1,200 @@
<template>
<view>
<view class="tabs">
<up-tabs :list="tabs" lineWidth="40rpx" lineHeight="5rpx" :current="currnetIndex" lineColor="#63b8af"
@click="clickTab" :inactiveStyle="{color:'#a3a3a3', fontSize: '32rpx', fontWeight: 'bold'}"
:activeStyle="{color:'#63b8af', fontSize: '32rpx', fontWeight: 'bold'}">
</up-tabs>
</view>
<swiper :style="{height: deviceInfo.screenHeight - deviceInfo.safeArea.top - 44 + 'px'}" v-if="deviceInfo"
:list="tabs" :autoplay="false"
:circular="true" :current="currnetIndex" @change="changeSwiper">
<swiper-item>
<LockInput :value="permanentName" title="名称" placeholder="给密码命名"
@changeInput="changePermanentInput"></LockInput>
<view class="text">{{ text }}</view>
<view class="button" @click="createPassword('permanent')">获取密码</view>
</swiper-item>
<swiper-item :style="{height: deviceInfo.windowHeight - 44 + 'px'}">
<LockInput :value="temporaryName" title="名称" placeholder="给密码命名"
@changeInput="changeTemporaryInput"></LockInput>
<view style="margin-top: 20rpx">
<LockDatetimePicker title="失效时间" :value="temporaryTime" :minDate="minDate"
placeholder="请选择失效时间" @changeTime="changeTemporaryTime"></LockDatetimePicker>
</view>
<view class="text">{{ text }}</view>
<view class="button" @click="createPassword('temporary')">获取密码</view>
</swiper-item>
</swiper>
</view>
</template>
<script>
import { mapActions, mapState } from 'pinia'
import { useBasicStore } from '@/stores/basic'
import LockInput from '@/components/LockInput/LockInput.vue'
import LockDatetimePicker from '@/components/LockDatetimePicker/LockDatetimePicker.vue'
import { createPsaawordRequest } from '@/api/keyboardPwd'
import { useBluetoothStore } from '@/stores/bluetooth'
import { useLockStore } from '@/stores/lock'
export default {
data () {
return {
tabs: [{
name: '永久'
}, {
name: '限时'
}],
permanentName: '',
temporaryName: '',
temporaryTime: Number(new Date()),
minDate: Number(new Date()),
currnetIndex: 0,
deviceInfo: null,
pending: false,
text: '密码生成后请在当日2359前使用一次进行激活否则过点后未激活则失效。密码激活后有效期内不限次数使用。'
}
},
components: {
LockInput,
LockDatetimePicker
},
computed: {
...mapState(useBluetoothStore, ['currentLockInfo']),
...mapState(useLockStore, ['passwordSearch']),
},
async onLoad() {
this.deviceInfo = await this.getDeviceInfo()
this.temporaryTime = this.setTime()
},
methods: {
...mapActions(useBasicStore, ['getDeviceInfo', 'backAndToast']),
...mapActions(useLockStore, ['getPasswordList', 'updatePasswordSearch']),
setTime() {
const now = new Date()
now.setMinutes(0, 0, 0)
now.setDate(now.getDate() + 1)
return now.getTime()
},
async createPassword(type) {
const that = this
if((type === 'temporary' && this.temporaryName === '') || (type === 'permanent' && this.permanentName === '')) {
uni.showToast({
title: '名称不能为空',
icon: 'none'
})
return
}
if(this.pending) {
return
}
this.pending = true
let params = {
lockId: this.currentLockInfo.lockId,
isCoerced: 2,
pwdRight: 0
}
if(type === 'temporary') {
params.keyboardPwdName = this.temporaryName
params.keyboardPwdType = 3
params.startDate = new Date().getTime()
params.endDate = this.temporaryTime
params.hoursStart = 0
params.hoursEnd = 0
} else {
params.startDate = 0
params.endDate = 0
params.keyboardPwdName = this.permanentName
params.keyboardPwdType = 2
params.hoursStart = 0
params.hoursEnd = 0
}
const { code, data, message } = await createPsaawordRequest(params)
if(code === 0) {
this.updatePasswordSearch({
...this.passwordSearch,
pageNo: 1
})
this.getPasswordList(this.passwordSearch)
uni.showModal({
title: '密码生成成功',
content: `密码:${data.keyboardPwd}`,
cancelText: '复制',
success: (res) => {
if(res.confirm) {
uni.navigateBack()
} else {
uni.setClipboardData({
data: data.keyboardPwd,
success: () => {
that.backAndToast('复制成功')
}
})
}
}
})
} else {
uni.showToast({
title: message,
icon: 'none'
})
}
this.pending = false
},
changePermanentInput(e) {
this.permanentName = e
},
changeTemporaryInput(e) {
this.temporaryName = e
},
changeTemporaryTime(e) {
this.temporaryTime = e
},
clickTab(data) {
this.currnetIndex = data.index
},
changeSwiper(e) {
this.currnetIndex = e.detail.current
}
}
}
</script>
<style lang="scss">
page {
background-color: $uni-bg-color-grey;
}
</style>
<style lang="scss" scoped>
.tabs {
display: flex;
justify-content: center;
}
.text {
margin-top: 40rpx;
margin-bottom: 50rpx;
color: #262626;
font-size: 26rpx;
padding: 0 32rpx;
}
.button {
border-radius: 64rpx;
width: 686rpx;
margin-left: 32rpx;
height: 100rpx;
line-height: 100rpx;
text-align: center;
background-color: #63b8af;
color: #fff;
font-size: 32rpx;
font-weight: bold;
}
</style>

View File

@ -2,17 +2,18 @@
<view>
<view v-if="!penging">
<view v-if="isLogin">
<scroll-view scroll-y="true" :style="{ height: 'calc(100vh)' }" lower-threshold="100"
@refresherrefresh="refresherList" :refresher-enabled="true" @scrolltolower="nextPage"
<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">
<up-search :searchIconSize="48" :inputStyle="{ fontSize: '32rpx' }" @focus="getFocus" @blur="getBlur"
:height="80" placeholder="搜索" :clearabled="false" @change="changeSearch"
v-model="search.searchStr" bgColor="#ffffff" :showAction="false" maxlength="20"></up-search>
v-model="lockSearch.searchStr" bgColor="#ffffff" :showAction="false" maxlength="20"></up-search>
</view>
<view class="lock-list" v-if="!penging">
<view v-if="lockList.length === 0 && search.searchStr === '' && !focus">
<image src="/static/images/icon_add.png" mode="aspectFill" class="button-add-big" @click="toSearchDevice"></image>
<view v-if="lockList.length === 0 && lockSearch.searchStr === '' && !focus">
<image src="/static/images/icon_add_round.png" mode="aspectFill" class="button-add-big"
@click="toSearchDevice"></image>
<view class="text">填加锁时手机必须在锁旁边</view>
</view>
<view v-else>
@ -32,9 +33,9 @@
<view>{{getRole(lock.userType)}}</view>
</view>
</view>
<view class="lock-name">{{lock.lockName}}</view>
<view class="lock-name">{{lock.lockAlias}}</view>
<view class="lock-time">
<view v-if="lock.keyType !== 1" style="font-size: 32rpx">{{ getTimeLimit(lock.keyType) }}</view>
<view v-if="lock.keyType === 1" 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>
@ -45,19 +46,23 @@
</view>
</view>
</scroll-view>
<image v-if="lockList.length !== 0" src="/static/images/icon_add.png" mode="aspectFill"
<image v-if="lockList.length !== 0" src="/static/images/icon_add_round.png" mode="aspectFill"
class="button-add" @click="toSearchDevice"></image>
</view>
<view v-else>
<view class="button-login" @click="login">登录</view>
<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>
</view>
</template>
<script>
import { timeFormat } from 'uview-plus'
import { getUserInfoRequest } from '@/api/user'
import { getUserInfoRequest, loginRequest } from '@/api/user'
import { useUserStore } from '@/stores/user'
import { useLockStore } from '@/stores/lock'
import { useBluetoothStore } from '@/stores/bluetooth'
@ -67,68 +72,130 @@
export default {
data() {
return {
search: {
pageNo: 1,
pageSize: 50,
searchStr: ''
},
refresherTriggered: false,
focus: false,
penging: true
penging: true,
deviceInfo: null
}
},
computed: {
...mapState(useUserStore, ['userInfo', 'isLogin']),
...mapState(useLockStore, ['lockList', 'lockTotal']),
...mapState(useLockStore, ['lockList', 'lockTotal', 'lockSearch']),
...mapState(useBluetoothStore, ['bluetoothStatus', 'isInitBluetooth']),
},
async onLoad() {
uni.showLoading({
title: '加载中'
title: '加载中',
mask: true
})
const accountInfo = uni.getAccountInfoSync()
console.log(accountInfo)
getApp().globalData.appid = accountInfo.miniProgram.appId
getApp().globalData.envVersion = accountInfo.miniProgram.envVersion
this.deviceInfo = await this.getDeviceInfo()
const token = uni.getStorageSync('token')
if(token) {
this.updateLoginStatus(true)
const code = await this.getUserInfo()
if(code === 0) {
await this.getLockList(this.search)
}
this.getLockList(this.lockSearch)
await this.getUserInfo()
} else {
await this.homeLogin()
}
this.penging = false
uni.hideLoading()
},
methods: {
timeFormat,
...mapActions(useUserStore, ['updateUserInfo', 'updateLoginStatus', 'login']),
...mapActions(useLockStore, ['getLockList', 'getRole', 'getTimeLimit']),
...mapActions(useBluetoothStore, ['getBluetoothStatus', 'initAndListenBluetooth', 'updateCurrentLockInfo', 'checkSetting']),
...mapActions(useBasicStore, ['routeJump']),
...mapActions(useUserStore, ['updateUserInfo', 'updateLoginStatus', 'phoneLogin', 'getUserInfo']),
...mapActions(useLockStore, ['getLockList', 'getRole', 'getTimeLimit', 'updateLockSearch']),
...mapActions(useBluetoothStore, ['getBluetoothStatus', 'initAndListenBluetooth', 'updateCurrentLockInfo',
'checkSetting', 'updateKeyId']),
...mapActions(useBasicStore, ['routeJump', 'getDeviceInfo']),
homeLogin() {
const that = this
return new Promise((resolve) => {
uni.login({
provider: 'weixin',
success: async function (loginRes) {
const { code, data } = await loginRequest({
js_code: loginRes.code
})
if(code === 0) {
uni.setStorageSync('openid', data.openid)
if(data.accessToken) {
uni.setStorageSync('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: function () {
uni.showToast({
title: '登录失败,请重试',
icon: 'none'
})
resolve(false)
}
})
})
},
async getphonenumber(data) {
if(data.detail.errMsg === 'getPhoneNumber:fail user deny') {
return
}
const result = await this.phoneLogin({
encryptedData: data.detail.encryptedData,
iv: data.detail.iv
})
if(!result) {
uni.showToast({
title: '登录失败,请重试',
icon: 'none'
})
}
},
async nextPage() {
if(this.lockList.length < this.lockTotal) {
this.search.pageNo++
const { code } = await this.getLockList(this.search)
const sreach = {
...this.lockSearch,
pageNo: this.lockSearch.pageNo + 1
}
const { code } = await this.getLockList(search)
if(code !== 0) {
this.search.pageNo--
this.updateLockSearch(search)
}
}
},
async refresherList() {
this.refresherTriggered = true
this.search.pageNo = 1
await this.getLockList(this.search)
this.updateLockSearch({
...this.lockSearch,
pageNo: 1
})
await this.getLockList(this.lockSearch)
uni.showToast({
title: '刷新成功',
icon: 'none'
})
this.refresherTriggered = false
},
async changeSearch(data) {
this.search.searchStr = data
await this.getLockList(this.search)
},
async getUserInfo() {
const { code, data } = await getUserInfoRequest()
if(code === 0) {
this.updateUserInfo(data)
console.log(this.userInfo)
}
return code
this.updateLockSearch({
...this.lockSearch,
searchStr: data
})
await this.getLockList(this.lockSearch)
},
getFocus() {
this.focus = true
@ -163,7 +230,16 @@
result = await this.initAndListenBluetooth()
}
if(result) {
this.updateCurrentLockInfo(lock)
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'
})
@ -189,7 +265,6 @@ page {
}
.button-login {
margin-top: 40vh;
border-radius: 46rpx;
width: 650rpx;
height: 120rpx;
@ -314,4 +389,12 @@ page {
margin-top: 32rpx;
font-weight: bold;
}
.tips {
margin-top: 40vh;
padding: 32rpx 0;
text-align: center;
font-size: 28rpx;
color: #999999;
}
</style>

View File

@ -1,205 +0,0 @@
<template>
<view class="content">
<view v-if="!showConnect">
<button class="button" v-if="showAdd" @click="getList">添加设备</button>
<button class="button" v-else @click="stopGetList">停止搜索</button>
<view class="device-list" v-for="(item, index) in deviceList" :key="item.deviceId" @click="connect(item)">{{item.name}}</view>
</view>
<view v-else>
<button class="button" @click="bindUser">添加用户</button>
<button class="button" @click="openDoorOperate">开门</button>
<button class="button" @click="closeDoorOperate">关门</button>
<button class="button" @click="reset">恢复出厂设置</button>
<button class="button" @click="getLockStatusResult">获取锁状态</button>
<button class="button" @click="resetPassword">重置锁密码</button>
<button class="button" @click="setPassword">设置密码</button>
<button class="button" @click="deletePassword">删除密码</button>
<view>名称{{currentLockInfo.name}}</view>
<view>设备Id{{currentLockInfo.deviceId}}</view>
</view>
</view>
</template>
<script>
import { useBluetoothStore } from '@/stores/bluetooth'
import { mapState, mapActions } from 'pinia'
export default {
data() {
return {
showAdd: true,
showConnect: false,
keyId: '0',
authUid: '294',
onlineToken: '0'
}
},
computed: {
...mapState(useBluetoothStore, ['deviceList', 'currentLockInfo'])
},
onLoad () {},
methods: {
...mapActions(useBluetoothStore, ['getBluetoothDevices', 'stopGetBluetoothDevices', 'updateCurrentLockInfo',
'connectBluetoothDevice', 'getPublicKey', 'getCommKey', 'getLockStatus', 'addLockUser', 'timestampToArray',
'openDoor', 'resetDevice', 'resetLockPassword', 'setLockPassword']),
async deletePassword() {
const timestamp = parseInt(new Date().getTime() / 1000)
const data = await this.setLockPassword({
keyId: this.keyId,
uid: this.authUid,
pwdNo: 2,
operate: 2,
isAdmin: 1,
pwd: '000000',
userCountLimit: 0,
startTime: timestamp,
endTime: timestamp
})
console.log('设置密码返回', data)
},
async setPassword() {
const timestamp = parseInt(new Date().getTime() / 1000)
const endTimestamp = timestamp + 3600 * 24 * 365
const data = await this.setLockPassword({
keyId: this.keyId,
uid: this.authUid,
pwdNo: 1,
operate: 0,
isAdmin: 1,
pwd: '000000',
userCountLimit: 0xffff,
startTime: timestamp,
endTime: endTimestamp
})
console.log('设置密码返回', data)
},
async resetPassword() {
const { code } = await this.resetLockPassword({
uid: this.authUid,
keyId: this.keyId
})
console.log('重置密码返回', code)
},
async openDoorOperate() {
const { code } = await this.openDoor({
name: this.currentLockInfo.name,
uid: this.authUid,
openMode: 0,
openTime: parseInt(new Date().getTime() / 1000),
onlineToken: this.onlineToken
})
console.log('开门返回', code)
},
async closeDoorOperate() {
const { code } = await this.openDoor({
name: this.currentLockInfo.name,
uid: this.authUid,
openMode: 32,
openTime: parseInt(new Date().getTime() / 1000),
onlineToken: this.onlineToken
})
console.log('关门返回', code)
},
async reset() {
const { code } = await this.resetDevice({
name: this.currentLockInfo.name,
authUid: this.authUid,
keyId: this.keyId
})
console.log('恢复出厂设置返回', code)
},
getList() {
this.getBluetoothDevices()
this.showAdd = false
},
stopGetList() {
this.stopGetBluetoothDevices()
this.showAdd = true
},
async connect(item) {
this.updateCurrentLockInfo({
name: item.name,
deviceId: item.deviceId
})
const result = await this.connectBluetoothDevice()
if(result) {
this.showConnect = true
this.stopGetBluetoothDevices()
}
},
async bindUser() {
const { code } = await this.getPublicKey(this.currentLockInfo.name)
console.log('获取公钥返回', code)
if(code !== 0) {
return
}
const { code: getCommKeyCode } = await this.getCommKey(this.currentLockInfo.name, this.keyId, this.authUid,
parseInt(new Date().getTime() / 1000))
console.log('获取私钥返回', getCommKeyCode)
if(getCommKeyCode !== 0) {
return
}
const timestamp = parseInt(new Date().getTime() / 1000)
const { code: addUserCode } = await this.addLockUser({
name: this.currentLockInfo.name,
keyId: this.keyId,
authUid: this.authUid,
uid: this.authUid,
publicKey: this.currentLockInfo.publicKey,
commKey: this.currentLockInfo.commKey,
openMode: 1,
keyType: 1,
startDate: timestamp,
expireDate: 0xffffffff,
useCountLimit: 0xffff,
isRound: 0,
weekRound: 0,
startHour: 0,
startMin: 0,
endHour: 0,
endMin: 0,
role: 0xff,
password: (Math.floor(Math.random() * 900000) + 100000).toString()
})
console.log('添加用户返回', addUserCode)
},
getLockStatusResult() {
const timnestamp = parseInt(new Date().getTime() / 1000)
this.getLockStatus({
name: this.currentLockInfo.name,
uid: this.authUid,
nowTime: timnestamp,
localTime: timnestamp
})
}
}
}
</script>
<style lang="scss" scoped>
.content {
padding-top: 160rpx;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
.button {
margin-top: 40rpx;
width: 400rpx;
height: 108rpx;
line-height: 108rpx;
background: #3F536E;
color: #ffffff !important;
}
.device-list {
margin-top: 10rpx;
height: 100rpx;
width: 500rpx;
line-height: 100rpx;
text-align: center;
border: #dd524d solid 2rpx;
}
</style>

View File

@ -0,0 +1,120 @@
<template>
<view>
<view class="item">
<view class="item-title">名称</view>
<view class="item-content">{{ currentKeyInfo.nickname }}</view>
</view>
<view class="item" style="margin-top: 2rpx">
<view class="item-title">有效期</view>
<view v-if="currentKeyInfo.keyType === 1">永久</view>
<view v-else>
<view class="item-content">{{ timeFormat(currentKeyInfo.startDate, 'yyyy-mm-dd h:M') }}</view>
<view class="item-content">{{ timeFormat(currentKeyInfo.endDate, 'yyyy-mm-dd h:M') }}</view>
</view>
</view>
<view class="item" style="margin-top: 20rpx">
<view class="item-title">接收者</view>
<view class="item-content">{{ currentKeyInfo.username }}</view>
</view>
<view class="item" style="margin-top: 2rpx">
<view class="item-title">发送人</view>
<view class="item-content">{{ currentKeyInfo.senderUsername }}</view>
</view>
<view class="item" style="margin-top: 2rpx">
<view class="item-title">发送时间</view>
<view class="item-content">{{ timeFormat(currentKeyInfo.sendDate, 'yyyy-mm-dd h:M') }}</view>
</view>
<view class="button" @click="deleteKey">删除钥匙</view>
</view>
</template>
<script>
import { mapActions, mapState } from 'pinia'
import { useLockStore } from '@/stores/lock'
import { timeFormat } from 'uview-plus'
import { deleteKeyRequest } from '@/api/key'
import { useBasicStore } from '@/stores/basic'
export default {
data () {
return {}
},
computed: {
...mapState(useLockStore, ['currentKeyInfo', 'keySearch']),
},
methods: {
timeFormat,
...mapActions(useLockStore, ['updateKeySearch', 'getKeyList']),
...mapActions(useBasicStore, ['backAndToast']),
async deleteKey () {
const that = this
uni.showModal({
title: '提示',
content: '确定要删除该钥匙',
async success(res) {
if(res.confirm) {
uni.showLoading({
title: '删除中',
mask: true
})
const { code: requestCode, message } = await deleteKeyRequest({
keyId: that.currentKeyInfo.keyId
})
if(requestCode === 0) {
uni.hideLoading()
that.updateKeySearch({
...that.keySearch,
pageNo: 1
})
await that.getKeyList(that.keySearch)
that.backAndToast('删除成功')
} else {
uni.hideLoading()
uni.showToast({
title: message,
icon: 'none'
})
}
}
}
})
}
},
}
</script>
<style lang="scss">
page {
background-color: $uni-bg-color-grey;
}
</style>
<style lang="scss" scoped>
.item {
padding: 24rpx 32rpx;
background-color: #FFFFFF;
display: flex;
align-items: center;
justify-content: space-between;
font-size: 32rpx;
font-weight: 500;
}
.tips {
padding: 24rpx 32rpx;
font-size: 24rpx;
color: #999999;
}
.button {
margin: 32rpx;
width: 686rpx;
height: 88rpx;
background-color: #df282d;
color: white;
text-align: center;
line-height: 88rpx;
border-radius: 44rpx;
font-weight: bold;
}
</style>

313
pages/keyList/keyList.vue Normal file
View File

@ -0,0 +1,313 @@
<template>
<view>
<scroll-view v-if="deviceInfo" scroll-y="true" :style="{height: deviceInfo.screenHeight - deviceInfo.safeArea.top + 'px'}" lower-threshold="100"
@refresherrefresh="refresherList" :refresher-enabled="true" @scrolltolower="nextPage"
:refresher-triggered="refresherTriggered">
<view class="search">
<up-search shape="square" :searchIconSize="48" :inputStyle="{ fontSize: '32rpx' }" :height="80" placeholder="搜索"
:clearabled="false" @change="changeSearch"
v-model="keySearch.searchStr" bgColor="#ffffff" :showAction="false" maxlength="20"></up-search>
</view>
<view style="padding: 32rpx 0 calc(env(safe-area-inset-bottom) + 250rpx) 0">
<view v-if="keyList.length === 0 && requestFinished">
<image class="empty-list" src="/static/images/background_empty_list.png" mode="aspectFill"></image>
<view class="empty-list-text">暂无数据</view>
</view>
<view v-else>
<up-swipe-action>
<up-swipe-action-item ref="swipeItem" :options="options" v-for="(key, index) in keyList"
:key="key.keyboardPwdId" :threshold="50" @click="deleteKey(key)">
<view class="key" @click="toKeyDetail(key)">
<image class="key-left" :src="key.headUrl" mode="aspectFill"></image>
<view class="key-right">
<view style="display: flex; align-items: center">
<view class="key-right-top">{{ key.keyName }}</view>
<view class="key-status" :style="{ color: (key.keyStatus === 110401) ? '#63b8af' : '#df282d' }">
{{ getKeyStatus(key.keyStatus) }}
</view>
</view>
<view class="key-right-bottom">{{ key.timeText }}</view>
</view>
</view>
<view class="line"></view>
</up-swipe-action-item>
</up-swipe-action>
</view>
</view>
</scroll-view>
<view class="button">
<view class="button-reset" @click="resetKey">重置钥匙</view>
<view class="button-create" @click="toCreateKey">发送钥匙</view>
</view>
</view>
</template>
<script>
import { useBasicStore } from '@/stores/basic'
import { mapActions, mapState } from 'pinia'
import { useBluetoothStore } from '@/stores/bluetooth'
import { useLockStore } from '@/stores/lock'
import { useUserStore } from '@/stores/user'
import { deletePsaawordRequest, resetPsaawordListRequest } from '@/api/keyboardPwd'
import { deleteKeyRequest, resetKeyRequest } from '@/api/key'
export default {
data () {
return {
deviceInfo: null,
refresherTriggered: false,
requestFinished: false,
options: [{
text: '删除',
style: {
backgroundColor: '#f56c6c'
}
}]
}
},
computed: {
...mapState(useUserStore, ['userInfo']),
...mapState(useBluetoothStore, ['currentLockInfo', 'keyId']),
...mapState(useLockStore, ['keyTotal', 'keyList', 'keySearch']),
},
async onLoad() {
uni.showLoading({
title: '加载中',
mask: true
})
this.deviceInfo = await this.getDeviceInfo()
this.updateKeySearch({
...this.keySearch,
lockId: this.currentLockInfo.lockId
})
const { code, meesage } = await this.getKeyList(this.keySearch)
this.requestFinished = true
uni.hideLoading()
},
methods: {
...mapActions(useBasicStore, ['routeJump', 'getDeviceInfo']),
...mapActions(useLockStore, ['getKeyList', 'updateCurrentKeyInfo', 'updateKeySearch', 'getKeyStatus']),
toKeyDetail(key) {
this.updateCurrentKeyInfo(key)
this.routeJump({
name: 'keyDetail'
})
},
async deleteKey(data) {
const key = data
const that = this
let index = this.keyList.findIndex(item => item.keyId === key.keyId)
that.$refs.swipeItem[index].closeHandler()
uni.showModal({
title: '提示',
content: '确定要删除该钥匙',
async success(res) {
if(res.confirm) {
uni.showLoading({
title: '删除中',
mask: true
})
const { code: requestCode, message } = await deleteKeyRequest({
keyId: key.keyId
})
if(requestCode === 0) {
uni.hideLoading()
uni.showToast({
title: '删除成功',
icon: 'none'
})
that.updateKeySearch({
...that.keySearch,
pageNo: 1
})
await that.getKeyList(that.keySearch)
} else {
uni.hideLoading()
uni.showToast({
title: message,
icon: 'none'
})
}
}
}
})
},
async resetKey() {
const that = this
uni.showModal({
title: '提示',
content: '确定要重置钥匙,该锁的所有钥匙都将被删除',
async success(res) {
if(res.confirm) {
const { code: requestCode, message } = await resetKeyRequest({
lockId: that.currentLockInfo.lockId,
})
console.log('重置钥匙返回', requestCode, message)
if(requestCode === 0) {
uni.showToast({
title: '重置钥匙成功',
icon: 'none'
})
that.updateKeySearch({
...that.keySearch,
pageNo: 1
})
await that.getKeyList(that.keySearch)
} else {
uni.showToast({
title: message,
icon: 'none'
})
}
}
}
})
},
toCreateKey() {
this.routeJump({
name: 'createKey'
})
},
async refresherList() {
this.refresherTriggered = true
this.updateKeySearch({
...this.keySearch,
pageNo: 1
})
const { code, meesage } = await this.getKeyList(this.keySearch)
if(code === 0) {
uni.showToast({
title: '刷新成功',
icon: 'none'
})
}
this.refresherTriggered = false
},
async nextPage() {
if(this.keyTotal <= this.keySearch.pageNo * this.keySearch.pageSize) {
return
}
const pageNo = this.keySearch.pageNo + 1
const params = {
...this.keySearch,
pageNo
}
const { code, meesage } = await this.getKeyList(params)
if(code === 0) {
this.updateKeySearch({
...this.keySearch,
pageNo
})
}
},
async changeSearch(data) {
this.updateKeySearch({
...this.keySearch,
searchStr: data
})
const { code, meesage } = await this.getKeyList(this.keySearch)
},
},
}
</script>
<style lang="scss">
page {
background-color: $uni-bg-color-grey;
}
</style>
<style lang="scss" scoped>
.search {
margin-top: 32rpx;
width: 686rpx !important;
margin-left: 32rpx;
}
.button {
display: flex;
align-items: center;
position: fixed;
bottom: calc(env(safe-area-inset-bottom) + 48rpx);
font-weight: bold;
.button-reset {
margin-left: 50rpx;
width: 300rpx;
height: 88rpx;
background-color: #df282d;
color: white;
text-align: center;
line-height: 88rpx;
border-radius: 44rpx;
}
.button-create {
margin-left: 50rpx;
width: 300rpx;
height: 88rpx;
background-color: #63b8af;
color: white;
text-align: center;
line-height: 88rpx;
border-radius: 44rpx;
}
}
.key {
display: flex;
align-items: center;
background-color: #FFFFFF;
height: 120rpx;
width: 750rpx;
.key-left {
margin-left: 32rpx;
width: 80rpx;
height: 80rpx;
border-radius: 50%;
}
.key-right {
margin-left: 32rpx;
margin-right: 32rpx;
width: 574rpx;
.key-right-top {
font-size: 32rpx;
font-weight: bold;
padding-bottom: 6rpx;
}
.key-right-bottom {
font-size: 26rpx;
color: #999999;
}
}
.key-status {
margin-left: auto;
font-size: 26rpx;
color: #63b8af;
}
}
.line {
width: 100%;
height: 2rpx;
background: #EBEBEB;
}
.empty-list {
width: 150rpx;
height: 150rpx;
margin: 300rpx auto 20rpx 50%;
transform: translateX(-50%);
}
.empty-list-text {
text-align: center;
font-size: 32rpx;
color: #999999;
}
</style>

View File

@ -1,17 +1,332 @@
<template>
<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="/static/images/icon_power.png" 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) }}</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.userType === 110301">
<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.userType === 110301"
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.userType === 110301"
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>
</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>
</view>
</template>
<script>
import { useBluetoothStore } from '@/stores/bluetooth'
import { useBasicStore } from '@/stores/basic'
import { mapState, mapActions } from 'pinia'
import SwitchLoading from '@/components/SwitchLoading/SwitchLoading.vue'
import { useLockStore } from '@/stores/lock'
import { useUserStore } from '@/stores/user'
import { getLockNetTokenRequest } from '@/api/lock'
import { timeFormat } from 'uview-plus'
export default {
data () {
return {}
return {
time: 0,
onlineToken: '0',
pending: false
}
},
computed: {
...mapState(useBluetoothStore, ['currentLockInfo']),
...mapState(useUserStore, ['userInfo'])
},
components: {
SwitchLoading
},
onLoad() {
uni.setNavigationBarTitle({
title: this.currentLockInfo.lockAlias
})
this.getServeTime()
},
methods: {
...mapActions(useLockStore, ['getRole']),
...mapActions(useBluetoothStore, ['openDoor', 'updateServerTimestamp']),
...mapActions(useBasicStore, ['routeJump']),
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
} else {
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)
}
},
async openDoorOperate(type) {
if(this.pending) {
return
}
uni.vibrateLong()
this.pending = true
this.$refs.loading.open()
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: openMode,
openTime: parseInt(new Date().getTime() / 1000) + this.time,
onlineToken: this.onlineToken
})
if(code === 0) {
uni.showToast({
title: `${type === 'close' ? '关' : '开'}锁成功`,
icon: 'none'
})
} else {
uni.showToast({
title: `${type === 'close' ? '关' : '开'}锁失败,请保证在锁附近`,
icon: 'none'
})
}
this.$refs.loading.close()
this.pending = false
}
}
}
</script>
<style lang="scss">
<style lang="scss" scoped>
.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: 50rpx;
}
.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;
padding-bottom: 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;
padding-bottom: 32rpx;
display: flex;
align-items: center;
font-size: 32rpx;
flex-wrap: wrap;
text-align: center;
margin-left: 43rpx;
.menu-main-view {
width: 150rpx;
.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>

View File

@ -31,35 +31,132 @@
<image class="icon-arrow" src="/static/images/icon_arrow.png" mode="aspectFill"></image>
</view>
</view>
<view class="button-logout" @click="logout">退出</view>
<view class="env" v-if="envVersion !== 'release' && env">
<view class="env-text">{{env.name}}环境 版本号{{env.version}}+{{env.buildNumber}}</view>
<view class="env-button" @click="show=true">切换环境</view>
</view>
<label for="changePhone">
<view class="switch-account">切换账号</view>
</label>
</view>
<view v-else>
<view class="button-login" @click="login">登录</view>
<view class="tips">因智能门锁与账号绑定登录为手机号登录</view>
<label for="phone">
<view class="button-login">登录</view>
</label>
<view class="env" v-if="envVersion !== 'release' && env">
<view class="env-text">{{env.name}}环境 版本号{{env.version}}+{{env.buildNumber}}</view>
<view class="env-button" @click="show=true">切换环境</view>
</view>
</view>
</view>
<button open-type="contact" id="contact"></button>
<button open-type="contact" style="display:none" id="contact"></button>
<button open-type="getPhoneNumber" style="display:none" id="phone" @getphonenumber="getphonenumber"></button>
<button open-type="getPhoneNumber" style="display:none" id="changePhone" @getphonenumber="changePhone"></button>
<up-action-sheet :actions="envList" :closeOnClickOverlay="true" title="切换环境" cancelText="取消" :closeOnClickAction="true"
:show="show" :safeAreaInsetBottom="true" @close="show=false" @select="selectEnv"></up-action-sheet>
</template>
<script>
import { useBasicStore } from '@/stores/basic'
import { useUserStore } from '@/stores/user'
import { useLockStore } from '@/stores/lock'
import { mapState, mapActions } from 'pinia'
import { phoneLoginRequest } from '@/api/user'
import env from '@/config/env'
export default {
data() {
return {
buttonInfo: null
buttonInfo: null,
env: null,
envVersion: '',
envList: [],
show: false
}
},
computed: {
...mapState(useUserStore, ['isLogin'])
...mapState(useUserStore, ['isLogin']),
...mapState(useLockStore, ['lockSearch'])
},
async onLoad() {
this.buttonInfo = await this.getButtonInfo()
this.env = env[getApp().globalData.getEnvConfig()]
this.envVersion = getApp().globalData.envVersion
for (let key in env) {
this.envList.push({
...env[key],
key: key
})
}
console.log(this.envList)
},
methods: {
...mapActions(useBasicStore, ['getButtonInfo', 'routeJump']),
...mapActions(useUserStore, ['updateLoginStatus', 'login']),
...mapActions(useLockStore, ['getLockList', 'updateLockSearch']),
...mapActions(useUserStore, ['updateLoginStatus', 'phoneLogin', 'updateUserInfo', 'getUserInfo',
'checkSession']),
selectEnv(env) {
uni.setStorageSync('envVersion', env.key)
uni.removeStorageSync('token')
uni.removeStorageSync('openid')
uni.reLaunch({
url: '/pages/home/home'
})
},
async changePhone(res) {
if(res.detail.errMsg === 'getPhoneNumber:fail user deny') {
return
}
const result = await this.checkSession()
if(!result) {
uni.showToast({
title: '登录失败,请重试',
icon: 'none'
})
return
}
const openid = uni.getStorageSync('openid')
const { code, data, message } = await phoneLoginRequest({
encryptedData: res.detail.encryptedData,
iv: res.detail.iv,
rebind: true,
openid
})
if(code === 0) {
uni.setStorageSync('token', data.accessToken)
this.updateLockSearch({
...this.lockSearch,
pageNo: 1
})
this.getUserInfo()
this.getLockList(this.lockSearch)
uni.showToast({
title: '账号切换成功',
icon: 'none'
})
} else {
uni.showToast({
title: message,
icon: 'none'
})
}
},
async getphonenumber(data) {
if(data.detail.errMsg === 'getPhoneNumber:fail user deny') {
return
}
const result = await this.phoneLogin({
encryptedData: data.detail.encryptedData,
iv: data.detail.iv
})
if(!result) {
uni.showToast({
title: '登录失败,请重试',
icon: 'none'
})
}
},
toUsereInfo() {
this.routeJump({
name: 'userInfo'
@ -72,21 +169,8 @@
type
}
})
},
logout() {
const that = this
uni.showModal({
title: '提示',
content: '确定退出登录吗?',
success: (res) => {
if (res.confirm) {
uni.removeStorageSync('token')
that.updateLoginStatus(false)
}
}
})
}
},
}
}
</script>
@ -136,23 +220,48 @@ page {
background: #EBEBEB;
}
.button-logout {
.switch-account {
position: absolute;
border-radius: 46rpx;
bottom: 60rpx;
width: 710rpx;
width: 600rpx;
height: 80rpx;
line-height: 80rpx;
text-align: center;
margin-left: 20rpx;
background: #ec433c;
margin-left: 75rpx;
background: #63b8af;
color: #ffffff;
font-size: 40rpx;
font-weight: bold;
}
.env {
position: absolute;
bottom: 180rpx;
width: 600rpx;
line-height: 80rpx;
text-align: center;
margin-left: 75rpx;
font-size: 40rpx;
font-weight: bold;
.env-text {
color: #999999;
font-size: 30rpx;
}
.env-button {
border-radius: 46rpx;
background: #f0ad4e;
color: #ffffff;
font-size: 40rpx;
font-weight: bold;
width: 600rpx;
height: 80rpx;
}
}
.button-login {
margin-top: 40vh;
border-radius: 46rpx;
width: 650rpx;
height: 120rpx;
@ -164,4 +273,12 @@ page {
font-size: 48rpx;
font-weight: bold;
}
.tips {
margin-top: 35vh;
padding: 32rpx 0;
text-align: center;
font-size: 28rpx;
color: #999999;
}
</style>

View File

@ -0,0 +1,148 @@
<template>
<view>
<view class="item">
<view class="item-title">密码</view>
<view class="item-content">{{ currentPasswordInfo.keyboardPwd }}</view>
</view>
<view class="item" style="margin-top: 2rpx">
<view class="item-title">名称</view>
<view class="item-content">{{ currentPasswordInfo.keyboardPwdName }}</view>
</view>
<view class="item" style="margin-top: 2rpx">
<view class="item-title">有效期</view>
<view v-if="currentPasswordInfo.keyboardPwdType === 2">永久</view>
<view v-else>
<view class="item-content">{{ timeFormat(currentPasswordInfo.startDate, 'yyyy-mm-dd h:M') }}</view>
<view class="item-content">{{ timeFormat(currentPasswordInfo.endDate, 'yyyy-mm-dd h:M') }}</view>
</view>
</view>
<view class="item" style="margin-top: 20rpx">
<view class="item-title">发送人</view>
<view class="item-content">{{ currentPasswordInfo.senderUsername }}</view>
</view>
<view class="item" style="margin-top: 2rpx">
<view class="item-title">发送时间</view>
<view class="item-content">{{ timeFormat(currentPasswordInfo.sendDate, 'yyyy-mm-dd h:M') }}</view>
</view>
<view class="tips">密码生成后请在当日2359前使用一次进行激活否则过0点后未激活则失效</view>
<view class="button" @click="deletePassword">删除密码</view>
</view>
</template>
<script>
import { mapActions, mapState } from 'pinia'
import { useLockStore } from '@/stores/lock'
import { timeFormat } from 'uview-plus'
import { deletePsaawordRequest } from '@/api/keyboardPwd'
import { useBluetoothStore } from '@/stores/bluetooth'
import { useUserStore } from '@/stores/user'
import { useBasicStore } from '@/stores/basic'
export default {
data () {
return {}
},
computed: {
...mapState(useLockStore, ['currentPasswordInfo', 'passwordSearch']),
...mapState(useBluetoothStore, ['currentLockInfo', 'keyId']),
...mapState(useUserStore, ['userInfo'])
},
methods: {
timeFormat,
...mapActions(useBluetoothStore, ['setLockPassword']),
...mapActions(useLockStore, ['updatePasswordSearch', 'getPasswordList']),
...mapActions(useBasicStore, ['backAndToast']),
async deletePassword () {
const that = this
uni.showModal({
title: '提示',
content: '确定要删除该密码',
async success(res) {
if(res.confirm) {
uni.showLoading({
title: '删除中',
mask: true
})
const timestamp = parseInt(new Date().getTime() / 1000)
const { code } = await that.setLockPassword({
keyId: that.keyId.toString(),
uid: that.userInfo.uid.toString(),
pwdNo: that.currentPasswordInfo.pwdUserNo,
operate: 3,
isAdmin: that.currentPasswordInfo.pwdRight,
pwd: that.currentPasswordInfo.keyboardPwd,
userCountLimit: 0xFFFF,
startTime: timestamp,
endTime: timestamp
})
if(code === 0) {
const { code: requestCode, message } = await deletePsaawordRequest({
lockId: that.currentLockInfo.lockId,
keyboardPwdId: that.currentPasswordInfo.keyboardPwdId,
deleteType: 1
})
if(requestCode === 0) {
uni.hideLoading()
that.updatePasswordSearch({
...that.passwordSearch,
pageNo: 1
})
that.getPasswordList(that.passwordSearch)
that.backAndToast('删除成功')
} else {
uni.hideLoading()
uni.showToast({
title: message,
icon: 'none'
})
}
} else {
uni.hideLoading()
uni.showToast({
title: '删除失败,请保持在锁附近',
icon: 'none'
})
}
}
}
})
}
}
}
</script>
<style lang="scss">
page {
background-color: $uni-bg-color-grey;
}
</style>
<style lang="scss" scoped>
.item {
padding: 24rpx 32rpx;
background-color: #FFFFFF;
display: flex;
align-items: center;
justify-content: space-between;
font-size: 32rpx;
font-weight: 500;
}
.tips {
padding: 24rpx 32rpx;
font-size: 24rpx;
color: #999999;
}
.button {
margin: 32rpx;
width: 686rpx;
height: 88rpx;
background-color: #df282d;
color: white;
text-align: center;
line-height: 88rpx;
border-radius: 44rpx;
font-weight: bold;
}
</style>

View File

@ -0,0 +1,334 @@
<template>
<view>
<scroll-view v-if="deviceInfo" scroll-y="true" :style="{height: deviceInfo.screenHeight - deviceInfo.safeArea.top + 'px'}" lower-threshold="100"
@refresherrefresh="refresherList" :refresher-enabled="true" @scrolltolower="nextPage"
:refresher-triggered="refresherTriggered">
<view class="search">
<up-search shape="square" :searchIconSize="48" :inputStyle="{ fontSize: '32rpx' }" :height="80" placeholder="搜索"
:clearabled="false" @change="changeSearch"
v-model="passwordSearch.searchStr" bgColor="#ffffff" :showAction="false" maxlength="20"></up-search>
</view>
<view style="padding: 32rpx 0 calc(env(safe-area-inset-bottom) + 250rpx) 0">
<view v-if="passwordList.length === 0 && requestFinished">
<image class="empty-list" src="/static/images/background_empty_list.png" mode="aspectFill"></image>
<view class="empty-list-text">暂无数据</view>
</view>
<view v-else>
<up-swipe-action>
<up-swipe-action-item ref="swipeItem" :options="options" v-for="(password, index) in passwordList"
:key="password.keyboardPwdId" :threshold="50" @click="deletePassword(password)">
<view class="password" @click="toPasswordDetail(password)">
<image class="password-left" src="/static/images/icon_lock_transparent.png" mode="aspectFill"></image>
<view class="password-right">
<view class="password-right-top">{{ password.keyboardPwdName }}</view>
<view class="password-right-bottom">{{ password.timeText }}</view>
</view>
</view>
<view class="line"></view>
</up-swipe-action-item>
</up-swipe-action>
</view>
</view>
</scroll-view>
<view class="button">
<view class="button-reset" @click="resetPassword">重置密码</view>
<view class="button-create" @click="toCreatePassword">获取密码</view>
</view>
</view>
</template>
<script>
import { useBasicStore } from '@/stores/basic'
import { mapActions, mapState } from 'pinia'
import { useBluetoothStore } from '@/stores/bluetooth'
import { useLockStore } from '@/stores/lock'
import { useUserStore } from '@/stores/user'
import { deletePsaawordRequest, resetPsaawordListRequest } from '@/api/keyboardPwd'
export default {
data () {
return {
deviceInfo: null,
refresherTriggered: false,
requestFinished: false,
options: [{
text: '删除',
style: {
backgroundColor: '#f56c6c'
}
}]
}
},
computed: {
...mapState(useUserStore, ['userInfo']),
...mapState(useBluetoothStore, ['currentLockInfo', 'keyId']),
...mapState(useLockStore, ['passwordTotal', 'passwordList', 'passwordSearch']),
},
async onLoad() {
uni.showLoading({
title: '加载中',
mask: true
})
this.deviceInfo = await this.getDeviceInfo()
this.updatePasswordSearch({
...this.passwordSearch,
lockId: this.currentLockInfo.lockId,
lockStatus: this.currentLockInfo.lockStatus
})
const { code, meesage } = await this.getPasswordList(this.passwordSearch)
this.requestFinished = true
uni.hideLoading()
},
methods: {
...mapActions(useBasicStore, ['routeJump', 'getDeviceInfo']),
...mapActions(useLockStore, ['getPasswordList', 'updateCurrentPasswordInfo', 'updatePasswordSearch']),
...mapActions(useBluetoothStore, ['resetLockPassword', 'setLockPassword']),
toPasswordDetail(password) {
this.updateCurrentPasswordInfo(password)
this.routeJump({
name: 'passwordDetail'
})
},
async deletePassword(data) {
const password = data
const that = this
let index = this.passwordList.findIndex(item => item.keyboardPwdId === password.keyboardPwdId)
that.$refs.swipeItem[index].closeHandler()
uni.showModal({
title: '提示',
content: '确定要删除该密码',
async success(res) {
if(res.confirm) {
uni.showLoading({
title: '删除中',
mask: true
})
const timestamp = parseInt(new Date().getTime() / 1000)
const { code } = await that.setLockPassword({
keyId: that.keyId.toString(),
uid: that.userInfo.uid.toString(),
pwdNo: password.pwdUserNo,
operate: 3,
isAdmin: password.pwdRight,
pwd: password.keyboardPwd,
userCountLimit: 0xFFFF,
startTime: timestamp,
endTime: timestamp
})
if(code === 0) {
const { code: requestCode, message } = await deletePsaawordRequest({
lockId: that.currentLockInfo.lockId,
keyboardPwdId: password.keyboardPwdId,
deleteType: 1
})
if(requestCode === 0) {
uni.hideLoading()
uni.showToast({
title: '删除成功',
icon: 'none'
})
that.updatePasswordSearch({
...that.passwordSearch,
pageNo: 1
})
await that.getPasswordList(that.passwordSearch)
} else {
uni.hideLoading()
uni.showToast({
title: message,
icon: 'none'
})
}
} else {
uni.hideLoading()
uni.showToast({
title: '删除失败,请保持在锁附近',
icon: 'none'
})
}
}
}
})
},
async resetPassword() {
const that = this
uni.showModal({
title: '提示',
content: '确定要重置密码,该锁的所有密码都将被删除',
async success(res) {
if(res.confirm) {
const { code } = await that.resetLockPassword({
uid: that.userInfo.uid.toString(),
keyId: that.currentLockInfo.keyId.toString()
})
if(code === 0) {
const { code: requestCode, message } = await resetPsaawordListRequest({
lockId: that.currentLockInfo.lockId,
passwordKey: that.currentLockInfo.bluetooth.passwordKey
})
console.log('重置密码返回', requestCode, message)
if(requestCode === 0) {
uni.showToast({
title: '重置密码成功',
icon: 'none'
})
that.updatePasswordSearch({
...that.passwordSearch,
pageNo: 1
})
that.getPasswordList(that.passwordSearch)
} else {
uni.showToast({
title: message,
icon: 'none'
})
}
} else {
uni.showToast({
title: '重置密码失败,请保持在锁附近',
icon: 'none'
})
}
}
}
})
},
toCreatePassword() {
this.routeJump({
name: 'createPassword'
})
},
async refresherList() {
this.refresherTriggered = true
this.updatePasswordSearch({
...this.passwordSearch,
pageNo: 1
})
const { code, meesage } = await this.getPasswordList(this.passwordSearch)
if(code === 0) {
uni.showToast({
title: '刷新成功',
icon: 'none'
})
}
this.refresherTriggered = false
},
async nextPage() {
if(this.passwordTotal <= this.passwordSearch.pageNo * this.passwordSearch.pageSize) {
return
}
const pageNo = this.passwordSearch.pageNo + 1
const params = {
...this.passwordSearch,
pageNo
}
const { code, meesage } = await this.getPasswordList(params)
if(code === 0) {
that.updatePasswordSearch({
...that.passwordSearch,
pageNo
})
}
},
async changeSearch(data) {
this.updatePasswordSearch({
...this.passwordSearch,
searchStr: data
})
const { code, meesage } = await this.getPasswordList(this.passwordSearch)
},
},
}
</script>
<style lang="scss">
page {
background-color: $uni-bg-color-grey;
}
</style>
<style lang="scss" scoped>
.search {
margin-top: 32rpx;
width: 686rpx !important;
margin-left: 32rpx;
}
.button {
display: flex;
align-items: center;
position: fixed;
bottom: calc(env(safe-area-inset-bottom) + 48rpx);
font-weight: bold;
.button-reset {
margin-left: 50rpx;
width: 300rpx;
height: 88rpx;
background-color: #df282d;
color: white;
text-align: center;
line-height: 88rpx;
border-radius: 44rpx;
}
.button-create {
margin-left: 50rpx;
width: 300rpx;
height: 88rpx;
background-color: #63b8af;
color: white;
text-align: center;
line-height: 88rpx;
border-radius: 44rpx;
}
}
.password {
display: flex;
align-items: center;
background-color: #FFFFFF;
height: 120rpx;
width: 750rpx;
.password-left {
margin-left: 32rpx;
width: 80rpx;
height: 80rpx;
}
.password-right {
margin-right: 32rpx;
.password-right-top {
font-size: 32rpx;
font-weight: bold;
padding-bottom: 6rpx;
}
.password-right-bottom {
font-size: 26rpx;
color: #999999;
}
}
}
.line {
width: 100%;
height: 2rpx;
background: #EBEBEB;
}
.empty-list {
width: 150rpx;
height: 150rpx;
margin: 300rpx auto 20rpx 50%;
transform: translateX(-50%);
}
.empty-list-text {
text-align: center;
font-size: 32rpx;
color: #999999;
}
</style>

View File

@ -1,17 +1,162 @@
<template>
<view>
<scroll-view v-if="deviceInfo" class="scroll-view" scroll-y="true" :style="{ height: deviceInfo.screenHeight - deviceInfo.statusBarHeight + 'px' }">
<view style="padding-bottom: calc(env(safe-area-inset-bottom) + 300rpx)">
<view class="device" v-for="device in deviceList" @click="connect(device)">
<view class="device" style="justify-content:flex-start;">
<image class="device-lock" src="/static/images/icon_door_lock.png"></image>
<view class="device-name">{{device.name}}</view>
</view>
<image class="device-add" src="/static/images/icon_add.png"></image>
</view>
</view>
</scroll-view>
<view class="loading">
<up-loading-icon size="80rpx" text="搜索中" :vertical="true" textSize="32rpx"></up-loading-icon>
</view>
</view>
</template>
<script>
import { mapState, mapActions } from 'pinia'
import { useBluetoothStore } from '@/stores/bluetooth'
import { useBasicStore } from '@/stores/basic'
import { useUserStore } from '@/stores/user'
export default {
data () {
return {}
return {
deviceInfo: null
}
},
computed: {
...mapState(useBluetoothStore, ['deviceList', 'currentLockInfo', 'serverTimestamp', 'keyId']),
...mapState(useUserStore, ['userInfo'])
},
async onLoad() {
this.deviceInfo = await this.getDeviceInfo()
this.getBluetoothDevices()
},
onUnload() {
this.stopGetBluetoothDevices()
},
methods: {
...mapActions(useBluetoothStore, ['getBluetoothDevices', 'stopGetBluetoothDevices', 'updateCurrentLockInfo',
'getPublicKey', 'getCommKey', 'connectBluetoothDevice', 'updateServerTimestamp', 'getLockStatus']),
...mapActions(useBasicStore, ['getDeviceInfo', 'routeJump']),
async connect(device) {
uni.showLoading({
title: '连接中',
mask: true
})
const { code: serverTimestampCode, message } = await this.updateServerTimestamp()
if(serverTimestampCode !== 0) {
uni.showToast({
title: message,
icon: 'none'
})
uni.hideLoading()
return
}
this.updateCurrentLockInfo({
name: device.name,
deviceId: device.deviceId
})
const result = await this.connectBluetoothDevice()
this.stopGetBluetoothDevices()
if(result) {
const { code: getPublicKeyCode } = await this.getPublicKey(this.currentLockInfo.name)
console.log('获取公钥返回', getPublicKeyCode, [...this.currentLockInfo.publicKey])
if(getPublicKeyCode !== 0) {
uni.hideLoading()
uni.showToast({
title: '连接失败,请重试',
icon: 'none'
})
return
}
const { code: getCommKeyCode } = await this.getCommKey(this.currentLockInfo.name, this.keyId,
this.userInfo.uid.toString(), this.serverTimestamp)
console.log('获取私钥返回', getCommKeyCode)
if(getCommKeyCode !== 0) {
uni.hideLoading()
uni.showToast({
title: '连接失败,请重试',
icon: 'none'
})
return
}
const date = new Date()
const timestamp = parseInt(date.getTime() / 1000) - date.getTimezoneOffset() * 60
const { code } = await this.getLockStatus({
name: this.currentLockInfo.name,
uid: this.userInfo.uid,
nowTime: this.serverTimestamp,
localTime: timestamp
})
if (code !== 0) {
uni.hideLoading()
uni.showToast({
title: '连接失败,请重试',
icon: 'none'
})
return
}
this.routeJump({
type: 'redirectTo',
name: 'selectAddress'
})
} else {
this.getBluetoothDevices()
uni.showToast({
title: '连接失败,请重试',
icon: 'none'
})
uni.hideLoading()
}
}
}
}
</script>
<style lang="scss">
page {
background-color: $uni-bg-color-grey;
}
</style>
<style lang="scss" scoped>
.loading {
position: fixed;
bottom: calc(env(safe-area-inset-bottom) + 50rpx) ;
left: 50%;
transform: translateX(-50%);
}
.device {
background: #ffffff;
display: flex;
align-items: center;
justify-content: space-between;
height: 100rpx;
.device-lock {
margin-left: 24rpx;
width: 72rpx;
height: 72rpx;
}
.device-name {
margin-left: 24rpx;
font-size: 28rpx;
font-weight: bold;
}
.device-add {
float: right;
width: 60rpx;
height: 60rpx;
margin-right: 24rpx;
}
}
</style>

View File

@ -0,0 +1,256 @@
<template>
<view>
<view class="title">地理位置</view>
<view v-if="show">
<map class="map" :longitude="longitude" :latitude="latitude" :scale="16" :markers="markers"
:enable-zoom="false" :enable-scroll="false"></map>
</view>
<view v-else class="map"></view>
<view class="explain">检查以确保以下地址是正确的</view>
<view class="view-address" :style="{height: calculateStringTotalWidth(address) > 44 ? '120rpx' : '80rpx'}">
<view class="address">{{ address }}</view>
</view>
<view class="bottom">
<view class="skip" @click="skipAddress">跳过</view>
<view class="confirm" @click="toBindLock">下一步</view>
</view>
</view>
</template>
<script>
import { test } from 'uview-plus'
import { useBasicStore } from '@/stores/basic'
import { mapState, mapActions } from 'pinia'
import { getGeocodeAddress } from '@/api/geocode'
import { useBluetoothStore } from '@/stores/bluetooth'
export default {
data () {
return {
latitude: null,
longitude: null,
show: false,
markers: [],
address: '',
first: true
}
},
computed: {
...mapState(useBluetoothStore, ['currentLockInfo'])
},
onLoad() {
this.getLocation()
},
onShow() {
if(this.first) {
this.first = false
} else {
if(!this.show) {
this.getLocation()
}
}
},
methods: {
...mapActions(useBasicStore, ['routeJump', 'calculateStringTotalWidth']),
...mapActions(useBluetoothStore, ['updateCurrentLockInfo']),
toBindLock() {
const that = this
if(this.address === '') {
uni.showModal({
title: '提示',
content: '暂未获取到地理位置信息,请重试',
confirmText: '重试',
success: (res) => {
if(res.confirm) {
that.getLocation()
}
}
})
return
}
this.routeJump({
type: 'redirectTo',
name: 'bindLock'
})
},
skipAddress() {
const that = this
if(this.address !== '') {
uni.showModal({
title: '提示',
content: '已获取当前地理位置,确定跳过吗',
confirmText: '跳过',
cancelText: '取消',
success: (res) => {
if(res.confirm) {
const lockInfo = that.currentLockInfo
delete lockInfo.position
that.updateCurrentLockInfo(lockInfo)
this.routeJump({
type: 'redirectTo',
name: 'bindLock'
})
}
}
})
return
}
this.routeJump({
type: 'redirectTo',
name: 'bindLock'
})
},
async getLocation() {
const that = this
uni.getLocation({
isHighAccuracy: true,
type: 'gcj02',
async success (res) {
that.latitude = res.latitude
that.longitude = res.longitude
that.markers = [{
id: 1,
latitude: res.latitude,
longitude: res.longitude,
iconPath: '/static/images/icon_address.png',
width: '48rpx',
height: '48rpx'
}]
that.show = true
const { code, data: result } = await getGeocodeAddress({
latitude: that.latitude,
longitude: that.longitude
})
if(code === 0) {
that.address = result.addr
const position = {
...result,
latitude: that.latitude,
longitude: that.longitude
}
console.log('获取地理位置信息', that.currentLockInfo)
that.updateCurrentLockInfo({
...that.currentLockInfo,
position
})
console.log('获取地理位置信息', that.currentLockInfo)
}
},
fail (res) {
if(res.errMsg === 'getLocation:fail auth deny') {
uni.showModal({
title: '提示',
content: '拒绝授权将无法使用定位功能',
confirmText: '去授权',
cancelText: '跳过',
success (res) {
if (res.confirm) {
uni.openSetting()
} else {
that.routeJump({
type: 'redirectTo',
name: 'bindLock'
})
}
}
})
} else {
uni.showModal({
title: '提示',
content: '获取地理位置信息失败',
confirmText: '重试',
cancelText: '跳过',
success (res) {
if (res.confirm) {
that.getLocation()
} else {
that.routeJump({
type: 'redirectTo',
name: 'bindLock'
})
}
}
})
}
}
})
}
},
}
</script>
<style lang="scss">
page {
background-color: $uni-bg-color-grey;
}
</style>
<style lang="scss" scoped>
.map {
width: 750rpx;
height: 650rpx;
}
.title {
padding: 40rpx 30rpx;
font-size: 80rpx;
font-weight: bold;
}
.explain {
padding: 20rpx 30rpx 0 30rpx;
font-size: 40rpx;
}
.view-address {
display: flex;
align-items: center;
width: 690rpx;
color: #c4c4c4;
font-size: 32rpx;
margin: 0 30rpx;
border-bottom: #63b8af 3rpx solid;
.address {
width: 690rpx;
text-align: left;
word-break: break-all;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 2;
overflow: hidden;
text-overflow: ellipsis;
white-space: normal;
}
}
.bottom {
width: 600rpx;
display: flex;
justify-content: space-between;
padding: 0 75rpx;
position: fixed;
bottom: calc(30rpx + env(safe-area-inset-bottom));
.skip {
width: 225rpx;
height: 80rpx;
line-height: 80rpx;
text-align: center;
font-size: 36rpx;
border-radius: 64rpx;
border: 3rpx solid #63b8af;
}
.confirm {
border-radius: 64rpx;
color: #FFFFFF;
background-color: #63b8af;
width: 225rpx;
height: 80rpx;
line-height: 80rpx;
text-align: center;
font-size: 36rpx;
}
}
</style>

218
pages/setting/setting.vue Normal file
View File

@ -0,0 +1,218 @@
<template>
<view>
<view class="view">
<view class="view-button">
<view>名称</view>
<view class="view-button" style="padding: 0">
<view class="info">{{currentLockInfo.lockAlias}}</view>
</view>
</view>
<view class="view-button">
<view>锁编号</view>
<view class="view-button" style="padding: 0">
<view class="info">{{currentLockInfo.name}}</view>
</view>
</view>
<view class="view-line"></view>
<view class="view-button">
<view>MAC/ID</view>
<view class="view-button" style="padding: 0">
<view class="info">{{currentLockInfo.mac}}</view>
</view>
</view>
<view class="view-line"></view>
<view class="view-button">
<view>电量</view>
<view class="view-button" style="padding: 0">
<view class="info">{{currentLockInfo.electricQuantity}}%</view>
</view>
</view>
</view>
<view class="view" v-if="currentLockInfo.userType === 110301">
<view class="view-button">
<view>开锁时是否需联网</view>
<view class="view-button" style="padding: 0">
<up-switch v-model="unlockApp" :size="40" activeColor="#63b8af" :asyncChange="true"
@change="changeUnlockApp" :activeValue="1" :inactiveValue="0"></up-switch>
</view>
</view>
</view>
<view class="button-logout" @click="deleteLock">删除</view>
</view>
</template>
<script>
import { useBluetoothStore } from '@/stores/bluetooth'
import { useUserStore } from '@/stores/user'
import { mapActions, mapState } from 'pinia'
import { deleteLockRequest } from '@/api/lock'
import { useLockStore } from '@/stores/lock'
import { updateLockSettingRequest } from '@/api/lockSetting'
import { deleteKeyRequest } from '@/api/key'
import { useBasicStore } from '@/stores/basic'
export default {
data () {
return {
unlockApp: 0
}
},
computed: {
...mapState(useUserStore, ['userInfo']),
...mapState(useBluetoothStore, ['keyId', 'currentLockInfo']),
...mapState(useLockStore, ['lockSearch'])
},
onLoad() {
this.unlockApp = this.currentLockInfo.lockSetting.appUnlockOnline
},
methods: {
...mapActions(useBluetoothStore, ['resetDevice', 'updateCurrentLockInfo']),
...mapActions(useLockStore, ['getLockList', 'updateLockSearch']),
...mapActions(useBasicStore, ['backAndToast']),
async changeUnlockApp(value) {
uni.showLoading({
title: '更新中',
mask: true
})
const { code, message } = await updateLockSettingRequest({
lockId: this.currentLockInfo.lockId,
appUnlockOnline: value
})
if(code === 0) {
this.unlockApp = value
const data = this.currentLockInfo
data.lockSetting.appUnlockOnline = value
this.updateCurrentLockInfo(data)
uni.hideLoading()
uni.showToast({
title: '更新成功',
icon: 'none'
})
} else {
uni.hideLoading()
uni.showToast({
title: message,
icon: 'none'
})
}
},
deleteLock() {
const that = this
uni.showModal({
title: '提示',
content: '确定删除该门锁吗?',
success: async function (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) {
const { code, message } = await deleteLockRequest({
lockId: that.currentLockInfo.lockId
})
if(code === 0) {
uni.hideLoading()
that.updateLockSearch({
...that.lockSearch,
pageNo: 1
})
that.getLockList(that.lockSearch)
that.backAndToast('删除成功', 2)
uni.navigateBack()
} else {
uni.hideLoading()
uni.showToast({
title: 'message',
icon: 'none'
})
}
} else {
uni.hideLoading()
uni.showToast({
title: '删除失败',
icon: 'none'
})
}
} else {
const { code } = await deleteKeyRequest({
keyId: that.keyId
})
if(code === 0) {
uni.hideLoading()
that.updateLockSearch({
...that.lockSearch,
pageNo: 1
})
that.getLockList(that.lockSearch)
that.backAndToast('删除成功', 2)
uni.navigateBack()
} else {
uni.hideLoading()
uni.showToast({
title: 'message',
icon: 'none'
})
}
}
}
}
})
}
}
}
</script>
<style lang="scss">
page {
background-color: $uni-bg-color-grey;
}
</style>
<style lang="scss" scoped>
.button-logout {
position: absolute;
border-radius: 46rpx;
bottom: calc(env(safe-area-inset-bottom) + 30rpx);
width: 600rpx;
height: 80rpx;
line-height: 80rpx;
text-align: center;
margin-left: 75rpx;
background: #ec433c;
color: #ffffff;
font-size: 40rpx;
font-weight: bold;
}
.view {
margin-top: 32rpx;
border-radius: 32rpx;
width: 710rpx;
margin-left: 20rpx;
background: #FFFFFF;
}
.view-button {
padding: 0 40rpx;
display: flex;
justify-content: space-between;
align-items: center;
color: #292826;
font-size: 32rpx;
font-weight: bold;
line-height: 80rpx;
}
.view-line {
width: 100%;
height: 3rpx;
background: #EBEBEB;
}
</style>

View File

@ -42,7 +42,7 @@ export default {
},
methods: {
...mapActions(useUserStore, ['updateUserInfo']),
...mapActions(useBasicStore, ['routeJump']),
...mapActions(useBasicStore, ['routeJump', 'backAndToast']),
updateInputEmail(data) {
this.email = data.detail.value
},
@ -100,14 +100,7 @@ export default {
...this.userInfo,
email: this.email
})
uni.navigateBack({
complete () {
uni.showToast({
title: '邮箱更新成功',
icon: 'none'
})
}
})
this.backAndToast('邮箱绑定成功')
} else {
uni.showToast({
title: message,

View File

@ -10,6 +10,7 @@
import { mapActions, mapState } from 'pinia'
import { useUserStore } from '@/stores/user'
import { updateUserInfoRequest } from '@/api/user'
import { useBasicStore } from '@/stores/basic'
export default {
data() {
@ -26,6 +27,7 @@ export default {
},
methods: {
...mapActions(useUserStore, ['updateUserInfo']),
...mapActions(useBasicStore, ['backAndToast']),
updateInput(data) {
this.nickname = data.detail.value
console.log(data)
@ -50,14 +52,7 @@ export default {
...this.userInfo,
nickname: this.nickname
})
uni.navigateBack({
complete() {
uni.showToast({
title: '昵称更新成功',
icon: 'none'
})
}
})
this.backAndToast('昵称更新成功')
} else {
uni.showToast({
title: '昵称更新失败',

View File

@ -1,11 +1,12 @@
<template>
<view>
<input class="input" :password="true" style="margin-top: 48rpx;" :value="oldPassword" maxlength="20"
placeholder="请输入原密码" placeholder-class="input-placeholder" :focus="true" @input="updateOldPassword"></input>
<input class="input" :password="true" :value="newPassword" maxlength="20" placeholder="请输入新密码"
<input class="input" :password="true" :value="password" maxlength="20" placeholder="请输入新密码"
placeholder-class="input-placeholder" @input="updateNewPassword"></input>
<input class="input" :password="true" :value="confirmPassword" maxlength="20" placeholder="请确认密码"
placeholder-class="input-placeholder" @input="updateConfirmPassword"></input>
<view class="view-top">
<input type="number" class="input-verify" :value="verificationCode" maxlength="6" placeholder="请输入验证码"
placeholder-class="input-placeholder" @input="updateInputCode"></input>
<view class="button-verify" @click="getPhoneCode">{{text}}</view>
</view>
<view class="text-tips">密码必须是8-20至少包括数字/字母/符号中的2种</view>
<view class="button" @click="updatePassword">保存</view>
</view>
@ -14,15 +15,17 @@
<script>
import { mapActions, mapState } from 'pinia'
import { useUserStore } from '@/stores/user'
import { updatePasswordRequest } from '@/api/user'
import { changePasswordRequest, getEmailCodeRequest, updatePasswordRequest } from '@/api/user'
import { test } from 'uview-plus'
import { useBasicStore } from '@/stores/basic'
export default {
data () {
return {
oldPassword: '',
newPassword: '',
confirmPassword: '',
pending: false
text: '获取验证码',
password: '',
pending: false,
verificationCode: ''
}
},
computed: {
@ -30,34 +33,61 @@ export default {
},
methods: {
...mapActions(useUserStore, ['updateUserInfo']),
updateOldPassword (data) {
this.oldPassword = data.detail.value
},
...mapActions(useBasicStore, ['backAndToast']),
updateNewPassword (data) {
this.newPassword = data.detail.value
this.password = data.detail.value
},
updateConfirmPassword (data) {
this.confirmPassword = data.detail.value
updateInputCode (data) {
this.verificationCode = data.detail.value
},
async getPhoneCode () {
if (this.text !== '获取验证码') {
return
}
const { code, message } = await getEmailCodeRequest({
channel: '1',
codeType: 9
})
if (code === 0) {
this.updateTime()
} else {
uni.showToast({
title: message,
icon: 'none'
})
}
},
updateTime () {
let time = 120
this.text = `${time} s`
const now = new Date().getTime()
const timer = setInterval(() => {
const second = parseInt((new Date().getTime() - now) / 1000)
this.text = `${time - second} s`
if (time <= second) {
clearInterval(timer)
this.text = '获取验证码'
}
}, 1000)
},
async updatePassword () {
if (this.oldPassword === '' || this.newPassword === '' || this.confirmPassword === '') {
if (this.password === '') {
uni.showToast({
title: '密码不能为空',
icon: 'none'
})
return
}
if (this.newPassword.length < 8 || this.newPassword.length > 20 || this.confirmPassword.length < 8 ||
this.confirmPassword.length > 20 || this.oldPassword.length < 8 || this.oldPassword.length > 20) {
if (this.password.length < 8 || this.password.length > 20) {
uni.showToast({
title: '密码长度必须是8-20位',
icon: 'none'
})
return
}
if (this.newPassword !== this.confirmPassword) {
if (!(this.verificationCode.length === 6 && test.digits(this.verificationCode))) {
uni.showToast({
title: '两次密码输入不一致',
title: '验证码为6位纯数字',
icon: 'none'
})
return
@ -66,24 +96,13 @@ export default {
return
}
this.pending = true
const { code, message } = await updatePasswordRequest({
oldPassword: this.oldPassword,
newPassword: this.newPassword,
date: new Date().getTime()
const { code, message } = await changePasswordRequest({
verificationCode: this.verificationCode,
password: this.password,
channel: '1'
})
if (code === 0) {
this.updateUserInfo({
...this.userInfo,
nickname: this.nickname
})
uni.navigateBack({
complete () {
uni.showToast({
title: '密码重置成功',
icon: 'none'
})
}
})
this.backAndToast('密码重置成功')
} else {
uni.showToast({
title: message,
@ -107,7 +126,7 @@ page {
border-radius: 16rpx;
background: #FFFFFF;
margin-left: 35rpx;
margin-top: 24rpx;
margin-top: 48rpx;
height: 108rpx;
width: 616rpx;
padding-left: 32rpx;
@ -140,4 +159,34 @@ page {
font-size: 28rpx;
color: #9B9B9B;
}
.view-top {
display: flex;
align-items: center;
}
.input-verify {
border-radius: 16rpx;
background: #FFFFFF;
margin-left: 35rpx;
margin-top: 48rpx;
height: 108rpx;
width: 316rpx;
padding-left: 32rpx;
padding-right: 32rpx;
}
.button-verify {
margin-top: 48rpx;
height: 108rpx;
width: 265rpx;
line-height: 108rpx;
border-radius: 16rpx;
text-align: center;
font-size: 32rpx;
margin-left: 35rpx;
background: #63b8af;
color: #FFFFFF;
}
</style>

View File

@ -46,6 +46,7 @@
import { getQuestionAnswerRequest, getQuestionListRequest, updateQuestionAnswerRequest } from '@/api/safeAnswer'
import { mapActions, mapState } from 'pinia'
import { useUserStore } from '@/stores/user'
import { useBasicStore } from '@/stores/basic'
export default {
data() {
@ -75,6 +76,7 @@ export default {
},
methods: {
...mapActions(useUserStore, ['updateUserInfo']),
...mapActions(useBasicStore, ['backAndToast']),
async updateAnswer() {
console.log('答案', this.answer)
for(let i = 0; i < this.answer.length; i++) {
@ -107,14 +109,7 @@ export default {
...this.userInfo,
haveSafeAnswer: 1
})
uni.navigateBack({
complete() {
uni.showToast({
title: '安全问题设置成功',
icon: 'none'
})
}
})
this.backAndToast('设置成功')
} else {
uni.showToast({
title: message,

View File

@ -19,14 +19,16 @@
</view>
</view>
<view class="view-line"></view>
<view class="view-button">
<view>手机号</view>
<view class="view-button" style="padding: 0">
<view v-if="userInfo.mobile !== ''" class="info">{{userInfo.mobile}}</view>
<view v-else class="red-dot"></view>
<image class="icon-arrow" src="/static/images/icon_arrow.png" mode="aspectFill"></image>
<label for="phone">
<view class="view-button">
<view>手机号</view>
<view class="view-button" style="padding: 0">
<view v-if="userInfo.mobile !== ''" class="info">{{userInfo.mobile}}</view>
<view v-else class="red-dot"></view>
<image class="icon-arrow" src="/static/images/icon_arrow.png" mode="aspectFill"></image>
</view>
</view>
</view>
</label>
<view class="view-line"></view>
<view class="view-button" @click="toUpdateEmail">
<view>邮箱</view>
@ -58,6 +60,7 @@
</view>
</view>
<button open-type="chooseAvatar" style="display:none" id="avatar" @chooseavatar="chooseAvatar"></button>
<button open-type="getPhoneNumber" style="display:none" id="phone" @getphonenumber="rebindPhone"></button>
</view>
</template>
@ -66,7 +69,7 @@ import { mapActions, mapState } from 'pinia'
import { useUserStore } from '@/stores/user'
import { useBasicStore } from '@/stores/basic'
import { getUploadParamsRequest } from '@/api/file'
import { updateUserInfoRequest } from '@/api/user'
import { rebindPhoneRequest, updateUserInfoRequest } from '@/api/user'
export default {
data() {
@ -79,11 +82,44 @@ export default {
},
methods: {
...mapActions(useBasicStore, ['routeJump']),
...mapActions(useUserStore, ['updateUserInfo']),
...mapActions(useUserStore, ['updateUserInfo', 'checkSession', 'getUserInfo']),
async rebindPhone(detail) {
if(detail.detail.errMsg === 'getPhoneNumber:fail user deny') {
return
}
if(this.pending) {
return
}
this.pending = true
const result = await this.checkSession()
if(result) {
const { code, data, message } = await rebindPhoneRequest({
encryptedData: detail.detail.encryptedData,
iv: detail.detail.iv
})
if(code === 0) {
this.getUserInfo()
// this.updateUserInfo({
// ...this.userInfo,
// mobile: data.mobile
// })
uni.showToast({
title: '更换成功',
icon: 'none'
})
} else {
uni.showToast({
title: message,
icon: 'none'
})
}
} else {
this.rebindPhone()
}
this.pending = false
},
chooseAvatar(e) {
console.log(e)
const that = this
if(that.pending) {
return
}

View File

@ -1,5 +1,6 @@
<template>
<view>
<view class="tips">为了你的账号安全修改账号前请先使用验证码验证</view>
<view class="view-top">
<input type="number" class="input" :value="verificationCode" maxlength="20" placeholder="请输入验证码"
placeholder-class="input-placeholder" @input="updateInput"></input>
@ -87,37 +88,6 @@ export default {
},
updateInput(data) {
this.verificationCode = data.detail.value
},
async updateName() {
if(this.nickname === '') {
uni.showToast({
title: '昵称不能为空',
icon: 'none'
})
return
}
const { code } = await updateUserInfoRequest({
nickname: this.nickname
})
if(code === 0) {
this.updateUserInfo({
...this.userInfo,
nickname: this.nickname
})
uni.navigateBack({
complete() {
uni.showToast({
title: '昵称更新成功',
icon: 'none'
})
}
})
} else {
uni.showToast({
title: '昵称更新失败',
icon: 'none'
})
}
}
}
}
@ -130,6 +100,12 @@ page {
</style>
<style scoped lang="scss">
.tips {
padding: 24rpx 32rpx 0 32rpx;
font-size: 28rpx;
color: #999999;
}
.button-verify {
margin-top: 48rpx;
height: 108rpx;

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 50 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 4.3 KiB

After

Width:  |  Height:  |  Size: 880 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

BIN
static/images/icon_door_lock.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.3 KiB

BIN
static/images/icon_menu.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

BIN
static/images/icon_role.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.5 KiB

BIN
static/images/icon_tips.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.8 KiB

View File

@ -61,6 +61,51 @@ const pages = [
name: 'searchDevice',
path: '/pages/searchDevice/searchDevice',
tabBar: false
},
{
name: 'selectAddress',
path: '/pages/selectAddress/selectAddress',
tabBar: false
},
{
name: 'bindLock',
path: '/pages/bindLock/bindLock',
tabBar: false
},
{
name: 'setting',
path: '/pages/setting/setting',
tabBar: false
},
{
name: 'keyList',
path: '/pages/keyList/keyList',
tabBar: false
},
{
name: 'createKey',
path: '/pages/createKey/createKey',
tabBar: false
},
{
name: 'passwordList',
path: '/pages/passwordList/passwordList',
tabBar: false
},
{
name: 'createPassword',
path: '/pages/createPassword/createPassword',
tabBar: false
},
{
name: 'passwordDetail',
path: '/pages/passwordDetail/passwordDetail',
tabBar: false
},
{
name: 'keyDetail',
path: '/pages/keyDetail/keyDetail',
tabBar: false
}
]
@ -120,5 +165,37 @@ export const useBasicStore = defineStore('basic', {
return
})
},
// 计算字符串长度
calculateStringTotalWidth(str) {
let totalWidth = 0
// 遍历字符串中的每一个字符
for (let i = 0; i < str.length; i++) {
const char = str[i]
// 判断当前字符是全角字符还是半角字符
if (/[\uFF01-\uFF60\uFFE0-\uFFE6\u4E00-\u9FFF\u3000-\u303F]/.test(char)) {
// 全角字符宽度为 2
totalWidth += 2
} else {
// 半角字符宽度为 1
totalWidth += 1
}
}
return totalWidth
},
// 回退页面并弹出toast提示
backAndToast(message, delta = 1) {
const that = this
uni.navigateBack({
delta: delta,
complete: function () {
setTimeout(() => {
uni.showToast({
title: message,
icon: 'none'
})
}, 200)
}
})
}
}
})

View File

@ -5,11 +5,20 @@ import { defineStore } from 'pinia'
import crc from 'crc'
import { sm4 } from 'sm-crypto'
import { md5 } from 'js-md5'
import { getServerDatetime } from '@/api/check'
import { getUserNoListRequest, updateLockUserNoRequest } from '@/api/key'
import { updateElectricQuantityRequest } from '@/api/room'
import { reportOpenDoorRequest } from '@/api/lockRecords'
import { updateTimezoneOffsetRequest } from '@/api/user'
// 定时器
let timer
// 特性值回调
let characteristicValueCallback = null
// 搜索次数
let searchNumber = 10
// 搜索提示标志
let searchTipFlag = true
// 命令ID
const cmdIds = {
@ -58,14 +67,40 @@ export const useBluetoothStore = defineStore('ble', {
// 消息序号
messageCount: 1,
// 是否初始化蓝牙
isInitBluetooth: false
isInitBluetooth: false,
// 服务器时间
serverTimestamp: 0,
// 设备keyID
keyId: '0'
}
},
actions: {
// 更新keyId
updateKeyId(keyId) {
this.keyId = keyId
},
// 二进制转字符串
uint8ArrayToString(uint8Array) {
let str = ''
for (let i = 0; i < uint8Array.length; i++) {
if (uint8Array[i] !== 0) {
str += String.fromCharCode(uint8Array[i]);
}
}
return str
},
// 更新服务端时间戳
async updateServerTimestamp() {
const { code, data, message } = await getServerDatetime({})
if(code === 0) {
this.serverTimestamp = parseInt(data.date / 1000)
}
return { code, data, message }
},
// 初始化并监听
async initAndListenBluetooth() {
async initAndListenBluetooth(tipFlag) {
// 初始化蓝牙
const initResult = await this.initBluetooth()
const initResult = await this.initBluetooth(tipFlag)
if (initResult) {
// 更新蓝牙初始化状态
this.updateInitBluetooth(true)
@ -86,7 +121,7 @@ export const useBluetoothStore = defineStore('ble', {
this.isInitBluetooth = value
},
// 初始化蓝牙模块
initBluetooth() {
initBluetooth(tipFlag = true) {
const that = this
// 初始化蓝牙模块
return new Promise(resolve => {
@ -119,6 +154,23 @@ export const useBluetoothStore = defineStore('ble', {
resolve(true)
return
}
if(err.errno === 3 && tipFlag) {
uni.showModal({
title: '提示',
content: '蓝牙功能需要附近设备权限,请前往设置开启微信的附近设备权限后再试',
showCancel: false,
confirmText: '确定',
success(res) {
if (res.confirm) {
uni.openAppAuthorizeSetting({
success (res) {
console.log(res)
}
})
}
}
})
}
resolve(false)
}
})
@ -168,7 +220,7 @@ export const useBluetoothStore = defineStore('ble', {
if(binaryData[14] === 0) {
that.updateCurrentLockInfo({
...that.currentLockInfo,
publicKey: binaryData.slice(15, 31)
publicKey: [...binaryData.slice(15, 31)]
})
}
characteristicValueCallback({
@ -191,7 +243,8 @@ export const useBluetoothStore = defineStore('ble', {
that.updateCurrentLockInfo({
...that.currentLockInfo,
commKey: decrypted.slice(3, 19),
signKey: decrypted.slice(19, 35)
signKey: decrypted.slice(19, 35),
pwdTimestamp: this.arrayToTimestamp(decrypted.slice(35, 39)) * 1000
})
console.log('commKey', Array.from(that.currentLockInfo.commKey))
console.log('signKey', Array.from(that.currentLockInfo.signKey))
@ -211,28 +264,28 @@ export const useBluetoothStore = defineStore('ble', {
case cmdIds.getLockStatus:
if (decrypted[2] === 0) {
const lockConfig = {
vendor: decrypted.slice(3, 23),
vendor: that.uint8ArrayToString(decrypted.slice(3, 23)),
product: decrypted[23],
model: decrypted.slice(24, 44),
fwVersion: decrypted.slice(44, 64),
hwVersion: decrypted.slice(64, 84),
serialNum0: decrypted.slice(84, 100),
serialNum1: decrypted.slice(100, 116),
btDeviceName: decrypted.slice(116, 132),
battRemCap: decrypted[132],
battRemCapStandby: decrypted[133],
restoreCounter: decrypted.slice(134, 136),
restoreDate: decrypted.slice(136, 140),
icPartNo: decrypted.slice(140, 150),
inDate: decrypted.slice(150, 154),
mac: decrypted.slice(154, 174),
featurevalueLength: decrypted[174],
featureValue: decrypted.slice(175, 175 + decrypted[174]),
featureEnValLength: decrypted[175 + decrypted[174]],
featureEnVal: decrypted.slice(176 + decrypted[174], 176 + decrypted[174] + decrypted[175 + decrypted[174]]),
model: that.uint8ArrayToString(decrypted.slice(24, 44)),
fwVersion: that.uint8ArrayToString(decrypted.slice(44, 64)),
hwVersion: that.uint8ArrayToString(decrypted.slice(64, 84)),
serialNum0: that.uint8ArrayToString(decrypted.slice(84, 100)),
serialNum1: that.uint8ArrayToString(decrypted.slice(100, 116)),
btDeviceName: that.uint8ArrayToString(decrypted.slice(116, 132)),
electricQuantity: decrypted[132],
electricQuantityStandby: decrypted[133],
restoreCount: decrypted[134] * 256 + decrypted[135],
restoreDate: this.arrayToTimestamp(decrypted.slice(136, 140)),
icPartNo: that.uint8ArrayToString(decrypted.slice(140, 150)),
indate: this.arrayToTimestamp(decrypted.slice(150, 154)),
mac: that.uint8ArrayToString(decrypted.slice(154, 174)),
timezoneOffset: new Date().getTimezoneOffset() * 60
}
that.updateCurrentLockInfo({
...that.currentLockInfo,
featureValue: that.uint8ArrayToString(decrypted.slice(175, 175 + decrypted[174])),
featureSettingValue: that.uint8ArrayToString(decrypted.slice(176 + decrypted[174], 176 + decrypted[174] + decrypted[175 + decrypted[174]])),
featureSettingParams: Array.from(decrypted.slice(176 + decrypted[174] + decrypted[175 + decrypted[174]])),
lockConfig
})
console.log('获取锁状态成功', that.currentLockInfo.lockConfig)
@ -246,6 +299,13 @@ export const useBluetoothStore = defineStore('ble', {
...that.currentLockInfo,
token: decrypted.slice(42,46)
})
if(decrypted[46] === 0) {
that.updateCurrentLockInfo({
...that.currentLockInfo,
lockUserNo: decrypted[47] * 256 + decrypted[48]
})
}
console.log('添加用户结果', decrypted[46], that.currentLockInfo.token)
characteristicValueCallback({
code: decrypted[46]
})
@ -282,11 +342,47 @@ export const useBluetoothStore = defineStore('ble', {
break
}
break
case cmdIds.openDoor:
that.updateCurrentLockInfo({
...that.currentLockInfo,
token: decrypted.slice(2,6)
})
console.log('开门', decrypted[6], that.currentLockInfo.token)
characteristicValueCallback({
code: decrypted[6]
})
if(decrypted[6] === 0) {
updateElectricQuantityRequest({
lockId: that.currentLockInfo.lockId,
electricQuantity: decrypted[7],
electricQuantityStandby: decrypted[9]
}).then(res => {
if(res.code === 0) {
that.updateCurrentLockInfo({
...that.currentLockInfo,
electricQuantityDate: res.data.electricQuantityDate
})
}
})
reportOpenDoorRequest({
lockId: that.currentLockInfo.lockId,
keyId: that.keyId,
}).then(res => {
console.log('上报开门结果', res)
})
updateTimezoneOffsetRequest({
timezoneOffset: new Date().getTimezoneOffset() * 60
}).then(res => {
console.log('上报时区结果', res)
})
}
break
default:
that.updateCurrentLockInfo({
...that.currentLockInfo,
token: decrypted.slice(2,6)
})
console.log('默认结果', decrypted[6], that.currentLockInfo.token)
characteristicValueCallback({
code: decrypted[6]
})
@ -335,6 +431,7 @@ export const useBluetoothStore = defineStore('ble', {
this.getBluetoothStatus()
return
}
this.deviceList = []
uni.startBluetoothDevicesDiscovery({
success: function (res) {
setTimeout(() => {
@ -343,6 +440,23 @@ export const useBluetoothStore = defineStore('ble', {
},
fail: async function (res) {
console.log('开始搜索失败', res)
if(res.errno === 1509008) {
uni.showModal({
title: '提示',
content: '安卓手机蓝牙功能需要定位权限,请前往设置开启微信的定位权限后再试',
showCancel: false,
success(res) {
if (res.confirm) {
uni.openAppAuthorizeSetting({
success (res) {
console.log(res)
}
})
}
uni.navigateBack()
}
})
}
if(res.errCode === 10000) {
// 重新初始化蓝牙适配器
await that.initBluetooth()
@ -362,7 +476,26 @@ export const useBluetoothStore = defineStore('ble', {
timer = setInterval(() => {
uni.getBluetoothDevices({
success(res) {
searchNumber--
if(searchNumber === 0 && searchTipFlag) {
uni.showModal({
title: '提示',
content: '长时间未搜索到任何设备,请确认微信的附近设备权限开启后再试',
showCancel: false,
success() {
uni.openAppAuthorizeSetting({
success (res) {
console.log(res)
}
})
uni.navigateBack()
}
})
}
const deviceList = res.devices
if(deviceList.length !== 0) {
searchTipFlag = false
}
that.deviceList = []
for(let i = 0; i < deviceList.length; i++) {
if(deviceList[i]?.advertisServiceUUIDs) {
@ -386,6 +519,8 @@ export const useBluetoothStore = defineStore('ble', {
},
// 停止搜索蓝牙设备
stopGetBluetoothDevices() {
searchNumber = 10
searchTipFlag = true
clearInterval(timer)
uni.stopBluetoothDevicesDiscovery()
},
@ -396,7 +531,15 @@ export const useBluetoothStore = defineStore('ble', {
title: '提示',
content: '蓝牙尚未打开,请先打开蓝牙',
showCancel: false,
confirmText: '确定',
success(res) {
if (res.confirm) {
wx.openSystemBluetoothSetting({
success (res) {
console.log(res)
}
})
}
}
})
} else if(this.bluetoothStatus === 2 || this.bluetoothStatus === 3) {
uni.showModal({
@ -466,7 +609,7 @@ export const useBluetoothStore = defineStore('ble', {
success (res) {
let serviceId
for(let i = 0; i < res.services.length; i++) {
if(res.services[i].isPrimary) {
if(res.services[i].uuid.indexOf('FFF0') !== -1) {
serviceId = res.services[i].uuid
}
}
@ -520,6 +663,10 @@ export const useBluetoothStore = defineStore('ble', {
})
},
fail(res) {
if(res.errno === 1509007) {
resolve(true)
return
}
uni.showToast({
title: '连接失败,请靠近设备并保持设备处于唤醒状态',
icon: 'none'
@ -564,23 +711,127 @@ export const useBluetoothStore = defineStore('ble', {
}
})
},
// 查找设备并连接
async searchAndConnectDevice() {
const that = this
let timer1
let timer2
return new Promise((resolve) => {
uni.startBluetoothDevicesDiscovery({
success: function (res) {
timer2 = setTimeout(() => {
uni.stopBluetoothDevicesDiscovery()
clearInterval(timer1)
resolve(false)
}, 10500)
timer1 = setInterval(() => {
uni.getBluetoothDevices({
success(res) {
const deviceList = res.devices
for(let i = 0; i < deviceList.length; i++) {
if(deviceList[i]?.name === that.currentLockInfo.name) {
const uuid = deviceList[i]?.advertisServiceUUIDs[0]
if(uuid && uuid.slice(2,8)==='758824' && uuid.slice(30,32)==='01') {
uni.stopBluetoothDevicesDiscovery()
clearTimeout(timer2)
clearInterval(timer1)
resolve(deviceList[i].deviceId)
break
}
}
}
},
fail: async function (res) {
console.log('获取设备列表失败', res)
if(res.errCode === 10000) {
// 重新初始化蓝牙适配器
await that.initBluetooth()
}
}
})
}, 1000)
},
fail: async function (res) {
console.log('开始搜索失败', res)
if(res.errCode === 10000) {
// 重新初始化蓝牙适配器
await that.initBluetooth()
that.searchAndConnectDevice()
} else {
resolve(false)
}
}
})
})
},
// 检查是否已添加为用户
async checkLockUser(flag = false) {
console.log('检查是否已添加为用户', this.currentLockInfo.lockUserNo)
if(this.currentLockInfo.lockUserNo === 0 || flag) {
const timestamp = parseInt(new Date().getTime() / 1000)
const password = (Math.floor(Math.random() * 900000) + 100000).toString()
console.log('用户未添加,开始添加用户')
const { code: addUserCode } = await this.addLockUser({
name: this.currentLockInfo.name,
keyId: this.keyId,
authUid: this.currentLockInfo.senderUserId.toString(),
uid: this.currentLockInfo.uid.toString(),
openMode: 1,
keyType: 0,
startDate: this.currentLockInfo.startDate === 0 ? timestamp : this.currentLockInfo.startDate,
expireDate: this.currentLockInfo.endDate === 0 ? 0xffffffff : this.currentLockInfo.endDate,
useCountLimit: this.currentLockInfo.keyType === 3 ? 1 : 0xffff,
isRound: 0,
weekRound: 0,
startHour: 0,
startMin: 0,
endHour: 0,
endMin: 0,
role: 0,
password
})
console.log('添加用户蓝牙结果', addUserCode)
if(addUserCode === 0) {
const { code } = await updateLockUserNoRequest({
keyId: this.keyId,
lockUserNo: this.currentLockInfo.lockUserNo
})
console.log('添加用户请求结果', code)
return true
} else if(addUserCode === 0x0c) {
console.log('用户达上限,开始清理用户')
const { code: requestCode, data: requestData } = await getUserNoListRequest({
lockId: this.currentLockInfo.lockId
})
console.log('获取用户列表请求结果', requestCode, requestData)
if(requestCode !== 0) return false
const userNoList = requestData.userNos
const { code: cleanCode } = await this.cleanLockUser({
name: this.currentLockInfo.name,
keyId: this.keyId,
authUid: this.currentLockInfo.senderUserId.toString(),
uid: this.currentLockInfo.uid.toString(),
userNoList: userNoList
})
console.log('清理用户蓝牙结果', cleanCode)
if(cleanCode === 0) {
return await this.checkLockUser()
} else {
return false
}
} else if(addUserCode === 0x0f) {
return true
} else {
return false
}
} else {
return true
}
},
// 写入特征值
async writeBLECharacteristicValue(binaryData) {
const that = this
// 确认蓝牙状态正常
if(this.bluetoothStatus !== 0) {
console.log('写入未执行', this.bluetoothStatus)
this.getBluetoothStatus()
return
}
// 确认设备连接正常
if(!that.currentLockInfo.connected) {
const result = await that.connectBluetoothDevice()
if(!result) return
}
console.log('设备ID', that.currentLockInfo.deviceId)
console.log('设备名称:', that.currentLockInfo.name)
console.log('设备主服务:', that.currentLockInfo.serviceId)
@ -668,6 +919,37 @@ export const useBluetoothStore = defineStore('ble', {
},
// 获取公钥
async getPublicKey(name) {
// 确认蓝牙状态正常
if(this.bluetoothStatus !== 0) {
console.log('写入未执行', this.bluetoothStatus)
this.getBluetoothStatus()
return {
code: -1
}
}
// 确认设备连接正常
if(!this.currentLockInfo.connected) {
const srerchResult = await this.searchAndConnectDevice()
this.updateCurrentLockInfo({
...this.currentLockInfo,
deviceId: srerchResult
})
console.log('设备ID', this.currentLockInfo.deviceId)
if(!srerchResult) {
return {
code: -1
}
}
const result = await this.connectBluetoothDevice()
console.log('连接结果', result)
if(!result) {
return {
code: -1
}
}
}
const headArray = this.createPackageHeader(0, 42)
const conentArray = new Uint8Array(42)
@ -685,6 +967,37 @@ export const useBluetoothStore = defineStore('ble', {
},
// 获取私钥
async getCommKey(name, keyId, authUid, nowTime) {
// 确认蓝牙状态正常
if(this.bluetoothStatus !== 0) {
console.log('写入未执行', this.bluetoothStatus)
this.getBluetoothStatus()
return {
code: -1
}
}
// 确认设备连接正常
if(!this.currentLockInfo.connected) {
const srerchResult = await this.searchAndConnectDevice()
this.updateCurrentLockInfo({
...this.currentLockInfo,
deviceId: srerchResult
})
console.log('设备ID', this.currentLockInfo.deviceId)
if(!srerchResult) {
return {
code: -1
}
}
const result = await this.connectBluetoothDevice()
console.log('连接结果', result)
if(!result) {
return {
code: -1
}
}
}
const length = 2 + 40 + 40 + 20 + 4 + 1 + 16
const headArray = this.createPackageHeader(2, length)
const conentArray = new Uint8Array(length)
@ -736,6 +1049,37 @@ export const useBluetoothStore = defineStore('ble', {
},
// 获取锁状态
async getLockStatus(data) {
// 确认蓝牙状态正常
if(this.bluetoothStatus !== 0) {
console.log('写入未执行', this.bluetoothStatus)
this.getBluetoothStatus()
return {
code: -1
}
}
// 确认设备连接正常
if(!this.currentLockInfo.connected) {
const srerchResult = await this.searchAndConnectDevice()
this.updateCurrentLockInfo({
...this.currentLockInfo,
deviceId: srerchResult
})
console.log('设备ID', this.currentLockInfo.deviceId)
if(!srerchResult) {
return {
code: -1
}
}
const result = await this.connectBluetoothDevice()
console.log('连接结果', result)
if(!result) {
return {
code: -1
}
}
}
const { name, uid, nowTime, localTime } = data
const length = 2 + 40 + 20 + 4 + 4
const headArray = this.createPackageHeader(3, length)
@ -769,10 +1113,46 @@ export const useBluetoothStore = defineStore('ble', {
array[3] = (timestamp & 0xff)
return array
},
// 二进制转时间戳
arrayToTimestamp(array) {
const timestamp = (array[0] << 24) | (array[1] << 16) | (array[2] << 8) | array[3]
return timestamp >>> 0
},
// 添加用户
async addLockUser(data) {
// 确认蓝牙状态正常
if(this.bluetoothStatus !== 0) {
console.log('写入未执行', this.bluetoothStatus)
this.getBluetoothStatus()
return {
code: -1
}
}
// 确认设备连接正常
if(!this.currentLockInfo.connected) {
const srerchResult = await this.searchAndConnectDevice()
this.updateCurrentLockInfo({
...this.currentLockInfo,
deviceId: srerchResult
})
console.log('设备ID', this.currentLockInfo.deviceId)
if(!srerchResult) {
return {
code: -1
}
}
const result = await this.connectBluetoothDevice()
console.log('连接结果', result)
if(!result) {
return {
code: -1
}
}
}
const { name, authUid, uid, keyId, openMode, keyType, startDate, expireDate, useCountLimit, isRound, weekRound,
startHour, startMin, endHour, endMin, role, password, publicKey, commKey } = data
startHour, startMin, endHour, endMin, role, password } = data
const length = 2 + 40 + 20 + 40 + 20 + 1 + 1 + 4 + 4 + 2 + 1 + 1 + 1 + 1 + 1 + 1 + 1 + 20 + 4 + 1 + 16
const headArray = this.createPackageHeader(3, length)
const conentArray = new Uint8Array(length)
@ -825,24 +1205,33 @@ export const useBluetoothStore = defineStore('ble', {
conentArray.set(md5Array, 166)
const cebArray = sm4.encrypt(conentArray, commKey, { mode: 'ecb', output: 'array' })
const cebArray = sm4.encrypt(conentArray, this.currentLockInfo.commKey, { mode: 'ecb', output: 'array' })
const packageArray = this.createPackageEnd(headArray, cebArray)
await this.writeBLECharacteristicValue(packageArray)
await this.writeBLECharacteristicValue(packageArray, false)
return this.getWriteResult(this.addLockUser, data)
},
// 获取写入结果
getWriteResult(request, params) {
const that = this
return new Promise(resolve => {
const timer = setTimeout(() => {
const getWriteResultTimer = setTimeout(() => {
resolve({ code: -1 })
}, 10000)
}, 20000)
characteristicValueCallback = async (data) => {
// code 6 token过期,重新获取
if(data.code === 6) {
resolve(await request(params))
} else if(data.code === 4) {
const checkResult = await that.checkLockUser(true)
if(checkResult) {
resolve(await request(params))
} else {
clearTimeout(getWriteResultTimer)
resolve(data)
}
} else {
clearTimeout(timer)
clearTimeout(getWriteResultTimer)
resolve(data)
}
}
@ -850,6 +1239,45 @@ export const useBluetoothStore = defineStore('ble', {
},
// 开门
async openDoor(data) {
// 确认蓝牙状态正常
if(this.bluetoothStatus !== 0) {
console.log('写入未执行', this.bluetoothStatus)
this.getBluetoothStatus()
return {
code: -1
}
}
// 确认设备连接正常
if(!this.currentLockInfo.connected) {
const srerchResult = await this.searchAndConnectDevice()
this.updateCurrentLockInfo({
...this.currentLockInfo,
deviceId: srerchResult
})
console.log('设备ID', this.currentLockInfo.deviceId)
if(!srerchResult) {
return {
code: -1
}
}
const result = await this.connectBluetoothDevice()
console.log('连接结果', result)
if(!result) {
return {
code: -1
}
}
}
// 检查是否已添加为用户
const checkResult = await this.checkLockUser()
if (!checkResult) {
return {
code: -1
}
}
const { name, uid, openMode, openTime, onlineToken } = data
const length = 2 + 40 + 20 + 1 + 4 + 4 + 1 + 16 + 16
const headArray = this.createPackageHeader(3, length)
@ -870,11 +1298,14 @@ export const useBluetoothStore = defineStore('ble', {
conentArray.set(this.timestampToArray(openTime), 63)
conentArray.set(this.currentLockInfo.token, 67)
console.log('开门时token', this.currentLockInfo.token)
conentArray.set(this.currentLockInfo.token || this.timestampToArray(openTime), 67)
conentArray[71] = 16
const md5Array = this.md5Encrypte(name + uid, this.currentLockInfo.token, this.currentLockInfo.signKey)
const md5Array = this.md5Encrypte(name + uid, this.currentLockInfo.token || this.timestampToArray(openTime), this.currentLockInfo.signKey)
conentArray.set(md5Array, 72)
@ -885,13 +1316,132 @@ export const useBluetoothStore = defineStore('ble', {
const cebArray = sm4.encrypt(conentArray, this.currentLockInfo.commKey, { mode: 'ecb', output: 'array' })
const packageArray = this.createPackageEnd(headArray, cebArray)
await this.writeBLECharacteristicValue(packageArray)
this.writeBLECharacteristicValue(packageArray)
return this.getWriteResult(this.openDoor, data)
},
// 清理用户
async cleanLockUser(data) {
// 确认蓝牙状态正常
if(this.bluetoothStatus !== 0) {
console.log('写入未执行', this.bluetoothStatus)
this.getBluetoothStatus()
return {
code: -1
}
}
// 确认设备连接正常
if(!this.currentLockInfo.connected) {
const srerchResult = await this.searchAndConnectDevice()
this.updateCurrentLockInfo({
...this.currentLockInfo,
deviceId: srerchResult
})
console.log('设备ID', this.currentLockInfo.deviceId)
if(!srerchResult) {
return {
code: -1
}
}
const result = await this.connectBluetoothDevice()
console.log('连接结果', result)
if(!result) {
return {
code: -1
}
}
}
const { name, authUid, keyId, uid, userNoList } = data
const length = 2 + 40 + 20 + 40 + 20 + 2 + userNoList.length + 4 + 1 + 16
const headArray = this.createPackageHeader(3, length)
const conentArray = new Uint8Array(length)
conentArray[0] = cmdIds.cleanUser / 256
conentArray[1] = cmdIds.cleanUser % 256
for (let i = 0; i < name.length; i++) {
conentArray[i + 2] = name.charCodeAt(i)
}
for (let i = 0; i < authUid.length; i++) {
conentArray[i + 42] = authUid.charCodeAt(i)
}
for (let i = 0; i < keyId.length; i++) {
conentArray[i + 62] = keyId.charCodeAt(i)
}
for (let i = 0; i < uid.length; i++) {
conentArray[i + 102] = uid.charCodeAt(i)
}
conentArray[122] = userNoList.length / 256
conentArray[123] = userNoList.length % 256
for (let i = 0; i < userNoList.length; i++) {
conentArray[i + 124] = userNoList[i]
}
conentArray.set(this.currentLockInfo.token || new Uint8Array([0, 0, 0, 0]), 124 + userNoList.length)
conentArray[128 + userNoList.length] = 16
const md5Array = this.md5Encrypte(authUid + keyId, this.currentLockInfo.token || new Uint8Array([0, 0, 0, 0]), this.currentLockInfo.publicKey)
conentArray.set(md5Array, 129 + userNoList.length)
const cebArray = sm4.encrypt(conentArray, this.currentLockInfo.commKey, { mode: 'ecb', output: 'array' })
const packageArray = this.createPackageEnd(headArray, cebArray)
await this.writeBLECharacteristicValue(packageArray)
return this.getWriteResult(this.cleanLockUser, data)
},
// 恢复出厂设置
async resetDevice(data) {
// 确认蓝牙状态正常
if(this.bluetoothStatus !== 0) {
console.log('写入未执行', this.bluetoothStatus)
this.getBluetoothStatus()
return {
code: -1
}
}
// 确认设备连接正常
if(!this.currentLockInfo.connected) {
const srerchResult = await this.searchAndConnectDevice()
this.updateCurrentLockInfo({
...this.currentLockInfo,
deviceId: srerchResult
})
console.log('设备ID', this.currentLockInfo.deviceId)
if(!srerchResult) {
return {
code: -1
}
}
const result = await this.connectBluetoothDevice()
console.log('连接结果', result)
if(!result) {
return {
code: -1
}
}
}
// 检查是否已添加为用户
const checkResult = await this.checkLockUser()
if (!checkResult) {
return {
code: -1
}
}
const { name, authUid, keyId } = data
const length = 2 + 40 + 20 + 4 + 1 + 16
const headArray = this.createPackageHeader(3, length)
@ -907,10 +1457,10 @@ export const useBluetoothStore = defineStore('ble', {
for(let i = 0; i < authUid.length; i++) {
conentArray[i + 42] = authUid.charCodeAt(i)
}
conentArray.set(this.currentLockInfo.token, 62)
conentArray.set(this.currentLockInfo.token || new Uint8Array([0, 0, 0, 0]), 62)
conentArray[66] = 16
const md5Array = this.md5Encrypte(name, this.currentLockInfo.token, this.currentLockInfo.publicKey)
const md5Array = this.md5Encrypte(name, this.currentLockInfo.token || new Uint8Array([0, 0, 0, 0]), this.currentLockInfo.publicKey)
conentArray.set(md5Array, 67)
const cebArray = sm4.encrypt(conentArray, this.currentLockInfo.commKey, { mode: 'ecb', output: 'array' })
@ -923,6 +1473,45 @@ export const useBluetoothStore = defineStore('ble', {
},
// 重置开锁密码
async resetLockPassword(data) {
// 确认蓝牙状态正常
if(this.bluetoothStatus !== 0) {
console.log('写入未执行', this.bluetoothStatus)
this.getBluetoothStatus()
return {
code: -1
}
}
// 确认设备连接正常
if(!this.currentLockInfo.connected) {
const srerchResult = await this.searchAndConnectDevice()
this.updateCurrentLockInfo({
...this.currentLockInfo,
deviceId: srerchResult
})
console.log('设备ID', this.currentLockInfo.deviceId)
if(!srerchResult) {
return {
code: -1
}
}
const result = await this.connectBluetoothDevice()
console.log('连接结果', result)
if(!result) {
return {
code: -1
}
}
}
// 检查是否已添加为用户
const checkResult = await this.checkLockUser()
if (!checkResult) {
return {
code: -1
}
}
const { keyId, uid } = data
const length = 2 + 1 + 1 + 40 + 20 + 4 + 1 + 16
const headArray = this.createPackageHeader(3, length)
@ -944,11 +1533,11 @@ export const useBluetoothStore = defineStore('ble', {
conentArray[i + 44] = uid.charCodeAt(i)
}
conentArray.set(this.currentLockInfo.token, 64)
conentArray.set(this.currentLockInfo.token || new Uint8Array([0, 0, 0, 0]), 64)
conentArray[68] = 16
const md5Array = this.md5Encrypte(keyId + uid, this.currentLockInfo.token, this.currentLockInfo.signKey)
const md5Array = this.md5Encrypte(keyId + uid, this.currentLockInfo.token || new Uint8Array([0, 0, 0, 0]), this.currentLockInfo.signKey)
conentArray.set(md5Array, 69)
@ -960,13 +1549,47 @@ export const useBluetoothStore = defineStore('ble', {
return this.getWriteResult(this.resetLockPassword, data)
},
// 清理用户
async cleanUser(data) {
const { name, authUid, keyId, uid, userNoLength, userNoList } = data
// const length = 2 + 40 + 20 + 40 + 20 + 2 + userNoLength * 4 + 1 + 16
},
// 设置密码
async setLockPassword(data) {
// 确认蓝牙状态正常
if(this.bluetoothStatus !== 0) {
console.log('写入未执行', this.bluetoothStatus)
this.getBluetoothStatus()
return {
code: -1
}
}
// 确认设备连接正常
if(!this.currentLockInfo.connected) {
const srerchResult = await this.searchAndConnectDevice()
this.updateCurrentLockInfo({
...this.currentLockInfo,
deviceId: srerchResult
})
console.log('设备ID', this.currentLockInfo.deviceId)
if(!srerchResult) {
return {
code: -1
}
}
const result = await this.connectBluetoothDevice()
console.log('连接结果', result)
if(!result) {
return {
code: -1
}
}
}
// 检查是否已添加为用户
const checkResult = await this.checkLockUser()
if (!checkResult) {
return {
code: -1
}
}
const { keyId, uid, pwdNo, operate, isAdmin, pwd, userCountLimit, startTime, endTime} = data
const length = 2 + 1 + 1 + 40 + 20 + 2 + 1 + 1 + 20 + 2 + 4 + 4 + 4 + 1 + 16
const headArray = this.createPackageHeader(3, length)
@ -1001,14 +1624,14 @@ export const useBluetoothStore = defineStore('ble', {
conentArray[88] = userCountLimit / 256
conentArray[89] = userCountLimit % 256
conentArray.set(this.currentLockInfo.token, 90)
conentArray.set(this.currentLockInfo.token || new Uint8Array([0, 0, 0, 0]), 90)
conentArray.set(this.timestampToArray(startTime), 94)
conentArray.set(this.timestampToArray(endTime), 98)
conentArray[102] = 16
const md5Array = this.md5Encrypte(keyId + uid, this.currentLockInfo.token, this.currentLockInfo.signKey)
const md5Array = this.md5Encrypte(keyId + uid, this.currentLockInfo.token || new Uint8Array([0, 0, 0, 0]), this.currentLockInfo.signKey)
conentArray.set(md5Array, 103)

View File

@ -3,6 +3,9 @@
*/
import { defineStore } from 'pinia'
import { getLockListRequest } from '@/api/lock'
import { getPsaawordListRequest } from '@/api/keyboardPwd'
import { timeFormat } from 'uview-plus'
import { getKeyListRequest } from '@/api/key'
export const useLockStore = defineStore('lock', {
state() {
@ -10,10 +13,59 @@ export const useLockStore = defineStore('lock', {
// 锁列表
lockList: [],
// 锁总数
lockTotal: 0
lockTotal: 0,
// 锁列表搜索数据
lockSearch: {
pageNo: 1,
pageSize: 50,
searchStr: ''
},
// 密码列表
passwordList: [],
// 密码总数
passwordTotal: 0,
// 当前密码详情
currentPasswordInfo: {},
// 密码列表搜索数据
passwordSearch: {
pageNo: 1,
pageSize: 50,
searchStr: ''
},
// 电子钥匙总数
keyTotal: 0,
// 电子钥匙列表
keyList: [],
// 当前电子钥匙详情
currentKeyInfo: {},
// 电子钥匙列表搜索数据
keySearch: {
pageNo: 1,
pageSize: 50,
searchStr: '',
endDate: '0',
startDate: '0',
keyStatus: [110401,110402,110412],
keyRight: 0
},
}
},
actions: {
updateLockSearch(search) {
this.lockSearch = search
},
updatePasswordSearch(search) {
this.passwordSearch = search
},
updateKeySearch(search) {
this.keySearch = search
},
updateCurrentKeyInfo(info) {
this.currentKeyInfo = info
},
updateCurrentPasswordInfo(info) {
this.currentPasswordInfo = info
},
getRole(userType) {
if(userType === 110301) {
return '超级管理员'
@ -21,6 +73,17 @@ export const useLockStore = defineStore('lock', {
return '普通用户'
}
},
getKeyStatus(keyStatus) {
if(keyStatus === 110401) {
return '正常'
} else if(keyStatus === 110402) {
return '待接收'
} else if(keyStatus === 110412) {
return '已过期'
} else {
return ''
}
},
getTimeLimit(keyType) {
if(keyType === 1) {
return '永久'
@ -49,6 +112,56 @@ export const useLockStore = defineStore('lock', {
})
return { code, message }
}
},
async getPasswordList(params) {
const { code, data, message } = await getPsaawordListRequest(params)
if(code === 0) {
this.passwordTotal = data.total
for(let i = 0; i < data.list.length; i++) {
if(data.list[i].keyboardPwdType === 2) {
data.list[i].timeText = `${timeFormat(new Date(data.list[i].created_at), 'yyyy-mm-dd h:M')} 永久`
} else if(data.list[i].keyboardPwdType === 3) {
data.list[i].timeText = `${data.list[i].validTimeStr} 限时`
}
}
if(params.pageNo === 1) {
this.passwordList = data.list
} else {
this.passwordList = this.passwordList.concat(data.list)
}
return { code }
} else {
uni.showToast({
title: message,
icon: 'none'
})
return { code, message }
}
},
async getKeyList(params) {
const { code, data, message } = await getKeyListRequest(params)
if(code === 0) {
this.keyTotal = data.total
for(let i = 0; i < data.list.length; i++) {
if(data.list[i].keyType === 2) {
data.list[i].timeText = `${timeFormat(new Date(data.list[i].startDate), 'yyyy-mm-dd h:M')} - ${timeFormat(new Date(data.list[i].endDate), 'yyyy-mm-dd h:M')} 限时`
} else {
data.list[i].timeText = `${timeFormat(new Date(data.list[i].startDate), 'yyyy-mm-dd h:M')} 永久`
}
}
if(params.pageNo === 1) {
this.keyList = data.list
} else {
this.keyList = this.keyList.concat(data.list)
}
return { code }
} else {
uni.showToast({
title: message,
icon: 'none'
})
return { code, message }
}
}
}
})

View File

@ -2,7 +2,7 @@
* @description 用户信息数据持久化
*/
import { defineStore } from 'pinia'
import { getUserInfoRequest } from '@/api/user'
import { getUserInfoRequest, loginRequest, phoneLoginRequest } from '@/api/user'
import { useLockStore } from '@/stores/lock'
export const useUserStore = defineStore('user', {
@ -21,18 +21,57 @@ export const useUserStore = defineStore('user', {
updateLoginStatus(status) {
this.isLogin = status
},
async login() {
uni.setStorageSync('token', '3021|MZv7iEf0NwjCPSGx4QWs37zOjeVN3GrSJ2v7D56L7db1fcc5')
// 获取用户信息
async getUserInfo() {
const { code, data } = await getUserInfoRequest()
await useLockStore().getLockList({
pageNo: 1,
pageSize: 50
})
if(code === 0) {
this.updateUserInfo(data)
this.updateLoginStatus(true)
}
this.isLogin = true
return this.isLogin
return code
},
async phoneLogin(params) {
const { iv, encryptedData } = params
const openid = uni.getStorageSync('openid')
const { code, data, message } = await phoneLoginRequest({ iv, encryptedData, openid })
if(code === 0) {
uni.setStorageSync('token', data.accessToken)
this.getUserInfo()
useLockStore().updateLockSearch({
...useLockStore().lockSearch,
pageNo: 1
})
useLockStore().getLockList(useLockStore().lockSearch)
return true
} else {
return false
}
},
async checkSession() {
return new Promise((resolve) => {
uni.checkSession({
success() {
resolve(true)
},
fail() {
uni.login({
provider: 'weixin',
success: async function (loginRes) {
const { code, data } = await loginRequest({
js_code: loginRes.code
})
if (code === 0) {
uni.setStorageSync('openid', data.openid)
}
resolve(false)
},
fail() {
resolve(false)
}
})
}
})
})
}
}
})

View File

@ -77,3 +77,7 @@ $uni-color-subtitle: #555555; // 二级标题颜色
$uni-font-size-subtitle:26px;
$uni-color-paragraph: #3F536E; // 文章段落颜色
$uni-font-size-paragraph:15px;
.u-picker__view {
height: 500rpx !important;
}

View File

@ -1,4 +1,4 @@
import baseConfig from '@/config/env'
import env from '@/config/env'
/*
* config
@ -12,9 +12,12 @@ import baseConfig from '@/config/env'
const request = (config) => {
return new Promise((resolve) => {
const baseConfig = env[getApp().globalData.getEnvConfig()]
const token = config?.token ? config.token : uni.getStorageSync('token')
const headerDefault = {
version: baseConfig.version,
appid: getApp().globalData.appid,
version: baseConfig.version + '+' + baseConfig.buildNumber,
authorization: `Bearer ${token}`
}
const URL = config.baseUrl ? config.baseUrl + config.url : baseConfig.baseUrl + config.url
@ -62,18 +65,14 @@ const request = (config) => {
resolve({ code: -1, data, message: '网络不太好哦,请稍后再试' })
},
async complete(res) {
const env =
baseConfig.baseUrl.indexOf('test') !== -1
? 'test'
: baseConfig.baseUrl.indexOf('pre') !== -1
? 'pre'
: 'release'
console.log(URL.substring(baseConfig.baseUrl.length + 1), {
env: getApp().globalData.getEnvConfig(),
url: URL.substring(baseConfig.baseUrl.length + 1),
req: config?.data || {},
code: res?.data?.errorCode,
res: res?.data?.data,
token: header?.authorization || '',
message: res?.data?.errorMsg,
duration: new Date().getTime() - timestamp
})
}