index.js 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593
  1. // nova-werun/pages/home/sport/sport-start/index.js
  2. const Parse = getApp().Parse;
  3. const company = getApp().globalData.company;
  4. Page({
  5. /**
  6. * 页面的初始数据
  7. */
  8. data: {
  9. //屏幕高度
  10. statusBarHeight: 0, // 状态栏高度
  11. screenHeight: 0, // 屏幕高度
  12. customHeight: 0, // 自定义导航栏高度(如小程序右上角胶囊按钮)
  13. bottomNavHeight: 0, // 底部导航栏高度
  14. contentHeight: 0, // 可用内容高度
  15. contentHeight2: 0,
  16. contentpadding: 0, //顶部padding高度
  17. navheight: 0,
  18. //地图
  19. longitude: 0,
  20. latitude: 0,
  21. markers: [],
  22. //是否暂停
  23. isstop: false,
  24. //标题
  25. title: '',
  26. percentage: '',
  27. timer: null,
  28. startTime: 0,
  29. show: false,
  30. //
  31. timer2: null,
  32. totalSeconds: 0, // 用于存储总秒数
  33. formattedTime: '00:00:00', // 用于存储格式化后的时间
  34. isRunning: false, // 计时器是否在运行
  35. steps: 0, //运动步数
  36. startsteps: 0,
  37. defferentstep: 0,
  38. distance: 0,
  39. calorie: 0,
  40. pace: 0,
  41. },
  42. /**
  43. * 生命周期函数--监听页面加载
  44. */
  45. onLoad: async function (options) {
  46. // 计算
  47. const systemInfo = wx.getSystemInfoSync();
  48. const statusBarHeight = systemInfo.statusBarHeight || 0;
  49. const screenHeight = systemInfo.screenHeight || 0;
  50. const custom = wx.getMenuButtonBoundingClientRect();
  51. const customHeight = custom.height + 10 + 2 || 0;
  52. const bottomNavHeight = systemInfo.screenHeight - systemInfo.safeArea.bottom || 0;
  53. const navheight = (statusBarHeight + customHeight) * 750 / systemInfo.windowWidth;
  54. const contentHeight = (screenHeight - bottomNavHeight) * 750 / systemInfo.windowWidth;
  55. this.setData({
  56. statusBarHeight,
  57. screenHeight,
  58. customHeight,
  59. bottomNavHeight,
  60. contentHeight,
  61. navheight,
  62. });
  63. this.setData({
  64. title: options.id
  65. })
  66. // 获取初始步数
  67. // 获取初始步数
  68. this.getWeRunData2()
  69. console.log(this.data.startsteps);
  70. // 地图
  71. await this.Getlocation();
  72. //开启后台定位
  73. this.startbackgroumd()
  74. },
  75. /**
  76. * 生命周期函数--监听页面初次渲染完成
  77. */
  78. onReady: function () {
  79. },
  80. /**
  81. * 生命周期函数--监听页面显示
  82. */
  83. onShow: function () {
  84. if (!this.data.isRunning&&!this.data.isstop) {
  85. this.startTimer();
  86. }
  87. },
  88. /**
  89. * 生命周期函数--监听页面隐藏
  90. */
  91. onHide: function () {
  92. if (this.data.isRunning) {
  93. this.stopTimer();
  94. }
  95. },
  96. /**
  97. * 生命周期函数--监听页面卸载
  98. */
  99. onUnload: function () {
  100. this.stopTimer();
  101. },
  102. /**
  103. * 页面相关事件处理函数--监听用户下拉动作
  104. */
  105. onPullDownRefresh: function () {
  106. },
  107. /**
  108. * 页面上拉触底事件的处理函数
  109. */
  110. onReachBottom: function () {
  111. },
  112. /**
  113. * 用户点击右上角分享
  114. */
  115. onShareAppMessage: function () {
  116. },
  117. //获取当前位置信息
  118. Getlocation() {
  119. // 获取当前位置信息
  120. wx.getLocation({
  121. type: 'gcj02',
  122. success: (res) => {
  123. const {
  124. latitude,
  125. longitude
  126. } = res;
  127. console.log('获取到的经纬度:', latitude, longitude); // 添加日志
  128. //调用api解析地址
  129. wx.request({
  130. url: 'https://api.map.baidu.com/reverse_geocoding/v3/?ak=sHZTomd7grslfP7sPKB8tRgT49FK9TEu&output=json&coordtype=gcj02&location=' + latitude + ',' + longitude,
  131. data: {},
  132. header: {
  133. 'Content-Type': 'application/json'
  134. },
  135. success: (ops) => { // 使用箭头函数
  136. const address = ops.data.result.formatted_address;
  137. this.setData({
  138. address: address,
  139. latitude: latitude, // 保证 latitude 被设置
  140. longitude: longitude, // 保证 longitude 被设置
  141. markers: [{ // 设置 markers
  142. id: 1,
  143. latitude: latitude,
  144. longitude: longitude,
  145. iconPath: 'https://file-cloud.fmode.cn/13WZ0W7u3l/20240724/7ebg0k104325941.png?imageView2/1/w/200/h/200', // 自定义标记图标
  146. width: 40,
  147. height: 40,
  148. // callout: {
  149. // content: address, // 可以显示解析出的地址
  150. // color: '#ffffff',
  151. // bgColor: '#7F56B2',
  152. // padding: 10,
  153. // borderRadius: 5,
  154. // display: 'ALWAYS'
  155. // }
  156. }]
  157. });
  158. },
  159. fail: function (resq) {
  160. wx.showModal({
  161. title: '信息提示',
  162. content: '请求失败',
  163. showCancel: false,
  164. confirmColor: '#f37938'
  165. });
  166. },
  167. complete: function () {}
  168. })
  169. },
  170. fail: (err) => {
  171. console.error(err);
  172. wx.showToast({
  173. title: '获取位置失败',
  174. icon: 'none'
  175. });
  176. }
  177. });
  178. },
  179. stop() {
  180. this.setData({
  181. isstop: !this.data.isstop
  182. })
  183. if (this.data.isstop) {
  184. this.stopTimer()
  185. } else {
  186. this.startTimer()
  187. }
  188. console.log(this.data.isstop);
  189. },
  190. //结束光环展示
  191. startIncrease() {
  192. // 记录开始时间
  193. this.setData({
  194. startTime: Date.now(),
  195. });
  196. // 清除之前的定时器
  197. if (this.data.timer) {
  198. clearInterval(this.data.timer);
  199. }
  200. // 设置定时器,每隔 40 毫秒更新一次 percentage
  201. this.setData({
  202. timer: setInterval(() => {
  203. const currentTime = Date.now();
  204. const elapsedTime = currentTime - this.data.startTime;
  205. const percentage = Math.min((elapsedTime / 4000) * 100, 100);
  206. this.setData({
  207. percentage: `conic-gradient(from 0deg, #015EEA ${percentage}%, white 0%)`,
  208. });
  209. if (percentage >= 100) {
  210. wx.showToast({
  211. title: '运动已结束',
  212. icon: 'success',
  213. duration: 500
  214. });
  215. setTimeout(() => {
  216. this.onConfirm()
  217. }, 500)
  218. }
  219. // 如果达到 100%,清除定时器
  220. if (percentage >= 100) {
  221. clearInterval(this.data.timer);
  222. this.setData({
  223. timer: null,
  224. });
  225. }
  226. }, 40),
  227. });
  228. },
  229. stopIncrease() {
  230. // 清除定时器
  231. if (this.data.timer) {
  232. clearInterval(this.data.timer);
  233. this.setData({
  234. timer: null,
  235. });
  236. // 如果未达到 100%,清零 percentage
  237. const elapsedTime = Date.now() - this.data.startTime;
  238. if (elapsedTime < 4000) {
  239. this.setData({
  240. percentage: '',
  241. });
  242. }
  243. }
  244. },
  245. //点击返回按钮
  246. goback() {
  247. this.setData({
  248. show: true,
  249. isstop: false,
  250. })
  251. },
  252. //取消弹窗
  253. onClose() {
  254. this.setData({
  255. show: false
  256. });
  257. },
  258. //确认返回
  259. onConfirm() {
  260. wx.navigateBack({
  261. delta: 1 // 返回的页面层数,1 表示返回上一页
  262. });
  263. },
  264. //开始计时或继续计时
  265. startTimer: function () {
  266. if (this.data.isRunning) return; // 如果已经在运行,则不再启动
  267. const that = this;
  268. this.data.timer2 = setInterval(function () {
  269. that.data.totalSeconds += 1; // 增加总秒数
  270. that.setData({
  271. formattedTime: that.formatTime(that.data.totalSeconds) // 更新格式化后的时间
  272. });
  273. // 每30秒调用一次getWeRunData
  274. if (that.data.totalSeconds % 30 === 0) {
  275. that.getWeRunData(); // 调用获取微信步数的函数
  276. }
  277. // 每4秒调用一次onLocationChange
  278. if (that.data.totalSeconds % 4 === 0) {
  279. that.onLocationChange((res) => {
  280. // 这里可以处理位置变化的逻辑
  281. console.log('位置已更新:', res);
  282. });
  283. }
  284. }, 1000);
  285. this.setData({
  286. isRunning: true // 设置为运行状态
  287. });
  288. },
  289. //暂停
  290. stopTimer: function () {
  291. clearInterval(this.data.timer2);
  292. this.setData({
  293. isRunning: false // 设置为未运行状态
  294. });
  295. },
  296. formatTime: function (seconds) {
  297. const hours = Math.floor(seconds / 3600);
  298. const minutes = Math.floor((seconds % 3600) / 60);
  299. const secs = seconds % 60;
  300. // 格式化为两位数
  301. const formattedHours = String(hours).padStart(2, '0');
  302. const formattedMinutes = String(minutes).padStart(2, '0');
  303. const formattedSeconds = String(secs).padStart(2, '0');
  304. return `${formattedHours}:${formattedMinutes}:${formattedSeconds}`;
  305. },
  306. //获取初始步数
  307. async getWeRunData2() {
  308. wx.getWeRunData({
  309. success: (res) => {
  310. // 获取到的加密数据
  311. const encryptedData = res.encryptedData;
  312. const iv = res.iv;
  313. const userInfo = wx.getStorageSync('userInfo');
  314. const session_key = userInfo.session_key
  315. // 这里需要调用你的后端接口进行解密
  316. // 假设你有一个解密函数 decryptData
  317. this.decryptData(encryptedData, iv, session_key).then(async steps => {
  318. await this.progressdate(steps)
  319. console.log('再次赋值');
  320. await this.setData({
  321. startsteps: steps,
  322. });
  323. console.log('用户步数:', this.data.steps);
  324. }).catch(err => {
  325. console.error('解密失败:', err);
  326. });
  327. },
  328. fail: (err) => {
  329. console.error('获取运动数据失败:', err);
  330. }
  331. });
  332. },
  333. //获取微信步数
  334. async getWeRunData() {
  335. wx.getWeRunData({
  336. success: (res) => {
  337. // 获取到的加密数据
  338. const encryptedData = res.encryptedData;
  339. const iv = res.iv;
  340. const userInfo = wx.getStorageSync('userInfo');
  341. const session_key = userInfo.session_key
  342. // 这里需要调用你的后端接口进行解密
  343. // 假设你有一个解密函数 decryptData
  344. this.decryptData(encryptedData, iv, session_key).then(async steps => {
  345. await this.progressdate(steps)
  346. console.log('再次赋值');
  347. await this.setData({
  348. steps: steps,
  349. defferentstep: steps - this.data.startsteps
  350. });
  351. console.log('用户步数:', this.data.steps);
  352. }).catch(err => {
  353. console.error('解密失败:', err);
  354. });
  355. },
  356. fail: (err) => {
  357. console.error('获取运动数据失败:', err);
  358. }
  359. });
  360. },
  361. // 解密
  362. decryptData(encryptedData, iv, session_key) {
  363. return new Promise((resolve, reject) => {
  364. // 发送请求到后端进行解密
  365. wx.request({
  366. url: 'https://server.fmode.cn/api/wxapp/decrypt_phone', // 替换为你的后端解密接口
  367. method: 'get',
  368. data: {
  369. encryptedData: encryptedData,
  370. iv: iv,
  371. appId: 'wxe6ecc0193c09696c',
  372. sessionKey: session_key
  373. },
  374. success: (res) => {
  375. // if (res.data && res.data.steps) {
  376. // console.log(res.data.steps);
  377. // // resolve(res.data.steps); // 返回步数
  378. // // const steps = 123456
  379. // resolve(steps);
  380. // } else {
  381. // reject('解密返回数据格式错误');
  382. // }
  383. if (res.data.data) {
  384. const stepInfoList = res.data.data.stepInfoList
  385. const todaylist = stepInfoList.filter(item => {
  386. console.log(this.isToday(item));
  387. return this.isToday(item); // 使用 isToday 函数判断是否是今天
  388. });
  389. const steps = todaylist[0].step;
  390. resolve(steps);
  391. } else {
  392. reject('解密返回数据格式错误');
  393. }
  394. },
  395. fail: (err) => {
  396. reject(err);
  397. }
  398. });
  399. });
  400. },
  401. // 判断日期是否是今天
  402. isToday(item) {
  403. const today = new Date();
  404. const date = new Date(item.timestamp * 1000); // 假设时间戳是以秒为单位
  405. // 比较年、月、日
  406. return date.getFullYear() === today.getFullYear() &&
  407. date.getMonth() === today.getMonth() &&
  408. date.getDate() === today.getDate();
  409. },
  410. //创建过程数据
  411. async progressdate(steps) {
  412. if (steps == this.data.steps) {
  413. console.log('步数没变');
  414. return
  415. }
  416. const currentUser = Parse.User.current();
  417. let Userquery = new Parse.Query('_User');
  418. Userquery.equalTo('company', company);
  419. Userquery.equalTo('objectId', currentUser.id);
  420. Userquery.notEqualTo('isDeleted', true)
  421. let user = await Userquery.first();
  422. let companyPointer = Parse.Object.extend('Company').createWithoutData(company);
  423. let query = new Parse.Object('ActivityRunLog');
  424. query.set('user', user.toPointer())
  425. query.set('company', companyPointer)
  426. query.set('stage', 'progress ')
  427. query.set('steps', this.data.steps)
  428. try {
  429. // let saveDate2 = await Comment.save();
  430. console.log("新数据保存成功");
  431. } catch (error) {
  432. console.error("保存数据时出现错误:", error);
  433. }
  434. },
  435. // 开启后台定位
  436. startbackgroumd() {
  437. wx.startLocationUpdateBackground({
  438. success: (res) => {
  439. // 开始监听GPS数据
  440. this.onLocationChange((res) => {
  441. // 在这里处理位置变化的逻辑
  442. console.log('位置已更新:', res);
  443. });
  444. },
  445. fail: (res) => {
  446. // 授权失败后引导用户打开定位信息
  447. // 可以添加提示或引导用户操作的代码
  448. }
  449. });
  450. },
  451. // 监听位置变化
  452. onLocationChange(callback) {
  453. console.log('运行了');
  454. // 监听位置变化
  455. this.Getlocation2()
  456. // wx.onLocationChange((res) => {
  457. // console.log('res', res);
  458. // // 计算距离
  459. // let distance = this.haversineDistance(this.data.longitude, this.data.latitude, res.longitude, res.latitude);
  460. // this.setData({
  461. // distance: this.data.distance + distance,
  462. // calorie: this.getCalorie(60, this.data.distance), // 假设体重为60kg
  463. // latitude: res.latitude,
  464. // longitude: res.longitude
  465. // });
  466. // // 打印更新后的经纬度(可选)
  467. // console.log('更新后的经纬度:', this.data.latitude, this.data.longitude);
  468. // // 调用回调函数(如果有提供的话)
  469. // if (callback) {
  470. // callback(res);
  471. // }
  472. // });
  473. },
  474. //位置变化
  475. Getlocation2() {
  476. // 获取当前位置信息
  477. wx.getLocation({
  478. type: 'gcj02',
  479. success: (res) => {
  480. const {
  481. latitude,
  482. longitude
  483. } = res;
  484. if (this.data.longitude == res.longitude && this.data.latitude == res.latitude) {
  485. console.log('位置没变');
  486. } else {
  487. // 计算距离
  488. let distance = this.haversineDistance(this.data.longitude, this.data.latitude, res.longitude, res.latitude);
  489. // 更新总距离
  490. const totalDistance = parseFloat((Number(this.data.distance) + Number(distance)).toFixed(3));
  491. // 计算卡路里
  492. const calorie = this.getCalorie(60, totalDistance); // 假设体重为60kg
  493. const pace = parseFloat(Number((totalDistance / (this.data.totalSeconds / 3600)).toFixed(2))); // 配速(km/h)
  494. console.log('总距离',totalDistance,'段距离',distance,'总卡路里',calorie,'配速',pace);
  495. // 更新状态
  496. this.setData({
  497. pace,
  498. distance: totalDistance,
  499. calorie: calorie,
  500. latitude: res.latitude,
  501. longitude: res.longitude
  502. });
  503. console.log('更新经纬度:', latitude, longitude); // 添加日志
  504. }
  505. },
  506. fail: (err) => {
  507. console.error(err);
  508. wx.showToast({
  509. title: '获取位置失败',
  510. icon: 'none'
  511. });
  512. }
  513. });
  514. },
  515. //计算两点距离
  516. haversineDistance(longitude1, latitude1, longitude2, latitude2) {
  517. let lat1 = latitude1
  518. let lon1 = longitude1
  519. let lat2 = latitude2
  520. let lon2 = longitude2
  521. function toRad(degree) {
  522. return degree * Math.PI / 180;
  523. }
  524. const R = 6371e3; // 地球半径,单位为米
  525. const φ1 = toRad(lat1);
  526. const φ2 = toRad(lat2);
  527. const Δφ = toRad(lat2 - lat1);
  528. const Δλ = toRad(lon2 - lon1);
  529. const a = Math.sin(Δφ / 2) * Math.sin(Δφ / 2) +
  530. Math.cos(φ1) * Math.cos(φ2) *
  531. Math.sin(Δλ / 2) * Math.sin(Δλ / 2);
  532. const c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1 - a));
  533. const distance = (R * c) / 1000; // 距离,单位为公里
  534. console.log('计算出距离', distance);
  535. return parseFloat(distance.toFixed(3));
  536. },
  537. //计算卡路里
  538. getCalorie(weight, distance) {
  539. weight = weight || 60 // 国人平均体重 60kg
  540. const Calorie = weight * distance * 0.75
  541. console.log('卡路里为', Calorie);
  542. return parseFloat(Calorie.toFixed(2)); // 保留两位小数并转换为数字
  543. }
  544. })