- uni-app x官方无内置蓝牙API(文档明确见插件市场),为实现去插件化,工程整体迁移为uni-app vue3(vue/ts),App端内置完整BLE API,标准基座即可运行 - 新建src/BluetoothManager.ts:基于uni内置BLE API(createBLEConnection/notifyBLECharacteristicValueChange/writeBLECharacteristicValue(writeNoResponse)/setBLEMTU/getBLEDeviceRSSI等),保持原插件type约定,页面业务逻辑不变 - 删除uni_modules/android-ble及未使用的lime-*、uni-wifi;保留uni-icons、uni-scss - uts模块全部移植为ts(src/mcm、api、common):UTSJSONObject改Record<string,any>,适配vue3 getStorageSync空值返回空串的差异 - 7个页面uvue转vue:模板不变,显式import ref与生命周期钩子;OTA固件读取由java.io改为plus.io+uni.base64ToArrayBuffer - manifest.json去uni-app-x改app-plus结构,补Android 12+蓝牙权限;删除platformConfig.json;运行改用标准基座
346 lines
7.2 KiB
Vue
346 lines
7.2 KiB
Vue
<template>
|
|
<!-- #ifdef APP -->
|
|
<scroll-view style="flex:1">
|
|
<!-- #endif -->
|
|
|
|
<view class="page">
|
|
<view v-if="registerFlag == false">
|
|
<view class="textType">
|
|
<text >email</text>
|
|
</view>
|
|
<view class="input-wrapper">
|
|
<uni-icons type="person" size="20" class="input-icon" />
|
|
<input
|
|
class="inputType"
|
|
type="text"
|
|
v-model="account"
|
|
prefixIcon="person"
|
|
placeholder="email"
|
|
/>
|
|
</view>
|
|
<view class="textType">
|
|
<text >pinCode</text>
|
|
</view>
|
|
<view class="input-wrapper">
|
|
<uni-icons type="starhalf" size="20" class="input-icon" />
|
|
<input
|
|
class="inputType"
|
|
type="text"
|
|
v-model="pinCode"
|
|
prefixIcon="person"
|
|
placeholder="pinCode"
|
|
/>
|
|
<view
|
|
class="code-btn"
|
|
:class="{ disabled: isCountingDown }"
|
|
@tap="onGetCodeTap"
|
|
>
|
|
<text class="textCode">{{ isCountingDown ? `${countdown}s` : "get code" }}</text>
|
|
</view>
|
|
</view>
|
|
<view class="buttonType">
|
|
<button class="buttonSize" @tap="onNextStep">next</button>
|
|
</view>
|
|
</view>
|
|
<view v-else>
|
|
<view class="textType">
|
|
<text>password</text>
|
|
</view>
|
|
<view class="input-wrapper">
|
|
<uni-icons type="locked" size="20" class="input-icon" />
|
|
<input
|
|
class="inputType"
|
|
type="text"
|
|
:password="!showPwd"
|
|
v-model="password"
|
|
placeholder="password"
|
|
/>
|
|
<!-- 右侧“眼睛”按钮 -->
|
|
<view class="toggle" @tap="showPwd = !showPwd">
|
|
<uni-icons :type="showPwd ? 'eye' : 'eye-filled'" size="20" />
|
|
</view>
|
|
</view>
|
|
|
|
<view class="textType">
|
|
<text>enter the password again</text>
|
|
</view>
|
|
<view class="input-wrapper">
|
|
<uni-icons type="locked" size="20" class="input-icon" />
|
|
<input
|
|
class="inputType"
|
|
type="text"
|
|
:password="!showAgainPwd"
|
|
v-model="passwordAgain"
|
|
placeholder="password"
|
|
/>
|
|
<!-- 右侧“眼睛”按钮 -->
|
|
<view class="toggle" @tap="showAgainPwd = !showAgainPwd">
|
|
<uni-icons :type="showAgainPwd ? 'eye' : 'eye-filled'" size="20" />
|
|
</view>
|
|
</view>
|
|
<view class="textTip">
|
|
<text class="textSize">tips: The password should be at least six characters long and include both uppercase and lowercase letters as well as numbers.</text>
|
|
</view>
|
|
|
|
<view class="buttonType">
|
|
<button class="buttonSize" @tap="GetRegisteredUserReq">register</button>
|
|
</view>
|
|
</view>
|
|
</view>
|
|
<!-- #ifdef APP -->
|
|
</scroll-view>
|
|
<!-- #endif -->
|
|
|
|
</template>
|
|
|
|
<script setup lang="ts">
|
|
import { ref } from 'vue'
|
|
import { RegisteredUserReqApi, GetPindCodeReqApi } from '../../api/requestApi'
|
|
|
|
const showPwd = ref(false)
|
|
const showAgainPwd = ref(false)
|
|
|
|
const account = ref("")
|
|
const password = ref("")
|
|
const passwordAgain = ref("")
|
|
const pinCode = ref("")
|
|
const resPinCode = ref("")
|
|
const registerFlag = ref(false)
|
|
|
|
async function GetpindCode() {
|
|
|
|
try {
|
|
const res = await GetPindCodeReqApi({
|
|
userName: account.value,
|
|
userPassword: password.value,
|
|
rndcode: "123"
|
|
})
|
|
// console.log("res = ", res)
|
|
const statusCode = res["statusCode"] as number
|
|
const data = res["data"] as Record<string, any>
|
|
if(statusCode == 200) {
|
|
resPinCode.value = (data["pinCode"] as string) ?? ""
|
|
uni.showToast({
|
|
title: "send pinCode success",
|
|
icon: 'success',
|
|
duration: 2000
|
|
})
|
|
} else {
|
|
const msg = (data["result"] as string | null) ?? "The user has registered"
|
|
uni.showToast({
|
|
title: msg + ": " + statusCode,
|
|
icon: 'error',
|
|
duration: 2000
|
|
})
|
|
}
|
|
} catch (e) {
|
|
|
|
} finally {
|
|
|
|
}
|
|
}
|
|
|
|
async function GetRegisteredUserReq(){
|
|
if(password.value != passwordAgain.value) {
|
|
uni.showToast({
|
|
title: "The password is inconsistent",
|
|
icon: 'error',
|
|
duration: 2000
|
|
})
|
|
return
|
|
}
|
|
try {
|
|
const res = await RegisteredUserReqApi({
|
|
userName: account.value,
|
|
userPassword: password.value,
|
|
rndcode: "123"
|
|
})
|
|
const statusCode = res["statusCode"] as number
|
|
const data = res["data"] as Record<string, any>
|
|
// console.log("res = ", res)
|
|
if(statusCode == 200) {
|
|
const msg = (data["result"] as string | null) ?? "Registration successful"
|
|
uni.showToast({
|
|
title: msg,
|
|
icon: "success",
|
|
duration: 2000,
|
|
mask: true
|
|
})
|
|
uni.redirectTo({
|
|
url: "/pages/login/login"
|
|
})
|
|
} else{
|
|
const msg = (data["result"] as string | null) ?? "The user has registered"
|
|
uni.showToast({
|
|
title: msg + ": " + statusCode,
|
|
icon: 'error',
|
|
duration: 2000
|
|
})
|
|
}
|
|
} catch (e) {
|
|
|
|
} finally {
|
|
|
|
}
|
|
|
|
}
|
|
|
|
function onNextStep() {
|
|
console.log("pinCode.value = ", pinCode.value)
|
|
console.log("resPinCode.value = ", resPinCode.value)
|
|
if (pinCode.value.length == 0 || pinCode.value != resPinCode.value) {
|
|
uni.showToast({
|
|
title: "pindCode error",
|
|
icon: 'error',
|
|
duration: 2000
|
|
})
|
|
return
|
|
}
|
|
registerFlag.value = true
|
|
}
|
|
|
|
|
|
const isCountingDown = ref(false)
|
|
const countdown = ref(0)
|
|
let countdownTimer: number = 0
|
|
|
|
function startCountdown(sec: number) {
|
|
// 防止重复启动
|
|
if (countdownTimer != 0) {
|
|
clearInterval(countdownTimer)
|
|
countdownTimer = 0
|
|
}
|
|
|
|
isCountingDown.value = true
|
|
countdown.value = sec
|
|
|
|
countdownTimer = setInterval(() => {
|
|
countdown.value--
|
|
if (countdown.value <= 0) {
|
|
clearInterval(countdownTimer)
|
|
countdownTimer = 0
|
|
isCountingDown.value = false
|
|
countdown.value = 0
|
|
}
|
|
}, 1000)
|
|
}
|
|
|
|
async function onGetCodeTap() {
|
|
// 倒计时期间不允许再点
|
|
if (isCountingDown.value) return
|
|
if(account.value.length == 0) {
|
|
uni.showToast({
|
|
title: "please enter email",
|
|
icon: 'error',
|
|
duration: 2000
|
|
})
|
|
return
|
|
}
|
|
// 先调你的接口
|
|
await GetpindCode()
|
|
startCountdown(60) // 60秒倒计时
|
|
}
|
|
|
|
</script>
|
|
|
|
<style scoped>
|
|
.page{
|
|
flex: 1;
|
|
display: flex;
|
|
flex-direction: column;
|
|
padding: 20rpx;
|
|
}
|
|
|
|
.buttonType {
|
|
width: 100%;
|
|
display: flex;
|
|
align-items: center;
|
|
justify-content: center;
|
|
margin-top: 20rpx;
|
|
}
|
|
|
|
.buttonSize {
|
|
width: 90%;
|
|
border-radius: 10rpx;
|
|
background-color: #6d91b3;
|
|
}
|
|
|
|
.buttonBack {
|
|
width: 80%;
|
|
border-radius: 10rpx;
|
|
background-color: #eee;
|
|
}
|
|
|
|
.input-wrapper {
|
|
position: relative;
|
|
width: 90%;
|
|
height: 80rpx;
|
|
margin: 20rpx auto;
|
|
}
|
|
|
|
.inputType {
|
|
width: 100%;
|
|
height: 100%;
|
|
box-sizing: border-box;
|
|
padding-left: 80rpx; /* 给左边图标腾位置 */
|
|
padding-right: 230rpx;
|
|
border: 2rpx solid #ddd;
|
|
border-radius: 12rpx;
|
|
font-size: 28rpx;
|
|
}
|
|
|
|
.input-icon {
|
|
position: absolute;
|
|
left: 20rpx;
|
|
top: 50%;
|
|
transform: translateY(-50%);
|
|
}
|
|
|
|
.toggle {
|
|
position: absolute;
|
|
right: 20rpx;
|
|
top: 50%;
|
|
transform: translateY(-50%);
|
|
}
|
|
|
|
.textType {
|
|
width: 600rpx;
|
|
margin: 10rpx auto 0;
|
|
}
|
|
|
|
.textTip {
|
|
width: 90%;
|
|
margin: 20rpx auto;
|
|
}
|
|
.textSize {
|
|
font-size: 32rpx;
|
|
color: #6d91b3;
|
|
}
|
|
|
|
.code-btn {
|
|
display: flex;
|
|
transform: none;
|
|
position: absolute;
|
|
right: 5rpx;
|
|
top: 5rpx;
|
|
background-color: #f8f8f8;
|
|
height: 70rpx;
|
|
width: 200rpx;
|
|
align-items: center;
|
|
justify-content: center;
|
|
border-radius: 12rpx;
|
|
}
|
|
|
|
.code-btn.disabled {
|
|
opacity: 0.5;
|
|
pointer-events: none;
|
|
}
|
|
|
|
.textCode {
|
|
font-size: 32rpx;
|
|
color: #6d91b3;
|
|
line-height: 1.4;
|
|
}
|
|
|
|
</style>
|