congming_huose-apk/pages/device/addhubwifi.vue

798 lines
18 KiB
Vue
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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="device-detail">
<view class="background"></view>
<!-- 头部导航 -->
<view class="header">
<view class="back-btn" @click="onCancel">
</view>
<view class="header-title">{{ $t('addWifiTitle') }}</view>
<view class="header-placeholder"></view>
</view>
<!-- 蓝牙连接状态 -->
<view class="bluetooth-status">
<view class="status-indicator">
<text v-if="ver_dataflag == 1" class="status-dot status-dot-disconnected"></text>
<text v-if="ver_dataflag == 2" class="status-dot status-dot-connecting"></text>
<text v-if="ver_dataflag == 3" class="status-dot status-dot-connected"></text>
<view v-if="ver_dataflag == 1" class="status-text status-text-disconnected">
{{ $t('bluetoothConnectionStatus') }}
</view>
<view v-if="ver_dataflag == 2" class="status-text status-text-connecting">
{{ $t('bluetoothStatus') }}
</view>
<view v-if="ver_dataflag == 3" class="status-text status-text-connected">
{{ $t('bluetoothStatus') }}
</view>
</view>
<view class="status-action"
:class="{
'status-action-disconnected': ver_dataflag == 1,
'status-action-connecting': ver_dataflag == 2,
'status-action-connected': ver_dataflag == 3
}"
@click="getlj"
v-if="ver_dataflag == 1">
{{ $t('clickToConnect') }}
</view>
<view class="status-action status-action-connecting" v-if="ver_dataflag == 2">
{{ $t('connecting') }}
</view>
<view class="status-action status-action-connected" v-if="ver_dataflag == 3">
{{ $t('connected') }}
</view>
</view>
<!-- 内容区域 -->
<view class="content-container">
<view class="wifi-form">
<view class="wifi-item wifi-item-border">
<view class="wifi-label">
<image class="wifi-icon" src="https://api.ccttiot.com/smartmeter/img/static/ukxdSJmKDJ8YxvaHcfEb" mode="aspectFit"></image>
{{ $t('wifiNameLabel') }}
</view>
<input class="wifi-input" type="text" v-model="wifiname" :placeholder="$t('wifiNamePlaceholder')" />
</view>
<view class="wifi-item">
<view class="wifi-label">
<image class="wifi-icon" src="https://api.ccttiot.com/smartmeter/img/static/uibUVxsJvMdNRMXi0TWw" mode="aspectFit"></image>
{{ $t('wifiPasswordLabel') }}
</view>
<input class="wifi-input" type="text" v-model="wifipwd" :placeholder="$t('wifiPasswordPlaceholder')" />
</view>
</view>
<view class="warning-text">
{{ $t('wifiFrequencyWarning') }}
</view>
</view>
<!-- 底部按钮 -->
<view class="button-container">
<view class="btn-secondary" @click="btnsh">
Позже
</view>
<view class="btn-primary" @click="btnjx">
{{ $t('completeText') }}
</view>
</view>
<!-- 蓝牙连接加载遮罩 -->
<view class="loading-overlay" v-if="tcflag">
<view class="loading-content" v-if="ver_dataflag == 2">
<view class="spinner"></view>
<text class="loading-text">{{ $t('bluetoothConnectingWait') }}</text>
</view>
<view class="error-content" v-if="ver_dataflag == 1">
<view class="error-title">
{{ $t('bluetoothConnectionFailed') }}
</view>
<view class="error-message">{{shibaitxt}}</view>
<view class="error-actions">
<view class="error-btn error-btn-cancel" @click="btnqx">
{{ $t('cancelConnection') }}
</view>
<view class="error-btn error-btn-retry" @click="getlj">
{{ $t('clickToRetry') }}
</view>
</view>
</view>
</view>
<!-- WiFi配置加载遮罩 -->
<view class="loading-overlay" v-if="wififlag">
<view class="loading-content">
<view class="spinner"></view>
<text class="loading-text">{{ $t('hubConfiguringWait') }}</text>
</view>
</view>
</view>
</template>
<script>
import i18n from '@/common/i18n/index.js';
var xBlufi = require("@/common/blufi/xBlufi.js")
export default {
data() {
return {
deviceid: '',
type: '',
ver_dataflag: 1,
devicesarr: [],
deviceid: '',
devicename: '',
mac: '',
shibaitxt:'',
tcflag:false,
shibainum:0,
wifiname:'',
wifipwd:'',
wififlag:false
}
},
computed: {
$t() {
return i18n.t;
}
},
onLoad(option) {
console.log(option);
xBlufi.initXBlufi(1)
xBlufi.notifyStartDiscoverBle({
'isStart': true
})
xBlufi.listenDeviceMsgEvent(true, this.funListenDeviceMsgEvent)
if (option.mac) {
this.mac = option.mac
this.getlj()
}
},
methods: {
// 点击稍后再说
btnsh(){
uni.reLaunch({
url:'/pages/index/index'
})
},
// 点击取消连接
btnqx(){
this.ver_dataflag = 1
this.tcflag = false
},
// 点击返回上一级
onCancel() {
uni.navigateBack({
delta: 1
})
},
// 点击继续下一步
btnjx() {
if(this.ver_dataflag == 3){
if(this.wifiname == ''){
uni.showModal({
title: this.$t('warmPrompt'),
content: this.$t('wifiNameCannotBeEmpty'),
showCancel: false,
success: function(res) {
}
})
}else if(this.wifipwd == ''){
uni.showModal({
title: this.$t('warmPrompt'),
content: this.$t('wifiPasswordCannotBeEmpty'),
showCancel: false,
success: function(res) {
}
})
}else{
this.wififlag = true
xBlufi.notifySendCustomData({
      customData: "ssid@" + this.wifiname + "pass@" + this.wifipwd
 })
}
}else{
uni.showModal({
title: this.$t('warmPrompt'),
content: this.$t('pleaseConnectDeviceFirst'),
showCancel: false,
success: function(res) {
}
})
}
},
// 进行蓝牙连接
getlj() {
console.log('蓝牙连接开始');
let that = this
const findDevice = () => {
that.ver_dataflag = 2
that.tcflag = true
const matchedDevice = that.devicesarr.find(device => {
return device.name.slice(-12) == that.mac.slice(-12)
})
if (matchedDevice) {
xBlufi.notifyStartDiscoverBle({
'isStart': false
})
xBlufi.notifyConnectBle({
isStart: true,
deviceId: matchedDevice.deviceId,
name: matchedDevice.name
})
that.deviceid = matchedDevice.deviceId
that.devicename = matchedDevice.name
setTimeout(() => {
if (that.ver_dataflag == 3) {
that.tcflag = false
uni.showToast({ title: this.$t('connectionSuccess'), icon: 'success', duration: 3000})
} else {
that.ver_dataflag = 1
that.shibainum = 0
that.getztm()
}
}, 4000)
} else {
if (that.shibainum < 7) {
that.shibainum++
that.findDeviceTimer = setTimeout(findDevice.bind(that), 1000) // 使用 bind 保持 this 上下文
} else {
that.ver_dataflag = 1
that.shibainum = 0
that.getztm()
}
}
}
findDevice()
},
// 获取蓝牙失败状态码
getztm(){
let that = this
uni.openBluetoothAdapter({
success: function (res) {
console.log('蓝牙适配器正常,但未发现设备');
that.shibaitxt = that.$t('deviceNotFoundCheck')
},
fail: function (err) {
console.error('蓝牙适配器初始化失败', err)
console.log('错误码:', err.errCode, '错误信息:', err.errMsg)
const errMsg = err.errMsg || ''
if(errMsg.includes('auth deny') || errMsg.includes('authorize')){
that.shibaitxt = that.$t('bluetoothAuthDenied')
}else if(errMsg.includes('not available') || errMsg.includes('unavailable')){
that.shibaitxt = that.$t('bluetoothUnavailable')
}else if(errMsg.includes('open fail')){
that.shibaitxt = that.$t('bluetoothOpenFailed')
}else if(errMsg.includes('already opened')){
console.log('蓝牙适配器已打开,但未找到设备')
that.shibaitxt = that.$t('deviceNotFoundCheck')
}else if(errMsg.includes('system permission denied')){
that.shibaitxt = that.$t('systemBluetoothPermissionDenied')
}else if(errMsg.includes('bluetooth service unavailable')){
that.shibaitxt = that.$t('bluetoothServiceUnavailable')
}else{
that.shibaitxt = that.$t('bluetoothInitFailed') + (err.errMsg || '未知错误,请重试')
}
}
})
},
// 获取附近蓝牙设备列表
funListenDeviceMsgEvent: function(options) {
switch (options.type) {
case xBlufi.XBLUFI_TYPE.TYPE_STATUS_CONNECTED:
if (!options.result) {
this.ver_dataflag = 1
this.tcflag = true
console.log(this.ver_dataflag, '断开断开');
}
break;
case xBlufi.XBLUFI_TYPE.TYPE_GET_DEVICE_LISTS:
if (options.result) {
this.devicesarr = options.data
}
break;
case xBlufi.XBLUFI_TYPE.TYPE_CONNECTED:
console.log("连接回调:" + JSON.stringify(options))
if (options.result == true) {
setTimeout(() => {
this.ver_dataflag = 3
}, 2000)
xBlufi.notifyInitBleEsp32({
deviceId: this.deviceid
})
this.deviceid = options.data.deviceId
} else {
this.ver_dataflag = 1
}
break;
case xBlufi.XBLUFI_TYPE.TYPE_RECIEVE_CUSTON_DATA:
console.log("1收到设备发来的自定义数据结果", options.data)
break;
case xBlufi.XBLUFI_TYPE.TYPE_CONNECT_ROUTER_RESULT:
uni.hideLoading();
if (!options.result) {
this.wififlag = false
let that = this;
uni.showModal({
title: this.$t('warmPrompt'),
content: this.$t('configFailedCheckWifi'),
showCancel: false,
success: function(res) {
}
})
} else {
if (options.data.progress == 100) {
this.wififlag = false
let that = this
uni.showModal({
title: this.$t('warmPrompt'),
content: this.$t('wifiConfigSuccess').replace('{wifiName}', that.wifiname),
showCancel: false,
success: function(res) {
uni.reLaunch({
url: '/pages/index/index'
})
}
})
}
}
break;
case xBlufi.XBLUFI_TYPE.TYPE_GET_DEVICE_LISTS_START:
if (!options.result) {
console.log('蓝牙未开启')
this.tcflag = true
this.getztm()
return
}
break;
}
},
}
}
</script>
<style scoped lang="less">
// 颜色变量
@primary-color: #0F0F0F;
@success-color: #61E24F;
@warning-color: #FF4444;
@info-color: #2788F7;
@bg-color: #F3F5F6;
@text-primary: #333333;
@text-secondary: #727272;
@text-tertiary: #7F7F7F;
@border-color: #D8D8D8;
@border-light: #E5E5E5;
@white: #FFFFFF;
@shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.08);
// 基础布局
.device-detail {
min-height: 100vh;
position: relative;
background-color: @bg-color;
}
.background {
width: 100%;
height: 50vh;
position: fixed;
top: 0;
left: 0;
z-index: 0;
background: linear-gradient(180deg, @primary-color 0%, @primary-color 100%);
}
// 头部导航
.header {
display: flex;
align-items: center;
justify-content: space-between;
width: 100%;
height: 126rpx;
padding: 0 40rpx;
box-sizing: border-box;
background-color: @white;
margin-top: 102rpx;
border-radius: 30rpx 30rpx 0 0;
position: relative;
z-index: 1;
box-shadow: @shadow;
.back-btn {
font-size: 36rpx;
font-weight: 600;
color: @text-primary;
cursor: pointer;
transition: opacity 0.3s;
width: 60rpx;
height: 60rpx;
display: flex;
align-items: center;
justify-content: center;
&:active {
opacity: 0.6;
}
}
.header-title {
font-size: 36rpx;
font-weight: 600;
color: @text-primary;
flex: 1;
text-align: center;
}
.header-placeholder {
width: 60rpx;
}
}
// 蓝牙连接状态
.bluetooth-status {
width: 100%;
background-color: @white;
display: flex;
align-items: center;
justify-content: space-between;
padding: 30rpx 40rpx;
box-sizing: border-box;
position: relative;
z-index: 1;
.status-indicator {
display: flex;
align-items: center;
font-size: 32rpx;
font-weight: 600;
margin-right: 20rpx;
flex: 1;
.status-dot {
display: block;
width: 16rpx;
height: 16rpx;
border-radius: 50%;
margin-right: 20rpx;
flex-shrink: 0;
&.status-dot-disconnected {
background-color: @warning-color;
}
&.status-dot-connecting {
background-color: @info-color;
animation: pulse 2s ease-in-out infinite;
}
&.status-dot-connected {
background-color: @success-color;
}
}
.status-text {
font-size: 32rpx;
font-weight: 600;
&.status-text-disconnected {
color: @warning-color;
}
&.status-text-connecting {
color: @info-color;
}
&.status-text-connected {
color: @success-color;
}
}
}
.status-action {
font-size: 28rpx;
padding: 16rpx 32rpx;
border-radius: 12rpx;
transition: all 0.3s;
flex-shrink: 0;
cursor: pointer;
&.status-action-disconnected {
color: @warning-color;
border: 2rpx solid @warning-color;
background-color: transparent;
&:active {
background-color: rgba(255, 68, 68, 0.1);
}
}
&.status-action-connecting {
color: @info-color;
border: 2rpx solid @info-color;
background-color: rgba(39, 136, 247, 0.1);
}
&.status-action-connected {
color: @success-color;
border: 2rpx solid @success-color;
background-color: rgba(97, 226, 79, 0.1);
}
}
}
// 内容区域
.content-container {
display: flex;
flex-direction: column;
min-height: calc(100vh - 400rpx);
background-color: @bg-color;
padding-bottom: 200rpx;
position: relative;
z-index: 1;
}
.wifi-form {
width: 100%;
background: @white;
border-radius: 0;
margin-top: 34rpx;
box-shadow: @shadow;
.wifi-item {
padding: 40rpx 38rpx;
box-sizing: border-box;
&.wifi-item-border {
border-bottom: 2rpx solid @border-light;
}
.wifi-label {
display: flex;
align-items: center;
font-size: 28rpx;
color: @text-tertiary;
margin-bottom: 24rpx;
.wifi-icon {
width: 36rpx;
height: 36rpx;
margin-right: 16rpx;
}
}
.wifi-input {
width: 100%;
font-size: 32rpx;
color: @text-primary;
border: none;
border-bottom: 2rpx solid @border-light;
padding: 16rpx 0;
background: transparent;
transition: border-color 0.3s;
&::placeholder {
color: #B9B9B9;
}
&:focus {
border-bottom-color: @primary-color;
outline: none;
}
}
}
}
.warning-text {
width: 100%;
text-align: center;
font-size: 28rpx;
color: @text-secondary;
margin-top: 68rpx;
padding: 0 70rpx;
box-sizing: border-box;
line-height: 1.6;
}
// 底部按钮
.button-container {
width: 100%;
height: 126rpx;
padding: 24rpx 40rpx 40rpx;
box-sizing: border-box;
background: @white;
border-radius: 0;
position: fixed;
left: 0;
bottom: 0;
z-index: 10;
display: flex;
justify-content: space-between;
align-items: center;
gap: 20rpx;
box-shadow: 0 -4rpx 20rpx rgba(0, 0, 0, 0.05);
.btn-secondary {
width: 200rpx;
height: 80rpx;
line-height: 80rpx;
text-align: center;
border: 2rpx solid @primary-color;
background-color: transparent;
color: @primary-color;
font-size: 32rpx;
border-radius: 50rpx;
transition: all 0.3s;
cursor: pointer;
&:active {
background-color: rgba(15, 15, 15, 0.1);
}
}
.btn-primary {
flex: 1;
height: 80rpx;
line-height: 80rpx;
text-align: center;
background: @primary-color;
color: @white;
font-size: 32rpx;
font-weight: 500;
border-radius: 50rpx;
transition: all 0.3s;
cursor: pointer;
box-shadow: 0 4rpx 12rpx rgba(15, 15, 15, 0.2);
&:active {
opacity: 0.8;
transform: scale(0.98);
}
}
}
// 加载遮罩层
.loading-overlay {
position: fixed;
bottom: 0;
left: 50%;
transform: translateX(-50%);
width: 100%;
max-width: 750rpx;
min-height: 400rpx;
background: @white;
border-radius: 30rpx 30rpx 0 0;
z-index: 1000;
display: flex;
flex-direction: column;
align-items: center;
padding: 100rpx 40rpx 60rpx;
box-sizing: border-box;
box-shadow: 0 -4rpx 30rpx rgba(0, 0, 0, 0.15);
animation: slideUp 0.3s ease-out;
.loading-content {
display: flex;
flex-direction: column;
align-items: center;
width: 100%;
}
.error-content {
display: flex;
flex-direction: column;
align-items: center;
width: 100%;
.error-title {
font-size: 36rpx;
font-weight: 600;
margin-bottom: 40rpx;
color: @warning-color;
text-align: center;
}
.error-message {
font-size: 28rpx;
color: @text-secondary;
display: block;
width: 100%;
padding: 0 30rpx;
text-align: center;
line-height: 1.6;
margin-bottom: 60rpx;
}
.error-actions {
display: flex;
align-items: center;
justify-content: space-between;
width: 100%;
gap: 20rpx;
.error-btn {
flex: 1;
height: 80rpx;
line-height: 80rpx;
text-align: center;
border-radius: 12rpx;
font-size: 30rpx;
transition: all 0.3s;
cursor: pointer;
&.error-btn-cancel {
border: 2rpx solid #8d8d8d;
color: @text-secondary;
background-color: transparent;
&:active {
background-color: rgba(0, 0, 0, 0.05);
}
}
&.error-btn-retry {
background-color: @primary-color;
color: @white;
border: 2rpx solid @primary-color;
&:active {
opacity: 0.8;
}
}
}
}
}
}
// 加载动画
.spinner {
width: 56rpx;
height: 56rpx;
border-radius: 50%;
border: 6rpx solid #E0E0E0;
border-top-color: @success-color;
animation: spin 1s linear infinite;
margin-bottom: 30rpx;
}
.loading-text {
font-size: 28rpx;
color: @text-secondary;
text-align: center;
line-height: 1.6;
}
// 动画
@keyframes spin {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}
@keyframes pulse {
0%, 100% {
opacity: 1;
transform: scale(1);
}
50% {
opacity: 0.6;
transform: scale(1.1);
}
}
@keyframes slideUp {
from {
transform: translateX(-50%) translateY(100%);
}
to {
transform: translateX(-50%) translateY(0);
}
}
</style>