move.tsx 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504
  1. import { View, Text, Image } from "@tarojs/components";
  2. import './move.scss'
  3. import { useDispatch, useSelector } from "react-redux";
  4. import { getScenario, getThemeColor } from "@/features/health/hooks/health_hooks";
  5. import { useEffect, useState } from "react";
  6. import Taro from "@tarojs/taro";
  7. import { checkStart, setResult } from "@/store/action_results";
  8. import RequestType, { thirdPartRequest } from "@/services/thirdPartRequest";
  9. import { setAuth } from "@/features/trackSomething/hooks/werun";
  10. import { useTranslation } from "react-i18next";
  11. import { getActiveMoves, getActiveMovesCurrent, uploadActiveMoves } from "@/services/health";
  12. import dayjs from "dayjs";
  13. import { TimeFormatter } from "@/utils/time_format";
  14. import { jumpPage } from "@/features/trackTimeDuration/hooks/Common";
  15. let timer
  16. export default function Move() {
  17. const health = useSelector((state: any) => state.health);
  18. const [allowRun, setAllowRun] = useState(false)
  19. const [loaded, setLoaded] = useState(false)
  20. const [count, setCount] = useState(0)
  21. const [data, setData] = useState<any>(null)
  22. const [list, setList] = useState<any>([])
  23. const [total, setTotal] = useState(0)
  24. const [index, setIndex] = useState(1)
  25. const [hideCurrentRecent, setHideCurrentRecent] = useState(false)
  26. const [moreActive, setMoreActive] = useState(false)
  27. const [hours, setHours] = useState<any>([])
  28. const [startDate, setStarteDate] = useState('')
  29. const { t } = useTranslation()
  30. const dispatch = useDispatch()
  31. useEffect(() => {
  32. getMovesCurrent()
  33. getMovesHistory()
  34. Taro.getSetting({
  35. success: res => {
  36. //第一步,检测是否有授权 - 没有授权
  37. if (!res.authSetting['scope.werun']) {
  38. setAllowRun(false)
  39. Taro.setStorage({ key: 'auth', data: false })
  40. }
  41. else {
  42. setAllowRun(true)
  43. Taro.setStorage({ key: 'auth', data: true })
  44. }
  45. }
  46. })
  47. timer = setInterval(() => {
  48. setCount((index) => index + 1)
  49. const date = new Date()
  50. if (date.getMinutes() == 10 && date.getSeconds() == 0) {
  51. getMovesCurrent()
  52. getMovesHistory()
  53. }
  54. }, 1000)
  55. return () => {
  56. clearInterval(timer)
  57. }
  58. }, [])
  59. global.updateMove = ()=>{
  60. getMovesCurrent()
  61. getMovesHistory()
  62. }
  63. function getMovesCurrent() {
  64. getActiveMovesCurrent().then(res => {
  65. setLoaded(true)
  66. setData(res)
  67. setStarteDate((res as any).start_date)
  68. var array = (res as any).hours
  69. var temps: any = []
  70. for (var i = array.length - 1; i >= 0; i--) {
  71. var obj = array[i]
  72. if (obj.status != 'WFS') {
  73. temps.push(obj)
  74. }
  75. }
  76. setHours(temps)
  77. // setHours((res as any).hours.reverse())
  78. })
  79. }
  80. function getMovesHistory() {
  81. getActiveMoves().then(res => {
  82. if (index == 1) {
  83. setList((res as any).data)
  84. setTotal((res as any).total)
  85. }
  86. else {
  87. setList([...list, ...(res as any).data])
  88. }
  89. })
  90. }
  91. function tapLog() {
  92. // if (getStatus() != 'open') {
  93. // return
  94. // }
  95. if (allowRun) {
  96. checkout()
  97. }
  98. else {
  99. setAuth(successAuth, refuseAuth, t)
  100. }
  101. }
  102. function successAuth() {
  103. Taro.setStorage({ key: 'auth', data: true })
  104. setAllowRun(true)
  105. }
  106. function refuseAuth() {
  107. // setTitle('开启');
  108. Taro.setStorage({ key: 'auth', data: false })
  109. setAllowRun(false)
  110. }
  111. function checkout() {
  112. dispatch(checkStart());
  113. getWeRunData(false)
  114. }
  115. function getWeRunData(autoCheck = false) {
  116. if (autoCheck) {
  117. return
  118. }
  119. else {
  120. dispatch(checkStart());
  121. }
  122. // setTitle('打卡');
  123. setAllowRun(true)
  124. let schedule_id = null
  125. const hour = currentHourPeriod()
  126. data.hours.map(item => {
  127. if (item.hour == hour) {
  128. schedule_id = item.schedule_id
  129. }
  130. })
  131. thirdPartRequest(RequestType.RequestTypeWXRunData).then(res => {
  132. // var params = {
  133. // is_manual: autoCheck ? 0 : 1,
  134. // timestamp: time,
  135. // encryptedData: (res as any).encryptedData,
  136. // iv: (res as any).iv,
  137. // date: strDate,
  138. // cloudID: (res as any).cloudID,
  139. // }
  140. uploadActiveMoves({
  141. wechat_run: {
  142. encryptedData: (res as any).encryptedData,
  143. iv: (res as any).iv,
  144. },
  145. start_date: startDate,
  146. date: dayjs().format('YYYYMMDD'),
  147. hour: hour,
  148. timestamp: new Date().getTime(),
  149. schedule_id: schedule_id
  150. }).then(res => {
  151. Taro.showToast({
  152. title: '上报成功',
  153. icon: 'none'
  154. })
  155. getMovesCurrent()
  156. getMovesHistory()
  157. })
  158. }).catch(_ => {
  159. dispatch(setResult({ isSuccess: false }) as any)
  160. })
  161. }
  162. function currentCheckHour() {
  163. let currentHour = new Date().getHours()
  164. const minute = new Date().getMinutes()
  165. if (minute < 10) {
  166. currentHour -= 1;
  167. if (currentHour < 0) {
  168. currentHour = 23
  169. }
  170. }
  171. return currentHour
  172. }
  173. function currentHourChecked() {
  174. const currentHour = currentCheckHour()
  175. var isChecked = false
  176. data.hours.map((item) => {
  177. if (item.hour == currentHour && item.status != 'NOT_CHECK') {
  178. isChecked = true
  179. }
  180. })
  181. return isChecked;
  182. }
  183. function getStatus() {
  184. const minute = new Date().getMinutes()
  185. if (minute >= 50 || minute < 10) {
  186. var isChecked = currentHourChecked()
  187. if (isChecked) {
  188. return 'upcoming'
  189. }
  190. return 'open'
  191. }
  192. return 'upcoming'
  193. }
  194. function currentFootDesc() {
  195. var isChecked = currentHourChecked()
  196. const minute = new Date().getMinutes()
  197. if (isChecked && (minute < 10 || minute >= 50)) {
  198. let time = new Date()
  199. time.setMinutes(50)
  200. time.setSeconds(0)
  201. time.setMilliseconds(0)
  202. // 获取总秒数
  203. const totalSeconds = Math.floor((time.getTime() + 3600 * 1000 - new Date().getTime()) / 1000);
  204. // 计算小时、分钟和秒
  205. // const hours = String(Math.floor(totalSeconds / 3600)).padStart(2, '0');
  206. const minutes = String(Math.floor((totalSeconds % 3600) / 60)).padStart(2, '0');
  207. const seconds = String(totalSeconds % 60).padStart(2, '0');
  208. // return `${hours}:${minutes}:${seconds}`;
  209. return `Opening soon ${minutes}:${seconds}`
  210. }
  211. const pre = new Date().getTime()
  212. const next = new Date().getTime() + 3600 * 1000
  213. if (minute >= 50) {
  214. return `Now open ${dayjs(pre).format('HH:50')}-${dayjs(next).format('HH:10')}`
  215. }
  216. else if (minute < 10) {
  217. let time = new Date()
  218. time.setMinutes(10)
  219. time.setSeconds(0)
  220. time.setMilliseconds(0)
  221. // 获取总秒数
  222. const totalSeconds = Math.floor((time.getTime() - new Date().getTime()) / 1000);
  223. // 计算小时、分钟和秒
  224. // const hours = String(Math.floor(totalSeconds / 3600)).padStart(2, '0');
  225. const minutes = String(Math.floor((totalSeconds % 3600) / 60)).padStart(2, '0');
  226. const seconds = String(totalSeconds % 60).padStart(2, '0');
  227. // return `${hours}:${minutes}:${seconds}`;
  228. return `Closing in ${minutes}:${seconds}`
  229. }
  230. let time = new Date()
  231. time.setMinutes(50)
  232. time.setSeconds(0)
  233. time.setMilliseconds(0)
  234. // 获取总秒数
  235. const totalSeconds = Math.floor((time.getTime() - new Date().getTime()) / 1000);
  236. // 计算小时、分钟和秒
  237. // const hours = String(Math.floor(totalSeconds / 3600)).padStart(2, '0');
  238. const minutes = String(Math.floor((totalSeconds % 3600) / 60)).padStart(2, '0');
  239. const seconds = String(totalSeconds % 60).padStart(2, '0');
  240. // return `${hours}:${minutes}:${seconds}`;
  241. return `Opening soon ${minutes}:${seconds}`
  242. }
  243. function getDuration() {
  244. let now = new Date()
  245. let pre = new Date().getTime()
  246. if (now.getMinutes() < 10) {
  247. pre -= 3600 * 1000
  248. }
  249. let next = pre + 3600 * 1000
  250. if (currentHourChecked()) {
  251. pre = next
  252. next = pre + 3600 * 1000
  253. }
  254. return dayjs(pre).format('HH:00') + '-' + dayjs(next).format('HH:00')
  255. }
  256. function getCurrentTarget() {
  257. let now = new Date()
  258. let pre = new Date().getTime()
  259. if (now.getMinutes() < 10) {
  260. pre -= 3600 * 1000
  261. }
  262. if (currentHourChecked()) {
  263. pre += 3600 * 1000
  264. }
  265. const hour = new Date(pre).getHours()
  266. for (var i = 0; i < data.hours.length; i++) {
  267. if (data.hours[i].hour == hour) {
  268. return `${data.hours[i].real_steps}/${data.hours[i].target_steps}`
  269. }
  270. }
  271. }
  272. function process() {
  273. return `${data.stat.active_hours} / ${data.stat.waking_hours}`
  274. }
  275. function currentHourPeriod() {
  276. const time = new Date()
  277. let hour = time.getHours()
  278. //假如00:09,则为23小时
  279. if (time.getMinutes() < 10) {
  280. hour--;
  281. }
  282. if (hour < 0) {
  283. hour = 23
  284. }
  285. return hour
  286. }
  287. function isExtra() {
  288. const hour = currentHourPeriod()
  289. var isFind = false
  290. data.hours.map((item) => {
  291. if (item.hour == hour) {
  292. isFind = true
  293. }
  294. })
  295. return !isFind
  296. }
  297. function currentContent() {
  298. const hour = currentHourPeriod()
  299. let info: any = null
  300. data.hours.map(item => {
  301. if (item.hour == hour) {
  302. info = item
  303. }
  304. })
  305. return <View className="move-header">
  306. <View className="header-current">
  307. <View className="current_intro">
  308. <View style={{ color: (new Date().getMinutes() < 10 && getStatus() == 'open') ? getThemeColor(health.mode) : '#b2b2b2' }}>{getDuration()}</View>
  309. {
  310. isExtra() ? <Text style={{ color: getThemeColor('SLEEP') }}>is extra</Text> : <Text>{getCurrentTarget()} steps</Text>
  311. }
  312. </View>
  313. <View className="log_btn" style={{
  314. backgroundColor: getStatus() == 'open' ? getThemeColor(health.mode) : '#6666661A',
  315. color: getStatus() == 'open' ? '#fff' : '#666666'
  316. }} onClick={tapLog}>Log</View>
  317. <View className="border_footer_line" />
  318. </View>
  319. <View className="current_footer">
  320. <Text>{currentFootDesc()}</Text>
  321. <Text onClick={() => {
  322. jumpPage(`/_health/pages/move_schedule?hours=${JSON.stringify(data.hours)}`)
  323. }}>Show More</Text>
  324. </View>
  325. </View>
  326. }
  327. function currentHistory(item) {
  328. var start = item.hour
  329. var isYesterday = start > new Date().getHours()
  330. var end = start + 1
  331. start = (start + '').padStart(2, '0')
  332. end = (end + '').padStart(2, '0')
  333. return <View className="current_history_item">
  334. <View style={{ display: 'flex', flexDirection: 'column' }}>
  335. <Text className="current_item_period">{isYesterday ? '昨天' : ''}{start}:00-{end}:00</Text>
  336. {
  337. item.is_extra && <Text style={{ color: getThemeColor('SLEEP') }}>is Extra</Text>
  338. }
  339. <Text>{item.real_steps}/{item.target_steps}</Text>
  340. </View>
  341. {
  342. item.status == 'MISSED' && <Text className="missed">Missed</Text>
  343. }
  344. {
  345. item.status == 'SEDENTARY' && <Image className="history_item_detail_icon" src={require('@assets/_health/sit.png')} />
  346. }
  347. {
  348. item.status == 'ACTIVE' && <Image className="history_item_detail_icon" src={require('@assets/_health/walk.png')} />
  349. }
  350. <View className="border_footer_line" />
  351. </View>
  352. }
  353. function activeHour() {
  354. return <View className="summary">
  355. <View className="summary_header">
  356. <Text>Active Hours</Text>
  357. <Image className="arrow_hide"
  358. onClick={() => {
  359. setHideCurrentRecent(!hideCurrentRecent)
  360. }}
  361. src={hideCurrentRecent ? require('@assets/_health/arrow_down.png') : require('@assets/_health/arrow_up.png')} />
  362. <View style={{ flex: 1 }} />
  363. <Text style={{ color: getThemeColor(health.mode) }}>{process()}</Text>
  364. <View className="border_footer_line" />
  365. </View>
  366. {
  367. !hideCurrentRecent && <View className="summary_content">
  368. {
  369. hours.map((item, index) => {
  370. // if (item.status == 'NOT_CHECK') return <View key={index} />
  371. if (index >= 3 && !moreActive) return <View key={index} />
  372. return <View key={index}>
  373. {
  374. currentHistory(item)
  375. }
  376. </View>
  377. })
  378. }
  379. {
  380. hours.length > 3 && <View className="recent_btn_bg">
  381. {
  382. moreActive ? <View className="recent_btn" onClick={() => setMoreActive(false)}>收起</View> : <View className="recent_btn" onClick={() => setMoreActive(true)}>展开剩余{hours.length - 3}条</View>
  383. }
  384. <View className="border_footer_line" />
  385. </View>
  386. }
  387. </View>
  388. }
  389. {
  390. hours.length == 0 && <View className="no_current">
  391. <Text>No Active Hours Yet.</Text>
  392. <View className="border_footer_line"/>
  393. </View>
  394. }
  395. <View className="summary_footer">
  396. <View className="summary_footer_item">
  397. <Text className="light_desc">Steps</Text>
  398. <Text>{data.stat.real_steps}/{data.stat.target_steps}</Text>
  399. </View>
  400. <View className="summary_footer_item">
  401. <Text className="light_desc">Calories</Text>
  402. <Text>{data.stat.real_calories}/{data.stat.target_calories}</Text>
  403. </View>
  404. <View className="summary_footer_item">
  405. <Text className="light_desc">Distance</Text>
  406. <Text>{data.stat.real_distance}/{data.stat.target_distance}</Text>
  407. </View>
  408. </View>
  409. </View>
  410. }
  411. if (!loaded) return <View />
  412. return <View style={{ display: 'flex', flexDirection: 'column' }}>
  413. <Text className="move-title">Move Every Hour</Text>
  414. <Text onClick={() => {
  415. jumpPage('./move_setting_time')
  416. }}>Setting</Text>
  417. {
  418. currentContent()
  419. }
  420. {
  421. activeHour()
  422. }
  423. {
  424. list.length > 0 && <View className="history">
  425. <View className="history_header">
  426. <Text className="history_header_title">Recent</Text>
  427. <View className="border_footer_line" />
  428. </View>
  429. {
  430. list.map((item, index) => {
  431. return <View key={index} className="history_item">
  432. <Text className="history_item_date">{(item.date + '').substring(6, 8)}</Text>
  433. <View className="history_item_detail_bg">
  434. <Image className="history_item_detail_icon" src={require('@assets/_health/walk.png')} />
  435. <Text>每小时活动 {item.real_hours}/{item.target_hours} · Steps {item.stat.real_steps}/{item.stat.target_steps}</Text>
  436. </View>
  437. <View className="border_footer_line" />
  438. </View>
  439. })
  440. }
  441. </View>
  442. }
  443. {
  444. (list.length > 0) && (total == list.length) && <Text className="no_more">没有更多了</Text>
  445. }
  446. </View>
  447. }