slambb před 3 roky
rodič
revize
60c0bff9a5

+ 4 - 0
App.vue

@@ -2,11 +2,15 @@
 	export default {
 		onLaunch: function() {
 			console.log('App Launch')
+
+			// #ifdef APP-PLUS
 			// 锁住屏幕正方向
 			plus.screen.lockOrientation('portrait-primary');
 			// 屏幕常亮
 			// 下面这个打包要开启手机设置权限
 			plus.device.setWakelock(true);
+			// #endif
+
 		},
 		onShow: function() {
 			console.log('App Show')

+ 2 - 2
manifest.json

@@ -2,8 +2,8 @@
     "name" : "Heart_Tool",
     "appid" : "__UNI__8D02B4B",
     "description" : "",
-    "versionName" : "1.0.2",
-    "versionCode" : 20220806,
+    "versionName" : "1.0.6",
+    "versionCode" : 20221021,
     "transformPx" : false,
     /* 5+App特有相关 */
     "app-plus" : {

+ 23 - 13
pages.json

@@ -1,18 +1,27 @@
 {
 	"pages": [ //pages数组中第一项表示应用启动页,参考:https://uniapp.dcloud.io/collocation/pages
+		// {
+		// 	"path": "pages/index/index",
+		// 	"style": {
+		// 		"navigationBarTitleText": "心率测试"
+
+		// 	}
+		// }
+		//    ,
 		{
-			"path": "pages/index/index",
+			"path": "pages/HeartRate/HeartRate",
 			"style": {
-				"navigationBarTitleText": "心率测试"
+				"navigationBarTitleText": "心率测试",
+				"enablePullDownRefresh": false
 				
 			}
-		}
-	    ,{
-            "path" : "pages/game/game",
-            "style" :                                                                                    
-            {
-                "navigationBarTitleText": "",
-                "enablePullDownRefresh": false,
+
+		},
+		{
+			"path": "pages/game/game",
+			"style": {
+				"navigationBarTitleText": "",
+				"enablePullDownRefresh": false,
 				"app-plus": {
 					"titleNView": false,
 					"subNVues": [{
@@ -26,10 +35,11 @@
 						}
 					}]
 				}
-            }
-            
-        }
-    ],
+			}
+
+		}
+
+	],
 	"globalStyle": {
 		"navigationBarTextStyle": "black",
 		"navigationBarTitleText": "心率测试",

+ 1272 - 0
pages/HeartRate/HeartRate.vue

@@ -0,0 +1,1272 @@
+<template>
+	<view>
+		<view class="flex flex-direction align-center" style="background-color: #F0F0E9;">
+			<!-- 669 rpx -->
+			<view class="flex flex-direction justify-center " style="height: 669rpx;">
+				<view class="position-relative flex-direction align-center" style="color: #606162;">
+					<view class="position-absolute-center" style="height: 96rpx;">
+						<image style="height: 96rpx;width: 117rpx;" src="../../static/heart/heart@2x.png"></image>
+					</view>
+					<view class="flex align-end" style="width: 480rpx;padding-left: 117rpx;">
+						<block v-if="heartRate === 0 || !bTimerPlay">
+							<view class="text-center" style="font-size: 125rpx; width: 240rpx; ">--</view>
+						</block>
+						<block v-else>
+							<view class="text-center" style="font-size: 125rpx; width: 240rpx; ">{{heartRate}}</view>
+							<view style="font-size: 68rpx;">bpm</view>
+						</block>
+					</view>
+
+				</view>
+				<view style="text-align: center;margin-top: 60px;">
+					<text style="font-size: 59rpx;color: #8D8D8D;">{{nums}}</text>
+				</view>
+			</view>
+			<view class="cu-progress sm striped active">
+				<view class="bg-green" :style="[{ width:loading}]"></view>
+			</view>
+		</view>
+
+		<view class="bg-white padding-top-sm">
+			<view class="heart-view flex justify-center align-center padding " style="margin-top: 0;">
+				<view style="color: #606162;">播报状态,关闭后需要手动开启播报:</view>
+				<switch :checked="treatmentStatu.checked" @change="switchChange"></switch>
+			</view>
+
+			<view class="flex align-center justify-around">
+				<view class="margin-bottom-sm">最小值</view>
+				<view class="margin-bottom-sm">最大值</view>
+			</view>
+			<view class="flex align-center justify-center">
+				<button style="width: 300rpx; height: 98rpx; background-color: #F6F7F9;" class="cu-btn" @tap="showModal"
+					data-target="showPickerModal" data-type="min">
+					<span style="color:#F8A574; font-weight:bold;font-size: 20px; ">{{rateMin}} </span>
+				</button>
+				<view style="border: 2rpx solid #D8D8D8;width: 22rpx;height: 4rpx;margin: 50rpx 31rpx;"></view>
+				<button style="width: 300rpx;height: 98rpx; background-color: #F6F7F9;" class="cu-btn" @tap="showModal"
+					data-target="showPickerModal" data-type="max">
+					<span style="color:#F8A574; font-weight:bold;font-size: 20px;">{{rateMax}}</span>
+				</button>
+
+			</view>
+			<!-- background-color: #c6c6c6; -->
+			<!-- <scroll-view scroll-y="true" style="height: 200rpx;">
+				<view class="card-view padding-top padding-bottom" v-for="(item,index) in devicesList" :key="index"
+					:id="'task_'+index" :class="item.bRatio?'hardware-border':''" :style="{'z-index': threeZIndex}">
+					<view class="flex justify-between align-center">
+						<view class="flex justify-start align-center">
+							<image style="margin-left: 20rpx; width: 200rpx;height: 120rpx;" :src="item.icon"
+								mode="aspectFit">
+							</image>
+							<view style="width: 350rpx;margin-left: 10rpx;">
+								<view style="margin: 20rpx 0rpx; font-weight: bold; font-size: 18px; color: #565656;">
+									{{item.cname}}
+								</view>
+								<view style="font-size: 12px; white-space:pre-wrap;" class="make-text-bPurple">
+									{{item.describe}}{{item.bOldDevice?'(旧手柄)':''}}
+								</view>
+							</view>
+			
+						</view>
+						<image style="margin-right: 60rpx; width: 60rpx;height: 60rpx;"
+							:src="item.bRatio?'/static/devicesOther/radio-b.png':'/static/devicesOther/radio-g.png'"
+							mode="aspectFit" @tap="_onRadio(item,$event)"></image>
+					</view>
+				</view>
+			</scroll-view> -->
+			<!--  :class="item.bRatio?'hardware-border':''" -->
+			<view class="heart-view padding-top padding-bottom" v-for="(item,index) in devicesList" :key="index"
+				:id="'task_'+index" :style="{'z-index': threeZIndex}">
+				<view class="flex justify-between align-center">
+					<view class="flex justify-start align-center">
+						<image style="margin-left: 20rpx; width: 200rpx;height: 110rpx;" :src="item.icon"
+							mode="aspectFit">
+						</image>
+						<view style="margin-left: 10rpx;">
+							<view style="margin-bottom: 20rpx; font-weight: bold; font-size: 18px; color: #606162;">
+								{{item.cname}}
+							</view>
+							<view v-if="item.bRatio" style="font-size: 12px; white-space:pre-wrap;" class="make-text-bPurple">
+								已连接
+							</view>
+							<view v-else style="color: #8D8D8D;">未连接</view>
+						</view>
+
+					</view>
+					<view class="position-absolute-right-top">
+						<image style="width: 239rpx;height: 173rpx;" :src="item.bRatio?'/static/heart/radio-r@2x.png':'/static/heart/radio-g@2x.png'"
+						mode="aspectFit" @tap="_onRadio(item,$event)"></image>
+					</view>
+					<!-- <image style="margin-right: 60rpx; width: 60rpx;height: 60rpx;"
+						:src="item.bRatio?'/static/devicesOther/radio-b.png':'/static/devicesOther/radio-g.png'"
+						mode="aspectFit" @tap="_onRadio(item,$event)"></image> -->
+				</view>
+			</view>
+
+			<view class="flex margin position-relative">
+				<!-- 	清零计时 -->
+				<view class="position-relative" style="width: 282rpx ;height: 173rpx;" @click="closeTimer()">
+					<image style="width: 282rpx ;height: 173rpx;" src="../../static/heart/b_clear@2x.png"></image>
+					<view class="position-absolute-center flex text-18px text-bold">
+						<view style="color: #BE7F59;width: 150rpx;">清零</view>
+					</view>
+				</view>
+				<view class="position-absolute-right-top">
+					<image style="width: 495rpx ;height: 173rpx;" src="../../static/heart/b_start@2x.png"></image>
+					<view v-if="timer" class="position-absolute-center flex text-18px text-bold"  @click="pauseTime()">
+						<image style="width: 53rpx;height: 53rpx;" src="../../static/heart/pause@2x.png"></image>
+						<view style="color: #4D857C;width: 150rpx;margin-left: 30rpx;">暂停计时</view>
+					</view>
+					<view v-else class="position-absolute-center flex text-18px text-bold"  @click="startTimer()">
+						<image style="width: 53rpx;height: 53rpx;" src="../../static/heart/play@2x.png"></image>
+						<view style="color: #4D857C;width: 150rpx;margin-left: 30rpx;">开始计时</view>
+					</view>
+				</view>
+				<!-- 	<button style="margin-top: 2px;" type="warn" @click="pauseTime()">
+					暂停计时
+				</button> -->
+
+			</view>
+
+
+		</view>
+
+
+
+
+
+
+
+		<view class="cu-modal bottom-modal" :class="modalName == 'showPickerModal' ? 'show' : ''"
+			@touchmove.stop.prevent="moveHandle">
+			<view class="cu-dialog" style="border-top-right-radius: 20rpx; border-top-left-radius: 20rpx;">
+				<myPicker v-if="modalName == 'showPickerModal' ? true:false" :pickerObj="pickerObj"
+					@confirmEvent="onConfirm" @cancelEvent="onCancel"></myPicker>
+			</view>
+		</view>
+	</view>
+
+
+</template>
+
+<script>
+	import config from '@/common/config.js';
+	import reqUtil from '@/util/util-js/requstUtil.js';
+	import ble from '@/util/util-js/BLE.js';
+
+	import myPicker from '@/components/slambb-picker/slambb-picker.vue';
+	import pickerData from '@/components/slambb-picker/picker.js';
+
+	import {
+		mapState,
+		mapMutations
+	} from 'vuex';
+	// 获取 module
+	var testModule = uni.requireNativePlugin("TestModule")
+	export default {
+		computed: mapState(['bOpenBluetooth', 'bOpenSuccess', 'bListenAdapterStateChange', 'bListenDeviceFound',
+			'BLEConnectDevice', 'BLEGetServices', 'cIndex', 'bConnection', 'bVerifiedConnection', 'BLEInfoList',
+			'BLEDeviceShowList', 'finallyUseDevice', 'systemInfo', 'guideUnlockState', 'currentModeIndex',
+			"bListenerAccArray", 'LocationGameUrl', 'bGamePlaying'
+		]),
+		components: {
+			myPicker
+		},
+		data() {
+			return {
+				// 设备列表
+				devicesList: [],
+				currentItem: null,
+				//设置一个旧的连接item
+				oldItem: null,
+				searchObj: null,
+				//设置搜索超时时间
+				searchTimeOut: null,
+				option: null,
+				saveObj: null,
+				//搜索提示定时器
+				searchMac: null,
+				//
+				getServicesTimeout: null,
+				writeMacTimeout: null,
+				//限制关闭连接
+				bLimitClose: false,
+
+				//是否需要检测发起的连接蓝牙是否匹配上
+				bTestBondConnect: true,
+				//是否显示
+				bShow: true,
+				//切换连接
+				bSwitch: false,
+
+				//提示
+				circular: false,
+				currentHeight: 0,
+				threeTipHeight: 0,
+				threeZIndex: 0,
+				currentIndex: 5,
+
+				/**
+				 * 模块
+				 */
+				modalName: null,
+				pikerType: '',
+				pickerObj: {
+					pickerType: 'dateItem',
+					pickerTitle: '记时间'
+				},
+				//测试
+				text: '',
+				extraLine: [],
+
+				bShowGame: false,
+
+				bPlayText: false,
+				playTime: 0,
+
+				/**
+				 * 计时器部分
+				 */
+				timer: null,
+				nums: '0时0分0秒',
+				bTimerPlay: false,
+				bTimerPause: false,
+				hour: 0,
+				minute: 0,
+				second: 0,
+				millisecond: 0,
+				loading: '0%',
+				loadingCount: 0,
+				bLoading: false,
+
+				/**
+				 * 录音
+				 */
+				speechEngine: 'baidu',
+				searchText: '',
+
+				/**
+				 * 当前心率值
+				 */
+				// rateText: "0",
+				heartRate: 0,
+				bReach: false,
+				rateMax: 140,
+				rateMin: 120,
+
+				/**
+				 * switch
+				 */
+				treatmentStatu: {
+					checked: false
+				},
+				//检测录音权限
+				getRecordAudioPermissionInterval: null
+			}
+		},
+		onLoad(op) {
+			this.add("初始化");
+			uni.$on('retryConnectBLESuccess', this.onRetryConnectBLESuccess);
+			uni.$on('callbackCloseBLE', this.hardCallbackCloseBLE);
+			uni.$on('listenerBLE', this.onListenerBLE);
+
+			this.BLEInfoList.forEach((item, index, selfarr) => {
+				if (
+					// item.usageMode == 'CL809' ||
+					// 	item.usageMode == 'CL806' ||
+					// 	item.usageMode == 'CL831' ||
+					//CL838
+					item.usageMode == 'CL') {
+					let item = Object.assign({}, selfarr[index], {
+						bOldDevice: false
+					});
+					this.devicesList.push(item);
+				}
+			})
+
+
+			//subview
+			/**
+			 * $on 之后要调用 $off,不然会重复绑定
+			 */
+			this.onGetLocationGameUrl();
+			uni.$on("log", this.add)
+
+
+		},
+		onUnload() {
+			uni.$off('retryConnectBLESuccess', this.onRetryConnectBLESuccess);
+			uni.$off('callbackCloseBLE', this.hardCallbackCloseBLE);
+			uni.$off('listenerBLE', this.onListenerBLE);
+			//清除定时器
+			this.onClearTimeout();
+			uni.$off("log", this.add);
+
+			//停止语音
+			this.stopSpeak();
+			//释放唤醒资源
+			this.releaseToSpeech();
+		},
+		onShow() {
+			this.bShow = true;
+			uni.$on('updateBLEDeviceData', this.callbackUpdateBLEData);
+
+		},
+		onReady() {
+
+			// #ifdef APP-PLUS
+			//初始化语音
+			this.initTextToSpeech();
+			this.$set(this.treatmentStatu, 'checked', true);
+			//如果没有开启麦克风。可能导致唤醒失败
+			//轮询检测麦克风权限
+			if (this.getRecordAudioPermissionInterval) {
+				clearInterval(this.getRecordAudioPermissionInterval);
+				this.getRecordAudioPermissionInterval = null;
+			}
+			//开始检测
+			this.getRecordAudioPermissionInterval = setInterval(() => {
+				var ret = testModule.getRecordAudioPermission();
+				// console.log(ret);
+				if (ret.bPermission) {
+					if (this.getRecordAudioPermissionInterval) {
+						clearInterval(this.getRecordAudioPermissionInterval);
+						this.getRecordAudioPermissionInterval = null;
+					}
+					this.initWakeup();
+				}
+			}, 1000)
+			// this.initWakeup();
+			// #endif
+		},
+		onHide() {
+			//如果蓝牙弹出匹配框,会触发onHide。这时候处理蓝牙连接流程检测应等onShow 时候,再检测
+			this.bShow = false;
+
+			uni.$off('updateBLEDeviceData', this.callbackUpdateBLEData);
+		},
+		methods: {
+			...mapMutations(['initAdapter', 'onCreateBLESuccess', 'onGetBLEDeviceServices', 'onOnlyCloseBLEConnection',
+				'onGetRSSITransDistance',
+				'addBLEDevice', 'onWriteBLEConnectionValue', 'deleteBLEDevice', 'B_GetBondedDevices',
+				'B_OpenBLESetting', 'setGuideUnlockState', 'switchLevelList', 'onSetLocationGameUrl',
+				'onGetLocationGameUrl', 'onTestEmit', 'B_CloseBLEConnection', 'B_OnSetMTU'
+			]),
+			onEmit() {
+				this.onTestEmit();
+			},
+			/**
+			 * 显示游戏
+			 */
+			onShowGame() {
+				// uni.$emit("showGame");
+				// this.bShowGame = true;
+				uni.navigateTo({
+					url: "../game/game"
+				})
+			},
+			//设备回调事件
+			callbackUpdateBLEData(data) {
+				//如果在监听状态时候隐藏页面,返回
+				// console.log(data)
+				function getIntValue(formatType, offset) {
+					console.log("getIntValue:" + formatType + " = " + offset);
+					let array = new Uint8Array(data.ab);
+					console.log(array);
+					console.log(array[offset]);
+
+					switch (formatType) {
+						case 17:
+							return ble.unsignedByteToInt(array[offset]);
+						case 18:
+							return ble.unsignedBytesToInt(array[offset], array[offset + 1]);
+					}
+				}
+				// console.log(res, res.serviceId.toLocaleLowerCase());
+				let offset = 0;
+				let flags = getIntValue(17, offset);
+				console.log("flags:", flags);
+				let hearRateType = (flags & 1) == 0 ? 17 : 18;
+				console.log("hearRateType:" + hearRateType);
+				offset = offset + 1;
+				this.heartRate = getIntValue(hearRateType, offset);
+				console.log("heartRate:" + this.heartRate);
+				this.text = JSON.stringify(data.hex) + "\n" + "心率:" + this.heartRate + "bpm" + "\n";
+				// this.rateText = this.heartRate;
+
+				if (this.heartRate != 0) {
+					if (!this.bLoading) {
+						this.bLoading = true;
+						this.loadingCount = 0;
+					}
+					if (!this.bReach) {
+						if (this.heartRate >= this.rateMin && this.heartRate <= this.rateMax) {
+							this.playSpeak("达到运动心率" + this.heartRate);
+							console.log("达到运动心率");
+							//达到一次目标后播报
+							this.bReach = true;
+						}
+					} else {
+						if (this.heartRate < this.rateMin || this.heartRate > this.rateMax) {
+							this.playSpeak("当前心率" + this.heartRate);
+						}
+					}
+				} else if (this.bTimerPlay) {
+					this.bLoading = false;
+					//暂停心率
+					this.pauseTime();
+				}
+
+
+
+			},
+
+			onClearTimeout() {
+				// 退出后,清除计时器
+				if (this.searchMac) {
+					clearTimeout(this.searchMac);
+					this.searchMac = null;
+				}
+				//servicesTimeout
+				if (this.getServicesTimeout) {
+					clearTimeout(this.getServicesTimeout);
+					this.getServicesTimeout = null;
+				}
+				//写入指令writeMacTimeout
+				if (this.writeMacTimeout) {
+					clearTimeout(this.writeMacTimeout);
+					this.writeMacTimeout = null;
+				}
+
+				if (this.searchTimeOut) {
+					clearTimeout(this.searchTimeOut);
+					this.searchTimeOut = null;
+				}
+			},
+			//监听回调
+			onListenerBLE(res) {
+				console.log('onListenerBLE:', res, this.saveObj);
+				this.add(res.value);
+			},
+			/**
+			 * 连接设备成功
+			 */
+			ConnectionSuccess() {
+				for (let i = 0; i < this.devicesList.length; i++) {
+					let eq = this.devicesList[i];
+					//CL806_Bandage
+					if ((eq.ename.indexOf("CL806") > -1 && this.BLEConnectDevice.id == 0) ||
+						(eq.ename.indexOf("CL831") > -1 && this.BLEConnectDevice.id == 1) ||
+						(eq.ename.indexOf("CL809") > -1 && this.BLEConnectDevice.id == 2) ||
+						// CL838
+						(eq.ename.indexOf("CL") > -1 && this.BLEConnectDevice.id == 3)
+					) {
+
+						eq.bRatio = true;
+						this.currentItem = eq;
+						console.log('连接成功ConnectionSuccess:', eq);
+						uni.hideToast();
+					}
+				}
+			},
+			onCheckBondDevice() {
+				console.log("onCheckBondDevice", this.bTestBondConnect);
+				if (this.bTestBondConnect && this.bShow) {
+					/**
+					 * 假如手机没有匹配,断开连接
+					 */
+					console.log(2, this.finallyUseDevice);
+					this.B_GetBondedDevices({
+						deviceId: this.finallyUseDevice.deviceId,
+						success: (bondedDevice) => {
+							uni.hideToast();
+							if (bondedDevice == null) {
+								// if (plus.os.name == 'Android') 
+								{
+									//此问题 华为手机容易出现
+									//android手机已配对的设备 不存在,但是app 又直接连接成功了。提示,并且断开app连接
+									//1.关闭当前连接
+									this.onOnlyCloseBLEConnection({
+										getSuccess: () => {
+											this.currentItem.bRatio = false;
+											this.currentItem = null;
+											this.saveObj = null;
+										}
+									});
+									//2.跳转蓝牙设置
+									uni.showModal({
+										title: '蓝牙配对失败',
+										content: '请跳转后点击配对BGBox_2020,成功后手动跳转回哔蹦重新连接。',
+										success: (res) => {
+											if (res.confirm) {
+												this.B_OpenBLESetting();
+											}
+										}
+									})
+								}
+
+							}
+						}
+					});
+					this.bTestBondConnect = false;
+				} else {
+					uni.hideToast();
+				}
+			},
+			onRetryConnectBLESuccess() {
+				this.ConnectionSuccess();
+			},
+			hardCallbackCloseBLE() {
+
+				if (this.BLEConnectDevice == null && this.currentItem && !this.bSwitch) {
+					this.currentItem.bRatio = false;
+					this.currentItem = null;
+					this.saveObj = null;
+				}
+				//如果限制不走重连
+				if (this.bLimitClose) return;
+
+				console.log(this.currentItem, this.oldItem);
+				if (this.oldItem && this.currentItem && this.currentItem.id == this.oldItem.id) {
+					// this.$store.state.bConnection = false;
+					if (this.currentItem.bRatio) {
+						uni.showToast({
+							title: '设备断开连接!',
+							icon: 'none',
+							duration: 2000,
+							mask: true
+						})
+						this.currentItem.bRatio = false;
+						this.oldItem = null;
+						this.currentItem = null;
+						//断开连接
+						this.$store.state.bVerifiedConnection = false;
+					}
+				} else if (this.bConnection && this.BLEConnectDevice) {
+					//假如匹配过程中断开连接
+					this.$store.state.BLEConnectDevice = null;
+					this.$store.state.bConnection = false;
+					// uni.hideToast();
+					// console.log("//假如匹配过程中断开连接");
+					uni.showToast({
+						title: '连接失败,尝试重新连接。',
+						icon: 'none',
+						mask: true,
+						duration: 2000
+					})
+					if (this.getServicesTimeout) {
+						clearTimeout(this.getServicesTimeout);
+						this.getServicesTimeout = null;
+					}
+				}
+
+			},
+			/**
+			 * 开始查找设备
+			 * */
+			startBluetoothDeviceDiscovery() {
+				//在页面显示的时候判断是都已经初始化完成蓝牙适配器若成功,则开始查找设备
+				let _self = this;
+				if (_self.bOpenBluetooth) {
+					//1.第一步还是先进行设备搜索
+					_self.onCanStart();
+				} else {
+					_self.initAdapter(() => {
+						_self.startBluetoothDeviceDiscovery();
+					});
+				}
+			},
+			/**
+			 * 通过检测手机连接的设备进行连接,null 的话进行搜索操作
+			 */
+			onBondedDeviceConnect(data) {
+				let _self = this;
+				//获取手机本身已连接的硬件,
+				//示例
+				// {
+				// 	"deviceId": "C5:5C:19:04:00:30",
+				// 	"name": "BGBox_202012",
+				// 	"RSSI": -74,
+				// 	"localName": "BGBox_20201",
+				// 	"advertisServiceUUIDs": ["00001812-0000-1000-8000-00805F9B34FB", "0000FFF0-0000-1000-8000-00805F9B34FB"]
+				// }
+
+				console.log("onBoded ***************:", _self.finallyUseDevice);
+
+				_self.B_GetBondedDevices({
+					deviceId: _self.finallyUseDevice == null ? null : _self.finallyUseDevice.deviceId,
+					success: (bondedDevice) => {
+						console.log("bondedDevice:", bondedDevice);
+						//获取已和蓝牙连接的设备
+						if (bondedDevice != null) {
+							//如果用户匹配了对应的设备,直接用对应的设备来连接
+							let setDevice = {
+								"deviceId": bondedDevice.address,
+								"name": bondedDevice.name,
+								"RSSI": -74,
+								"localName": "",
+								"advertisServiceUUIDs": ["00001812-0000-1000-8000-00805F9B34FB",
+									"0000FFF0-0000-1000-8000-00805F9B34FB"
+								]
+							}
+
+							let obj = Object.assign({}, setDevice, _self.currentItem);
+							//finallyUserDevice 就是最后一次使用搜索到的设备信息
+							_self.saveObj = Object.assign({}, setDevice, {
+								id: _self.currentItem.id
+							});
+							uni.showToast({
+								title: '设备连接中...',
+								icon: 'loading',
+								duration: 10000,
+								mask: true
+							})
+							console.log("GetBondedDevices:::===", setDevice, _self.currentItem, obj);
+							// 先直连,然后判断版本
+							_self._onConnectDevice(obj);
+
+							//getBond后发起的连接,不需要检测了
+							_self.bTestBondConnect = false;
+						} else {
+							console.log("没有获取到绑定的设备");
+							uni.showToast({
+								title: '获取匹配设备失败',
+								icon: 'none',
+								mask: true
+							})
+						}
+					}
+				});
+			},
+			//开始搜索
+			onCanStart() {
+				let _self = this;
+				uni.showToast({
+					title: '设备连接中...',
+					icon: 'loading',
+					duration: 15000,
+					mask: true
+				})
+				// 1.已经搜索到的,根据蓝牙回调的mac 地址判断合法性。
+				uni.startBluetoothDevicesDiscovery({
+					allowDuplicatesKey: true,
+					success: res => {
+						// console.log("startBluetoothDevicesDiscovery:", res);
+
+						_self.bSwitch = false;
+						_self.onBluetoothDeviceFound();
+					},
+					fail: res => {
+						console.log("搜索失败!");
+						_self.initAdapter(() => {
+							_self.startBluetoothDeviceDiscovery();
+						});
+					}
+				});
+				// 2.没有搜索到的,一段时间后处理。比如手机已经连接了设备,但是app 里面搜索不到,也没记录使用过的。
+				if (_self.searchTimeOut) {
+					clearTimeout(_self.searchTimeOut);
+					_self.searchTimeOut = null;
+				}
+				//搜索一段时间后,停止搜索
+				if (plus.os.name == 'iOS') {
+					_self.searchTimeOut = setTimeout(() => {
+						_self.stopBluetoothDevicesDiscovery();
+						//搜索失败后,再检查是否和手机配对的设备连接
+						_self.onBondedDeviceConnect();
+					}, 4000)
+				} else {
+					_self.searchTimeOut = setTimeout(() => {
+						_self.stopBluetoothDevicesDiscovery();
+					}, 8000)
+				}
+
+			},
+			/**
+			 * 停止搜索蓝牙设备
+			 */
+			stopBluetoothDevicesDiscovery() {
+				uni.stopBluetoothDevicesDiscovery({
+					success: e => {
+						// console.log('停止搜索蓝牙设备:' + e.errMsg);
+					},
+					fail: e => {
+						console.log('停止搜索蓝牙设备失败,错误码:' + e.errCode);
+					}
+				});
+			},
+			/**
+			 * 发现外围设备
+			 */
+			onBluetoothDeviceFound() {
+				let _self = this;
+				_self.searchObj = null;
+				uni.onBluetoothDeviceFound(res => {
+					/**
+					 * 获取在蓝牙模块生效期间所有已发现的蓝牙设备。包括已经和本机处于连接状态的设备。
+					 */
+					// console.log("onBluetoothDeviceFound:", res); Fitcent_
+					res.devices.forEach(device => {
+						if (
+						// device.name.indexOf('CL809') > -1 ||
+						// 	device.name.indexOf('CL806') > -1 ||
+						// 	device.name.indexOf('CL831') > -1 ||
+							//CL838
+							device.name.indexOf('CL') > -1
+						) {
+							//如果搜索的设备名 不是对应当前设备类型,过滤
+							// if (_self.currentItem.deviceName.indexOf('CL806') == -1 || _self.currentItem.deviceName.indexOf('CL831') == -1) return;
+							//寻找到对应设备时候,其余的返回
+							if (_self.searchObj) return;
+							_self.searchObj = device;
+							if (_self.searchTimeOut) {
+								clearTimeout(_self.searchTimeOut);
+								_self.searchTimeOut = null;
+							}
+							//currentItem 是mode 页面选中的item
+							let obj = Object.assign({}, device, _self.currentItem);
+
+							_self.saveObj = Object.assign({}, {
+								id: _self.currentItem.id
+							}, device);
+
+							console.log(device, "****", obj, _self.saveObj, _self.currentItem)
+							// 先直连,然后判断版本
+							_self._onConnectDevice(obj);
+							_self.stopBluetoothDevicesDiscovery();
+						}
+					})
+
+				})
+			},
+
+			onBack() {
+				uni.navigateBack({
+					delta: 1
+				})
+			},
+
+			// 提示点击连接设备
+			_onConnectDevice(item) {
+				//servicesTimeout
+				if (this.getServicesTimeout) {
+					clearTimeout(this.getServicesTimeout);
+					this.getServicesTimeout = null;
+				}
+				//写入指令writeMacTimeout
+				if (this.writeMacTimeout) {
+					clearTimeout(this.writeMacTimeout);
+					this.writeMacTimeout = null;
+				}
+				//创建一个连接,需要对应close
+				this.onCreateBLESuccess({
+					item: item,
+					getSuccess: () => {
+						console.log("****创建一个连接*******");
+						this.getServicesTimeout = setTimeout(() => {
+							this.onGetBLEDeviceServices({
+								item: item,
+								success: (res) => {
+									console.log("******getBLEDeviceServices************",
+										res);
+									//连接成功了,设置旧的item
+									this.oldItem = this.currentItem;
+									setTimeout(() => {
+										this.saveObj = Object.assign({}, this
+											.saveObj, {
+												deviceMac: '',
+												bOldDevice: false
+											});
+										this.addBLEDevice(this.saveObj);
+										this.ConnectionSuccess();
+
+									}, 2000)
+								}
+							});
+						}, 2500);
+					}
+				})
+			},
+
+			_onRadio(item, event) {
+				console.log(this.bConnection, this.BLEGetServices);
+				if (this.bConnection && this.BLEGetServices && this.BLEGetServices.length == 0) {
+					console.log("***直接获取服务*******");
+					this.onGetBLEDeviceServices({
+						item: item,
+						success: (res) => {
+							console.log("*****getBLEDeviceServices************");
+							//连接成功了,设置旧的item
+							this.oldItem = this.currentItem;
+
+							setTimeout(() => {
+								this.saveObj = Object.assign({}, this
+									.saveObj, {
+										deviceMac: '',
+										bOldDevice: false
+									});
+								this.addBLEDevice(this.saveObj);
+								this.ConnectionSuccess();
+
+							}, 2000)
+
+						}
+					});
+					return;
+				}
+				if (!item.bRatio) {
+					//设置默认值
+					this.bTestBondConnect = true;
+					if (this.BLEConnectDevice) {
+						this.onOnlyCloseBLEConnection({
+							getSuccess: () => {
+								if (this.currentItem)
+									this.currentItem.bRatio = false;
+
+								this.currentItem = null;
+								this.currentItem = item;
+								this.bSwitch = true;
+								console.log("this.currentItem1 ==:", this.currentItem, this
+									.bOpenBluetooth,
+									this.BLEConnectDevice);
+								this.startBluetoothDeviceDiscovery();
+							}
+						});
+						return;
+					}
+					this.currentItem = null;
+					this.currentItem = item;
+					// console.log("this.currentItem ==2:", this.currentItem, this.bOpenBluetooth, this.BLEConnectDevice);
+					this.startBluetoothDeviceDiscovery();
+				}
+			},
+			onClise() {
+				this.B_CloseBLEConnection({
+					deviceId: "C5:5C:19:04:01:22"
+				});
+			},
+			//跳转进入升级页面,
+			onNavUpdateDevice() {
+				//需要连接设备后,才能进入升级
+				if (!this.currentItem || !this.currentItem.bRatio || !this.BLEConnectDevice) {
+					uni.showToast({
+						title: '请先连接硬件!',
+						icon: 'none'
+					})
+					return;
+				}
+				this.bLimitClose = true;
+				uni.navigateTo({
+					// url: "../devices-update/devices-update?deviceType=" + this.option.deviceType
+					url: "../devices-update/devices-update"
+				})
+			},
+
+			add: function(data) {
+				// console.log(data)
+
+				if (this.extraLine.length > 500) {
+					this.extraLine = [];
+					this.text = this.extraLine.join('\n');
+				}
+
+				this.extraLine.push(data);
+				this.text = this.extraLine.join('\n');
+
+
+			},
+			remove: function(e) {
+				if (this.extraLine.length > 0) {
+					// this.extraLine.pop();
+					this.extraLine = [];
+					this.text = this.extraLine.join('\n');
+				}
+			},
+			writeBLEValue(str) {
+				this.onWriteBLEConnectionValue({
+					value: str
+				})
+			},
+			onKeyInput: function(event) {
+				this.onSetLocationGameUrl(event.target.value);
+			},
+
+			initTextToSpeech() {
+				// 调用同步方法
+				var ret = testModule.initTextToSpeech();
+				console.log("initTextToSpeech:", ret);
+				// uni.showToast({
+				// 	title: "initTextToSpeech"
+				// })
+			},
+			onGetRecordAudioPermission() {
+				var ret = testModule.getRecordAudioPermission();
+				// console.log(ret);
+				// console.log(ret.bPermission)
+			},
+			initWakeup() {
+				console.log("initWakeup!");
+				// 调用同步方法
+				testModule.initWakeup(null, (data) => {
+					if (data.code === 200) {
+						console.log("开启唤醒失败");
+						uni.showToast({
+							title: "初始化唤醒失败!",
+							icon: 'none'
+						})
+						//停止语音
+						this.stopSpeak();
+						this.$set(this.treatmentStatu, 'checked', false);
+					} else if (data.code === 0) {
+						uni.showToast({
+							title: "初始化唤醒成功!",
+							icon: 'none'
+						})
+						this.$set(this.treatmentStatu, 'checked', true);
+						//记录一下回调
+						plus.globalEvent.addEventListener("onWakeup", this.onWakeupCallback);
+						setTimeout(() => {
+							this.recordSpeakDown();
+						}, 1000)
+					}
+				});
+
+			},
+			onWakeupCallback(data) {
+				// this.text = JSON.stringify(data);
+				console.log("onWakeupCallback:" + JSON.stringify(data));
+				if (data.word == "实时心率" || data.word == "播放") {
+					uni.showToast({
+						icon: 'none',
+						title: data.word
+					})
+					this.playSpeak('当前心率' + this.heartRate);
+				} else if (data.word == "停止播报") {
+					this.$set(this.treatmentStatu, 'checked', false) // 手动修改switch的状态,视图会同步更新
+					//停止语音
+					this.stopSpeak();
+					//停止唤醒
+					this.releaseToSpeech();
+				}
+
+			},
+			releaseToSpeech() {
+				// 调用同步方法
+				var ret = testModule.releaseToSpeech();
+				// console.log(ret);
+				uni.showToast({
+					title: "releaseToSpeech",
+					icon:'none'
+				})
+			},
+			playSpeak(text) {
+				let currentTime = new Date().getTime() - this.playTime;
+				if (currentTime > 3000) {
+					// 调用同步方法
+					var ret = testModule.playSpeak(text);
+					// console.log(ret);
+					this.playTime = new Date().getTime();
+				}
+
+			},
+			stopSpeak() {
+				// 调用同步方法
+				var ret = testModule.stopSpeak();
+				console.log(ret);
+				uni.showToast({
+					title: "stopSpeak",
+					icon:'none'
+				})
+			},
+
+			startLuyin() {
+				console.log('语音输入')
+				let _this = this;
+				let options = {};
+				options.engine = _this.speechEngine
+				options.punctuation = false; // 是否需要标点符号 
+				options.timeout = 10 * 1000; //超时时间
+				plus.speech.startRecognize(options, function(s) {
+					console.log(s) //识别的结果
+					_this.searchText = s
+					plus.speech.stopRecognize(); // 关闭
+				});
+
+			},
+			recordSpeakDown() {
+				// 调用同步方法
+				var ret = testModule.recordSpeakDown();
+				console.log("recordSpeakDown" + JSON.stringify(ret));
+				// uni.showToast({
+				// 	title: "recordSpeakDown"
+				// })
+			},
+			recordSpeakUp() {
+				// 调用同步方法
+				var ret = testModule.recordSpeakUp();
+				console.log("recordSpeakUp" + JSON.stringify(ret));
+				// uni.showToast({
+				// 	title: "recordSpeakUp"
+				// })
+			},
+
+			/**
+			 * 计时器
+			 */
+			startTimer() {
+				//用心率判断是否佩戴上
+				if (this.heartRate === 0) {
+					uni.showToast({
+						title: "清连接并佩戴后开始",
+						icon: 'none'
+					})
+					return;
+				}
+				if (this.timer) {
+					clearInterval(this.timer);
+					this.timer = null;
+				}
+				this.timer = setInterval(() => {
+					this.millisecond = this.millisecond + 50;
+					if (this.millisecond >= 1000) {
+						this.millisecond = 0;
+						this.second = this.second + 1;
+
+						//计算进度条数据
+						if (this.bLoading && this.loadingCount < 60) {
+							this.loadingCount++;
+							this.loading = Math.round((this.loadingCount / 60) * 100) + "%";
+						}
+					}
+					if (this.second >= 60) {
+						this.second = 0;
+						this.minute = this.minute + 1;
+					}
+
+					if (this.minute >= 60) {
+						this.minute = 0;
+						this.hour = this.hour + 1;
+					}
+					this.nums = this.hour + '时' + this.minute + '分' + this.second + '秒';
+				}, 50);
+				this.bTimerPlay = true;
+			},
+			pauseTime() {
+				if (this.timer) {
+					clearInterval(this.timer);
+					this.timer = null;
+				}
+			},
+			closeTimer() {
+				this.hour = this.minute = this.second = 0; //初始化
+				this.millisecond = 0; //毫秒
+				if (this.timer) {
+					clearInterval(this.timer);
+					this.timer = null;
+				}
+				this.nums = 0 + '时' + 0 + '分' + 0 + '秒';
+				this.bTimerPlay = false;
+			},
+
+
+			onRateChange(type, value) {
+				switch (type) {
+					case "min":
+						if ((value < 0 && this.rateMin > 0) || (value > 0 && this.rateMin + 1 < this.rateMax)) {
+							this.rateMin += value;
+						} else {
+							uni.showToast({
+								icon: 'none',
+								title: 'rateMin 不能超过最大值或者小于0',
+							})
+						}
+
+						break;
+					case "max":
+						if ((value < 0 && this.rateMin + 1 < this.rateMax) || value > 0) {
+							this.rateMax += value;
+						} else {
+							uni.showToast({
+								icon: 'none',
+								title: 'rateMax 不能小于最小值',
+							})
+						}
+						break;
+				}
+			},
+
+
+			onConfirm(args) {
+				this.hasUpdate = true;
+				console.log('onConfirm', args);
+				let tempValue = parseInt(args.value);
+				if (this.pikerType == 'min') {
+
+					if (tempValue < this.rateMax) {
+						this.rateMin = parseInt(args.value);
+					} else {
+						uni.showToast({
+							icon: 'none',
+							title: '不能大于或等于最大值',
+						})
+					}
+				} else if (this.pikerType == 'max') {
+					if (tempValue > this.rateMin) {
+						this.rateMax = parseInt(args.value);
+					} else {
+						uni.showToast({
+							icon: 'none',
+							title: '不能小于或等于最小值',
+						})
+					}
+				}
+				this.hideModal();
+			},
+			onCancel(value) {
+				console.log('onCancel', value);
+				this.modalName = null;
+			},
+			showModal(e) {
+				// console.log(e.currentTarget.type);
+				this.modalName = e.currentTarget.dataset.target;
+				this.pikerType = e.currentTarget.dataset.type;
+				switch (this.pikerType) {
+					case 'min':
+						this.$set(this.pickerObj, 'pickerLeftList', pickerData.getRateList().rateList);
+						this.$set(this.pickerObj, 'pickerRightList', []);
+						this.$set(this.pickerObj, 'pickerUnit', '');
+						this.$set(this.pickerObj, 'pickerType', 'singleItem');
+						this.$set(this.pickerObj, 'pickerTitle', '最小心率');
+						this.$set(this.pickerObj, 'defaultValue', this.rateMin);
+						this.$set(this.pickerObj, 'showInput', true);
+						break;
+					case 'max':
+						this.$set(this.pickerObj, 'pickerLeftList', pickerData.getRateList().rateList);
+						this.$set(this.pickerObj, 'pickerRightList', []);
+						this.$set(this.pickerObj, 'pickerUnit', '');
+						this.$set(this.pickerObj, 'pickerType', 'singleItem');
+						this.$set(this.pickerObj, 'pickerTitle', '最大心率');
+						this.$set(this.pickerObj, 'defaultValue', this.rateMax);
+						this.$set(this.pickerObj, 'showInput', true);
+						break;
+				}
+			},
+			hideModal(e) {
+				this.modalName = null;
+				this.pikerType = '';
+			},
+			moveHandle() {
+				return;
+			},
+
+			switchChange(e) {
+				let value = e.target.value
+				let that = this
+				this.$set(this.treatmentStatu, 'checked', value) // 将点击改变的状态赋给treatmentStatu.checked
+				if (value) {
+					uni.showModal({
+						title: '提示',
+						content: '是否开启语音播报?',
+						success: function(res) {
+							if (res.confirm) {
+								//todo 打开语音播报
+								console.log('用户点击确定')
+								that.initTextToSpeech();
+								that.initWakeup();
+							} else if (res.cancel) {
+								that.$set(that.treatmentStatu, 'checked', false) // 手动修改switch的状态,视图会同步更新
+							}
+						}
+					});
+				} else {
+					uni.showModal({
+						title: '提示',
+						content: '是否关闭语音播报?',
+						success: function(res) {
+							if (res.confirm) {
+								//todo 关闭语音播报
+								console.log('用户点击确定')
+								//停止语音
+								that.stopSpeak();
+								that.releaseToSpeech();
+							} else if (res.cancel) {
+								that.$set(that.treatmentStatu, 'checked', true) // 手动修改switch的状态,视图会同步更新
+							}
+						}
+					});
+				}
+			}
+		}
+
+	}
+</script>
+
+<style>
+	page{
+		background-color: #FFFFFF;
+	}
+	
+	.hardware-border {
+		border: 1rpx solid #9898FF;
+		box-sizing: border-box;
+	}
+
+	.swiper {
+		flex: 1;
+		background-color: rgba(0, 0, 0, 0.32);
+	}
+
+	.swiper-item {
+		flex: 1;
+	}
+
+	.bluetooth-guide-number {
+		border-radius: 18px;
+		border-width: 1rpx;
+		width: 30px;
+		height: 30px;
+		text-align: center;
+		line-height: 30px;
+		color: #000000;
+		/* border-color: #FFFFFF; */
+		background-color: #FFFFFF;
+		margin-right: 13px;
+	}
+
+	.text-box {
+		margin: 20rpx;
+		display: flex;
+		width: 95%;
+		min-height: 300rpx;
+		max-height: 600rpx;
+		background-color: #c6c6c6;
+		justify-content: center;
+		align-items: center;
+		text-align: left;
+		font-size: 30upx;
+		color: #353535;
+		line-height: 1.8;
+		border: 1rpx solid #555555;
+		overflow-y: hidden;
+	}
+
+
+	.content {
+		align-content: center;
+		height: 300rpx;
+		background-color: #F4F5F6;
+	}
+
+	.uni-input {
+		border: 1rpx solid #000000;
+	}
+
+	.recordingStyle {
+		border-radius: 20px;
+		text-align: center;
+		color: #fff;
+		font-size: 15px;
+		background-color: #409eff;
+		margin-bottom: 15px;
+	}
+</style>

+ 134 - 43
pages/index/index.vue

@@ -24,28 +24,31 @@
 			<view class="title">Cocos预览地址:{{LocationGameUrl}}</view>
 			<input class="uni-input" :value="LocationGameUrl" @input="onKeyInput" placeholder="输入cocos预览地址" />
 		</view> -->
-
-		<view class="card-view padding-top padding-bottom" v-for="(item,index) in devicesList" :key="index"
-			:id="'task_'+index" :class="item.bRatio?'hardware-border':''" :style="{'z-index': threeZIndex}">
-			<view class="flex justify-between align-center">
-				<view class="flex justify-start align-center">
-					<image style="margin-left: 20rpx; width: 200rpx;height: 120rpx;" :src="item.icon" mode="aspectFit">
-					</image>
-					<view style="width: 350rpx;margin-left: 10rpx;">
-						<view style="margin: 20rpx 0rpx; font-weight: bold; font-size: 18px; color: #565656;">
-							{{item.cname}}
-						</view>
-						<view style="font-size: 12px; white-space:pre-wrap;" class="make-text-bPurple">
-							{{item.describe}}{{item.bOldDevice?'(旧手柄)':''}}
+		<scroll-view scroll-y="true" style="height: 200px;background-color: #c6c6c6;">
+			<view class="card-view padding-top padding-bottom" v-for="(item,index) in devicesList" :key="index"
+				:id="'task_'+index" :class="item.bRatio?'hardware-border':''" :style="{'z-index': threeZIndex}">
+				<view class="flex justify-between align-center">
+					<view class="flex justify-start align-center">
+						<image style="margin-left: 20rpx; width: 200rpx;height: 120rpx;" :src="item.icon"
+							mode="aspectFit">
+						</image>
+						<view style="width: 350rpx;margin-left: 10rpx;">
+							<view style="margin: 20rpx 0rpx; font-weight: bold; font-size: 18px; color: #565656;">
+								{{item.cname}}
+							</view>
+							<view style="font-size: 12px; white-space:pre-wrap;" class="make-text-bPurple">
+								{{item.describe}}{{item.bOldDevice?'(旧手柄)':''}}
+							</view>
 						</view>
-					</view>
 
+					</view>
+					<image style="margin-right: 60rpx; width: 60rpx;height: 60rpx;"
+						:src="item.bRatio?'/static/devicesOther/radio-b.png':'/static/devicesOther/radio-g.png'"
+						mode="aspectFit" @tap="_onRadio(item,$event)"></image>
 				</view>
-				<image style="margin-right: 60rpx; width: 60rpx;height: 60rpx;"
-					:src="item.bRatio?'/static/devicesOther/radio-b.png':'/static/devicesOther/radio-g.png'"
-					mode="aspectFit" @tap="_onRadio(item,$event)"></image>
 			</view>
-		</view>
+		</scroll-view>
+
 
 
 
@@ -83,8 +86,17 @@
 			close
 		</button> -->
 		<view style="text-align: center;">
-			<text style="font-size: 80px;">{{heartRate}}</text>
-			<text style="font-size: 20px;">bpm</text>
+			<!-- :class="?'active':''" :style="[{ width:loading?'60%':''}]" -->
+			<view>稳定心率进度:</view>
+			<view class="cu-progress round sm striped active">
+				<view class="bg-green" :style="[{ width:loading}]"></view>
+			</view>
+			<!--  -->
+			<text v-if="heartRate === 0 || !bTimerPlay" style="font-size: 80px;">--</text>
+			<block v-else>
+				<text style="font-size: 80px;">{{heartRate}}</text>
+				<text style="font-size: 20px;">bpm</text>
+			</block>
 		</view>
 		<view style="text-align: center;margin: 10px 0px;">
 			<text style="font-size: 40px;color: blue;">{{nums}}</text>
@@ -99,6 +111,8 @@
 			<button style="margin-top: 2px;" type="warn" @click="closeTimer()">
 				清零计时
 			</button>
+
+			<!-- <button @click="onGetRecordAudioPermission()">test</button> -->
 		</view>
 		<!-- <scroll-view class="text-box" scroll-y="true" >
 			<scroll-view scroll-x="true">
@@ -111,7 +125,6 @@
 		</view> -->
 		<!-- <view class="content">
 		</view> -->
-
 		<view class="cu-modal bottom-modal" :class="modalName == 'showPickerModal' ? 'show' : ''"
 			@touchmove.stop.prevent="moveHandle">
 			<view class="cu-dialog" style="border-top-right-radius: 20rpx; border-top-left-radius: 20rpx;">
@@ -205,11 +218,14 @@
 				timer: null,
 				nums: '0时0分0秒',
 				bTimerPlay: false,
-				bTimerpause: false,
+				bTimerPause: false,
 				hour: 0,
 				minute: 0,
 				second: 0,
 				millisecond: 0,
+				loading: '0%',
+				loadingCount: 0,
+				bLoading: false,
 
 				/**
 				 * 录音
@@ -232,6 +248,8 @@
 				treatmentStatu: {
 					checked: false
 				},
+				//检测录音权限
+				getRecordAudioPermissionInterval: null
 			}
 		},
 		onLoad(op) {
@@ -241,7 +259,11 @@
 			uni.$on('listenerBLE', this.onListenerBLE);
 
 			this.BLEInfoList.forEach((item, index, selfarr) => {
-				if (item.usageMode == 'CL806' || item.usageMode == 'CL831') {
+				if (
+				item.usageMode == 'CL809' ||
+					item.usageMode == 'CL806' ||
+					item.usageMode == 'CL831' ||
+					item.usageMode == 'CL838') {
 					let item = Object.assign({}, selfarr[index], {
 						bOldDevice: false
 					});
@@ -278,11 +300,31 @@
 
 		},
 		onReady() {
+
+			// #ifdef APP-PLUS
 			//初始化语音
 			this.initTextToSpeech();
 			this.$set(this.treatmentStatu, 'checked', true);
 			//如果没有开启麦克风。可能导致唤醒失败
-			this.initWakeup();
+			//轮询检测麦克风权限
+			if (this.getRecordAudioPermissionInterval) {
+				clearInterval(this.getRecordAudioPermissionInterval);
+				this.getRecordAudioPermissionInterval = null;
+			}
+			//开始检测
+			this.getRecordAudioPermissionInterval = setInterval(() => {
+				var ret = testModule.getRecordAudioPermission();
+				// console.log(ret);
+				if (ret.bPermission) {
+					if (this.getRecordAudioPermissionInterval) {
+						clearInterval(this.getRecordAudioPermissionInterval);
+						this.getRecordAudioPermissionInterval = null;
+					}
+					this.initWakeup();
+				}
+			}, 1000)
+			// this.initWakeup();
+			// #endif
 		},
 		onHide() {
 			//如果蓝牙弹出匹配框,会触发onHide。这时候处理蓝牙连接流程检测应等onShow 时候,再检测
@@ -339,19 +381,31 @@
 				this.text = JSON.stringify(data.hex) + "\n" + "心率:" + this.heartRate + "bpm" + "\n";
 				// this.rateText = this.heartRate;
 
-				if (!this.bReach) {
-					if (this.heartRate >= this.rateMin && this.heartRate <= this.rateMax) {
-						this.playSpeak("达到运动心率" + this.heartRate);
-						console.log("达到运动心率");
-						//达到一次目标后播报
-						this.bReach = true;
+				if (this.heartRate != 0) {
+					if (!this.bLoading) {
+						this.bLoading = true;
+						this.loadingCount = 0;
 					}
-				} else {
-					if (this.heartRate < this.rateMin || this.heartRate > this.rateMax) {
-						this.playSpeak("当前心率" + this.heartRate);
+					if (!this.bReach) {
+						if (this.heartRate >= this.rateMin && this.heartRate <= this.rateMax) {
+							this.playSpeak("达到运动心率" + this.heartRate);
+							console.log("达到运动心率");
+							//达到一次目标后播报
+							this.bReach = true;
+						}
+					} else {
+						if (this.heartRate < this.rateMin || this.heartRate > this.rateMax) {
+							this.playSpeak("当前心率" + this.heartRate);
+						}
 					}
+				} else if (this.bTimerPlay) {
+					this.bLoading = false;
+					//暂停心率
+					this.pauseTime();
 				}
 
+
+
 			},
 
 			onClearTimeout() {
@@ -389,7 +443,10 @@
 					let eq = this.devicesList[i];
 					//CL806_Bandage
 					if ((eq.ename.indexOf("CL806") > -1 && this.BLEConnectDevice.id == 0) ||
-						(eq.ename.indexOf("CL831") > -1 && this.BLEConnectDevice.id == 1)) {
+						(eq.ename.indexOf("CL831") > -1 && this.BLEConnectDevice.id == 1) ||
+						(eq.ename.indexOf("CL809") > -1 && this.BLEConnectDevice.id == 2) ||
+						(eq.ename.indexOf("CL838") > -1 && this.BLEConnectDevice.id == 3)
+					) {
 
 						eq.bRatio = true;
 						this.currentItem = eq;
@@ -634,10 +691,13 @@
 					/**
 					 * 获取在蓝牙模块生效期间所有已发现的蓝牙设备。包括已经和本机处于连接状态的设备。
 					 */
-					// console.log("onBluetoothDeviceFound:", res);
+					// console.log("onBluetoothDeviceFound:", res); Fitcent_
 					res.devices.forEach(device => {
-						if (device.name.indexOf('Fitcent_CL806') > -1 || device.name.indexOf('CL831') > -
-							1) {
+						if (device.name.indexOf('CL809') > -1 ||
+							device.name.indexOf('CL806') > -1 ||
+							device.name.indexOf('CL831') > -1 ||
+							device.name.indexOf('CL838') > -1
+						) {
 							//如果搜索的设备名 不是对应当前设备类型,过滤
 							// if (_self.currentItem.deviceName.indexOf('CL806') == -1 || _self.currentItem.deviceName.indexOf('CL831') == -1) return;
 							//寻找到对应设备时候,其余的返回
@@ -818,20 +878,35 @@
 			initTextToSpeech() {
 				// 调用同步方法
 				var ret = testModule.initTextToSpeech();
+				console.log("initTextToSpeech:", ret);
+				// uni.showToast({
+				// 	title: "initTextToSpeech"
+				// })
+			},
+			onGetRecordAudioPermission() {
+				var ret = testModule.getRecordAudioPermission();
 				console.log(ret);
-				uni.showToast({
-					title: "initTextToSpeech"
-				})
+				console.log(ret.bPermission)
 			},
 			initWakeup() {
+				console.log("initWakeup!");
 				// 调用同步方法
 				testModule.initWakeup(null, (data) => {
 					if (data.code === 200) {
 						console.log("开启唤醒失败");
+						uni.showToast({
+							title: "初始化唤醒失败!",
+							icon: 'none'
+						})
 						//停止语音
 						this.stopSpeak();
 						this.$set(this.treatmentStatu, 'checked', false);
-					}else if(data.code === 0){
+					} else if (data.code === 0) {
+						uni.showToast({
+							title: "初始化唤醒成功!",
+							icon: 'none'
+						})
+						this.$set(this.treatmentStatu, 'checked', true);
 						//记录一下回调
 						plus.globalEvent.addEventListener("onWakeup", this.onWakeupCallback);
 						setTimeout(() => {
@@ -839,11 +914,11 @@
 						}, 1000)
 					}
 				});
-				
+
 			},
 			onWakeupCallback(data) {
 				// this.text = JSON.stringify(data);
-				console.log(JSON.stringify(data));
+				console.log("onWakeupCallback:" + JSON.stringify(data));
 				if (data.word == "实时心率" || data.word == "播放") {
 					uni.showToast({
 						icon: 'none',
@@ -921,6 +996,14 @@
 			 * 计时器
 			 */
 			startTimer() {
+				//用心率判断是否佩戴上
+				if(this.heartRate === 0){
+					uni.showToast({
+						title:"请佩戴好手表后开始",
+						icon:'none'
+					})
+					return;
+				}
 				if (this.timer) {
 					clearInterval(this.timer);
 					this.timer = null;
@@ -930,6 +1013,12 @@
 					if (this.millisecond >= 1000) {
 						this.millisecond = 0;
 						this.second = this.second + 1;
+						
+						//计算进度条数据
+						if (this.bLoading && this.loadingCount < 60) {
+							this.loadingCount++;
+							this.loading = Math.round((this.loadingCount / 60) *100) + "%";
+						}
 					}
 					if (this.second >= 60) {
 						this.second = 0;
@@ -942,6 +1031,7 @@
 					}
 					this.nums = this.hour + '时' + this.minute + '分' + this.second + '秒';
 				}, 50);
+				this.bTimerPlay = true;
 			},
 			pauseTime() {
 				if (this.timer) {
@@ -957,6 +1047,7 @@
 					this.timer = null;
 				}
 				this.nums = 0 + '时' + 0 + '分' + 0 + '秒';
+				this.bTimerPlay = false;
 			},
 
 

binární
static/heart/b_clear@2x.png


binární
static/heart/b_start@2x.png


binární
static/heart/device838@2x.png


binární
static/heart/heart@2x.png


binární
static/heart/pause@2x.png


binární
static/heart/play@2x.png


binární
static/heart/radio-g@2x.png


binární
static/heart/radio-r@2x.png


+ 10 - 0
util/util-css/main.css

@@ -4171,6 +4171,16 @@ scroll-view.cu-steps .cu-item {
 	z-index: 2;
 }
 
+.heart-view {
+	/* border: 1rpx solid #000000; */
+	border-radius: 17px;
+	margin: 35rpx;
+	overflow: hidden;
+	background-color: #F6F7F9;
+	position: relative;
+	z-index: 2;
+}
+
 
 .only-arrow {
 	width: 60rpx;

+ 48 - 0
util/util-js/devices.js

@@ -49,6 +49,54 @@ const getDeviceList = function() {
 				/***********/
 				currentVersion: '', //当前固件版本
 				latestVersion: '' //服务器最新固件版本
+			},
+			{
+				id: 2,
+				cname: "CL809",
+				ename: "CL809_Bandage",
+				icon: "/static/devicesIcon/bandage.png",
+				mIcon: "/static/devicesIcon/bandage.png",
+				bRatio: false,
+				usageMode: 'CL809',
+				describe: '胸口绑带',
+				limitType: 'rebound', //限制回弹版本
+				deviceType: 'Hardware', //指的是使用手机本身加速计计算
+				deviceName: 'CL809', //连接硬件名称
+				limitDis: 0.5,
+				/**
+				 * 主服务的 uuid Cofing
+				 */
+				primaryUUID: '', //蓝牙主服务id
+				PRIMARY_SERVICE: '0000180D-0000-1000-8000-00805f9b34fb',
+				PRIMARY_WRITE: '00002A38-0000-1000-8000-00805f9b34fb',
+				PRIMARY_NOTIFY: '00002A37-0000-1000-8000-00805f9b34fb',
+				/***********/
+				currentVersion: '', //当前固件版本
+				latestVersion: '' //服务器最新固件版本
+			},
+			{
+				id: 3,
+				cname: "心率带",//CL838
+				ename: "CL_Bandage", //CL838_Bandage
+				icon: "/static/heart/device838@2x.png",
+				mIcon: "/static/heart/device838@2x.png",
+				bRatio: false,
+				usageMode: 'CL', //CL838
+				describe: '心率带',
+				limitType: 'rebound', //限制回弹版本
+				deviceType: 'Hardware', //指的是使用手机本身加速计计算
+				deviceName: 'CL', //连接硬件名称 CL838
+				limitDis: 0.5,
+				/**
+				 * 主服务的 uuid Cofing
+				 */
+				primaryUUID: '', //蓝牙主服务id
+				PRIMARY_SERVICE: '0000180D-0000-1000-8000-00805f9b34fb',
+				PRIMARY_WRITE: '00002A38-0000-1000-8000-00805f9b34fb',
+				PRIMARY_NOTIFY: '00002A37-0000-1000-8000-00805f9b34fb',
+				/***********/
+				currentVersion: '', //当前固件版本
+				latestVersion: '' //服务器最新固件版本
 			}
 		];