1. 完成消息提醒相关功能

This commit is contained in:
范鹏 2025-02-25 15:07:52 +08:00
parent a5b2018949
commit 7b34291570
22 changed files with 2443 additions and 26 deletions

View File

@ -55,3 +55,12 @@ export function getFingerprintRequest(data) {
data
})
}
// 获取胁迫指纹列表
export function getCoercedListRequest(data) {
return request({
url: '/fingerprint/getCoercedList',
method: 'POST',
data
})
}

View File

@ -37,3 +37,12 @@ export function deleteLockRequest(data) {
data
})
}
// 锁用户列表
export function lockUserListRequest(data) {
return request({
url: '/lock/lockKeysList',
method: 'POST',
data
})
}

View File

@ -73,3 +73,57 @@ export function lockDataUploadRequest(data) {
data
})
}
// 获取锁通知设置
export function getLockNoticeSettingRequest(data) {
return request({
url: '/lockSetting/getLockNoticeSetting',
method: 'POST',
data
})
}
// 更新锁通知设置
export function updateLockNoticeSettingRequest(data) {
return request({
url: '/lockSetting/updateLockNoticeSetting',
method: 'POST',
data
})
}
// 获取通知列表
export function getNoticeListRequest(data) {
return request({
url: '/lockNoticeSettingAccount/list',
method: 'POST',
data
})
}
// 更新通知
export function updateNoticeRequest(data) {
return request({
url: '/lockNoticeSettingAccount/update',
method: 'POST',
data
})
}
// 删除通知
export function deleteNoticeRequest(data) {
return request({
url: '/lockNoticeSettingAccount/delete',
method: 'POST',
data
})
}
// 添加通知
export function addNoticeRequest(data) {
return request({
url: '/lockNoticeSettingAccount/add',
method: 'POST',
data
})
}

View File

@ -101,3 +101,11 @@ export function updateTimezoneOffsetRequest(data) {
})
}
// 获取url
export function getWebUrlRequest(data) {
return request({
url: '/v2/service/getPackageUrl',
method: 'POST',
data
})
}

30
constant/keyType.js Normal file
View File

@ -0,0 +1,30 @@
export const keysType = {
1: {
name: '电子钥匙',
icon: '/static/images/icon_user.png'
},
2: {
name: '密码',
icon: '/static/images/icon_password.png'
},
3: {
name: '指纹',
icon: '/static/images/icon_fingerprint_white.png'
},
4: {
name: '卡',
icon: '/static/images/icon_card_white.png'
},
5: {
name: '人脸',
icon: '/static/images/icon_face_white.png'
},
6: {
name: '掌静脉',
icon: '/static/images/icon_palm_vein_white.png'
},
7: {
name: '遥控',
icon: '/static/images/icon_remote_white.png'
}
}

View File

