// pages/plan/plan.js
import bleUtils from "../../utils/ble"
import { submitRecord } from "../../service/api"
Page({
data: {
money: 0,
bleStatus: {
isConnect: wx.getStorageSync('isConnectionBluetooth'),
bluetoothText: '已连接',
},
timer: null,
totalTime: 5 * 60, // 总秒数
remainingTime: 5 * 60, // 剩余秒数
formattedTime: '05:00', // 格式化后的时间
tabCurrentIndex: 1,
modelTabList: ['', '模式01', '模式02', '模式03', '模式04'],
powerValue: 0,
hotModelList: ['关', '弱', '中', '强'],
hotTabCurrentIndex: 0,
rateModelList: [30, 50, 1000, 5000],
rateTabCurrentIndex: null,
rateValue: '',
baseInfo: {
therapyMode: '',
duration: null,
startTime: null, //理疗开始时间
endTime: null, //理疗结束时间
dataInfo: []
}, //保存理疗数据
physioDataInfo: {
deviceMac: '', //设备mac地址
planType: '', //方案类型
info: []
},//保存理疗数据
canvasContext: null,
canvasWidth: 0,
canvasHeight: 0,
_countdownStart: 0, // 新增字段存储计时基准时间
_countdownTotal: 0, // 存储总时长
isStop: true, // 是否暂停
isCountdownStarted: false, // 倒计时是否启动
},
/**
* 生命周期函数--监听页面加载
*/
onLoad(options) {
// 设置屏幕常亮
wx.setKeepScreenOn({ keepScreenOn: true })
bleUtils.bleTool.onDiscoveryData = this.handleDiscoveryData.bind(this);
// bleUtils.bleTool.onDeviceStatus = this.handleDeviceStatus.bind(this);
// bleUtils.bleTool.writeBLECharacteristicValue({ "Orde": "ReadStatus" });
if(!this.data.isCountdownStarted) {
this.sendStartCommands([{ "SetTime": this.data.remainingTime }, { "SetMode": this.data.tabCurrentIndex}])
}
this.initCanvas();
},
onShow() {
// 倒计时未开始, 直接return
if (!this.data.isCountdownStarted) return;
const saved = wx.getStorageSync('countdownState');
if (saved) {
const now = Date.now()
const remaining = Math.max(0, saved.total - (now - saved.start) / 1000);
// 仅在实际剩余时间变化超过1秒时更新显示
if (remaining > 0) {
this.setData({
remainingTime: remaining,
formattedTime: this.formatTime(Math.floor(remaining)),
_countdownStart: saved.start,
_countdownTotal: saved.total,
})
// 始终更新进度条以保证流畅性
this.drawProgress((remaining / saved.total) * 100);
this.clearTimer();
this.updateCountdown()
} else {
this.handleCountdownEnd()
}
}
},
// 页面隐藏时保存状态
onHide() {
const { remainingTime, isStop, isCountdownStarted } = this.data;
if (remainingTime > 0 && !isStop && isCountdownStarted) {
wx.setStorageSync('countdownState', {
start: this.data._countdownStart,
total: this.data._countdownTotal
});
}
},
onUnload() {
// 页面卸载时清除计时器
this.clearCanvas();
this.clearTimer();
// bleUtils.bleTool.onDeviceStatus = null;
},
// 设备返回数据的回调
handleDiscoveryData(res) {
console.log(res);
const newDataPoint = {
inten: this.data.planInfo.intensity,
heat: this.data.planInfo.heatLevel,
electric: res.CurrentLev,
receiveDataTime: new Date().getTime(),
xAxis: this.formatTime(this.data._countdownTotal - this.data.remainingTime),
}
this.data.baseInfo.dataInfo.push(newDataPoint);
this.setData({
'baseInfo.dataInfo': this.data.baseInfo.dataInfo
})
},
// 全部理疗完成时的重置
resetAllData() {
this.resetCurrentSessionData();
this.setData({
'physioDataInfo.info': []
});
},
// 重置当前部位接收设备返回的数据
resetCurrentSessionData() {
this.setData({
baseInfo: {
duration: null,
therapyMode: '',
startTime: null, //理疗开始时间
endTime: null, //理疗结束时间
dataInfo: []
}
})
},
// 初始化画布
initCanvas() {
const query = wx.createSelectorQuery();
query.select('#myCanvasId')
.fields({
node: true,
size: true
})
.exec(async (res) => {
const canvas = res[0].node;
const canvasContext = canvas.getContext('2d');
// 适配高清屏
const dpr = wx.getWindowInfo().pixelRatio;
canvas.width = res[0].width * dpr;
canvas.height = res[0].height * dpr;
canvasContext.scale(dpr, dpr);
this.setData({ canvasContext, canvasWidth: res[0].width, canvasHeight: res[0].height });
this.drawProgress(100); // 初始绘制满圆环
});
},
// 绘制进度(百分比)
drawProgress(progress) {
const { canvasContext, canvasWidth, canvasHeight } = this.data;
if (!canvasContext) return;
const centerX = canvasWidth / 2;
const centerY = canvasHeight - 25; // 圆心放在底部
const radius = 125;
// 使用线性插值实现平滑过渡
const currentProgress = Math.min(100, Math.max(0, progress))
// 清空画布
canvasContext.clearRect(0, 0, canvasWidth, canvasHeight);
// 绘制底层灰色圆环
canvasContext.beginPath();
canvasContext.arc(centerX, centerY, radius, -Math.PI, 0);
canvasContext.strokeStyle = '#F7F8FA';
canvasContext.lineWidth = 15;
canvasContext.stroke();
// 绘制进度圆环(逆时针方向)
if (currentProgress > 0) {
const startAngle = -Math.PI;
const endAngle = startAngle + (Math.PI * currentProgress) / 100;
canvasContext.beginPath();
canvasContext.arc(centerX, centerY, radius, startAngle, endAngle);
canvasContext.strokeStyle = '#009DDC';
canvasContext.lineWidth = 15;
canvasContext.lineCap = 'round';
canvasContext.stroke();
}
},
// 开始倒计时
startCountdown() {
this.sendStartCommands([{ "SetWork": "Run" }, { "SuccessiveData": "ON" }])
const now = Date.now();
const tempTime = this.data.remainingTime;
const startTimestamp = now - (this.data.totalTime - tempTime) * 1000;
this.setData({
isCountdownStarted: true,
remainingTime: tempTime,
formattedTime: this.formatTime(tempTime),
_countdownStart: startTimestamp, // 新增字段存储计时基准时间
_countdownTotal: this.data.totalTime, // 存储总时长
});
// 清除旧计时器
this.clearTimer();
// 启动新计时器
this.updateCountdown();
},
// 倒计时更新逻辑
updateCountdown() {
const now = Date.now();
const elapsed = (now - this.data._countdownStart) / 1000;
const remaining = Math.max(0, this.data._countdownTotal - elapsed);
if (remaining <= 0) {
this.handleCountdownEnd();
return;
}
// 更新显示(仅在变化时更新)
if (remaining !== this.data.remainingTime) {
this.setData({
remainingTime: Math.floor(remaining),
formattedTime: this.formatTime(Math.floor(remaining))
});
const progress = (remaining / this.data._countdownTotal) * 100;
this.drawProgress(progress);
}
this.data.timer = setTimeout(() => this.updateCountdown(), 10);
},
// 处理倒计时结束
handleCountdownEnd() {
this.clearTimer();
this.setData({
'baseInfo.endTime': new Date().getTime(),
remainingTime: 0,
formattedTime: this.formatTime(0),
isCountdownStarted: false,
});
this.drawProgress(0);
// 原有后续逻辑
this.sendStartCommands([{ "SuccessiveData": "OFF" }]);
const baseInfo = { ...this.data.baseInfo };
this.data.physioDataInfo.info.push(baseInfo);
this.setData({
'physioDataInfo.info': [...this.data.physioDataInfo.info]
});
//理疗结束
this.handleAllComplete();
},
// 全部完成的处理
handleAllComplete() {
wx.showModal({
title: '提示',
content: '时间到,理疗结束!',
showCancel: false,
complete: (res) => {
if (res.confirm) {
submitRecord(this.data.physioDataInfo).then(res => {
if (res.code == 0) {
wx.removeStorageSync('countdownState');
this.resetAllData();
wx.switchTab({ url: '/pages/index/index' });
}
});
}
}
})
},
//模式改变
changeTabHandle(E) {
const { index } = E.currentTarget.dataset
this.setData({ tabCurrentIndex: index });
this.sendStartCommands([{"SetMode": index}])
if(this.data.powerValue) {
this.sendStartCommands([{"SetInten": this.data.powerValue}])
}
},
//电流强度
powerChange(E) {
const { value } = E.detail;
const tempValue = Number(value);
if (tempValue <= 0 || tempValue > 99) {
wx.showToast({
title: `电流强度${ tempValue <= 0 ? '不得低于0档' : '不得超过99档' }`,
icon: 'none'
})
return false;
}
this.setData({ powerValue: tempValue });
this.sendStartCommands([{"SetInten": this.data.powerValue}])
},
decOrAddValue(E) {
const { type } = E.currentTarget.dataset;
if (type === 'dec' && this.data.powerValue == 0) {
wx.showToast({
title: '电流强度不得低于0档',
icon: 'none'
});
return false
} else if (type === 'add' && this.data.powerValue >= 99) {
wx.showToast({
title: '电流强度不得超过99档',
icon: 'none'
})
return false;
}
this.setData({
powerValue: type === 'add' ? this.data.powerValue + 1 : this.data.powerValue - 1
})
this.sendStartCommands([{"SetInten": this.data.powerValue}])
},
//加热强度
changeHotTabHandle(E) {
const { index } = E.currentTarget.dataset
this.setData({ hotTabCurrentIndex: index });
this.sendStartCommands([{"SetHeat": index}])
},
// 理疗频率
changeRateTabHandle(E) {
if(!this.data.tabCurrentIndex) {
return wx.showToast({ title: '请选择理疗模式', icon: 'none' })
}
const { index } = E.currentTarget.dataset;
this.setData({ rateTabCurrentIndex: index, rateValue: this.data.rateModelList[index] })
this.sendStartCommands([{"SetCustMode": this.setCustMode(this.data.tabCurrentIndex, this.data.rateValue)}])
if(this.data.powerValue) {
this.sendStartCommands([{"SetInten": this.data.powerValue}])
}
},
rateChange(E) {
if(!this.data.tabCurrentIndex) return wx.showToast({ title: '请选择理疗模式', icon: 'none' });
const { value } = E.detail;
const tempValue = Number(value);
if (tempValue < 10 || tempValue > 5000) {
wx.showToast({
title: `理疗频率${ tempValue < 10 ? '不得小于10HZ' : '不得超过5000HZ' }`,
icon: 'none'
})
return false;
}
this.setData({ rateValue: tempValue });
const index = this.data.rateModelList.findIndex(item => item == this.data.rateValue);
if(index != -1) {
this.setData({ rateTabCurrentIndex: index })
} else {
this.setData({ rateTabCurrentIndex: null })
}
this.sendStartCommands([{"SetCustMode": this.setCustMode(this.data.tabCurrentIndex, this.data.rateValue)}])
if(this.data.powerValue) {
this.sendStartCommands([{"SetInten": this.data.powerValue}])
}
},
rateInput(E) {
const { value } = E.detail;
if(value != '') this.setData({ rateTabCurrentIndex: null })
},
decOrAddRateValue(E) {
if(!this.data.tabCurrentIndex) return wx.showToast({ title: '请选择理疗模式', icon: 'none' });
const { type } = E.currentTarget.dataset;
if ((type === 'dec' && this.data.rateValue <= 10) || (type === 'add' && this.data.rateValue >= 5000)) {
wx.showToast({
title: `理疗频率${ type == 'dec' ? '不得小于10HZ' : '不得超过5000HZ' }`,
icon: 'none'
});
return false
}
this.setData({
rateValue: type === 'add' ? this.data.rateValue + 10 : this.data.rateValue - 10
})
const index = this.data.rateModelList.findIndex(item => item == this.data.rateValue);
if(index != -1) {
this.setData({ rateTabCurrentIndex: index })
} else {
this.setData({ rateTabCurrentIndex: null })
}
this.sendStartCommands([{"SetCustMode": this.setCustMode(this.data.tabCurrentIndex, this.data.rateValue)}])
if(this.data.powerValue) {
this.sendStartCommands([{"SetInten": this.data.powerValue}])
}
},
// 开始理疗
startHandle() {
if(!wx.getStorageSync('token')) return this.isLogin();
if(this.data.isStop && !this.data.isCountdownStarted) this.setData({'baseInfo.startTime': new Date().getTime()});
this.startCountdown();
this.setData({ isStop: false })
},
// 暂停理疗
stopHandle() {
this.clearTimer();
this.setData({ isStop: true });
this.sendStartCommands([{ "SetWork": "Stop" }, { "SuccessiveData": "OFF" }]);
},
// 格式化时间为 MM:SS
formatTime(seconds) {
const mins = Math.floor(seconds / 60).toString().padStart(2, '0');
const secs = (seconds % 60).toString().padStart(2, '0');
return `${mins}:${secs}`;
},
// 清空canvas
clearCanvas() {
this.setData({ canvasContext: null })
},
// 清理计时器
clearTimer() {
if (this.data.timer) {
clearTimeout(this.data.timer);
this.setData({ timer: null });
}
},
// 根据模式设置频率
setCustMode(typeMode, rate) {
let mode = {
"adj_num": 250,
"frequency": rate,
"timer_cont": 10,
"max_cont": 10,
"amp_num": 30
};
if(typeMode == 2) {
mode["adj_num"] = 200;
mode["timer_cont"] = 6;
}
return mode
},
// 批量发送多个指令给设备
sendStartCommands(initCommands) {
bleUtils.bleTool.sendCommands(initCommands, function() {
console.log('sendStartCommands')
})
},
// 是否登录
isLogin() {
wx.showModal({
content: '此功能需登陆后才可使用',
complete: (res) => {
if (res.confirm) wx.redirectTo({ url: '/pages/login/login' });
}
})
},
})优化我的代码