Metric.tsx 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532
  1. import { View, Text, Image, PageContainer } from "@tarojs/components";
  2. import './Metric.scss'
  3. import { useDispatch, useSelector } from "react-redux";
  4. import { useEffect, useRef, useState } from "react";
  5. import Taro from "@tarojs/taro";
  6. import { metricCards, metricFollows, metricGroups, uploadMetric, uploadSteps } from "@/services/trackSomething";
  7. import { TimeFormatter } from "@/utils/time_format";
  8. import MetricItem from "./MetricItem";
  9. import NoData from "@/components/view/NoData";
  10. import Layout from "@/components/layout/layout";
  11. import { MetricModalType, ModalType, NaviBarTitleShowType, TemplateType } from "@/utils/types";
  12. import { useTranslation } from "react-i18next";
  13. import { jumpPage } from "@/features/trackTimeDuration/hooks/Common";
  14. import TitleView from "@/features/trackTimeDuration/components/TitleView";
  15. import { setTabbarStatus } from "@/store/common";
  16. import MetricModalChoose from "./MetricModalChoose";
  17. import MetricModalOrder from './MetricModalOrder';
  18. import MetricModalAdd from "./MetricModalAdd";
  19. import MetricModalTime from "./MetricModalTime";
  20. import Modal from "@/components/layout/Modal";
  21. import { rpxToPx } from "@/utils/tools";
  22. import { AtActivityIndicator } from "taro-ui";
  23. let useNavigation;
  24. if (process.env.TARO_ENV == 'rn') {
  25. useNavigation = require("@react-navigation/native").useNavigation
  26. }
  27. export default function Component(props: any) {
  28. const { t } = useTranslation()
  29. const user = useSelector((state: any) => state.user);
  30. const [list, setList] = useState([])
  31. const [metricItem, setMetricItem] = useState({})
  32. const [strTime, setStrTime] = useState('')
  33. const [time, setTime] = useState(0)
  34. const [showErrorPage, setErrorPage] = useState(false)
  35. const [setupTime, setSetupTime] = useState(0)
  36. const [triggered, setTriggered] = useState(true)
  37. const [count, setCount] = useState(0)
  38. const [showPageContainer, setShowPageContainer] = useState(false)
  39. const [modalType, setModalType] = useState(MetricModalType.none)
  40. const [themeColor, setThemeColor] = useState('#00ffff')
  41. const [groups, setGroups] = useState([])
  42. const [orignalGroups, setOrignalGroups] = useState([])
  43. const [limits, setLimits] = useState(null)
  44. const [orders, setOrders] = useState([])
  45. const [newOrders, setNewOrders] = useState([])
  46. const [resultOrders, setResultOrders] = useState([])
  47. const [tempMetricItem, setTempMetricItem] = useState(null)
  48. const [loaded, setLoaded] = useState(false)
  49. const dispatch = useDispatch();
  50. let navigation;
  51. if (useNavigation) {
  52. navigation = useNavigation()
  53. }
  54. //未登录<->已登录 状态切换时,执行一次授权检查
  55. useEffect(() => {
  56. getCards();
  57. getGroups();
  58. const now = new Date();
  59. const nextMidnight = new Date(now.getFullYear(), now.getMonth(), now.getDate() + 1, 0, 0, 0);
  60. const timeUntilMidnight = nextMidnight.getTime() - now.getTime();
  61. setTimeout(() => {
  62. setCount(count + 1)
  63. }, timeUntilMidnight);
  64. }, [user.isLogin])
  65. const openModal = () => {
  66. setSetupTime(new Date().getTime())
  67. setModalType(MetricModalType.add)
  68. setShowPageContainer(true)
  69. };
  70. global.refreshMetric = () => {
  71. getCards()
  72. }
  73. function getCards() {
  74. setTriggered(true)
  75. metricCards().then(res => {
  76. Taro.stopPullDownRefresh()
  77. setErrorPage(false)
  78. setList((res as any).cards)
  79. setTriggered(false)
  80. if ((res as any).cards.length > 0) {
  81. var obj = (res as any).cards[0]
  82. setThemeColor(obj.theme_color)
  83. }
  84. var list: any = [];
  85. (res as any).cards.map(item => {
  86. list.push({
  87. name: item.name,
  88. code: item.code,
  89. is_following: true
  90. })
  91. })
  92. setOrders(list)
  93. setResultOrders(list)
  94. setLoaded(true)
  95. }).catch(e => {
  96. Taro.stopPullDownRefresh()
  97. if (list.length == 0) {
  98. setErrorPage(true)
  99. }
  100. setTriggered(false)
  101. setLoaded(false)
  102. })
  103. }
  104. function getGroups() {
  105. metricGroups().then(res => {
  106. var array = (res as any).groups
  107. setOrignalGroups(JSON.parse(JSON.stringify(array)))
  108. setGroups(array)
  109. setLimits((res as any).num_options_limit)
  110. setNewOrders([])
  111. })
  112. }
  113. //ts 把数组items: [{code: "_walk", value: 2180},{code: "_walk", value: 4444}]中的value取出来,/分割,组成字符串,如2180/4444
  114. function getValues(items) {
  115. var values = ''
  116. items.map((item, index) => {
  117. if (index == 0) {
  118. values = item.value
  119. }
  120. else {
  121. values = values + '/' + item.value
  122. }
  123. })
  124. return values
  125. }
  126. function goDetail(item) {
  127. if (user.isLogin) {
  128. if (!item.latest_record) {
  129. return
  130. }
  131. global.selMetricItem = item
  132. jumpPage('/pages/common/RecordsHistory?type=metric&refreshList=getCards&code='
  133. + item.code + `&title=${item.name}` + '&themeColor=' + item.theme_color, 'RecordsHistory', navigation, {
  134. type: 'metric',
  135. refreshList: 'getCards',
  136. code: item.code,
  137. title: item.name,
  138. themeColor: item.theme_color
  139. })
  140. }
  141. else {
  142. jumpPage('/pages/account/ChooseAuth', 'ChooseAuth', navigation)
  143. }
  144. }
  145. function record(item: any) {
  146. if (user.isLogin) {
  147. //check data
  148. var schemas = item.schemas
  149. for (var i = 0; i < schemas.length; i++) {
  150. var obj = schemas[i]
  151. if ((!obj.min && obj.min != 0) || !obj.max || !obj.step || (!obj.default_value && obj.default_value != 0)) {
  152. Taro.showToast({
  153. title: t('feature.common.maintain'),
  154. icon: 'none'
  155. })
  156. return;
  157. }
  158. }
  159. var now = new Date();
  160. // var t = (now.getHours() < 10 ? '0' + now.getHours() : now.getHours()) + ":" + (now.getMinutes() < 10 ? '0' + now.getMinutes() : now.getMinutes());
  161. // setStrTime(t)
  162. setStrTime(TimeFormatter.dateTimeFormate(now.getTime()))
  163. setTime(now.getTime())
  164. setMetricItem(item)
  165. openModal()
  166. }
  167. else {
  168. jumpPage('/pages/account/ChooseAuth', 'ChooseAuth', navigation)
  169. }
  170. }
  171. function chooseTime(e) {
  172. setTime(e);
  173. setStrTime(TimeFormatter.dateTimeFormate(e))
  174. }
  175. const limitDay = 500
  176. const limit = new Date().getTime() - limitDay * 3600 * 1000 * 24;
  177. //获取参考值
  178. function getTargetValue(item) {
  179. for (var i = 0; i < item.references.length; i++) {
  180. if (item.references[i].selected) {
  181. for (var j = 0; j < item.references[i].categories.length; j++) {
  182. if (item.references[i].categories[j].is_default) {
  183. return item.references[i].categories[j].value_range
  184. }
  185. }
  186. }
  187. }
  188. return null
  189. }
  190. function detail() {
  191. if (!loaded){
  192. return <View style={{ width: rpxToPx(750), height: rpxToPx(900), display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
  193. <AtActivityIndicator size={40} color="#fff"/>
  194. {/* <Tabbar index={0} /> */}
  195. </View>
  196. }
  197. return <View>
  198. <View className="metric_container" >
  199. {
  200. list.map((item: any, index: number) => {
  201. var unit = ''
  202. var value = t('feature.track_something.metric.no_record')
  203. var desc = t('feature.track_something.metric.check_unlock_data')
  204. var showDetail = false;
  205. if (item.latest_record) {
  206. if (item.type == 'composite') {
  207. unit = item.latest_record.unit ? item.latest_record.unit : ''
  208. value = item.latest_record.value
  209. }
  210. else {
  211. unit = item.schemas[0].default_unit
  212. value = getValues(item.latest_record.items)
  213. }
  214. desc = TimeFormatter.dateDescription(item.latest_record.timestamp, true)
  215. showDetail = true
  216. }
  217. if (!user.isLogin) {
  218. value = t('feature.track_something.metric.un_login')
  219. desc = t('feature.track_something.metric.login_can_check')
  220. }
  221. if (getTargetValue(item)) {
  222. desc = getTargetValue(item)
  223. }
  224. // desc = '90~140/60~90 mmHg'
  225. return <MetricItem title={item.name}
  226. value={value}
  227. unit={unit}
  228. desc={desc}
  229. btnText={t('feature.track_something.btn_record')}
  230. isDisabled={false}
  231. showDetail={showDetail}
  232. themeColor={item.theme_color}
  233. onClickDetail={() => { goDetail(item) }}
  234. onClick={() => { record(item) }}
  235. showTag={item.type == 'composite'}
  236. tagName={t('feature.track_something.metric.composite')}
  237. />
  238. })
  239. }
  240. {
  241. user.isLogin && list.length > 0 && <View className="add_more" onClick={chooseMore}>
  242. <Image style={{ width: rpxToPx(48), height: rpxToPx(48) }} src={require('@assets/images/add2.png')} />
  243. <Text className="add_more_text">{t('feature.workout.add_more')}</Text>
  244. </View>
  245. }
  246. </View>
  247. {
  248. process.env.TARO_ENV=='weapp'?<View className="space_width" ></View>:
  249. <View style={{height:0}}></View>
  250. }
  251. </View>
  252. }
  253. function addBtnClick() {
  254. Taro.showActionSheet({
  255. itemList: [t('feature.track_something.metric.choose_metric'),
  256. t('feature.track_something.metric.order')]
  257. })
  258. .then(res => {
  259. switch (res.tapIndex) {
  260. case 0:
  261. chooseMore()
  262. break;
  263. case 1:
  264. global.metricAdd = false
  265. setModalType(MetricModalType.order)
  266. setShowPageContainer(true)
  267. break;
  268. }
  269. })
  270. .catch(err => {
  271. console.log(err.errMsg)
  272. })
  273. }
  274. function chooseMore() {
  275. global.metricAdd = true
  276. setModalType(MetricModalType.choose)
  277. setShowPageContainer(true)
  278. }
  279. function headerView() {
  280. return <TitleView title={t('page.metric.title')} showAddBtn={loaded && !showErrorPage ? true : false} onClick={addBtnClick}>
  281. </TitleView>
  282. }
  283. function metricModalContent() {
  284. global.set_time = new Date().getTime()
  285. switch (modalType) {
  286. case MetricModalType.choose:
  287. return <MetricModalChoose
  288. themeColor={themeColor}
  289. cancel={modalCancel}
  290. confirm={modalConfirm}
  291. array={groups}
  292. orders={orders}
  293. newOrders={newOrders}
  294. limit={limits}
  295. />
  296. case MetricModalType.order:
  297. return <MetricModalOrder
  298. themeColor={themeColor}
  299. cancel={modalOrderCancel}
  300. confirm={modalOrderConfirm}
  301. array={resultOrders} />
  302. case MetricModalType.add:
  303. return <MetricModalAdd
  304. item={tempMetricItem ? tempMetricItem : metricItem}
  305. strTime={strTime}
  306. showPicker={(tempItem) => {
  307. var temp = JSON.parse(JSON.stringify(tempItem))
  308. temp.schemas.map(item => {
  309. if (item.tempValue) {
  310. item.default_value = item.tempValue
  311. }
  312. })
  313. setTempMetricItem(temp)
  314. setModalType(MetricModalType.time)
  315. }}
  316. cancel={modalAddCancel}
  317. confirm={modalAddConfirm} />
  318. case MetricModalType.time:
  319. return <MetricModalTime
  320. item={metricItem}
  321. time={time}
  322. limit={limit}
  323. limitDay={limitDay}
  324. cancel={() => {
  325. setModalType(MetricModalType.add)
  326. }}
  327. confirm={(e) => {
  328. chooseTime(e)
  329. setModalType(MetricModalType.add)
  330. }}
  331. />
  332. }
  333. return <View />
  334. }
  335. function modalCancel() {
  336. setShowPageContainer(false)
  337. onPageContainerCancel()
  338. }
  339. function modalConfirm(datas, newOrders, resultOrders, groups) {
  340. setOrders(datas)
  341. setGroups(groups)
  342. setNewOrders(newOrders)
  343. setResultOrders(resultOrders)
  344. setModalType(MetricModalType.order)
  345. }
  346. function modalOrderCancel() {
  347. if (global.metricAdd) {
  348. setModalType(MetricModalType.choose)
  349. }
  350. else {
  351. setShowPageContainer(false)
  352. onPageContainerCancel()
  353. }
  354. }
  355. function modalOrderConfirm(datas) {
  356. var array: any = []
  357. datas.map(item => {
  358. array.push(item.code)
  359. })
  360. metricFollows({ codes: array }).then(res => {
  361. getCards()
  362. getGroups()
  363. setList((res as any).cards)
  364. // setOrignalGroups(groups)
  365. })
  366. setShowPageContainer(false)
  367. }
  368. function modalAddCancel() {
  369. setShowPageContainer(false)
  370. onPageContainerCancel()
  371. }
  372. function modalAddConfirm(result) {
  373. setShowPageContainer(false)
  374. var date = new Date(time)
  375. var strDate = (date.getFullYear() + '') + (date.getMonth() + 1 < 10 ? '0' + (date.getMonth() + 1) : (date.getMonth() + 1)) + (date.getDate() < 10 ? '0' + date.getDate() : date.getDate());
  376. var array: any[] = [];
  377. result.schemas.map((item, index) => {
  378. array.push({
  379. code: item.code,
  380. value: item.tempValue && item.tempValue > 0 ? item.tempValue : item.default_value
  381. })
  382. })
  383. var params = {
  384. code: result.code,
  385. timestamp: time,
  386. date: strDate,
  387. items: array,
  388. extra: {
  389. set_time: setupTime,
  390. confirm_time: new Date().getTime()
  391. }
  392. }
  393. global.postBtnUpdateStatus('normal');
  394. uploadMetric(params).then(res => {
  395. // getCards();
  396. result.schemas.map((item, index) => {
  397. item.tempValue = ''
  398. })
  399. list.map((item, index) => {
  400. if ((item as any).code == (res as any).code) {
  401. (item as any).latest_record = (res as any).latest_record;
  402. (item as any).schemas = (res as any).schemas;
  403. }
  404. })
  405. setList(JSON.parse(JSON.stringify(list)))
  406. global.postBtnUpdateStatus('idle');
  407. setTimeout(() => {
  408. setCount(count + 1)
  409. }, 31000)
  410. getCards()
  411. })
  412. }
  413. function onPageContainerCancel() {
  414. if (modalType == MetricModalType.choose || modalType == MetricModalType.order) {
  415. var array: any = [];
  416. list.map(item => {
  417. array.push({
  418. name: (item as any).name,
  419. code: (item as any).code,
  420. is_following: true
  421. })
  422. })
  423. setOrders(array)
  424. setNewOrders([])
  425. setGroups(JSON.parse(JSON.stringify(orignalGroups)))
  426. // setGroups(orignalGroups)
  427. }
  428. else {
  429. setTempMetricItem(null)
  430. }
  431. }
  432. function modalContent() {
  433. if (showPageContainer) {
  434. if (process.env.TARO_ENV == 'weapp') {
  435. return <Modal dismiss={() => {
  436. onPageContainerCancel()
  437. setShowPageContainer(false)
  438. }} confirm={() => { }}>
  439. {
  440. metricModalContent()
  441. }
  442. </Modal>
  443. }
  444. else if (process.env.TARO_ENV == 'rn') {
  445. return <PageContainer style={{ backgroundColor: '#1c1c1c' }}
  446. overlayStyle={{ backgroundColor: 'rgba(0,0,0,0.9)' }}
  447. customStyle={{ backgroundColor: '#1c1c1c' }}
  448. closeOnSlideDown={false}
  449. show={showPageContainer} round={true} overlay={true} position='bottom'
  450. onClickOverlay={onPageContainerCancel}
  451. onAfterLeave={() => { setShowPageContainer(false); setModalType(MetricModalType.none) }}
  452. onBeforeEnter={() => { dispatch(setTabbarStatus(false)); Taro.hideTabBar() }}
  453. onBeforeLeave={() => { dispatch(setTabbarStatus(true)); Taro.showTabBar() }}
  454. >
  455. {
  456. metricModalContent()
  457. }
  458. </PageContainer>
  459. }
  460. }
  461. return <View />
  462. }
  463. return <View style={{ position: 'relative' }}>
  464. <Layout children={showErrorPage ? <NoData refresh={() => { getCards() }} /> : detail()}
  465. title={t('page.metric.title')}
  466. type={TemplateType.customHeader}
  467. header={headerView()}
  468. refresh={() => { getCards() }}
  469. triggered={triggered}
  470. titleShowStyle={NaviBarTitleShowType.scrollToShow}
  471. showPullToRefresh={true}
  472. />
  473. {
  474. modalContent()
  475. }
  476. </View>
  477. }