@ -414,6 +414,76 @@
"navigationBarTitleText": "电机功率设置",
"disableScroll": true
}
},
{
"path": "pages/catEye/catEye",
"style": {
"navigationBarTitleText": "猫眼设置",
"disableScroll": true
}
},
{
"path": "pages/messageReminder/messageReminder",
"style": {
"navigationBarTitleText": "消息提醒",
"disableScroll": true
}
},
{
"path": "pages/openDoorNotice/openDoorNotice",
"style": {
"navigationBarTitleText": "家人到家",
"disableScroll": true
}
},
{
"path": "pages/noticeDetail/noticeDetail",
"style": {
"navigationBarTitleText": "家人详情",
"disableScroll": true
}
},
{
"path": "pages/lockUser/lockUser",
"style": {
"navigationBarTitleText": "锁用户",
"disableScroll": true
}
},
{
"path": "pages/noticeWay/noticeWay",
"style": {
"navigationBarTitleText": "提醒方式",
"disableScroll": true
}
},
{
"path": "pages/notOpenDoor/notOpenDoor",
"style": {
"navigationBarTitleText": "N天未开门",
"disableScroll": true
}
},
{
"path": "pages/lowElecNotice/lowElecNotice",
"style": {
"navigationBarTitleText": "低电量提醒",
"disableScroll": true
}
},
{
"path": "pages/coercionOpenDoor/coercionOpenDoor",
"style": {
"navigationBarTitleText": "胁迫开门",
"disableScroll": true
}
},
{
"path": "pages/coercionFingerprint/coercionFingerprint",
"style": {
"navigationBarTitleText": "指纹列表",
"disableScroll": true
}
}
],
"globalStyle": {

11
pages/catEye/catEye.vue Normal file
View File

@ -0,0 +1,11 @@
<template>
<view> </view>
</template>
<script setup></script>
<style lang="scss">
page {
background-color: $uni-bg-color-grey;
}
</style>

View File

@ -0,0 +1,329 @@
<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="searchStr"
bgColor="#ffffff"
:showAction="false"
maxlength="50"
></up-search>
</view>
<view style="padding: 0 0 calc(env(safe-area-inset-bottom) + 250rpx) 0">
<view v-if="list.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>
<view v-for="(item, index) in list" :key="index">
<view class="item" @click="change(item)">
<image
class="item-left"
:src="keysType[item.openLockType].icon"
mode="aspectFit"
></image>
<view class="item-right">
<view style="display: flex; align-items: center">
<view class="item-right-top">{{ item?.name }}</view>
</view>
<view class="item-right-bottom">{{ item?.timeText }}</view>
</view>
<view class="ml-a mr-4">
<image
class="w-40 h-40"
v-if="info?.id === item.id && info?.openLockType === item.openLockType"
src="/static/images/icon_select.png"
></image>
<image v-else class="w-40 h-40" src="/static/images/icon_not_select.png"></image>
</view>
</view>
<view class="line"></view>
</view>
</view>
</view>
</scroll-view>
<view
v-if="list.length > 0"
@click="confirm"
class="pos-fixed bottom-[calc(env(safe-area-inset-bottom)+48rpx)] w-686 mx-4 h-88 line-height-88rpx text-center bg-#63b8af text-white rounded-3xl"
>确定
</view>
</view>
</template>
<script setup>
import { getCurrentInstance, ref } from 'vue'
import { timeFormat } from 'uview-plus'
import { onLoad } from '@dcloudio/uni-app'
import { useBasicStore } from '@/stores/basic'
import { useBluetoothStore } from '@/stores/bluetooth'
import { keysType } from '@/constant/keyType'
import { getCoercedListRequest } from '@/api/fingerprint'
const instance = getCurrentInstance().proxy
const eventChannel = instance.getOpenerEventChannel()
const $basic = useBasicStore()
const $bluetooth = useBluetoothStore()
const searchStr = ref('')
const list = ref([])
const lockId = ref(null)
const deviceInfo = ref(null)
const requestFinished = ref(false)
const info = ref(null)
const pageSize = 50
const pageNo = ref(1)
const total = ref(0)
const refresherTriggered = ref(false)
onLoad(async options => {
if (JSON.parse(options.info)) {
info.value = JSON.parse(options.info)
info.value = {
...info.value,
name: info.value.remark,
openLockType: info.value.openDoorType,
id: info.value.openDoorId
}
}
uni.showLoading({
title: '加载中',
mask: true
})
deviceInfo.value = await $basic.getDeviceInfo()
lockId.value = $bluetooth.currentLockInfo.lockId
const { code, message } = await getList({
pageNo: pageNo.value,
searchStr: searchStr.value
})
requestFinished.value = true
uni.hideLoading()
if (code !== 0) {
uni.showToast({
title: message,
icon: 'none'
})
}
})
const refresherList = async () => {
refresherTriggered.value = true
pageNo.value = 1
const { code } = await getList({
pageNo: pageNo.value,
searchStr: searchStr.value
})
if (code === 0) {
uni.showToast({
title: '刷新成功',
icon: 'none'
})
}
refresherTriggered.value = false
}
const nextPage = async () => {
if (total.value <= pageNo.value * pageSize) {
return
}
const no = pageNo.value + 1
const { code } = await getList(no, false)
if (code === 0) {
pageNo.value = no
}
}
const confirm = () => {
if (info.value) {
eventChannel.emit('confirm', {
id: info.value.id,
type: info.value.openLockType,
name: info.value.name
})
}
}
const getList = async params => {
const { code, data, message } = await getCoercedListRequest({
lockId: lockId.value,
pageSize,
...params
})
if (code === 0) {
total.value = data.total
for (let i = 0; i < data.list.length; i++) {
data.list[i].openLockType = 3
data.list[i].name = data.list[i].fingerprintName
data.list[i].id = data.list[i].fingerprintId
if (data.list[i].fingerprintType === 1) {
data.list[i].timeText = timeFormat(data.list[i].createDate, 'yyyy-mm-dd hh:MM') + ' 永久'
} else if (data.list[i].fingerprintType === 2) {
data.list[i].timeText =
timeFormat(data.list[i].startDate, 'yyyy-mm-dd hh:MM') +
' - ' +
timeFormat(data.list[i].endDate, 'yyyy-mm-dd hh:MM') +
' 限时'
} else {
data.list[i].timeText =
timeFormat(data.list[i].startDate, 'yyyy-mm-dd') +
' - ' +
timeFormat(data.list[i].endDate, 'yyyy-mm-dd') +
' 循环'
}
}
if (params.pageNo === 1) {
list.value = data.list
} else {
list.value = list.value.concat(data.list)
}
return { code }
}
return { code, message }
}
const changeSearch = async data => {
pageNo.value = 1
const { code, message } = await getList({
pageNo: pageNo.value,
searchStr: data
})
if (code !== 0) {
uni.showToast({
title: message,
icon: 'none'
})
}
}
const change = item => {
info.value = item
}
</script>
<style lang="scss">
page {
background-color: $uni-bg-color-grey;
}
</style>
<style lang="scss" scoped>
.search {
padding: 32rpx;
width: 686rpx !important;
}
.button {
display: flex;
align-items: center;
position: fixed;
bottom: calc(env(safe-area-inset-bottom) + 20rpx);
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;
}
}
.item {
display: flex;
align-items: center;
background-color: #ffffff;
height: 120rpx;
width: 750rpx;
.item-left {
margin-left: 32rpx;
width: 80rpx;
height: 80rpx;
}
.item-right {
margin-right: 32rpx;
margin-left: 32rpx;
width: 574rpx;
.item-right-top {
max-width: 400rpx;
font-size: 32rpx;
font-weight: bold;
padding-bottom: 6rpx;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.item-right-bottom {
font-size: 24rpx;
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;
}
.status {
margin-left: auto;
font-size: 26rpx;
color: #df282d;
}
</style>

View File

@ -0,0 +1,159 @@
<template>
<view>
<view class="text-sm h-80rpx py-3 mx-4">
当被胁迫要求强行开锁时使用胁迫指纹会触发报警报警消息会推送给管理员该功能需要锁联网
</view>
<scroll-view
v-if="deviceInfo"
scroll-y="true"
:style="{
height:
deviceInfo.screenHeight -
deviceInfo.safeArea.top -
(deviceInfo.screenWidth / 750) * 128 +
'px'
}"
lower-threshold="100"
@refresherrefresh="refresherList"
:refresher-enabled="true"
@scrolltolower="nextPage"
:refresher-triggered="refresherTriggered"
>
<view v-if="list.length === 0">
<image
class="w-150 h-150 transform-translate-x-[-50%] mt-300rpx mr-a mb-20rpx ml-50%"
src="/static/images/background_empty_list.png"
mode="aspectFill"
></image>
<view class="text-center text-base text-#999999">暂无数据</view>
</view>
<view v-else class="pb-[calc(env(safe-area-inset-bottom)+250rpx)]">
<view
v-for="(item, index) in list"
:key="index"
class="flex items-center bg-white px-4 py-2 mt-4rpx"
@click="toJump(item)"
>
<image :src="keysType[item.settingValue.openDoorType].icon" class="w-80 h-80" />
<view class="ml-3">
<view class="break-all max-w-500 text-base">{{ item.settingValue.remark }}</view>
<view class="text-sm text-gray-500">{{
keysType[item.settingValue.openDoorType].name
}}</view>
</view>
<view class="ml-a"> <up-icon name="arrow-right"></up-icon></view>
</view>
</view>
</scroll-view>
<view
@click="toJump(null)"
class="flex items-center justify-center bg-white shadow-sm rounded-md pos-fixed bottom-[calc(env(safe-area-inset-bottom)+48rpx)] w-686 mx-4 h-100"
><view class="flex items-center">
<up-icon name="plus-circle-fill" color="#002ce5" size="50"></up-icon
><view class="text-lg text-#63b8af ml-2 font-bold">胁迫指纹</view>
</view></view
>
</view>
</template>
<script setup>
import { onMounted, ref } from 'vue'
import { useBasicStore } from '@/stores/basic'
import { getNoticeListRequest } from '@/api/setting'
import { useBluetoothStore } from '@/stores/bluetooth'
import { keysType } from '@/constant/keyType'
const $basic = useBasicStore()
const $bluetooth = useBluetoothStore()
const deviceInfo = ref(null)
const refresherTriggered = ref(false)
const list = ref([])
const pageSize = 50
const pageNo = ref(1)
const total = ref(0)
onMounted(async () => {
uni.showLoading({
title: '加载中'
})
deviceInfo.value = await $basic.getDeviceInfo()
await getList(pageNo.value, true)
})
const refresherList = async () => {
refresherTriggered.value = true
pageNo.value = 1
const { code } = await getList(pageNo.value, false)
if (code === 0) {
uni.showToast({
title: '刷新成功',
icon: 'none'
})
}
refresherTriggered.value = false
}
const nextPage = async () => {
if (total.value <= pageNo.value * pageSize) {
return
}
const no = pageNo.value + 1
const { code } = await getList(no, false)
if (code === 0) {
pageNo.value = no
}
}
const getList = async (no, flag) => {
const { code, data, message } = await getNoticeListRequest({
lockId: $bluetooth.currentLockInfo.lockId,
noticeType: 20,
pageSize,
pageNo: no
})
if (flag) {
uni.hideLoading()
}
if (code === 0) {
total.value = data.total
if (no === 1) {
list.value = data.list
} else {
list.value = list.value.concat(data.list)
}
} else {
uni.showToast({
title: message,
icon: 'none'
})
}
return { code, message }
}
const toJump = (data = null) => {
$basic.routeJump({
name: 'noticeDetail',
params: {
info: data ? JSON.stringify(data) : null,
title: data ? '指纹详情' : '胁迫指纹',
mode: 'coercion'
},
events: {
refresherList() {
pageNo.value = 1
getList(pageNo.value, false)
}
}
})
}
</script>
<style lang="scss">
page {
background-color: $uni-bg-color-grey;
}
</style>

View File

@ -441,7 +441,6 @@
}
const { code, data } = await $bluetooth.setLockPassword(params)
if (code === 0 && data.status === 0) {
console.log(1111, data)
const { code: requestCode, message } = await addCustomPasswordRequest({
lockId: $bluetooth.currentLockInfo.lockId,
isCoerced: 2,

392
pages/lockUser/lockUser.vue Normal file
View File

@ -0,0 +1,392 @@
<template>
<view>
<scroll-view
v-if="deviceInfo"
scroll-y="true"
:style="{ height: deviceInfo.screenHeight - deviceInfo.safeArea.top + 'px' }"
lower-threshold="100"
>
<view class="search">
<up-search
shape="square"
:searchIconSize="48"
:inputStyle="{ fontSize: '32rpx' }"
:height="80"
placeholder="搜索"
:clearabled="false"
@change="changeSearch"
v-model="searchStr"
bgColor="#ffffff"
:showAction="false"
maxlength="50"
></up-search>
</view>
<view style="padding: 0 0 calc(env(safe-area-inset-bottom) + 250rpx) 0">
<view v-if="list.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>
<view v-for="(item, index) in list" :key="index">
<view class="item" @click="change(item)">
<image
class="item-left"
:src="keysType[item.openLockType].icon"
mode="aspectFit"
></image>
<view class="item-right">
<view style="display: flex; align-items: center">
<view class="item-right-top">{{ item?.name }}</view>
</view>
<view class="item-right-bottom">{{ item?.timeText }}</view>
</view>
<view class="ml-a mr-4">
<image
class="w-40 h-40"
v-if="info?.id === item.id && info?.openLockType === item.openLockType"
src="/static/images/icon_select.png"
></image>
<image v-else class="w-40 h-40" src="/static/images/icon_not_select.png"></image>
</view>
</view>
<view class="line"></view>
</view>
</view>
</view>
</scroll-view>
<view
v-if="list.length > 0"
@click="confirm"
class="pos-fixed bottom-[calc(env(safe-area-inset-bottom)+48rpx)] w-686 mx-4 h-88 line-height-88rpx text-center bg-#63b8af text-white rounded-3xl"
>确定
</view>
</view>
</template>
<script setup>
import { getCurrentInstance, ref } from 'vue'
import { timeFormat } from 'uview-plus'
import { onLoad } from '@dcloudio/uni-app'
import { useBasicStore } from '@/stores/basic'
import { useBluetoothStore } from '@/stores/bluetooth'
import { lockUserListRequest } from '@/api/lock'
import { keysType } from '@/constant/keyType'
const instance = getCurrentInstance().proxy
const eventChannel = instance.getOpenerEventChannel()
const $basic = useBasicStore()
const $bluetooth = useBluetoothStore()
const searchStr = ref('')
const list = ref([])
const lockId = ref(null)
const deviceInfo = ref(null)
const requestFinished = ref(false)
const info = ref(null)
onLoad(async options => {
if (JSON.parse(options.info)) {
info.value = JSON.parse(options.info)
info.value = {
...info.value,
name: info.value.remark,
openLockType: info.value.openDoorType,
id: info.value.openDoorId
}
}
uni.showLoading({
title: '加载中',
mask: true
})
deviceInfo.value = await $basic.getDeviceInfo()
lockId.value = $bluetooth.currentLockInfo.lockId
const { code, message } = await getList({
searchStr: searchStr.value
})
requestFinished.value = true
uni.hideLoading()
if (code !== 0) {
uni.showToast({
title: message,
icon: 'none'
})
}
})
const confirm = () => {
if (info.value) {
eventChannel.emit('confirm', {
id: info.value.id,
type: info.value.openLockType,
name: info.value.name
})
}
}
const getList = async params => {
const { code, data, message } = await lockUserListRequest({
...params,
lockId: lockId.value
})
if (code === 0) {
const result = []
for (let i = 0; i < data.length; i++) {
if (data[i].openLockType === 1) {
for (let j = 0; j < data[i].keys.length; j++) {
data[i].keys[j].openLockType = data[i].openLockType
data[i].keys[j].name = data[i].keys[j].keyName
if (data[i].keys[j].keyType === 1) {
data[i].keys[j].timeText =
`${timeFormat(new Date(data[i].keys[j].created_at), 'yyyy-mm-dd h:M')} 永久`
} else if (data[i].keys[j].keyType === 2) {
data[i].keys[j].timeText =
`${timeFormat(new Date(data[i].keys[j].startDate), 'yyyy-mm-dd h:M')} - ${timeFormat(new Date(data[i].keys[j].endDate), 'yyyy-mm-dd h:M')} 限时`
} else if (data[i].keys[j].keyType === 3) {
data[i].keys[j].timeText =
`${timeFormat(new Date(data[i].keys[j].created_at), 'yyyy-mm-dd h:M')} 单次`
} else {
data[i].keys[j].timeText = `循环`
}
}
}
if (data[i].openLockType === 2) {
for (let j = 0; j < data[i].keys.length; j++) {
data[i].keys[j].openLockType = data[i].openLockType
data[i].keys[j].name = data[i].keys[j].keyboardPwdName
data[i].keys[j].id = data[i].keys[j].keyboardPwdId
if (data[i].keys[j].keyboardPwdType === 2) {
data[i].keys[j].timeText =
`${timeFormat(new Date(data[i].keys[j].created_at), 'yyyy-mm-dd h:M')} 永久`
} else if (data[i].keys[j].keyboardPwdType === 3) {
data[i].keys[j].timeText = `${data[i].keys[j].validTimeStr} 限时`
} else {
let text = ''
if (data[i].keys[j].keyboardPwdType === 5) {
text = '周末'
} else if (data[i].keys[j].keyboardPwdType === 6) {
text = '每日'
} else if (data[i].keys[j].keyboardPwdType === 7) {
text = '工作日'
} else if (data[i].keys[j].keyboardPwdType === 8) {
text = '周一'
} else if (data[i].keys[j].keyboardPwdType === 9) {
text = '周二'
} else if (data[i].keys[j].keyboardPwdType === 10) {
text = '周三'
} else if (data[i].keys[j].keyboardPwdType === 11) {
text = '周四'
} else if (data[i].keys[j].keyboardPwdType === 12) {
text = '周五'
} else if (data[i].keys[j].keyboardPwdType === 13) {
text = '周六'
} else if (data[i].keys[j].keyboardPwdType === 14) {
text = '周日'
}
data[i].keys[j].timeText =
`${text} ${data[i].keys[j].hoursStart}:00-${data[i].keys[j].hoursEnd}:00 循环`
}
}
}
if (data[i].openLockType === 3) {
for (let j = 0; j < data[i].keys.length; j++) {
data[i].keys[j].openLockType = data[i].openLockType
data[i].keys[j].name = data[i].keys[j].fingerprintName
data[i].keys[j].id = data[i].keys[j].fingerprintId
if (data[i].keys[j].fingerprintType === 1) {
data[i].keys[j].timeText =
timeFormat(data[i].keys[j].createDate, 'yyyy-mm-dd hh:MM') + ' 永久'
} else if (data[i].keys[j].fingerprintType === 2) {
data[i].keys[j].timeText =
timeFormat(data[i].keys[j].startDate, 'yyyy-mm-dd hh:MM') +
' - ' +
timeFormat(data[i].keys[j].endDate, 'yyyy-mm-dd hh:MM') +
' 限时'
} else {
data[i].keys[j].timeText =
timeFormat(data[i].keys[j].startDate, 'yyyy-mm-dd') +
' - ' +
timeFormat(data[i].keys[j].endDate, 'yyyy-mm-dd') +
' 循环'
}
}
}
if (data[i].openLockType === 4) {
for (let j = 0; j < data[i].keys.length; j++) {
data[i].keys[j].openLockType = data[i].openLockType
data[i].keys[j].name = data[i].keys[j].cardName
data[i].keys[j].id = data[i].keys[j].cardId
if (data[i].keys[j].cardType === 1) {
data[i].keys[j].timeText =
timeFormat(data[i].keys[j].createDate, 'yyyy-mm-dd hh:MM') + ' 永久'
} else if (data[i].keys[j].cardType === 2) {
data[i].keys[j].timeText =
timeFormat(data[i].keys[j].startDate, 'yyyy-mm-dd hh:MM') +
' - ' +
timeFormat(data[i].keys[j].endDate, 'yyyy-mm-dd hh:MM') +
' 限时'
} else {
data[i].keys[j].timeText =
timeFormat(data[i].keys[j].startDate, 'yyyy-mm-dd') +
' - ' +
timeFormat(data[i].keys[j].endDate, 'yyyy-mm-dd') +
' 循环'
}
}
}
if (data[i].openLockType === 5) {
for (let j = 0; j < data[i].keys.length; j++) {
data[i].keys[j].openLockType = data[i].openLockType
data[i].keys[j].id = data[i].keys[j].faceId
if (data[i].keys[j].faceType === 1) {
data[i].keys[j].timeText =
timeFormat(data[i].keys[j].createDate, 'yyyy-mm-dd hh:MM') + ' 永久'
} else if (data[i].keys[j].faceType === 2) {
data[i].keys[j].timeText =
timeFormat(data[i].keys[j].startDate, 'yyyy-mm-dd hh:MM') +
' - ' +
timeFormat(data[i].keys[j].endDate, 'yyyy-mm-dd hh:MM') +
' 限时'
} else {
data[i].keys[j].timeText =
timeFormat(data[i].keys[j].startDate, 'yyyy-mm-dd') +
' - ' +
timeFormat(data[i].keys[j].endDate, 'yyyy-mm-dd') +
' 循环'
}
}
}
result.push(...data[i].keys)
}
console.log(result)
list.value = result
return { code }
}
return { code, message }
}
const changeSearch = async data => {
const { code, message } = await getList({
searchStr: data
})
if (code !== 0) {
uni.showToast({
title: message,
icon: 'none'
})
}
}
const change = item => {
info.value = item
}
</script>
<style lang="scss">
page {
background-color: $uni-bg-color-grey;
}
</style>
<style lang="scss" scoped>
.search {
padding: 32rpx;
width: 686rpx !important;
}
.button {
display: flex;
align-items: center;
position: fixed;
bottom: calc(env(safe-area-inset-bottom) + 20rpx);
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;
}
}
.item {
display: flex;
align-items: center;
background-color: #ffffff;
height: 120rpx;
width: 750rpx;
.item-left {
margin-left: 32rpx;
width: 80rpx;
height: 80rpx;
}
.item-right {
margin-right: 32rpx;
margin-left: 32rpx;
width: 574rpx;
.item-right-top {
max-width: 400rpx;
font-size: 32rpx;
font-weight: bold;
padding-bottom: 6rpx;
white-space: nowrap;
overflow: hidden;
text-overflow: ellipsis;
}
.item-right-bottom {
font-size: 24rpx;
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;
}
.status {
margin-left: auto;
font-size: 26rpx;
color: #df282d;
}
</style>

View File

@ -0,0 +1,135 @@
<template>
<view>
<view class="text-sm h-80rpx py-3 mx-4">
打开提醒后当锁电量低于20%10%5%系统会给指定对象发送提醒消息电量读取方式网关读取或APP读取
</view>
<view class="bg-white mx-4 rounded-md mt-4 text-base">
<view class="flex items-center p-3">
<view>低电量提醒</view>
<switch
@click="changeCheck()"
:checked="check"
class="transform-scale-90 ml-a"
:disabled="true"
color="#002ce5"
/>
</view>
</view>
<view v-if="check" class="bg-white mx-4 rounded-md mt-4 text-base" @click="toNoticeWay">
<view class="flex items-center p-3">
<view>提醒方式</view>
<view class="ml-a"><up-icon name="arrow-right"></up-icon></view>
</view>
<view class="pb-4">
<view class="flex items-center bg-#f4f4f4 mx-4 py-1 px-2 rounded-md">
<view>APP推送</view>
<view class="text-gray-500 ml-a">管理员</view>
</view>
<view v-for="(list, index) in lowElecNoticeWayList" :key="index">
<view
v-if="list.accounts.length > 0"
class="flex items-center bg-#f4f4f4 mx-4 py-1 px-2 rounded-md mt-4"
>
<view>{{ list.type === 'mail' ? '邮件提醒' : '短信提醒' }}</view>
<view class="text-sm ml-a text-right">
<view
class="text-gray-500"
v-for="(item, ListIndex) in list.accounts"
:key="ListIndex"
>
{{ item.account }}
</view>
</view>
</view>
</view>
</view>
</view>
<view
@click="update"
class="pos-fixed bg-#63b8af bottom-[calc(env(safe-area-inset-bottom)+48rpx)] w-686 mx-4 h-88 line-height-88rpx text-center text-white rounded-3xl"
>保存
</view>
</view>
</template>
<script setup>
import { onMounted, ref } from 'vue'
import { useBluetoothStore } from '@/stores/bluetooth'
import { useBasicStore } from '@/stores/basic'
import { updateLockNoticeSettingRequest } from '@/api/setting'
const $bluetooth = useBluetoothStore()
const $basic = useBasicStore()
const check = ref(false)
const lowElecNoticeWayList = ref([])
const pending = ref(false)
onMounted(() => {
check.value = $bluetooth.currentLockNoticeSetting.lowElecNoticeState === 1
lowElecNoticeWayList.value = $bluetooth.currentLockNoticeSetting.lowElecNoticeWayList
})
const update = async () => {
if (pending.value) return
pending.value = true
uni.showLoading({
title: '更新中'
})
const data = {
lockId: $bluetooth.currentLockInfo.lockId,
lowElecNoticeState: check.value ? 1 : 0,
lowElecNoticeWayList: lowElecNoticeWayList.value
}
const { code, message } = await updateLockNoticeSettingRequest(data)
pending.value = false
uni.hideLoading()
if (code === 0) {
$bluetooth.updateCurrentLockNoticeSetting({
...$bluetooth.currentLockNoticeSetting,
...data
})
$basic.backAndToast('更新成功')
} else if (code === 434) {
uni.showModal({
title: '提示',
content: message,
showCancel: false
})
} else {
uni.showToast({
title: message,
icon: 'none'
})
}
}
const changeCheck = () => {
check.value = !check.value
}
const toNoticeWay = () => {
$basic.routeJump({
name: 'noticeWay',
params: {
info:
lowElecNoticeWayList.value.length === 0
? null
: JSON.stringify({ noticeWay: lowElecNoticeWayList.value })
},
events: {
confirm(data) {
lowElecNoticeWayList.value = data.noticeWay
uni.navigateBack()
}
}
})
}
</script>
<style lang="scss">
page {
background-color: $uni-bg-color-grey;
}
</style>

View File

@ -0,0 +1,140 @@
<template>
<view>
<view v-if="requestFinish">
<view
class="py-3 px-4 bg-white flex items-center justify-between text-base"
@click="toJump('openDoorNotice')"
>
<view class="item-title">开门通知</view>
<view><up-icon name="arrow-right"></up-icon></view>
</view>
<view
class="py-3 px-4 bg-white flex items-center justify-between text-base mt-4rpx"
@click="toJump('notOpenDoor')"
>
<view class="item-title">N天未开门</view>
<view class="flex items-center">
<view class="mr-2">{{
$bluetooth.currentLockNoticeSetting.dayNotOpenDoorState ? '已启用' : '未启用'
}}</view>
<up-icon name="arrow-right"></up-icon>
</view>
</view>
<view class="py-3 px-4 bg-white !py-2 flex items-center justify-between text-base mt-4rpx">
<view class="item-title">门未关好</view>
<switch
@click="
updateSetting(
'doorNotCloseState',
$bluetooth.currentLockNoticeSetting.doorNotCloseState ? 0 : 1
)
"
:checked="$bluetooth.currentLockNoticeSetting.doorNotCloseState"
class="transform-scale-90"
:disabled="true"
color="#002ce5"
/>
</view>
<view class="py-3 px-4 bg-white !py-2 flex items-center justify-between text-base mt-4rpx">
<view class="item-title">防拆报警</view>
<switch
@click="
updateSetting(
'tamperAlarmState',
$bluetooth.currentLockNoticeSetting.tamperAlarmState ? 0 : 1
)
"
:checked="$bluetooth.currentLockNoticeSetting.tamperAlarmState"
class="transform-scale-90"
:disabled="true"
color="#002ce5"
/>
</view>
<view
class="py-3 px-4 bg-white flex items-center justify-between text-base mt-4rpx"
@click="toJump('lowElecNotice')"
>
<view class="item-title">低电量提醒</view>
<view class="flex items-center">
<view class="mr-2">{{
$bluetooth.currentLockNoticeSetting.lowElecNoticeState ? '已启用' : '未启用'
}}</view>
<up-icon name="arrow-right"></up-icon>
</view>
</view>
<view
class="py-3 px-4 bg-white flex items-center justify-between text-base mt-4rpx"
@click="toJump('coercionOpenDoor')"
>
<view class="item-title">胁迫开门</view>
<view><up-icon name="arrow-right"></up-icon></view>
</view>
<view class="text-center text-sm mt-10">此模块功能需要锁联网后设置方可生效</view>
</view>
</view>
</template>
<script setup>
import { onMounted, ref } from 'vue'
import { getLockNoticeSettingRequest, updateLockNoticeSettingRequest } from '@/api/setting'
import { useBluetoothStore } from '@/stores/bluetooth'
import { useBasicStore } from '@/stores/basic'
const $bluetooth = useBluetoothStore()
const $basic = useBasicStore()
const requestFinish = ref(false)
const pending = ref(false)
onMounted(async () => {
const { code, data, message } = await getLockNoticeSettingRequest({
lockId: $bluetooth.currentLockInfo.lockId
})
if (code === 0) {
requestFinish.value = true
$bluetooth.updateCurrentLockNoticeSetting(data)
} else {
uni.showToast({
title: message,
icon: 'none'
})
}
})
const updateSetting = async (type, value) => {
if (pending.value) return
pending.value = true
uni.showLoading({
title: '更新中'
})
const { code, message } = await updateLockNoticeSettingRequest({
lockId: $bluetooth.currentLockInfo.lockId,
[type]: value
})
uni.hideLoading()
pending.value = false
if (code === 0) {
const info = $bluetooth.currentLockNoticeSetting
info[type] = value
$bluetooth.updateCurrentLockNoticeSetting(info)
} else {
uni.showToast({
title: message,
icon: 'none'
})
}
}
const toJump = name => {
$basic.routeJump({
name
})
}
</script>
<style lang="scss">
page {
background-color: $uni-bg-color-grey;
}
</style>

View File

@ -0,0 +1,215 @@
<template>
<view>
<view class="text-sm h-80rpx py-3 mx-4">
经过以上设定的时间锁没有被开启系统会给指定对象发送提醒消息该功能需要锁联网
</view>
<view class="bg-white mx-4 rounded-md mt-4 text-base">
<view class="flex items-center p-3">
<view>N天未开门提醒</view>
<switch
@click="changeCheck()"
:checked="check"
class="transform-scale-90 ml-a"
:disabled="true"
color="#002ce5"
/>
</view>
<view
v-if="check"
@click="show = true"
class="flex items-center p-3 border-t-1 border-t-solid border-t-gray-200"
>
<view>未开门时间</view>
<view class="ml-a"> {{ value }} </view>
<view class="ml-2"><up-icon name="arrow-right"></up-icon></view>
</view>
</view>
<view v-if="check" class="bg-white mx-4 rounded-md mt-4 text-base" @click="toNoticeWay">
<view class="flex items-center p-3">
<view>提醒方式</view>
<view class="ml-a"><up-icon name="arrow-right"></up-icon></view>
</view>
<view class="pb-4">
<view class="flex items-center bg-#f4f4f4 mx-4 py-1 px-2 rounded-md">
<view>APP推送</view>
<view class="text-gray-500 ml-a">管理员</view>
</view>
<view v-for="(list, index) in dayNotOpenDoorNoticeWayList" :key="index">
<view
v-if="list.accounts.length > 0"
class="flex items-center bg-#f4f4f4 mx-4 py-1 px-2 rounded-md mt-4"
>
<view>{{ list.type === 'mail' ? '邮件提醒' : '短信提醒' }}</view>
<view class="text-sm ml-a text-right">
<view
class="text-gray-500"
v-for="(item, ListIndex) in list.accounts"
:key="ListIndex"
>
{{ item.account }}
</view>
</view>
</view>
</view>
</view>
</view>
<view
@click="update"
class="pos-fixed bg-#63b8af bottom-[calc(env(safe-area-inset-bottom)+48rpx)] w-686 mx-4 h-88 line-height-88rpx text-center text-white rounded-3xl"
>保存
</view>
<up-picker
:show="show"
:columns="columns"
:itemHeight="70"
:visibleItemCount="5"
:defaultIndex="[value - 1]"
@close="show = false"
@cancel="show = false"
@confirm="changeValue"
></up-picker>
</view>
</template>
<script setup>
import { onShow } from '@dcloudio/uni-app'
import { onMounted, ref } from 'vue'
import { useBluetoothStore } from '@/stores/bluetooth'
import { useBasicStore } from '@/stores/basic'
import { updateLockNoticeSettingRequest } from '@/api/setting'
import { useUserStore } from '@/stores/user'
const $bluetooth = useBluetoothStore()
const $basic = useBasicStore()
const $user = useUserStore()
const check = ref(false)
const value = ref(0)
const dayNotOpenDoorNoticeWayList = ref([])
const show = ref(false)
const pending = ref(false)
const columns = ref([[]])
const flag = ref(false)
onShow(() => {
if (flag.value) {
$user.getUserInfo()
flag.value = false
}
})
onMounted(() => {
columns.value = [
Array.from({ length: 15 }, (_, i) => ({
text: `${i + 1}`,
value: i + 1
}))
]
check.value = $bluetooth.currentLockNoticeSetting.dayNotOpenDoorState === 1
value.value =
$bluetooth.currentLockNoticeSetting.dayNotOpenDoorValue === 0
? 3
: $bluetooth.currentLockNoticeSetting.dayNotOpenDoorValue
dayNotOpenDoorNoticeWayList.value =
$bluetooth.currentLockNoticeSetting.dayNotOpenDoorNoticeWayList
})
const changeValue = e => {
value.value = columns.value[0][e.indexs[0]].value
show.value = false
}
const update = async () => {
if (pending.value) return
if ($user.userInfo.isVip === 0) {
uni.showModal({
title: '提示',
content: '该功能是高级功能,请开通后在使用',
confirmText: '去开通',
success: async res => {
if (res.confirm) {
const { code, data, message } = await $user.getWebUrl()
if (code === 0) {
$basic.routeJump({
name: 'webview',
params: {
url: encodeURIComponent(data.vip_buy_url)
}
})
flag.value = true
} else {
uni.showToast({
title: message,
icon: 'none'
})
}
}
}
})
return
}
pending.value = true
uni.showLoading({
title: '更新中'
})
const data = {
lockId: $bluetooth.currentLockInfo.lockId,
dayNotOpenDoorState: check.value ? 1 : 0,
dayNotOpenDoorValue: value.value,
dayNotOpenDoorNoticeWayList: dayNotOpenDoorNoticeWayList.value
}
const { code, message } = await updateLockNoticeSettingRequest(data)
pending.value = false
uni.hideLoading()
if (code === 0) {
$bluetooth.updateCurrentLockNoticeSetting({
...$bluetooth.currentLockNoticeSetting,
...data
})
$basic.backAndToast('更新成功')
} else if (code === 434) {
uni.showModal({
title: '提示',
content: message,
showCancel: false
})
} else {
uni.showToast({
title: message,
icon: 'none'
})
}
}
const changeCheck = () => {
check.value = !check.value
}
const toNoticeWay = () => {
$basic.routeJump({
name: 'noticeWay',
params: {
info:
dayNotOpenDoorNoticeWayList.value.length === 0
? null
: JSON.stringify({ noticeWay: dayNotOpenDoorNoticeWayList.value })
},
events: {
confirm(data) {
dayNotOpenDoorNoticeWayList.value = data.noticeWay
uni.navigateBack()
}
}
})
}
</script>
<style lang="scss">
page {
background-color: $uni-bg-color-grey;
}
</style>

View File

@ -0,0 +1,422 @@
<template>
<view>
<view class="bg-white mx-4 rounded-md mt-4 text-base">
<view class="flex items-center p-3" @click="toSelect">
<view>{{ mode === 'all' ? '开门方式' : '胁迫指纹' }}</view>
<view v-if="info?.settingValue?.openDoorType" class="ml-a">
{{
mode === 'all'
? keysType[info.settingValue.openDoorType].name
: info?.settingValue?.remark
}}
</view>
<view v-else class="ml-a">请选择</view>
<view class="ml-2"><up-icon name="arrow-right"></up-icon></view>
</view>
<view
v-if="mode === 'all'"
class="flex items-center p-3 border-t-1 border-t-solid border-t-gray-200"
@click="openModal"
>
<view>家人</view>
<view class="ml-a break-all max-w-500">
<view v-if="type === 'edit'">{{ info?.settingValue?.remark }}</view>
<view v-else>
<input
class="w-450 h-60 text-right font-bold text-base"
:value="remark"
maxlength="50"
placeholder="请输入"
placeholder-class="text-base line-height-60rpx font-bold text-right"
@input="updateName"
/></view>
</view>
</view>
</view>
<view class="bg-white mx-4 rounded-md mt-4 text-base" @click="toNoticeWay">
<view class="flex items-center p-3">
<view>提醒方式</view>
<view class="ml-a"><up-icon name="arrow-right"></up-icon></view>
</view>
<view class="pb-4">
<view class="flex items-center bg-#f4f4f4 mx-4 py-1 px-2 rounded-md">
<view>APP推送</view>
<view class="text-gray-500 ml-a">管理员</view>
</view>
<view
class="flex items-center bg-#f4f4f4 mx-4 py-1 px-2 rounded-md mt-4"
v-if="
info?.settingValue?.noticeWay && info?.settingValue?.noticeWay[0]?.accounts.length > 0
"
>
<view>邮件提醒</view>
<view class="text-sm ml-a text-right">
<view
class="text-gray-500"
v-for="(item, index) in info.settingValue.noticeWay[0].accounts"
:key="index"
>
{{ item.account }}
</view></view
>
</view>
<view
class="flex items-center bg-#f4f4f4 mx-4 py-1 px-2 rounded-md mt-4"
v-if="
info?.settingValue?.noticeWay && info?.settingValue?.noticeWay[1]?.accounts.length > 0
"
>
<view>短信提醒</view>
<view class="ml-a text-sm text-right">
<view
class="text-gray-500"
v-for="(item, index) in info.settingValue.noticeWay[1].accounts"
:key="index"
>
{{ item.account }}</view
>
</view></view
>
</view>
</view>
<view
v-if="type === 'edit'"
@click="deleteData"
class="pos-fixed bottom-[calc(env(safe-area-inset-bottom)+48rpx)] w-686 mx-4 h-88 line-height-88rpx text-center bg-#df2a2c text-white rounded-3xl"
>删除
</view>
<view
v-else
@click="create"
class="pos-fixed bottom-[calc(env(safe-area-inset-bottom)+48rpx)] w-686 mx-4 h-88 line-height-88rpx text-center text-white rounded-3xl"
:class="[canCreate ? 'bg-#63b8af' : 'bg-#9d9ca1']"
>保存
</view>
<ModalInput
v-if="info"
ref="modalInput"
title="修改名字"
:autoClose="false"
placeholder="请输入"
:value="info?.settingValue?.remark"
@confirm="changeName"
/>
</view>
</template>
<script setup>
import { onLoad, onShow } from '@dcloudio/uni-app'
import { computed, getCurrentInstance, ref } from 'vue'
import { useBasicStore } from '@/stores/basic'
import { keysType } from '@/constant/keyType'
import { addNoticeRequest, deleteNoticeRequest, updateNoticeRequest } from '@/api/setting'
import { useBluetoothStore } from '@/stores/bluetooth'
import { useUserStore } from '@/stores/user'
const instance = getCurrentInstance().proxy
const eventChannel = instance.getOpenerEventChannel()
const $basic = useBasicStore()
const $bluetooth = useBluetoothStore()
const $user = useUserStore()
const info = ref(null)
const remark = ref('')
const type = ref('add')
const modalInput = ref(null)
const pending = ref(false)
const mode = ref('all')
const flag = ref(false)
const canCreate = computed(() => {
return info.value?.settingValue?.openDoorId
})
onLoad(options => {
if (options.title) {
uni.setNavigationBarTitle({
title: options.title
})
}
if (JSON.parse(options.info)) {
info.value = JSON.parse(options.info)
type.value = 'edit'
}
if (options.mode) {
mode.value = options.mode
}
})
onShow(() => {
if (flag.value) {
$user.getUserInfo()
flag.value = false
}
})
const create = async () => {
if (pending.value && canCreate) return
if ($user.userInfo.isVip === 0) {
uni.showModal({
title: '提示',
content: '该功能是高级功能,请开通后在使用',
confirmText: '去开通',
success: async res => {
if (res.confirm) {
const { code, data, message } = await $user.getWebUrl()
if (code === 0) {
$basic.routeJump({
name: 'webview',
params: {
url: encodeURIComponent(data.vip_buy_url)
}
})
flag.value = true
} else {
uni.showToast({
title: message,
icon: 'none'
})
}
}
}
})
return
}
pending.value = true
uni.showLoading({
title: '保存中'
})
const { code, message } = await addNoticeRequest({
lockId: $bluetooth.currentLockInfo.lockId,
noticeType: mode.value === 'all' ? 10 : 20,
settingValue: info.value.settingValue
})
uni.hideLoading()
pending.value = false
if (code === 0) {
$basic.backAndToast('保存成功')
eventChannel.emit('refresherList')
} else if (code === 434) {
uni.showModal({
title: '提示',
content: message,
showCancel: false
})
} else {
uni.showToast({
title: message,
icon: 'none'
})
}
}
const openModal = () => {
if (type.value !== 'edit') return
modalInput.value.open()
}
const updateName = e => {
remark.value = e.detail.value
info.value = {
...info.value,
settingValue: {
...info.value.settingValue,
remark: e.detail.value
}
}
}
const deleteData = async () => {
uni.showModal({
title: '提示',
content: '确定删除吗?',
success: async res => {
if (res.confirm) {
if (pending.value) return
pending.value = true
uni.showLoading({
title: '删除中'
})
const { code, message } = await deleteNoticeRequest({
lockNoticeSettingAccountId: info.value.id
})
uni.hideLoading()
pending.value = false
if (code === 0) {
$basic.backAndToast('删除成功')
eventChannel.emit('refresherList')
} else {
uni.showToast({
title: message,
icon: 'none'
})
}
}
}
})
}
const toNoticeWay = () => {
$basic.routeJump({
name: 'noticeWay',
params: {
info: info.value?.settingValue ? JSON.stringify(info.value.settingValue) : null
},
events: {
async confirm(data) {
if (type.value === 'add') {
info.value = {
settingValue: {
...(info.value?.settingValue || {}),
noticeWay: data.noticeWay
}
}
uni.navigateBack()
return
}
if (pending.value) return
pending.value = true
uni.showLoading({
title: '更新中'
})
const params = {
lockNoticeSettingAccountId: info.value.id,
settingValue: {
...info.value.settingValue,
noticeWay: data.noticeWay
}
}
const { code, message } = await updateNoticeRequest(params)
uni.hideLoading()
pending.value = false
if (code === 0) {
info.value = {
...info.value,
settingValue: params.settingValue
}
$basic.backAndToast('更新成功')
eventChannel.emit('refresherList')
} else {
uni.showToast({
title: message,
icon: 'none'
})
}
}
}
})
}
const changeName = async name => {
if (pending.value) return
pending.value = true
uni.showLoading({
title: '更新中'
})
const params = {
lockNoticeSettingAccountId: info.value.id,
settingValue: {
...info.value.settingValue,
remark: name
}
}
const { code, message } = await updateNoticeRequest(params)
uni.hideLoading()
pending.value = false
if (code === 0) {
modalInput.value.close()
info.value = {
...info.value,
settingValue: params.settingValue
}
uni.showToast({
title: '更新成功',
icon: 'none'
})
eventChannel.emit('refresherList')
} else {
uni.showToast({
title: message,
icon: 'none'
})
}
}
const toSelect = () => {
$basic.routeJump({
name: mode.value === 'all' ? 'lockUser' : 'coercionFingerprint',
params: {
info: info.value?.settingValue ? JSON.stringify(info.value.settingValue) : null
},
events: {
async confirm(data) {
if (type.value === 'add') {
info.value = {
settingValue: {
...(info.value?.settingValue || {}),
openDoorId: data.id,
openDoorType: data.type,
remark: data.name
}
}
remark.value = data.name
uni.navigateBack()
return
}
if (pending.value) return
pending.value = true
uni.showLoading({
title: '更新中'
})
const params = {
lockNoticeSettingAccountId: info.value.id,
settingValue: {
...info.value.settingValue,
openDoorId: data.id,
openDoorType: data.type,
remark: data.name
}
}
const { code, message } = await updateNoticeRequest(params)
uni.hideLoading()
pending.value = false
if (code === 0) {
info.value = {
...info.value,
settingValue: params.settingValue
}
$basic.backAndToast('更新成功')
eventChannel.emit('refresherList')
} else if (code === 434) {
uni.showModal({
title: '提示',
content: message,
showCancel: false
})
} else {
uni.showToast({
title: message,
icon: 'none'
})
}
}
}
})
}
</script>
<style lang="scss">
page {
background-color: $uni-bg-color-grey;
}
</style>

View File

@ -0,0 +1,191 @@
<template>
<view>
<view class="py-3 px-4 bg-white flex items-center justify-between text-base">
<view>APP推送</view>
<view class="ml-a">管理员</view>
</view>
<view class="pt-2 px-4 bg-white text-base mt-20rpx">
<view class="flex items-center w-full pb-2">
<view>邮件提醒</view>
<view class="ml-a h-50">
<up-icon
name="plus-circle-fill"
color="#002ce5"
size="50"
v-if="emailList.length < 3"
@click="addItem('email')"
></up-icon
></view>
</view>
<view v-if="emailList.length > 0">
<view
v-for="(item, index) in emailList"
:key="index"
class="flex items-center border-t-1 border-t-solid border-t-gray-200"
>
<up-icon
name="close-circle-fill"
color="#858585"
size="40"
@click="deleteItem('email', index)"
></up-icon>
<view class="ml-3">接收者</view>
<view class="ml-a">
<input
class="py-2 w-450 h-60 text-right font-bold text-base"
:value="item.account"
maxlength="50"
placeholder="请输入Email"
placeholder-class="text-base line-height-60rpx font-bold text-right"
@input="updateName('email', index, $event)"
/>
</view> </view
></view>
</view>
<view class="pt-2 px-4 bg-white text-base mt-20rpx">
<view class="flex items-center w-full pb-2">
<view>短信提醒</view>
<view class="ml-a h-50">
<up-icon
name="plus-circle-fill"
color="#002ce5"
size="50"
v-if="smsList.length < 3"
@click="addItem('sms')"
></up-icon
></view>
</view>
<view v-if="smsList.length > 0">
<view
v-for="(item, index) in smsList"
:key="index"
class="flex items-center border-t-1 border-t-solid border-t-gray-200"
>
<up-icon
name="close-circle-fill"
color="#858585"
size="40"
@click="deleteItem('sms', index)"
></up-icon>
<view class="ml-3">接收者</view>
<view class="ml-a">
<input
class="py-2 w-450 h-60 text-right font-bold text-base"
:value="item.account"
type="number"
maxlength="11"
placeholder="请输入手机号"
placeholder-class="text-base line-height-60rpx font-bold text-right"
@input="updateName('sms', index, $event)"
/>
</view> </view
></view>
</view>
<view
@click="confirm"
class="mt-10 w-686 mx-4 h-88 line-height-88rpx text-center bg-#63b8af text-white rounded-3xl"
>确定
</view>
</view>
</template>
<script setup>
import { onLoad } from '@dcloudio/uni-app'
import { getCurrentInstance, ref } from 'vue'
import test from 'uview-plus/libs/function/test'
const instance = getCurrentInstance().proxy
const eventChannel = instance.getOpenerEventChannel()
const info = ref(null)
const emailList = ref([])
const smsList = ref([])
onLoad(options => {
info.value = JSON.parse(options.info)
if (info.value?.noticeWay) {
emailList.value = info.value.noticeWay[0].accounts
smsList.value = info.value.noticeWay[1].accounts
}
})
const confirm = () => {
for (let i = 0; i < emailList.value.length; i++) {
if (test.email(emailList.value[i].account) === false) {
uni.showToast({
title: '含不符合规范的邮箱',
icon: 'none'
})
return
}
}
for (let i = 0; i < smsList.value.length; i++) {
if (test.mobile(smsList.value[i].account) === false) {
uni.showToast({
title: '含不符合规范的手机号',
icon: 'none'
})
return
}
}
eventChannel.emit('confirm', {
noticeWay: [
{ type: 'mail', accounts: emailList.value },
{ type: 'sms', accounts: smsList.value }
]
})
}
const addItem = type => {
if (type === 'email') {
emailList.value.push({ countryCode: 0, account: '' })
} else {
smsList.value.push({ countryCode: 86, account: '' })
}
}
const deleteItem = (type, index) => {
if (type === 'email') {
emailList.value.splice(index, 1)
} else {
smsList.value.splice(index, 1)
}
}
const updateName = (type, index, data) => {
if (type === 'email') {
emailList.value[index].account = data.detail.value
} else {
smsList.value[index].account = data.detail.value
}
}
</script>
<style lang="scss">
page {
background-color: $uni-bg-color-grey;
}
</style>
<style scoped lang="scss">
.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: 60rpx;
font-size: 36rpx;
font-weight: bold;
line-height: 108rpx;
}
</style>

View File

@ -0,0 +1,158 @@
<template>
<view>
<view class="text-sm h-80rpx py-3 mx-4">
若锁没有联网除电子钥匙外密码指纹等开门提醒无法及时发送请根据你的实际情况选择
</view>
<scroll-view
v-if="deviceInfo"
scroll-y="true"
:style="{
height:
deviceInfo.screenHeight -
deviceInfo.safeArea.top -
(deviceInfo.screenWidth / 750) * 128 +
'px'
}"
lower-threshold="100"
@refresherrefresh="refresherList"
:refresher-enabled="true"
@scrolltolower="nextPage"
:refresher-triggered="refresherTriggered"
>
<view v-if="list.length === 0">
<image
class="w-150 h-150 transform-translate-x-[-50%] mt-300rpx mr-a mb-20rpx ml-50%"
src="/static/images/background_empty_list.png"
mode="aspectFill"
></image>
<view class="text-center text-base text-#999999">暂无数据</view>
</view>
<view v-else class="pb-[calc(env(safe-area-inset-bottom)+250rpx)]">
<view
v-for="(item, index) in list"
:key="index"
class="flex items-center bg-white px-4 py-2 mt-4rpx"
@click="toJump(item)"
>
<image :src="keysType[item.settingValue.openDoorType].icon" class="w-80 h-80" />
<view class="ml-3">
<view class="break-all max-w-500 text-base">{{ item.settingValue.remark }}</view>
<view class="text-sm text-gray-500">{{
keysType[item.settingValue.openDoorType].name
}}</view>
</view>
<view class="ml-a"> <up-icon name="arrow-right"></up-icon></view>
</view>
</view>
</scroll-view>
<view
@click="toJump(null)"
class="flex items-center justify-center bg-white shadow-sm rounded-md pos-fixed bottom-[calc(env(safe-area-inset-bottom)+48rpx)] w-686 mx-4 h-100"
><view class="flex items-center">
<up-icon name="plus-circle-fill" color="#002ce5" size="50"></up-icon
><view class="text-lg text-#63b8af ml-2 font-bold">添加家人</view>
</view></view
>
</view>
</template>
<script setup>
import { onMounted, ref } from 'vue'
import { useBasicStore } from '@/stores/basic'
import { getNoticeListRequest } from '@/api/setting'
import { useBluetoothStore } from '@/stores/bluetooth'
import { keysType } from '@/constant/keyType'
const $basic = useBasicStore()
const $bluetooth = useBluetoothStore()
const deviceInfo = ref(null)
const refresherTriggered = ref(false)
const list = ref([])
const pageSize = 50
const pageNo = ref(1)
const total = ref(0)
onMounted(async () => {
uni.showLoading({
title: '加载中'
})
deviceInfo.value = await $basic.getDeviceInfo()
await getList(pageNo.value, true)
})
const refresherList = async () => {
refresherTriggered.value = true
pageNo.value = 1
const { code } = await getList(pageNo.value, false)
if (code === 0) {
uni.showToast({
title: '刷新成功',
icon: 'none'
})
}
refresherTriggered.value = false
}
const nextPage = async () => {
if (total.value <= pageNo.value * pageSize) {
return
}
const no = pageNo.value + 1
const { code } = await getList(no, false)
if (code === 0) {
pageNo.value = no
}
}
const getList = async (no, flag) => {
const { code, data, message } = await getNoticeListRequest({
lockId: $bluetooth.currentLockInfo.lockId,
noticeType: 10,
pageSize,
pageNo: no
})
if (flag) {
uni.hideLoading()
}
if (code === 0) {
total.value = data.total
if (no === 1) {
list.value = data.list
} else {
list.value = list.value.concat(data.list)
}
} else {
uni.showToast({
title: message,
icon: 'none'
})
}
return { code, message }
}
const toJump = (data = null) => {
$basic.routeJump({
name: 'noticeDetail',
params: {
info: data ? JSON.stringify(data) : null,
title: data ? '家人详情' : '添加家人'
},
events: {
refresherList() {
pageNo.value = 1
getList(pageNo.value, false)
}
}
})
}
</script>
<style lang="scss">
page {
background-color: $uni-bg-color-grey;
}
</style>

View File

@ -112,10 +112,25 @@
<view class="item-title">面容开锁</view>
<view><up-icon name="arrow-right"></up-icon></view>
</view>
<!-- <view class="py-3 px-4 bg-white flex items-center justify-between text-base">-->
<!-- <view class="item-title">消息提醒</view>-->
<!-- <view><up-icon name="arrow-right"></up-icon></view>-->
<!-- </view>-->
<view
v-if="$bluetooth.currentLockSetting.lockBasicInfo.keyRight === 1"
class="py-3 px-4 bg-white flex items-center justify-between text-base mt-4rpx"
@click="toJump('messageReminder')"
>
<view class="item-title">消息提醒</view>
<view><up-icon name="arrow-right"></up-icon></view>
</view>
<view
v-if="
$bluetooth.currentLockSetting.lockFeature.isSupportCatEye !== 1 &&
$bluetooth.currentLockSetting.lockBasicInfo.keyRight === 1
"
class="py-3 px-4 bg-white flex items-center justify-between text-base mt-4rpx"
@click="toJump('catEye')"
>
<view class="item-title">猫眼设置</view>
<view><up-icon name="arrow-right"></up-icon></view>
</view>
<view
v-if="
$bluetooth.currentLockSetting.lockFeature.openDirection === 1 &&

View File

@ -15,27 +15,30 @@
}
},
async onLoad(options) {
this.env = await env[await getApp().globalData.getEnvConfig()]
const officialAccounts = {
default: {
url: '/app/introduce',
name: '介绍'
},
userAgreement: {
url: '/app/userAgreement',
name: '用户协议'
},
privacy: {
url: '/app/privacy',
name: '隐私政策'
if (options.url) {
this.url = decodeURIComponent(options.url)
} else {
this.env = await env[await getApp().globalData.getEnvConfig()]
const officialAccounts = {
default: {
url: '/app/introduce',
name: '介绍'
},
userAgreement: {
url: '/app/userAgreement',
name: '用户协议'
},
privacy: {
url: '/app/privacy',
name: '隐私政策'
}
}
const item = officialAccounts[options?.type] || officialAccounts.default
this.url = this.env.webviewBaseUrl + item.url
uni.setNavigationBarTitle({
title: item.name
})
}
const item = officialAccounts[options?.type] || officialAccounts.default
this.url = this.env.webviewBaseUrl + item.url
console.log(this.url)
uni.setNavigationBarTitle({
title: item.name
})
}
}
</script>

View File

@ -306,6 +306,56 @@ const pages = [
name: 'motorTorsion',
path: '/pages/motorTorsion/motorTorsion',
tabBar: false
},
{
name: 'catEye',
path: '/pages/catEye/catEye',
tabBar: false
},
{
name: 'messageReminder',
path: '/pages/messageReminder/messageReminder',
tabBar: false
},
{
name: 'openDoorNotice',
path: '/pages/openDoorNotice/openDoorNotice',
tabBar: false
},
{
name: 'noticeDetail',
path: '/pages/noticeDetail/noticeDetail',
tabBar: false
},
{
name: 'lockUser',
path: '/pages/lockUser/lockUser',
tabBar: false
},
{
name: 'noticeWay',
path: '/pages/noticeWay/noticeWay',
tabBar: false
},
{
name: 'notOpenDoor',
path: '/pages/notOpenDoor/notOpenDoor',
tabBar: false
},
{
name: 'lowElecNotice',
path: '/pages/lowElecNotice/lowElecNotice',
tabBar: false
},
{
name: 'coercionOpenDoor',
path: '/pages/coercionOpenDoor/coercionOpenDoor',
tabBar: false
},
{
name: 'coercionFingerprint',
path: '/pages/coercionFingerprint/coercionFingerprint',
tabBar: false
}
]

View File

@ -127,6 +127,8 @@ export const useBluetoothStore = defineStore('ble', {
currentLockInfo: {},
// 当前锁设置信息
currentLockSetting: null,
// 当前锁消息设置
currentLockNoticeSetting: null,
// 消息序号
messageCount: 1,
// 是否初始化蓝牙
@ -140,6 +142,10 @@ export const useBluetoothStore = defineStore('ble', {
}
},
actions: {
// 更新锁消息设置
updateCurrentLockNoticeSetting(lockNoticeSetting) {
this.currentLockNoticeSetting = { ...this.currentLockNoticeSetting, ...lockNoticeSetting }
},
// 保存刚绑定的设备名称
updateBindedDeviceName(name) {
if (!this.bindedDeviceNameList.includes(name)) {

View File

@ -2,7 +2,7 @@
* @description 用户信息数据持久化
*/
import { defineStore } from 'pinia'
import { getUserInfoRequest, loginRequest, phoneLoginRequest } from '@/api/user'
import { getUserInfoRequest, getWebUrlRequest, loginRequest, phoneLoginRequest } from '@/api/user'
import { useLockStore } from '@/stores/lock'
import { setStorage, getStorage } from '@/utils/storage'
import { useNotificationStore } from '@/stores/notification'
@ -13,7 +13,8 @@ export const useUserStore = defineStore('user', {
// 用户信息
userInfo: {},
// 登录状态
isLogin: false
isLogin: false,
webUrl: null
}
},
actions: {
@ -82,6 +83,17 @@ export const useUserStore = defineStore('user', {
}
})
})
},
async getWebUrl() {
if (this.webUrl) {
return { code: 0, data: this.webUrl }
}
const { code, data, message } = await getWebUrlRequest()
if (code === 0) {
this.webUrl = data
return { code, data }
}
return { code, message }
}
}
})