schedules.tsx 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568
  1. import { createSchedule, delSchedule, getLabelsEvent, getSchedules } from "@/services/health";
  2. import { View, Text, Image } from "@tarojs/components";
  3. import { useRouter } from "@tarojs/taro";
  4. import { useEffect, useState } from "react";
  5. import { useSelector } from "react-redux";
  6. import './schedules.scss';
  7. import { rpxToPx } from "@/utils/tools";
  8. import { getThemeColor } from "@/features/health/hooks/health_hooks";
  9. import { jumpPage } from "@/features/trackTimeDuration/hooks/Common";
  10. import Modal from "@/components/layout/Modal.weapp";
  11. import TimePicker from "@/features/common/TimePicker";
  12. import showAlert from "@/components/basic/Alert";
  13. import { IconAdd, IconError, IconNotificationOff, IconSuccess } from "@/components/basic/Icons";
  14. import AddLabel from "../components/add_label";
  15. import NewButton, { NewButtonType } from "../base/new_button";
  16. import showActionSheet from "@/components/basic/ActionSheet";
  17. import Card from "../components/card";
  18. import StatusIndicator, { StatusType } from "../base/status_indicator";
  19. import { MainColorType } from "@/context/themes/color";
  20. import { AtSwipeAction } from "taro-ui"
  21. import ScheduleItem from "../components/schedule_item";
  22. import { useTranslation } from "react-i18next";
  23. import NewHeader, { NewHeaderType } from "../components/new_header";
  24. import ListFooter from "../components/list_footer";
  25. let useRoute;
  26. let useNavigation;
  27. let scenario = '';
  28. if (process.env.TARO_ENV == 'rn') {
  29. useRoute = require("@react-navigation/native").useRoute
  30. useNavigation = require("@react-navigation/native").useNavigation
  31. }
  32. export default function Schedules() {
  33. let navigation, showActionSheetWithOptions;
  34. let router
  35. if (useNavigation) {
  36. navigation = useNavigation()
  37. }
  38. if (process.env.TARO_ENV == 'rn') {
  39. router = useRoute()
  40. }
  41. else {
  42. router = useRouter()
  43. }
  44. const [list, setList] = useState<any>([])
  45. const [labels, setLabels] = useState<any>([])
  46. const [labels2, setLabels2] = useState<any>([])
  47. const [showAutoSave, setShowAutoSave] = useState(false)
  48. const [errors, setErrors] = useState<any>(router.params.errors ? JSON.parse(router.params.errors) : [])
  49. const [showTimePicker, setShowTimePicker] = useState(false)
  50. const health = useSelector((state: any) => state.health);
  51. const [selItem, setSelItem] = useState<any>(null)
  52. const [selIndex, setSelIndex] = useState(-1)
  53. const [showModal, setShowModal] = useState(false)
  54. const [highlight, setHighlight] = useState(true)
  55. const [tempArray, setTempArray] = useState<any>([])
  56. const [btnEnable, setBtnEnable] = useState(true)
  57. const [selMode, setSleMode] = useState(router.params.mode);
  58. const [isEat, setIsEat] = useState(false)
  59. const [loaded, setLoaded] = useState(false)
  60. const { t } = useTranslation()
  61. useEffect(() => {
  62. if (selMode == '' && router.params.schedules) {
  63. setList(JSON.parse(router.params.schedules))
  64. if (router.params.errors) {
  65. setErrors(JSON.parse(router.params.errors))
  66. }
  67. }
  68. else {
  69. schedules()
  70. if (selMode != '') {
  71. global.refreshSchedules2 = () => {
  72. schedules()
  73. }
  74. }
  75. }
  76. }, [])
  77. global.tempRefresh = () => {
  78. schedules()
  79. }
  80. function schedules() {
  81. let windows = ''
  82. switch (selMode) {
  83. case 'FAST':
  84. if (router.params.isFastSleep){
  85. windows = 'FAST,SLEEP';
  86. }
  87. else {
  88. windows = 'FAST,EAT';
  89. }
  90. break
  91. case 'EAT':
  92. windows = 'EAT,FAST';
  93. break
  94. case 'SLEEP':
  95. windows = 'SLEEP,ACTIVE';
  96. break
  97. case 'ACTIVE':
  98. windows = 'ACTIVE,SLEEP';
  99. break
  100. case 'DAY':
  101. windows = 'DAY,NIGHT';
  102. break
  103. case 'NIGHT':
  104. windows = 'NIGHT,DAY';
  105. break
  106. }
  107. getSchedules({ window: windows }).then(res => {
  108. setList((res as any).data)
  109. setErrors([])
  110. setLoaded(true)
  111. setTimeout(() => {
  112. setHighlight(false)
  113. }, 2000)
  114. }).catch(e => {
  115. })
  116. getLabelsEvent({ window: 'EAT' }).then(res => {
  117. setLabels((res as any).labels)
  118. })
  119. getLabelsEvent({ window: 'ACTIVE' }).then(res => {
  120. setLabels2((res as any).labels)
  121. })
  122. }
  123. function getTitle() {
  124. switch (selMode) {
  125. case 'FAST':
  126. if (router.params.isFastSleep){
  127. return '断食和睡眠';
  128. }
  129. return '断食和进食';
  130. case 'EAT':
  131. return '进食和断食';
  132. case 'SLEEP':
  133. return '睡眠和活动';
  134. case 'ACTIVE':
  135. return '活动和睡眠';
  136. case 'DAY':
  137. return '白天和夜晚';
  138. case 'NIGHT':
  139. return '夜晚和白天';
  140. }
  141. return '全部'
  142. }
  143. function modalContent() {
  144. const strTime = selItem.time
  145. var title = selItem.title
  146. var color = getThemeColor(selItem.window)
  147. return <TimePicker time={strTime}
  148. color={color}
  149. title={title}
  150. confirm={(e) => {
  151. selItem.time = e
  152. setSelItem(selItem)
  153. setShowTimePicker(false)
  154. var array = JSON.parse(JSON.stringify(list))
  155. array[selIndex].time = e
  156. array[selIndex].op_ms = new Date().getTime()
  157. setList(array)
  158. checkData(array)
  159. // confirmPickerTime(e)
  160. }}
  161. cancel={() => {
  162. setShowTimePicker(false)
  163. }} />
  164. }
  165. function checkData(array) {
  166. setTempArray(array)
  167. createSchedule({
  168. schedules: array,
  169. // only_check: true
  170. }).then(res => {
  171. checkResultData(res)
  172. })
  173. }
  174. function checkResultData(res) {
  175. if ((res as any).result) {
  176. setShowAutoSave(true)
  177. setErrors([])
  178. setList((res as any).schedules)
  179. global.refreshWindow()
  180. if (global.refreshSchedules) {
  181. global.refreshSchedules()
  182. }
  183. if (global.refreshSchedules2) {
  184. global.refreshSchedules2()
  185. }
  186. }
  187. else {
  188. setShowAutoSave(false)
  189. setList((res as any).schedules)
  190. setErrors((res as any).error_messages ? (res as any).error_messages : [])
  191. var array = (res as any).conflict_windows;
  192. var showMore = false;
  193. if (array.length > 2) {
  194. showMore = true;
  195. }
  196. else if (array.length == 1) {
  197. showMore = false;
  198. }
  199. else {
  200. // 判断是否同时存在 FAST 和 EAT
  201. const containsFastAndEat = array.includes('FAST') && array.includes('EAT');
  202. // 判断是否同时存在 SLEEP 和 ACTIVE
  203. const containsSleepAndActive = array.includes('SLEEP') && array.includes('ACTIVE');
  204. // 最终结果
  205. const result = containsFastAndEat || containsSleepAndActive;
  206. showMore = !result;
  207. }
  208. if (selMode != '' && showMore) {
  209. showAlert({
  210. title: t('health.schedule_conflict'),
  211. content: t('health.conflict_desc'),
  212. showCancel: false,
  213. confirmText: t('health.check_conflict'),
  214. confirm: () => {
  215. setSleMode('')
  216. // jumpPage(`./schedules?mode=&schedules=${JSON.stringify((res as any).schedules)}&errors=${JSON.stringify((res as any).error_messages)}`)
  217. }
  218. })
  219. }
  220. else {
  221. // setList((res as any).schedules)
  222. // setErrors((res as any).error_messages ? (res as any).error_messages : [])
  223. }
  224. }
  225. }
  226. function add(isEat) {
  227. var isMax = false
  228. if (isEat) {
  229. setIsEat(true)
  230. const countFastWindows = list.filter(item => item.window === 'EAT').length;
  231. if (countFastWindows >= 4) {
  232. isMax = true
  233. }
  234. }
  235. else {
  236. setIsEat(false)
  237. const countFastWindows = list.filter(item => item.window === 'ACTIVE').length;
  238. if (countFastWindows >= 4) {
  239. isMax = true
  240. }
  241. }
  242. if (isMax) {
  243. showAlert({
  244. title: '会员',
  245. content: '会员desc',
  246. showCancel: true,
  247. confirm: () => {
  248. jumpPage('/pages/store/product_list', 'ProductList', navigation)
  249. }
  250. })
  251. return;
  252. }
  253. setShowModal(true)
  254. }
  255. function getAddColor() {
  256. if (selMode == 'FAST' || selMode == 'EAT') {
  257. return getThemeColor('EAT')
  258. }
  259. return getThemeColor('ACTIVE')
  260. }
  261. function tapEdit() {
  262. }
  263. function itemStyle(obj) {
  264. if (obj.is_conflict) {
  265. return {
  266. // backgroundColor: '#FF00001A',
  267. // color: '#FF0000'
  268. backgroundColor: '#B2B2B21A',
  269. color: '#000',
  270. borderWidth: 2,
  271. borderColor: '#FF0000',
  272. borderStyle: 'solid'
  273. }
  274. }
  275. if (errors.length > 0) {
  276. return {
  277. backgroundColor: '#B2B2B21A',
  278. color: '#000'
  279. }
  280. }
  281. return {
  282. backgroundColor: obj.window == selMode ? getThemeColor(selMode) + '1A' : '#B2B2B21A',
  283. color: obj.window == selMode ? getThemeColor(selMode) : '#000'
  284. }
  285. }
  286. function showAll() {
  287. if (!btnEnable) {
  288. return
  289. }
  290. if (errors.length == 0) {
  291. jumpPage('/_health/pages/schedules?mode=')
  292. }
  293. else {
  294. setBtnEnable(false)
  295. createSchedule({
  296. schedules: tempArray,
  297. return_all: true
  298. // only_check: true
  299. }).then(res => {
  300. setBtnEnable(true)
  301. jumpPage('/_health/pages/schedules?mode=&schedules=' + JSON.stringify((res as any).schedules) + '&errors=' + JSON.stringify((res as any).error_messages))
  302. })
  303. }
  304. }
  305. function more() {
  306. var items: any = ['设置提醒', '个性化名称'];
  307. if (errors.length == 0) {
  308. items.push(t('health.reset_schedule'))
  309. }
  310. showActionSheet({
  311. showActionSheetWithOptions: showActionSheetWithOptions,
  312. title: 'Oprate Title',
  313. itemList: items,
  314. success: (res) => {
  315. if (res == 0 || res == 1) {
  316. var url = `./schedules_edit?type=${res == 0 ? 'reminder' : 'name'}&mode=${selMode}`
  317. console.log(url)
  318. jumpPage(url)
  319. }
  320. else if (res == 2) {
  321. jumpPage('/_health/pages/guide_begin')
  322. }
  323. }
  324. });
  325. }
  326. function getOpPage() {
  327. if (selMode == '') return 'SCHEDULE_ALL'
  328. switch (selMode) {
  329. case 'FAST':
  330. return 'SCHEDULE_FAST_EAT';
  331. case 'EAT':
  332. return 'SCHEDULE_EAT_FAST';
  333. case 'SLEEP':
  334. return 'SCHEDULE_SLEEP_ACTIVE';
  335. case 'ACTIVE':
  336. return 'SCHEDULE_ACTIVE_SLEEP';
  337. }
  338. }
  339. if (!loaded) return <View />
  340. return <View>
  341. <View style={{ display: 'flex', flexDirection: 'column' }}>
  342. <NewHeader type={NewHeaderType.left} title={getTitle()} />
  343. <Card>
  344. <View>
  345. {
  346. errors.map((item, index) => {
  347. return <View key={index} className='error_tip'>
  348. <StatusIndicator type={StatusType.img}
  349. fontColor="#000"
  350. fontSize={rpxToPx(24)}
  351. text={item} color={MainColorType.error}>
  352. <IconError color="#fff" width={rpxToPx(26)} />
  353. </StatusIndicator>
  354. </View>
  355. // return <View key={index} className='error_tip'>{item}</View>
  356. })
  357. }
  358. {
  359. showAutoSave && <View className='success_tip'>
  360. <StatusIndicator type={StatusType.img}
  361. fontColor="#000"
  362. fontSize={rpxToPx(24)}
  363. text='已自动保存' color={MainColorType.success}>
  364. <IconSuccess color="#fff" width={rpxToPx(26)} />
  365. </StatusIndicator>
  366. </View>
  367. }
  368. {
  369. !health.finish_setup && selMode == '' && <View className='success_tip' style={{ backgroundColor: MainColorType.blue + '1A', color: MainColorType.blue }}>You haven't finished setting up your schedule yet!</View>
  370. }
  371. {
  372. list.map((obj, i) => {
  373. return <ScheduleItem
  374. index={i}
  375. count={list.length}
  376. key={i * 100}
  377. obj={obj}
  378. highlight={false}
  379. showLine={i < list.length - 1}
  380. errors={errors}
  381. selMode={selMode}
  382. disable={!health.finish_setup}
  383. onChange={time => {
  384. obj.time = time
  385. setSelItem(obj)
  386. setShowTimePicker(false)
  387. var array = JSON.parse(JSON.stringify(list))
  388. array[i].time = time
  389. array[i].op_ms = new Date().getTime()
  390. setList(array)
  391. checkData(array)
  392. }}
  393. onDelete={() => {
  394. delSchedule(obj.id).then(res => {
  395. var array = JSON.parse(JSON.stringify(list))
  396. array.splice(i, 1)
  397. setList(array)
  398. global.refreshWindow()
  399. })
  400. }}
  401. />
  402. })
  403. }
  404. </View>
  405. </Card>
  406. <View style={{ height: 20, flexShrink: 0 }} />
  407. {
  408. !router.params.isFastSleep && selMode != '' && selMode != 'DAY' && selMode != 'NIGHT' && <Card><View className='item_add'
  409. onClick={() => add((selMode == 'FAST' || selMode == 'EAT'))}>
  410. <StatusIndicator type={StatusType.img}
  411. fontColor={getAddColor()}
  412. fontSize={rpxToPx(34)}
  413. text={(selMode == 'FAST' || selMode == 'EAT') ? t('health.add_meal') : t('health.add_active')} color={getAddColor()}>
  414. <IconAdd color="#fff" width={rpxToPx(20)} />
  415. </StatusIndicator>
  416. {/* <IconAdd color={getAddColor()} width={rpxToPx(34)} />
  417. <View className='toolbar_btn' style={{ color: getAddColor() }} >添加</View> */}
  418. <View style={{ flex: 1 }} />
  419. </View></Card>
  420. }
  421. {
  422. selMode == '' && <Card>
  423. <View className='item_add'
  424. style={{ position: 'relative' }}
  425. onClick={() => add(true)}>
  426. <StatusIndicator type={StatusType.img}
  427. fontColor={MainColorType.eat}
  428. fontSize={rpxToPx(34)}
  429. text={t('health.add_meal')} color={MainColorType.eat}>
  430. <IconAdd color="#fff" width={rpxToPx(20)} />
  431. </StatusIndicator>
  432. <View style={{ flex: 1 }} />
  433. <View className='border_footer_line' style={{ left: rpxToPx(66) }} />
  434. </View>
  435. <View className='item_add'
  436. onClick={() => add(false)}>
  437. <StatusIndicator type={StatusType.img}
  438. fontColor={MainColorType.active}
  439. fontSize={rpxToPx(34)}
  440. text={t('health.add_active')} color={MainColorType.active}>
  441. <IconAdd color="#fff" width={rpxToPx(20)} />
  442. </StatusIndicator>
  443. <View style={{ flex: 1 }} />
  444. </View>
  445. </Card>
  446. }
  447. {
  448. health.finish_setup && <View style={{
  449. backgroundColor: 'transparent',
  450. position: 'relative',
  451. display: 'flex',
  452. alignItems: 'center',
  453. justifyContent: 'center',
  454. height: rpxToPx(128),
  455. width: rpxToPx(750)
  456. }}>
  457. {
  458. selMode != '' && <View style={{ width: rpxToPx(316), height: rpxToPx(128), display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
  459. <NewButton
  460. type={NewButtonType.link}
  461. title='View Full Schedule'
  462. onClick={showAll}
  463. >
  464. </NewButton>
  465. </View>}
  466. <NewButton
  467. btnStyle={{
  468. position: 'absolute',
  469. top: rpxToPx(42),
  470. right: rpxToPx(40)
  471. }}
  472. type={NewButtonType.more}
  473. onClick={more}
  474. />
  475. </View>
  476. }
  477. {/* <View style={{ height: 100, flexShrink: 0 }} /> */}
  478. <ListFooter />
  479. <View style={{ flex: 1 }} />
  480. {
  481. selMode == '' && !health.finish_setup && <View className="main_footer">
  482. <NewButton
  483. type={NewButtonType.fill}
  484. title={t('health.finish_setup')}
  485. disable={errors.length > 0}
  486. color={MainColorType.blue}
  487. width={rpxToPx(646)}
  488. height={rpxToPx(96)}
  489. onClick={() => {
  490. jumpPage('/_health/pages/guide_begin')
  491. }}
  492. />
  493. </View>
  494. }
  495. {/* <View className="edit_footer_btn" style={{ color: getThemeColor(health.mode), backgroundColor: getThemeColor(health.mode) + '33' }} onClick={tapEdit}>批量编辑</View> */}
  496. {
  497. showTimePicker && <Modal
  498. testInfo={null}
  499. dismiss={() => {
  500. setShowTimePicker(false)
  501. }}
  502. confirm={() => { }}>
  503. {
  504. modalContent()
  505. }
  506. </Modal>
  507. }
  508. {
  509. showModal && <AddLabel labels={isEat ? labels : labels2}
  510. window={isEat ? 'EAT' : 'ACTIVE'}
  511. color={isEat ? MainColorType.eat : MainColorType.active}
  512. disMiss={() => setShowModal(false)}
  513. op_page={getOpPage()}
  514. confirm={(res) => {
  515. checkResultData(res)
  516. }}
  517. />
  518. }
  519. </View>
  520. </View>
  521. }