ConsolePicker.tsx 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266
  1. import { View, Text, PickerView, PickerViewColumn } from "@tarojs/components";
  2. import Taro from "@tarojs/taro";
  3. import { forwardRef, useEffect, useImperativeHandle, useState } from "react";
  4. import { useTranslation } from "react-i18next";
  5. import '@components/input/LimitPickers.scss'
  6. import { TimeFormatter } from "@/utils/time_format";
  7. import { AtActivityIndicator } from 'taro-ui';
  8. import React from "react";
  9. let currentValueList;
  10. const Component = forwardRef((props: {
  11. themeColor?: string, title?: string,
  12. min: number,
  13. max: number,
  14. current: number,
  15. onChange: Function,
  16. onCancel: Function,
  17. duration?: number,
  18. isFast?: boolean,
  19. isEnd?: boolean,
  20. endTimestamp?: number,
  21. isTimeout?: boolean,
  22. isLoading?: boolean
  23. }, ref) => {
  24. const today = new Date();
  25. const [loaded, setLoaded] = useState(false)
  26. const [values, setValues] = useState([0, today.getHours(), today.getMinutes()])
  27. const [days, setDays] = useState(['0'])
  28. const [todayIndex, setTodayIndex] = useState(0)
  29. const { t } = useTranslation()
  30. const hours: number[] = [];
  31. var color = props.themeColor ? props.themeColor : '#ff0000'
  32. var alpha = alphaToHex(0.4)
  33. for (let i = 0; i <= 23; i++) {
  34. hours.push(i);
  35. }
  36. const minutes: number[] = [];
  37. for (let i = 0; i <= 59; i++) {
  38. minutes.push(i);
  39. }
  40. function resetPickerData() {
  41. if (currentValueList) {
  42. setValues(currentValueList)
  43. }
  44. }
  45. useImperativeHandle(ref, () => ({
  46. resetPickerData: resetPickerData
  47. }));
  48. useEffect(() => {
  49. var minDate = new Date(props.min)
  50. var maxDate = new Date(props.max)
  51. var currentDate = new Date(props.current)
  52. minDate.setHours(0)
  53. minDate.setMinutes(0)
  54. minDate.setSeconds(0)
  55. minDate.setMilliseconds(0)
  56. maxDate.setHours(0)
  57. maxDate.setMinutes(0)
  58. maxDate.setSeconds(0)
  59. maxDate.setMilliseconds(0)
  60. // 计算两个日期的时间戳差异(以毫秒为单位)
  61. const timeDifference = Math.abs(maxDate.getTime() - minDate.getTime());
  62. // 将时间戳差异转换为天数
  63. const count = Math.ceil(timeDifference / (1000 * 3600 * 24));
  64. var list: any = [];
  65. var selIndex = 0;
  66. for (var i = 0; i <= count; i++) {
  67. var dt = new Date(props.min + i * 24 * 3600 * 1000)
  68. const month = dt.getMonth() + 1;
  69. const day = dt.getDate();
  70. // const weekday = ['周日', '周一', '周二', '周三', '周四', '周五', '周六'][date.getDay()];
  71. const weekday = TimeFormatter.getDayOfWeek(dt.getDay(), true);
  72. // const formattedDate = global.language == 'en' ? `${weekday} ${TimeFormatter.getMonth(month)} ${day}` : `${TimeFormatter.getMonth(month)}${day}日 ${weekday}`;
  73. const formattedDate = global.language == 'en' ? `${TimeFormatter.getMonth(month)} ${day}` : `${TimeFormatter.getMonth(month)}${day}日`;
  74. var today1 = new Date();
  75. var yesterday = new Date(today1.getTime() - 24 * 3600 * 1000)
  76. if (dt.getMonth() == currentDate.getMonth() && dt.getDate() == currentDate.getDate()) {
  77. selIndex = i
  78. }
  79. if (dt.getMonth() == today1.getMonth() && dt.getDate() == today1.getDate()) {
  80. list.push(TimeFormatter.getTodayUnit());
  81. setTodayIndex(i)
  82. }
  83. else if (dt.getMonth() == yesterday.getMonth() && dt.getDate() == yesterday.getDate()) {
  84. list.push(TimeFormatter.getYesterdayUnit());
  85. }
  86. else {
  87. list.push(formattedDate);
  88. }
  89. }
  90. setDays(list)
  91. currentValueList = [selIndex, currentDate.getHours(), currentDate.getMinutes()]
  92. setValues([selIndex, currentDate.getHours(), currentDate.getMinutes()])
  93. setTimeout(() => {
  94. setValues([selIndex, currentDate.getHours(), currentDate.getMinutes()])
  95. }, 50)
  96. setLoaded(true)
  97. }, [])
  98. function alphaToHex(alpha) {
  99. var alphaValue = Math.round(alpha * 255); // 将透明度乘以255并四舍五入
  100. var hexValue = alphaValue.toString(16); // 将整数转换为十六进制字符串
  101. if (hexValue.length === 1) {
  102. hexValue = "0" + hexValue; // 如果十六进制字符串只有一位,补零
  103. }
  104. return hexValue;
  105. }
  106. function cancel(e) {
  107. if (process.env.TARO_ENV == 'weapp') {
  108. e.stopPropagation()
  109. }
  110. props.onCancel()
  111. }
  112. function confirm(e) {
  113. if (process.env.TARO_ENV == 'weapp') {
  114. e.stopPropagation()
  115. }
  116. var date = new Date();
  117. date.setDate(new Date(props.max).getDate() - (days.length - values[0] - 1));
  118. const year = date.getFullYear();
  119. const month = date.getMonth() + 1;
  120. const day = date.getDate();
  121. const time = `${year}-${expandZero(month)}-${expandZero(day)}T${expandZero(hours[values[1]])}:${expandZero(minutes[values[2]])}:${expandZero((new Date(global.set_time)).getSeconds())}`;
  122. if (getTimestamp(time) > global.set_time) {
  123. setValues([todayIndex, (new Date(global.set_time)).getHours(), (new Date(global.set_time)).getMinutes()])
  124. setTimeout(() => {
  125. setValues([todayIndex, (new Date(global.set_time)).getHours(), (new Date(global.set_time)).getMinutes()])
  126. }, 300);
  127. Taro.showToast({
  128. icon: 'none',
  129. title: t('feature.common.toast.min_time_value'),
  130. })
  131. return
  132. }
  133. props.onChange(new Date(time).getTime())
  134. }
  135. function expandZero(num: number): string {
  136. return num < 10 ? `0${num}` : `${num}`;
  137. }
  138. function getTimestamp(dateTimeString: string): number {
  139. const timestamp = Date.parse(dateTimeString);
  140. return timestamp;
  141. }
  142. function onPickerChange(e) {
  143. setValues(e.detail.value)
  144. }
  145. function getEndTime() {
  146. var date = new Date(props.max);
  147. date.setDate(date.getDate() - (days.length - values[0] - 1));
  148. date.setHours(values[1])
  149. date.setMinutes(values[2])
  150. var newDate = new Date(date.getTime() + props.duration!).getTime()
  151. if (global.language == 'en') {
  152. return TimeFormatter.dateDescription(newDate, true) + ' at ' + TimeFormatter.timeDescription(newDate)
  153. }
  154. return TimeFormatter.dateDescription(newDate, true) + ' ' + TimeFormatter.timeDescription(newDate)
  155. }
  156. function getEndTimestamp(timestamp) {
  157. if (global.language == 'en') {
  158. return TimeFormatter.dateDescription(timestamp, true) + ' at ' + TimeFormatter.timeDescription(timestamp)
  159. }
  160. return TimeFormatter.dateDescription(timestamp, true) + ' ' + TimeFormatter.timeDescription(timestamp)
  161. }
  162. function pickerTimeText() {
  163. if (props.isTimeout) {
  164. if (props.isFast) {
  165. return t('feature.track_time_duration.console.real_fast_end_tip', { time: getEndTimestamp(props.endTimestamp!) })
  166. }
  167. else {
  168. return t('feature.track_time_duration.console.real_sleep_end_tip', { time: getEndTimestamp(props.endTimestamp!) })
  169. }
  170. }
  171. if (props.isFast) {
  172. if (props.isEnd) {
  173. return t('feature.track_time_duration.console.real_fast_end_tip', { time: getEndTimestamp(props.endTimestamp!) })
  174. }
  175. return t('feature.track_time_duration.console.real_fast_start_tip', { time: getEndTime() })
  176. }
  177. else {
  178. if (props.isEnd) {
  179. return t('feature.track_time_duration.console.real_sleep_end_tip', { time: getEndTimestamp(props.endTimestamp!) })
  180. }
  181. return t('feature.track_time_duration.console.real_sleep_start_tip', { time: getEndTime() })
  182. }
  183. }
  184. function pickerDetail() {
  185. return <View style={{ display: 'flex', flexDirection: 'column', opacity: loaded ? 1 : 0 }}>
  186. <Text className='modal_title' style={{ color: color }}>{props.title ? props.title : '测试标题 '}</Text>
  187. <View style={{ backgroundColor: 'transparent', position: 'relative' }}>
  188. <PickerView
  189. itemStyle={{ color: '#fff', margin: 0, padding: 0 }}
  190. value={values}
  191. className="picker"
  192. maskClass="picker-mask"
  193. style={{ color: '#fff' }}
  194. onChange={onPickerChange}
  195. immediateChange={true}
  196. indicatorStyle='height: 50px;color:red;'>
  197. <PickerViewColumn style='flex:0 0 45%'>
  198. {days.map(item => {
  199. return (
  200. <View style={{ display: 'flex', alignItems: 'center', justifyContent: 'center', color: '#fff',height:50 }}>{item}</View>
  201. );
  202. })}
  203. </PickerViewColumn>
  204. <PickerViewColumn>
  205. {hours.map(item => {
  206. return (
  207. <View style={{ display: 'flex', alignItems: 'center', justifyContent: 'center', color: '#fff',height:50 }}>{item < 10 ? `0${item}` : item}</View>
  208. );
  209. })}
  210. </PickerViewColumn>
  211. <PickerViewColumn>
  212. {minutes.map(item => {
  213. return (
  214. <View style={{ display: 'flex', alignItems: 'center', justifyContent: 'center', color: '#fff',height:50 }}>{item < 10 ? `0${item}` : item}</View>
  215. );
  216. })}
  217. </PickerViewColumn>
  218. </PickerView>
  219. </View>
  220. <Text className="pickerEndTime" style={{ color: props.isTimeout ? 'red' : '#ffffff' }}>{pickerTimeText()}</Text>
  221. <View className='modal_operate'>
  222. <View className='modal_btn' style={{ backgroundColor: color + alpha }} onClick={cancel}>
  223. <Text className='modal_cancel_text' style={{ color: color, fontWeight: 'bold' }}>{t('feature.common.picker_cancel_btn')}</Text>
  224. </View>
  225. <View className='btn_space' />
  226. <View className='modal_btn' style={{ backgroundColor: color, flexDirection: 'row', alignItems: 'center', justifyContent: 'center', opacity: props.isLoading ? 0.6 : 1 }} onClick={confirm}>
  227. {
  228. props.isLoading && process.env.TARO_ENV == 'rn' && <View style={{ display: 'flex', overflow: 'hidden', height: 20, marginRight: 5 }}><AtActivityIndicator mode="center" color="#000" /></View>
  229. }
  230. <Text className='modal_confirm_text' style={{ color: '#000', fontWeight: 'bold' }}>{t('feature.common.picker_confirm_btn')}</Text>
  231. </View>
  232. </View>
  233. </View>
  234. }
  235. return pickerDetail()
  236. })
  237. export default React.memo(Component);