Metric.tsx 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443
  1. import { View, Text, ScrollView } from "@tarojs/components";
  2. import './Metric.scss'
  3. import { useSelector } from "react-redux";
  4. import { useEffect, useRef, useState } from "react";
  5. import Taro from "@tarojs/taro";
  6. import { metricCards, uploadMetric, uploadSteps } from "@/services/trackSomething";
  7. import { TimeFormatter } from "@/utils/time_format";
  8. import MetricItem from "./MetricItem";
  9. import Modal from "@/components/layout/Modal";
  10. import LimitPickers from "@/components/input/LimitPickers";
  11. import SlidngScale from "@/components/input/SlidngScale";
  12. import NoData from "@/components/view/NoData";
  13. import { alphaToHex } from "@/utils/tools";
  14. import Layout from "@/components/layout/layout";
  15. import { NaviBarTitleShowType, TemplateType } from "@/utils/types";
  16. import { useTranslation } from "react-i18next";
  17. let useNavigation;
  18. if (process.env.TARO_ENV == 'rn') {
  19. useNavigation = require("@react-navigation/native").useNavigation
  20. }
  21. export default function Component(props: any) {
  22. const { t } = useTranslation()
  23. const user = useSelector((state: any) => state.user);
  24. const [list, setList] = useState([])
  25. const [isModalOpen, setIsModalOpen] = useState(false);
  26. const [isTimePickerOpen, setIsTimePickerOpen] = useState(false);
  27. const [pickerValue, setPickerValue] = useState([]);
  28. const [pickerItems, setPickerItems] = useState([]);
  29. const [isPoint, setIsPoint] = useState(false)
  30. const [metricItem, setMetricItem] = useState({})
  31. const [strTime, setStrTime] = useState('')
  32. const [time, setTime] = useState(0)
  33. const [showErrorPage, setErrorPage] = useState(false)
  34. const [setupTime, setSetupTime] = useState(0)
  35. const [triggered, setTriggered] = useState(true)
  36. const limitPickerRef = useRef<any>(null);
  37. const [count, setCount] = useState(0)
  38. //未登录<->已登录 状态切换时,执行一次授权检查
  39. useEffect(() => {
  40. getCards();
  41. const now = new Date();
  42. const nextMidnight = new Date(now.getFullYear(), now.getMonth(), now.getDate() + 1, 0, 0, 0);
  43. const timeUntilMidnight = nextMidnight.getTime() - now.getTime();
  44. setTimeout(() => {
  45. setCount(count + 1)
  46. }, timeUntilMidnight);
  47. }, [user.isLogin])
  48. const openModal = () => {
  49. setSetupTime(new Date().getTime())
  50. setIsModalOpen(true);
  51. };
  52. const closeModal = () => {
  53. setIsModalOpen(false);
  54. };
  55. global.refreshMetric = () => {
  56. getCards()
  57. }
  58. function getCards() {
  59. setTriggered(true)
  60. metricCards().then(res => {
  61. Taro.stopPullDownRefresh()
  62. setErrorPage(false)
  63. setList((res as any).cards)
  64. setTriggered(false)
  65. }).catch(e => {
  66. Taro.stopPullDownRefresh()
  67. if (list.length == 0) {
  68. setErrorPage(true)
  69. }
  70. setTriggered(false)
  71. })
  72. }
  73. //ts 把数组items: [{code: "_walk", value: 2180},{code: "_walk", value: 4444}]中的value取出来,/分割,组成字符串,如2180/4444
  74. function getValues(items) {
  75. var values = ''
  76. items.map((item, index) => {
  77. if (index == 0) {
  78. values = item.value
  79. }
  80. else {
  81. values = values + '/' + item.value
  82. }
  83. })
  84. return values
  85. }
  86. function goDetail(item) {
  87. if (user.isLogin) {
  88. if (!item.latest_record) {
  89. return
  90. }
  91. Taro.navigateTo({
  92. url: '/pages/common/RecordsHistory?type=metric&refreshList=getCards&code=' + item.code + `&title=${item.name}` + '&themeColor=' + item.theme_color
  93. })
  94. }
  95. else {
  96. Taro.navigateTo({
  97. url: '/pages/account/ChooseAuth'
  98. })
  99. }
  100. }
  101. function record(item: any) {
  102. if (user.isLogin) {
  103. var now = new Date();
  104. var t = (now.getHours() < 10 ? '0' + now.getHours() : now.getHours()) + ":" + (now.getMinutes() < 10 ? '0' + now.getMinutes() : now.getMinutes());
  105. setStrTime(t)
  106. setTime(now.getTime())
  107. setMetricItem(item)
  108. openModal()
  109. }
  110. else {
  111. Taro.navigateTo({
  112. url: '/pages/account/ChooseAuth'
  113. })
  114. }
  115. }
  116. function pointPicker(item: any) {
  117. var min = item.min
  118. var max = item.max
  119. var step = item.step
  120. var value = item.default_value
  121. var items: number[] = []
  122. var items2: number[] = []
  123. var value0Index = 0
  124. var value1Index = 0
  125. for (var i = min; i <= max; i++) {
  126. if (i == Math.floor(value)) {
  127. value0Index = i - min
  128. }
  129. items.push(i)
  130. // items2.push(i)
  131. }
  132. for (var i = 0; i <= 9; i++) {
  133. items2.push(i)
  134. }
  135. value1Index = Math.round(10 * (value - Math.floor(value)));
  136. setPickerValue([value0Index, value1Index] as any)
  137. setPickerItems([items, items2] as any)
  138. setIsPoint(true)
  139. openModal()
  140. }
  141. function singlePicker(item: any) {
  142. var min = item.min
  143. var max = item.max
  144. var step = 1//item.step
  145. var value = item.default_value
  146. var items: number[] = []
  147. for (var i = min; i <= max; i += step) {
  148. if (i == value) {
  149. setPickerValue([i - min] as any)
  150. }
  151. items.push(i)
  152. }
  153. setPickerItems([items] as any)
  154. setIsPoint(false)
  155. openModal()
  156. }
  157. function pickerChanged(e) {
  158. closeModal();
  159. var params = {}
  160. var date = new Date(time)
  161. var strDate = (date.getFullYear() + '') + (date.getMonth() + 1 < 10 ? '0' + (date.getMonth() + 1) : (date.getMonth() + 1)) + (date.getDate() < 10 ? '0' + date.getDate() : date.getDate());
  162. if ((metricItem as any).schemas[0].type == 'DECIMAL') {
  163. var array = pickerItems;
  164. var value = array[0][e[0]] + '.' + array[1][e[1]]
  165. params = {
  166. code: (metricItem as any).code,
  167. timestamp: time,
  168. date: strDate,
  169. items: [{ code: (metricItem as any).schemas[0].code, value: value }]
  170. }
  171. }
  172. else if ((metricItem as any).schemas.length > 1) {
  173. }
  174. else {
  175. var array = pickerItems;
  176. var value = array[0][e[0]] + ''
  177. params = {
  178. code: (metricItem as any).code,
  179. timestamp: time,
  180. date: strDate,
  181. items: [{ code: (metricItem as any).schemas[0].code, value: value }]
  182. }
  183. }
  184. uploadMetric(params).then(res => {
  185. list.map((item, index) => {
  186. if ((item as any).code == (res as any).code) {
  187. (item as any).latest_record = (res as any).latest_record
  188. }
  189. })
  190. setList(list)
  191. setTimeout(() => {
  192. setCount(count + 1)
  193. }, 31000)
  194. // getCards();
  195. })
  196. }
  197. function showTimePicker() {
  198. setIsTimePickerOpen(true)
  199. }
  200. function chooseTime(e) {
  201. setTime(e);
  202. setStrTime(TimeFormatter.formatTimestamp(e))
  203. setIsTimePickerOpen(false)
  204. }
  205. function cancelModal() {
  206. (metricItem as any).schemas.map((item, index) => {
  207. item.tempValue = ''
  208. })
  209. closeModal()
  210. }
  211. function confirmModal() {
  212. closeModal()
  213. var date = new Date(time)
  214. var strDate = (date.getFullYear() + '') + (date.getMonth() + 1 < 10 ? '0' + (date.getMonth() + 1) : (date.getMonth() + 1)) + (date.getDate() < 10 ? '0' + date.getDate() : date.getDate());
  215. var array: any[] = [];
  216. (metricItem as any).schemas.map((item, index) => {
  217. array.push({
  218. code: item.code,
  219. value: item.tempValue && item.tempValue > 0 ? item.tempValue : item.default_value
  220. })
  221. })
  222. var params = {
  223. code: (metricItem as any).code,
  224. timestamp: time,
  225. date: strDate,
  226. items: array,
  227. extra: {
  228. set_time: setupTime,
  229. confirm_time: new Date().getTime()
  230. }
  231. }
  232. global.postBtnUpdateStatus('normal');
  233. uploadMetric(params).then(res => {
  234. // getCards();
  235. (metricItem as any).schemas.map((item, index) => {
  236. item.tempValue = ''
  237. })
  238. list.map((item, index) => {
  239. if ((item as any).code == (res as any).code) {
  240. (item as any).latest_record = (res as any).latest_record;
  241. (item as any).schemas = (res as any).schemas;
  242. }
  243. })
  244. setList(JSON.parse(JSON.stringify(list)))
  245. global.postBtnUpdateStatus('idle');
  246. setTimeout(() => {
  247. setCount(count + 1)
  248. }, 31000)
  249. })
  250. }
  251. const limitDay = 500
  252. const limit = new Date().getTime() - limitDay * 3600 * 1000 * 24;
  253. function detail() {
  254. return <View>
  255. <View className="metric_container">
  256. {
  257. list.map((item: any, index: number) => {
  258. var unit = ''
  259. var value = t('feature.track_something.metric.no_record')
  260. var desc = t('feature.track_something.metric.check_unlock_data')
  261. var showDetail = false;
  262. if (item.latest_record) {
  263. unit = item.schemas[0].default_unit
  264. value = getValues(item.latest_record.items)
  265. desc = TimeFormatter.dateDescription(item.latest_record.timestamp, true)
  266. showDetail = true
  267. }
  268. if (!user.isLogin) {
  269. value = t('feature.track_something.metric.un_login')
  270. desc = t('feature.track_something.metric.login_can_check')
  271. }
  272. return <MetricItem title={item.name}
  273. value={value}
  274. unit={unit}
  275. desc={desc}
  276. btnText={t('feature.track_something.btn_record')}
  277. isDisabled={false}
  278. showDetail={showDetail}
  279. themeColor={item.theme_color}
  280. onClickDetail={() => { goDetail(item) }}
  281. onClick={() => { record(item) }}
  282. />
  283. })
  284. }
  285. </View>
  286. <View className="space_width" ></View>
  287. </View>
  288. }
  289. function detailContent() {
  290. // if (process.env.TARO_ENV=='rn'){
  291. // return <ScrollView>
  292. // {
  293. // detail()
  294. // }
  295. // <View style={{height:150}}/>
  296. // </ScrollView>
  297. // }
  298. return detail()
  299. }
  300. return <View style={{ position: 'relative' }}>
  301. <Layout children={showErrorPage ? <NoData refresh={() => { getCards() }} /> : detailContent()}
  302. title={t('page.metric.title')}
  303. type={TemplateType.flex}
  304. refresh={() => { getCards() }}
  305. triggered={triggered}
  306. titleShowStyle={NaviBarTitleShowType.scrollToShow}
  307. />
  308. {
  309. isModalOpen && <Modal dismiss={closeModal} title={(metricItem as any).name}
  310. themeColor={(metricItem as any).theme_color}
  311. confirm={confirmModal}>
  312. <View style={{
  313. display: 'flex', flexDirection: 'column',
  314. width: '100%', color: '#000'
  315. }}>
  316. <Text className='modal_title' style={{ color: (metricItem as any).theme_color }}>{(metricItem as any).name ? (metricItem as any).name : ''}</Text>
  317. <View style={{ position: 'relative' }}>
  318. {
  319. (metricItem as any).schemas.map((item, index) => {
  320. return <View key={index}>
  321. {
  322. (metricItem as any).schemas.length > 1 && <Text style={{
  323. textAlign: 'center', width: '100%', display: 'flex',
  324. justifyContent: 'center', color: (metricItem as any).theme_color
  325. }}>{item.name}</Text>
  326. }
  327. <SlidngScale step={item.step} min={item.min} max={item.max} default_value={item.default_value}
  328. unit={item.default_unit}
  329. themeColor={(metricItem as any).theme_color}
  330. changed={(e) => { item.tempValue = e }} />
  331. </View>
  332. })
  333. }
  334. </View>
  335. <View className="change_time_bg ">
  336. <View className="gray1 time_bg" onClick={showTimePicker}>
  337. <Text className="time" >{strTime}</Text>
  338. </View>
  339. </View>
  340. <View className='modal_operate'>
  341. <View className='modal_btn' style={{ backgroundColor: (metricItem as any).theme_color + alphaToHex(0.4) }} onClick={cancelModal}>
  342. <Text className='modal_cancel_text' style={{ color: (metricItem as any).theme_color }}>{
  343. t('feature.common.picker_cancel_btn')
  344. }</Text>
  345. </View>
  346. <View className='btn_space' />
  347. <View className='modal_btn' style={{ backgroundColor: (metricItem as any).theme_color }} onClick={confirmModal}>
  348. <Text className='modal_confirm_text' style={{ color: '#000' }}>{
  349. t('feature.common.picker_confirm_btn')
  350. }</Text>
  351. </View>
  352. </View>
  353. {/* <View style={{ marginBottom: 20, marginTop: 20, display: 'flex', flexDirection: 'row', width: '100%' }}>
  354. <Text style={{ flex: 1, textAlign: 'center', height: 50 }} onClick={cancelModal}>取消</Text>
  355. <Text style={{ flex: 1, textAlign: 'center', height: 50 }} onClick={confirmModal}>确认</Text>
  356. </View> */}
  357. </View>
  358. </Modal>
  359. }
  360. {
  361. isTimePickerOpen && <Modal
  362. themeColor={(metricItem as any).theme_color}
  363. dismiss={() => setIsTimePickerOpen(false)} confirm={() => {
  364. var picker = limitPickerRef.current;
  365. chooseTime((picker as any).getConfirmData());
  366. setIsTimePickerOpen(false);
  367. }}>
  368. <LimitPickers ref={limitPickerRef}
  369. isRealTime={true} time={time} limit={limit}
  370. title={t('feature.track_something.picker_datetime')}
  371. themeColor={(metricItem as any).theme_color}
  372. limitDay={limitDay} onCancel={() => { setIsTimePickerOpen(false) }}
  373. onChange={(e) => {
  374. chooseTime(e)
  375. // pickerConfirm(e)
  376. // hidePicker()
  377. }} />
  378. </Modal>
  379. }
  380. </View>
  381. }