move.tsx 34 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930
  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, { useDidShow } 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. import { rpxToPx } from "@/utils/tools";
  16. import { IconActive, IconMore, IconSit } from "@/components/basic/Icons";
  17. import NewHeader, { NewHeaderType } from "../components/new_header";
  18. import NewButton, { NewButtonType } from "../base/new_button";
  19. import { MainColorType } from "@/context/themes/color";
  20. import StatusIndicator, { StatusType } from "../base/status_indicator";
  21. import showActionSheet from "@/components/basic/ActionSheet";
  22. import ListFooter from "../components/list_footer";
  23. import StickyDateList from "../components/sticky_date_list";
  24. let timer
  25. let myScrollTop = 0
  26. export default function Move() {
  27. const health = useSelector((state: any) => state.health);
  28. const [allowRun, setAllowRun] = useState(false)
  29. const [loaded, setLoaded] = useState(false)
  30. const [loadAuth, setLoadAuth] = useState(false)
  31. const [count, setCount] = useState(0)
  32. const [data, setData] = useState<any>(null)
  33. const [list, setList] = useState<any>([])
  34. const [total, setTotal] = useState(0)
  35. const [index, setIndex] = useState(1)
  36. const [hideCurrentRecent, setHideCurrentRecent] = useState(false)
  37. const [moreActive, setMoreActive] = useState(false)
  38. const [hours, setHours] = useState<any>([])
  39. const [startDate, setStarteDate] = useState('')
  40. const [btnDisable,setBtnDisable] = useState(false)
  41. const [isPulling, setIsPulling] = useState(false)
  42. const [itemLayouts, setItemLayouts] = useState<any>([])
  43. const [showDate, setShowDate] = useState(false)
  44. const [date, setDate] = useState('')
  45. const [page, setPage] = useState(1)
  46. const [loading, setLoading] = useState(false)
  47. let navigation, showActionSheetWithOptions;
  48. const { t } = useTranslation()
  49. const dispatch = useDispatch()
  50. useEffect(() => {
  51. getMovesCurrent()
  52. getMovesHistory(1)
  53. checkAuth()
  54. timer = setInterval(() => {
  55. setCount((index) => index + 1)
  56. const date = new Date()
  57. if (date.getMinutes() == 10 && date.getSeconds() == 0) {
  58. getMovesCurrent()
  59. getMovesHistory(1)
  60. }
  61. }, 1000)
  62. return () => {
  63. myScrollTop = 0
  64. clearInterval(timer)
  65. }
  66. }, [])
  67. useEffect(() => {
  68. if (list.length > 0) {
  69. setTimeout(() => {
  70. measureItemLayouts()
  71. }, 500)
  72. }
  73. }, [list])
  74. function measureItemLayouts() {
  75. const query = Taro.createSelectorQuery()
  76. list.forEach((item, index) => {
  77. query.select(`#history-${index}`).boundingClientRect()
  78. });
  79. query.exec((res) => {
  80. var layouts: any = []
  81. res.forEach((rect, index) => {
  82. if (rect) {
  83. layouts[index] = rect.top + myScrollTop
  84. }
  85. });
  86. setItemLayouts(layouts)
  87. })
  88. }
  89. function checkAuth() {
  90. Taro.getSetting({
  91. success: res => {
  92. //第一步,检测是否有授权 - 没有授权
  93. if (!res.authSetting['scope.werun']) {
  94. setAllowRun(false)
  95. Taro.setStorage({ key: 'auth', data: false })
  96. }
  97. else {
  98. setAllowRun(true)
  99. Taro.setStorage({ key: 'auth', data: true })
  100. }
  101. setLoadAuth(true)
  102. }
  103. })
  104. }
  105. useDidShow(() => {
  106. checkAuth()
  107. getMovesCurrent()
  108. // getMovesHistory(1)
  109. })
  110. global.updateMove = () => {
  111. getMovesCurrent()
  112. getMovesHistory(1)
  113. }
  114. function getMovesCurrent() {
  115. getActiveMovesCurrent().then(res => {
  116. setLoaded(true)
  117. setData(res)
  118. setStarteDate((res as any).start_date)
  119. var array = (res as any).hours
  120. var temps: any = []
  121. for (var i = array.length - 1; i >= 0; i--) {
  122. var obj = array[i]
  123. if (obj.status != 'WFS') {
  124. temps.push(obj)
  125. }
  126. }
  127. setHours(temps)
  128. // setHours((res as any).hours.reverse())
  129. })
  130. }
  131. function getMovesHistory(page) {
  132. setIndex(page)
  133. // setIsPulling(true)
  134. setLoading(true)
  135. getActiveMoves({
  136. page: page,
  137. limit: 10,
  138. }
  139. ).then(res => {
  140. if (page == 1) {
  141. setList((res as any).data)
  142. setTotal((res as any).total)
  143. }
  144. else {
  145. setList([...list, ...(res as any).data])
  146. }
  147. setIsPulling(false)
  148. setLoading(false)
  149. }).catch(e => {
  150. setLoading(false)
  151. })
  152. }
  153. function more() {
  154. if (loading) return;
  155. if (total == list.length) return;
  156. var index = page;
  157. index++;
  158. setPage(index)
  159. getMovesHistory(index)
  160. }
  161. function onScroll(e) {
  162. var top = e.detail.scrollTop
  163. myScrollTop = top
  164. if (e.detail.scrollTop > 70) {
  165. Taro.setNavigationBarTitle({
  166. title:t('health.move_every_hour')
  167. })
  168. }
  169. else {
  170. Taro.setNavigationBarTitle({
  171. title:''
  172. })
  173. }
  174. if (itemLayouts.length > 0) {
  175. var i = -1
  176. var date = ''
  177. list.forEach((item, index) => {
  178. if (top >= itemLayouts[index] - 50) {
  179. i = index
  180. // var currentDate = (list[index].date + '').substring(0, 6)
  181. // date = currentDate.substring(0, 4) + '年' + currentDate.substring(4, 6) + '月'
  182. var currentDate = list[index].date + ''
  183. var date1 = currentDate.substring(0, 4) + '-' + currentDate.substring(4, 6) + '-'+ currentDate.substring(6, 8)
  184. var timestamp = new Date(date1).getTime()
  185. if (TimeFormatter.isTimestampInThisWeek(timestamp)) {
  186. date = t('health.this_week')
  187. }
  188. else if (dayjs(timestamp).format('YYYY') == dayjs().format('YYYY')) {
  189. date = global.language == 'en' ? dayjs(timestamp).format('MMMM') : dayjs(timestamp).format('MMMM')
  190. }
  191. else {
  192. date = global.language == 'en' ? dayjs(timestamp).format('YYYY') : dayjs(timestamp).format('YYYY年')
  193. }
  194. }
  195. })
  196. setShowDate(i != -1)
  197. setDate(date)
  198. }
  199. else {
  200. setShowDate(false)
  201. setDate('')
  202. }
  203. }
  204. function tapLog() {
  205. // if (getStatus() != 'open') {
  206. // return
  207. // }
  208. if (isExtra()) {
  209. return;
  210. }
  211. if (allowRun) {
  212. checkout()
  213. }
  214. else {
  215. setAuth(successAuth, refuseAuth, t)
  216. }
  217. }
  218. function successAuth() {
  219. Taro.setStorage({ key: 'auth', data: true })
  220. setAllowRun(true)
  221. }
  222. function refuseAuth() {
  223. Taro.setStorage({ key: 'auth', data: false })
  224. setAllowRun(false)
  225. }
  226. function checkout() {
  227. dispatch(checkStart());
  228. getWeRunData(false)
  229. }
  230. function getWeRunData(autoCheck = false) {
  231. if (autoCheck) {
  232. return
  233. }
  234. else {
  235. dispatch(checkStart());
  236. }
  237. // setTitle('打卡');
  238. setAllowRun(true)
  239. let schedule_id = null
  240. const hour = currentHourPeriod()
  241. data.hours.map(item => {
  242. if (item.hour == hour) {
  243. schedule_id = item.schedule_id
  244. }
  245. })
  246. if (btnDisable) return
  247. setBtnDisable(true)
  248. thirdPartRequest(RequestType.RequestTypeWXRunData).then(res => {
  249. console.log(res)
  250. uploadActiveMoves({
  251. wechat_run: {
  252. encryptedData: (res as any).encryptedData,
  253. iv: (res as any).iv,
  254. },
  255. start_date: startDate,
  256. date: dayjs().format('YYYYMMDD'),
  257. hour: hour,
  258. timestamp: new Date().getTime(),
  259. schedule_id: schedule_id
  260. }).then(res => {
  261. if ((res as any).error_code == 'WX_STEP_PARSE_FAIL') {
  262. retry(hour, schedule_id)
  263. return
  264. }
  265. // Taro.showToast({
  266. // title: '上报成功',
  267. // icon: 'none'
  268. // })
  269. // console.log(res)
  270. // console.log('move result')
  271. jumpPage('./post_result?data=' + JSON.stringify((res as any).post_result))
  272. getMovesCurrent()
  273. getMovesHistory(1)
  274. setBtnDisable(false)
  275. }).catch(e=>{
  276. setBtnDisable(false)
  277. })
  278. }).catch(_ => {
  279. dispatch(setResult({ isSuccess: false }) as any)
  280. setBtnDisable(false)
  281. })
  282. }
  283. function retry(hour, schedule_id) {
  284. Taro.login().then(res0 => {
  285. thirdPartRequest(RequestType.RequestTypeWXRunData).then(res => {
  286. uploadActiveMoves({
  287. code: res0.code,
  288. wechat_run: {
  289. encryptedData: (res as any).encryptedData,
  290. iv: (res as any).iv,
  291. },
  292. start_date: startDate,
  293. date: dayjs().format('YYYYMMDD'),
  294. hour: hour,
  295. timestamp: new Date().getTime(),
  296. schedule_id: schedule_id
  297. }).then(res => {
  298. if ((res as any).error_code == 'WX_STEP_PARSE_FAIL') {
  299. Taro.showToast({
  300. title: '获取失败',
  301. icon: 'none'
  302. })
  303. return
  304. }
  305. jumpPage('./post_result?data=' + JSON.stringify((res as any).post_result))
  306. getMovesCurrent()
  307. getMovesHistory(1)
  308. })
  309. }).catch(_ => {
  310. dispatch(setResult({ isSuccess: false }) as any)
  311. })
  312. })
  313. }
  314. function currentCheckHour() {
  315. let currentHour = new Date().getHours()
  316. const minute = new Date().getMinutes()
  317. if (minute < 10) {
  318. currentHour -= 1;
  319. if (currentHour < 0) {
  320. currentHour = 23
  321. }
  322. }
  323. return currentHour
  324. }
  325. function currentHourChecked() {
  326. const currentHour = currentCheckHour()
  327. var isChecked = false
  328. data.hours.map((item) => {
  329. if (item.hour == currentHour && item.status != 'WFS') {
  330. isChecked = true
  331. }
  332. })
  333. return isChecked;
  334. }
  335. function getStatus() {
  336. const minute = new Date().getMinutes()
  337. if (minute >= 50 || minute < 10) {
  338. var isChecked = currentHourChecked()
  339. if (isChecked) {
  340. return 'upcoming'
  341. }
  342. return 'open'
  343. }
  344. return 'upcoming'
  345. }
  346. function getDuration() {
  347. let now = new Date()
  348. let pre = new Date().getTime()
  349. if (now.getMinutes() < 10) {
  350. pre -= 3600 * 1000
  351. }
  352. let next = pre + 3600 * 1000
  353. if (currentHourChecked()) {
  354. pre = next
  355. next = pre + 3600 * 1000
  356. }
  357. return dayjs(pre).format('HH:00') + '-' + dayjs(next).format('HH:00')
  358. }
  359. function getCurrentTarget() {
  360. let now = new Date()
  361. let pre = new Date().getTime()
  362. if (now.getMinutes() < 10) {
  363. pre -= 3600 * 1000
  364. }
  365. if (currentHourChecked()) {
  366. pre += 3600 * 1000
  367. }
  368. const hour = new Date(pre).getHours()
  369. for (var i = 0; i < data.hours.length; i++) {
  370. if (data.hours[i].hour == hour) {
  371. return `${data.hours[i].target_steps}`//`${data.hours[i].real_steps}/${data.hours[i].target_steps}`
  372. }
  373. }
  374. }
  375. function process() {
  376. return `${data.stat.active_hours} / ${data.stat.waking_hours}`
  377. }
  378. function currentHourPeriod() {
  379. const time = new Date()
  380. let hour = time.getHours()
  381. //假如00:09,则为23小时
  382. if (time.getMinutes() < 10) {
  383. hour--;
  384. }
  385. if (hour < 0) {
  386. hour = 23
  387. }
  388. return hour
  389. }
  390. function isExtra() {
  391. const hour = currentHourPeriod()
  392. var isFind = false
  393. data.hours.map((item) => {
  394. if (item.hour == hour) {
  395. isFind = true
  396. }
  397. })
  398. return !isFind
  399. }
  400. function currentHistory(item) {
  401. var start = item.hour
  402. var isYesterday = start > new Date().getHours()
  403. var end = start + 1
  404. start = (start + '').padStart(2, '0')
  405. end = (end + '').padStart(2, '0')
  406. return <View className="current_history_item">
  407. <View style={{ display: 'flex', flexDirection: 'column' }}>
  408. <Text className="h24" style={{ color: MainColorType.g02, lineHeight: rpxToPx(36) + 'px' }}>{start}:00-{end}:00</Text>
  409. {
  410. item.is_extra && <Text style={{ color: getThemeColor('SLEEP') }}>is Extra</Text>
  411. }
  412. <Text className="h24 bold"><Text className="h34 bold">{item.real_steps}</Text> / {item.target_steps} {t('health.steps')}</Text>
  413. <View style={{
  414. height: rpxToPx(18), flexShrink: 0,
  415. }} />
  416. </View>
  417. {
  418. item.status == 'MISSED' && <Text className="missed">{t('health.missed')}</Text>
  419. }
  420. {
  421. item.status == 'SEDENTARY' && <IconSit width={rpxToPx(44)} color={MainColorType.g02} />
  422. }
  423. {
  424. item.status == 'ACTIVE' && <IconActive width={rpxToPx(44)} color={MainColorType.active} />
  425. }
  426. <View className="border_footer_line" />
  427. </View>
  428. }
  429. function activeHour() {
  430. return <View className="summary">
  431. <View className="summary_header">
  432. {/* <Text className="h34 bold">Last {hours.length} Hour{hours.length == 1 ? '' : 's'}</Text> */}
  433. <Text className="h34 bold">{t('health.past_few_hour')}</Text>
  434. <View className="border_footer_line" />
  435. </View>
  436. {
  437. !hideCurrentRecent && <View className="summary_content2">
  438. {
  439. hours.map((item, index) => {
  440. if (index >= 3 && !moreActive) return <View key={index} />
  441. return <View key={index}>
  442. {
  443. currentHistory(item)
  444. }
  445. </View>
  446. })
  447. }
  448. {
  449. hours.length > 3 && <View className="recent_btn_bg">
  450. <View style={{ height: rpxToPx(68), minWidth: rpxToPx(200), display: 'flex' }}>
  451. <NewButton
  452. onClick={() => {
  453. setMoreActive(!moreActive)
  454. }}
  455. type={NewButtonType.link}
  456. fontSize={rpxToPx(24)}
  457. title={moreActive ? t('health.show_less') : t('health.show_all',{count:hours.length})}
  458. btnStyle={{ minWidth: rpxToPx(170) }}
  459. />
  460. </View>
  461. <View className="border_footer_line" />
  462. </View>
  463. }
  464. </View>
  465. }
  466. {
  467. hours.length == 0 && <View className="no_current">
  468. <Text>{t('health.no_active_hour')}</Text>
  469. <View className="border_footer_line" />
  470. </View>
  471. }
  472. </View>
  473. }
  474. function formatTimeInterval(startTimestamp, endTimestamp) {
  475. // 计算时间间隔(以秒为单位)
  476. const intervalSeconds = Math.floor((endTimestamp - startTimestamp) / 1000);
  477. // 处理负值的情况
  478. if (intervalSeconds < 0) {
  479. throw new Error("End timestamp must be greater than start timestamp");
  480. }
  481. // 如果时间间隔大于1小时
  482. if (intervalSeconds >= 3600) {
  483. const hours = Math.floor(intervalSeconds / 3600);
  484. const minutes = Math.floor((intervalSeconds % 3600) / 60);
  485. const seconds = intervalSeconds % 60;
  486. return `${String(hours).padStart(2, '0')}:${String(minutes).padStart(2, '0')}:${String(seconds).padStart(2, '0')}`;
  487. } else {
  488. // 如果时间间隔小于1小时
  489. const minutes = Math.floor(intervalSeconds / 60);
  490. const seconds = intervalSeconds % 60;
  491. return `${String(minutes).padStart(2, '0')}:${String(seconds).padStart(2, '0')}`;
  492. }
  493. }
  494. function consoleFooter() {
  495. if (isExtra()) {
  496. var now = new Date()
  497. var target = new Date()
  498. target.setHours(data.hours[0].hour)
  499. target.setMinutes(50)
  500. target.setSeconds(0)
  501. target.setMilliseconds(0)
  502. var timestamp = target.getTime()
  503. if (now.getTime() > timestamp) {
  504. timestamp += 24 * 3600 * 1000
  505. }
  506. return <Text className="console_footer">{formatTimeInterval(new Date().getTime(), timestamp)}</Text>
  507. }
  508. if (getStatus() == 'open') {
  509. if (new Date().getMinutes() < 10) {
  510. let time = new Date()
  511. time.setMinutes(10)
  512. time.setSeconds(0)
  513. time.setMilliseconds(0)
  514. // 获取总秒数
  515. const totalSeconds = Math.floor((time.getTime() - new Date().getTime()) / 1000);
  516. // 计算小时、分钟和秒
  517. // const hours = String(Math.floor(totalSeconds / 3600)).padStart(2, '0');
  518. const minutes = String(Math.floor((totalSeconds % 3600) / 60)).padStart(2, '0');
  519. const seconds = String(totalSeconds % 60).padStart(2, '0');
  520. return <Text className="console_footer" style={{ color: getThemeColor('ACTIVE') }}>Ending soon {minutes}:{seconds}</Text>
  521. }
  522. const pre = new Date().getTime()
  523. const next = new Date().getTime() + 3600 * 1000
  524. return <Text className="console_footer">Check In available {dayjs(pre).format('HH:50')}-{dayjs(next).format('HH:10')}</Text>
  525. }
  526. const minute = new Date().getMinutes()
  527. var isChecked = currentHourChecked()
  528. if (isChecked && (minute < 10 || minute >= 50)) {
  529. debugger
  530. let time = new Date()
  531. time.setMinutes(50)
  532. time.setSeconds(0)
  533. time.setMilliseconds(0)
  534. // 获取总秒数
  535. const totalSeconds = Math.floor((time.getTime() + 3600 * 1000 - new Date().getTime()) / 1000);
  536. // 计算小时、分钟和秒
  537. // const hours = String(Math.floor(totalSeconds / 3600)).padStart(2, '0');
  538. const minutes = String(Math.floor((totalSeconds % 3600) / 60)).padStart(2, '0');
  539. const seconds = String(totalSeconds % 60).padStart(2, '0');
  540. // return `${hours}:${minutes}:${seconds}`;
  541. return <Text className="console_footer">Opening soon {minutes}:{seconds}</Text>
  542. }
  543. let time = new Date()
  544. time.setMinutes(50)
  545. time.setSeconds(0)
  546. time.setMilliseconds(0)
  547. // 获取总秒数
  548. const totalSeconds = Math.floor((time.getTime() - new Date().getTime()) / 1000);
  549. // 计算小时、分钟和秒
  550. // const hours = String(Math.floor(totalSeconds / 3600)).padStart(2, '0');
  551. const minutes = String(Math.floor((totalSeconds % 3600) / 60)).padStart(2, '0');
  552. const seconds = String(totalSeconds % 60).padStart(2, '0');
  553. return <Text className="console_footer">Opening soon {minutes}:{seconds}</Text>
  554. }
  555. function hourIndex() {
  556. const { time_slots } = data.period
  557. const str = getDuration()
  558. const hour = parseInt(str.substring(0, 2))
  559. var selItem: any = null
  560. time_slots.map((item) => {
  561. if (item.hour == hour) {
  562. selItem = item;
  563. }
  564. })
  565. return <View className="summary_status_bg">
  566. <StatusIndicator
  567. type={StatusType.normal}
  568. color={selItem.type == 'SLEEP' ? getThemeColor('SLEEP') : getThemeColor('ACTIVE')}
  569. text={`${selItem.type == 'SLEEP' ? t('health.sleeping_hour') : t('health.waking_hour')}${selItem.index}`}
  570. // fontSize={}
  571. // fontColor={}
  572. />
  573. {/* <View className="summary_status_point" style={{ backgroundColor: selItem.type == 'SLEEP' ? getThemeColor('SLEEP') : getThemeColor('ACTIVE') }} /> */}
  574. {/* <Text className="summary_status_text">{selItem.type == 'SLEEP' ? 'SLEEP HOUR ' : 'WAKING HOUR '}{selItem.index}</Text> */}
  575. </View>
  576. }
  577. function becomeTime() {
  578. var hour = new Date().getHours()
  579. var tHour = parseInt(getDuration().substring(0, 2))
  580. if (isExtra()) {
  581. tHour = data.hours[0].hour
  582. }
  583. if (hour > tHour) {
  584. return global.language == 'en'?t('health.check_begin_at',{time:getDuration().substring(0, 2)+':50 tomorrow'}):
  585. t('health.check_begin_at',{time:'明天'+getDuration().substring(0, 2)+':50'})
  586. // return `Check in begins at ${getDuration().substring(0, 2)}:50 tomorrow`
  587. }
  588. return t('health.check_begin_at',{time:getDuration().substring(0, 2)+':50'})
  589. }
  590. function consolePanel() {
  591. // if (!allowRun) {
  592. // return <View className="move_console move_console_auth">
  593. // <Text className="console_duration" style={{ color: '#000', fontSize: rpxToPx(34) }}>你还没有开启步数授权</Text>
  594. // <Text className="console_open_tip">体验Move More需要您开启微信运动授权,点击下方按钮进行授权</Text>
  595. // <View className="console_checkbtn"
  596. // onClick={tapLog}
  597. // style={{ color: '#fff', backgroundColor: getThemeColor('ACTIVE') }}>去授权</View>
  598. // </View>
  599. // }
  600. return <View className="move_console">
  601. <View style={{ flex: 1 }} />
  602. {hourIndex()}
  603. <Text className={isExtra() ? 'h24' : 'h34'} style={{
  604. color: MainColorType.g01,
  605. lineHeight: isExtra() ? rpxToPx(28) + 'px' : rpxToPx(42) + 'px'
  606. }}>{getDuration()}</Text>
  607. <View style={{ height: rpxToPx(12) }} />
  608. <Text className={isExtra() ? 'bold h50' : 'bold h34'} style={{
  609. color: isExtra() ? getThemeColor('SLEEP') : getThemeColor('ACTIVE'),
  610. lineHeight: isExtra() ? rpxToPx(60) + 'px' : rpxToPx(42) + 'px'
  611. }}>{
  612. isExtra() ? 'Sleep well.' : getStatus() == 'open' ? `Think you've hit ${getCurrentTarget()} steps this hour?` : `Hit ${getCurrentTarget()} steps within the hour`
  613. }</Text>
  614. <View style={{ height: rpxToPx(48), }} />
  615. {
  616. isExtra() ? <NewButton type={NewButtonType.gray}
  617. width={rpxToPx(502)}
  618. height={rpxToPx(96)}
  619. fontSize={rpxToPx(26)}
  620. bold
  621. // disable
  622. title={becomeTime()}
  623. /> : getStatus() == 'open' ?
  624. <NewButton type={NewButtonType.fill}
  625. onClick={tapLog}
  626. width={rpxToPx(502)}
  627. height={rpxToPx(96)}
  628. fontSize={rpxToPx(26)}
  629. title="I Think So!"
  630. bold
  631. color={getThemeColor('ACTIVE')}
  632. />
  633. :
  634. <NewButton type={NewButtonType.gray}
  635. onClick={tapLog}
  636. width={rpxToPx(502)}
  637. height={rpxToPx(96)}
  638. fontSize={rpxToPx(26)}
  639. bold
  640. // disable
  641. title={becomeTime()}
  642. />
  643. // <View className="console_checkbtn" onClick={tapLog}>{becomeTime()}</View>
  644. }
  645. {
  646. consoleFooter()
  647. }
  648. <View style={{ height: rpxToPx(48) }} />
  649. </View>
  650. }
  651. function toolBtnText() {
  652. var str = ''
  653. if (data.hours.length - hours.length ==1){
  654. str = t('health.upcoming')
  655. }
  656. else {
  657. str = t('health.upcomings')
  658. }
  659. return `${str} (${data.hours.length - hours.length > 0 ? data.hours.length - hours.length : '0'})`
  660. }
  661. function showMore() {
  662. showActionSheet({
  663. showActionSheetWithOptions: showActionSheetWithOptions,
  664. title: t('health.more_actions'),
  665. itemList: [t('health.change_step_goal')],
  666. success: (res) => {
  667. if (res == 0) {
  668. jumpPage('./move_setting_time')
  669. }
  670. }
  671. });
  672. }
  673. function tools() {
  674. return <View className="tools">
  675. <View style={{ width: rpxToPx(74) }} />
  676. <NewButton type={NewButtonType.link}
  677. title={toolBtnText()}
  678. onClick={() => {
  679. jumpPage(`/_health/pages/move_schedule?hours=${JSON.stringify(data.hours)}`)
  680. }}
  681. >
  682. <Image className="calendar" src={require('@assets/_health/calendar.png')} />
  683. </NewButton>
  684. <NewButton type={NewButtonType.more} onClick={() => {
  685. showMore()
  686. }} />
  687. </View>
  688. }
  689. function summary() {
  690. return <View className="move_summary">
  691. <View className="summary_header">
  692. <Text className="h34 bold">{t('health.summary')}</Text>
  693. <View style={{ flex: 1 }} />
  694. {
  695. data.last_updated && <Text style={{ color: '#b2b2b2', fontSize: 10, fontWeight: 'normal' }}>{t('health.last_updated')} {dayjs(data.last_updated).format('HH:mm')}</Text>
  696. }
  697. <View className="border_footer_line" />
  698. </View>
  699. <View className="summary_content">
  700. <View className="summary_footer">
  701. <View className="summary_footer_item">
  702. <Text className="light_desc">{t('health.sedentary_hours')}</Text>
  703. <Text style={{ fontWeight: 'bold', fontSize: rpxToPx(24) }}><Text style={{ fontSize: rpxToPx(34) }}>{data.stat.sedentary_hours}</Text> / {data.stat.waking_hours}</Text>
  704. </View>
  705. <View className="summary_footer_item">
  706. <Text className="light_desc">{t('health.active_hours')}</Text>
  707. <Text style={{ color: getThemeColor('ACTIVE'), fontWeight: 'bold', fontSize: rpxToPx(24) }}><Text style={{ fontSize: rpxToPx(34) }}>{data.stat.active_hours}</Text> / {data.stat.waking_hours}</Text>
  708. </View>
  709. <View className="summary_footer_item">
  710. <Text className="light_desc">{t('health.missed_hours')}</Text>
  711. <Text style={{ fontWeight: 'bold', fontSize: rpxToPx(24) }}><Text style={{ fontSize: rpxToPx(34) }}>{data.stat.missed_hours}</Text> / {data.stat.waking_hours}</Text>
  712. </View>
  713. </View>
  714. <View className="summary_footer" style={{ marginTop: rpxToPx(48) }}>
  715. <View className="summary_footer_item">
  716. <Text className="light_desc">{t('health.step')}</Text>
  717. <Text style={{ fontWeight: 'bold', fontSize: rpxToPx(24) }}><Text style={{ fontSize: rpxToPx(34) }}>{data.stat.real_steps}</Text> / {data.stat.target_steps}</Text>
  718. </View>
  719. <View className="summary_footer_item">
  720. <Text className="light_desc">{t('health.calories')}</Text>
  721. <Text style={{ fontWeight: 'bold', fontSize: rpxToPx(24) }}><Text style={{ fontSize: rpxToPx(34) }}>{data.stat.real_calories}</Text> / {data.stat.target_calories}{t('health.kcal')}</Text>
  722. </View>
  723. <View className="summary_footer_item">
  724. <Text className="light_desc">{t('health.distance')}</Text>
  725. <Text style={{ fontWeight: 'bold', fontSize: rpxToPx(24) }}><Text style={{ fontSize: rpxToPx(34) }}>{data.stat.real_distance}</Text> / {data.stat.target_distance}{t('health.km')}</Text>
  726. </View>
  727. </View>
  728. </View>
  729. </View>
  730. }
  731. function goDetail(item) {
  732. jumpPage('./move_detail?id=' + item.id)
  733. }
  734. function historyDate(item, index) {
  735. if (index == 0) {
  736. if (global.language == 'zh' && TimeFormatter.isToday(item.timestamp)) {
  737. return '今天'
  738. }
  739. if (global.language == 'zh' && TimeFormatter.isYesterday(item.timestamp)) {
  740. return '昨天'
  741. }
  742. return dayjs(item.timestamp).format('DD')
  743. }
  744. if (dayjs(item.timestamp).format('MM-DD') ==
  745. dayjs(list[index - 1].timestamp).format('MM-DD')) {
  746. return ''
  747. }
  748. if (global.language == 'zh' && TimeFormatter.isToday(item.timestamp)) {
  749. return '今天'
  750. }
  751. if (global.language == 'zh' && TimeFormatter.isYesterday(item.timestamp)) {
  752. return '昨天'
  753. }
  754. return dayjs(item.timestamp).format('DD')
  755. }
  756. function historyMonth(item, index) {
  757. if (index == 0) {
  758. if (global.language == 'zh' && TimeFormatter.isToday(item.timestamp)) {
  759. return ''
  760. }
  761. if (global.language == 'zh' && TimeFormatter.isYesterday(item.timestamp)) {
  762. return ''
  763. }
  764. return dayjs(item.timestamp).format('MMM')
  765. }
  766. if (dayjs(item.timestamp).format('MM-DD') ==
  767. dayjs(list[index - 1].timestamp).format('MM-DD')) {
  768. return ''
  769. }
  770. if (global.language == 'zh' && TimeFormatter.isToday(item.timestamp)) {
  771. return ''
  772. }
  773. if (global.language == 'zh' && TimeFormatter.isYesterday(item.timestamp)) {
  774. return ''
  775. }
  776. return dayjs(item.timestamp).format('MMM')
  777. }
  778. if (!loaded || !loadAuth) return <View />
  779. return <StickyDateList onRefresherRefresh={() => {
  780. setIsPulling(true)
  781. setPage(1)
  782. getMovesHistory(1)
  783. }} isPulling={isPulling}
  784. onScroll={onScroll}
  785. showDate={showDate}
  786. date={date}
  787. loadMore={more}>
  788. <View style={{ display: 'flex', flexDirection: 'column' }}>
  789. <NewHeader type={NewHeaderType.left} title={t('health.move_every_hour')} />
  790. {
  791. consolePanel()
  792. }
  793. {
  794. tools()
  795. }
  796. {
  797. summary()
  798. }
  799. {
  800. activeHour()
  801. }
  802. {
  803. list.length > 0 && <View className="history">
  804. <View className="history_header">
  805. <Text className="h50 bold">{t('health.recents')}</Text>
  806. </View>
  807. {
  808. list.map((item, index) => {
  809. return <View key={index} id={`history-${index}`} className="history_item" onClick={() => goDetail(item)}>
  810. {/* <Text className="history_item_date">{(item.date + '').substring(6, 8)}</Text> */}
  811. <View className="cell_date" >
  812. <View className="h42 bold" style={{ lineHeight: rpxToPx(60) + 'px' }}>{historyDate(item,index)}</View>
  813. <View className="h24 bold" style={{ marginLeft: rpxToPx(6), marginTop: rpxToPx(13), lineHeight: rpxToPx(47) + 'px' }}>{historyMonth(item,index)}</View>
  814. </View>
  815. <View className="history_item_detail_bg">
  816. <IconActive width={rpxToPx(32)} color={MainColorType.active} />
  817. <Text style={{ fontSize: rpxToPx(26), marginLeft: rpxToPx(16) }}> {item.description}</Text>
  818. {/* <Text style={{ fontSize: rpxToPx(26), marginLeft: rpxToPx(16) }}> Hours Active {item.active_hours} · Total Steps {item.stat.real_steps}</Text> */}
  819. </View>
  820. {/* <View className="border_footer_line" /> */}
  821. </View>
  822. })
  823. }
  824. </View>
  825. }
  826. <View style={{backgroundColor:'#fff',height:rpxToPx(40),flexShrink:0}}/>
  827. <ListFooter noMore={(list.length > 0) && (total == list.length)} />
  828. </View>
  829. </StickyDateList>
  830. }