SFH5/pagesb/initLock/index.vue
2026-03-16 11:10:28 +08:00

729 lines
24 KiB
Vue
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<view class="init-lock-wrap" :class="[`${themeInfo.theme}-theme`]">
<NavBar></NavBar>
<view class="search" @click="showScanPopup()">
<uv-icon name="search" size="20" color="#c0bdc0"></uv-icon>
</view>
<view class="lock-info" v-if="state.lockInfo.lockData">
<view class="name">{{ state.lockInfo.deviceName || state.lockInfo.MAC }}</view>
<view class="lock-wrap">
<uv-loading-icon mode="spinner" size="40" :color="themeInfo.activeColor" v-if="state.initLoading"></uv-loading-icon>
<uv-icon name="lock-fill" size="70" :color="themeInfo.activeColor" v-else></uv-icon>
</view>
<view class="tip">该智能锁已初始化成功</view>
<view class="setting-wrap">
<view class="wifi" @click="openNetworkPopup">
<uv-icon name="setting" size="20" :color="themeInfo.activeColor"></uv-icon>
<view class="text">WiFi</view>
</view>
</view>
</view>
<view class="empty-lock" v-else>
<view class="empty" @click="showScanPopup(true)">
<uv-icon name="plus" size="36" color="#c0bdc0"></uv-icon>
</view>
<view class="text">添加锁时手机必须在锁旁边</view>
</view>
<!-- 搜索智能锁弹框 -->
<uv-popup ref="scanPopup" class="scan-popup" mode="bottom" closeable :close-on-click-overlay="false">
<view class="scan-wrap">
<template v-if="state.initLockList?.length">
<view class="name">成功初始化的智能锁列表</view>
<view class="item-wrap" @click="selectLock(item)">
<div class="item active" v-for="item in state.initLockList" :key="item.lockId">
<text>{{ item.deviceName || item.MAC }}</text>
<uv-icon v-if="item.lockId == state.lockInfo.lockId" name="checkmark-circle" size="14" :color="themeInfo.activeColor"></uv-icon>
</div>
</view>
</template>
<view class="btn-wrap">
<view class="btn" @click="startScan">开始扫描</view>
<view class="btn">停止扫描</view>
</view>
<view class="loading-wrap" v-if="state.searchLoading">
<uv-loading-icon mode="spinner" vertical text="扫描附近的智能锁设备中"></uv-loading-icon>
</view>
<template v-else-if="state.searchList?.length">
<view class="name">扫描的智能锁列表</view>
<view class="item-wrap">
<div class="item" v-for="item in state.searchList" :key="item.deviceId" @click="handleSetName(item)" :class="{ 'active': item.isSettingMode }">
<text>{{ item.deviceName || item.MAC }}</text>
<uv-icon v-if="item.isSettingMode" name="plus-circle" size="14" :color="themeInfo.activeColor"></uv-icon>
</div>
</view>
</template>
<view class="empty-wrap" v-else>
<uv-icon name="empty-data" size="26" color="#b3b4b5"></uv-icon>
<text class="text">智能锁为空~</text>
</view>
</view>
</uv-popup>
<!-- 修改名称 -->
<uv-modal
ref="nameModal"
title="修改名称"
showCancelButton
:closeOnClickOverlay="false"
confirmText="确认初始化"
:confirmColor="themeInfo.activeColor"
@confirm="handleInitLock">
<uv-input v-model="state.lockAlias" placeholder="请输入名称" border="surround"></uv-input>
</uv-modal>
<!-- 配置网络 -->
<uv-popup ref="networkPopup" mode="bottom" :close-on-click-overlay="false">
<view class="network-wrap">
<view class="nav-wrap">
<uv-icon name="arrow-leftward" :color="themeInfo.iconColor" @click="closeNetwork"></uv-icon>
<view class="name">配置网络</view>
</view>
<view class="tip">不支持 5G WiFi 网络请选择 2.4G WiFi 网络进行配置</view>
<view class="input-wrap" @click="openWifiPopup">
<view class="label">WiFi 名称</view>
<view class="name">{{ wifiData.selectWifi.SSID }}</view>
<uv-icon name="arrow-right"></uv-icon>
</view>
<view class="input-wrap">
<view class="label">WiFi 密码</view>
<uv-input v-model="wifiData.password" placeholder="请输入WiFi密码" border="none"></uv-input>
</view>
<view class="btn" @click="handleConfigWifi">确定</view>
</view>
</uv-popup>
<uv-popup ref="wifiPopup" mode="bottom" :close-on-click-overlay="false">
<view class="wifi-wrap">
<view class="nav-wrap">
<text style="z-index: 9;" @click="closeWifiPopup">取消</text>
<view class="name">选择网络</view>
<text style="z-index: 9;" @click="handleSearchWifi">搜索</text>
</view>
<view class="loading-wrap" v-if="wifiData.isLoading">
<uv-loading-icon mode="spinner" vertical text="正在扫描智能锁附近可用wifi列表"></uv-loading-icon>
</view>
<template v-if="wifiData.list?.length">
<view class="wifi-item" v-for="(item, index) in wifiData.list" :key="index" @click="selectWifi(item)">
{{ item.SSID }}
</view>
</template>
</view>
</uv-popup>
</view>
</template>
<script setup>
import { ref, reactive, onMounted } from "vue";
import CryptoJs from "crypto-js";
import { useMainStore } from "@/store/index.js";
const { themeInfo } = useMainStore();
import { useLockApi } from "/Apis/lock.js";
import { ttLockRequest, ttLockRequest2 } from "./lockInitApi.js";
import NavBar from "@/components/navBar.vue";
const lockApi = useLockApi();
const scanPopup = ref();
const nameModal = ref();
const wifiPopup = ref();
const networkPopup = ref();
const state = reactive({
searchLoading: false, // 正在搜索智能锁设备中
initLoading: false, // 正在初始化智能锁中
lockInfo: {}, // 当前的智能锁
initInfo: {}, // 选择初始化的智能锁
initLockList: [],
searchList: [],
ttToken: "",
lockAlias: ""
});
const getLockList = async () => {
const openId = uni.getStorageSync("openId");
const { code, data } = await lockApi.GetInitLockList({ openId });
if (code == 200) {
state.initLockList = data;
if (state.initLockList?.length) {
state.lockInfo = state.initLockList[0];
}
}
}
const selectLock = (item) => {
state.lockInfo = item;
}
onMounted(() => {
getLockList();
});
const showScanPopup = (check) => {
if (check && state.lockInfo.lockData) return;
scanPopup.value.open();
startScan();
}
const checkBlueTooth = () => {
return new Promise(resolve => {
uni.openBluetoothAdapter({
success() {
resolve(true);
},
fail(err) {
console.log(err);
uni.showToast({
title: `蓝牙初始化失败:${err.errCode === 10001 ? "请打开蓝牙" : err.errMsg}`,
icon: 'none'
});
resolve(false);
}
});
});
}
// 开始扫描附近的智能锁设备
const startScan = async () => {
const openBlue = await checkBlueTooth();
if (!openBlue || state.searchLoading) return;
state.searchLoading = true;
requirePlugin("ttPlugin", ({ startScanBleDevice }) => {
// 开启蓝牙设备扫描
startScanBleDevice((lockDevice, lockList) => {
// TODO 成功扫描到设备
state.searchList = lockList;
state.searchLoading = false;
stopScan();
}, (err) => {
console.error(err);
uni.showToast({
title: `蓝牙扫描智能锁设备失败:${err.errorMsg}`,
icon: 'none'
});
state.searchLoading = false;
}
);
});
};
// 关闭蓝牙设备扫描
const stopScan = () => {
requirePlugin("ttPlugin", ({ stopScanBleDevice }) => {
stopScanBleDevice().then(res => {
state.searchLoading = false;
});
});
}
// 获取通通登录后的用户 token
const getLoginToken = () => {
return new Promise(resolve => {
if (state.ttToken) {
resolve(true);
return;
}
ttLockRequest("/oauth2/token", "POST", {
"client_id": "7946f0d923934a61baefb3303de4d132",
"client_secret": "56d9721abbc3d22a58452c24131a5554",
"grant_type": "password",
"redirect_uri": "http://www.sciener.cn",
"username": "+8617727694079",
"password": CryptoJs.MD5(String("lqx123456")).toString()
}).then(res => {
state.ttToken = res.access_token;
if (state.ttToken) {
uni.setStorageSync("tt_access_token", state.ttToken);
resolve(true);
} else {
resolve(false);
}
});
});
}
const handleSetName = (deviceFromScan) => {
if (!deviceFromScan.isSettingMode) {
uni.showToast({
title: `智能锁${deviceFromScan.deviceName || deviceFromScan.MAC}已被初始化,当前不可添加`,
icon: 'none'
});
return;
}
state.initInfo = deviceFromScan;
nameModal.value.open();
}
// 初始化智能锁
const handleInitLock = () => {
nameModal.value.close();
let deviceFromScan = state.initInfo;
if (state.initLoading) return;
state.initLoading = true;
uni.showLoading({
title: '正在初始化智能锁'
});
requirePlugin("ttPlugin", ({ getLockVersion, initLock }) => {
// 更新智能锁版本信息
getLockVersion({ deviceFromScan }).then(res => {
if (res.errorCode == 0) {
initLock({ deviceFromScan }).then(async result => {
uni.hideLoading();
state.initLoading = false;
if (result.errorCode == 0) {
console.log('----智能锁数据----', result);
const res = await getLoginToken();
if (!res) {
uni.showToast({
title: "获取tt用户token失败正在重置智能锁",
icon: 'none'
});
handleResetLock(result.lockData);
return;
}
ttLockRequest2("/v3/lock/initialize", "POST", {
"lockData": result.lockData,
"lockAlias": state.lockAlias
}).then(res => {
console.log('------tt用户同步数据------', res);
if (res.lockId) {
uni.showToast({
title: "智能锁已成功添加",
icon: 'none'
});
state.lockInfo = {
lockId: String(res.lockId),
lockData: result.lockData,
deviceId: state.initInfo.deviceId,
deviceName: state.lockAlias || state.initInfo.deviceName,
}
saveLockData();
openNetworkPopup();
} else {
uni.showToast({
title: "智能锁数据上传失败, 正在重置智能锁",
icon: 'none'
});
handleResetLock(result.lockData);
}
});
scanPopup.value.close();
} else {
uni.showToast({
title: `初始化智能锁失败:${result.errorMsg}`,
icon: 'none'
});
}
});
} else {
uni.hideLoading();
state.initLoading = false;
uni.showToast({
title: `更新智能锁版本信息失败:${res.errorMsg}`,
icon: 'none'
});
};
});
});
}
// 锁的数据绑定到小程序账户
const saveLockData = () => {
if (!state.lockInfo.lockData) return;
const openId = uni.getStorageSync("openId");
lockApi.SaveInitLock({
openId,
...state.lockInfo
});
}
// 重置智能锁
const handleResetLock = (lockData) => {
requirePlugin("ttPlugin", ({ resetLock }) => {
resetLock({ lockData }).then(res => {
if (res.errorCode == 0) {
uni.showToast({
title: '智能锁重置成功',
icon: 'none'
});
} else {
uni.showToast({
title: `智能锁重置失败,请长按重置键进行设备重置:${res.errorMsg}`,
icon: 'none'
});
}
});
});
}
const wifiData = reactive({
password: "",
isLoading: false,
list: [],
selectWifi: {},
});
// 打开网络配置
const openNetworkPopup = () => {
wifiData.password = "";
networkPopup.value.open();
}
// 关闭网络配置
const closeNetwork = () => {
networkPopup.value.close();
}
const openWifiPopup = () => {
wifiPopup.value.open();
if (!wifiData.list?.length) handleSearchWifi();
}
const closeWifiPopup = () => {
wifiPopup.value.close();
}
const handleSearchWifi = () => {
if (wifiData.isLoading) return;
wifiData.isLoading = true;
wifiData.list = [];
requirePlugin("ttPlugin", ({ scanWifi }) => {
scanWifi({ lockData: state.lockInfo.lockData }).then(res => {
wifiData.isLoading = false;
if (res.errorCode == 0) {
wifiData.list = res.data.wifiList;
uni.showToast({
title: "扫描智能锁附近可用wifi列表成功",
icon: 'none'
});
} else {
uni.showToast({
title: `扫描智能锁附近可用wifi列表失败${res.errorMsg}`,
icon: 'none'
});
}
})
});
}
const selectWifi = (item) => {
wifiData.selectWifi = item;
closeWifiPopup();
}
// 获取锁对应绑定的 wifi 信息
// const getLockWifi = () => {
// ttLockRequest2("/v3/wifiLock/detail", "POST", {
// "lockId": state.lockInfo.lockId,
// }).then(res => {
// wifiData.wifiInfo = res;
// });
// }
// 配置锁的 wifi
const handleConfigWifi = () => {
if (!wifiData.password) {
uni.showToast({
title: "请输入wifi密码",
icon: 'none'
});
return;
}
uni.showLoading({ title: "正在配置wifi信息" });
requirePlugin("ttPlugin", ({ configWifi, configServer }) => {
configWifi({
config: {
SSID: wifiData.selectWifi.SSID,
password: wifiData.password
},
lockData: state.lockInfo.lockData
}).then(res => {
if (res.errorCode == 0) {
// 配置服务器信息
configServer({
config: {
server: "cnwifilock.ttlock.com",
port: 4999,
},
lockData: state.lockInfo.lockData
}).then(async res => {
if (res.errorCode == 0) {
const res = await getLoginToken();
if (!res) {
uni.hideLoading();
uni.showToast({
title: "获取tt用户token失败",
icon: 'none'
});
return;
}
// 服务器配置成功
ttLockRequest2("/v3/wifiLock/updateNetwork", "POST", {
lockId: state.lockInfo.lockId, // 智能锁ID
networkName: wifiData.selectWifi.SSID, // 连接的网络名称
rssi: wifiData.selectWifi.rssi, // Wifi信号强度
useStaticIp: false, // 是否使用静态IP
}).then(result => {
console.log('---wifi上传到服务器的配置---', result);
uni.hideLoading();
if (result.errcode === 0) {
// getLockWifi();
uni.showToast({
title: "Wifi参数已上传服务器",
icon: 'none'
});
closeNetwork();
} else {
uni.showToast({
title: "Wifi参数上传服务器失败",
icon: 'none'
});
}
}).catch(err => {
console.log(err)
uni.hideLoading();
});
} else {
uni.showToast({
title: `配置服务器信息失败:${res.errorMsg}`,
icon: 'none'
});
uni.hideLoading();
}
});
} else {
uni.showToast({
title: `配置wifi信息失败${res.errorMsg}`,
icon: 'none'
});
uni.hideLoading();
}
})
});
}
</script>
<style lang="scss" scoped>
.init-lock-wrap {
position: relative;
min-height: 100vh;
background: #FFFFFF;
.search {
position: absolute;
right: 20px;
}
.lock-info {
display: flex;
flex-direction: column;
align-items: center;
.name {
margin-bottom: 80rpx;
color: #333333;
}
.lock-wrap {
display: flex;
justify-content: center;
align-items: center;
width: 400rpx;
height: 400rpx;
border-radius: 50%;
box-shadow: 0px 6px 10px rgba($color: #000000, $alpha: 0.2);
}
.tip {
margin-top: 40rpx;
}
.setting-wrap {
width: 100%;
margin-top: 60rpx;
padding-top: 60rpx;
border-top: 1px solid #f3f3ed;
.wifi {
display: flex;
align-items: center;
flex-direction: column;
width: 25%;
.text {
margin-top: 16rpx;
font-size: 24rpx;
}
}
}
}
.empty-lock {
display: flex;
flex-direction: column;
align-items: center;
margin-top: 160rpx;
.empty {
display: flex;
justify-content: center;
align-items: center;
width: 400rpx;
height: 400rpx;
border-radius: 50%;
box-shadow: 0px 6px 10px rgba($color: #000000, $alpha: 0.2);
}
.text {
margin-top: 140rpx;
color: #656265;
font-size: 28rpx;
}
}
.scan-wrap {
padding: 60rpx 20rpx 0;
height: calc(100vh - 400rpx);
.btn-wrap {
display: flex;
margin: 30rpx 0;
.btn {
padding: 20rpx;
margin-right: 30rpx;
font-size: 28rpx;
font-weight: bold;
color: var(--text-color);
background: var(--active-color);
border-radius: 16rpx;
}
}
.name {
margin: 20rpx 0;
color: #333333;
}
.item-wrap {
.item {
position: relative;
display: flex;
align-items: center;
justify-content: space-between;
height: 40px;
padding: 0 32rpx;
color: #333333;
background: #F2F4F8;
border-bottom: 1px solid #E8E8E8;
&.active {
background: #F9FBD8;
}
}
}
.empty-wrap {
display: flex;
flex-direction: column;
align-items: center;
margin-top: 200rpx;
.text {
margin-top: 10rpx;
text-align: center;
color: #b3b4b5;
font-size: 32rpx;
}
}
.loading-wrap {
margin-top: 200rpx;
}
}
.network-wrap {
height: calc(100vh - 400rpx);
background: #F6F4F7;
.nav-wrap {
display: flex;
align-items: center;
justify-content: center;
padding: 20rpx;
background: var(--main-color);
.name {
flex: 1;
text-align: center;
}
}
.tip {
padding: 20rpx 0 20rpx 20rpx;
font-size: 28rpx;
background: #F2F6Fc;
}
.input-wrap {
display: flex;
align-items: center;
justify-content: space-between;
padding: 30rpx 20rpx;
font-size: 28rpx;
background: #FFFFFF;
border-bottom: 1px solid #F2F6Fc;
.name {
flex: 1;
text-align: right;
margin-right: 20rpx;
}
:deep(input) {
text-align: right !important;
}
}
.btn {
margin: 40rpx 80rpx 0;
padding: 20rpx 0;
border-radius: 6px;
text-align: center;
background: var(--main-color);
}
}
.wifi-wrap {
height: 600rpx;
overflow: auto;
background: #FFFFFF;
.nav-wrap {
display: flex;
align-items: center;
justify-content: center;
padding: 20rpx;
border-bottom: 1px solid #F2F6Fc;
.name {
flex: 1;
text-align: center;
}
}
.loading-wrap {
margin-top: 120rpx;
}
.wifi-item {
padding: 30rpx 0 30rpx 40rpx;
border-bottom: 1px solid #F2F6Fc;
}
}
}
</style>