Schedule.tsx 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623
  1. import { View, Text } from "@tarojs/components";
  2. import trackTimeService, { machine } from "@/store/trackTimeMachine"
  3. import { useEffect, useState } from "react";
  4. import Taro from "@tarojs/taro";
  5. import { TimeFormatter } from "@/utils/time_format";
  6. // import "taro-ui/dist/style/components/float-layout.scss";
  7. import { delRecord } from "@/services/trackTimeDuration";
  8. import Modal from "@/components/layout/Modal";
  9. import Rings, { BgRing, CurrentDot, RealRing, RingCommon } from './Rings';
  10. import { getBgRing, getCommon, getDot, getReal, getTarget } from "../hooks/RingData";
  11. import Timeline from "@/components/view/Timeline";
  12. export default function Component(props: { type?: string, data?: any, delSuccess?: Function }) {
  13. const [checkData, setCheckData] = useState(null)
  14. const [key, setKey] = useState('');
  15. const [value, setValue] = useState('');
  16. const [isOpen, setIsOpen] = useState(false);
  17. const [isLatest, setIsLatest] = useState(props.type == 'latest');
  18. const [timerId, setTimerId] = useState(null)
  19. const [counter, setCounter] = useState(0)
  20. const canvasId = props.type == 'latest' ? 'latest' : props.type == 'record' ? props.data.id : 'current'
  21. useEffect(() => {
  22. getStateDetail();
  23. }, [machine.context.currentStatus])
  24. useEffect(() => {
  25. if (machine.context.checkData) {
  26. setCheckData(machine.context.checkData as any);
  27. }
  28. getStateDetail();
  29. }, [machine.context.checkData]);
  30. useEffect(() => {
  31. trackTimeService.onTransition(state => {
  32. getStateDetail();
  33. });
  34. }, []);
  35. function getStateDetail() {
  36. if (props.type == 'latest') {
  37. if (machine.context.checkData) {
  38. setKey((machine.context.checkData as any).latest_record.scenario);
  39. setValue((machine.context.checkData as any).latest_record.status);
  40. }
  41. return
  42. }
  43. var state = trackTimeService.getSnapshot().value
  44. if ((state as any).FAST_SLEEP) {
  45. setKey('FAST_SLEEP');
  46. setValue((state as any).FAST_SLEEP);
  47. }
  48. if ((state as any).FAST) {
  49. setKey('FAST');
  50. setValue((state as any).FAST);
  51. }
  52. if ((state as any).SLEEP) {
  53. setKey('SLEEP');
  54. setValue((state as any).SLEEP);
  55. }
  56. }
  57. function editSchedule() {
  58. Taro.navigateTo({
  59. url: '/pages/clock/SetSchedule'
  60. })
  61. }
  62. function showStage(e) {
  63. setIsLatest(false);
  64. setIsOpen(true)
  65. debugger
  66. e.stopPropagation()
  67. }
  68. function showLatest(e) {
  69. // startTimer();
  70. setIsLatest(true)
  71. setIsOpen(true)
  72. debugger
  73. e.stopPropagation()
  74. }
  75. function getTime(t1: number, t2: number) {
  76. return TimeFormatter.calculateTimeDifference(t1, t2)
  77. }
  78. function getStepATime(obj) {
  79. if (obj.status == 'COMPLETED' && obj.sleep.status == 'NOT_STARTED') {
  80. return '未知'
  81. }
  82. return obj.status == 'ONGOING1' ?
  83. getTime(obj.fast.real_start_time, (new Date()).getTime()) :
  84. obj.sleep.real_start_time ? getTime(obj.sleep.real_start_time, obj.fast.real_start_time ? obj.fast.real_start_time : obj.fast.target_start_time) :
  85. getTime(obj.sleep.target_start_time, obj.fast.real_start_time ? obj.fast.real_start_time : obj.fast.target_start_time)
  86. }
  87. function getStepBTime(obj) {
  88. if (obj.status == 'ONGOING1') return 'Next up'
  89. if (obj.status == 'ONGOING2') return getTime(obj.sleep.real_start_time, (new Date()).getTime())
  90. if (obj.status == 'WAIT_FOR_START') return getTime(obj.sleep.target_end_time, obj.sleep.target_start_time)
  91. if (obj.sleep.status == 'NOT_COMPLETED' || obj.sleep.status == 'NOT_STARTED') return '未知'
  92. return getTime(obj.sleep.real_end_time, obj.sleep.real_start_time)
  93. }
  94. function getStepCTime(obj) {
  95. if (obj.status == 'ONGOING1') return 'Final stage'
  96. if (obj.status == 'ONGOING2') return 'Next up'
  97. if (obj.status == 'ONGOING3') return getTime(obj.sleep.real_end_time, (new Date()).getTime())
  98. if (obj.status == 'WAIT_FOR_START') return getTime(obj.fast.target_end_time, obj.sleep.target_end_time)
  99. if (obj.sleep.status == 'NOT_COMPLETED' || obj.sleep.status == 'NOT_STARTED') return '未知'
  100. return getTime(obj.fast.real_end_time, obj.sleep.real_end_time)
  101. }
  102. function layoutContent() {
  103. debugger
  104. //当前断食阶段
  105. var obj = isLatest ? (checkData as any).latest_record : (checkData as any).current_record
  106. if (props.type == 'record') {
  107. obj = props.data
  108. }
  109. return <View style={{ flexDirection: 'column', display: 'flex', color: '#000', backgroundColor: '#fff', paddingTop: 50, paddingBottom: 50, position: 'relative' }}>
  110. <Text style={{ position: 'absolute', top: 5, right: 20 }} onClick={() => setIsOpen(false)}>关闭</Text>
  111. {
  112. obj.status == 'WAIT_FOR_START' ? <Text>断食阶段目标</Text> :
  113. obj.status == 'COMPLETED' ? <Text>断食阶段</Text> :
  114. <Text>当前断食阶段</Text>
  115. }
  116. <View style={{ flexDirection: 'row', display: 'flex' }}>
  117. <Text>阶段A</Text>
  118. <Text style={{ color: obj.status == 'ONGOING1' ? '#AAFF00' : '', marginLeft: 20, marginRight: 20 }}> 睡前断食</Text>
  119. <Text style={{ color: obj.status == 'ONGOING1' ? '#AAFF00' : '' }}> {
  120. getStepATime(obj)
  121. }</Text>
  122. </View>
  123. <View style={{ flexDirection: 'row', display: 'flex' }}>
  124. <Text>阶段B</Text>
  125. <Text style={{ color: obj.status == 'ONGOING2' ? '#AAFF00' : '', marginLeft: 20, marginRight: 20 }}> 睡眠中断食</Text>
  126. <Text style={{ color: obj.status == 'ONGOING2' ? '#AAFF00' : '' }}>
  127. {
  128. getStepBTime(obj)
  129. }
  130. </Text>
  131. </View>
  132. <View style={{ flexDirection: 'row', display: 'flex' }}>
  133. <Text>阶段C</Text>
  134. <Text style={{ color: obj.status == 'ONGOING3' ? '#AAFF00' : '', marginLeft: 20, marginRight: 20 }}> 起床后断食</Text>
  135. <Text style={{ color: obj.status == 'ONGOING3' ? '#AAFF00' : '' }}>
  136. {
  137. getStepCTime(obj)
  138. }
  139. </Text>
  140. </View>
  141. </View>
  142. }
  143. //🚫❌⭕️✅
  144. function statusString(isFast: boolean, isStart: boolean, data: any) {
  145. if (props.type == 'latest' || props.type == 'record') {
  146. if (isFast) {
  147. if (data.fast.status == 'COMPLETED') {
  148. return '✅'
  149. }
  150. }
  151. else {
  152. if (data.sleep.status == 'COMPLETED') {
  153. return '✅'
  154. }
  155. else if (data.sleep.status == 'NOT_STARTED') {
  156. return '🚫'
  157. }
  158. else if (data.sleep.status == 'NOT_COMPLETED') {
  159. return isStart ? '✅' : '🚫'
  160. }
  161. }
  162. }
  163. if (value == 'WAIT_FOR_START') {
  164. return '⭕️'
  165. }
  166. else if (value == 'ONGOING') {
  167. if (isFast && isStart) {
  168. return '✅'
  169. }
  170. else if (!isFast && isStart) {
  171. return '✅'
  172. }
  173. }
  174. else if (value == 'ONGOING1') {
  175. if (isFast && isStart) {
  176. return '✅'
  177. }
  178. }
  179. else if (value == 'ONGOING2') {
  180. if (isStart) {
  181. return '✅'
  182. }
  183. }
  184. else if (value == 'ONGOING3') {
  185. if (isFast && !isStart) {
  186. return '⭕️'
  187. }
  188. else {
  189. return '✅'
  190. }
  191. }
  192. return '⭕️'
  193. }
  194. function getStatus(isFast: boolean, isStart: boolean, data: any) {
  195. if (props.type == 'latest' || props.type == 'record') {
  196. if (isFast) {
  197. if (data.fast.status == 'COMPLETED') {
  198. return 'done'
  199. }
  200. }
  201. else {
  202. if (data.sleep.status == 'COMPLETED') {
  203. return 'done'
  204. }
  205. else if (data.sleep.status == 'NOT_STARTED') {
  206. return 'un_done'
  207. }
  208. else if (data.sleep.status == 'NOT_COMPLETED') {
  209. return isStart ? 'done' : 'un_done'
  210. }
  211. }
  212. }
  213. if (value == 'WAIT_FOR_START') {
  214. return 'padding'
  215. }
  216. else if (value == 'ONGOING') {
  217. if (isFast && isStart) {
  218. return 'done'
  219. }
  220. else if (!isFast && isStart) {
  221. return 'done'
  222. }
  223. }
  224. else if (value == 'ONGOING1') {
  225. if (isFast && isStart) {
  226. return 'done'
  227. }
  228. }
  229. else if (value == 'ONGOING2') {
  230. if (isStart) {
  231. return 'done'
  232. }
  233. }
  234. else if (value == 'ONGOING3') {
  235. if (isFast && !isStart) {
  236. return 'padding'
  237. }
  238. else {
  239. return 'done'
  240. }
  241. }
  242. return 'padding'
  243. }
  244. function scheduleItems(data) {
  245. if (!data) {
  246. return <View></View>
  247. }
  248. var obj = props.type == 'latest' ? (data as any).latest_record : (data as any).current_record;
  249. if (props.type == 'record') {
  250. obj = data//(data as any).latest_record
  251. }
  252. var timelineItems: any = [];
  253. if (obj.fast) {
  254. timelineItems.push(
  255. {
  256. status: getStatus(true, true, obj),
  257. title: '开始断食',
  258. content: formateTime(obj.fast, false),
  259. }
  260. )
  261. }
  262. if (obj.sleep) {
  263. timelineItems.push(
  264. {
  265. status: getStatus(false, true, obj),
  266. title: '开始睡眠',
  267. content: formateTime(obj.sleep, false),
  268. }
  269. )
  270. }
  271. if (obj.sleep) {
  272. timelineItems.push(
  273. {
  274. status: getStatus(false, false, obj),
  275. title: '结束睡眠',
  276. content: formateTime(obj.sleep, true),
  277. }
  278. )
  279. }
  280. if (obj.fast) {
  281. timelineItems.push(
  282. {
  283. status: getStatus(true, false, obj),
  284. title: '结束断食',
  285. content: formateTime(obj.fast, true),
  286. }
  287. )
  288. }
  289. return <Timeline items={timelineItems} />
  290. // return <View>
  291. // {
  292. // obj && <View style={{ flexDirection: 'column', display: 'flex' }}>
  293. // {obj.fast && <Text>{statusString(true, true, obj)}开始断食:{formateTime(obj.fast, false)}</Text>}
  294. // {obj.sleep && <Text>{statusString(false, true, obj)}开始睡眠:{formateTime(obj.sleep, false)}</Text>}
  295. // {obj.sleep && <Text>{statusString(false, false, obj)}结束睡眠:{formateTime(obj.sleep, true)}</Text>}
  296. // {obj.fast && <Text>{statusString(true, false, obj)}结束断食:{formateTime(obj.fast, true)}</Text>}
  297. // </View>
  298. // }
  299. // </View>
  300. }
  301. function formateTime(obj: any, isEnd: boolean) {
  302. if (isEnd) {
  303. if (obj.real_end_time) {
  304. return TimeFormatter.formatTimestamp(obj.real_end_time)
  305. }
  306. else {
  307. return TimeFormatter.formatTimestamp(obj.target_end_time)
  308. }
  309. }
  310. else {
  311. if (obj.real_start_time) {
  312. return TimeFormatter.formatTimestamp(obj.real_start_time)
  313. }
  314. else {
  315. return TimeFormatter.formatTimestamp(obj.target_start_time)
  316. }
  317. }
  318. }
  319. function more(e) {
  320. Taro.showActionSheet({
  321. itemList: ['删除', '分享']
  322. })
  323. .then(res => {
  324. console.log(res.tapIndex)
  325. switch (res.tapIndex) {
  326. case 0:
  327. {
  328. del()
  329. }
  330. break;
  331. }
  332. })
  333. .catch(err => {
  334. console.log(err.errMsg)
  335. })
  336. e.stopPropagation()
  337. }
  338. function del() {
  339. Taro.showModal({
  340. title: '删除记录',
  341. content: '确定删除该记录吗?',
  342. success: res => {
  343. if (res.confirm) {
  344. delRecord(props.data.id
  345. ).then(res => {
  346. Taro.showToast({
  347. title: '删除成功'
  348. })
  349. props.delSuccess && props.delSuccess(props.data)
  350. // Taro.navigateBack()
  351. })
  352. }
  353. }
  354. })
  355. }
  356. function all() {
  357. if (props.type == 'latest') {
  358. Taro.navigateTo({
  359. url: '/pages/common/RecordsHistory?type=time&title=Time'
  360. })
  361. }
  362. }
  363. // const common: RingCommon = {
  364. // useCase: 'Record',
  365. // radius: 50,
  366. // lineWidth: 8,
  367. // isFast: true,
  368. // status: getRecord()?getRecord().status:'WAIT_FOR_START'
  369. // }
  370. // const common2: RingCommon = {
  371. // useCase: 'Record',
  372. // radius: 40,
  373. // lineWidth: 8,
  374. // isFast: true,
  375. // status: getRecord()?getRecord().status:'WAIT_FOR_START'
  376. // }
  377. // const bgRing: BgRing = {
  378. // color: '#262626'
  379. // }
  380. // const realRing: RealRing = {
  381. // color: '#AAFF00',
  382. // startArc: 0,
  383. // durationArc: 0
  384. // }
  385. // const realRing2: RealRing = {
  386. // color: '#00FFFF',
  387. // startArc: 0,
  388. // durationArc: 0
  389. // }
  390. // const currentDot: CurrentDot = {
  391. // color: '#AAFF00',
  392. // lineWidth: 8,
  393. // borderColor: 'black'
  394. // }
  395. // const currentDot2: CurrentDot = {
  396. // color: '#00FFFF',
  397. // lineWidth: 8,
  398. // borderColor: 'black'
  399. // }
  400. // function startArc(time: number) {
  401. // var date = new Date(time);
  402. // var hour = date.getHours();
  403. // var minute = date.getMinutes();
  404. // var second = date.getSeconds();
  405. // return (hour * 3600 + minute * 60 + second) / (24 * 3600) * 2 * Math.PI - Math.PI / 2.0;
  406. // }
  407. function durationArc(start_time: number, end_time: number) {
  408. var duration = (end_time - start_time) / 1000;
  409. return duration / (24 * 3600) * 2 * Math.PI;
  410. }
  411. function getRecord() {
  412. var detail = props.type == 'record' ? props.data : checkData
  413. if (detail) {
  414. var record = detail;
  415. if (props.type == 'latest') {
  416. record = detail.latest_record
  417. }
  418. else if (props.type != 'record') {
  419. record = detail.current_record
  420. }
  421. return record;
  422. }
  423. return
  424. }
  425. function bigRing() {
  426. var record = getRecord()
  427. if (!record) return null
  428. var common = getCommon(null, true)
  429. var bgRing = getBgRing()
  430. if (props.type == 'record' || props.type == 'latest') {
  431. var realRing = getReal(record, true, true)
  432. return <Rings common={common} bgRing={bgRing} canvasId={canvasId} realRing={realRing} />
  433. }
  434. else {
  435. var currentDot1 = getDot(record, true)
  436. var targetBigRing1 = getTarget(record, true)
  437. if (record.status == 'ONGOING') {
  438. var realRing1 = getReal(record, true, false)
  439. return <Rings common={common} bgRing={bgRing} currentDot={currentDot1} realRing={realRing1} targetRing={targetBigRing1} canvasId={canvasId} />
  440. }
  441. if (record.status == 'WAIT_FOR_START') {
  442. return <Rings common={common} bgRing={bgRing} currentDot={currentDot1} canvasId={canvasId} />
  443. }
  444. var realRing1 = getReal(record, true, false)
  445. return <Rings common={common} bgRing={bgRing} realRing={realRing1} currentDot={currentDot1} targetRing={targetBigRing1} canvasId={canvasId} />
  446. }
  447. }
  448. function smallRing() {
  449. if (!checkData) return null
  450. var record = getRecord()
  451. if (record.scenario == 'FAST_SLEEP') {
  452. var common = getCommon(null, false)
  453. var bgRing = getBgRing()
  454. var realRing = getReal(record, false, false)
  455. if (props.type == 'record' || props.type == 'latest') {
  456. if (record.sleep.status == 'WAIT_FOR_END') {
  457. realRing.durationArc = durationArc(record.sleep.target_start_time, (new Date()).getTime())
  458. return <Rings common={common} bgRing={bgRing} canvasId={canvasId + 'small'} realRing={realRing} />
  459. }
  460. else if (record.sleep.status == 'NOT_COMPLETED') {
  461. realRing.durationArc = 0.01
  462. return <Rings common={common} bgRing={bgRing} canvasId={canvasId + 'small'} realRing={realRing} />
  463. }
  464. else if (record.sleep.status == 'COMPLETED') {
  465. realRing = getReal(record, false, true)
  466. return <Rings common={common} bgRing={bgRing} canvasId={canvasId + 'small'} realRing={realRing} />
  467. }
  468. return <Rings common={common} bgRing={bgRing} canvasId={canvasId + 'small'} />
  469. }
  470. else {
  471. var currentDot = getDot(record, false)
  472. var targetRing = getTarget(record, false)
  473. if (record.status == 'ONGOING2') {
  474. var realRing = getReal(record, false, false)
  475. return <Rings common={common} bgRing={bgRing} realRing={realRing} currentDot={currentDot} targetRing={targetRing} canvasId={canvasId + 'small'} />
  476. }
  477. if (record.status == 'ONGOING3') {
  478. currentDot.color = 'rgba(0, 255, 255, 0.5)'
  479. }
  480. return <Rings common={common} bgRing={bgRing} currentDot={currentDot} canvasId={canvasId + 'small'} />
  481. }
  482. // if (record.sleep.status == 'WAIT_FOR_END') {
  483. // realRing2.startArc = startArc(record.sleep.target_start_time)
  484. // realRing2.durationArc = durationArc(record.sleep.target_start_time, (new Date()).getTime())
  485. // return <Rings common={common2} bgRing={bgRing} canvasId={canvasId + 'small'} realRing={realRing2} />
  486. // }
  487. // else if (record.sleep.status == 'NOT_COMPLETED') {
  488. // realRing2.startArc = startArc(record.sleep.target_start_time)
  489. // realRing2.durationArc = 0.01
  490. // return <Rings common={common2} bgRing={bgRing} canvasId={canvasId + 'small'} realRing={realRing2} />
  491. // }
  492. // else if (record.status == 'WAIT_FOR_START') {
  493. // common2.useCase = 'Clock'
  494. // return <Rings common={common2} bgRing={bgRing} currentDot={currentDot2} canvasId={canvasId + 'small'} />
  495. // }
  496. // return <Rings common={common2} bgRing={bgRing} canvasId={canvasId + 'small'} />
  497. }
  498. return null
  499. }
  500. return <View style={{ flexDirection: 'column', display: 'flex', alignItems: 'center', position: 'relative' }} onClick={all}>
  501. {
  502. (props.type == 'latest' || props.type == 'record') &&
  503. <View style={{ position: 'relative', zIndex: 1 }}>
  504. {
  505. bigRing()
  506. }
  507. <View style={{ display: 'flex', position: 'absolute', left: 0, right: 0, top: 0, bottom: 0, alignItems: 'center', justifyContent: 'center' }}>
  508. {
  509. smallRing()
  510. }
  511. </View>
  512. </View>
  513. }
  514. {
  515. props.type == 'latest' ? <Text style={{ color: 'red' }}>Latest</Text> :
  516. <Text>{value == 'WAIT_FOR_START' ? 'Schedule' : 'Log in Progress'}</Text>
  517. }
  518. {
  519. scheduleItems(props.type == 'record' ? props.data : checkData)
  520. }
  521. {
  522. (props.type != 'record' && value == 'WAIT_FOR_START') && <Text onClick={editSchedule}>调整日程</Text>
  523. }
  524. {
  525. ((props.type == 'record' && props.data.scenario == 'FAST_SLEEP') || (props.type == 'latest' && key == 'FAST_SLEEP')) && <Text onClick={showLatest}>Durations by stage</Text>
  526. }
  527. {
  528. props.type != 'record' && props.type != 'latest' && key == 'FAST_SLEEP' && (value == 'WAIT_FOR_START' ? <Text onClick={showStage}>Duration goals by stage</Text> : <Text onClick={showStage}>Current stage</Text>)
  529. }
  530. {
  531. key == 'FAST_SLEEP' && isOpen && props.type != 'record' && checkData && <Modal children={layoutContent()} dismiss={() => setIsOpen(false)} confirm={() => { }} />
  532. }
  533. {
  534. key == 'FAST_SLEEP' && isOpen && props.type == 'record' && props.data.scenario == 'FAST_SLEEP' && <Modal children={layoutContent()} dismiss={() => setIsOpen(false)} confirm={() => { }} />
  535. }
  536. {/* {
  537. key == 'FAST_SLEEP' && <AtFloatLayout
  538. isOpened={isOpen}
  539. onClose={() => {
  540. // stopTimer()
  541. setIsOpen(false)
  542. }}
  543. title="这是个标题">
  544. {
  545. props.type != 'record'&&checkData && layoutContent()
  546. }
  547. {
  548. props.type == 'record' && props.data.scenario=='FAST_SLEEP' && layoutContent()
  549. }
  550. </AtFloatLayout>
  551. } */}
  552. {
  553. props.type == 'record' && <Text style={{ position: 'absolute', right: 20, top: 20 }} onClick={more}>More</Text>
  554. }
  555. <Text style={{ opacity: 0 }}>{counter}</Text>
  556. </View >
  557. }