import Taro from "@tarojs/taro"; import dayjs from "dayjs"; import classnames from "classnames"; import { debounce } from "lodash"; import React, { Component, PropsWithChildren, useEffect, useState, } from "react"; /*** redux ***/ import { connect } from "react-redux"; /*** redux end ***/ import { Block, View, Text, Image, Video, Input, Button, } from "@tarojs/components"; import { notifyBLECharacteristicValueChange, sendCommand, } from "@/utils/bluetoothWXAPI"; import { deviceCommandSamples, bleCommandSamples, } from "@/components/bluetoot/connection/wl200"; import { minSecToS, s_to_ms, s_to_hms, sleep, s_to_s } from "@/utils/util"; // import { DeviceToolKit as DeviceToolKitWE100 } from "@flossom-npm/iot-translater-we100"; import { DeviceToolKit as DeviceToolKitWM, TResponseFromDevice as TResponseFromDeviceWM, } from "@flossom-npm/iot-translater"; import commandMap from "@/utils/commandMap"; import { Popup } from "@antmjs/vantui"; import { fr200BleCommand } from "@/components/bluetoot/connection/fr200"; import { go, getStorageSync, setStorageSync, msg } from "@/utils/traoAPI"; import { InstrumentInfo } from "@/utils/Interface"; /* 公共组件 */ import Navbar from "@/components/navbar/navbar"; import PopupCountdown from "@/components/popup/popup-countdown"; import PopupStepTips from "@/components/popup/popup-step-tips"; import PopupConfirm from "@/components/popup/popup-confirm"; import PopupAlert from "@/components/popup/popup-alert"; import PopupStatus from "@/components/popup/popup-status"; import PopupInstrumentUploadTips from "@/components/popup/popup-instrument-upload-tips"; import ConnectionBluetoot from "@/components/bluetoot/connection"; /* 公共组件 END */ /* 本页组件 */ import ElectricityView from "./components/ElectricityView/Electricity"; import ModeListView from "./components/ModeList/FR200"; import Footer from "./components/Footer/FR200"; import WaterTest from "./components/WaterTest/index"; /* 本页组件 END */ import Echarts from "./components/Echart"; import Gears from "./components/Gears"; import "./FR200.less"; const deviceToolKitInstanceFR200 = new DeviceToolKitWM("FR200"); let deviceToolKitInstance = deviceToolKitInstanceFR200; let currentTimeTimer: any = null; // 当前项目时间定时器 let CountdownTimer: any = null; let timer: any = null; let showTipsTimer: any = null; let loadingTipsTimer: any = null; // 蓝牙连接提示 // 设备运行时间校准频率,每多少秒校准一次 const TIME_CALIBRATION_FREQUENCY = 5; const MODE_WORKING_ENUM = { STANDBY: "standby", // 待命 WORKING: "working", // 工作 PAUSE: "pause", END: "end", }; // 不同模式启动前的倒计时时间 let CountDownTime = {}; let DeviceSyncData = { totalWorkingMinutes: 0, totalWorkingSeconds: 0, }; class IotCarePlanFR200 extends Component { constructor(props) { super(props); this.state = { name: "FR200", title: "FR200", // 页面标题 // 当前设备 currentDevice: { name: "", model: "", }, /** 连接设备 */ hasVersion: false, // 是否已查询到版本号 basicModeList: [], //模式列表 modelActiveIndex: 0, //模式下标 sliderProgress: 22, DeviceConnectStatus: 1, // 设备连接状态 0未连接 1已连接 Electricity: 4, // WL200电量 matrixElectricity: 4, // WE200发箍电量 workMode: "", //当前模式 // 挡位调节组件参数 GearData: [ { name: "额头", forehead: 5, Total: 10 }, { name: "左脸颊", forehead: 6, Total: 10 }, { name: "右脸颊", forehead: 2, Total: 10 }, ], waterStepList: [ { value: "Step1", name: "额头", finish: false, schedule: 0, color: "#c2e5f3", forehead: 0, }, { value: "Step2", name: "左脸颊", finish: false, schedule: 0, color: "#c2e5f3", forehead: 0, }, { value: "Step3", name: "右脸颊", finish: false, schedule: 0, color: "#c2e5f3", forehead: 0, }, ], waterStepIndex: 0, // 进度条定时器 timerIdSchedule: null, timerIdSuccess: null, currentShowDialog: "", showVideoPlayBtn: true, // 视频播放按钮 duration: 0, // 视频总时长 hadShowBreakTips: false, // 是否展示过支架断开提示 isConnectShow: false, // 是否弹出连蓝牙弹窗:在蓝牙断开时弹出 /** 连接设备 End */ /** 护理过程 */ currentGear: 5, // 默认档位 isRuningTest: 1, // 是否正在测试 isShowStepTips: false, // 是否显示介绍步骤弹窗 isConnectionBlutoot: true, // 是否已连接蓝牙 isShowNurse: true, // 是否开始并显示护理 FR200默认已经开始准备护理 isStopNurse: false, // 是否暂停护理 isEndNurse: false, // 是否结束护理 errorTips: "", // 错误提示 /** 护理过程 END*/ // 模式列表 currentWorkModeType: 1, // 现在的模式类型 currentVideoSrc: "", // 现在模式的视频地址 isModeLock: false, // 模式是否锁定 isSwitchActiveMode: false, // 是否显示弹窗切换模式 ModeList: [], ModeType: "all", // all 1基础护理 2专区护理 3专研促渗 4敏期护理 5智能测肤 ActiveModeItem: { openSourceData: [], }, // 当前选中模式 SwitchActiveModeItem: {}, // 切换选中模式 ModeID: "mode_", // 模式KEY activeModeID: "", // 当前选中模式ID:用于高亮 ModeStepIndex: 0, // 当前护理功效步骤:每个步骤时间不定,所以时间另外计算,根据步骤显示 ModeStepTimeArray: [], // 护理功效时间步骤,用于切换显示GIF // TestModeStepIndex: 0, // 水分测试步骤 EssenceStepIndex: 0, // 精华促渗步骤 MaskModeStepIndex: 0, // 面膜促渗步骤 EssenceBuzzingIndex: 0, // 精华蜂鸣 EssenceVibrateIndex: 0, // 精华震动 MaskModeBuzzingIndex: 0, // 面膜蜂鸣 MaskModeVibrateIndex: 0, // 面膜震动 currentServiceData: { // 当前展示的开启暂停GIF: 因为时间判断不方便,所以单独领出来 startSource: "", stopSource: "", }, // 倒计时 isShowCountdown: false, // 倒计时弹窗 countdown: 3, // 是否结束护理 isEndCarePlan: false, currentTime: "10:00", // 倒计时时间:WR200以视频为准 currentVideoTime: "10:00", // 当前视频时间 // 护理时间不够 isNotEnoughTime: false, // 通用错误提示 isShowErrorTipsText: false, // 护理模式切换错误弹窗 errorTipsText: "", // 护理模式切换错误提示 isShowNursingSuccess: false, // 护理成功弹窗 isShowTipsSave: false, // 切换模式时,提示是否保存部分护理记录 // 初次护理弹窗 isFirstTipShow: false, nurseInfo: [], // 上一次护理记录未生成,是否继续连接设备 isShowReReadRecordConnect: false, // isFirstEntryMode: false, // 模式首次打开 isShowHistoryMsg: false, // 是否显示正在同步历史 showEcharts: false, echartsData: "", //传给echarts图表的数据 }; } // 不涉及渲染的页面变量 isRuning: any = true; // 设备默认运行中:fr200贴脸就会自动开始工作 jsonStatus: any = {}; // 同步设备返回数据,用于结束 workJsonStatus: any = {}; // 同步工作中的仪器 tempModeCurrent: any = {}; // 临时保存的当前模式 elapsedTime: any = 0; // 设备已运行时间 workStatus: any = ""; // 工作状态 FR200NursingHistory: any = null; // 护理缓存历史 hadCheckReport = false; // 是否已检查仪器护理记录 hadGotInstrumentHistoryData = false; // 是否已缓存仪器历史数据 hadLoadedPage = false; // 判断是否首次进入页面 // FR200同步状态 FR200Status: any = { gear: 1, // 1-5 挡位 isCharging: false, // 是否充电 joulePerSecond: 0, // 每秒焦耳 0-10 nasolabialOrMandibularOutput: false, // 鼻唇或下颌输出 partition: 0, // 分割? pointOutChangeSide: false, // 交叉输出点 impedance: 107, // 阻抗/能量等级:1档<200 200<2档<280 280<3档<360 后面以此类推每加一档+80抗阻 }; // 1档 等于0<200,2档等于200<280,3档等于280<360 /** FR200模式类型:名称 */ ModeTypeArray: string[] = [ "all", "base", "eyes", "zone", "permeation", "sensitive", "intelligence", ]; /** 基础版:脸部/眼部/PRO 设备使用时,会自动开启暂停 */ BaseModeType: string[] = [ "face", "eyes", "nasolabialFold", "mandibularLine", "headLiftingPro", ]; async onLoad(option) { console.log(option, "跳转过来的数据"); if (option.modeId) { this.setState({ activeModeID: option.modeId }); } // 保持屏幕常亮 Taro.setKeepScreenOn({ keepScreenOn: true, }); this.initData(); this.getInstrumentClockSummary(); this.getInstrumentClockDetail(); } componentDidMount() {} componentWillUnmount() {} componentDidShow() { console.log("页面显示了"); if (!this.hadLoadedPage) { this.hadLoadedPage = true; // 二次进入页面(非首次进入) return; } this.getFR200NursingHistory(); // 重置初始值,每次进入页面重新检查面罩护理记录 this.hadCheckReport = false; this.hadGotInstrumentHistoryData = false; } componentDidHide() { console.log("Hide"); // 页面隐藏后,下次显示需要重新检查记录 this.hadCheckReport = false; } async initData() { let obj = getStorageSync("instrument_detail"); if (obj) { this.setState({ currentDevice: obj, title: obj.name, }); await this.GetModeList(obj.id); // 如果不存在设备模式值,则判断为首次进入,弹窗提示 let isFirstTipShow = getStorageSync("first_instrument_" + obj.id); if (!isFirstTipShow) { this.firstNurseInfo(); } } // 开发者工具 const platform = Taro.getSystemInfoSync().platform; if (platform !== "devtools") { // 仅手机端初始化蓝牙 this.init(); } } getOption() { const option = { grid: { top: 8, right: 8, bottom: 24, left: 36 }, xAxis: { type: "category", data: ["Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"], }, yAxis: { type: "value", }, series: [ { data: [820, 932, 901, 934, 1290, 1330, 1320], type: "line", smooth: true, }, ], tooltip: { trigger: "axis", }, // title: { // text: 'ECharts 示例' // }, // tooltip: {}, // xAxis: { // data: ['A', 'B', 'C', 'D', 'E'] // }, // yAxis: {}, // series: [{ // name: '数量', // type: 'bar', // data: [5, 20, 36, 10, 10] // }] }; return option; } async init() { // 查询离线记录汇总 const queryInstructionParams = { commandType: "InfoQuery", infoQueryType: "offlineClockInInfo", dataType: "summary", }; let commandBuffer = deviceToolKitInstance.toBleCommand( queryInstructionParams as any ); sendCommand({ value: commandBuffer }).then((res) => { console.log("查询查询离线记录汇总 参数为=>", queryInstructionParams); }); // 监听蓝牙连接状态改变 Taro.onBLEConnectionStateChange(this.listener); await this.notifyBLECharacteristicValueChange(); // this.handleWorkStatus(false, MODE_WORKING_ENUM.STANDBY); } listener = (res) => { console.log("listener res", res); if (res?.connected) return; // 蓝牙未连接才执行下面逻辑 Taro.offBLECharacteristicValueChange((res) => { console.log("offBLECharacteristicValueChange", res); }); clearTimeout(loadingTipsTimer); console.log(commandMap.WL200Command, "监听到蓝牙断开, 打开断开提示"); this.workStatus = ""; // 显示蓝牙断开弹窗 this.setState({ isConnectShow: true, // 打开蓝牙链接弹窗 isConnectionBlutoot: false, // 断开蓝牙 // isShowCountdown: false, // 关闭倒计时,防止倒计时还在运行 }); }; // 获取模式列表 GetModeList = async (id) => { let params = { instrumentId: id, }; let res = await InstrumentInfo.modeInfoList(params); if (res.data.code === 200) { if (res.data.data.length > 0) { this.setState({ ModeList: res.data.data, // 模式列表 ActiveModeItem: res.data.data[0], // 让模式列表正常显示 ModeType: this.ModeTypeArray[res.data.data[0].modeClass], }); if (this.state.activeModeID != "") { let res1 = res.data.data.find((e) => e.id == this.state.activeModeID); setTimeout(() => { this.modeCurrentFun(res1); }, 100); } else { setTimeout(() => { this.modeCurrentFun(res.data.data[0]); }, 100); } let res1 = res.data.data.find((e) => e.id == this.state.activeModeID); setTimeout(() => { this.modeCurrentFun(res1); }, 100); } else { this.setState({ ModeList: [] }); } } }; /** * 倒计时弹窗 * param 重置倒计时 * callback 回调函数 */ showCountdownFun(count = 3, callback: any = null) { this.setState({ countdown: count, }); setTimeout(() => { clearInterval(CountdownTimer); this.setState({ isShowCountdown: true, }); CountdownTimer = setInterval(() => { if (this.state.countdown === 0) { clearInterval(CountdownTimer); this.setState({ isShowCountdown: false, }); if (callback) callback(); } else { this.setState({ countdown: this.state.countdown - 1, }); } }, 1000); }, 0); } /** * @name 选中/切换护理模式 * */ modeCurrentFun = async (data, isNotCheck = false) => { let { isShowNurse, activeModeID } = this.state; // 护理检查改变模式,是否提示切换护理模式 // isNotCheck为真时,不进行校验,直接切换 this.tempModeCurrent = data; // 仅在未开始护理前,切换模式的时候提示模式弹窗 // FR200默认开始护理 if (!isShowNurse) { this.openStepTips(); } // 如果按钮不可点击则报错,内部自带检查底部按钮函数 // this.onEmitErrorTips(); if (!isNotCheck) { let isReturn = this.modeRuningChange(); if (isReturn) return; } // 根据模式,动态设置底部按钮样式 let currentWorkModeType = 1; if (data.modeType === "moistureTest") { currentWorkModeType = 3; } else if ( data.modeType === "maskPenetration" || data.modeType === "essence" ) { currentWorkModeType = 2; } this.setState({ ActiveModeItem: data, activeModeID: data.id, ModeID: "mode_" + data.id, ModeStepIndex: 0, waterStepIndex: 0, // 水分测试步骤 EssenceStepIndex: 0, // 精华促渗步骤 MaskModeStepIndex: 0, // 面膜促渗步骤 ModeType: this.ModeTypeArray[data.modeClass], currentWorkModeType, }); // 切换模式时:重新设置视频地址 this.VideoSrcLoad(data.modeVideo); // 开发中,暂时允许直接切换 // 每次切换模式时清空一下历史数据 this.changeItemUpdateFR200NursingHistory(); this.stepNext(); // 仅切换模式,不执行开始逻辑 // FR200水分测试不可自动运行,需手动点击开始测试,手动启动检测 // 其他模式可以自动运行 if (data.modeType !== "moistureTest") { setTimeout(() => { this.onNursingTap("switch"); }, 800); } }; /** 设备运行中切换模式 */ modeRuningChange() { // 运行中切换模式逻辑 // 当且仅当,护理模式时长大于设备最低时常时,提示是否保存护理记录。 // 若不满足,直接切换 if ( this.workStatus === MODE_WORKING_ENUM.PAUSE || this.workStatus === MODE_WORKING_ENUM.WORKING ) { const { totalWorkingMinutes, totalWorkingSeconds } = DeviceSyncData; const totalTime = totalWorkingMinutes * 60 + totalWorkingSeconds; // 设备时间 let { ActiveModeItem } = this.state; if (!ActiveModeItem || totalTime === 0) { return false; } if (this.isRuning && this.state.DeviceConnectStatus == 1) { // 提示切换护理模式 if (this.isCheckNurseTime()) { // 满足时间条件,提示是否保存部分护理记录 this.judgementWorkStatus( MODE_WORKING_ENUM.PAUSE, this.state.ActiveModeItem?.modeType ); this.setState({ isShowTipsSave: true, }); return true; } } } return false; } // 加减组件- 减号操作 handleMinus = async (data, inde) => { let GearData = this.state.GearData; if (GearData[inde].forehead !== 1) { GearData[inde].forehead = GearData[inde].forehead - 1; this.setState({ GearData }); } }; // 加减组件- 加号操作 handleAdd = async (data, inde) => { let GearData = this.state.GearData; if (GearData[inde].forehead !== GearData[inde].Total) { GearData[inde].forehead = GearData[inde].forehead + 1; this.setState({ GearData }); } }; /** 切换护理模式 */ switchModeCurrentFun = async (data) => { this.setState({ SwitchActiveModeItem: data, activeModeID: data.id, ModeID: "mode_" + data.id, }); }; // 打开模式切换弹窗 openModeSwitch = () => { console.log("openModeSwitch"); this.setState({ isSwitchActiveMode: true, }); }; // 取消并关闭切换护理模式弹窗 cancelModeSwitchBtn = () => { this.setState({ isSwitchActiveMode: false, }); }; // 弹窗确定切换护理模式 confirmModeSwitchBtn = () => { let { SwitchActiveModeItem } = this.state; this.cancelModeSwitchBtn(); this.modeCurrentFun(SwitchActiveModeItem); this.setState({ ModeType: this.ModeTypeArray[SwitchActiveModeItem.modeClass], }); }; stepNext = () => { let modeClass = this.state.ActiveModeItem.modeClass; this.workStatus = "pause"; this.isRuning = true; // 暂停也是运行中 this.setState({ ModeType: this.ModeTypeArray[modeClass], isShowNurse: true, isStopNurse: true, }); setTimeout(() => { this.handleWorkStatus(false, MODE_WORKING_ENUM.STANDBY); }); }; /** 开始护理按钮:点击开始,页面进行到下一步 */ onStartNurse = async () => { this.stepNext(); setTimeout(() => { this.onNursingTap(); // // 倒计时弹窗: 倒计时完成后,自动开始,并判断弹窗 // let downNum = CountDownTime[this.state.ActiveModeItem.modeType] || 3; // this.showCountdownFun(downNum, () => {}); }, 500); // 如果检查失败,则报错 this.onEmitErrorTips(); }; /** 完成护理,跳转到水分 */ onsuccess = async () => { let waterStepList = this.state.waterStepList; this.setState({ isShowNursingSuccess: true, }); // return; let { currentDevice, ActiveModeItem } = this.state; let params: any = {}; params = { instrumentId: currentDevice.id, instrumentName: currentDevice.name, modeId: ActiveModeItem.id, modeName: ActiveModeItem.modeName, nursingTime: s_to_hms(this.elapsedTime), nursingData: JSON.stringify({ GearData: [...waterStepList], }), }; // params = { ...params, ...nursingData }; let res: any = await InstrumentInfo.apiNursingLog.addLog( JSON.stringify(params) ); setTimeout(async () => { this.setState({ isShowNursingSuccess: false, }); let date = new Date(); let year = date.getFullYear(); let month = (date.getMonth() + 1).toString().padStart(2, "0"); let day = date.getDate().toString().padStart(2, "0"); let formattedDate = `${year}.${month}.${day}`; this.moistureTest( params.nursingData, formattedDate, ActiveModeItem.id, currentDevice.id ); }, 2000); }; async moistureTest( nursingData, formattedDate, ActiveModeItemId, currentDeviceId ) { let data = { queryDate: formattedDate, instrumentId: currentDeviceId, }; let res = await InstrumentInfo.fr200.moistureTest(data); let echartsData = res.data.rows; for (let i = 0; i < echartsData.length; i++) { for (let j = i + 1; j < echartsData.length; j++) { if ( echartsData[i].createTime.split(" ")[0] == echartsData[j].createTime.split(" ")[0] ) { let result = Date.parse(echartsData[i].createTime) - Date.parse(echartsData[j].createTime); if (result < 0) { echartsData.splice(i, 1); } else { echartsData.splice(j, 1); } } } } let gears: any = []; let eDate: any = []; echartsData?.map((item) => { console.log(item, "查看数据"); const result = item.createTime.split(" ")[0].substring(5); eDate.push(result); item.nursingData = JSON.parse(item.nursingData); let level: any = 0; item.nursingData?.GearData?.map((gear) => { level = level + gear.forehead; }); level = Math.floor(level / 3); gears.push(level); }); echartsData = { gears, eDate, }; setStorageSync("moistureEachtsData", JSON.stringify(echartsData)); let report =true // go(`/recoding/pages/moisture_test_report/moisture_test_report?data=${allData.nursingData}&date=${allData.createTime}&modeId=${allData.modeId}&id=${allData.instrumentId}&echartsData=${JSON.stringify(echartsData)}`); go( `/recoding/pages/moisture_test_report/moisture_test_report?data=${nursingData}&date=${formattedDate}&modeId=${ActiveModeItemId}&id=${currentDeviceId}&echartsData=${JSON.stringify( echartsData )}&report=${report }` ); } /** * @name 不可切换光照提示 * @description 检测紧贴肌肤 */ onEmitErrorTips = async () => { setTimeout(() => { let { ActiveModeItem } = this.state; // 按钮不可点击时,提示报错 this.showTips("检测到您的设备没有紧贴肌肤,请紧贴肌肤后重新尝试"); }); }; look() { let that = this; // this.setState({ showEcharts: !this.state.showEcharts }); let stop = 0; let time = setInterval(function () { stop++; let random = Math.random(); that.setState({ echartsData: random }); if (stop >= 20) { clearInterval(time); } }, 1000); } updata() {} // 绘制能量图 drawProwerPicture() {} /** 切换光照 */ onSwitchChange = async () => { // todo let { isStopNurse } = this.state; if (isStopNurse) { // 开始光照逻辑 this.onNursingTap(); this.switchVideoPlay(); // 开始 } else { // 暂停光照逻辑 this.handleWorkStatus(false, MODE_WORKING_ENUM.PAUSE); this.switchVideoPause(); // 暂停 } this.setState({ isStopNurse: !isStopNurse, }); }; /** * @name 每次进入设备运行页,打开首个模式的介绍弹窗 */ openStepTips = () => { let isFirstEntryModeNot = getStorageSync( "isFirstEntryMode_" + this.state.currentDevice.id ); // 1.如果没有持久化不再提示,每次进入都会弹窗提示 if (!isFirstEntryModeNot) { // 2.必须要有数据才弹窗 if (this.state.ActiveModeItem.openSourceData.length > 0) { this.setState({ isShowStepTips: true }); } } }; closeStepTips = (data) => { if (data.isLocal) { setStorageSync("isFirstEntryMode_" + this.state.currentDevice.id, true); // 关闭首次进入弹窗 } this.setState({ isShowStepTips: false }); }; /** 蓝牙连接相关 */ switchBLEMatch = (jsonStatus: any) => { console.log("蓝牙连接相关", jsonStatus); switch (jsonStatus.bleCommandType) { // 如果设备配对链接发送配对码的时候,设备应答小程序配对码是否正确。 case "SendMatchCode": if (jsonStatus.matchedSuccess) { console.log("设备配对成功"); this.setState({ DeviceConnectStatus: 1, }); } break; } }; // 蓝牙特征更改 notifyBLECharacteristicValueChange = () => { console.log("notifyBLECharacteristicValueChange deviceInfo 参数为=>"); const bluetoothInfo = this.props.bluetoothInfo; notifyBLECharacteristicValueChange({ deviceId: bluetoothInfo.deviceId, servicesuuid: bluetoothInfo.servicesuuid, characteristicsuuid1: bluetoothInfo.characteristicsuuid1, characteristicsuuid0: bluetoothInfo.characteristicsuuid0, }).then((res) => { Taro.onBLECharacteristicValueChange((value) => { const jsonStatus: any = deviceToolKitInstance.toJsonStatus(value.value); console.log( "onBLECharacteristicValueChange jsonStatus => ", jsonStatus ); if (!jsonStatus || jsonStatus == null) { return; } this.workStatus = jsonStatus.workStatus; // 记录工作状态 // end 和 endWork 都是护理结束, endWork不关机, end 关机, 对小程序而言处理流程都一样 if (jsonStatus.workStatus && jsonStatus.workStatus == "endWork") { jsonStatus.workStatus = "end"; console.log(jsonStatus.workStatus, "护理结束"); } switch (jsonStatus?.commandType) { //设备配对和连接的返回 case "BleMatch": // 蓝牙相关指令 this.switchBLEMatch(jsonStatus); break; //设备状态同步 case "DeviceStatusSync": if (jsonStatus.battery) { // 防止抖动 this.setState({ Electricity: jsonStatus.battery, }); } switch (jsonStatus.workStatus) { case "standby": //设备的待机状态 可能workMode字段为空 console.log("设备状态同步 待机状态", jsonStatus); if (this.BaseModeType.includes(jsonStatus.workMode)) { this.openTips("检测到您的设备没有紧贴肌肤,请紧贴肌肤"); } break; case "pause": //设备的暂停状态 console.log("设备状态同步 暂停状态", jsonStatus); if (this.BaseModeType.includes(jsonStatus.workMode)) { this.openTips( "检测到您的设备没有紧贴肌肤,请紧贴肌肤后重新尝试" ); } break; case "working": //设备的运行中状态 console.log("设备状态同步 工作中状态", jsonStatus.workMode); // 脸部模式 if (this.BaseModeType.includes(jsonStatus.workMode)) { this.closeTips(); } break; default: break; } if (jsonStatus.workMode === "moistureTest") { if ("success" === jsonStatus.testStatus) { let waterStepList = this.state.waterStepList; // 到达第几个step let waterStepIndex = this.state.waterStepIndex; // 代表5秒以后最后拿到的结果 if (waterStepList[waterStepIndex].finish) { // 获取等级 waterStepList[waterStepIndex].forehead = jsonStatus.waterLevel; // 检测完成 if (waterStepIndex === 2) { this.setState({ isRuningTest: 4, }); } else { // 启动检测 this.setState({ isRuningTest: 1, }); } // 一共有3条数据 let num = waterStepIndex; if (waterStepIndex < 2) { num = waterStepIndex + 1; this.waterTestNext(num); } this.setState({ waterStepIndex: num, waterStepList, }); } } else { let waterStepList = this.state.waterStepList; let waterStepIndex = this.state.waterStepIndex; // 5秒后获取的结果 if (waterStepList[waterStepIndex].finish) { // 获取失败后,把进度条清理0 waterStepList[waterStepIndex].schedule = 0; waterStepList[waterStepIndex].finish = false; this.setState({ waterStepList, isRuningTest: 3, }); } } // 水分测试 // testStatus:success } console.log("设备运行中?", this.isRuning); if (jsonStatus?.workMode === this.state.ActiveModeItem.modeType) { if (this.state.workMode !== jsonStatus?.workMode) { this.setState({ workMode: jsonStatus?.workMode, // 仅当设备上报模式与小程序一致时,才允许改变小程序变量缓存 }); } // 判断设备是否在运行中(护理中) // 仅当设备模式与小程序是否一致,才允许更改设备运行时间 if (this.isRuning) { if ( this.state.DeviceConnectStatus === 1 && jsonStatus.workStatus !== MODE_WORKING_ENUM.END ) { // 水分测试手动检测时间,不自动计算倒计时 if (jsonStatus?.workMode !== "moistureTest") this.updateDeviceSyncData( { totalWorkingMinutes: jsonStatus.totalWorkingMinutes, totalWorkingSeconds: jsonStatus.totalWorkingSeconds, }, jsonStatus ); } } } break; //设备对控制指令的响应 case "DeviceControl": console.log("设备控制响应", jsonStatus); if (jsonStatus.responseStatus == "OK") { console.log("发送控制指令成功"); this.workJsonStatus = jsonStatus; this.workStatus = jsonStatus.workStatus; setTimeout(() => console.log("this.workStatus", this.workStatus)); if (jsonStatus.battery) { this.setState({ Electricity: jsonStatus.battery, }); } // 判断设备主动上报的关机事件 if (jsonStatus.workStatus === MODE_WORKING_ENUM.END) { // 判断id是否一致, 一致的话则生成护理报表, 并提示 if (jsonStatus.id == this.FR200NursingHistory.id) { debounce( this.checkInstrumentRecord.bind(this, jsonStatus), 500 ); } return; } if (jsonStatus?.workMode === this.state.ActiveModeItem.modeType) { this.setState({ workMode: jsonStatus?.workMode, // 仅当设备上报模式与小程序一致时,才允许改变小程序变量缓存 }); // 判断是否在isRuning(护理中) // 仅当设备模式与小程序是否一致,才允许更改设备运行时间 if ( this.state.DeviceConnectStatus === 1 && this.isRuning && jsonStatus.workStatus !== MODE_WORKING_ENUM.END ) { this.updateDeviceSyncData( { totalWorkingMinutes: jsonStatus.totalWorkingMinutes, totalWorkingSeconds: jsonStatus.totalWorkingSeconds, }, jsonStatus ); } } if ( jsonStatus.workMode === MODE_WORKING_ENUM.WORKING && this.isRuning ) { const { ActiveModeItem } = this.state; const item = ActiveModeItem; if (jsonStatus.workMode !== item.modeType) { clearTimeout(loadingTipsTimer); this.setState({ isShowCountdown: false, }); } } } break; // 设备对信息查询指令的响应 case "InfoQuery": console.log("设备对信息查询指令的响应 InfoQuery", jsonStatus); switch (jsonStatus.infoQueryType) { // 离线记录 case "offlineClockInInfo": console.log("离线记录", jsonStatus); break; // 版本信息 case "versionInfo": console.log("版本信息", jsonStatus); break; // 时间同步 case "timeSync": console.log("时间同步", jsonStatus); break; default: break; } break; default: break; } }); /** * 延迟600毫秒获取附属设备状态 */ const querySubDeviceArrayBuffer = deviceToolKitInstance.toBleCommand({ ...bleCommandSamples.querySubDevice, queryType: "WL200", } as any); setTimeout(() => { console.log("发送查询附属设备指令 querySubDeviceArrayBuffer"); sendCommand({ value: querySubDeviceArrayBuffer, }); }, 600); /** * 延迟500毫秒获取设备电量 */ const queryDeviceArrayBuffer = deviceToolKitInstance.toBleCommand( bleCommandSamples.queryDeviceStatus as any ); setTimeout(() => { console.log("发送查询设备电量指令"); sendCommand({ value: queryDeviceArrayBuffer, }); }, 500); }); }; /**监听关机事件*/ onEndDevice = () => { this.rmFR200NursingHistory(this.FR200NursingHistory, true); // 判断护理时间,如果不足,则提示不足 if (!this.isCheckNurseTime()) { this.setState({ isNotEnoughTime: true }); } else { this.endNurseFun(); } }; /** 同步设备运行信息:运行时间 */ updateDeviceSyncData = (newData, jsonStatus) => { DeviceSyncData = { ...DeviceSyncData, ...newData, }; if (newData.hasOwnProperty("totalWorkingSeconds")) { this.renderDeviceStatus.renderWorkTime(jsonStatus); } }; // 页面同步护理剩余时间 renderDeviceStatus = { renderWorkTime: (jsonStatus) => { const { totalWorkingMinutes, totalWorkingSeconds } = DeviceSyncData; let { ActiveModeItem, currentTime } = this.state; const totalTime = totalWorkingMinutes * 60 + totalWorkingSeconds; console.log("仪器上报的已经运行的总秒数", totalTime); console.log("时间校准频率,默认5秒一次", TIME_CALIBRATION_FREQUENCY); //对比仪器上报运行的总秒数 和小程序页面运行的已经运行的总秒数,如果不一致就进行校准 let sceneTime = ActiveModeItem?.breakTimeStr ? minSecToS(ActiveModeItem.breakTimeStr) : minSecToS(this.state.currentVideoTime); // 场景时间 console.log("场景时间 sceneTime", sceneTime); console.log("当前显示时间 currentTime", currentTime); console.log("设备运行时间 totalTime", totalTime); // 更新界面倒计时 this.resetTimer(); if ( sceneTime > totalTime && this.isRuning && this.state.DeviceConnectStatus == 1 ) { // 界面倒计时同步设备时间 const t = sceneTime - totalTime; // 场景时间 - 已运行时间 = 剩余时间 this.setState({ currentTime: s_to_ms(t), }); } else { this.setState({ currentTime: "00:00", }); this.judgementWorkStatus(MODE_WORKING_ENUM.END, jsonStatus.workMode); } // 每次同步后,更新历史缓存 setTimeout(() => { this.updateFR200NursingHistory(null, jsonStatus); }, 100); }, }; // 仪器开始倒计时 setLoadingTips(time) { this.setState({ countdown: time, }); if (time >= 0) { loadingTipsTimer = setTimeout(() => { this.setLoadingTips(--time); }, 1000); } else { // 停止倒计时 // that.data.startSettingCountDown = false; this.setState({ isShowCountdown: false, }); } } /** * 设备上报不同状态 * params 工作状态 工作模式 响应状态 */ judgementWorkStatus(nWorkStatus, nWorkMode) { const { ActiveModeItem, ModeList, currentVideoTime } = this.state; const opts: any = {}; // ActiveModeItem let nowModeItem; if (nWorkMode) { nowModeItem = ModeList.find((item) => { return item.modeType === nWorkMode; }); } opts.workStatus = nWorkStatus; let nowCurrentTime = currentVideoTime; // 完成重连同步则删除重连时间字段 if (ActiveModeItem?.breakTimeStr) { nowCurrentTime = ActiveModeItem?.breakTimeStr; } const statusF = { sleep: () => { this.setState({ isShowCountdown: false, }); }, standby: () => { this.setState({ isShowCountdown: false, }); }, setting: () => { this.isRuning = true; this.setState({ title: "正在护理", isStopNurse: false, }); if (nowModeItem) { opts.currentTime = nowCurrentTime; } // 倒计时loading // if (!this.state.isShowCountdown) { // this.setState({ // isShowCountdown: true, // }); // this.setLoadingTips(CountDownTime[workMode] || 6); // } }, working: () => { this.setState({ title: "正在护理", isStopNurse: false, isShowCountdown: false, }); this.closeTips(); }, pause: () => { clearInterval(currentTimeTimer); this.setState({ isShowCountdown: false, }); this.setState({ title: "暂停护理", isStopNurse: true, }); }, end: () => { // 已进入了报告阶段, 防止重复进入, 主要防止在手动点击结束护理接收到仪器消息 console.log("END 护理结束"); clearInterval(currentTimeTimer); this.endnursing(true); }, }; statusF[nWorkStatus] && statusF[nWorkStatus](); if (Object.keys(opts).length) { this.setState(opts); } } /** * 保存护理报告 * 1.是否跳转 2.数据 * */ saveNurseReport = async (isJump = true, from) => { this.endNurseFun(); }; /** * 结束护理 * param isAuto 是否仪器自动结束 */ endnursing = (isAuto) => { if (isAuto == true) { // 仪器自动上报完成, 直接上报并跳转报告页 clearInterval(currentTimeTimer); const isEnough = this.isCheckNurseTime(); if (isEnough) { this.saveNurseReport(true, "endnursing"); } } else { // 手动点击结束, 弹出弹窗, 看看是否需要结束 this.onEndPlan(); } }; // 重置并同步计时器 resetTimer = () => { // 切换模式后, 需要重新设置计时器, 以防进行中的计时器 currentTimeTimer && clearInterval(currentTimeTimer); currentTimeTimer = setInterval(() => { let { DeviceConnectStatus, currentTime, ActiveModeItem, currentVideoTime, } = this.state; if ( this.workStatus == MODE_WORKING_ENUM.WORKING && this.isRuning && DeviceConnectStatus == 1 ) { let totalSeconds = ActiveModeItem?.breakTimeStr ? minSecToS(ActiveModeItem.breakTimeStr) : minSecToS(currentVideoTime); // 现在的倒计时剩余时间:同步时检查是否断开重连,如果是,则使用断开的剩余时长,进行倒计时计算 let currentSeconds = minSecToS(currentTime); let checkTime = totalSeconds - currentSeconds; // 缓存经过的时间:用于接口提交 this.elapsedTime = checkTime; // fr200不需要加上中断的时间 // 如果存在中断时间,则要加上间隔的时间 // if (ActiveModeItem?.breakTimeStr) { // let intervalTime = // minSecToS(ActiveModeItem.modeTimeStr) - // minSecToS(ActiveModeItem.breakTimeStr); // this.elapsedTime += intervalTime; // } console.log("this.elapsedTime", this.elapsedTime); // 判断剩余时间是否大于1 if (currentSeconds >= 1) { // 小程序显示倒计时 this.setState({ currentTime: s_to_ms(--currentSeconds), }); // 根据不同的模式,切换步骤到下一步 if (ActiveModeItem.modeType === "essence") { this.essencePenetrationNext(); } else if (ActiveModeItem.modeType === "maskPenetration") { this.maskPenetrationNext(); } } else { clearInterval(currentTimeTimer); this.setState({ currentTime: "00:00", waterStepIndex: 0, // 水分测试步骤 EssenceStepIndex: 0, // 精华促渗步骤 MaskModeStepIndex: 0, // 面膜促渗步骤 }); this.saveNurseReport(true, "setTimer"); // 保存护理计划,并且结束 } } }, 1000); }; /** * @name 水分测试下一步,手动调用 * @description 步骤+1并设置视频 */ waterTestNext(index) { let ActiveModeItem = this.state.ActiveModeItem; if (index === 0 && ActiveModeItem.stepOneVideo) { this.VideoSrcLoad(ActiveModeItem.stepOneVideo); } else if (index === 1 && ActiveModeItem.stepTwoVideo) { this.VideoSrcLoad(ActiveModeItem.stepTwoVideo); } else if (index === 2 && ActiveModeItem.stepThreeVideo) { this.VideoSrcLoad(ActiveModeItem.stepThreeVideo); } } /** * @name 面膜促渗下一步,根据时间自动调用 * @description 1.切换步骤 2.检测记录蜂鸣/震动的时间节点与步骤 * */ maskPenetrationNext() { let { MaskModeStepIndex, MaskModeBuzzingIndex, MaskModeVibrateIndex, ActiveModeItem, } = this.state; // 模式多个步骤节点切换 // 已运行时间达到下一节点时,切换 let GearLength = ActiveModeItem.modeGear.length; if (GearLength && MaskModeStepIndex < GearLength) { let GearTime = minSecToS(ActiveModeItem.modeGear[MaskModeStepIndex].time); if (this.elapsedTime > GearTime) { // 已运行时间达到下一节点,且存在下一节点,步骤切换时更新 if (MaskModeStepIndex < GearLength) { let index = MaskModeStepIndex + 1; // 提前步骤+1 this.setState({ MaskModeStepIndex: index, }); this.FR200AutoChangeGear(index); } } } let BuzzingLength = ActiveModeItem.modeBuzzing.length; if (BuzzingLength && MaskModeStepIndex < GearLength) { let BuzzingTime = minSecToS( ActiveModeItem.modeBuzzing[MaskModeStepIndex].time ); if (this.elapsedTime > BuzzingTime) { if (MaskModeBuzzingIndex < BuzzingLength) { let index = MaskModeBuzzingIndex + 1; // 提前步骤+1 this.setState({ MaskModeBuzzingIndex: index, }); this.FR200Buzzing(); } } } let VibrateLength = ActiveModeItem.modeVibrate.length; if (VibrateLength && MaskModeStepIndex < VibrateLength) { let VibrateTime = minSecToS( ActiveModeItem.modeVibrate[MaskModeStepIndex].time ); if (this.elapsedTime > VibrateTime) { if (MaskModeVibrateIndex < VibrateLength) { let index = MaskModeVibrateIndex + 1; // 提前步骤+1 this.setState({ MaskModeVibrateIndex: index, }); this.FR200Vibrate(); } } } } /** @name 精华促渗下一步,根据时间自动调用 */ essencePenetrationNext() { let { EssenceStepIndex, EssenceBuzzingIndex, EssenceVibrateIndex, ActiveModeItem, } = this.state; // 模式多个步骤节点切换 // 已运行时间达到下一节点时,切换 let GearLength = ActiveModeItem.modeGear.length; if (GearLength && EssenceBuzzingIndex < GearLength) { let GearTime = minSecToS( ActiveModeItem.modeGear[EssenceBuzzingIndex].time ); if (this.elapsedTime > GearTime) { // 已运行时间达到下一节点,且存在下一节点,步骤切换时更新 if (EssenceStepIndex < GearLength) { let index = EssenceStepIndex + 1; // 提前步骤+1 this.setState({ EssenceStepIndex: index, }); this.FR200AutoChangeGear(index); } } } let BuzzingLength = ActiveModeItem.modeBuzzing.length; if (BuzzingLength && EssenceBuzzingIndex < BuzzingLength) { let BuzzingTime = minSecToS( ActiveModeItem.modeBuzzing[EssenceBuzzingIndex].time ); if (this.elapsedTime > BuzzingTime) { if (EssenceBuzzingIndex < BuzzingLength) { let index = EssenceBuzzingIndex + 1; // 提前步骤+1 this.setState({ EssenceBuzzingIndex: index, }); this.FR200Buzzing(); } } } let VibrateLength = ActiveModeItem.modeVibrate.length; if (VibrateLength && EssenceBuzzingIndex < VibrateLength) { let VibrateTime = minSecToS( ActiveModeItem.modeBuzzing[EssenceBuzzingIndex].time ); if (this.elapsedTime > VibrateTime) { if (EssenceVibrateIndex < VibrateLength) { let index = EssenceVibrateIndex + 1; // 提前步骤+1 this.setState({ EssenceVibrateIndex: index, }); this.FR200Vibrate(); } } } } /**切换挡位发起蜂鸣:可以切换到现在的挡位?*/ FR200Buzzing = () => { console.log("FR200Buzzing 蜂鸣", this.elapsedTime); const { ActiveModeItem, currentGear } = this.state; let sendParams: any = { ...deviceCommandSamples.pause, workMode: ActiveModeItem.modeType, // 使用模式 workStatus: "working", gear: currentGear, }; if (currentGear > 1) { sendParams.gear = currentGear - 1; let pauseArrayBuffer = deviceToolKitInstance.toBleCommand( sendParams as any ); sendCommand({ value: pauseArrayBuffer, }); } else { sendParams.gear = currentGear + 1; let pauseArrayBuffer = deviceToolKitInstance.toBleCommand( sendParams as any ); sendCommand({ value: pauseArrayBuffer, }); } setTimeout(() => { sendParams.gear = currentGear; let pauseArrayBuffer = deviceToolKitInstance.toBleCommand( sendParams as any ); sendCommand({ value: pauseArrayBuffer, }); }, 300); }; /** * @name FR200自动切换挡位 * @description 需要传参索引 */ FR200AutoChangeGear = (index: number = 0) => { console.log("FR200AutoChangeGear 切换挡位", this.elapsedTime); const { ActiveModeItem, GearData } = this.state; let gear = GearData[index].forehead; // 同步挡位 this.setState({ currentGear: gear, }); let sendParams: any = { ...deviceCommandSamples.pause, workMode: ActiveModeItem.modeType, // 使用模式 workStatus: "working", gear: gear, }; const pauseArrayBuffer = deviceToolKitInstance.toBleCommand( sendParams as any ); sendCommand({ value: pauseArrayBuffer, }).then(() => { this.workStatus = "working"; this.resetTimer(); }); }; /** * @name FR200震动 * @description 同步设备工作却不改变档位,仅震动 */ FR200Vibrate = () => { console.log("FR200Vibrate FR200震动", this.elapsedTime); const { ActiveModeItem, currentGear } = this.state; let sendParams: any = { ...deviceCommandSamples.pause, workMode: ActiveModeItem.modeType, // 使用模式 workStatus: "working", gear: currentGear, }; const pauseArrayBuffer = deviceToolKitInstance.toBleCommand( sendParams as any ); sendCommand({ value: pauseArrayBuffer, }); }; executePromises = async () => { let waterStepList = this.state.waterStepList; let waterStepIndex = this.state.waterStepIndex; let that =this await new Promise((resolve) => { setTimeout(() => { waterStepList[waterStepIndex].schedule = 100; that.setState({ waterStepList, }); resolve(); }, 3000); }).then(() => { return new Promise((resolve) => { setTimeout(() => { waterStepList[waterStepIndex].finish = true; that.setState({ waterStepList, }); resolve(); }, 2000); }); }); }; // 检测并控制工作状态 handleWorkStatus = async (isBtnClick: boolean, workStatus) => { const { DeviceConnectStatus, ActiveModeItem } = this.state; let newWorkStatus = workStatus || (this.workStatus == MODE_WORKING_ENUM.WORKING ? "pause" : "working"); // if (isBtnClick && newWorkStatus == "working") { // // todo FR200 不判断舱体,判断肌肤 // if (!ActiveModeItem.isCabinMode && DeviceConnectStatus != 1) { // console.log("DeviceConnectStatus", DeviceConnectStatus); // this.showTips("检测到您的设备没有紧贴肌肤,请紧贴肌肤后点击重新检测"); // return; // } // } let sendParams: any = { ...deviceCommandSamples.pause, workMode: ActiveModeItem.modeType, // 使用模式 workStatus: newWorkStatus, }; console.log(ActiveModeItem, "查看工作状态"); // 水分测试需要特殊处理 // 水分测试准备 水分测试工作 水分测试启动 if (ActiveModeItem.modeType === "moistureTest") { let that = this; sendParams.testStatus = "standby"; // 切换为准备 // 3秒定时器,逻辑3秒把进度条弄成100,再加2秒获取最后结果 if (isBtnClick) { that.setState({ isRuningTest: 2, }); this.executePromises() sendParams.testStatus = "start"; // 点击开始再开始 console.log("点击开始", isBtnClick); } } if (ActiveModeItem.modeType === "maskPenetration") { sendParams.gear = this.state.currentGear; // 点击开始再开始 } console.log("准备发送自定义或工作指令", ActiveModeItem, sendParams); const pauseArrayBuffer = deviceToolKitInstance.toBleCommand( sendParams as any ); sendCommand({ value: pauseArrayBuffer, }).then(() => { this.workStatus = newWorkStatus; this.resetTimer(); console.info( `handleWorkStatus 发送${newWorkStatus}指令成功 参数为 =>`, sendParams ); }); }; /** * @name 点击开始护理 * @params type 传值 switch 则用于区分是否切换模式的启动,如果是WL200的切换模式,则倒计时 */ onNursingTap(type = "") { // 如果已禁止运行,则停止执行后续逻辑 if (this.state.isRuningTest === 2) return; // 防止多次点击 if (this.state.hadClickStart) return; this.setState({ hadClickStart: true, }); setTimeout(() => { this.setState({ hadClickStart: false, }); }, 500); const { ActiveModeItem, DeviceConnectStatus } = this.state; if (DeviceConnectStatus != 1) { console.log("DeviceConnectStatus 开始处", DeviceConnectStatus); this.showTips("检测到FR200未连接成功,请确认FR200开机并佩戴"); return; } // 仅在切换模式的时候,弹窗倒计时. // if (type === "switch") { // let downNum = CountDownTime[ActiveModeItem.modeType] || 3; // this.showCountdownFun(downNum, () => {}); // 倒计时弹窗 // } // 开始执行护理 this.workStatus = MODE_WORKING_ENUM.WORKING; // 不管当前什么状态,直接设为工作状态 this.handleWorkStatus(true, MODE_WORKING_ENUM.WORKING); this.setState({ isStopNurse: false, }); } // 结束护理 endNurseFun() { this.handleWorkStatus(false, "end"); if (this.isCheckNurseTime()) { this.PostNursingLogClock(); } else { // 时间不满足,回到主页 this.handleWorkStatus(false, "end"); this.setState({ isEndCarePlan: false, isNotEnoughTime: true, }); } } /** 检查时间是否达标仪器最低护理时间 */ isCheckNurseTime() { const { currentDevice, currentVideoTime } = this.state; let sceneTime = minSecToS(currentVideoTime); const timeRemaining = sceneTime - minSecToS(this.state.currentTime); // 当前模式已运行时间 let nursingTimeStr = currentDevice?.nursingTimeStr; let nursingTime = nursingTimeStr ? minSecToS(nursingTimeStr) : 60; // 设备生成护理记录至少需要运行时间 console.log("检查已运行时间", timeRemaining, nursingTime); if (timeRemaining >= nursingTime) { return true; } else { return false; } } /*** 护理记录 START ***/ /** 小程序查询护理记录概要 */ getInstrumentClockSummary() { this.hadGotInstrumentHistoryData = true; console.log("发送指令clockSummary 获取设备护理概要"); setTimeout(() => { const queryClockSummary = deviceToolKitInstance.toBleCommand({ ...(fr200BleCommand.InfoQuery.clockSummary as any), }); sendCommand({ value: queryClockSummary, }); }, 1000); } /** 小程序查询最近一条护理详情 */ getInstrumentClockDetail() { this.hadGotInstrumentHistoryData = true; console.log("发送指令clockDetail 查询最近一条护理详情"); setTimeout(() => { const queryClockSummary = deviceToolKitInstance.toBleCommand({ ...(fr200BleCommand.InfoQuery.clockDetail as any), }); sendCommand({ value: queryClockSummary, }); }, 2000); } /** * @title 检查护理记录 * @description * 1.判断是否存在工作状态:如果不存在,则等待两秒用于连接设备与赋值,再执行后面的代码 * * 2.判断是否已存在缓存的护理记录:如果没有历史,则缓存 * * 3.判断是否当天(如果不是当天,则删除记录) * * 4.判断设备状态-未运行/已完成/待机 * 4-1.已有缓存护理记录,判断ID一致,同步时间。若主动结束,需判断时间是否满足仪器最低护理时间,满足直接跳转护理报告页;不满足需提示不满足,回到首页 * 4-2.已有缓存护理记录,判断ID不一致(同步异常)。直接提交,固定设置为一分钟。 * * 5.判断设备状态-运行中 * 正常执行逻辑 * * */ checkInstrumentRecord = async (jsonStatus: any) => { console.log("检查护理记录"); let { currentDevice, ActiveModeItem, ModeList, currentVideoTime } = this.state; await sleep(2); let isSyncHistory = Taro.getStorageSync("isSyncHistory"); if (isSyncHistory) { this.setState({ isShowHistoryMsg: false }); Taro.removeStorageSync("isSyncHistory"); } console.log( "this.workJsonStatus", this.workJsonStatus, this.workJsonStatus.workMode ); if (this.workJsonStatus.workMode) { // FR200可能要判断是否水分测试 Test // console.log("现在运行的模式:", this.workJsonStatus.workMode); } // 2.判断是否已存在缓存的护理记录:如果没有历史,则缓存 let workStatus = this.workJsonStatus.workStatus; let FR200NursingHistory = this.FR200NursingHistory; if (!this.FR200NursingHistory) { console.log("小程序缓存没有数据, 忽略"); if ( workStatus == MODE_WORKING_ENUM.WORKING || workStatus == MODE_WORKING_ENUM.PAUSE ) { // 缓存没有数据, 要存缓存 this.setFR200NursingHistory(jsonStatus); } return; } // 3.判断是否当天(如果不是当天,则删除记录) if (!dayjs().isSame(FR200NursingHistory?.createDate, "day")) { console.log("小程序缓存有数据,但是不是当天数据,忽略"); this.rmFR200NursingHistory(FR200NursingHistory); return; } // 仪器缓存模式,判断是否存在于现有模式中 let recordModeItem = ModeList.find((item) => { return item.id == FR200NursingHistory.modeId; }); if (!FR200NursingHistory || !recordModeItem) { console.log("仪器有数据, 但是缓存没有数据, 忽略"); return; } console.log("workStatus", workStatus); console.log("jsonStatus", jsonStatus, this.workJsonStatus); console.log("currentTime", FR200NursingHistory.currentTime); let historyElapsedTime = minSecToS(currentVideoTime) - minSecToS(FR200NursingHistory.currentTime); this.elapsedTime = this.elapsedTime > historyElapsedTime ? this.elapsedTime : historyElapsedTime; // 4.判断设备状态-未运行/已完成/待机 if ( workStatus == MODE_WORKING_ENUM.STANDBY || workStatus == MODE_WORKING_ENUM.END || !workStatus ) { // 判断id是否一致, 一致的话则生成护理报表 if (jsonStatus.id == FR200NursingHistory.id) { console.log("id一致, 设备没有运行/已完成/待机"); let totalSeconds = jsonStatus.totalSeconds; // 从仪器上获取的使用时间 let nursingTimeStr = currentDevice?.nursingTimeStr; let nursingTime = nursingTimeStr ? minSecToS(nursingTimeStr) : 60; // 设备生成护理记录至少需要运行时间 if (totalSeconds < nursingTime) { // 护理时间不足 this.setState({ isNotEnoughTime: true }); this.rmFR200NursingHistory(FR200NursingHistory); return; } // 小程序时间和设备时间,谁大用谁 let timeValue = totalSeconds > this.elapsedTime ? s_to_hms(totalSeconds) : s_to_hms(this.elapsedTime); let params = { instrumentId: currentDevice.id, instrumentName: currentDevice.name, modeId: ActiveModeItem.id, modeName: ActiveModeItem.modeName, nursingTime: timeValue, }; this.handleWorkStatus(false, "end"); let res: any = await this.PostNursingLogClock(params); console.log("res", res); this.rmFR200NursingHistory(FR200NursingHistory); } else { // ID不一致,同步异常,统一提交一分钟 let params = { instrumentId: currentDevice.id, instrumentName: currentDevice.name, modeId: ActiveModeItem.id, modeName: ActiveModeItem.modeName, nursingTime: "00:01:00", }; this.handleWorkStatus(false, "end"); let res: any = await this.PostNursingLogClock(params); console.log("res", res); this.rmFR200NursingHistory(FR200NursingHistory); } } else { console.log("id一致, 设备运行中或暂停"); // 5.判断设备状态-运行中 // 同步时间 if (jsonStatus.id == FR200NursingHistory.id) { if (FR200NursingHistory.currentTime) { this.isRuning = true; this.resetTimer(); } } } }; /** 获取小程序本地缓存的历史记录 */ getFR200NursingHistory() { this.FR200NursingHistory = Taro.getStorageSync("FR200NursingHistory"); console.log( this.FR200NursingHistory, "获取本地数据++++++++++++++++++++++++++++++++++++++++++" ); // 是否同步历史记录 let isSyncHistory = Taro.getStorageSync("isSyncHistory"); if (isSyncHistory) { let ActiveModeItem = this.FR200NursingHistory.ActiveModeItem; // 直接进入开始护理状态 this.setState({ isShowNurse: true, isShowHistoryMsg: true, videoTime: this.FR200NursingHistory.videoTime, tempModeCurrent: ActiveModeItem, ActiveModeItem: ActiveModeItem, activeModeID: ActiveModeItem.id, ModeID: "mode_" + ActiveModeItem.id, currentTime: this.FR200NursingHistory.currentTime, }); } } /** 设置WL200护理历史 */ setFR200NursingHistory = (jsonStatus: any) => { let { currentDevice, ActiveModeItem } = this.state; const params = { createDate: dayjs().format("YYYY-MM-DD"), workMode: jsonStatus.workMode, instrumentId: currentDevice.id, instrumentName: currentDevice.name, modeId: ActiveModeItem.id, modeName: ActiveModeItem.modeName, id: dayjs().format("YYYY-MM-DD HH:mm:ss"), neededTotalSeconds: jsonStatus.neededTotalSeconds, jsonStatus, ActiveModeItem: this.state.ActiveModeItem, }; this.FR200NursingHistory = JSON.parse(JSON.stringify(params)); Taro.setStorageSync("FR200NursingHistory", params); console.log("保存setFR200NursingHistory"); }; /** 更新WL200护理历史运行时间 */ updateFR200NursingHistory = (data: any = null, jsonStatus: any = null) => { this.FR200NursingHistory = Taro.getStorageSync("FR200NursingHistory"); if (this.FR200NursingHistory) { let params: any = this.FR200NursingHistory; // 设置当前时间 params.currentTime = this.state.currentTime; // params.videoTime; // 设置正确封面 if (!data) { if (jsonStatus) { // 缓存每秒数据 if (!params.dataArray) params.dataArray = []; params.dataArray.push(jsonStatus); params.jsonStatus = jsonStatus; params.workMode = jsonStatus?.workMode; params.modeId = this.state.ActiveModeItem.id; params.modeName = this.state.ActiveModeItem.modeName; } } else { params.jsonStatus = jsonStatus; params.workMode = jsonStatus?.workMode; params.modeId = data.id; params.modeName = data.modeName; } Taro.setStorageSync("FR200NursingHistory", params); console.log("更新updateFR200NursingHistory"); // 基础模式可在这里调用函数更新图标Echarts // 最新一条数据jsonStatus // 注意事项:只拿working状态 // todo } else { this.setFR200NursingHistory(jsonStatus); } }; // 改变模式时,清空dataArray,防止数据无限叠加 changeItemUpdateFR200NursingHistory() { this.FR200NursingHistory = Taro.getStorageSync("FR200NursingHistory"); if (this.FR200NursingHistory) { this.FR200NursingHistory.dataArray = []; Taro.setStorageSync("FR200NursingHistory", this.FR200NursingHistory); } } /** * @name 删除WL200护理历史 * @description 参数1 护理历史 参数2 强制删除 * 如果传入护理历史ID与现有ID相等,则删除。 * 如果参数二为真,则强制删除 */ rmFR200NursingHistory = (FR200NursingHistory, hard = false) => { const nowFR200NursingHistory = Taro.getStorageSync("FR200NursingHistory"); if (nowFR200NursingHistory) { Taro.setStorageSync("FR200Echart", nowFR200NursingHistory); // 临时保存用于观看和调试 } Taro.removeStorageSync("FR200NursingHistory"); }; // 脸部one todoPromise = async () => { const nowFR200NursingHistory = Taro.getStorageSync("FR200NursingHistory"); console.log(nowFR200NursingHistory, "nowFR200NursingHistory"); // 护理脸部 if ( [ "face", "eyes", "nasolabialFold", "mandibularLine", "headLiftingPro", ].includes(nowFR200NursingHistory.jsonStatus.workMode) ) { // 把working=工作中的状态数据筛选出来 let filtered = nowFR200NursingHistory.dataArray.filter( (item) => item.workStatus === "working" ); // 能量发数 filtered = filtered.slice(0, 360); // 脸部能量 let faceEnergy = 0; filtered.forEach((item) => { faceEnergy += item.joulePerSecond; }); // 计算平均数 // let sum = filtered.reduce((accumulator, currentValue) => accumulator + currentValue.impedance, 0); // let average = sum / filtered.length; // 最大 let max: any = Math.max(...filtered.map((item) => item.impedance)); max = this.determineTier(max); // 最小 let min: any = Math.min(...filtered.map((item) => item.impedance)); min = this.determineTier(min); // 平均数最大等级处于2 let average: any = max / 2; average = this.determineTier(average); // 能量图里面的图谱每10秒为一个数组 // 创建一个空数组用于存储分组后的结果 // 创建一个空数组用于存储分组后的结果 let groupedAa: any[] = []; // 使用循环遍历数组 aa for (let i = 0; i < filtered.length; i += 10) { // 提取每组的三个对象 let group = filtered.slice(i, i + 10); // 找到组中最大的 name 值 let maxName = Math.max(...group.map((obj) => obj.impedance)); // 计算并存储每组的平均数 let average: any = this.determineTier(maxName / 2); // let average = maxName / 2; if (average >= 1) { average = average - 1; average = average * 10; } // 将包含该组对象和平均数的对象添加到 groupedAa 数组中 groupedAa.push(average); } let nursingData = { // nursingTime:result, nursingData: JSON.stringify({ faceEnergy, max, min, average, groupedAa, filtered: filtered.length, workMode: nowFR200NursingHistory.workMode, }), }; return nursingData; } return { nursingData: JSON.stringify({ workMode: nowFR200NursingHistory.workMode, }), showFace: true } }; // 计算挡位 determineTier = (sun) => { // 定义每档的范围 const tiers = [0, 200, 280, 360, 440, 520, 600, 680, 760, 840]; // 遍历每一档的范围,找到 sun 所属的档 for (let i = 0; i < tiers.length; i++) { if (sun < tiers[i + 1]) { return i + 1; } } // 如果 sun 不在任何一档范围内,返回默认档或处理错误 return "10"; }; /** 提交护理记录:完成护理后自动调用,会跳转页面 */ PostNursingLogClock = async (data: any = null, isJump = true) => { // todo 建议写一个Promise异步函数,用 await 执行,在提交前处理好数据 // return; let { currentDevice, ActiveModeItem } = this.state; let params = {}; if (data) { params = data; } else { params = { instrumentId: currentDevice.id, instrumentName: currentDevice.name, modeId: ActiveModeItem.id, modeName: ActiveModeItem.modeName, nursingTime: s_to_hms(this.elapsedTime), }; } let res1: any = await this.todoPromise(); if (!res1?.showFace) { params = { ...params, ...res1 }; } console.log(res1, "查看返回数据"); let res2: any = await InstrumentInfo.apiNursingLog.addLog(params); console.log("PostNursingLogClock", res2); if (res2.data.code === 200) { let params = { instrumentId: currentDevice.id, }; // 上传护理完成的仪器ID let res = await InstrumentInfo.apiClock.addClockInstrument(params); console.log(res, "护理完成"); this.rmFR200NursingHistory(this.FR200NursingHistory); // 护理完成,删除记录 if (isJump) { this.setState({ isShowNursingSuccess: true, }); setTimeout(() => { this.setState({ isShowNursingSuccess: false, }); this.goFaceReport( res1, ActiveModeItem.id, res2.data.data, currentDevice.id ); // 跳转 }, 2000); } } }; /*** 护理记录 END ***/ //蓝牙断开连接处理 bluetoothDisconnectProcessing() { clearInterval(timer); Taro.offBLEConnectionStateChange(this.listener); // 需传入与监听时同一个的函数对象 Taro.offBLECharacteristicValueChange((res) => { console.log("offBLECharacteristicValueChange", res); }); if (!this.state.isToOTA) { Taro.closeBluetoothAdapter(); } } /** 会自动关闭的护理错误提示 */ showTips(ctx) { if (!ctx) return; if (showTipsTimer) clearTimeout(showTipsTimer); this.setState({ errorTips: ctx, }); showTipsTimer = setTimeout(() => { this.setState({ errorTips: "", }); }, 2000); } /** 不自动关闭的护理错误提示 */ openTips(ctx) { if (!ctx) return; this.setState({ errorTips: ctx, }); } /** 关闭护理错误提示 */ closeTips() { this.setState({ errorTips: "", }); } /** 结束护理弹窗 */ onEndPlan = async () => { this.setState({ isEndCarePlan: true, }); }; confirmEndBtn = () => { console.log("confirmEndBtn", this.isCheckNurseTime()); if (this.isCheckNurseTime()) { this.endNurseFun(); this.cancelEndBtn(); } else { this.handleWorkStatus(false, "end"); this.setState({ isEndCarePlan: false, isNotEnoughTime: true, }); } }; cancelEndBtn = () => { this.setState({ isEndCarePlan: false, }); }; /** 弹窗 END*/ // 打开通用错误弹窗 openErrorTipsText = (str) => { this.setState({ isShowErrorTipsText: true, errorTipsText: str, }); }; // 关闭通用错误弹窗 closeErrorTipsText = () => { this.setState({ isShowErrorTipsText: false, }); }; closeNotEnoughTime = () => { this.setState({ isNotEnoughTime: false, }); Taro.reLaunch({ url: "/pages/index/index", }); }; /** 完成护理提交:跳转护理报告页 */ goFaceReport = async (data, id, deviceid, currentDevice) => { let nursingData = JSON.parse(data.nursingData); // 跳转前置空定时器,防止重复提交 if (currentTimeTimer) clearInterval(currentTimeTimer); if ( [ "face", "eyes", "nasolabialFold", "mandibularLine", "headLiftingPro", ].includes(nursingData.workMode) ) { let ids = Number(deviceid); // 获取echarts数据 这个是获取接口更新echarts页面 let res2 = await InstrumentInfo.apiNursingLog.getStatiCDE(ids); let nursingDatas = JSON.parse(res2.data.data.nursingData); let obj = { modeName: res2.data.data.modeName, data: nursingDatas, }; let report = true; go( "/recoding/pages/face_report_one/face_report_one?id=" + ids + "&report=" + report + "&obj=" + JSON.stringify(obj) ); } else if ("moistureTest" === nursingData.workMode) { console.log("水分测试"); } else { console.log("跳转11111111"); let report = true; go( "/recoding/pages/face_report/face_report?id=" + deviceid + "&recordId=" + currentDevice + "&report=" + report ); } }; // 完成配对 pairingChange = () => { this.setState({ isConnectShow: false, isShowNurse: true, }); setTimeout(() => { this.onNursingTap("switch"); }); }; connectionClose = () => { this.setState({ isConnectShow: false, }); Taro.switchTab({ url: "/pages/index/index" }); }; // 手动护理模式切换:提示是否保存护理 /**仅关闭*/ closeTipsSave = () => { this.setState({ isShowTipsSave: false, }); }; /**关闭+切换*/ cancelTipsSave = () => { this.setState({ isShowTipsSave: false, }); this.modeCurrentFun(this.tempModeCurrent, true); // 不提交护理记录,也不进行校验 }; /**关闭+提交+切换*/ confirmTipsSave = async () => { this.setState({ isShowTipsSave: false, }); this.PostNursingLogClock(null, false); // 先提交护理记录 this.modeCurrentFun(this.tempModeCurrent, true); // 不进行校验 }; /** 初次护理信息弹窗 */ firstNurseInfo = async () => { let { currentDevice } = this.state; let res = await InstrumentInfo.firstNurseInfo({ instrumentId: currentDevice.id, }); console.log(res, "接口"); if (res.data.code === 200) { let isFirstTipShow = getStorageSync( "first_instrument_" + currentDevice.id ); console.log(isFirstTipShow, "查看返回值"); if (!isFirstTipShow) { if (res.data.data.length !== 0) { // 首次进入页面:自动打开打卡介绍弹窗 this.setState({ nurseInfo: res.data.data, isFirstTipShow: true }); } setStorageSync("first_instrument_" + currentDevice.id, true); } else { this.setState({ nurseInfo: res.data.data }); } } }; onTipShowOpen = async () => { this.setState({ isFirstTipShow: true }); }; onTipShowClose = async () => { setStorageSync("first_instrument_" + this.state.currentDevice.id, true); this.setState({ isFirstTipShow: false }); }; /** 初次护理信息弹窗 END */ customBack = () => { Taro.switchTab({ url: "/pages/index/index" }); }; onModeLockOpen = async () => { this.setState({ isModeLock: true }); }; onModeLockClose = async () => { this.setState({ isModeLock: false }); }; // 获取并设置视频时间 GetVideosTime = (event) => { console.log("获取并设置视频时间GetVideosTime", event?.detail?.duration); if (event?.detail?.duration) { let duration = Math.floor(event?.detail?.duration); let currentTime = s_to_ms(duration); this.setState({ currentTime, currentVideoTime: currentTime }); } }; VideoSrcLoad = (video: string = "") => { this.setState({ currentVideoSrc: "", }); setTimeout(() => { this.setState({ currentVideoSrc: video, }); }, 10); }; switchVideoPlay = () => { setTimeout(() => { let videoRef = Taro.createVideoContext("myVideo", this); videoRef.play(); }, 100); }; switchVideoPause = () => { setTimeout(() => { let videoRef = Taro.createVideoContext("myVideo", this); videoRef.pause(); }, 100); }; render() { let { title, isConnectShow, GearData, waterStepList, waterStepIndex, isShowStepTips, isShowNurse, isStopNurse, ModeList, ModeType, ModeStepIndex, ActiveModeItem, currentWorkModeType, isSwitchActiveMode, ModeID, activeModeID, isShowCountdown, countdown, Electricity, errorTips, isEndCarePlan, currentTime, DeviceConnectStatus, isShowErrorTipsText, errorTipsText, isNotEnoughTime, isShowNursingSuccess, currentDevice, isConnectionBlutoot, isShowTipsSave, isFirstTipShow, nurseInfo, isRuningTest, isShowHistoryMsg, isModeLock, currentVideoSrc, currentGear, showEcharts, echartsData, isShowReReadRecordConnect, currentServiceData, } = this.state; return ( } textAlgin="center" cancelButtonText="取消" confirmButtonText="确定" close={this.cancelModeSwitchBtn} confirm={this.confirmModeSwitchBtn} /> {ActiveModeItem.openSourceData.length > 0 && ( )} 当前模式已护理部分时间 是否保存护理记录 } cancelButtonText="取消" confirmButtonText="确认" textAlgin="center" close={this.closeTipsSave} cancel={this.cancelTipsSave} confirm={this.confirmTipsSave} /> { /*不需要做处理*/ }} /> {isConnectShow && ( {}} pairingChange={this.pairingChange} upgradeFun={() => {}} /> )} 正在同步护理记录... 倒计时:{currentTime} 档位:{currentGear} {ModeList.length > 0 && ( )} {/* */} {/* {(ActiveModeItem.modeType === "face" || ActiveModeItem.modeType === "eyes") && } */} {(ActiveModeItem.modeType === "maskPenetration" || ActiveModeItem.modeType === "essence") && ( )} {ActiveModeItem.modeType === "moistureTest" && ( )}