calendar.tsx 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385
  1. import { View, Text, Image } from "@tarojs/components";
  2. import './calendar.scss'
  3. import dayjs from "dayjs";
  4. import { useEffect, useState } from "react";
  5. import { rpxToPx } from "@/utils/tools";
  6. import { streaks } from "@/services/health";
  7. import { useSelector } from "react-redux";
  8. import { getThemeColor } from "./hooks/health_hooks";
  9. import NewButton, { NewButtonType } from "@/_health/base/new_button";
  10. import { MainColorType } from "@/context/themes/color";
  11. import { useTranslation } from "react-i18next";
  12. export default function Calendar(props: { year: number, month: number,mode:string }) {
  13. const weeks = ['日', '一', '二', '三', '四', '五', '六']
  14. const indexBeginWeek = 0;
  15. const [spaces, setSpaces] = useState<any>([])
  16. const [days, setDays] = useState<any>([])
  17. const health = useSelector((state: any) => state.health);
  18. const [year, setYear] = useState(props.year)
  19. const [month, setMonth] = useState(props.month)
  20. const [current, setCurrent] = useState<any>(null)
  21. const [loaded, setLoaded] = useState(false)
  22. const { t } = useTranslation()
  23. useEffect(() => {
  24. console.log('------------------')
  25. console.log(props.mode)
  26. loadData()
  27. }, [month,props.mode])
  28. // useEffect(()=>{
  29. // setYear(props.year)
  30. // setMonth(props.month)
  31. // // loadData(props.year,props.mo)
  32. // },[props.year,props.month,props.mode])
  33. function loadData() {
  34. const firstDay = new Date(year, month - 1, 1);
  35. const firstDayOfWeek = firstDay.getDay();
  36. const spaceCount = firstDayOfWeek > indexBeginWeek ? firstDayOfWeek - indexBeginWeek : firstDayOfWeek - indexBeginWeek + 7
  37. setSpaces(new Array(spaceCount).fill(''))
  38. // 创建一个 Date 对象来获取该月的最后一天
  39. const lastDay = new Date(year, month, 0); // 0 表示上一个月的最后一天
  40. const totalDays = lastDay.getDate();
  41. var list: any = []
  42. for (var i = 1; i <= totalDays; i++) {
  43. var obj: any = {
  44. day: i
  45. }
  46. // if (i == 1 || i == 10 || i == 15) {
  47. // obj.begin = true
  48. // }
  49. // if (i == 2 || i == 11 || i == 16) {
  50. // obj.right = true
  51. // }
  52. // if (i == 22) {
  53. // obj.full = true
  54. // }
  55. list.push(obj)
  56. }
  57. setDays(list)
  58. streaks({
  59. window: props.mode,
  60. month: year + (month + '').padStart(2, '0')
  61. }).then(res => {
  62. const array = (res as any).streaks
  63. for (var i = 0; i < list.length; i++) {
  64. var obj = list[i]
  65. var strMonth = month < 10 ? '0' + month : month + ''
  66. var strDay = obj.day < 10 ? '0' + obj.day : obj.day
  67. const d = parseInt(year + '' + strMonth + strDay)
  68. for (var j = 0; j < array.length; j++) {
  69. const streak = array[j]
  70. if (d == streak.start_date && streak.start_date == streak.end_date) {
  71. obj.full = true
  72. obj.current = streak.is_current
  73. obj.showStreak = true;
  74. }
  75. else if (d == streak.start_date && streak.start_date < streak.end_date) {
  76. obj.begin = true
  77. obj.current = streak.is_current
  78. obj.showStreak = true;
  79. }
  80. else if (d == streak.end_date && streak.start_date < streak.end_date) {
  81. obj.right = true
  82. obj.current = streak.is_current
  83. obj.showStreak = true;
  84. }
  85. else if (streak.start_date < d && d < streak.end_date) {
  86. obj.center = true
  87. obj.current = streak.is_current
  88. obj.showStreak = true;
  89. }
  90. }
  91. obj.isToday = false
  92. if (year == new Date().getFullYear() && month == new Date().getMonth() + 1 && obj.day == new Date().getDate()) {
  93. obj.isToday = true
  94. }
  95. }
  96. setDays([...list])
  97. setLoaded(true)
  98. setCurrent((res as any).current)
  99. // console.log(list)
  100. }).catch(e => {
  101. })
  102. }
  103. function itemClass(item) {
  104. if (item.begin) {
  105. if (item.isToday) {
  106. return 'left_calendar_item today_content'
  107. }
  108. return 'left_calendar_item'
  109. }
  110. if (item.center) {
  111. if (item.isToday) {
  112. return 'center_calendar_item today_content'
  113. }
  114. return 'center_calendar_item'
  115. }
  116. if (item.right) {
  117. if (item.isToday) {
  118. return 'right_calendar_item today_content'
  119. }
  120. return 'right_calendar_item'
  121. }
  122. if (item.full) {
  123. // if (item.isToday) {
  124. // return 'full_calendar_item today_content'
  125. // }
  126. return 'full_calendar_item'
  127. }
  128. // if (item.isToday) {
  129. // return 'today1'
  130. // }
  131. return 'calendar_item'
  132. }
  133. function itemTextClass(item) {
  134. if (item.begin) {
  135. return 'left_day'
  136. }
  137. if (item.center) {
  138. return 'center_day'
  139. }
  140. if (item.right) {
  141. return 'right_day'
  142. }
  143. if (item.full) {
  144. if (item.isToday) {
  145. return 'normal_day'
  146. }
  147. return 'full_day'
  148. }
  149. return 'normal_day'
  150. }
  151. function bgColor(item) {
  152. if (item.showStreak && !item.current) {
  153. return '#B2B2B21A'
  154. }
  155. if (itemClass(item) == 'calendar_item') {
  156. return 'transparent'
  157. }
  158. if (item.isToday) {
  159. if (item.full) {
  160. return 'transparent'
  161. }
  162. }
  163. // if (item.isToday) {
  164. // return '#000'
  165. // }
  166. return getThemeColor(props.mode) + '33'
  167. }
  168. function textColor(item) {
  169. // if (itemTextClass(item) == 'normal_day') {
  170. // return '#000000'
  171. // }
  172. if (item.isToday) {
  173. if (item.begin || item.full || item.right || item.left) {
  174. return '#000'
  175. }
  176. return '#ffffff'
  177. }
  178. return '#000000'
  179. // return getThemeColor(props.mode)
  180. }
  181. function leftSpace(days) {
  182. if (days < 10) {
  183. return rpxToPx(98)
  184. }
  185. else if (days < 100) {
  186. return rpxToPx(71)
  187. }
  188. else if (days < 1000) {
  189. return rpxToPx(54)
  190. }
  191. }
  192. function getYearMonth() {
  193. const date = new Date(year, month - 1);
  194. if (global.language == 'en') {
  195. return dayjs(date.getTime()).format('MMMM YYYY')
  196. }
  197. return dayjs(date.getTime()).format('YYYY年MMM')
  198. }
  199. function content() {
  200. return <View className="calendar_main2">
  201. <View className="calendar_header">
  202. <NewButton type={NewButtonType.img} onClick={() => {
  203. const date = new Date(year, month - 1); // month - 1 因为月份是从 0 开始
  204. date.setMonth(date.getMonth() - 1);
  205. setYear(date.getFullYear())
  206. setMonth(date.getMonth() + 1)
  207. }}>
  208. <Image src={require('@assets/_health/pre.png')} style={{ width: rpxToPx(36), height: rpxToPx(36) }} />
  209. </NewButton>
  210. <Text style={{ width: rpxToPx(236), textAlign: 'center', fontSize: rpxToPx(28) }}>{getYearMonth()}</Text>
  211. <NewButton type={NewButtonType.img} onClick={() => {
  212. const date = new Date(year, month + 1); // month - 1 因为月份是从 0 开始
  213. date.setMonth(date.getMonth() - 1);
  214. setYear(date.getFullYear())
  215. setMonth(date.getMonth() + 1)
  216. }}>
  217. <Image src={require('@assets/_health/next.png')} style={{ width: rpxToPx(36), height: rpxToPx(36) }} />
  218. </NewButton>
  219. </View>
  220. <View className="calendar_body">
  221. <View className="calendar_weekly">
  222. {
  223. weeks.map((item, index) => {
  224. return <View className="week_item" key={index}>{item}</View>
  225. })
  226. }
  227. </View>
  228. <View className="calendar_main3">
  229. {
  230. spaces.map((item, i) => {
  231. return <View className="calendar_item" style={{ width: rpxToPx(94) }} key={i * 10} />
  232. })
  233. }
  234. {
  235. days.map((item, index) => {
  236. if (item.isToday && item.right) {
  237. return <View key={index} className="calendar_item" style={{ width: rpxToPx(94), position: 'relative' }}>
  238. <View className="full_right_today" style={{backgroundColor: getThemeColor(props.mode) + '33'}}/>
  239. <View className="fullToday">
  240. <View className="today3"/>
  241. </View>
  242. <Text >{item.day}</Text>
  243. </View>
  244. }
  245. return <View className={itemClass(item)} style={{ width: rpxToPx(94), backgroundColor: bgColor(item), position: 'relative' }} key={index}>
  246. {
  247. !item.full && item.isToday && <View className="today2" />
  248. }
  249. {
  250. item.full && item.isToday && <View className="fullToday" style={{ backgroundColor: getThemeColor(props.mode) + '33' }}>
  251. <View className="today3" />
  252. </View>
  253. }
  254. <Text className={itemTextClass(item)} style={{ color: textColor(item), zIndex: 1, fontWeight: item.isToday ? 'bold' : 'normal' }}>{item.day}</Text>
  255. </View>
  256. })
  257. }
  258. </View>
  259. </View>
  260. <View className="calendar_footer">
  261. </View>
  262. </View>
  263. }
  264. function logTitle() {
  265. switch (props.mode) {
  266. case 'FAST':
  267. return t('health.log_fast_streak')
  268. case 'EAT':
  269. return t('health.log_eat_streak')
  270. case 'SLEEP':
  271. return t('health.log_sleep_streak')
  272. case 'ACTIVE':
  273. return t('health.log_active_streak')
  274. }
  275. return ''
  276. }
  277. function streakTitle() {
  278. var year = parseInt((current.start_date + '').substring(0, 4))
  279. var month = parseInt((current.start_date + '').substring(4, 6)) - 1
  280. var date = parseInt((current.start_date + '').substring(6, 8))
  281. var dt = new Date(year, month, date)
  282. var format = global.language == 'en' ? 'MMM D' : 'M月D日'
  283. return t('health.since_date', { date: dayjs(dt.getTime()).format(format) })
  284. }
  285. function todayExpire() {
  286. if (!current.reset) return false
  287. var date = new Date(current.reset)
  288. if (date.getDate() == new Date().getDate()) {
  289. return true;
  290. }
  291. return false;
  292. }
  293. function streakDesc() {
  294. var format = global.language == 'en' ? 'MMM D' : 'M月D日'
  295. if (current.days == 0) {
  296. if (current.prev_reset) {
  297. return t('health.last_expire_date_time', {
  298. date: dayjs(current.prev_reset).format(format),
  299. time: dayjs(current.prev_reset).format('HH:mm'),
  300. })
  301. }
  302. }
  303. else {
  304. if (todayExpire()) {
  305. return t('health.expire_date_time', {
  306. date: dayjs(current.reset).format(format),
  307. time: dayjs(current.reset).format('HH:mm'),
  308. })
  309. }
  310. else {
  311. return t('health.until_date_time', {
  312. date: dayjs(current.reset).format(format),
  313. time: dayjs(current.reset).format('HH:mm'),
  314. })
  315. }
  316. }
  317. return ''
  318. }
  319. function currentContent() {
  320. return <View style={{ display: 'flex', flexDirection: 'row', height: rpxToPx(208) }}>
  321. {
  322. loaded && <View style={{ display: 'flex', flexDirection: 'row' }}>
  323. <View className="streak_days" style={{
  324. color: getThemeColor(props.mode),
  325. marginLeft: leftSpace(current ? current.days : 0),
  326. marginRight: rpxToPx(36),
  327. marginTop: rpxToPx(24)
  328. }}>{current ? current.days : '0'}</View>
  329. <View style={{ display: 'flex', flexDirection: 'column', marginTop: rpxToPx(36) }}>
  330. <View className="h44 bold" style={{ color: getThemeColor(props.mode) }}>{current.days == 1 ? t('health.day') : t('health.days')}</View>
  331. <View className="h24">{current.days == 0 ? logTitle() : streakTitle()}</View>
  332. <View className="h20" style={{ color: todayExpire() ? MainColorType.error : MainColorType.g02, marginTop: rpxToPx(12) }}>{streakDesc()}</View>
  333. </View>
  334. </View>
  335. }
  336. </View>
  337. }
  338. return <View className="calendar_main">
  339. {
  340. currentContent()
  341. }
  342. {
  343. content()
  344. }
  345. </View>
  346. }