4278 lines
132 KiB
Vue
4278 lines
132 KiB
Vue
<template>
|
||
<view class="page">
|
||
<u-navbar :is-back="false" :border-bottom="false" :background="bgc" title-color='#2E4975' title-size='36'
|
||
height='8'></u-navbar>
|
||
<view class="title" style="display: flex;padding: 0;align-items: center;font-size: 48rpx;font-weight: 600;color: #48893B;">
|
||
<image style="width: 60rpx;margin-right: 10rpx;height: 50rpx;" src="https://api.ccttiot.com/smartmeter/img/static/u6wBJM2Dah2jLVGeSrKL" mode="aspectFit"></image> {{ $t('app.name') }}
|
||
</view>
|
||
<!-- 加载状态遮罩层 -->
|
||
<view class="loading-mask" v-if="isLoading">
|
||
<view class="loading-content">
|
||
<u-loading mode="circle" size="60" color="#7FAD76"></u-loading>
|
||
<text class="loading-text">{{ $t('index.loading') }}</text>
|
||
</view>
|
||
</view>
|
||
<!-- 背景图 -->
|
||
<!-- 没设备时展示 -->
|
||
<image class="bjimg" v-if="bjflag" src="https://api.ccttiot.com/smartmeter/img/static/uz8MR6BuZW2qRANnYgLu"
|
||
mode=""></image>
|
||
<!-- 有设备时展示 -->
|
||
<image class="bjimg" v-else src="https://api.ccttiot.com/smartmeter/img/static/uXBpJuaUs0XUNb8HTahh" mode="">
|
||
</image>
|
||
<view class="wusb" v-if="bjflag">
|
||
<text>{{ $t('index.noDevice') }}</text>
|
||
<view class="bang" @click="btnaddsb">
|
||
{{ $t('index.bind') }}
|
||
</view>
|
||
</view>
|
||
<view class="" v-else>
|
||
<view class="selectbox">
|
||
<view class="shebeiname" >
|
||
<view class="" @click="btnksxz">
|
||
{{user.deviceName == undefined ? '--' : user.deviceName}}
|
||
<image style="width: 32rpx;height: 26rpx;margin-left: 14rpx;" v-if="!xuanzeflag" src="https://api.ccttiot.com/smartmeter/img/static/uwHOBxvbJjkhx1uDiQHI" mode=""></image>
|
||
<image style="width: 32rpx;height: 26rpx;margin-left: 14rpx;" v-else class="rotated-image" src="https://api.ccttiot.com/smartmeter/img/static/uwHOBxvbJjkhx1uDiQHI" mode=""></image>
|
||
</view>
|
||
<view style="margin-left: 10rpx;" class="lj" @click="btnlj" v-if="vardataflag == 1">
|
||
{{ $t('index.bleDirect') }}
|
||
</view>
|
||
<view class="lj" v-if="vardataflag == 2">
|
||
{{ $t('index.connecting') }}
|
||
</view>
|
||
<view class="lj" style="color: #7FAD76;border: 1px solid #7FAD76;" v-if="vardataflag == 3" @click="btnduankai">
|
||
{{ $t('index.connected') }}
|
||
</view>
|
||
</view>
|
||
<view class="selectshezhi" style="display: flex;">
|
||
<view class="gateway-item" @click="goToGatewayList" style="position: relative;padding: 0 20rpx;">
|
||
<image class="gateway-icon-circle" src="https://api.ccttiot.com/ad93702b02429b8adf394569460b8b2a-1758263546164.png" mode="aspectFill"></image>
|
||
<view class="gateway-content" style="display: flex;align-items: center;">
|
||
</view>
|
||
</view>
|
||
<image style="margin-left: 10rpx;" @click="btnsz" src="https://api.ccttiot.com/smartmeter/img/static/uvCJ8ro0MpfGZd53vypJ" mode=""></image>
|
||
</view>
|
||
</view>
|
||
<view v-if=" ver != '' && ver.slice(3) != version" @click="btnsz" class="" style="width: 100%;height: 76rpx;margin-top: 20rpx;">
|
||
<image style="width: 100%;height: 76rpx;" src="https://api.ccttiot.com/smartmeter/img/static/uFqC94yHg1YXw1GoqTsL" mode=""></image>
|
||
</view>
|
||
<!-- 设备信息 -->
|
||
<view class="devicebox">
|
||
<view class="devicetop">
|
||
<view class="devicelt" @click="btnsz">
|
||
<image :src="imgpic" mode="aspectFit"></image>
|
||
<view class="xinghao">
|
||
<view class="one" style="display: flex;align-items: center;">
|
||
{{ $t('common.model') }}:{{user.model == undefined ? '--' : user.model}}
|
||
<view class="" style="display: flex;align-items: center;margin-left: 20rpx;" v-if="vardataflag != 3">
|
||
<view class="signal-bars" style="display: flex;align-items: flex-end;">
|
||
<view class="signal-bar" :class="{'active': signalStrength >= 1}" style="width: 6rpx;height: 10rpx;background-color: #E0E0E0;margin-right: 3rpx;border-radius: 1rpx;"></view>
|
||
<view class="signal-bar" :class="{'active': signalStrength >= 2}" style="width: 6rpx;height: 14rpx;background-color: #E0E0E0;margin-right: 3rpx;border-radius: 1rpx;"></view>
|
||
<view class="signal-bar" :class="{'active': signalStrength >= 3}" style="width: 6rpx;height: 18rpx;background-color: #E0E0E0;margin-right: 3rpx;border-radius: 1rpx;"></view>
|
||
<view class="signal-bar" :class="{'active': signalStrength >= 4}" style="width: 6rpx;height: 22rpx;background-color: #E0E0E0;border-radius: 1rpx;"></view>
|
||
</view>
|
||
</view>
|
||
<view class="battery-wrap" style="font-weight: 400;font-size: 26rpx;margin-left: 20rpx;display: flex;align-items: center;">
|
||
<view class="battery-box">
|
||
<view class="battery-fill" :style="{ width: (batteryPercent != null ? (Number(batteryPercent) < 100 ? Number(batteryPercent) + 10 : 100) : 0) + '%', backgroundColor: batteryColor }"></view>
|
||
<!-- <view class="battery-text">{{ batteryPercent != null ? batteryPercent + '%' : '--' }}</view> -->
|
||
</view>
|
||
<view class="battery-nub"></view>
|
||
</view>
|
||
</view>
|
||
<view class="">{{ $t('common.mac') }}:{{user.mac == undefined ? '--' : user.mac}}</view>
|
||
</view>
|
||
</view>
|
||
<view v-if="vardataflag != 3" class="refresh-btn" :class="{ 'is-refreshing': isRefreshing }" @click="onRefreshClick">
|
||
<text class="refresh-icon">{{ isRefreshing ? '⋯' : '↻' }}</text>
|
||
<text class="refresh-text">{{ isRefreshing ? $t('index.refreshing') : $t('index.refresh') }}</text>
|
||
</view>
|
||
</view>
|
||
<!-- SATER:双通道定时(A/B)斜跨样式(梯形tab) -->
|
||
<view class="channel-switch" v-if="pre === 'SATER'">
|
||
<view class="channel-bg channel-bg-a" :class="{ active: stareChannel === 'A' }"></view>
|
||
<view class="channel-bg channel-bg-b" :class="{ active: stareChannel === 'B' }"></view>
|
||
<view class="channel-text channel-text-a" :class="{ active: stareChannel === 'A' }" @click.stop="setStareChannel('A')">
|
||
<text>A</text>
|
||
<view class="channel-indicator" v-if="stareChannel === 'A'"></view>
|
||
</view>
|
||
<view class="channel-text channel-text-b" :class="{ active: stareChannel === 'B' }" @click.stop="setStareChannel('B')">
|
||
<text>B</text>
|
||
<view class="channel-indicator" v-if="stareChannel === 'B'"></view>
|
||
</view>
|
||
</view>
|
||
<view class="deviceweek" v-if="['WATER', 'DATER', 'TATER', 'SATER'].includes(pre)" @click="btntime">
|
||
<view class="weeklist">
|
||
<view class="" v-if="!ver_data.p_set1 || ver_data.p_set1[3] == 0">
|
||
<view class="kg" style="color: rgba(0, 0, 0, .3);" v-if="!ver_data.p_set1 || ver_data.p_set1[3] == 0">
|
||
OFF
|
||
</view>
|
||
<view class="weekday" style="color: rgba(0,0,0,.3);">
|
||
P1
|
||
</view>
|
||
<view class="time" style="color: rgba(0,0,0,.3);">
|
||
{{ver_data.p_set1 && ver_data.p_set1[0] !== undefined ? ver_data.p_set1[0].toString().padStart(2, '0') + ':' + ver_data.p_set1[1].toString().padStart(2, '0') : '--:--'}}
|
||
</view>
|
||
<view class="interval" style="color: rgba(0,0,0,.3);" v-if="ver_data.p_set1 && ver_data.p_set1[4] > 1">
|
||
{{ $t('index.everyNdays', { n: ver_data.p_set1[4] }) }}
|
||
</view>
|
||
<view style="color: rgba(0,0,0,.3);" class="interval" v-else>
|
||
{{ $t('index.daily') }}
|
||
</view>
|
||
</view>
|
||
<view class="" v-else>
|
||
<view class="kg" style="color: #7FAD76;">
|
||
ON
|
||
</view>
|
||
<view class="weekday">
|
||
P1
|
||
</view>
|
||
<view class="time">
|
||
{{ver_data.p_set1 && ver_data.p_set1[0] !== undefined ? ver_data.p_set1[0].toString().padStart(2, '0') + ':' + ver_data.p_set1[1].toString().padStart(2, '0') : '--:--'}}
|
||
</view>
|
||
<view class="interval" v-if="ver_data.p_set1 && ver_data.p_set1[4] > 1">
|
||
{{ $t('index.everyNdays', { n: ver_data.p_set1[4] }) }}
|
||
</view>
|
||
<view class="interval" v-else>
|
||
{{ $t('index.daily') }}
|
||
</view>
|
||
</view>
|
||
<view class="img">
|
||
<image v-if="shouldShowIcon(1)"
|
||
src="https://api.ccttiot.com/smartmeter/img/static/u9iZpd6bW6bUsUay4uvH" mode="">
|
||
</image>
|
||
</view>
|
||
</view>
|
||
<view class="weeklist">
|
||
<view class="" v-if="!ver_data.p_set2 || ver_data.p_set2[3] == 0">
|
||
<view class="kg" style="color: rgba(0, 0, 0, .3);" v-if="!ver_data.p_set2 || ver_data.p_set2[3] == 0">
|
||
OFF
|
||
</view>
|
||
<view class="weekday" style="color: rgba(0,0,0,.3);">
|
||
P2
|
||
</view>
|
||
<view class="time" style="color: rgba(0,0,0,.3);">
|
||
{{ver_data.p_set2 && ver_data.p_set2[0] !== undefined ? ver_data.p_set2[0].toString().padStart(2, '0') + ':' + ver_data.p_set2[1].toString().padStart(2, '0') : '--:--'}}
|
||
</view>
|
||
<view class="interval" style="color: rgba(0,0,0,.3);" v-if="ver_data.p_set2 && ver_data.p_set2[4] > 1">
|
||
{{ $t('index.everyNdays', { n: ver_data.p_set2[4] }) }}
|
||
</view>
|
||
<view style="color: rgba(0,0,0,.3);" class="interval" v-else>
|
||
{{ $t('index.daily') }}
|
||
</view>
|
||
</view>
|
||
<view class="" v-else>
|
||
<view class="kg" style="color: #7FAD76;">
|
||
ON
|
||
</view>
|
||
<view class="weekday">
|
||
P2
|
||
</view>
|
||
<view class="time">
|
||
{{ver_data.p_set2 && ver_data.p_set2[0] !== undefined ? ver_data.p_set2[0].toString().padStart(2, '0') + ':' + ver_data.p_set2[1].toString().padStart(2, '0') : '--:--'}}
|
||
</view>
|
||
<view class="interval" v-if="ver_data.p_set2 && ver_data.p_set2[4] > 1">
|
||
{{ $t('index.everyNdays', { n: ver_data.p_set2[4] }) }}
|
||
</view>
|
||
<view class="interval" v-else>
|
||
{{ $t('index.daily') }}
|
||
</view>
|
||
</view>
|
||
<view class="img">
|
||
<image v-if="shouldShowIcon(2)"
|
||
src="https://api.ccttiot.com/smartmeter/img/static/u9iZpd6bW6bUsUay4uvH" mode="">
|
||
</image>
|
||
</view>
|
||
</view>
|
||
<view class="weeklist">
|
||
<view class="" v-if="!ver_data.p_set3 || ver_data.p_set3[3] == 0">
|
||
<view class="kg" style="color: rgba(0, 0, 0, .3);" v-if="!ver_data.p_set3 || ver_data.p_set3[3] == 0">
|
||
OFF
|
||
</view>
|
||
<view class="weekday" style="color: rgba(0,0,0,.3);">
|
||
P3
|
||
</view>
|
||
<view class="time" style="color: rgba(0,0,0,.3);">
|
||
{{ver_data.p_set3 && ver_data.p_set3[0] !== undefined ? ver_data.p_set3[0].toString().padStart(2, '0') + ':' + ver_data.p_set3[1].toString().padStart(2, '0') : '--:--'}}
|
||
</view>
|
||
<view class="interval" style="color: rgba(0,0,0,.3);" v-if="ver_data.p_set3 && ver_data.p_set3[4] > 1">
|
||
{{ $t('index.everyNdays', { n: ver_data.p_set3[4] }) }}
|
||
</view>
|
||
<view style="color: rgba(0,0,0,.3);" class="interval" v-else>
|
||
{{ $t('index.daily') }}
|
||
</view>
|
||
</view>
|
||
<view class="" v-else>
|
||
<view class="kg" style="color: #7FAD76;">
|
||
ON
|
||
</view>
|
||
<view class="weekday">
|
||
P3
|
||
</view>
|
||
<view class="time">
|
||
{{ver_data.p_set3 && ver_data.p_set3[0] !== undefined ? ver_data.p_set3[0].toString().padStart(2, '0') + ':' + ver_data.p_set3[1].toString().padStart(2, '0') : '--:--'}}
|
||
</view>
|
||
<view class="interval" v-if="ver_data.p_set3 && ver_data.p_set3[4] > 1">
|
||
{{ $t('index.everyNdays', { n: ver_data.p_set3[4] }) }}
|
||
</view>
|
||
<view class="interval" v-else>
|
||
{{ $t('index.daily') }}
|
||
</view>
|
||
</view>
|
||
<view class="img">
|
||
<image v-if="shouldShowIcon(3)"
|
||
src="https://api.ccttiot.com/smartmeter/img/static/u9iZpd6bW6bUsUay4uvH" mode="">
|
||
</image>
|
||
</view>
|
||
</view>
|
||
<view class="weeklist">
|
||
<view class="" v-if="!ver_data.p_set4 || ver_data.p_set4[3] == 0">
|
||
<view class="kg" style="color: rgba(0, 0, 0, .3);" v-if="!ver_data.p_set4 || ver_data.p_set4[3] == 0">
|
||
OFF
|
||
</view>
|
||
<view class="weekday" style="color: rgba(0,0,0,.3);">
|
||
P4
|
||
</view>
|
||
<view class="time" style="color: rgba(0,0,0,.3);">
|
||
{{ver_data.p_set4 && ver_data.p_set4[0] !== undefined ? ver_data.p_set4[0].toString().padStart(2, '0') + ':' + ver_data.p_set4[1].toString().padStart(2, '0') : '--:--'}}
|
||
</view>
|
||
<view class="interval" style="color: rgba(0,0,0,.3);" v-if="ver_data.p_set4 && ver_data.p_set4[4] > 1">
|
||
{{ $t('index.everyNdays', { n: ver_data.p_set4[4] }) }}
|
||
</view>
|
||
<view style="color: rgba(0,0,0,.3);" class="interval" v-else>
|
||
{{ $t('index.daily') }}
|
||
</view>
|
||
</view>
|
||
<view class="" v-else>
|
||
<view class="kg" style="color: #7FAD76;">
|
||
ON
|
||
</view>
|
||
<view class="weekday">
|
||
P4
|
||
</view>
|
||
<view class="time">
|
||
{{ver_data.p_set4 && ver_data.p_set4[0] !== undefined ? ver_data.p_set4[0].toString().padStart(2, '0') + ':' + ver_data.p_set4[1].toString().padStart(2, '0') : '--:--'}}
|
||
</view>
|
||
<view class="interval" v-if="ver_data.p_set4 && ver_data.p_set4[4] > 1">
|
||
{{ $t('index.everyNdays', { n: ver_data.p_set4[4] }) }}
|
||
</view>
|
||
<view class="interval" v-else>
|
||
{{ $t('index.daily') }}
|
||
</view>
|
||
</view>
|
||
<view class="img">
|
||
<image v-if="shouldShowIcon(4)"
|
||
src="https://api.ccttiot.com/smartmeter/img/static/u9iZpd6bW6bUsUay4uvH" mode="">
|
||
</image>
|
||
</view>
|
||
</view>
|
||
<view class="weeklist">
|
||
<view class="" v-if="!ver_data.p_set5 || ver_data.p_set5[3] == 0">
|
||
<view class="kg" style="color: rgba(0, 0, 0, .3);" v-if="!ver_data.p_set5 || ver_data.p_set5[3] == 0">
|
||
OFF
|
||
</view>
|
||
<view class="weekday" style="color: rgba(0,0,0,.3);">
|
||
P5
|
||
</view>
|
||
<view class="time" style="color: rgba(0,0,0,.3);">
|
||
{{ver_data.p_set5 && ver_data.p_set5[0] !== undefined ? ver_data.p_set5[0].toString().padStart(2, '0') + ':' + ver_data.p_set5[1].toString().padStart(2, '0') : '--:--'}}
|
||
</view>
|
||
<view class="interval" style="color: rgba(0,0,0,.3);" v-if="ver_data.p_set5 && ver_data.p_set5[4] > 1">
|
||
{{ $t('index.everyNdays', { n: ver_data.p_set5[4] }) }}
|
||
</view>
|
||
<view style="color: rgba(0,0,0,.3);" class="interval" v-else>
|
||
{{ $t('index.daily') }}
|
||
</view>
|
||
</view>
|
||
<view class="" v-else>
|
||
<view class="kg" style="color: #7FAD76;">
|
||
ON
|
||
</view>
|
||
<view class="weekday">
|
||
P5
|
||
</view>
|
||
<view class="time">
|
||
{{ver_data.p_set5 && ver_data.p_set5[0] !== undefined ? ver_data.p_set5[0].toString().padStart(2, '0') + ':' + ver_data.p_set5[1].toString().padStart(2, '0') : '--:--'}}
|
||
</view>
|
||
<view class="interval" v-if="ver_data.p_set5 && ver_data.p_set5[4] > 1">
|
||
{{ $t('index.everyNdays', { n: ver_data.p_set5[4] }) }}
|
||
</view>
|
||
<view class="interval" v-else>
|
||
{{ $t('index.daily') }}
|
||
</view>
|
||
</view>
|
||
<view class="img">
|
||
<image v-if="shouldShowIcon(5)"
|
||
src="https://api.ccttiot.com/smartmeter/img/static/u9iZpd6bW6bUsUay4uvH" mode="">
|
||
</image>
|
||
</view>
|
||
</view>
|
||
<view class="weeklist">
|
||
<view class="" v-if="!ver_data.p_set6 || ver_data.p_set6[3] == 0">
|
||
<view class="kg" style="color: rgba(0, 0, 0, .3);" v-if="!ver_data.p_set6 || ver_data.p_set6[3] == 0">
|
||
OFF
|
||
</view>
|
||
<view class="weekday" style="color: rgba(0,0,0,.3);">
|
||
P6
|
||
</view>
|
||
<view class="time" style="color: rgba(0,0,0,.3);">
|
||
{{ver_data.p_set6 && ver_data.p_set6[0] !== undefined ? ver_data.p_set6[0].toString().padStart(2, '0') + ':' + ver_data.p_set6[1].toString().padStart(2, '0') : '--:--'}}
|
||
</view>
|
||
<view class="interval" style="color: rgba(0,0,0,.3);" v-if="ver_data.p_set6 && ver_data.p_set6[4] > 1">
|
||
{{ $t('index.everyNdays', { n: ver_data.p_set6[4] }) }}
|
||
</view>
|
||
<view style="color: rgba(0,0,0,.3);" class="interval" v-else>
|
||
{{ $t('index.daily') }}
|
||
</view>
|
||
</view>
|
||
<view class="" v-else>
|
||
<view class="kg" style="color: #7FAD76;">
|
||
ON
|
||
</view>
|
||
<view class="weekday">
|
||
P6
|
||
</view>
|
||
<view class="time">
|
||
{{ver_data.p_set6 && ver_data.p_set6[0] !== undefined ? ver_data.p_set6[0].toString().padStart(2, '0') + ':' + ver_data.p_set6[1].toString().padStart(2, '0') : '--:--'}}
|
||
</view>
|
||
<view class="interval" v-if="ver_data.p_set6 && ver_data.p_set6[4] > 1">
|
||
{{ $t('index.everyNdays', { n: ver_data.p_set6[4] }) }}
|
||
</view>
|
||
<view class="interval" v-else>
|
||
{{ $t('index.daily') }}
|
||
</view>
|
||
</view>
|
||
<view class="img">
|
||
<image v-if="shouldShowIcon(6)"
|
||
src="https://api.ccttiot.com/smartmeter/img/static/u9iZpd6bW6bUsUay4uvH" mode="">
|
||
</image>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
<view class="" style="margin-top: 18rpx;display: flex;justify-content: space-between;" v-else>
|
||
<view class="" style="font-size: 24rpx;color: #50565A;text-align: center;margin-top: 10rpx;">
|
||
<view class="" style="display: flex;align-items: center;">
|
||
<image style="width:32rpx;height:32rpx;margin-right: 8rpx;" src="https://api.ccttiot.com/smartmeter/img/static/uhyYG0TLfWi9XUhTQz7D" mode=""></image>
|
||
{{ $t('index.startTime') }}
|
||
</view>
|
||
<view class="" style="margin-top:10rpx;font-size: 36rpx;font-weight: 600;" v-if="csbobj.hour">
|
||
{{csbobj.hour == undefined ? '--' : csbobj.hour + ':'}}{{csbobj.minute == undefined ? '' : csbobj.minute}}
|
||
</view>
|
||
<view class="" style="margin-top:10rpx;font-size: 36rpx;font-weight: 600;" v-else>
|
||
{{formatOnlineStartTime(zaixianobj)}}
|
||
</view>
|
||
</view>
|
||
<view class="" style="font-size: 24rpx;color: #50565A;margin-top: 10rpx;text-align: center;">
|
||
<view class="" style="display: flex;align-items: center;">
|
||
<image style="width:32rpx;height:32rpx;margin-right: 8rpx;font-size: 36rpx;font-weight: 600;" src="https://api.ccttiot.com/smartmeter/img/static/uuQaK98cmdQQnPWus27Z" mode=""></image>
|
||
{{ $t('index.workDuration') }}
|
||
</view>
|
||
<view class="" style="margin-top:10rpx;font-size: 36rpx;font-weight: 600;" v-if="csbobj.second">
|
||
{{csbobj.second == undefined ? '--' : csbobj.second + $t('common.sec')}}
|
||
</view>
|
||
<view class="" style="margin-top:10rpx;font-size: 36rpx;font-weight: 600;" v-else>
|
||
{{zaixianobj.t.value == undefined ? '--' : zaixianobj.t.value + $t('common.sec')}}
|
||
</view>
|
||
</view>
|
||
<view class="" style="font-size: 24rpx;color: #50565A;margin-top: 10rpx;text-align: center;">
|
||
<view class="" style="display: flex;align-items: center;">
|
||
<image style="width:32rpx;height:32rpx;margin-right: 8rpx;font-size: 36rpx;font-weight: 600;" src="https://api.ccttiot.com/smartmeter/img/static/uM6Yz4xVqFwzW6xlTRph" mode=""></image>
|
||
{{ $t('index.intervalDays') }}
|
||
</view>
|
||
<view class="" style="margin-top:10rpx;font-size: 36rpx;font-weight: 600;" v-if="csbobj.day">
|
||
{{csbobj.day == undefined ? '--' : csbobj.day + $t('common.day')}}
|
||
</view>
|
||
<view class="" style="margin-top:10rpx;font-size: 36rpx;font-weight: 600;" v-else>
|
||
{{zaixianobj.d.value == undefined ? '--' : zaixianobj.d.value + $t('common.day')}}
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
<!-- 开关选择 -->
|
||
<view class="dangqian">
|
||
<view class="" style="display: flex;">
|
||
<image src="https://lxnapi.ccttiot.com/smartmeter/img/static/ufHmxq7KI9PJhv8ey4ds" mode=""></image>
|
||
<view class="cen" @click="btntb">
|
||
<view class="shen">
|
||
{{ $t('index.currentTime') }} <text>{{ $t('index.sync') }}</text>
|
||
</view>
|
||
<view class="">
|
||
{{ $t('index.tapSyncHint') }}
|
||
</view>
|
||
</view>
|
||
</view>
|
||
<view class="rt" style="display: flex;align-items: center;" @click="btnsztime">
|
||
{{devicetime == '' ? '--' : devicetime.slice(0,5)}} <u-icon name="arrow-right" color="#7FAD76" size="32"></u-icon>
|
||
</view>
|
||
</view>
|
||
<view class="switchbox">
|
||
<view class="switch_he" v-if="['WATER', 'DATER', 'TATER', 'SATER'].includes(pre)">
|
||
<image src="https://api.ccttiot.com/smartmeter/img/static/u7NwkNOoQYYsvHVMkDlu" mode=""></image>
|
||
<view class="yushui">
|
||
{{ $t('index.rainSensor') }}
|
||
<u-switch v-if="yschecked" v-model="one" @change="btnyushui" :disabled="isRainSensorLocked" inactive-color="#eee"
|
||
active-color="#eee" size="40"></u-switch>
|
||
<u-switch v-else v-model="ones" @change="btnyushuis" :disabled="isRainSensorLocked" active-color="#7FAD76"
|
||
inactive-color="#7FAD76" size="40"></u-switch>
|
||
</view>
|
||
</view>
|
||
<view class="switch_he" v-if="['WATER', 'DATER', 'TATER', 'SATER'].includes(pre)">
|
||
<image src="https://api.ccttiot.com/smartmeter/img/static/u7kd92ocUgDN052nhp4R" mode=""></image>
|
||
<view class="yushui">
|
||
{{ $t('index.childLock') }} <u-switch v-if="etchecked" v-model="two" @change="btnertong" inactive-color="#eee"
|
||
active-color="#eee" size="40"></u-switch>
|
||
<u-switch v-else v-model="twos" @change="btnertongs" active-color="#7FAD76"
|
||
inactive-color="#7FAD76" size="40"></u-switch>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
<view class="" style="display: flex;justify-content: space-between;">
|
||
<!-- 定时 -->
|
||
<view class="dingshi_he" @click="btntime">
|
||
<view class="naoz">
|
||
<image src="https://api.ccttiot.com/smartmeter/img/static/uJPgzMejk9gaogWnCO9M" mode=""></image> <text v-if="['WATER', 'DATER', 'TATER', 'SATER'].includes(pre)">{{ $t('index.groups6') }}</text>
|
||
</view>
|
||
<view class="dstime">
|
||
{{ $t('index.timerMgmt') }} <image src="https://api.ccttiot.com/smartmeter/img/static/uagx3wGa7RYvqKqoSymy" mode="">
|
||
</image>
|
||
</view>
|
||
</view>
|
||
<!-- 手动浇水 -->
|
||
<view class="dingshi_he">
|
||
<view class="naoz" style="font-weight: 400;font-size: 26rpx;align-items: center;">
|
||
<image style="width: 170rpx;" src="https://api.ccttiot.com/smartmeter/img/static/uVYXvo4F6U6fJ5fP27BT" mode="aspectFill"></image>
|
||
<text v-if="(sdminutes || sdseconds) && pre === 'SATER'">{{stareChannel}}</text>
|
||
{{ sdminutes ? sdminutes + ':' : '' }}{{ sdseconds ? sdseconds : '' }}
|
||
<text v-if="showWateringCountdown && wateringCountdownSeconds" style="margin-left: 10rpx;color: #7FAD76;font-size: 24rpx;">
|
||
{{ wateringCountdownMinutes ? wateringCountdownMinutes + ':' : '' }}{{ wateringCountdownSeconds }}
|
||
</text>
|
||
</view>
|
||
<view class="dstime">
|
||
{{ $t('index.manualWater') }} <u-switch v-if="kgflag" v-model="jskeds" @change="btngb" inactive-color="#eee" active-color="#eee" size="40"></u-switch>
|
||
<u-switch v-else v-model="jsked" @change="btnkq" active-color="#7FAD76" inactive-color="#7FAD76" size="40"></u-switch>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
<!-- 浇水日志 -->
|
||
<view class="wateringlogbox" v-if="['WATER', 'DATER', 'TATER', 'SATER'].includes(pre)" @click="btnjs">
|
||
<view class="wateringlogtop">
|
||
<image src="https://api.ccttiot.com/smartmeter/img/static/uGh3pRM7mMRSHdmvtzv3" mode=""></image>
|
||
{{ $t('index.wateringLog') }}
|
||
</view>
|
||
<view class="wateringlogbd">
|
||
<view class="lt" >
|
||
<view class="">{{ $t('index.lastSprayDur') }}{{jsobj.sprayingTime == undefined ? '--' : jsobj.sprayingTime + 's'}}</view>
|
||
<view class="">{{ $t('index.lastWater') }}{{jsobj.lastWaterTime == undefined ? '--' : jsobj.lastWaterTime + 's'}}</view>
|
||
</view>
|
||
<view class="rt">
|
||
<image src="https://api.ccttiot.com/smartmeter/img/static/uagx3wGa7RYvqKqoSymy" mode=""></image>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
<view class="" @click="btnbc" v-if="['WATER', 'DATER', 'TATER', 'SATER'].includes(pre)" style="width: 100%;height: 80rpx;background-color: #7FAD76;color: #fff;text-align: center;font-size: 32rpx;line-height: 80rpx;margin-top: 30rpx;border-radius: 20rpx;">
|
||
{{ $t('index.saveTimer') }}
|
||
</view>
|
||
</view>
|
||
<!-- 切换设备 -->
|
||
<view class="tabsb" v-if="xuanzeflag">
|
||
<view class="selectbox">
|
||
<view class="selectname" @click="btnksxz">
|
||
{{ $t('index.switchDevice') }} <image v-if="!xuanzeflag" src="https://api.ccttiot.com/smartmeter/img/static/uwHOBxvbJjkhx1uDiQHI" mode=""></image>
|
||
<image v-else class="rotated-image" src="https://api.ccttiot.com/smartmeter/img/static/uwHOBxvbJjkhx1uDiQHI" mode=""></image>
|
||
</view>
|
||
</view>
|
||
<view class="sblist">
|
||
<view class="sbist_val" v-for="(item,index) in devicelist" :key="index" @click="btnactive(item.deviceId,index)">
|
||
<view class="lt">
|
||
<view class="">{{item.deviceName}}</view>
|
||
<view class="" style="font-weight: 400;font-size: 26rpx;color: #989090;margin-top: 10rpx;">
|
||
{{item.mac}}
|
||
</view>
|
||
</view>
|
||
<view class="rt">
|
||
<image :src="item.modelPicture" mode="aspectFit"></image>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
<view class="qdan" @click="btnaddsb">
|
||
{{ $t('index.addDevice') }}
|
||
</view>
|
||
</view>
|
||
<view class="mask" v-if="xuanzeflag" @click="btntc"></view>
|
||
<!-- 手动浇水 -->
|
||
<view class="manualjs" v-if="sdjsflag">
|
||
<view class="toptit">
|
||
{{ $t('index.pickDuration') }}
|
||
</view>
|
||
<view class="container" @click="btnshowjs">
|
||
<view class="">{{minute}}{{ $t('common.min') }}</view>
|
||
<text>:</text>
|
||
<view class="">{{second}}{{ $t('common.sec') }}</view>
|
||
</view>
|
||
<view class="anniu">
|
||
<view class="qx" @click="btnqx">
|
||
{{ $t('common.cancel') }}
|
||
</view>
|
||
<view class="qd" @click="btnqd">
|
||
{{ $t('common.confirm') }}
|
||
</view>
|
||
</view>
|
||
</view>
|
||
<view class="manualmask" v-if="sdjsflag"></view>
|
||
<!-- 选择浇水时间-->
|
||
<u-picker v-model="showjs" mode="time" :default-time="0" :params="params" @confirm="confirm"></u-picker>
|
||
<!-- 设置当前时间 -->
|
||
<u-picker v-model="timeflag" mode="time" :params="paramss" @confirm="confirmtime"></u-picker>
|
||
<tab-bar :indexs='0'></tab-bar>
|
||
<language-float />
|
||
|
||
<!-- 自定义错误弹窗 -->
|
||
<view class="error-modal-mask" v-if="showErrorModal" @click="closeErrorModal">
|
||
<view class="error-modal-container" @click.stop>
|
||
<view class="error-modal-header">
|
||
<view class="error-icon">⚠️</view>
|
||
<text class="error-modal-title">{{errorModalData.title}}</text>
|
||
</view>
|
||
<view class="error-modal-content">
|
||
<view class="error-message">{{errorModalData.message}}</view>
|
||
<view class="error-action" v-if="errorModalData.userAction">
|
||
<text class="action-label">{{ $t('index.suggestion') }}</text>
|
||
<text class="action-text">{{errorModalData.userAction}}</text>
|
||
</view>
|
||
</view>
|
||
<view class="error-modal-footer">
|
||
<view class="error-btn error-btn-cancel" v-if="errorModalData.showCancel" @click="closeErrorModal">
|
||
{{errorModalData.cancelText || $t('common.cancel')}}
|
||
</view>
|
||
<view class="error-btn error-btn-confirm" @click="handleErrorConfirm">
|
||
{{errorModalData.confirmText || $t('common.confirm')}}
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
</view>
|
||
</template>
|
||
|
||
<script>
|
||
import LanguageFloat from '@/components/language-float/language-float.vue'
|
||
var xBlufi = require("@/components/blufi/xBlufi.js")
|
||
export default {
|
||
components: {
|
||
LanguageFloat
|
||
},
|
||
data() {
|
||
return {
|
||
timeflag:false,
|
||
// 手动浇水“确定”按钮请求锁:请求未返回前禁止重复提交
|
||
isManualWaterRequesting: false,
|
||
// 点击确定后立即锁死雨水感应(不等请求返回),请求失败或浇水结束才解锁
|
||
rainSensorLockedByWatering: false,
|
||
// 手动浇水开关(开启/关闭)防频繁点击:接口模式等请求结束,蓝牙模式至少冷却3秒
|
||
isManualWaterSwitchRequesting: false,
|
||
manualWaterBtCooldownUntil: 0,
|
||
// 浇水倒计时到0后的延迟清理定时器(避免和新倒计时串台)
|
||
wateringCountdownResetTimer: null,
|
||
// 刷新按钮“刷新中”状态,约 1.5 秒
|
||
isRefreshing: false,
|
||
// 最近一次成功上传的时间戳(毫秒)
|
||
lastUploadTs: 0,
|
||
// 最近一次获取电量的时间戳(毫秒)
|
||
lastPowerTs: 0,
|
||
one: false,
|
||
ones: true,
|
||
two: false,
|
||
twos: true,
|
||
jsked:true,
|
||
jskeds:false,
|
||
yschecked: false,
|
||
etchecked: false,
|
||
kgflag: true,
|
||
activeshu: 0,
|
||
xuanzeflag: false,
|
||
selectedMinute: '1',
|
||
selectedSecond: '1',
|
||
sdjsflag: false,
|
||
showjs: false,
|
||
params: {
|
||
year: false,
|
||
month: false,
|
||
day: false,
|
||
hour: false,
|
||
minute: true,
|
||
second: true
|
||
},
|
||
paramss: {
|
||
year: false,
|
||
month: false,
|
||
day: false,
|
||
hour: true,
|
||
minute: true,
|
||
second: false
|
||
},
|
||
minute: '00',
|
||
second: '10',
|
||
hasShownVersionAlert: false,
|
||
deviceId: '',
|
||
name: '',
|
||
mac: '',
|
||
jstime: '10',
|
||
ver_data: {},
|
||
vardataflag:1,
|
||
showobj: {},
|
||
jstimeobj:{},
|
||
xctime: '--',
|
||
sctimejs: '--',
|
||
xctimesc: '--',
|
||
xctimesj: '',
|
||
sdminutes: '',
|
||
sdseconds: '',
|
||
wateringCountdownMinutes: '', // 浇水倒计时分钟(从showArray[4]获取)
|
||
wateringCountdownSeconds: '', // 浇水倒计时秒数(从showArray[4]获取)
|
||
showWateringCountdown: false, // 是否显示浇水倒计时(第三位和第四位都为1时才显示)
|
||
datalist: '',
|
||
devicelist:[],
|
||
shebid:'',
|
||
user:{},
|
||
lastChar:'',
|
||
timer:'',
|
||
wateringCountdownTimer: null, // 浇水倒计时定时器
|
||
bjflag: '',
|
||
userobj:{},
|
||
devicesarr:[],
|
||
intervalId: null,
|
||
imgflag:true,
|
||
ver:'',
|
||
pre:'',
|
||
csbobj:{},
|
||
disconnectTimer: null,
|
||
devicetime:'',
|
||
imgpic:'',
|
||
xinp:'',
|
||
version:'',
|
||
jiance:false,
|
||
shibainum:0,
|
||
searchStartTime: 0, // 记录开始搜索的时间戳
|
||
searchTimeout: 10000, // 10秒超时(单位:毫秒)
|
||
searchTimer: null, // 搜索定时器
|
||
isSearching: false, // 是否正在搜索中
|
||
dataTimeoutTimer: null ,// 5秒数据看门狗定时器
|
||
connectionStartTime: 0, // 记录连接开始时间
|
||
connectionTimeout: null, // 连接超时定时器
|
||
connectTimeout: null, // 配对超时定时器
|
||
errorModalShown: false, // 错误弹窗是否已显示(防止重复弹出)
|
||
currentErrorType: null, // 当前检测到的错误类型
|
||
showErrorModal: false, // 是否显示自定义错误弹窗
|
||
errorModalData: {}, // 错误弹窗数据
|
||
dianya:0,
|
||
sydl:'--',
|
||
signalStrength: 0, // 信号强度格数(1-4)
|
||
zaixianobj:{},
|
||
modelId:'',
|
||
jsobj:{},
|
||
isLoading: false ,// 设备请求加载状态
|
||
vatatxt:'',
|
||
sn:'',
|
||
onlineTime:'',
|
||
onlineStatus:'',
|
||
qiehuan:'',
|
||
// SATER 双通道(A/B)
|
||
stareChannel: 'A',
|
||
// SATER 按类型缓存:pA/pB/ver 三组数据不定序到达,存各自最新值
|
||
saterDataByType: { pA: '', pB: '', ver: '' },
|
||
// SATER 最近一次完整原始数据(包含 A/B),用于通道切换后重算
|
||
saterRawPayload: '',
|
||
// SATER 从 getDetail 接口解析的 A/B 组别(非蓝牙模式展示用)
|
||
saterListA: {},
|
||
saterListB: {}
|
||
}
|
||
},
|
||
computed: {
|
||
// 是否正在浇水(手动定时器或设备倒计时在跑),用于:浇水开关不可关闭、雨水感应不可点击
|
||
isWateringInProgress() {
|
||
const manualRunning = !!(this.sdminutes || this.sdseconds)
|
||
const deviceCountdownRunning = !!(this.showWateringCountdown && (this.wateringCountdownMinutes || this.wateringCountdownSeconds))
|
||
return manualRunning || deviceCountdownRunning
|
||
},
|
||
// 雨水感应是否锁死(点击确定那一刻即锁,请求失败或浇水结束才解锁)
|
||
isRainSensorLocked() {
|
||
return this.isWateringInProgress || this.rainSensorLockedByWatering
|
||
},
|
||
// 电量百分比 0-100,无效时为 null(显示 --)
|
||
batteryPercent() {
|
||
if (this.sydl == null || this.sydl === '' || isNaN(Number(this.sydl))) return null
|
||
return Math.min(100, Math.max(0, Math.floor(Number(this.sydl))))
|
||
},
|
||
// 电量条背景色:低红、中黄、高绿
|
||
batteryColor() {
|
||
if (this.batteryPercent == null) return '#E0E0E0'
|
||
if (this.batteryPercent <= 20) return '#F44336'
|
||
if (this.batteryPercent <= 49) return '#FFC107'
|
||
return '#7FAD76'
|
||
}
|
||
},
|
||
// 分享到好友(会话)
|
||
onShareAppMessage: function() {
|
||
return {
|
||
title: this.$t('app.name'),
|
||
path: '/pages/index/index'
|
||
}
|
||
},
|
||
|
||
// 分享到朋友圈
|
||
onShareTimeline: function() {
|
||
return {
|
||
title: this.$t('app.name'),
|
||
query: '',
|
||
path: '/pages/index/index'
|
||
}
|
||
},
|
||
onLoad(e) {
|
||
console.log(e,'000000000000000000000000000000000000');
|
||
if(e.q){
|
||
function getQueryParam(url, paramName) {
|
||
let regex = new RegExp(`[?&]${paramName}=([^&]*)`)
|
||
let results = regex.exec(url)
|
||
return results ? decodeURIComponent(results[1].replace(/\+/g, ' ')) : null
|
||
}
|
||
let sceneValue = e.q
|
||
let decodedValue = decodeURIComponent(sceneValue)
|
||
this.sn = getQueryParam(decodedValue, 's')
|
||
let data = {
|
||
sn:this.sn
|
||
}
|
||
this.$u.post(`/app/device/bindDevice`,data).then((res) => {
|
||
if (res.code == 200) {
|
||
uni.showModal({
|
||
title: this.$t('common.tip'),
|
||
content: this.$t('common.bindSuccess'),
|
||
showCancel: false,
|
||
confirmText: this.$t('common.gotIt')
|
||
})
|
||
}else if(res.code == 401){
|
||
this.jmlogin()
|
||
} else{
|
||
uni.showModal({
|
||
title: this.$t('common.tip'),
|
||
content: res.msg,
|
||
showCancel: false,
|
||
confirmText: this.$t('common.gotIt')
|
||
})
|
||
}
|
||
})
|
||
}
|
||
xBlufi.initXBlufi(1)
|
||
},
|
||
onShow() {
|
||
// 从定时页返回时:若当前是蓝牙已连接状态,先同步定时页可能写入的 BLE 数据并在首页解析
|
||
if (this.$store && this.$store.state.bleWaterData && this.$store.state.bleWaterData.raw && this.vardataflag === 3 && (this.pre === 'WATER' || this.pre === 'SATER')) {
|
||
const raw = this.$store.state.bleWaterData.raw
|
||
this.datalist = (raw.slice(-1) === ';' ? raw : raw.slice(0, -1) + ';')
|
||
if (this.pre === 'SATER') {
|
||
this.saterRawPayload = this.datalist
|
||
this.grisfjx()
|
||
} else {
|
||
this.getchuli()
|
||
}
|
||
this.$store.commit('clearBleWaterData')
|
||
}
|
||
// 蓝牙已连接时确保注册监听,以便返回首页后能继续收到设备推送
|
||
if (this.vardataflag === 3) {
|
||
xBlufi.listenDeviceMsgEvent(true, this.funListenDeviceMsgEvent)
|
||
}
|
||
// 请求个人信息
|
||
this.getinfo()
|
||
this.getshuju() // 页面加载时启动定时器
|
||
},
|
||
mounted() {
|
||
|
||
},
|
||
onHide() {
|
||
// 页面隐藏时清除定时器
|
||
this.clearTimer()
|
||
this.clearDisconnectTimer()
|
||
this.clearDataTimeoutTimer()
|
||
this.clearConnectionTimeout()
|
||
this.clearWateringCountdownTimer()
|
||
},
|
||
onUnload() {
|
||
// 页面卸载时清除定时器
|
||
this.clearTimer()
|
||
this.clearDisconnectTimer()
|
||
this.clearDataTimeoutTimer()
|
||
this.clearConnectionTimeout()
|
||
this.clearWateringCountdownTimer()
|
||
},
|
||
methods: {
|
||
pad2(v){
|
||
if (v == null || v === '' || v === undefined) return '-'
|
||
const s = String(v)
|
||
return s.length < 2 ? ('0' + s) : s
|
||
},
|
||
formatOnlineStartTime(obj){
|
||
const h = obj && obj.h ? obj.h.value : undefined
|
||
const m = obj && obj.m ? obj.m.value : undefined
|
||
if (h === undefined || m === undefined) return '--:--'
|
||
return `${this.pad2(h)}:${this.pad2(m)}`
|
||
},
|
||
// 点击弹窗进行隐藏
|
||
btntc(){
|
||
this.xuanzeflag = !this.xuanzeflag
|
||
},
|
||
|
||
// 将时间格式(HH:mm)转换为总秒数
|
||
timeToSeconds(timeStr) {
|
||
if (!timeStr || timeStr === '--' || !timeStr.includes(':')) {
|
||
return 0;
|
||
}
|
||
const [hours, minutes] = timeStr.split(':').map(Number);
|
||
return (hours || 0) * 3600 + (minutes || 0) * 60;
|
||
},
|
||
// 将总秒数转换为时间格式(HH:mm)
|
||
secondsToTime(seconds) {
|
||
if (!seconds && seconds !== 0) {
|
||
return '--';
|
||
}
|
||
const totalSeconds = parseInt(seconds) || 0;
|
||
const hours = Math.floor(totalSeconds / 3600);
|
||
const minutes = Math.floor((totalSeconds % 3600) / 60);
|
||
return `${String(hours).padStart(2, '0')}:${String(minutes).padStart(2, '0')}`;
|
||
},
|
||
// 点击断开连接
|
||
btnduankai(){
|
||
wx.closeBLEConnection({
|
||
deviceId: this.deviceId,
|
||
})
|
||
this.fullPageRefresh()
|
||
},
|
||
// 跳转到网关列表
|
||
goToGatewayList() {
|
||
uni.navigateTo({
|
||
url: '/pages/gateway/list'
|
||
})
|
||
},
|
||
// 请求浇花器数据
|
||
getsj(){
|
||
this.$u.get(`/app/device/getDetail?deviceId=${this.shebid}`).then(res => {
|
||
if (res.code == 200) {
|
||
this.zaixianobj = res.data.iotData
|
||
this.modelId = res.data.modelId
|
||
console.log(this.vardataflag,this.zaixianobj);
|
||
// 非蓝牙模式时,用后台数据组装渲染结构
|
||
if((this.pre === 'WATER' || this.pre === 'SATER') && this.vardataflag != 3 || this.pre === 'DATER' && this.vardataflag != 3 || this.pre === 'TATER' && this.vardataflag != 3){
|
||
this.ver_data = this.buildVerDataFromBackend(this.zaixianobj)
|
||
this.calculateNextWateringTime()
|
||
}
|
||
if(this.pre === 'SMSJ:' && this.vardataflag != 3){
|
||
this.ver_data = this.buildVerDataFromBackend(this.zaixianobj)
|
||
// this.ver_data = this.zaixianobj
|
||
console.log(this.ver_data,'55555555555prprprprprprprpr');
|
||
}
|
||
if (res.data && res.data.iotData && res.data.iotData.xinp !== undefined) {
|
||
this.xinp = res.data.iotData.xinp.value == undefined ? '--' : res.data.iotData.xinp.value
|
||
}
|
||
if (res.data && res.data.iotData && res.data.iotData.yudi !== undefined) {
|
||
if (!this.isRainSensorLocked) {
|
||
this.yschecked = res.data.iotData.yudi.value == 0 ? true : false
|
||
}
|
||
}
|
||
if (res.data && res.data.iotData && res.data.iotData.lock !== undefined) {
|
||
this.etchecked = res.data.iotData.lock.value == 0 ? true : false
|
||
}
|
||
// 优先从 nt.value 获取总秒数,如果没有则从 time.value 获取
|
||
let ntValue = res?.data?.iotData?.nt?.value;
|
||
if (ntValue !== undefined && ntValue !== null && ntValue !== '') {
|
||
// 从总秒数转换为时间格式
|
||
this.devicetime = this.secondsToTime(ntValue);
|
||
} else {
|
||
// 兼容旧数据,从 time.value 获取
|
||
let originalValue = (res?.data?.iotData?.time?.value ?? "").toString().trim();
|
||
// 去掉首尾可能多出来的英文双引号 "
|
||
originalValue = originalValue.replace(/^"|"$/g, '');
|
||
this.devicetime = originalValue || "--";
|
||
}
|
||
console.log('devicetime:', this.devicetime);
|
||
// 获取信号强度
|
||
if (res.data && res.data.iotData && res.data.iotData.RSSI !== undefined && res.data.iotData.RSSI.value !== undefined) {
|
||
this.signalStrength = this.calculateSignalStrength(res.data.iotData.RSSI.value)
|
||
} else {
|
||
this.signalStrength = 0
|
||
}
|
||
// 取值
|
||
this.onlineTime = res.data.onlineTime
|
||
this.onlineStatus = res.data.onlineStatus
|
||
if(res.data.iotData && res.data.iotData.bat !== undefined && res.data.iotData.bat.value !== undefined){
|
||
this.dianya = res.data.iotData.bat.value
|
||
this.getdianliang()
|
||
}
|
||
}
|
||
})
|
||
},
|
||
// 点击保存设置
|
||
btnbc(){
|
||
this.handleUserAction()
|
||
if(this.vardataflag != 3){
|
||
let parameters = {
|
||
time:this.xinp
|
||
}
|
||
let data = {
|
||
deviceId:this.shebid,
|
||
instructionKey:'sleep',
|
||
parameters:parameters
|
||
}
|
||
this.$u.post(`/app/sendCommandByGateway`,data).then(res => {
|
||
if (res.code == 200) {
|
||
uni.showToast({
|
||
title: this.$t('common.saveOk'),
|
||
icon: 'success',
|
||
duration:2000
|
||
})
|
||
}else{
|
||
uni.showToast({
|
||
title: res.msg,
|
||
icon: 'none',
|
||
duration:2000
|
||
})
|
||
}
|
||
})
|
||
}else{
|
||
this.$u.get(`/app/getModelCommand/${this.modelId}?instructionKey=sleep`).then(res =>{
|
||
if(res.code == 200){
|
||
uni.showToast({
|
||
title: this.$t('common.saveOk'),
|
||
icon: 'success',
|
||
duration:2000
|
||
})
|
||
xBlufi.notifySendCustomData({
|
||
customData: '11' + this.format(res.data, {time: this.xinp })
|
||
})
|
||
}else{
|
||
uni.hideLoading()
|
||
uni.showToast({
|
||
title: res.msg,
|
||
icon: 'none',
|
||
duration:2000
|
||
})
|
||
}
|
||
})
|
||
}
|
||
},
|
||
getbanbens(){
|
||
this.$u.get(`/app/model/${this.user.modelId}`).then(res => {
|
||
if (res.code == 200) {
|
||
this.version = res.data.version.slice(1)
|
||
console.log(this.version,'this.versionthis.versionthis.version');
|
||
}
|
||
})
|
||
},
|
||
// 点击进行设备录入
|
||
btnluru(){
|
||
this.datalist = ''
|
||
this.mac = ''
|
||
this.ver_data = {}
|
||
uni.reLaunch({
|
||
url:'/pages/myorder/index'
|
||
})
|
||
},
|
||
// 点击选择时间
|
||
btnshowjs() {
|
||
this.showjs = true
|
||
},
|
||
// 补零函数
|
||
formattedTime(minutes, seconds) {
|
||
// 将数字转换为字符串并补零
|
||
const formattedMinutes = String(minutes).padStart(2, '0')
|
||
const formattedSeconds = String(seconds).padStart(2, '0')
|
||
// 返回格式化后的时间字符串
|
||
console.log(`${formattedMinutes}:${formattedSeconds}`,'111111111')
|
||
return `${formattedMinutes}:${formattedSeconds}`
|
||
},
|
||
// 静默登录
|
||
jmlogin() {
|
||
let taht = this
|
||
wx.login({
|
||
success(res) {
|
||
if (res.code) {
|
||
let data = {
|
||
loginCode: res.code,
|
||
}
|
||
taht.$u.post(`/wxLogin`, data).then(res => {
|
||
if (res.code == 200) {
|
||
taht.getinfo()
|
||
uni.setStorageSync('token', res.token)
|
||
}else if(res.code == 10001){
|
||
that.bjflag = true
|
||
uni.showModal({
|
||
title: taht.$t('common.tip'),
|
||
content: taht.$t('common.promptLogin'),
|
||
success: function (res) {
|
||
if (res.confirm) {
|
||
uni.reLaunch({
|
||
url:'/pages/login/login'
|
||
})
|
||
} else if (res.cancel) {
|
||
|
||
}
|
||
}
|
||
})
|
||
}
|
||
})
|
||
}
|
||
}
|
||
})
|
||
},
|
||
// 获取用户信息
|
||
getinfo() {
|
||
this.isLoading = true // 开始加载
|
||
this.$u.get(`/system/user/profile`).then((res) => {
|
||
if (res.code == 200) {
|
||
this.userobj = res.data
|
||
uni.setStorageSync('user',res.data)
|
||
uni.setStorageSync('userId',res.data.userId)
|
||
// 根据用户id获取当前用户
|
||
this.getlist()
|
||
}else if(res.code == 401){
|
||
this.isLoading = false // 请求失败时隐藏加载状态
|
||
this.bjflag = true
|
||
this.jmlogin()
|
||
}
|
||
}).catch(() => {
|
||
this.isLoading = false // 请求异常时隐藏加载状态
|
||
})
|
||
},
|
||
// 获取设备列表
|
||
getlist(){
|
||
this.$u.get(`/app/device/list?userId=${this.userobj.userId}&classifyCodeList=1`).then((res) => {
|
||
if (res.code == 200) {
|
||
if(res.data.length > 0 || res.data != ''){
|
||
this.bjflag = false
|
||
this.devicelist = res.data
|
||
this.imgpic = res.data[0].modelPicture
|
||
this.mac = res.data[0].mac
|
||
this.pre = res.data[0].pre
|
||
console.log(this.pre,'454545454545454');
|
||
this.user = res.data[0]
|
||
this.shebid = res.data[0].deviceId
|
||
this.getbanbens()
|
||
this.getscjsjl()
|
||
setTimeout(()=>{
|
||
this.getsj()
|
||
},1000)
|
||
}else{
|
||
this.bjflag = true
|
||
}
|
||
this.isLoading = false // 请求完成后隐藏加载状态
|
||
}else{
|
||
this.isLoading = false
|
||
}
|
||
}).catch(() => {
|
||
this.isLoading = false // 请求异常时隐藏加载状态
|
||
})
|
||
},
|
||
// 点击连接蓝牙函数
|
||
btnlj() {
|
||
// 如果正在连接中,不重复连接
|
||
if (this.vardataflag === 2) {
|
||
return
|
||
}
|
||
// 如果已连接,提示用户
|
||
if (this.vardataflag === 3) {
|
||
uni.showToast({
|
||
title: this.$t('index.deviceConnected'),
|
||
icon: 'none',
|
||
duration: 2000
|
||
})
|
||
return
|
||
}
|
||
|
||
// 重置错误状态(允许重新检查错误)
|
||
this.errorModalShown = false
|
||
this.currentErrorType = null
|
||
this.shibainum = 0
|
||
this.connectionStartTime = Date.now() // 记录连接开始时间
|
||
this.connectionTimeout = null // 连接超时定时器
|
||
|
||
// 先检查蓝牙状态并尝试自动修复
|
||
this.checkAndFixBluetooth().then(() => {
|
||
// 蓝牙正常,开始连接流程
|
||
xBlufi.initXBlufi(1)
|
||
xBlufi.listenDeviceMsgEvent(true, this.funListenDeviceMsgEvent)
|
||
xBlufi.notifyStartDiscoverBle({
|
||
'isStart': true
|
||
})
|
||
this.vardataflag = 2
|
||
|
||
// 设置连接超时(30秒)
|
||
this.connectionTimeout = setTimeout(() => {
|
||
if (this.vardataflag === 2) {
|
||
this.handleConnectionTimeout()
|
||
}
|
||
}, 30000)
|
||
|
||
this.findDevice()
|
||
}).catch((error) => {
|
||
// 蓝牙检查失败,显示错误提示(只显示一次)
|
||
if (!this.errorModalShown) {
|
||
this.handleConnectionError(error)
|
||
}
|
||
})
|
||
},
|
||
// ios递归函数匹配
|
||
findDevice() {
|
||
let that = this;
|
||
const findDevices = () => {
|
||
console.log(that.shibainum,'失败次数');
|
||
// 如果已经尝试了10次及以上,直接返回不执行
|
||
if (that.shibainum > 10) {
|
||
that.vardataflag = 1
|
||
that.clearConnectionTimeout()
|
||
xBlufi.notifyStartDiscoverBle({
|
||
'isStart': false
|
||
});
|
||
// 诊断连接失败原因
|
||
that.diagnoseConnectionFailure();
|
||
return;
|
||
}
|
||
that.vatatxt = that.$t('index.searching');
|
||
const matchedDevice = that.devicesarr.find(device => {
|
||
if (device.name) {
|
||
return device.name.slice(-12) === that.mac.slice(-12);
|
||
}
|
||
});
|
||
if (matchedDevice) {
|
||
// 找到设备的处理逻辑...
|
||
that.vatatxt = that.$t('index.pairing');
|
||
that.clearConnectionTimeout() // 清除超时定时器
|
||
xBlufi.notifyStartDiscoverBle({
|
||
'isStart': false
|
||
});
|
||
console.log(matchedDevice, '找到匹配设备');
|
||
xBlufi.notifyConnectBle({
|
||
isStart: true,
|
||
deviceId: matchedDevice.deviceId,
|
||
name: matchedDevice.name
|
||
});
|
||
that.deviceId = matchedDevice.deviceId;
|
||
// 设置连接超时(8秒)
|
||
that.connectTimeout = setTimeout(() => {
|
||
if (that.vardataflag !== 3 && !that.errorModalShown) {
|
||
that.vardataflag = 1
|
||
that.shibainum = 0;
|
||
that.handleConnectionError({
|
||
type: 'CONNECT_TIMEOUT',
|
||
message: that.$t('index.ble.pairTimeout'),
|
||
userAction: that.$t('index.ble.pairTimeoutHint'),
|
||
canAutoFix: false
|
||
})
|
||
}
|
||
}, 8000);
|
||
} else {
|
||
// 未找到设备,递增计数器并继续尝试
|
||
that.shibainum++;
|
||
// 只有在还没到10次时才设置定时器继续查找
|
||
if (that.shibainum < 10) {
|
||
that.findDeviceTimer = setTimeout(findDevices, 1000);
|
||
} else {
|
||
that.vardataflag = 1
|
||
that.clearConnectionTimeout()
|
||
that.shibainum = 0;
|
||
// 诊断连接失败原因
|
||
that.diagnoseConnectionFailure();
|
||
}
|
||
}
|
||
};
|
||
// 重置计数器后开始查找
|
||
findDevices();
|
||
},
|
||
// 获取附近蓝牙设备列表
|
||
funListenDeviceMsgEvent: function(options) {
|
||
switch (options.type) {
|
||
case xBlufi.XBLUFI_TYPE.TYPE_STATUS_CONNECTED:
|
||
if (!options.result) {
|
||
this.vardataflag = 1
|
||
this.clearDisconnectTimer()
|
||
this.clearConnectionTimeout()
|
||
console.log('设备连接断开', options);
|
||
// 如果是在连接过程中断开,且还没显示过错误,才显示提示
|
||
if (this.connectionStartTime && (Date.now() - this.connectionStartTime) < 5000 && !this.errorModalShown) {
|
||
this.handleConnectionError({
|
||
type: 'CONNECTION_DISCONNECTED',
|
||
message: this.$t('index.ble.disconnected'),
|
||
userAction: this.$t('index.ble.disconnectedHint'),
|
||
canAutoFix: false
|
||
})
|
||
}
|
||
}
|
||
break;
|
||
case xBlufi.XBLUFI_TYPE.TYPE_GET_DEVICE_LISTS:
|
||
if (options.result) {
|
||
this.devicesarr = options.data
|
||
// console.log('搜索的附近设备列表',this.devicesarr);
|
||
}
|
||
break;
|
||
case xBlufi.XBLUFI_TYPE.TYPE_CONNECTED:
|
||
console.log("连接回调:" + JSON.stringify(options))
|
||
this.clearConnectionTimeout() // 清除连接超时定时器
|
||
xBlufi.notifyInitBleEsp32({
|
||
deviceId: this.deviceId
|
||
})
|
||
if (options.result == true){
|
||
console.log('服务发现成功:', options);
|
||
// 一连上立刻请求数据推送
|
||
this.vardataflag = 3
|
||
this.initDisconnectTimer()
|
||
this.datalist = ''
|
||
// this.startDataTimeoutTimer()
|
||
xBlufi.notifySendCustomData({
|
||
customData: "11get"
|
||
})
|
||
// 连接成功提示
|
||
uni.showToast({
|
||
title: this.$t('index.connectOk'),
|
||
icon: 'success',
|
||
duration: 2000
|
||
})
|
||
}else{
|
||
// 服务发现失败
|
||
this.vardataflag = 1
|
||
this.clearDisconnectTimer()
|
||
this.clearConnectionTimeout()
|
||
this.jiance = true
|
||
|
||
// 如果还没显示过错误,才显示
|
||
if (!this.errorModalShown) {
|
||
this.handleConnectionError({
|
||
type: 'SERVICE_DISCOVERY_FAILED',
|
||
message: this.$t('index.ble.serviceFail'),
|
||
userAction: this.$t('index.ble.serviceFailHint'),
|
||
canAutoFix: false
|
||
})
|
||
}
|
||
}
|
||
break
|
||
case xBlufi.XBLUFI_TYPE.TYPE_INIT_ESP32_RESULT:
|
||
|
||
break
|
||
case xBlufi.XBLUFI_TYPE.TYPE_RECIEVE_CUSTON_DATA:
|
||
console.log("收到设备数据:", options.data)
|
||
if(this.pre == 'SATER'){
|
||
// SATER 设备分 3 种类型不定序推送:pA(pA1:...)、pB(pB1:...)、ver(ver...@show:...)
|
||
// 按类型存最新值,集齐后解析并展示
|
||
let chunk = String(options.data || '')
|
||
if (chunk) {
|
||
chunk = chunk.slice(0, -1)
|
||
}
|
||
if (!chunk) return
|
||
if (!chunk.endsWith(';')) {
|
||
chunk += ';'
|
||
}
|
||
// 识别类型并更新该类型的最新数据
|
||
if (chunk.includes('pA1:')) {
|
||
this.saterDataByType.pA = chunk
|
||
} else if (chunk.includes('pB1:')) {
|
||
this.saterDataByType.pB = chunk
|
||
} else if (chunk.startsWith('ver')) {
|
||
this.saterDataByType.ver = chunk
|
||
} else {
|
||
return
|
||
}
|
||
const { pA, pB, ver } = this.saterDataByType
|
||
if (!pA || !pB || !ver) {
|
||
return
|
||
}
|
||
this.saterRawPayload = [ver, pA, pB].join('')
|
||
this.datalist = this.saterRawPayload
|
||
this.clearDataTimeoutTimer()
|
||
this.vardataflag = 3
|
||
this.grisfjx()
|
||
return
|
||
}
|
||
this.saterDataByType = { pA: '', pB: '', ver: '' }
|
||
this.saterRawPayload = ''
|
||
if (options.data.indexOf("prom:") !== -1) {
|
||
// console.log('固件升级中')
|
||
var indexOld = options.data.substring(options.data.indexOf('prom:'))
|
||
// console.log("indexOld", indexOld);
|
||
var load_num = indexOld.substring(
|
||
indexOld.indexOf("prom:") + 5,
|
||
indexOld.indexOf("@")
|
||
)
|
||
this.progress = Number(load_num);
|
||
// console.log("load_num", load_num);
|
||
console.log("升级进度:", this.progress)
|
||
if (this.progress === 6000) {
|
||
// console.log('固件成功')
|
||
this.progress = 100
|
||
uni.showToast({
|
||
title: this.$t('index.fwOk'),
|
||
icon: 'success',
|
||
duration: 2000
|
||
})
|
||
setTimeout(()=>{
|
||
this.shengjiflag = false
|
||
uni.reLaunch({
|
||
url:'/pages/index/index'
|
||
})
|
||
},2000)
|
||
}
|
||
if (this.progress === 9000) {
|
||
// console.log('固件升级失败')
|
||
this.progress = 99
|
||
uni.showToast({
|
||
title: this.$t('index.fwFail'),
|
||
icon: 'none',
|
||
duration: 2000
|
||
})
|
||
setTimeout(()=>{
|
||
this.shengjiflag = false
|
||
uni.reLaunch({
|
||
url:'/pages/index/index'
|
||
})
|
||
},1000)
|
||
}
|
||
} else {
|
||
this.datalist = options.data.slice(0, -1) + ";"
|
||
this.clearDataTimeoutTimer()
|
||
this.vardataflag = 3
|
||
if(this.pre == 'WATER' || this.pre == 'DATER' || this.pre == 'TATER'){
|
||
this.getchuli()
|
||
}else if(this.pre == 'SATER'){
|
||
this.grisfjx()
|
||
}else{
|
||
this.getcsbshuju()
|
||
}
|
||
}
|
||
break
|
||
case xBlufi.XBLUFI_TYPE.TYPE_GET_DEVICE_LISTS_START:
|
||
if (!options.result) {
|
||
this.vardataflag = 1
|
||
this.jiance = true
|
||
this.clearConnectionTimeout()
|
||
console.log('蓝牙搜索启动失败', options)
|
||
// 如果还没显示过错误,才显示
|
||
if (!this.errorModalShown) {
|
||
this.handleConnectionError({
|
||
type: 'SEARCH_START_FAILED',
|
||
message: this.$t('index.ble.searchStartFail'),
|
||
userAction: this.$t('index.ble.searchStartFailHint'),
|
||
canAutoFix: false
|
||
})
|
||
}
|
||
return
|
||
}
|
||
break
|
||
}
|
||
},
|
||
|
||
|
||
// 雨水感应开启
|
||
btnyushui() {
|
||
this.handleUserAction()
|
||
if (this.isRainSensorLocked) {
|
||
this.one = true
|
||
this.ones = false
|
||
uni.showToast({
|
||
title: this.$t('index.rainWait'),
|
||
icon: 'none',
|
||
duration: 2000
|
||
})
|
||
return
|
||
}
|
||
this.one = false
|
||
this.ones = true
|
||
if(this.vardataflag != 3){
|
||
uni.showLoading({
|
||
title: this.$t('index.opening')
|
||
})
|
||
|
||
this.$u.post(`/app/device/yudi/${this.shebid}`).then(res => {
|
||
if (res.code == 200) {
|
||
uni.hideLoading()
|
||
this.yschecked = false
|
||
uni.showToast({
|
||
title: this.$t('index.openOk'),
|
||
icon: 'success',
|
||
duration:2000
|
||
})
|
||
}else{
|
||
uni.hideLoading()
|
||
uni.showToast({
|
||
title: res.msg,
|
||
icon: 'none',
|
||
duration:2000
|
||
})
|
||
}
|
||
}).catch(error => {
|
||
uni.hideLoading() // 务必隐藏加载提示,避免一直显示
|
||
const errorMsg = error.message || '';
|
||
const isNetworkError = errorMsg.includes('Network Error') ||
|
||
errorMsg.includes('网络') ||
|
||
errorMsg.includes('超时') ||
|
||
errorMsg.includes('fail') || // 小程序请求失败的核心关键词
|
||
errorMsg.includes('request:fail');
|
||
uni.showToast({
|
||
title: isNetworkError ? this.$t('common.networkErrorAlt') : this.$t('common.requestFail'),
|
||
icon: 'none',
|
||
duration: 5000
|
||
})
|
||
// 可选:打印错误日志,方便调试
|
||
console.error('请求异常:', error)
|
||
})
|
||
}else{
|
||
this.$u.get(`/app/getModelCommand/${this.modelId}?instructionKey=yudi`).then(res =>{
|
||
if(res.code == 200){
|
||
uni.showLoading({
|
||
title: this.$t('index.opening')
|
||
})
|
||
this.yschecked = false
|
||
xBlufi.notifySendCustomData({
|
||
customData: '11' + res.data
|
||
})
|
||
uni.hideLoading()
|
||
}else{
|
||
uni.hideLoading()
|
||
uni.showToast({
|
||
title: res.msg,
|
||
icon: 'none',
|
||
duration:2000
|
||
})
|
||
}
|
||
})
|
||
}
|
||
},
|
||
// 点击同步时间
|
||
btntb(){
|
||
this.handleUserAction()
|
||
if(this.vardataflag != 3){
|
||
uni.showLoading({
|
||
title: this.$t('index.setting')
|
||
})
|
||
this.devicetime = this.getCurrentTime();
|
||
console.log(this.devicetime);
|
||
let data = {
|
||
deviceId:this.shebid,
|
||
time:this.devicetime,
|
||
}
|
||
this.$u.post(`/app/device/syncTime`,data).then(res => {
|
||
if (res.code == 200) {
|
||
uni.hideLoading()
|
||
uni.showToast({
|
||
title: this.$t('index.syncOk'),
|
||
icon: 'success',
|
||
duration:2000
|
||
})
|
||
}else{
|
||
uni.hideLoading()
|
||
this.devicetime = '--'
|
||
uni.showToast({
|
||
title: res.msg,
|
||
icon: 'none',
|
||
duration:2000
|
||
})
|
||
}
|
||
})
|
||
}else{
|
||
this.devicetime = this.getCurrentTime();
|
||
console.log(this.devicetime);
|
||
xBlufi.notifySendCustomData({
|
||
customData: '11date' + this.devicetime + ':' + '00' + ';'
|
||
})
|
||
// if(this.pre == 'WATER'){ //单阀
|
||
// this.$u.get(`/app/getModelCommand/${this.modelId}?instructionKey=syncTime`).then(res =>{
|
||
// if(res.code == 200){
|
||
// uni.showLoading({
|
||
// title: this.$t('index.setting')
|
||
// })
|
||
// xBlufi.notifySendCustomData({
|
||
// customData: '11' + this.format(res.data, {date: this.devicetime + ':' + '00' + ';'})
|
||
// })
|
||
// uni.hideLoading()
|
||
// }else{
|
||
// uni.hideLoading()
|
||
// uni.showToast({
|
||
// title: res.msg,
|
||
// icon: 'none',
|
||
// duration:2000
|
||
// })
|
||
// }
|
||
// })
|
||
}
|
||
},
|
||
// 点击显示设置时间
|
||
btnsztime(){
|
||
this.handleUserAction()
|
||
this.timeflag = true
|
||
},
|
||
getCurrentTime() {
|
||
const now = new Date();
|
||
const padZero = num => num.toString().padStart(2, '0');
|
||
const hours = padZero(now.getHours()); // 时 (00-23)
|
||
const minutes = padZero(now.getMinutes()); // 分 (00-59)
|
||
const seconds = padZero(now.getSeconds()); // 秒 (00-59)
|
||
return `${hours}:${minutes}`;
|
||
},
|
||
// 点击设置当前时间
|
||
confirmtime(e){
|
||
this.devicetime = e.hour + ':' + e.minute
|
||
console.log(this.devicetime);
|
||
if(this.vardataflag == 3){
|
||
xBlufi.notifySendCustomData({
|
||
customData: '11date' + this.devicetime + ':' + '00' + ';'
|
||
})
|
||
// this.$u.get(`/app/getModelCommand/${this.modelId}?instructionKey=syncTime`).then(res =>{
|
||
// if(res.code == 200){
|
||
// uni.showLoading({
|
||
// title: this.$t('index.setting')
|
||
// })
|
||
// xBlufi.notifySendCustomData({
|
||
// customData: '11' + this.format(res.data, {date: this.devicetime + ':' + '00' + ';'})
|
||
// })
|
||
// uni.hideLoading()
|
||
// }else{
|
||
// uni.hideLoading()
|
||
// uni.showToast({
|
||
// title: res.msg,
|
||
// icon: 'none',
|
||
// duration:2000
|
||
// })
|
||
// }
|
||
// })
|
||
}else{
|
||
uni.showLoading({
|
||
title: this.$t('index.setting')
|
||
})
|
||
this.devicetime = e.hour + ':' + e.minute
|
||
|
||
let data = {
|
||
deviceId:this.shebid,
|
||
time:this.devicetime,
|
||
|
||
}
|
||
this.$u.post(`/app/device/syncTime`,data).then(res => {
|
||
if (res.code == 200) {
|
||
uni.hideLoading()
|
||
uni.showToast({
|
||
title: this.$t('index.setOk'),
|
||
icon: 'success',
|
||
duration:2000
|
||
})
|
||
}else{
|
||
this.devicetime = '--'
|
||
uni.hideLoading()
|
||
uni.showToast({
|
||
title: res.msg,
|
||
icon: 'none',
|
||
duration:2000
|
||
})
|
||
}
|
||
})
|
||
}
|
||
},
|
||
// 雨水感应关闭
|
||
btnyushuis() {
|
||
this.handleUserAction()
|
||
if (this.isRainSensorLocked) {
|
||
this.one = false
|
||
this.ones = false
|
||
uni.showToast({
|
||
title: this.$t('index.rainWait'),
|
||
icon: 'none',
|
||
duration: 2000
|
||
})
|
||
return
|
||
}
|
||
this.one = false
|
||
this.ones = true
|
||
if(this.vardataflag != 3){
|
||
uni.showLoading({
|
||
title: this.$t('index.closing')
|
||
})
|
||
let data = {
|
||
deviceId:this.shebid,
|
||
instructionKey:'unyudi',
|
||
}
|
||
this.$u.post(`/app/device/unyudi/${this.shebid}`).then(res => {
|
||
if (res.code == 200) {
|
||
uni.hideLoading()
|
||
this.yschecked = true
|
||
uni.showToast({
|
||
title: this.$t('index.closeOk'),
|
||
icon: 'success',
|
||
duration:2000
|
||
})
|
||
}else{
|
||
uni.hideLoading()
|
||
uni.showToast({
|
||
title: res.msg,
|
||
icon: 'none',
|
||
duration:2000
|
||
})
|
||
}
|
||
})
|
||
}else{
|
||
this.$u.get(`/app/getModelCommand/${this.modelId}?instructionKey=unyudi`).then(res =>{
|
||
if(res.code == 200){
|
||
uni.showLoading({
|
||
title: this.$t('index.closing')
|
||
})
|
||
this.yschecked = true
|
||
xBlufi.notifySendCustomData({
|
||
customData: '11' + res.data
|
||
})
|
||
uni.hideLoading()
|
||
}else{
|
||
uni.hideLoading()
|
||
uni.showToast({
|
||
title: res.msg,
|
||
icon: 'none',
|
||
duration:2000
|
||
})
|
||
}
|
||
})
|
||
}
|
||
},
|
||
// 点击儿童锁开启
|
||
btnertong() {
|
||
this.handleUserAction()
|
||
this.two = false
|
||
this.twos = true
|
||
if(this.vardataflag != 3){
|
||
uni.showLoading({
|
||
title: this.$t('index.opening')
|
||
})
|
||
// let data = {
|
||
// deviceId:this.shebid,
|
||
// instructionKey:'lock',
|
||
// }
|
||
this.$u.post(`/app/device/lock/${this.shebid}`).then(res => {
|
||
if (res.code == 200) {
|
||
uni.hideLoading()
|
||
this.etchecked = false
|
||
uni.showToast({
|
||
title: this.$t('index.openOk'),
|
||
icon: 'success',
|
||
duration:2000
|
||
})
|
||
}else{
|
||
uni.hideLoading()
|
||
uni.showToast({
|
||
title: res.msg,
|
||
icon: 'none',
|
||
duration:2000
|
||
})
|
||
}
|
||
})
|
||
}else{
|
||
this.$u.get(`/app/getModelCommand/${this.modelId}?instructionKey=lock`).then(res =>{
|
||
if(res.code == 200){
|
||
uni.showLoading({
|
||
title: this.$t('index.opening')
|
||
})
|
||
this.etchecked = false
|
||
xBlufi.notifySendCustomData({
|
||
customData: '11' + res.data
|
||
})
|
||
uni.hideLoading()
|
||
}else{
|
||
uni.hideLoading()
|
||
uni.showToast({
|
||
title: res.msg,
|
||
icon: 'none',
|
||
duration:2000
|
||
})
|
||
}
|
||
})
|
||
}
|
||
},
|
||
// 点击儿童锁关闭
|
||
btnertongs() {
|
||
this.handleUserAction()
|
||
this.two = false
|
||
this.twos = true
|
||
if(this.vardataflag != 3){
|
||
uni.showLoading({
|
||
title: this.$t('index.closing')
|
||
})
|
||
// let data = {
|
||
// deviceId:this.shebid,
|
||
// instructionKey:'unlock',
|
||
// }
|
||
this.$u.post(`/app/device/unlock/${this.shebid}`).then(res => {
|
||
if (res.code == 200) {
|
||
uni.hideLoading()
|
||
this.etchecked = true
|
||
uni.showToast({
|
||
title: this.$t('index.closeOk'),
|
||
icon: 'success',
|
||
duration:2000
|
||
})
|
||
}else{
|
||
uni.hideLoading()
|
||
uni.showToast({
|
||
title: res.msg,
|
||
icon: 'none',
|
||
duration:2000
|
||
})
|
||
}
|
||
})
|
||
}else{
|
||
this.$u.get(`/app/getModelCommand/${this.modelId}?instructionKey=unlock`).then(res =>{
|
||
if(res.code == 200){
|
||
uni.showLoading({
|
||
title: this.$t('index.closing')
|
||
})
|
||
this.etchecked = true
|
||
xBlufi.notifySendCustomData({
|
||
customData: '11' + res.data
|
||
})
|
||
uni.hideLoading()
|
||
}else{
|
||
uni.hideLoading()
|
||
uni.showToast({
|
||
title: res.msg,
|
||
icon: 'none',
|
||
duration:2000
|
||
})
|
||
}
|
||
})
|
||
}
|
||
},
|
||
|
||
// 点击添加设备
|
||
btnaddsb() {
|
||
this.$u.get(`/system/user/profile`).then((res) => {
|
||
if (res.code == 200) {
|
||
uni.setStorageSync('user',res.data)
|
||
uni.setStorageSync('userId',res.data.userId)
|
||
this.vardataflag = 1
|
||
this.ver_data = {}
|
||
this.datalist = ''
|
||
uni.navigateTo({
|
||
url: '/page_user/lanya'
|
||
})
|
||
}else if(res.code == 401){
|
||
// this.jmlogin()
|
||
uni.showModal({
|
||
title: this.$t('common.tip'),
|
||
content: this.$t('common.promptLogin'),
|
||
success: function (res) {
|
||
if (res.confirm) {
|
||
uni.navigateTo({
|
||
url:'/pages/login/login'
|
||
})
|
||
} else if (res.cancel) {
|
||
|
||
}
|
||
}
|
||
})
|
||
}
|
||
})
|
||
},
|
||
|
||
// 点击选择浇水时间
|
||
confirm(e) {
|
||
this.handleUserAction()
|
||
this.minute = e.minute
|
||
this.second = e.second
|
||
this.jstime = Number(e.second) + Number(e.minute * 60)
|
||
},
|
||
|
||
// 点击跳转到定时页面
|
||
btntime() {
|
||
this.handleUserAction()
|
||
if(this.vardataflag != 3){
|
||
let url = '/page_user/dingshi?pre=' + this.pre + '&shebid=' + this.shebid
|
||
if (this.pre === 'SATER') {
|
||
url += '&channel=' + this.stareChannel
|
||
}
|
||
uni.navigateTo({ url })
|
||
}else{
|
||
if (this.pre === 'SATER') {
|
||
const saterRaw = this.saterRawPayload || this.datalist || ''
|
||
const list = encodeURIComponent(JSON.stringify(this.ver_data || {}))
|
||
const raw = encodeURIComponent(saterRaw)
|
||
uni.navigateTo({
|
||
url: '/page_user/dingshi?list=' + list + '&raw=' + raw + '&pre=' + this.pre + '&channel=' + this.stareChannel
|
||
})
|
||
return
|
||
}
|
||
uni.navigateTo({
|
||
url: '/page_user/dingshi?list=' + JSON.stringify(this.ver_data) + '&pre=' + this.pre
|
||
})
|
||
}
|
||
},
|
||
setStareChannel(ch) {
|
||
this.stareChannel = ch === 'B' ? 'B' : 'A'
|
||
if (this.pre === 'SATER') {
|
||
if (this.saterRawPayload) {
|
||
this.grisfjx()
|
||
} else if (this.vardataflag != 3 && (Object.keys(this.saterListA || {}).length || Object.keys(this.saterListB || {}).length)) {
|
||
const target = this.stareChannel === 'B' ? this.saterListB : this.saterListA
|
||
this.ver_data = this.normalizeVerDataSwitch(target) || {}
|
||
this.calculateNextWateringTime()
|
||
}
|
||
}
|
||
},
|
||
|
||
// 点击取消手动浇水
|
||
btnqx() {
|
||
this.handleUserAction()
|
||
this.sdjsflag = false
|
||
this.sdminutes = ''
|
||
this.sdseconds = ''
|
||
this.kgflag = true
|
||
this.jsked = true
|
||
this.jskeds = false
|
||
this.rainSensorLockedByWatering = false
|
||
},
|
||
// 替换
|
||
format(template, params) {
|
||
return template.replace(/\{(\w+)\}/g, (_, k) => params[k] ?? '');
|
||
},
|
||
// 手动浇水记录
|
||
getjl(){
|
||
let that = this
|
||
let data = {
|
||
deviceId:that.shebid,
|
||
sprayingTime:that.jstime
|
||
}
|
||
that.$u.post(`/app/wateringRecord`,data).then(res =>{})
|
||
},
|
||
// 请求上次浇水记录
|
||
getscjsjl(){
|
||
this.$u.get(`/app/wateringRecord/last/${this.shebid}`).then(res =>{
|
||
if(res.code == 200){
|
||
if(res.data){
|
||
this.jsobj = res.data
|
||
}else{
|
||
this.jsobj = {}
|
||
}
|
||
}
|
||
})
|
||
},
|
||
// 确定选择时间
|
||
btnqd() {
|
||
this.handleUserAction()
|
||
if (this.isManualWaterRequesting) {
|
||
uni.showToast({
|
||
title: this.$t('index.waitReq'),
|
||
icon: 'none',
|
||
duration: 2000
|
||
})
|
||
return
|
||
}
|
||
if (this.minute == '--' || this.second == '--') {
|
||
uni.showToast({
|
||
title: this.$t('index.pickWaterDur'),
|
||
icon: 'none',
|
||
duration: 3000
|
||
})
|
||
return
|
||
}
|
||
// 点击确定那一刻立即锁死雨水感应,不等请求返回
|
||
this.rainSensorLockedByWatering = true
|
||
this.isManualWaterRequesting = true
|
||
|
||
if(this.vardataflag == 3){
|
||
this.$u.get(`/app/getModelCommand/${this.modelId}?instructionKey=time`).then(res =>{
|
||
if(res.code == 200){
|
||
uni.showLoading({
|
||
title: this.$t('index.opening')
|
||
})
|
||
let cmd = this.format(res.data, {time: this.jstime})
|
||
// SATER:命令后追加 A/B,例如 timeA10
|
||
if (this.pre === 'SATER') {
|
||
cmd = String(cmd).replace(/^time/, 'time' + this.stareChannel)
|
||
}
|
||
xBlufi.notifySendCustomData({
|
||
customData: '11' + cmd + '@'
|
||
})
|
||
this.getjl()
|
||
uni.hideLoading()
|
||
this.startTimer(this.jstime)
|
||
this.sdjsflag = false
|
||
}else{
|
||
this.rainSensorLockedByWatering = false
|
||
uni.hideLoading()
|
||
uni.showToast({
|
||
title: res.msg,
|
||
icon: 'none',
|
||
duration:2000
|
||
})
|
||
}
|
||
}).catch(() => {
|
||
this.rainSensorLockedByWatering = false
|
||
uni.hideLoading()
|
||
uni.showToast({
|
||
title: this.$t('common.networkError'),
|
||
icon: 'none',
|
||
duration: 2000
|
||
})
|
||
}).finally(() => {
|
||
this.isManualWaterRequesting = false
|
||
})
|
||
}else{
|
||
|
||
let data = {
|
||
deviceId:this.shebid,
|
||
second:this.jstime,
|
||
|
||
}
|
||
this.$u.post(`/app/device/time`,data).then(res => {
|
||
if (res.code == 200) {
|
||
uni.showToast({
|
||
title: this.$t('index.openOk'),
|
||
icon: 'success',
|
||
duration:2000
|
||
})
|
||
this.getjl()
|
||
uni.hideLoading()
|
||
this.startTimer(this.jstime)
|
||
this.sdjsflag = false
|
||
}else{
|
||
this.rainSensorLockedByWatering = false
|
||
this.kgflag = true
|
||
this.jsked = false
|
||
this.jskeds = false
|
||
uni.hideLoading()
|
||
this.sdjsflag = false
|
||
uni.showToast({
|
||
title: res.msg,
|
||
icon: 'none',
|
||
duration:2000
|
||
})
|
||
}
|
||
}).catch(() => {
|
||
this.rainSensorLockedByWatering = false
|
||
uni.hideLoading()
|
||
uni.showToast({
|
||
title: this.$t('common.networkError'),
|
||
icon: 'none',
|
||
duration: 2000
|
||
})
|
||
}).finally(() => {
|
||
this.isManualWaterRequesting = false
|
||
})
|
||
}
|
||
},
|
||
|
||
// 手动浇水定时器
|
||
startTimer(totalSeconds) {
|
||
let remainingSeconds = totalSeconds
|
||
const sdminutes = ''
|
||
const sdseconds = ''
|
||
this.timer = setInterval(() => {
|
||
// 计算分钟和秒
|
||
const minutes = Math.floor(remainingSeconds / 60).toString()
|
||
const secs = (remainingSeconds % 60).toString()
|
||
// 格式化秒和分钟,确保是两位数
|
||
this.sdseconds = secs.padStart(2, '0')
|
||
this.sdminutes = (minutes > 0 ? minutes : '').padStart(2, '0') // 如果分钟为0,则不显示分钟
|
||
// 更新剩余秒数
|
||
remainingSeconds--
|
||
// 如果剩余秒数为0,则停止定时器
|
||
if (remainingSeconds <= 0) {
|
||
clearInterval(this.timer)
|
||
// 定时结束:清空计时、自动关闭浇水开关、解锁雨水感应
|
||
setTimeout(()=>{
|
||
this.sdminutes = ''
|
||
this.sdseconds = ''
|
||
this.kgflag = true
|
||
this.jsked = true
|
||
this.jskeds = false
|
||
this.rainSensorLockedByWatering = false
|
||
},1000)
|
||
}
|
||
}, 1000)
|
||
},
|
||
|
||
// 启动浇水倒计时定时器
|
||
startWateringCountdownTimer(totalSeconds) {
|
||
// 启动新倒计时前,先清理旧的 interval 和延迟清理 timeout,避免出现负数/串台
|
||
this.clearWateringCountdownTimer()
|
||
|
||
let remainingSeconds = Math.max(0, Math.floor(Number(totalSeconds) || 0))
|
||
if (remainingSeconds <= 0) {
|
||
this.wateringCountdownMinutes = ''
|
||
this.wateringCountdownSeconds = ''
|
||
this.showWateringCountdown = false
|
||
return
|
||
}
|
||
|
||
const updateDisplay = (secsLeft) => {
|
||
const safe = Math.max(0, Math.floor(Number(secsLeft) || 0))
|
||
const minutes = Math.floor(safe / 60).toString()
|
||
const secs = (safe % 60).toString()
|
||
this.wateringCountdownSeconds = secs.padStart(2, '0')
|
||
this.wateringCountdownMinutes = (minutes > 0 ? minutes : '').padStart(2, '0')
|
||
}
|
||
|
||
// 先设置初始值
|
||
updateDisplay(remainingSeconds)
|
||
|
||
this.wateringCountdownTimer = setInterval(() => {
|
||
// 先减少再渲染,避免“多显示1秒”,并确保永不小于0
|
||
remainingSeconds = Math.max(0, remainingSeconds - 1)
|
||
updateDisplay(remainingSeconds)
|
||
|
||
// 到0立刻停止,且用受控的 timeout 延迟清理展示(避免负数和旧 timeout 影响新倒计时)
|
||
if (remainingSeconds === 0) {
|
||
clearInterval(this.wateringCountdownTimer)
|
||
this.wateringCountdownTimer = null
|
||
this.wateringCountdownResetTimer = setTimeout(() => {
|
||
this.wateringCountdownMinutes = ''
|
||
this.wateringCountdownSeconds = ''
|
||
this.showWateringCountdown = false
|
||
this.rainSensorLockedByWatering = false
|
||
this.wateringCountdownResetTimer = null
|
||
}, 1000)
|
||
}
|
||
}, 1000)
|
||
},
|
||
|
||
// 清除浇水倒计时定时器
|
||
clearWateringCountdownTimer() {
|
||
if (this.wateringCountdownResetTimer) {
|
||
clearTimeout(this.wateringCountdownResetTimer)
|
||
this.wateringCountdownResetTimer = null
|
||
}
|
||
if (this.wateringCountdownTimer) {
|
||
clearInterval(this.wateringCountdownTimer)
|
||
this.wateringCountdownTimer = null
|
||
}
|
||
},
|
||
|
||
// 计算信号强度格数(根据RSSI值)
|
||
calculateSignalStrength(rssi) {
|
||
if (rssi === undefined || rssi === null) {
|
||
return 0
|
||
}
|
||
// 转换为数字
|
||
const rssiValue = Number(rssi)
|
||
// 0 到 -50:4格(最强)
|
||
if (rssiValue >= -50) {
|
||
return 4
|
||
}
|
||
// -50 到 -70:3格
|
||
if (rssiValue >= -70) {
|
||
return 3
|
||
}
|
||
// -70 到 -90:2格
|
||
if (rssiValue >= -90) {
|
||
return 2
|
||
}
|
||
// -90 以下:1格
|
||
return 1
|
||
},
|
||
|
||
// 关闭手动浇水
|
||
btnkq() {
|
||
this.handleUserAction()
|
||
// 防频繁点击(包含“确定”请求进行中、开关请求进行中)
|
||
// if (this.isManualWaterRequesting || this.isManualWaterSwitchRequesting) {
|
||
// // 回滚开关状态(当前是“关闭”动作,阻止时保持开启)
|
||
// this.jsked = true
|
||
// uni.showToast({
|
||
// title: this.$t('index.noTap'),
|
||
// icon: 'none',
|
||
// duration: 2000
|
||
// })
|
||
// return
|
||
// }
|
||
console.log('关闭')
|
||
// 蓝牙模式:每次点击后至少冷却3秒
|
||
if (this.vardataflag == 3) {
|
||
const now = Date.now()
|
||
if (now < this.manualWaterBtCooldownUntil) {
|
||
this.jsked = true
|
||
uni.showToast({
|
||
title: this.$t('index.noTap'),
|
||
icon: 'none',
|
||
duration: 2000
|
||
})
|
||
return
|
||
}
|
||
this.manualWaterBtCooldownUntil = now + 3000
|
||
this.isManualWaterSwitchRequesting = true
|
||
const unlockAt = this.manualWaterBtCooldownUntil
|
||
|
||
this.$u.get(`/app/getModelCommand/${this.modelId}?instructionKey=close`).then(res =>{
|
||
if(res.code == 200){
|
||
uni.showLoading({
|
||
title: this.$t('index.closing')
|
||
})
|
||
xBlufi.notifySendCustomData({
|
||
customData: "11" + res.data
|
||
})
|
||
clearInterval(this.timer)
|
||
this.sdminutes = ''
|
||
this.sdseconds = ''
|
||
this.kgflag = true
|
||
this.jsked = true
|
||
this.jskeds = false
|
||
this.rainSensorLockedByWatering = false
|
||
uni.hideLoading()
|
||
}else{
|
||
// 回滚开关(关闭失败则保持开启)
|
||
this.jsked = true
|
||
uni.hideLoading()
|
||
uni.showToast({
|
||
title: res.msg,
|
||
icon: 'none',
|
||
duration:2000
|
||
})
|
||
}
|
||
}).catch(() => {
|
||
// 回滚开关(关闭失败则保持开启)
|
||
this.jsked = true
|
||
uni.hideLoading()
|
||
uni.showToast({
|
||
title: this.$t('common.networkError'),
|
||
icon: 'none',
|
||
duration: 2000
|
||
})
|
||
}).finally(() => {
|
||
const delay = Math.max(0, unlockAt - Date.now())
|
||
setTimeout(() => {
|
||
this.isManualWaterSwitchRequesting = false
|
||
}, delay)
|
||
})
|
||
return
|
||
}
|
||
|
||
// 接口模式:等待请求结束后才能再次点击
|
||
this.isManualWaterSwitchRequesting = true
|
||
if(this.vardataflag != 3){
|
||
uni.showLoading({
|
||
title: this.$t('index.closing')
|
||
})
|
||
// let data = {
|
||
// deviceId:this.shebid,
|
||
// instructionKey:'close',
|
||
// }
|
||
this.$u.post(`/app/device/close/${this.shebid}`).then(res => {
|
||
if (res.code == 200) {
|
||
uni.showToast({
|
||
title: this.$t('index.closeOk'),
|
||
icon: 'success',
|
||
duration:2000
|
||
})
|
||
clearInterval(this.timer)
|
||
uni.hideLoading()
|
||
this.sdminutes = ''
|
||
this.sdseconds = ''
|
||
this.kgflag = true
|
||
this.jsked = true
|
||
this.jskeds = false
|
||
this.rainSensorLockedByWatering = false
|
||
}else{
|
||
// 回滚开关(关闭失败则保持开启)
|
||
this.jsked = true
|
||
uni.hideLoading()
|
||
uni.showToast({
|
||
title: res.msg,
|
||
icon: 'none',
|
||
duration:2000
|
||
})
|
||
}
|
||
}).catch(() => {
|
||
// 回滚开关(关闭失败则保持开启)
|
||
this.jsked = true
|
||
uni.hideLoading()
|
||
uni.showToast({
|
||
title: this.$t('common.networkError'),
|
||
icon: 'none',
|
||
duration: 2000
|
||
})
|
||
}).finally(() => {
|
||
this.isManualWaterSwitchRequesting = false
|
||
})
|
||
}
|
||
},
|
||
// 开启手动浇水
|
||
btngb() {
|
||
this.handleUserAction()
|
||
// 防频繁点击(包含“确定”请求进行中、开关请求进行中)
|
||
if (this.isManualWaterRequesting || this.isManualWaterSwitchRequesting) {
|
||
// 回滚开关状态(当前是“开启”动作,阻止时保持关闭)
|
||
this.jskeds = false
|
||
uni.showToast({
|
||
title: this.$t('index.noTap'),
|
||
icon: 'none',
|
||
duration: 2000
|
||
})
|
||
return
|
||
}
|
||
// 蓝牙模式:每次点击后至少冷却3秒
|
||
if (this.vardataflag == 3) {
|
||
const now = Date.now()
|
||
if (now < this.manualWaterBtCooldownUntil) {
|
||
this.jskeds = false
|
||
uni.showToast({
|
||
title: this.$t('index.noTap'),
|
||
icon: 'none',
|
||
duration: 2000
|
||
})
|
||
return
|
||
}
|
||
this.manualWaterBtCooldownUntil = now + 3000
|
||
this.isManualWaterSwitchRequesting = true
|
||
const unlockAt = this.manualWaterBtCooldownUntil
|
||
setTimeout(() => {
|
||
// 到时间自动解锁
|
||
if (Date.now() >= unlockAt) this.isManualWaterSwitchRequesting = false
|
||
}, 3000)
|
||
}
|
||
console.log('开启')
|
||
this.sdjsflag = true
|
||
this.kgflag = false
|
||
},
|
||
|
||
// 跳转到设置
|
||
btnsz() {
|
||
xBlufi.listenDeviceMsgEvent(false, this.funListenDeviceMsgEvent)
|
||
if(this.csbobj.day){
|
||
this.xctime = '111'
|
||
}
|
||
uni.navigateTo({
|
||
url: '/page_user/upload?deviceid=' + this.shebid + '&ver=' + this.ver + '&xctime=' + this.xctime + '&xipin=' + this.xinp + '&img=' + this.imgpic + '&varflag=' + this.vardataflag
|
||
})
|
||
},
|
||
// 选择设备
|
||
btnactive(deviceId,index) {
|
||
// wx.closeBLEConnection({
|
||
// deviceId: this.mac,
|
||
// })
|
||
// xBlufi.notifyConnectBle({
|
||
// isStart: false,
|
||
// deviceId: this.deviceId,
|
||
// name: this.name
|
||
// })
|
||
this.isLoading = true
|
||
this.shebid = deviceId
|
||
this.zaixianobj = {}
|
||
this.ver = ''
|
||
this.jsobj = {}
|
||
this.btnxuanze()
|
||
},
|
||
// 选择确定设备 shebid
|
||
btnxuanze() {
|
||
this.$u.put(`/app/toggleDevice?userId=${this.userobj.userId}&deviceId=${this.shebid}`).then(res => {
|
||
if(res.code == 200){
|
||
|
||
this.csbobj = {}
|
||
this.datalist = ''
|
||
this.mac = ''
|
||
this.ver_data = {}
|
||
this.name = ''
|
||
this.deviceId = ''
|
||
this.xuanzeflag = false
|
||
this.vardataflag = 1
|
||
this.sdseconds = ''
|
||
this.sdminutes = ''
|
||
this.kgflag = true
|
||
this.jsked = true
|
||
this.jskeds = false
|
||
this.getshebxq()
|
||
}else{
|
||
this.isLoading = false
|
||
uni.showToast({
|
||
title: res.msg,
|
||
icon: 'none',
|
||
duration: 2000
|
||
})
|
||
}
|
||
})
|
||
},
|
||
// 刷新按钮点击(避免编译成 e0 等匿名方法导致小程序报错)
|
||
onRefreshClick() {
|
||
if (this.isRefreshing) return
|
||
this.qiehuan = 1
|
||
this.getshebxq()
|
||
},
|
||
// 获取设备信息
|
||
getshebxq(){
|
||
if (this.isRefreshing) return
|
||
this.isRefreshing = true
|
||
const done = () => {
|
||
setTimeout(() => {
|
||
this.isLoading = false
|
||
this.isRefreshing = false
|
||
if(this.qiehuan == 1){
|
||
uni.showToast({
|
||
title: this.$t('index.refreshOk'),
|
||
icon: 'success',
|
||
duration:5000
|
||
})
|
||
}else{
|
||
uni.showToast({
|
||
title: this.$t('index.switchOk'),
|
||
icon: 'success',
|
||
duration: 5000
|
||
})
|
||
this.fullPageRefresh()
|
||
}
|
||
this.qiehuan = ''
|
||
}, 500)
|
||
}
|
||
this.$u.get(`/app/device/getDetail?deviceId=${this.shebid}`).then(res => {
|
||
if (res.code == 200) {
|
||
this.zaixianobj = res.data.iotData
|
||
this.modelId = res.data.modelId
|
||
console.log(this.vardataflag,this.zaixianobj);
|
||
// 非蓝牙模式时,用后台数据组装渲染结构
|
||
if((this.pre === 'WATER' || this.pre === 'SATER') && this.vardataflag != 3 || this.pre === 'TATER' && this.vardataflag != 3 || this.pre === 'DATER' && this.vardataflag != 3){
|
||
this.ver_data = this.buildVerDataFromBackend(this.zaixianobj)
|
||
this.calculateNextWateringTime()
|
||
}
|
||
if(this.pre === 'SMSJ:' && this.vardataflag != 3){
|
||
this.ver_data = this.buildVerDataFromBackend(this.zaixianobj)
|
||
// this.ver_data = this.zaixianobj
|
||
console.log(this.ver_data,'55555555555prprprprprprprpr');
|
||
}
|
||
if (res.data && res.data.iotData && res.data.iotData.xinp !== undefined) {
|
||
this.xinp = res.data.iotData.xinp.value == undefined ? '--' : res.data.iotData.xinp.value
|
||
}
|
||
if (res.data && res.data.iotData && res.data.iotData.yudi !== undefined) {
|
||
if (!this.isRainSensorLocked) {
|
||
this.yschecked = res.data.iotData.yudi.value == 0 ? true : false
|
||
}
|
||
}
|
||
if (res.data && res.data.iotData && res.data.iotData.lock !== undefined) {
|
||
this.etchecked = res.data.iotData.lock.value == 0 ? true : false
|
||
}
|
||
// 优先从 nt.value 获取总秒数,如果没有则从 time.value 获取
|
||
let ntValue = res?.data?.iotData?.nt?.value;
|
||
if (ntValue !== undefined && ntValue !== null && ntValue !== '') {
|
||
// 从总秒数转换为时间格式
|
||
this.devicetime = this.secondsToTime(ntValue);
|
||
} else {
|
||
// 兼容旧数据,从 time.value 获取
|
||
let originalValue = (res?.data?.iotData?.time?.value ?? "").toString().trim();
|
||
// 去掉首尾可能多出来的英文双引号 "
|
||
originalValue = originalValue.replace(/^"|"$/g, '');
|
||
this.devicetime = originalValue || "--";
|
||
}
|
||
console.log('devicetime:', this.devicetime);
|
||
// 获取信号强度
|
||
if (res.data && res.data.iotData && res.data.iotData.RSSI !== undefined && res.data.iotData.RSSI.value !== undefined) {
|
||
this.signalStrength = this.calculateSignalStrength(res.data.iotData.RSSI.value)
|
||
} else {
|
||
this.signalStrength = 0
|
||
}
|
||
// 取值
|
||
this.onlineTime = res.data.onlineTime
|
||
this.onlineStatus = res.data.onlineStatus
|
||
if(res.data.iotData && res.data.iotData.bat !== undefined && res.data.iotData.bat.value !== undefined){
|
||
this.dianya = res.data.iotData.bat.value
|
||
this.getdianliang()
|
||
}
|
||
}
|
||
done()
|
||
}).catch(() => {
|
||
this.isLoading = false
|
||
done()
|
||
})
|
||
},
|
||
|
||
// 点击切换设备
|
||
btnksxz() {
|
||
if (this.xuanzeflag == true) {
|
||
this.xuanzeflag = false
|
||
} else {
|
||
this.xuanzeflag = true
|
||
}
|
||
},
|
||
//跳转到浇水日志页
|
||
btnjs() {
|
||
uni.showToast({
|
||
title: this.$t('index.featureSoon'),
|
||
icon: 'none',
|
||
duration:2000
|
||
})
|
||
// uni.navigateTo({
|
||
// url: '/page_user/jiaoshui'
|
||
// })
|
||
},
|
||
// 每隔一时间发送一次获取数据
|
||
getshuju() {
|
||
const that = this
|
||
that.intervalId = setInterval(() => {
|
||
if (that.vardataflag == 3) {
|
||
that.datalist = ''
|
||
xBlufi.notifySendCustomData({
|
||
customData: "11get"
|
||
})
|
||
}
|
||
}, 5000)
|
||
},
|
||
// 清除定时器
|
||
clearTimer() {
|
||
if (this.intervalId) {
|
||
clearInterval(this.intervalId);
|
||
this.intervalId = null; // 重置定时器 ID
|
||
console.log("定时器已清除");
|
||
}
|
||
},
|
||
// 无闪屏整页刷新:重置状态并重新拉数,替代 uni.reLaunch 避免白屏闪一下
|
||
fullPageRefresh() {
|
||
// 1. 清除所有定时器
|
||
this.clearTimer()
|
||
this.clearConnectionTimeout()
|
||
this.clearDataTimeoutTimer()
|
||
if (this.disconnectTimer) {
|
||
clearTimeout(this.disconnectTimer)
|
||
this.disconnectTimer = null
|
||
}
|
||
if (this.searchTimer) {
|
||
clearTimeout(this.searchTimer)
|
||
this.searchTimer = null
|
||
}
|
||
if (this.wateringCountdownTimer) {
|
||
clearInterval(this.wateringCountdownTimer)
|
||
this.wateringCountdownTimer = null
|
||
}
|
||
if (this.wateringCountdownResetTimer) {
|
||
clearTimeout(this.wateringCountdownResetTimer)
|
||
this.wateringCountdownResetTimer = null
|
||
}
|
||
if (this.timer) {
|
||
clearInterval(this.timer)
|
||
this.timer = null
|
||
}
|
||
// 2. 关闭可能打开的弹窗
|
||
this.showErrorModal = false
|
||
this.xuanzeflag = false
|
||
// 3. 重置 data 到初始状态(不销毁页面,避免闪屏)
|
||
const initial = this.$options.data ? this.$options.data.call(this) : {}
|
||
Object.keys(initial).forEach(key => {
|
||
this[key] = initial[key]
|
||
})
|
||
// 4. 重新初始化并拉取数据(相当于重新进入页面)
|
||
xBlufi.initXBlufi(1)
|
||
this.getinfo()
|
||
this.getshuju()
|
||
},
|
||
// 处理抽水泵接收数据
|
||
getcsbshuju() {
|
||
// 1. 获取原始数据
|
||
const inputString = this.datalist;
|
||
// 2. 先处理分号(替换为逗号)
|
||
const normalizedString = inputString.replace(/;/g, ',');
|
||
// 3. 分割版本信息和数据部分
|
||
const parts = normalizedString.split(':');
|
||
console.log('分割结果:', parts); // 应该输出 ["ver3@p_set0", "11,5,603,99,19,9,0,"]
|
||
// 4. 处理数据部分(移除末尾可能的多余逗号)
|
||
let dataPart = parts[1];
|
||
if (dataPart.endsWith(',')) {
|
||
dataPart = dataPart.slice(0, -1);
|
||
}
|
||
// 5. 重新组合为期望的格式
|
||
const result = [parts[0], dataPart];
|
||
console.log('最终结果:', result); // ["ver3@p_set0", "11,5,603,99,19,9,0"]
|
||
const versionNumber = result[0].match(/ver(\d+)/)?.[1];
|
||
const ver = versionNumber ? parseInt(versionNumber) : 0;
|
||
this.ver = 'ver' + ver
|
||
|
||
console.log(this.ver,'ververver');
|
||
// 6. 继续您原来的处理逻辑
|
||
const timeParts = dataPart.split(',');
|
||
console.log(timeParts,'timePartstimePartstimePartstimePartstimePartstimePartstimePartstimePartstimePartstimePartstimePartstimePartstimePartstimePartstimePartstimePartstimePartstimePartstimeParts');
|
||
this.dianya = timeParts[timeParts.length - 1]
|
||
this.getdianliang()
|
||
this.csbobj = {
|
||
hour: String(timeParts[0]).padStart(2, '0'),
|
||
minute: String(timeParts[1]).padStart(2, '0'),
|
||
second: timeParts[2],
|
||
day: timeParts[3]
|
||
};
|
||
console.log(this.csbobj,'4564654654584654564654654654654654621654652165');
|
||
const hours = parseInt(timeParts[4]) || 0;
|
||
const minutes = parseInt(timeParts[5]) || 0;
|
||
this.devicetime = `${String(hours).padStart(2, '0')}:${String(minutes).padStart(2, '0')}`;
|
||
this.ver_data = {...this.csbobj};
|
||
// 生成当前时间字符串 yyyy-MM-dd HH:mm:ss
|
||
const now = new Date();
|
||
const yyyy = now.getFullYear();
|
||
const MM = String(now.getMonth() + 1).padStart(2, '0');
|
||
const dd = String(now.getDate()).padStart(2, '0');
|
||
const HH = String(now.getHours()).padStart(2, '0');
|
||
const mm = String(now.getMinutes()).padStart(2, '0');
|
||
const ss = String(now.getSeconds()).padStart(2, '0');
|
||
const at = `${yyyy}-${MM}-${dd} ${HH}:${mm}:${ss}`;
|
||
// 仅在已连接(vardataflag==3)且距离上次上传≥60秒时推送
|
||
console.log(this.ver_data);
|
||
if(this.vardataflag == 3){
|
||
const nowTs = Date.now();
|
||
if(!this.lastUploadTs || (nowTs - this.lastUploadTs) >= 2000){
|
||
let data = {
|
||
mac: this.mac,
|
||
params: {
|
||
h: {
|
||
value: String(this.ver_data.hour),
|
||
at
|
||
},
|
||
m: {
|
||
value: String(this.ver_data.minute),
|
||
at
|
||
},
|
||
t: {
|
||
value: String(this.ver_data.second),
|
||
at
|
||
},
|
||
d: {
|
||
value: String(this.ver_data.day),
|
||
at
|
||
},
|
||
time: {
|
||
value: String(this.devicetime),
|
||
at
|
||
},
|
||
nt:{
|
||
value: String(this.timeToSeconds(this.devicetime)),
|
||
at
|
||
}
|
||
}
|
||
}
|
||
data.params.xinp = { value: String(this.xinp), at }
|
||
this.$u.put(`/app/updateDeviceParam`, data).then(res => {}) //上传数据
|
||
this.lastUploadTs = nowTs;
|
||
}
|
||
}
|
||
console.log('设备时间:', this.devicetime);
|
||
},
|
||
// 获取电量
|
||
getdianliang(){
|
||
const nowTs = Date.now();
|
||
if(this.lastPowerTs && (nowTs - this.lastPowerTs) < 20000){
|
||
return
|
||
}
|
||
this.$u.get(`/app/device/remainingPower/${this.shebid}?voltage=${this.dianya / 1000}`).then(res => {
|
||
if (res.code == 200) {
|
||
this.sydl = res.data
|
||
this.lastPowerTs = nowTs
|
||
}
|
||
})
|
||
},
|
||
// 处理双阀的浇花器设备
|
||
grisfjx(){
|
||
const raw = String(this.saterRawPayload || this.datalist || '')
|
||
if (!raw) return
|
||
let source = raw.replace(/@/g, ';')
|
||
if (!source.endsWith(';')) source += ';'
|
||
const channel = this.stareChannel === 'B' ? 'B' : 'A'
|
||
const channelMap = {}
|
||
const pRegex = /p([AB])(\d+):([^;]+)/g
|
||
let pMatch
|
||
while ((pMatch = pRegex.exec(source)) !== null) {
|
||
const ch = pMatch[1]
|
||
const idx = Number(pMatch[2])
|
||
const val = pMatch[3]
|
||
if (ch === channel && idx >= 1 && idx <= 6) {
|
||
channelMap[idx] = val
|
||
}
|
||
}
|
||
const showMatch = source.match(/show:([^;]+)/)
|
||
const showPair = showMatch ? `show:${showMatch[1]}` : ''
|
||
const mergedPairs = []
|
||
if (showPair) {
|
||
mergedPairs.push(showPair)
|
||
}
|
||
for (let i = 1; i <= 6; i++) {
|
||
const pickedValue = channelMap[i] || '0,0,0,0,0'
|
||
mergedPairs.push(`p_set${i}:${pickedValue}`)
|
||
}
|
||
const rebuilt = `${mergedPairs.join(';')};`
|
||
this.datalist = rebuilt
|
||
// 复用已有解析流程(开关/倒计时/电量/上传等)
|
||
this.getchuli()
|
||
},
|
||
|
||
// 处理从设备接收数据
|
||
getchuli() {
|
||
const inputString = this.datalist;
|
||
// console.log('接收到的数据:', inputString);
|
||
// 检查是否有 @ 分隔符
|
||
const hasAtSymbol = inputString.includes('@')
|
||
let processedString
|
||
let currentDay = 0
|
||
if (hasAtSymbol) {
|
||
// 只分割第一个 @,保留后面的所有内容
|
||
const firstAtIndex = inputString.indexOf('@')
|
||
this.ver = inputString.substring(0, firstAtIndex) || ''
|
||
processedString = inputString.substring(firstAtIndex + 1) || ''
|
||
// 解析当前天数
|
||
const showMatch = processedString.match(/show:([^;]+)/)
|
||
if (showMatch) {
|
||
const showValues = showMatch[1].split(',');
|
||
currentDay = parseInt(showValues[showValues.length - 2]) || 0
|
||
}
|
||
} else {
|
||
processedString = inputString;
|
||
this.ver = '';
|
||
}
|
||
if(this.ver.length > 6){
|
||
this.ver = this.ver.slice(0,6)
|
||
}
|
||
console.log(this.ver,'ververver');
|
||
const version = this.ver;
|
||
// 使用正则表达式提取数字部分
|
||
const versionNumber = version.match(/\d+/); // 匹配连续的数字
|
||
|
||
// 检查并删除 @low:0 或 low:0
|
||
if (processedString.includes('@low:0') || processedString.includes('low:0')) {
|
||
// 删除 @low:0(可能在开头或中间)
|
||
processedString = processedString.replace(/@low:0@?/g, '');
|
||
// 删除 low:0@(在开头,后面跟着@)
|
||
processedString = processedString.replace(/^low:0@/g, '');
|
||
// 删除 low:0(处理各种位置的情况)
|
||
processedString = processedString.replace(/^low:0;?/g, ''); // 开头
|
||
processedString = processedString.replace(/;low:0;?/g, ';'); // 中间
|
||
processedString = processedString.replace(/;low:0$/g, ''); // 结尾
|
||
processedString = processedString.replace(/^low:0$/g, ''); // 只有 low:0
|
||
// 清理可能出现的连续分号或开头结尾的分号
|
||
processedString = processedString.replace(/;;+/g, ';').replace(/^;+|;+$/g, '');
|
||
}
|
||
|
||
const pairs = processedString.split(';').filter(Boolean)
|
||
console.log('分割后的数据对:', pairs)
|
||
const showObject = {}
|
||
const pSetObjects = {}
|
||
// 初始化所有p_set为默认值
|
||
for (let i = 1; i <= 6; i++) {
|
||
pSetObjects[`p_set${i}`] = [0, 0, 0, 0, 0]
|
||
}
|
||
// 解析字符串
|
||
pairs.forEach(pair => {
|
||
const [key, value] = pair.split(':')
|
||
console.log(key,'555555555555555555555555')
|
||
if (key === 'show') {
|
||
showObject.showArray = value.split(',').map(Number)
|
||
} else if (key.startsWith('p_set')) {
|
||
const values = value.split(',').map(Number)
|
||
// 确保数组长度为5
|
||
while (values.length < 5) {
|
||
values.push(0)
|
||
}
|
||
pSetObjects[key] = values
|
||
}
|
||
})
|
||
this.ver_data = pSetObjects
|
||
this.jstimeobj = pSetObjects
|
||
this.showobj = showObject
|
||
|
||
const showArray = (this.showobj && Array.isArray(this.showobj.showArray)) ? this.showobj.showArray : []
|
||
console.log(showArray,'this.showobj.showArraythis.showobj.showArraythis.showobj.showArray')
|
||
// 设置开关状态(浇水进行中或已点确定未结束时,不更新雨水感应状态)
|
||
if(showArray.length >= 2){
|
||
if (!this.isRainSensorLocked) {
|
||
this.yschecked = showArray[1] == 0 ? true : false
|
||
}
|
||
this.etchecked = showArray[0] == 0 ? true : false
|
||
}
|
||
// 获取浇水倒计时(第五位,索引为4),前提是第三位(索引2)和第四位(索引3)都必须是1
|
||
if(showArray.length > 4){
|
||
// 检查第三位(索引2)和第四位(索引3)是否都为1
|
||
const thirdValue = showArray[2]
|
||
const fourthValue = showArray[3]
|
||
if(thirdValue === 1 && fourthValue === 1){
|
||
// 第三位和第四位都是1,才取第五位的值
|
||
const countdown = showArray[4]
|
||
if(countdown && countdown > 0){
|
||
// 清除之前的倒计时定时器
|
||
this.clearWateringCountdownTimer()
|
||
// 仅更新设备倒计时显示,不改变浇水开关状态(仅用户点击才改变)
|
||
this.showWateringCountdown = true
|
||
// 启动倒计时
|
||
this.startWateringCountdownTimer(countdown)
|
||
} else {
|
||
// 倒计时为0,清除定时器与显示,不改变浇水开关状态
|
||
this.clearWateringCountdownTimer()
|
||
this.wateringCountdownMinutes = ''
|
||
this.wateringCountdownSeconds = ''
|
||
this.showWateringCountdown = false
|
||
}
|
||
} else {
|
||
// 第三位或第四位不是1,清除定时器与显示,不改变浇水开关状态
|
||
this.clearWateringCountdownTimer()
|
||
this.wateringCountdownMinutes = ''
|
||
this.wateringCountdownSeconds = ''
|
||
this.showWateringCountdown = false
|
||
}
|
||
} else {
|
||
// 数组长度不够,清除定时器与显示,不改变浇水开关状态
|
||
this.clearWateringCountdownTimer()
|
||
this.wateringCountdownMinutes = ''
|
||
this.wateringCountdownSeconds = ''
|
||
this.showWateringCountdown = false
|
||
}
|
||
this.dianya = showArray.length ? showArray[showArray.length - 1] : 0
|
||
console.log('this.yscheckedthis.yschecked',this.yschecked,this.etchecked)
|
||
this.getdianliang()
|
||
this.calculateNextWateringTime(currentDay)
|
||
console.log('解析后的数据:', {
|
||
ver_data: this.ver_data,
|
||
showobj: this.showobj,
|
||
nextTime: this.xctimesj,
|
||
nextTimeDiff: this.xctime,
|
||
prevTime: this.sctimejs
|
||
});
|
||
if(showArray.length > 9){
|
||
this.xinp = showArray[showArray.length - 2]
|
||
this.dianya = showArray[showArray.length - 1]
|
||
}else{
|
||
this.xinp = showArray.length ? showArray[showArray.length - 1] : 0
|
||
this.dianya = 0
|
||
}
|
||
console.log('息屏',this.xinp,'电压',this.dianya);
|
||
this.getdianliang()
|
||
|
||
// 非 WATER 情况也按 { value, at } 结构上传(大量字段统一包装)
|
||
if(this.vardataflag == 3){
|
||
const now = new Date();
|
||
const yyyy = now.getFullYear();
|
||
const MM = String(now.getMonth() + 1).padStart(2, '0');
|
||
const dd = String(now.getDate()).padStart(2, '0');
|
||
const HH = String(now.getHours()).padStart(2, '0');
|
||
const mm = String(now.getMinutes()).padStart(2, '0');
|
||
const ss = String(now.getSeconds()).padStart(2, '0');
|
||
const at = `${yyyy}-${MM}-${dd} ${HH}:${mm}:${ss}`;
|
||
const nowTs = Date.now();
|
||
if(!this.lastUploadTs || (nowTs - this.lastUploadTs) >= 2000){
|
||
const wrapParams = (obj) => {
|
||
const out = {};
|
||
if(!obj || typeof obj !== 'object') return out;
|
||
Object.keys(obj).forEach((key) => {
|
||
const raw = obj[key];
|
||
const value = (raw && typeof raw === 'object' && 'value' in raw) ? raw.value : raw;
|
||
out[key] = { value: String(value == null ? '' : value), at };
|
||
});
|
||
return out;
|
||
}
|
||
// 当后台参数对象为空时,从 BLE 解析的 ver_data 构造 h1/m1/s1/o1/d1 ...
|
||
const buildParamsFromState = () => {
|
||
const params = {}
|
||
if(this.ver_data && typeof this.ver_data === 'object'){
|
||
for(let i = 1; i <= 6; i++){
|
||
const arr = this.ver_data[`p_set${i}`]
|
||
if(Array.isArray(arr) && arr.length >= 5){
|
||
params[`h${i}`] = arr[0]
|
||
params[`m${i}`] = arr[1]
|
||
params[`s${i}`] = arr[2]
|
||
params[`o${i}`] = arr[3]
|
||
params[`d${i}`] = arr[4]
|
||
}
|
||
}
|
||
}
|
||
return params
|
||
}
|
||
// SATER:从 saterDataByType 构造 pA、pB 上传参数
|
||
const buildSaterParamsFromState = () => {
|
||
const params = {}
|
||
const { pA, pB } = this.saterDataByType || {}
|
||
if (pA) params.pA = pA.replace(/;+$/, '')
|
||
if (pB) params.pB = pB.replace(/;+$/, '')
|
||
return params
|
||
}
|
||
// 在 BLE 已连接场景,优先使用当前解析的 ver_data(p_set1..p_set6)
|
||
const hasVerData = (() => {
|
||
if(!this.ver_data || typeof this.ver_data !== 'object') return false;
|
||
for(let i = 1; i <= 6; i++){
|
||
const arr = this.ver_data[`p_set${i}`]
|
||
if(Array.isArray(arr) && arr.length >= 2 && (arr[0] > 0 || arr[1] > 0 || arr[2] > 0 || arr[3] > 0)){
|
||
return true
|
||
}
|
||
}
|
||
return false
|
||
})()
|
||
const hasSaterData = this.pre === 'SATER' && this.saterDataByType && (this.saterDataByType.pA || this.saterDataByType.pB)
|
||
const yiStr = String(showArray[6] == null ? 0 : showArray[6])
|
||
const erStr = String(showArray[7] == null ? 0 : showArray[7])
|
||
// console.log('22222222222222222222222222222222222222222222222222222',yiStr,erStr);
|
||
// 判断字符串长度是否为1(而不是<10)
|
||
let yi = yiStr.length === 1 ? '0' + yiStr : yiStr
|
||
let er = erStr.length === 1 ? '0' + erStr : erStr
|
||
// console.log('33333333333333333333333333333333333333333333333333',yi,er);
|
||
this.devicetime = yi + ':' + er
|
||
const hasBackend = this.zaixianobj && Object.keys(this.zaixianobj).length > 0
|
||
const chosenParams = hasSaterData ? buildSaterParamsFromState() : (hasVerData ? buildParamsFromState() : (hasBackend ? this.zaixianobj : {}))
|
||
// 合并额外状态字段:仅保留 yudi(雨水感应)、lock(儿童锁),
|
||
// 按你的要求直接使用布尔:开启=false,关闭=true
|
||
const mergedParams = { ...chosenParams }
|
||
mergedParams.yudi = this.yschecked ? 0 : 1
|
||
mergedParams.lock = this.etchecked ? 0 : 1
|
||
mergedParams.time = this.devicetime
|
||
mergedParams.bat = this.dianya
|
||
mergedParams.nt = { value: String(this.timeToSeconds(this.devicetime)),at }
|
||
let data = {
|
||
mac: this.mac,
|
||
params: wrapParams(mergedParams)
|
||
}
|
||
data.params.xinp = { value: String(this.xinp), at }
|
||
this.$u.put(`/app/updateDeviceParam`, data).then(res => {})
|
||
this.lastUploadTs = nowTs;
|
||
}
|
||
}
|
||
},
|
||
// 初始化断开蓝牙定时器
|
||
initDisconnectTimer() {
|
||
this.clearDisconnectTimer()
|
||
this.disconnectTimer = setTimeout(() => {
|
||
|
||
}, 600000)
|
||
},
|
||
// 重置断开蓝牙定时器
|
||
resetDisconnectTimer() {
|
||
this.initDisconnectTimer()
|
||
},
|
||
// 清除断开蓝牙定时器
|
||
clearDisconnectTimer() {
|
||
if(this.disconnectTimer) {
|
||
clearTimeout(this.disconnectTimer)
|
||
this.disconnectTimer = null
|
||
}
|
||
},
|
||
// 断开蓝牙连接
|
||
disconnectBluetooth() {
|
||
if(this.deviceId) {
|
||
wx.closeBLEConnection({
|
||
deviceId: this.deviceId,
|
||
success: () => {
|
||
console.log('蓝牙连接已断开')
|
||
this.vardataflag = 1
|
||
this.datalist = ''
|
||
this.saterDataByType = { pA: '', pB: '', ver: '' }
|
||
this.saterRawPayload = ''
|
||
this.clearDataTimeoutTimer()
|
||
uni.showToast({
|
||
title: this.$t('index.bleDisconnected'),
|
||
icon: 'none',
|
||
duration: 2000
|
||
})
|
||
}
|
||
})
|
||
xBlufi.notifyConnectBle({
|
||
isStart: false,
|
||
deviceId: this.deviceId,
|
||
name: this.name
|
||
})
|
||
}
|
||
},
|
||
// 清理5秒数据看门狗
|
||
clearDataTimeoutTimer() {
|
||
if (this.dataTimeoutTimer) {
|
||
clearTimeout(this.dataTimeoutTimer)
|
||
this.dataTimeoutTimer = null
|
||
}
|
||
},
|
||
// 监听用户操作的方法
|
||
handleUserAction() {
|
||
if(this.vardataflag == 3) {
|
||
this.resetDisconnectTimer()
|
||
}
|
||
},
|
||
// 获取时间显示
|
||
getTimeDisplay(setKey) {
|
||
const set = this.ver_data[setKey];
|
||
if (set && set[0] !== undefined) {
|
||
return `${set[0].toString().padStart(2, '0')}:${set[1].toString().padStart(2, '0')}`;
|
||
}
|
||
return '--:--';
|
||
},
|
||
// 判断是否是当前时间
|
||
isCurrentTime(setKey) {
|
||
const set = this.ver_data[setKey];
|
||
return set && set[0] == this.xctimesj.slice(0,2) && set[1] == this.xctimesj.slice(-2);
|
||
},
|
||
// 计算时间差值(分钟)
|
||
getTimeDifference(hour, minute) {
|
||
if (!this.devicetime) return Infinity;
|
||
const [currentHour, currentMinute] = this.devicetime.split(':').map(Number);
|
||
const currentTotalMinutes = currentHour * 60 + currentMinute;
|
||
const targetTotalMinutes = hour * 60 + minute;
|
||
|
||
// 计算时间差
|
||
let diff = targetTotalMinutes - currentTotalMinutes;
|
||
// 如果时间已过,加上24小时
|
||
if (diff <= 0) {
|
||
diff += 1440;
|
||
}
|
||
return diff;
|
||
},
|
||
|
||
// 判断定时是否开启(兼容 1 和 true)
|
||
isPsetEnabled(arr) {
|
||
return arr && (arr[3] === 1 || arr[3] === true);
|
||
},
|
||
// 计算下一次浇水时间(用于 xctime、xctimesj、小三角图标等)
|
||
calculateNextWateringTime(currentDay) {
|
||
if (!this.ver_data || typeof this.ver_data !== 'object') return
|
||
const day = (currentDay != null && currentDay >= 0) ? currentDay : Math.floor(Date.now() / 86400000)
|
||
const calculateTimeDifference = (date1, date2) => {
|
||
const diffMs = Math.abs(date1 - date2)
|
||
const diffMinutes = Math.floor(diffMs / (1000 * 60))
|
||
const diffHours = Math.floor(diffMinutes / 60)
|
||
const remainingMinutes = diffMinutes % 60
|
||
return { hours: diffHours, minutes: remainingMinutes }
|
||
}
|
||
const formatTime = (seconds) => {
|
||
const minutes = Math.floor(seconds / 60)
|
||
const remainingSeconds = seconds % 60
|
||
return this.$t('index.durationMinSec', { m: minutes, s: remainingSeconds })
|
||
}
|
||
const getTimeFromArray = (timeArray) => {
|
||
if (!timeArray || timeArray.length < 2) return null
|
||
const [hour, minute] = timeArray
|
||
const date = new Date()
|
||
date.setHours(hour, minute, 0, 0)
|
||
return date
|
||
}
|
||
let nextTime = null
|
||
let nextTimeDiff = null
|
||
let nextWaterDuration = null
|
||
let prevTime = null
|
||
for (const key in this.ver_data) {
|
||
if (this.ver_data.hasOwnProperty(key) && this.isPsetEnabled(this.ver_data[key])) {
|
||
const timeArray = this.ver_data[key]
|
||
const timeDate = getTimeFromArray(timeArray)
|
||
if (!timeDate) continue
|
||
const diff = calculateTimeDifference(timeDate, new Date())
|
||
const interval = timeArray[4] || 0
|
||
const shouldWaterToday = interval === 0 || (day % Math.max(1, interval) === 0)
|
||
if (timeDate > new Date() && shouldWaterToday) {
|
||
if (!nextTime || (diff.hours * 60 + diff.minutes < (nextTimeDiff?.hours || 0) * 60 + (nextTimeDiff?.minutes || 0))) {
|
||
nextTimeDiff = diff
|
||
nextTime = timeDate
|
||
nextWaterDuration = formatTime(timeArray[2])
|
||
}
|
||
} else if (!prevTime || timeDate > prevTime) {
|
||
prevTime = timeDate
|
||
}
|
||
}
|
||
}
|
||
this.xctime = nextTime ? this.$t('index.timeUntil', { h: nextTimeDiff.hours, m: nextTimeDiff.minutes }) : this.$t('index.noNextGap')
|
||
this.xctimesc = nextWaterDuration || this.$t('index.unknownDuration')
|
||
this.sctimejs = prevTime ? prevTime.toTimeString().slice(0, 5) : this.$t('index.noLastWater')
|
||
this.xctimesj = nextTime ? nextTime.toTimeString().slice(0, 5) : this.$t('index.noNextWater')
|
||
},
|
||
// 判断是否显示图标(下一次浇水小三角)
|
||
shouldShowIcon(pSetIndex) {
|
||
const pSet = this.ver_data[`p_set${pSetIndex}`];
|
||
if (!pSet || !this.isPsetEnabled(pSet)) return false;
|
||
|
||
const currentDiff = this.getTimeDifference(pSet[0], pSet[1]);
|
||
|
||
for (let i = 1; i <= 6; i++) {
|
||
const checkSet = this.ver_data[`p_set${i}`];
|
||
if (checkSet && this.isPsetEnabled(checkSet)) {
|
||
const checkDiff = this.getTimeDifference(checkSet[0], checkSet[1]);
|
||
if (checkDiff < currentDiff || (checkDiff === currentDiff && i < pSetIndex)) {
|
||
return false;
|
||
}
|
||
}
|
||
}
|
||
return true;
|
||
},
|
||
// 将后台返回的 h1/m1/o1/s1... 整合为 ver_data 结构
|
||
// SATER:从 pA、pB 解析六组定时;其他型号从 h1/m1/s1/o1/d1 解析
|
||
buildVerDataFromBackend(params){
|
||
if (this.pre === 'SATER' && (params?.pA || params?.pB)) {
|
||
const parsed = this.parseSaterFromApiParams(params)
|
||
this.saterListA = parsed.A
|
||
this.saterListB = parsed.B
|
||
const target = this.stareChannel === 'B' ? parsed.B : parsed.A
|
||
return this.normalizeVerDataSwitch(target)
|
||
}
|
||
const result = {}
|
||
for(let i = 1; i <= 6; i++){
|
||
const h = Number(params?.[`h${i}`]?.value)
|
||
const m = Number(params?.[`m${i}`]?.value)
|
||
const s = Number(params?.[`s${i}`]?.value)
|
||
const o = Number(params?.[`o${i}`]?.value)
|
||
const d = Number(params?.[`d${i}`]?.value)
|
||
const hour = isNaN(h) ? 0 : h
|
||
const minute = isNaN(m) ? 0 : m
|
||
const second = isNaN(s) ? 0 : s
|
||
const onoff = isNaN(o) ? 0 : o
|
||
const interval = isNaN(d) || d <= 0 ? 1 : d
|
||
result[`p_set${i}`] = [hour, minute, second, onoff, interval]
|
||
}
|
||
return result
|
||
},
|
||
// SATER:从 getDetail 的 pA、pB 解析为六组定时 { A: {p_set1..p_set6}, B: {...} }
|
||
parseSaterFromApiParams(params){
|
||
const getVal = (obj) => {
|
||
if (!obj) return ''
|
||
const v = (obj && typeof obj === 'object' && 'value' in obj) ? obj.value : obj
|
||
return String(v == null ? '' : v).replace(/^["\s]+|["\s]+$/g, '')
|
||
}
|
||
const pAStr = getVal(params.pA)
|
||
const pBStr = getVal(params.pB)
|
||
const raw = (pAStr ? pAStr + ';' : '') + (pBStr || '')
|
||
return this.parseSaterRawToAB(raw)
|
||
},
|
||
parseSaterRawToAB(rawInput){
|
||
const raw = String(rawInput || '')
|
||
const src = raw.replace(/@/g, ';')
|
||
const resultA = {}
|
||
const resultB = {}
|
||
for (let i = 1; i <= 6; i++) {
|
||
resultA[`p_set${i}`] = [0, 0, 0, 0, 0]
|
||
resultB[`p_set${i}`] = [0, 0, 0, 0, 0]
|
||
}
|
||
const reg = /p([AB])(\d+):([^;]+)/g
|
||
let m
|
||
while ((m = reg.exec(src)) !== null) {
|
||
const ch = m[1]
|
||
const idx = Number(m[2])
|
||
if (!idx || idx < 1 || idx > 6) continue
|
||
const nums = m[3].split(',').map(v => {
|
||
const n = parseInt(v, 10)
|
||
return isNaN(n) ? 0 : n
|
||
})
|
||
while (nums.length < 5) nums.push(0)
|
||
nums[3] = nums[3] === 1
|
||
const key = `p_set${idx}`
|
||
if (ch === 'A') resultA[key] = nums
|
||
if (ch === 'B') resultB[key] = nums
|
||
}
|
||
return { A: resultA, B: resultB }
|
||
},
|
||
normalizeVerDataSwitch(obj){
|
||
const out = {}
|
||
if (!obj || typeof obj !== 'object') return out
|
||
Object.keys(obj).forEach((k) => {
|
||
const arr = Array.isArray(obj[k]) ? [...obj[k]] : [0, 0, 0, 0, 0]
|
||
while (arr.length < 5) arr.push(0)
|
||
arr[3] = arr[3] === 1 || arr[3] === true
|
||
out[k] = arr
|
||
})
|
||
return out
|
||
},
|
||
// 检查并修复蓝牙状态
|
||
checkAndFixBluetooth() {
|
||
return new Promise((resolve, reject) => {
|
||
uni.getBluetoothAdapterState({
|
||
success: (res) => {
|
||
if (res.available) {
|
||
// 蓝牙可用,直接继续
|
||
resolve()
|
||
} else {
|
||
// 蓝牙不可用,尝试重新打开
|
||
this.fixBluetoothAdapter().then(resolve).catch(reject)
|
||
}
|
||
},
|
||
fail: (err) => {
|
||
// 获取状态失败,尝试重新初始化
|
||
this.fixBluetoothAdapter().then(resolve).catch(reject)
|
||
}
|
||
})
|
||
})
|
||
},
|
||
// 修复蓝牙适配器
|
||
fixBluetoothAdapter() {
|
||
return new Promise((resolve, reject) => {
|
||
uni.openBluetoothAdapter({
|
||
success: () => {
|
||
console.log('蓝牙适配器重新初始化成功')
|
||
resolve()
|
||
},
|
||
fail: (err) => {
|
||
const errMsg = err.errMsg || ''
|
||
let errorType = 'UNKNOWN'
|
||
let errorMessage = this.$t('index.ble.initFail')
|
||
let canAutoFix = false
|
||
let userAction = ''
|
||
|
||
if (errMsg.includes('auth deny') || errMsg.includes('authorize')) {
|
||
errorType = 'PERMISSION_DENIED'
|
||
errorMessage = this.$t('index.ble.permDenied')
|
||
userAction = this.$t('index.ble.permDeniedHint')
|
||
} else if (errMsg.includes('not available') || errMsg.includes('unavailable')) {
|
||
errorType = 'BLUETOOTH_UNAVAILABLE'
|
||
errorMessage = this.$t('index.ble.btUnavailable')
|
||
userAction = this.$t('index.ble.btUnavailableHint')
|
||
} else if (errMsg.includes('open fail')) {
|
||
errorType = 'OPEN_FAILED'
|
||
errorMessage = this.$t('index.ble.openFail')
|
||
userAction = this.$t('index.ble.openFailHint')
|
||
} else if (errMsg.includes('system permission denied')) {
|
||
errorType = 'SYSTEM_PERMISSION_DENIED'
|
||
errorMessage = this.$t('index.ble.sysPerm')
|
||
userAction = this.$t('index.ble.sysPermHint')
|
||
} else if (errMsg.includes('bluetooth service unavailable')) {
|
||
errorType = 'SERVICE_UNAVAILABLE'
|
||
errorMessage = this.$t('index.ble.svcUnavailable')
|
||
userAction = this.$t('index.ble.svcUnavailableHint')
|
||
canAutoFix = true
|
||
} else if (errMsg.includes('already opened')) {
|
||
// 已经打开,可以继续
|
||
resolve()
|
||
return
|
||
}
|
||
reject({
|
||
type: errorType,
|
||
message: errorMessage,
|
||
errMsg: errMsg,
|
||
canAutoFix: canAutoFix,
|
||
userAction: userAction
|
||
})
|
||
}
|
||
})
|
||
})
|
||
},
|
||
// 诊断连接失败原因(按优先级逐个检查,找到第一个错误就停止)
|
||
diagnoseConnectionFailure() {
|
||
// 如果已经显示过错误弹窗,不再重复诊断
|
||
if (this.errorModalShown) {
|
||
return
|
||
}
|
||
|
||
// 按优先级检查错误,找到第一个就停止
|
||
// 1. 首先检查蓝牙适配器状态
|
||
uni.getBluetoothAdapterState({
|
||
success: (res) => {
|
||
// 2. 检查蓝牙是否可用
|
||
if (!res.available) {
|
||
// 找到第一个错误:蓝牙不可用
|
||
this.handleConnectionError({
|
||
type: 'BLUETOOTH_UNAVAILABLE',
|
||
message: this.$t('index.ble.btUnavailable'),
|
||
userAction: this.$t('index.ble.btUnavailableHint'),
|
||
canAutoFix: true
|
||
})
|
||
return // 找到错误,停止检查
|
||
}
|
||
|
||
// 3. 蓝牙可用,检查是否找到设备
|
||
const deviceCount = this.devicesarr ? this.devicesarr.length : 0
|
||
|
||
if (deviceCount === 0) {
|
||
// 找到第二个错误:未搜索到任何设备
|
||
this.handleConnectionError({
|
||
type: 'NO_DEVICES_FOUND',
|
||
message: this.$t('index.ble.noDevices'),
|
||
userAction: this.$t('index.ble.noDevicesHint'),
|
||
canAutoFix: false
|
||
})
|
||
return // 找到错误,停止检查
|
||
}
|
||
|
||
// 4. 有设备但找不到目标设备
|
||
const matchedDevice = this.devicesarr.find(device => {
|
||
if (device.name) {
|
||
return device.name.slice(-12) === this.mac.slice(-12)
|
||
}
|
||
return false
|
||
})
|
||
|
||
if (!matchedDevice) {
|
||
// 找到第三个错误:未找到目标设备
|
||
this.handleConnectionError({
|
||
type: 'TARGET_DEVICE_NOT_FOUND',
|
||
message: this.$t('index.ble.targetNotFound', { n: deviceCount }),
|
||
userAction: this.$t('index.ble.targetNotFoundHint'),
|
||
canAutoFix: false
|
||
})
|
||
return // 找到错误,停止检查
|
||
}
|
||
|
||
// 如果所有检查都通过但没有连接成功,可能是其他原因
|
||
// 这种情况不应该发生,但为了安全起见还是处理一下
|
||
if (!this.errorModalShown) {
|
||
this.handleConnectionError({
|
||
type: 'UNKNOWN_ERROR',
|
||
message: this.$t('index.ble.unknown'),
|
||
userAction: this.$t('index.ble.unknownHint'),
|
||
canAutoFix: false
|
||
})
|
||
}
|
||
},
|
||
fail: (err) => {
|
||
// 获取状态失败,尝试重新初始化
|
||
this.fixBluetoothAdapter().then(() => {
|
||
// 修复成功,提示用户重试
|
||
if (!this.errorModalShown) {
|
||
this.handleConnectionError({
|
||
type: 'ADAPTER_ERROR',
|
||
message: this.$t('index.ble.adapterFixed'),
|
||
userAction: this.$t('index.ble.adapterFixedHint'),
|
||
canAutoFix: true,
|
||
autoFixed: true
|
||
})
|
||
}
|
||
}).catch((error) => {
|
||
// 修复失败,显示错误
|
||
if (!this.errorModalShown) {
|
||
this.handleConnectionError(error)
|
||
}
|
||
})
|
||
}
|
||
})
|
||
},
|
||
// 处理连接错误(只显示一次)
|
||
handleConnectionError(error) {
|
||
// 如果已经显示过错误弹窗,不再重复显示
|
||
if (this.errorModalShown) {
|
||
return
|
||
}
|
||
|
||
const errorInfo = error || {}
|
||
const type = errorInfo.type || 'UNKNOWN'
|
||
|
||
// 如果错误类型相同,不再重复显示
|
||
if (this.currentErrorType === type) {
|
||
return
|
||
}
|
||
|
||
// 标记错误弹窗已显示
|
||
this.errorModalShown = true
|
||
this.currentErrorType = type
|
||
|
||
const message = errorInfo.message || this.$t('index.ble.connectFailDefault')
|
||
const userAction = errorInfo.userAction || this.$t('index.ble.retryHint')
|
||
const canAutoFix = errorInfo.canAutoFix || false
|
||
const autoFixed = errorInfo.autoFixed || false
|
||
|
||
// 清除连接状态
|
||
this.vardataflag = 1
|
||
this.clearConnectionTimeout()
|
||
xBlufi.notifyStartDiscoverBle({
|
||
'isStart': false
|
||
})
|
||
|
||
// 如果已自动修复,显示成功提示
|
||
if (autoFixed) {
|
||
this.showCustomErrorModal({
|
||
title: this.$t('index.fixed'),
|
||
message: message,
|
||
userAction: userAction,
|
||
showCancel: false,
|
||
confirmText: this.$t('common.gotIt'),
|
||
onConfirm: () => {
|
||
// 重置错误状态,允许下次检查
|
||
this.errorModalShown = false
|
||
this.currentErrorType = null
|
||
// 可以自动重试
|
||
if (canAutoFix) {
|
||
setTimeout(() => {
|
||
this.btnlj()
|
||
}, 1000)
|
||
}
|
||
}
|
||
})
|
||
return
|
||
}
|
||
|
||
// 根据错误类型决定是否可以自动修复
|
||
if (canAutoFix && type === 'BLUETOOTH_UNAVAILABLE') {
|
||
this.showCustomErrorModal({
|
||
title: this.$t('index.connectFail'),
|
||
message: message,
|
||
userAction: userAction,
|
||
showCancel: true,
|
||
cancelText: this.$t('common.cancel'),
|
||
confirmText: this.$t('common.autoRepair'),
|
||
onConfirm: () => {
|
||
// 重置错误状态
|
||
this.errorModalShown = false
|
||
this.currentErrorType = null
|
||
// 尝试自动修复
|
||
this.fixBluetoothAdapter().then(() => {
|
||
uni.showToast({
|
||
title: this.$t('index.fixOkRetry'),
|
||
icon: 'success',
|
||
duration: 2000
|
||
})
|
||
// 修复成功后,允许重新检查错误
|
||
setTimeout(() => {
|
||
this.errorModalShown = false
|
||
this.currentErrorType = null
|
||
}, 2000)
|
||
}).catch((err) => {
|
||
// 修复失败,重新显示错误(重置状态后)
|
||
this.errorModalShown = false
|
||
this.currentErrorType = null
|
||
this.handleConnectionError(err)
|
||
})
|
||
}
|
||
})
|
||
} else {
|
||
// 需要用户手动处理
|
||
this.showCustomErrorModal({
|
||
title: this.$t('index.connectFail'),
|
||
message: message,
|
||
userAction: userAction,
|
||
showCancel: true,
|
||
cancelText: this.$t('common.cancel'),
|
||
confirmText: this.$t('common.retry'),
|
||
onConfirm: () => {
|
||
// 重置错误状态,允许下次检查
|
||
this.errorModalShown = false
|
||
this.currentErrorType = null
|
||
// 用户点击重试
|
||
setTimeout(() => {
|
||
this.btnlj()
|
||
}, 500)
|
||
}
|
||
})
|
||
}
|
||
},
|
||
// 显示自定义错误弹窗
|
||
showCustomErrorModal(data) {
|
||
this.errorModalData = {
|
||
title: data.title || this.$t('common.tip'),
|
||
message: data.message || '',
|
||
userAction: data.userAction || '',
|
||
showCancel: data.showCancel !== undefined ? data.showCancel : true,
|
||
cancelText: data.cancelText || this.$t('common.cancel'),
|
||
confirmText: data.confirmText || this.$t('common.confirm'),
|
||
onConfirm: data.onConfirm || (() => {})
|
||
}
|
||
this.showErrorModal = true
|
||
},
|
||
// 关闭错误弹窗
|
||
closeErrorModal() {
|
||
this.showErrorModal = false
|
||
// 重置错误状态
|
||
this.errorModalShown = false
|
||
this.currentErrorType = null
|
||
},
|
||
// 处理错误弹窗确认
|
||
handleErrorConfirm() {
|
||
if (this.errorModalData.onConfirm) {
|
||
this.errorModalData.onConfirm()
|
||
}
|
||
this.showErrorModal = false
|
||
// xBlufi.initXBlufi(1)
|
||
// xBlufi.listenDeviceMsgEvent(true, this.funListenDeviceMsgEvent)
|
||
// xBlufi.notifyStartDiscoverBle({
|
||
// 'isStart': true
|
||
// })
|
||
},
|
||
// 处理连接超时
|
||
handleConnectionTimeout() {
|
||
// 如果已经显示过错误弹窗,不再重复显示
|
||
if (this.errorModalShown) {
|
||
return
|
||
}
|
||
|
||
this.vardataflag = 1
|
||
this.clearConnectionTimeout()
|
||
xBlufi.notifyStartDiscoverBle({
|
||
'isStart': false
|
||
})
|
||
|
||
const elapsedTime = Date.now() - (this.connectionStartTime || Date.now())
|
||
this.handleConnectionError({
|
||
type: 'CONNECTION_TIMEOUT',
|
||
message: this.$t('index.ble.timeout', { s: Math.floor(elapsedTime / 1000) }),
|
||
userAction: this.$t('index.ble.timeoutHint'),
|
||
canAutoFix: false
|
||
})
|
||
},
|
||
// 清除连接超时定时器
|
||
clearConnectionTimeout() {
|
||
if (this.connectionTimeout) {
|
||
clearTimeout(this.connectionTimeout)
|
||
this.connectionTimeout = null
|
||
}
|
||
if (this.connectTimeout) {
|
||
clearTimeout(this.connectTimeout)
|
||
this.connectTimeout = null
|
||
}
|
||
},
|
||
}
|
||
}
|
||
</script>
|
||
|
||
<style lang="scss">
|
||
|
||
.dangqian{
|
||
width: 100%;
|
||
height: 160rpx;
|
||
background: #FFFFFF;
|
||
box-shadow: 0rpx 0rpx 38rpx 0rpx rgba(128,128,128,0.3);
|
||
filter: blur(0px);
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
padding: 0 26rpx;
|
||
box-sizing: border-box;
|
||
margin-top: 24rpx;
|
||
border-radius: 20rpx;
|
||
image{
|
||
width: 78rpx;
|
||
height: 78rpx;
|
||
}
|
||
.cen{
|
||
margin-left: 30rpx;
|
||
.shen{
|
||
font-weight: 600;
|
||
font-size: 28rpx;
|
||
color: #3D3D3D;
|
||
margin-bottom: 8rpx;
|
||
display: flex;
|
||
text{
|
||
width: 80rpx;
|
||
height: 40rpx;
|
||
background-color: #7FAD76;
|
||
color: #fff;
|
||
text-align: center;
|
||
line-height: 40rpx;
|
||
border-radius: 10rpx;
|
||
margin-left: 20rpx;
|
||
}
|
||
}
|
||
view{
|
||
font-size: 24rpx;
|
||
color: #808080;
|
||
}
|
||
}
|
||
.rt{
|
||
font-weight: 600;
|
||
font-size: 48rpx;
|
||
color: #50565A;
|
||
}
|
||
}
|
||
.rotated-image {
|
||
transform: rotate(180deg); /* 旋转 180 度 */
|
||
transition: transform 1s ease;
|
||
}
|
||
.container {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
margin-top: 22rpx;
|
||
view {
|
||
width: 242rpx;
|
||
height: 208rpx;
|
||
background: #F0F0F0;
|
||
border-radius: 24rpx 24rpx 24rpx 24rpx;
|
||
text-align: center;
|
||
line-height: 208rpx;
|
||
font-weight: 600;
|
||
font-size: 44rpx;
|
||
color: #3D3D3D;
|
||
}
|
||
text {
|
||
font-size: 80rpx;
|
||
margin-left: 46rpx;
|
||
margin-right: 46rpx;
|
||
}
|
||
}
|
||
.anniu {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
margin-top: 58rpx;
|
||
.qx {
|
||
width: 278rpx;
|
||
height: 80rpx;
|
||
border-radius: 10rpx 10rpx 10rpx 10rpx;
|
||
border: 2rpx solid #7FAD76;
|
||
font-size: 36rpx;
|
||
color: #7FAD76;
|
||
font-weight: 600;
|
||
line-height: 80rpx;
|
||
text-align: center;
|
||
}
|
||
.qd {
|
||
width: 278rpx;
|
||
height: 80rpx;
|
||
background: #7FAD76;
|
||
border-radius: 10rpx 10rpx 10rpx 10rpx;
|
||
font-size: 36rpx;
|
||
color: #fff;
|
||
font-weight: 600;
|
||
line-height: 80rpx;
|
||
text-align: center;
|
||
}
|
||
}
|
||
.selected-time {
|
||
margin-top: 20px;
|
||
font-size: 20px;
|
||
}
|
||
#activebor {
|
||
border: 1px solid #7FAD76;
|
||
}
|
||
@keyframes fadeInDown {
|
||
from {
|
||
opacity: 0;
|
||
transform: translateY(-20px);
|
||
/* 初始位置稍微在上方 */
|
||
}
|
||
to {
|
||
opacity: 1;
|
||
transform: translateY(0);
|
||
/* 最终位置 */
|
||
}
|
||
}
|
||
.mask{
|
||
width: 100%;
|
||
height: 100vh;
|
||
background: #3D3D3D;
|
||
opacity: 0.2;
|
||
position: fixed;
|
||
top: 0;
|
||
left: 0;
|
||
z-index: 1;
|
||
}
|
||
.manualmask {
|
||
width: 100%;
|
||
height: 100vh;
|
||
background: #3D3D3D;
|
||
opacity: 0.2;
|
||
position: fixed;
|
||
top: 0;
|
||
left: 0;
|
||
z-index: 99;
|
||
}
|
||
.manualjs {
|
||
width: 678rpx;
|
||
max-height: 610rpx;
|
||
background: #fff;
|
||
border-radius: 24rpx 24rpx 24rpx 24rpx;
|
||
position: fixed;
|
||
top: 538rpx;
|
||
left: 50%;
|
||
transform: translateX(-50%);
|
||
z-index: 100;
|
||
padding: 42rpx 44rpx;
|
||
box-sizing: border-box;
|
||
.toptit {
|
||
font-size: 36rpx;
|
||
color: #3D3D3D;
|
||
font-weight: 600;
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
}
|
||
}
|
||
.page {
|
||
padding: 0 20rpx;
|
||
padding-bottom: 300rpx;
|
||
box-sizing: border-box;
|
||
.tabsb {
|
||
width: 750rpx;
|
||
max-height: 95vh;
|
||
background: #FFFFFF;
|
||
position: fixed;
|
||
top: 0;
|
||
left: 0;
|
||
padding: 0 40rpx;
|
||
padding-bottom: 60rpx !important;
|
||
box-sizing: border-box;
|
||
border-radius: 0rpx 0rpx 50rpx 50rpx;
|
||
z-index: 2;
|
||
padding-top: 110rpx;
|
||
box-sizing: border-box;
|
||
animation: fadeInDown .5s ease-out forwards;
|
||
.qdan {
|
||
width: 100%;
|
||
height: 102rpx;
|
||
font-size: 48rpx;
|
||
color: #FFFFFF;
|
||
font-weight: 500;
|
||
line-height: 102rpx;
|
||
text-align: center;
|
||
background-color: #7FAD76;
|
||
border-radius: 20rpx;
|
||
margin-top: 40rpx;
|
||
}
|
||
.sblist {
|
||
width: 100%;
|
||
max-height: 68vh;
|
||
overflow: scroll;
|
||
margin-top: 50rpx;
|
||
.sbist_val {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
padding: 20rpx 30rpx;
|
||
margin-bottom: 28rpx;
|
||
box-sizing: border-box;
|
||
background: #F7F7F7;
|
||
border-radius: 20rpx 20rpx 20rpx 20rpx;
|
||
border: 1px solid transparent;
|
||
.lt {
|
||
font-size: 36rpx;
|
||
color: #3D3D3D;
|
||
font-weight: 600;
|
||
}
|
||
.rt {
|
||
image {
|
||
width: 124rpx;
|
||
height: 124rpx;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
.sbmask {
|
||
width: 100%;
|
||
height: 100vh;
|
||
background-color: rgb(238, 238, 238);
|
||
position: fixed;
|
||
top: 400rpx;
|
||
left: 0;
|
||
opacity: .5;
|
||
}
|
||
.wusb {
|
||
margin-top: 514rpx;
|
||
text-align: center;
|
||
text {
|
||
font-size: 28rpx;
|
||
color: #737B80;
|
||
}
|
||
view {
|
||
width: 212rpx;
|
||
height: 76rpx;
|
||
background: #7FAD76;
|
||
border-radius: 38rpx 38rpx 38rpx 38rpx;
|
||
border-radius: 50rpx;
|
||
font-size: 28rpx;
|
||
color: #FFFFFF;
|
||
text-align: center;
|
||
line-height: 76rpx;
|
||
margin: auto;
|
||
margin-top: 50rpx;
|
||
}
|
||
}
|
||
.wateringlogbox {
|
||
width: 100%;
|
||
height: 200rpx;
|
||
background: #FFFFFF;
|
||
box-shadow: 0rpx 0rpx 38rpx 0rpx rgba(128, 128, 128, 0.3);
|
||
filter: blur(0px);
|
||
border-radius: 20rpx;
|
||
margin-top: 20rpx;
|
||
padding: 30rpx;
|
||
box-sizing: border-box;
|
||
.wateringlogbd {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
.lt {
|
||
view {
|
||
font-size: 28rpx;
|
||
color: #50565A;
|
||
margin-top: 10rpx;
|
||
}
|
||
}
|
||
.rt {
|
||
image {
|
||
width: 114rpx;
|
||
height: 114rpx;
|
||
}
|
||
}
|
||
}
|
||
.wateringlogtop {
|
||
display: flex;
|
||
align-items: center;
|
||
font-size: 32rpx;
|
||
color: #3D3D3D;
|
||
font-weight: 600;
|
||
image {
|
||
width: 40rpx;
|
||
height: 40rpx;
|
||
margin-right: 14rpx;
|
||
}
|
||
}
|
||
}
|
||
.dingshi_he {
|
||
margin-top: 24rpx;
|
||
width: 350rpx;
|
||
height: 194rpx;
|
||
background: #FFFFFF;
|
||
box-shadow: 0rpx 0rpx 38rpx 0rpx rgba(128, 128, 128, 0.3);
|
||
filter: blur(0px);
|
||
border-radius: 20rpx;
|
||
padding: 26rpx;
|
||
box-sizing: border-box;
|
||
.naoz {
|
||
font-size: 40rpx;
|
||
color: #3D3D3D;
|
||
font-weight: 600;
|
||
display: flex;
|
||
justify-content: space-between;
|
||
image {
|
||
width: 78rpx;
|
||
height: 78rpx;
|
||
}
|
||
}
|
||
.dstime {
|
||
margin-top: 14rpx;
|
||
display: flex;
|
||
align-items: center;
|
||
width: 100%;
|
||
justify-content: space-between;
|
||
image {
|
||
width: 52rpx;
|
||
height: 52rpx;
|
||
}
|
||
}
|
||
}
|
||
.switchbox {
|
||
margin-top: 24rpx;
|
||
display: flex;
|
||
justify-content: space-between;
|
||
.switch_he {
|
||
width: 350rpx;
|
||
height: 194rpx;
|
||
background: #FFFFFF;
|
||
box-shadow: 0rpx 0rpx 38rpx 0rpx rgba(128, 128, 128, 0.3);
|
||
filter: blur(0px);
|
||
border-radius: 20rpx;
|
||
padding: 26rpx;
|
||
box-sizing: border-box;
|
||
.yushui {
|
||
width: 100%;
|
||
font-size: 28rpx;
|
||
color: #3D3D3D;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
margin-top: 20rpx;
|
||
}
|
||
image {
|
||
width: 78rpx;
|
||
height: 78rpx;
|
||
}
|
||
}
|
||
}
|
||
.devicebox {
|
||
width: 100%;
|
||
max-height: 800rpx;
|
||
background: #FFFFFF;
|
||
box-shadow: 0rpx 0rpx 38rpx 0rpx rgba(128, 128, 128, 0.3);
|
||
filter: blur(0px);
|
||
margin-top: 22rpx;
|
||
border-radius: 20rpx;
|
||
padding: 30rpx;
|
||
box-sizing: border-box;
|
||
.deviceweek {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
margin-top: 20rpx;
|
||
.weeklist {
|
||
text-align: center;
|
||
.kg {
|
||
font-size: 24rpx;
|
||
color: #999999;
|
||
font-weight: 600;
|
||
}
|
||
.weekday {
|
||
font-size: 36rpx;
|
||
color: #3D3D3D;
|
||
font-weight: 600;
|
||
margin-top: 4rpx;
|
||
}
|
||
.time {
|
||
font-size: 28rpx;
|
||
color: #3D3D3D;
|
||
margin-top: 4rpx;
|
||
}
|
||
.interval {
|
||
font-size: 24rpx;
|
||
color: #3D3D3D;
|
||
font-weight: 600;
|
||
margin-top: 4rpx;
|
||
}
|
||
.img {
|
||
text-align: center;
|
||
image {
|
||
width: 12rpx;
|
||
height: 12rpx;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
.devicetop {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
.devicert {
|
||
text-align: right;
|
||
.shoudong {
|
||
display: flex;
|
||
image {
|
||
width: 64rpx;
|
||
height: 32rpx;
|
||
margin-right: 8rpx;
|
||
}
|
||
font-size: 24rpx;
|
||
color: #3D3D3D;
|
||
}
|
||
.shengyu {
|
||
font-size: 24rpx;
|
||
color: #3D3D3D;
|
||
margin-top: 6rpx;
|
||
}
|
||
}
|
||
.devicelt {
|
||
display: flex;
|
||
.xinghao {
|
||
font-size: 24rpx;
|
||
color: #3D3D3D;
|
||
.one {
|
||
font-size: 28rpx !important;
|
||
color: #3D3D3D;
|
||
font-weight: 600 !important;
|
||
margin-top: 6rpx;
|
||
}
|
||
}
|
||
image {
|
||
width: 74rpx;
|
||
height: 80rpx;
|
||
margin-right: 32rpx;
|
||
}
|
||
}
|
||
.signal-bars {
|
||
.signal-bar {
|
||
transition: background-color 0.3s;
|
||
&.active {
|
||
background-color: #7FAD76 !important;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
/* 电池电量样式:边框包裹,背景按百分比填充,红黄绿 */
|
||
.battery-wrap {
|
||
display: flex;
|
||
align-items: center;
|
||
.battery-box {
|
||
position: relative;
|
||
width: 46rpx;
|
||
height: 24rpx;
|
||
line-height: 24rpx;
|
||
border: 2rpx solid #b1b1b1;
|
||
border-radius: 8rpx;
|
||
overflow: hidden;
|
||
background: #f5f5f5;
|
||
.battery-fill {
|
||
position: absolute;
|
||
left: 0;
|
||
top: 0;
|
||
bottom: 0;
|
||
min-width: 0;
|
||
border-radius: 8rpx;
|
||
transition: width 0.3s ease, background-color 0.3s ease;
|
||
/* width: 20rpx ; */
|
||
/* height: 24rpx ; */
|
||
border: 1px solid #fff;
|
||
}
|
||
.battery-text {
|
||
position: relative;
|
||
z-index: 1;
|
||
height: 100%;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
font-size: 16rpx;
|
||
color: #333;
|
||
font-weight: 500;
|
||
}
|
||
}
|
||
.battery-nub {
|
||
width: 4rpx;
|
||
height: 14rpx;
|
||
background: #b1b1b1;
|
||
border-radius: 0 4rpx 4rpx 0;
|
||
margin-left: 2rpx;
|
||
}
|
||
}
|
||
/* 刷新按钮 */
|
||
.refresh-btn {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
gap: 8rpx;
|
||
padding: 12rpx 24rpx;
|
||
border: 1rpx solid #7FAD76;
|
||
border-radius: 32rpx;
|
||
background: rgba(127, 173, 118, 0.08);
|
||
font-size: 26rpx;
|
||
color: #7FAD76;
|
||
&:active {
|
||
background: rgba(127, 173, 118, 0.2);
|
||
}
|
||
&.is-refreshing {
|
||
opacity: 0.85;
|
||
pointer-events: none;
|
||
}
|
||
.refresh-icon {
|
||
font-size: 28rpx;
|
||
line-height: 1;
|
||
}
|
||
.refresh-text {
|
||
font-weight: 500;
|
||
}
|
||
}
|
||
.selectbox {
|
||
width: 100%;
|
||
margin-top: 22rpx;
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
.shebeiname {
|
||
font-size: 36rpx;
|
||
color: #333;
|
||
font-weight: 600;
|
||
display: flex;
|
||
align-items: center;
|
||
.lj{
|
||
color: #e21616;
|
||
margin-left: 10rpx;
|
||
font-weight: 400;
|
||
font-size: 28rpx;
|
||
border: 1px solid #e21616;
|
||
padding: 4rpx;
|
||
box-sizing: border-box;
|
||
border-radius: 10rpx;
|
||
}
|
||
text {
|
||
display: inline-block;
|
||
width: 14rpx;
|
||
height: 14rpx;
|
||
background: #7FAD76;
|
||
border-radius: 50%;
|
||
box-shadow: 0rpx 0rpx 10rpx 0rpx rgba(0, 255, 102, 0.9), 0rpx 0rpx 30rpx 0rpx rgba(69, 255, 143, 0.9);
|
||
margin-left: 22rpx;
|
||
}
|
||
}
|
||
.shebeiadd {
|
||
image {
|
||
width: 214rpx;
|
||
height: 64rpx;
|
||
}
|
||
}
|
||
.selectname {
|
||
font-size: 48rpx;
|
||
color: #3D3D3D;
|
||
font-weight: 600;
|
||
z-index: 2;
|
||
image {
|
||
width: 32rpx;
|
||
height: 26rpx;
|
||
margin-left: 14rpx;
|
||
}
|
||
}
|
||
.selectnames {
|
||
font-size: 28rpx;
|
||
color: #3D3D3D;
|
||
border: 1px solid;
|
||
padding:0 20rpx;
|
||
box-sizing: border-box;
|
||
border-radius: 10rpx;
|
||
background-color: #7FAD76;
|
||
color: #fff;
|
||
height: 60rpx;
|
||
line-height: 60rpx;
|
||
image {
|
||
width: 32rpx;
|
||
height: 26rpx;
|
||
margin-left: 14rpx;
|
||
}
|
||
}
|
||
.selectshezhi {
|
||
image {
|
||
width: 60rpx;
|
||
height: 60rpx;
|
||
}
|
||
}
|
||
}
|
||
.bjimg {
|
||
width: 100%;
|
||
height: 100vh;
|
||
position: fixed;
|
||
top: 0;
|
||
left: 0;
|
||
z-index: -1;
|
||
}
|
||
.title {
|
||
box-sizing: border-box;
|
||
padding: 0 20rpx;
|
||
image {
|
||
width: 260rpx;
|
||
height: 60rpx;
|
||
}
|
||
}
|
||
.gateway-item {
|
||
display: flex;
|
||
align-items: end;
|
||
// padding: 20rpx;
|
||
padding-left: 20rpx;
|
||
box-sizing: border-box;
|
||
background: #FFFFFF;
|
||
|
||
.gateway-icon-circle {
|
||
width: 60rpx;
|
||
height: 60rpx;
|
||
background: #E5E5E5;
|
||
border-radius: 50%;
|
||
// margin-right: 10rpx;
|
||
flex-shrink: 0;
|
||
}
|
||
|
||
.gateway-content {
|
||
display: flex;
|
||
align-items: center;
|
||
.gateway-title {
|
||
font-size: 20rpx;
|
||
color: #333333;
|
||
font-weight: 500;
|
||
margin-bottom: 2rpx;
|
||
}
|
||
|
||
.gateway-desc {
|
||
font-size: 24rpx;
|
||
color: #7FAD76;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
.channel-switch{
|
||
position: relative;
|
||
width: 100%;
|
||
margin-top: 20rpx;
|
||
height: 72rpx;
|
||
border-radius: 12rpx;
|
||
overflow: hidden;
|
||
border: 2rpx solid #48893B;
|
||
box-sizing: border-box;
|
||
}
|
||
/* 背景层:梯形斜切(未选中白色,选中绿色) */
|
||
.channel-bg{
|
||
position: absolute;
|
||
top: 0;
|
||
left: 0;
|
||
right: 0;
|
||
bottom: 0;
|
||
background: #fff;
|
||
}
|
||
/* A:左梯形,右边缘斜(上窄下宽) */
|
||
.channel-bg.channel-bg-a{
|
||
clip-path: polygon(0 0, 48% 0, 52% 100%, 0 100%);
|
||
z-index: 1;
|
||
}
|
||
.channel-bg.channel-bg-a.active{
|
||
background: #48893B;
|
||
z-index: 2;
|
||
}
|
||
/* B:右梯形,左边缘斜 */
|
||
.channel-bg.channel-bg-b{
|
||
clip-path: polygon(48% 0, 100% 0, 100% 100%, 52% 100%);
|
||
z-index: 1;
|
||
}
|
||
.channel-bg.channel-bg-b.active{
|
||
background: #48893B;
|
||
z-index: 2;
|
||
}
|
||
/* 文字层:独立定位,不被裁剪 */
|
||
.channel-text{
|
||
position: absolute;
|
||
top: 0;
|
||
bottom: 0;
|
||
width: 50%;
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
justify-content: center;
|
||
z-index: 3;
|
||
font-size: 28rpx;
|
||
font-weight: 600;
|
||
color: #48893B;
|
||
}
|
||
.channel-text.channel-text-a{
|
||
left: 0;
|
||
}
|
||
.channel-text.channel-text-b{
|
||
right: 0;
|
||
left: auto;
|
||
}
|
||
.channel-text.active{
|
||
color: #fff;
|
||
}
|
||
/* 激活态底部指示条(绿色背景上显白色) */
|
||
.channel-indicator{
|
||
position: absolute;
|
||
bottom: 12rpx;
|
||
left: 50%;
|
||
transform: translateX(-50%);
|
||
width: 36rpx;
|
||
height: 6rpx;
|
||
border-radius: 3rpx;
|
||
background: #fff;
|
||
}
|
||
|
||
/* 加载遮罩层样式 */
|
||
.loading-mask {
|
||
position: fixed;
|
||
top: 0;
|
||
left: 0;
|
||
width: 100%;
|
||
height: 100vh;
|
||
background-color: rgba(255, 255, 255, 0.8);
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
z-index: 9999;
|
||
}
|
||
|
||
.loading-content {
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
justify-content: center;
|
||
padding: 40rpx;
|
||
background-color: #fff;
|
||
border-radius: 20rpx;
|
||
box-shadow: 0 4rpx 20rpx rgba(0, 0, 0, 0.1);
|
||
}
|
||
|
||
.loading-text {
|
||
margin-top: 20rpx;
|
||
font-size: 28rpx;
|
||
color: #666;
|
||
text-align: center;
|
||
}
|
||
|
||
/* 自定义错误弹窗样式 */
|
||
.error-modal-mask {
|
||
position: fixed;
|
||
top: 0;
|
||
left: 0;
|
||
width: 100%;
|
||
height: 100vh;
|
||
background-color: rgba(0, 0, 0, 0.5);
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
z-index: 10000;
|
||
animation: fadeIn 0.3s ease;
|
||
}
|
||
|
||
@keyframes fadeIn {
|
||
from {
|
||
opacity: 0;
|
||
}
|
||
to {
|
||
opacity: 1;
|
||
}
|
||
}
|
||
|
||
.error-modal-container {
|
||
width: 640rpx;
|
||
background: #FFFFFF;
|
||
border-radius: 24rpx;
|
||
overflow: hidden;
|
||
animation: slideUp 0.3s ease;
|
||
box-shadow: 0 8rpx 32rpx rgba(0, 0, 0, 0.15);
|
||
}
|
||
|
||
@keyframes slideUp {
|
||
from {
|
||
transform: translateY(100rpx);
|
||
opacity: 0;
|
||
}
|
||
to {
|
||
transform: translateY(0);
|
||
opacity: 1;
|
||
}
|
||
}
|
||
|
||
.error-modal-header {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
padding: 40rpx 40rpx 30rpx;
|
||
border-bottom: 1rpx solid #F0F0F0;
|
||
}
|
||
|
||
.error-icon {
|
||
font-size: 48rpx;
|
||
margin-right: 16rpx;
|
||
}
|
||
|
||
.error-modal-title {
|
||
font-size: 36rpx;
|
||
font-weight: 600;
|
||
color: #333333;
|
||
}
|
||
|
||
.error-modal-content {
|
||
padding: 40rpx;
|
||
max-height: 60vh;
|
||
overflow-y: auto;
|
||
}
|
||
|
||
.error-message {
|
||
font-size: 32rpx;
|
||
color: #FF6B6B;
|
||
font-weight: 500;
|
||
line-height: 1.6;
|
||
margin-bottom: 30rpx;
|
||
word-break: break-all;
|
||
}
|
||
|
||
.error-action {
|
||
background: #F8F9FA;
|
||
border-radius: 12rpx;
|
||
padding: 24rpx;
|
||
border-left: 4rpx solid #7FAD76;
|
||
}
|
||
|
||
.action-label {
|
||
font-size: 28rpx;
|
||
color: #7FAD76;
|
||
font-weight: 600;
|
||
display: block;
|
||
margin-bottom: 16rpx;
|
||
}
|
||
|
||
.action-text {
|
||
font-size: 28rpx;
|
||
color: #666666;
|
||
line-height: 1.8;
|
||
white-space: pre-line;
|
||
word-break: break-all;
|
||
}
|
||
|
||
.error-modal-footer {
|
||
display: flex;
|
||
border-top: 1rpx solid #F0F0F0;
|
||
}
|
||
|
||
.error-btn {
|
||
flex: 1;
|
||
height: 100rpx;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
font-size: 32rpx;
|
||
font-weight: 500;
|
||
cursor: pointer;
|
||
transition: all 0.3s;
|
||
}
|
||
|
||
.error-btn-cancel {
|
||
color: #999999;
|
||
border-right: 1rpx solid #F0F0F0;
|
||
background: #FAFAFA;
|
||
}
|
||
|
||
.error-btn-cancel:active {
|
||
background: #F0F0F0;
|
||
}
|
||
|
||
.error-btn-confirm {
|
||
color: #FFFFFF;
|
||
background: linear-gradient(135deg, #7FAD76 0%, #6A9A65 100%);
|
||
font-weight: 600;
|
||
}
|
||
|
||
.error-btn-confirm:active {
|
||
background: linear-gradient(135deg, #6A9A65 0%, #5A8A55 100%);
|
||
opacity: 0.9;
|
||
}
|
||
</style> |