Leon 1 năm trước cách đây
mục cha
commit
f47e053046

+ 1 - 0
src/_eat/pages/meal_list.tsx

@@ -40,6 +40,7 @@ export default function MealList() {
         delSchedule(list[index].id).then(res => {
             schedules()
             global.refreshWindow()
+            global.refreshHistory()
         })
     }
 

BIN
src/assets/images/add3.png


Những thai đổi đã bị hủy bỏ vì nó quá lớn
+ 3 - 0
src/components/basic/Icons.tsx


+ 68 - 4
src/features/health/History.scss

@@ -1,15 +1,66 @@
-.history_item{
+.history_item {
     display: flex;
     flex-direction: row;
-    align-items: center;
     width: 750px;
+    padding-top: 28px;
     padding-left: 40px;
     padding-right: 40px;
     box-sizing: border-box;
-    
+    padding-bottom: 40px;
+    position: relative;
+    background-color: #fff;
+
 }
 
-.no_more{
+.history_ring {
+    position: relative;
+    width: 76px;
+    height: 76px;
+    margin-right: 22px;
+    align-items: center;
+    justify-content: center;
+}
+
+.history_date {
+    position: absolute;
+    left: 0;
+    right: 0;
+    top: 0;
+    bottom: 0;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    color: #B2B2B2;
+    font-size: 30px;
+    font-weight: bold;
+}
+
+.history_content {
+    display: flex;
+    flex-direction: column;
+}
+
+.history_item_duration {
+    font-size: 34px;
+    line-height: 42px;
+    font-weight: bold;
+    margin-bottom: 8px;
+}
+
+.history_item_title {
+    font-size: 32px;
+    line-height: 44px;
+    font-weight: bold;
+    color: #000;
+}
+
+.history_item_desc {
+    font-size: 32px;
+    line-height: 44px;
+    color: #000;
+}
+
+.no_more {
     display: flex;
     align-items: center;
     justify-content: center;
@@ -17,4 +68,17 @@
     text-align: center;
     margin-top: 20px;
     color: #B2B2B2;
+}
+
+.media {
+    display: flex;
+    flex-direction: row;
+    flex-wrap: wrap;
+}
+
+.media_item {
+    width: 157px;
+    height: 157px;
+    margin-right: 2px;
+    margin-bottom: 2px;
 }

+ 81 - 27
src/features/health/HistoryItem.tsx

@@ -1,46 +1,100 @@
 import formatMilliseconds from "@/utils/format_time";
 import { View, Text, Image } from "@tarojs/components";
+import './History.scss'
 import Taro from "@tarojs/taro";
 import dayjs from "dayjs";
+import { useSelector } from "react-redux";
+import { getThemeColor } from "./hooks/health_hooks";
+import Rings, { RingCommon, BgRing, TargetRing, CurrentDot } from "@/features/trackTimeDuration/components/Rings";
+import { MainColorType } from "@/context/themes/color";
+import { durationArc, startArc } from "./util";
 
 export default function HistoryItem(props: { data: any, index: number }) {
-
+    const health = useSelector((state: any) => state.health);
     function preview(obj) {
+        var list:any = []
+        props.data.events.map((item) => {
+            item.moment.media.map((media) => {
+                list.push(media.url)
+            })
+        })
+
         Taro.previewImage({
             current: obj.url,
-            urls: [obj.url]
+            urls: list
         })
     }
 
-    return <View style={{display:'flex',flexDirection:'column'}}>
-        <Text style={{color:'blue'}}>Date:{dayjs(props.data.window_range.start_timestamp).format('YYYY-MM-DD')}</Text>
-        {
-            <Text style={{color:'blue'}}>开始时间:{dayjs(props.data.window_range.start_timestamp).format('MM-DD HH:mm')}</Text>
+    function getTitle(item) {
+        if (item.moment) {
+            return item.moment.title
+        }
+        if (health.mode == 'FAST') {
+            return '开始断食'
         }
-        {
-            props.data.window_range.end_timestamp && <Text style={{color:'blue'}}>结束时间:{dayjs(props.data.window_range.end_timestamp).format('MM-DD HH:mm')}</Text>
+        else if (health.mode == 'SLEEP') {
+            return '开始睡眠'
         }
-        {
-            props.data.window_range.end_timestamp && <Text style={{color:'blue'}}>时长:{formatMilliseconds(props.data.window_range.end_timestamp-props.data.window_range.start_timestamp)}</Text>
+        return ''
+    }
+
+    function ring() {
+        const common: RingCommon = {
+            useCase: 'ChooseScenario',
+            radius: 17,
+            lineWidth: 3,
+            isFast: true,
+            status: 'WAIT_FOR_START'
+        }
+
+        const bgRing: BgRing = {
+            color: MainColorType.ringBg
         }
-        {
-            props.data.events.map((item, i) => {
-                return <View key={i} >
-                    
-                    <View style={{ flexDirection: 'column', display: 'flex' }}>
-                        <Text>Time:{dayjs(item.real.start_timestamp).format('HH:mm')}</Text>
-                        <Text>{item.moment.title}</Text>
-                        <Text>{item.moment.description}</Text>
-                        <View>
+
+        const realRing = {
+            hideBg:true,
+            color:getThemeColor(health.mode),
+            startArc:startArc(props.data.window_range.start_timestamp),
+            durationArc:durationArc(props.data.window_range.start_timestamp,props.data.window_range.end_timestamp)
+        }
+
+
+        return <Rings common={common} bgRing={bgRing} realRing={realRing} canvasId={'history_' + props.index} />
+    }
+
+    return <View className="history_item">
+        <View className="history_ring">
+            {
+                ring()
+            }
+            <View className="history_date">{dayjs(props.data.window_range.start_timestamp).format('D')}</View>
+        </View>
+        <View className="history_content">
+            {
+                props.data.window_range.end_timestamp && <Text className="history_item_duration" style={{ color: getThemeColor(health.mode) }}>{formatMilliseconds(props.data.window_range.end_timestamp - props.data.window_range.start_timestamp)}</Text>
+            }
+            <Text>
+                {
+                    props.data.events.map((item, index) => {
+                        return <Text key={index}>
+                            <Text className="history_item_title">{dayjs(item.real.start_timestamp).format('HH:mm')} {getTitle(item)} </Text>
                             {
-                                item.moment.media.map((obj, j) => {
-                                    return <Image onClick={() => preview(obj)} src={obj.url} key={j * 10} style={{ width: 100, height: 100 }} />
-                                })
+                                item.moment && item.moment.description && <Text className="history_item_desc">{item.moment.description}</Text>
                             }
-                        </View>
-                    </View>
-                </View>
-            })
-        }
+                        </Text>
+                    })
+                }
+            </Text>
+            <View className="media" style={{ marginTop: 9 }}>
+                {
+                    props.data.events.map((item) => {
+                        return item.moment.media.map((obj, j) => {
+                            return <Image className="media_item" mode="aspectFill" onClick={() => preview(obj)} src={obj.url} key={j * 10} />
+                        })
+                    })
+                }
+            </View>
+        </View>
+        <View className="border_footer_line" />
     </View>
 }

+ 17 - 4
src/features/health/MainConsole.scss

@@ -2,23 +2,30 @@
     display: flex;
     flex-direction: row;
     align-items: center;
-    justify-content: space-between;
     padding-right: 40px;
     position: relative;
     margin-left: 64px;
-    height: 128px;
+    padding-top: 32px;
+    padding-bottom: 32px;
 }
 
 .timeline_left{
     display: flex;
     flex-direction: column;
-
+    flex:1;
 }
 
 .timeline_title{
     font-size: 24px;
     color: #808080;
-
+    overflow: hidden;
+    width: 500px;
+    white-space: nowrap;
+    display: -webkit-box;
+    -webkit-box-orient: vertical;
+    overflow: hidden;
+    -webkit-line-clamp: 1; // 限制为一行
+    text-overflow: ellipsis; // 超出部分显示省略号
 }
 
 .timeline_time{
@@ -97,3 +104,9 @@
     font-size: 24px;
 }
 
+.console_item_img{
+    width: 72px;
+    height: 72px;
+    border-radius: 36px;
+}
+

+ 38 - 8
src/features/health/MainConsole.tsx

@@ -1,4 +1,4 @@
-import { WindowType } from "@/utils/types";
+import { WindowStatusType, WindowType } from "@/utils/types";
 import { View, Text, Image } from "@tarojs/components";
 import dayjs from "dayjs";
 import { useEffect, useRef, useState } from "react";
@@ -110,12 +110,15 @@ export default function MainConsole(props: { type: WindowType }) {
         if (health.mode == 'DAY' || health.mode == 'NIGHT') {
             return item.title
         }
+        if (item.real) {
+            return dayjs(item.real.timestamp).format('HH:mm')
+        }
         return dayjs(item.target.timestamp).format('HH:mm')
     }
 
     function itemValue(item: any) {
         let themeColor: any = getThemeColor()
-        const scenario = getScenario(health.windows,health.mode)
+        const scenario = getScenario(health.windows, health.mode)
         if (item.action == 'END' && !scenario.real) {
             themeColor = '#B2B2B2'
         }
@@ -131,13 +134,22 @@ export default function MainConsole(props: { type: WindowType }) {
         return <View key={index} className="timeline_item" >
             <View className="timeline_left">
                 {
-                    health.mode != 'DAY' && health.mode != 'NIGHT' && <Text className="timeline_title">{item.title}</Text>
+                    health.mode != 'DAY' && health.mode != 'NIGHT' && <Text className="timeline_title">{itemTitle(item)}</Text>
                 }
                 <View style={{ display: 'flex', flexDirection: 'row', alignItems: 'center' }}>
-                    <Text className="timeline_time" onClick={() => edit(item)}>{itemTitle(item)}</Text>
+                    <Text className="timeline_time" onClick={() => edit(item)}>{health.mode != 'DAY' && health.mode != 'NIGHT' ? item.title : itemTitle(item)}</Text>
                 </View>
+                {
+                    item.moment && item.moment.description && <Text className="timeline_title">{item.moment.description}</Text>
+                }
 
             </View>
+            {
+                item.moment && item.moment.media && item.moment.media.length > 0 && <Image
+                    src={item.moment.media[0].url}
+                    mode="aspectFill"
+                    className="console_item_img" />
+            }
             {
                 itemValue(item)
             }
@@ -198,6 +210,7 @@ export default function MainConsole(props: { type: WindowType }) {
                 }
                 updateTarget(t, obj.event_id).then(res => {
                     global.refreshWindow()
+                    global.refreshHistory()
                     setShowPicker(false)
                 })
                 return;
@@ -213,6 +226,7 @@ export default function MainConsole(props: { type: WindowType }) {
                 }
                 updateTarget(t, obj.event_id).then(res => {
                     global.refreshWindow()
+                    global.refreshHistory()
                     setShowPicker(false)
                 })
                 return;
@@ -224,6 +238,7 @@ export default function MainConsole(props: { type: WindowType }) {
             time: strTime
         }, selItem.schedule_id).then(res => {
             global.refreshWindow()
+            global.refreshHistory()
             setShowPicker(false)
         })
     }
@@ -301,6 +316,7 @@ export default function MainConsole(props: { type: WindowType }) {
             setBtnDisable(false)
             setShowTimePicker(false)
             global.refreshWindow()
+            global.refreshHistory()
         }).catch(e => {
             setBtnDisable(false)
         })
@@ -420,10 +436,23 @@ export default function MainConsole(props: { type: WindowType }) {
 
         }
     }
+
+    function windowStatus() {
+        var statusType = getWindowStatus(health.windows, health.mode)
+        switch (statusType) {
+            case WindowStatusType.open:
+                return 'New open'
+            case WindowStatusType.process:
+                return 'In process'
+            case WindowStatusType.upcoming:
+                return 'Upcoming'
+        }
+        return ''
+    }
     return <View className="main-console-bg">
         <Image className="main_arrow" src={require('@assets/images/center_arrow.png')} />
         <View className="main_summary">
-            <View className="main_summary_status" style={{ color: getThemeColor() }}>{getWindowStatus(health.windows, health.mode)}</View>
+            <View className="main_summary_status" style={{ color: getThemeColor() }}>{windowStatus()}</View>
             <Text className="main_summary_time">{getCountownTime(health.windows, health.mode)}</Text>
             <Text className="main_summary_duration">Total {getDuration(health.windows, health.mode)}</Text>
             <View className="border_footer_line" />
@@ -435,10 +464,11 @@ export default function MainConsole(props: { type: WindowType }) {
         </View>
         <View className="main_footer">
             <Text className="main_footer_text" onClick={tapSwitchBtn}>{switchText()}</Text>
+            {/* {
+                (health.mode == 'EAT' || health.mode == 'ACTIVE') && <Text style={{ display: 'flex', alignItems: 'center', justifyContent: 'center' }} onClick={more}>更多</Text>
+            } */}
         </View>
-        {
-            (health.mode == 'EAT' || health.mode == 'ACTIVE') && <Text style={{ display: 'flex', alignItems: 'center', justifyContent: 'center' }} onClick={more}>更多</Text>
-        }
+
         {
             showTimePicker && modalContent()
         }

+ 11 - 11
src/features/health/MainDayNightCard.tsx

@@ -11,10 +11,11 @@ import momentT from 'moment';
 import { MainColorType } from "@/context/themes/color";
 import { useDispatch, useSelector } from "react-redux";
 import Calendar from "@/features/health/calendar";
-import { WindowType } from "@/utils/types";
+import { WindowStatusType, WindowType } from "@/utils/types";
 import { durationArc, isCurrentTimeInRange, startArc } from "./util";
 import { setMode } from "@/store/health";
 import { IconSwitch1, IconSwitch2 } from "@/components/basic/Icons";
+import { getScenario, getWindowStatus } from "./hooks/health_hooks";
 
 export default function MainDayNightCard(props: { count: number, typeChanged: Function, id: number, onClick: Function }) {
     const [isDay, setIsDay] = useState(true)
@@ -97,6 +98,11 @@ export default function MainDayNightCard(props: { count: number, typeChanged: Fu
     }
 
     function getRealArc() {
+        const status = getWindowStatus(health.windows,isDayMode?'DAY':'NIGHT')
+        if (status == WindowStatusType.upcoming){
+            const scenario = getScenario(health.windows,isDayMode?'DAY':'NIGHT')
+            return durationArc(new Date().getTime(),scenario.target.start_timestamp)
+        }
         const { day, night } = health.windows.night_day
         if (isDayMode) {
             return durationArc(day.target.start_timestamp, new Date().getTime())
@@ -132,6 +138,10 @@ export default function MainDayNightCard(props: { count: number, typeChanged: Fu
 
         var targetColor: string = isDayMode ? MainColorType.dayLight : MainColorType.nightLight
         var realColor: string = isDayMode ? MainColorType.day : MainColorType.night
+        const status = getWindowStatus(health.windows,isDayMode?'DAY':'NIGHT')
+        if (status == WindowStatusType.upcoming){
+            realColor = '#CCCCCC'
+        }
 
         const targetRing: TargetRing = {
             color: targetColor,
@@ -166,16 +176,6 @@ export default function MainDayNightCard(props: { count: number, typeChanged: Fu
         props.typeChanged(mode ? WindowType.day : WindowType.night)
     }
 
-    function timeStatus() {
-        if (isDay && isDayMode) {
-            return 'In Progress'
-        }
-        else if (!isDay && !isDayMode) {
-            return 'In Progress'
-        }
-        return 'Coming up'
-    }
-
     function switchIcon() {
         if (isDayMode) {
             if (isDay) {

+ 35 - 67
src/features/health/MainFastEatCard.tsx

@@ -18,10 +18,11 @@ import showAlert from "@/components/basic/Alert";
 import showActionSheet from "@/components/basic/ActionSheet";
 import { records } from "@/services/health";
 import MainHistory from "./MainHistory";
-import { WindowType } from "@/utils/types";
-import { isCurrentTimeInRange } from "./util";
+import { WindowStatusType, WindowType } from "@/utils/types";
+import { durationArc, isCurrentTimeInRange, startArc } from "./util";
 import { setMode } from "@/store/health";
 import { IconSwitch1, IconSwitch2 } from "@/components/basic/Icons";
+import { getScenario, getWindowStatus } from "./hooks/health_hooks";
 let useNavigation;
 
 let Linking, PushNotification;
@@ -39,7 +40,7 @@ if (process.env.TARO_ENV == 'rn') {
     checkNotification = require('@/utils/native_permission_check').checkNotification;
     useActionSheet = require('@expo/react-native-action-sheet').useActionSheet
 }
-export default function MainFastEatCard(props: { count: any, typeChanged: Function,id:number,onClick:Function }) {
+export default function MainFastEatCard(props: { count: any, typeChanged: Function, id: number, onClick: Function }) {
     const [isFastMode, setIsFastMode] = useState(true)
 
     const [showModal, setShowModal] = useState(false)
@@ -67,14 +68,14 @@ export default function MainFastEatCard(props: { count: any, typeChanged: Functi
         showActionSheetWithOptions = useActionSheet()
     }
 
-    useEffect(()=>{
-        if (health.mode == 'FAST'){
+    useEffect(() => {
+        if (health.mode == 'FAST') {
             setIsFastMode(true)
         }
-        else if (health.mode == 'EAT'){
+        else if (health.mode == 'EAT') {
             setIsFastMode(false)
         }
-    },[health.mode])
+    }, [health.mode])
 
     useEffect(() => {
         const { fast, eat } = health.windows.fast_eat
@@ -187,67 +188,34 @@ export default function MainFastEatCard(props: { count: any, typeChanged: Functi
         }
     }
 
+    function getRealArc(time) {
+        var date = new Date(time);
+        var hour = date.getHours();
+        var minute = date.getMinutes();
+        var second = date.getSeconds();
+        return (hour * 3600 + minute * 60 + second) / (24 * 3600) * 2 * Math.PI - Math.PI / 2.0;
+    }
 
+    function getRealDurationArc(start, end) {
+        var duration = (end - start) / 1000;
+        return duration / (24 * 3600) * 2 * Math.PI;
+    }
 
     function realRing() {
-        const { fast, eat } = health.windows.fast_eat
-        if (isFastMode) {
-            if (status != 'upcoming') {
-
-
-
-                // var starts: any = fast.period.start_time.split(':')
-                // var ends: any = fast.period.end_time.split(':')
-
-                var starts: any = startTime ? startTime.split(':') : fast.period.start_time.split(':')
-                const startSeconds = parseInt(starts[0] + '') * 3600 + parseInt(starts[1] + '') * 60
-
-                const color = MainColorType.fast
-                var startArc = startSeconds / (1440 * 60) * 2 * Math.PI - Math.PI / 2
-                var endSeconds = new Date().getHours() * 3600 + new Date().getMinutes() * 60 + new Date().getSeconds()
-                if (endTime) {
-                    var ends: any = endTime.split(':')
-                    endSeconds = parseInt(ends[0] + '') * 3600 + parseInt(ends[1] + '') * 60
-                }
-                const fastCount = endSeconds - startSeconds > 0 ? endSeconds - startSeconds : endSeconds - startSeconds + 1440 * 60
-                var durationArc = fastCount / (1440 * 60) * 2 * Math.PI
-
-                if (status == 'process') {
-                    var dt = new Date(fastData.target.start_time)
-                    var realSeconds = dt.getHours() * 3600 + dt.getMinutes() * 60 + dt.getSeconds()
-                    startArc = realSeconds / (1440 * 60) * 2 * Math.PI - Math.PI / 2
-                    durationArc = ((new Date().getTime() - fastData.target.start_time) / 1000) / (1440 * 60) * 2 * Math.PI
-                }
-
-                return {
-                    color,
-                    startArc,
-                    durationArc,
-                    radius: status == 'process' ? 90 : null,
-                    lineWidth: status == 'process' ? 15 : null
-                }
+        const status = getWindowStatus(health.windows, isFastMode ? 'FAST' : 'EAT')
+        const scenario = getScenario(health.windows, isFastMode ? 'FAST' : 'EAT')
+
+        if (status == WindowStatusType.upcoming) {
+            return {
+                color: '#cccccc',
+                startArc: getRealArc(new Date().getTime()),
+                durationArc: getRealDurationArc(new Date().getTime(), scenario.target.start_timestamp),
             }
         }
-        else {
-            if (isCurrentTimeInRange(fast.period.end_time, fast.period.start_time)) {
-                var starts: any = fast.period.end_time.split(':')
-                const startSeconds = parseInt(starts[0] + '') * 60 + parseInt(starts[1] + '')
-
-                const color = MainColorType.eat
-                const startArc = startSeconds / 1440 * 2 * Math.PI - Math.PI / 2
-                var endSeconds = new Date().getHours() * 60 + new Date().getMinutes()
-
-                const fastCount = endSeconds - startSeconds > 0 ? endSeconds - startSeconds : endSeconds - startSeconds + 1440
-                const durationArc = fastCount / 1440 * 2 * Math.PI
-
-                return {
-                    color,
-                    startArc,
-                    durationArc,
-                    radius: 90,
-                    lineWidth: 10
-                }
-            }
+        return {
+            color: isFastMode ? MainColorType.fast : MainColorType.eat,
+            startArc: getRealArc(scenario.target.start_timestamp),
+            durationArc: getRealDurationArc(scenario.target.start_timestamp, new Date().getTime())
         }
     }
 
@@ -311,7 +279,7 @@ export default function MainFastEatCard(props: { count: any, typeChanged: Functi
         //     whiteIcon: true
         // }
 
-        return <Rings common={common} bgRing={bgRing} scheduleRing={scheduleRing()} targetRing={targetRing()} realRing={realRing()} canvasId={'smal11l'+props.id} />
+        return <Rings common={common} bgRing={bgRing} scheduleRing={scheduleRing()} targetRing={targetRing()} realRing={realRing()} canvasId={'smal11l' + props.id} />
     }
 
     function formatTime(format: string, timestamp?: number) {
@@ -641,14 +609,14 @@ export default function MainFastEatCard(props: { count: any, typeChanged: Functi
     if (!fastData)
         return <View />
 
-    return <View style={{ alignItems: 'center', display: 'flex', flexDirection: 'column', width: rpxToPx(750/3), flexShrink: 0 }} onClick={()=>props.onClick()}>
+    return <View style={{ alignItems: 'center', display: 'flex', flexDirection: 'column', width: rpxToPx(750 / 3), flexShrink: 0 }} onClick={() => props.onClick()}>
 
-        <View style={{ width: rpxToPx(750/3), }} />
+        <View style={{ width: rpxToPx(750 / 3), }} />
         <View style={{ position: 'relative', }}>
             {
                 ring()
             }
-            <View className={health.selTab==1?'window_name window_name_sel':'window_name'}>{isFastMode ? 'Fast' : 'Eat'}</View>
+            <View className={health.selTab == 1 ? 'window_name window_name_sel' : 'window_name'}>{isFastMode ? 'Fast' : 'Eat'}</View>
             {/* <View className="ring_center">
                 {
                     isFastMode && <Text>{getFastStatus()}</Text>

+ 1 - 1
src/features/health/MainHistory.tsx

@@ -69,7 +69,7 @@ export default function MainHistory(props: { type: string }) {
     if (!loaded)
         return <View />
 
-    return <View style={{ width: rpxToPx(750) }}>
+    return <View style={{ width: rpxToPx(750),marginTop:rpxToPx(35) }}>
         {/* <Calendar year={2024} month={8}/> */}
         {/* <View style={{
             // position: 'sticky',

+ 26 - 59
src/features/health/MainSleepActiveCard.tsx

@@ -8,10 +8,11 @@ import moment from 'moment-timezone'
 import { MainColorType } from "@/context/themes/color";
 import { useDispatch, useSelector } from "react-redux";
 import { sleepWindow } from "@/services/trackTimeDuration";
-import { WindowType } from "@/utils/types";
+import { WindowStatusType, WindowType } from "@/utils/types";
 import { durationArc, isCurrentTimeInRange, startArc } from "./util";
 import { setMode } from "@/store/health";
 import { IconSwitch1, IconSwitch2 } from "@/components/basic/Icons";
+import { getScenario, getWindowStatus } from "./hooks/health_hooks";
 
 export default function MainSleepActiveCard(props: { count: any, typeChanged: Function, id: number, onClick: Function }) {
     const [isSleepMode, setIsSleepMode] = useState(true)
@@ -92,13 +93,6 @@ export default function MainSleepActiveCard(props: { count: any, typeChanged: Fu
         return durationArc(active.target.start_timestamp, active.target.end_timestamp)
     }
 
-    function getRealArc() {
-        const { sleep, active } = health.windows.sleep_active
-        if (isSleepMode) {
-            return durationArc(sleep.target.start_timestamp, new Date().getTime())
-        }
-        return durationArc(active.target.start_timestamp, new Date().getTime())
-    }
 
     function targetRing() {
         const color = isSleepMode ? '#D0C0FB' : '#FF498366'
@@ -112,62 +106,35 @@ export default function MainSleepActiveCard(props: { count: any, typeChanged: Fu
         }
     }
 
+    function getRealArc(time) {
+        var date = new Date(time);
+        var hour = date.getHours();
+        var minute = date.getMinutes();
+        var second = date.getSeconds();
+        return (hour * 3600 + minute * 60 + second) / (24 * 3600) * 2 * Math.PI - Math.PI / 2.0;
+    }
+
+    function getRealDurationArc(start, end) {
+        var duration = (end - start) / 1000;
+        return duration / (24 * 3600) * 2 * Math.PI;
+    }
+
     function realRing() {
-        const color = isSleepMode ? MainColorType.sleep : MainColorType.active
-        const { sleep, active } = health.windows.sleep_active
-        if (isSleepMode) {
-            if (!isCurrentTimeInRange(sleep.period.start_time, sleep.period.end_time)) {
-                return null
-            }
-        }
-        else {
-            if (!isCurrentTimeInRange(active.period.start_time, active.period.end_time)) {
-                return null
+        const status = getWindowStatus(health.windows, isSleepMode ? 'SLEEP' : 'ACTIVE')
+        const scenario = getScenario(health.windows, isSleepMode ? 'SLEEP' : 'ACTIVE')
+
+        if (status == WindowStatusType.upcoming) {
+            return {
+                color: '#cccccc',
+                startArc: getRealArc(new Date().getTime()),
+                durationArc: getRealDurationArc(new Date().getTime(), scenario.target.start_timestamp),
             }
         }
         return {
-            color: color,
-            startArc: getArc(),//-Math.PI / 2,
-            durationArc: getRealArc()
+            color: isSleepMode ? MainColorType.sleep : MainColorType.active,
+            startArc: getRealArc(scenario.target.start_timestamp),
+            durationArc: getRealDurationArc(scenario.target.start_timestamp, new Date().getTime())
         }
-        // if (isSleepMode) {
-        //     if (isCurrentTimeInRange(startScheduleTime, endScheduleTime)) {
-        //         var starts: any = startScheduleTime.split(':')
-        //         const startSeconds = parseInt(starts[0] + '') * 60 + parseInt(starts[1] + '')
-
-        //         const color = MainColorType.sleep
-        //         const startArc = startSeconds / 1440 * 2 * Math.PI - Math.PI / 2
-        //         var endSeconds = new Date().getHours() * 60 + new Date().getMinutes()
-
-        //         const fastCount = endSeconds - startSeconds > 0 ? endSeconds - startSeconds : endSeconds - startSeconds + 1440
-        //         const durationArc = fastCount / 1440 * 2 * Math.PI
-
-        //         return {
-        //             color,
-        //             startArc,
-        //             durationArc
-        //         }
-        //     }
-        // }
-        // else {
-        //     if (isCurrentTimeInRange(endScheduleTime, startScheduleTime)) {
-        //         var starts: any = endScheduleTime.split(':')
-        //         const startSeconds = parseInt(starts[0] + '') * 60 + parseInt(starts[1] + '')
-
-        //         const color = MainColorType.active
-        //         const startArc = startSeconds / 1440 * 2 * Math.PI - Math.PI / 2
-        //         var endSeconds = new Date().getHours() * 60 + new Date().getMinutes()
-
-        //         const fastCount = endSeconds - startSeconds > 0 ? endSeconds - startSeconds : endSeconds - startSeconds + 1440
-        //         const durationArc = fastCount / 1440 * 2 * Math.PI
-
-        //         return {
-        //             color,
-        //             startArc,
-        //             durationArc
-        //         }
-        //     }
-        // }
     }
 
     function ring() {

+ 2 - 2
src/features/health/Streak.scss

@@ -3,7 +3,7 @@
     position: fixed;
     top: -2px;
     left: 0;
-    z-index: 100000;
+    z-index: 10;
     right: 0;
     bottom: 0;
     overflow: hidden;
@@ -17,7 +17,7 @@
     position: fixed;
     top: -2px;
     left: 0;
-    z-index: 100000;
+    z-index: 10;
     right: 0;
     bottom: 0;
     overflow: hidden;

+ 39 - 15
src/features/health/hooks/health_hooks.tsx

@@ -1,4 +1,6 @@
+import { MainColorType } from "@/context/themes/color"
 import { TimeFormatter } from "@/utils/time_format"
+import { WindowStatusType } from "@/utils/types"
 
 export function getScenario(windows: any, mode: string) {
     switch (mode) {
@@ -23,18 +25,40 @@ export function getScenario(windows: any, mode: string) {
     }
 }
 
-export function getWindowStatus(windows: any, mode: any) {
+export function getThemeColor(mode: string) {
+    switch (mode) {
+        case 'FAST':
+            return MainColorType.fast
+
+        case 'EAT':
+            return MainColorType.eat
+
+        case 'DAY':
+            return MainColorType.day
+
+        case 'NIGHT':
+            return MainColorType.night
+
+        case 'SLEEP':
+            return MainColorType.sleep
+
+        case 'ACTIVE':
+            return MainColorType.active
+
+    }
+    return MainColorType.day
+}
+
+export function getWindowStatus(windows: any, mode: any): WindowStatusType {
     const scenario = getScenario(windows, mode)
     if (scenario.real) {
-        return 'In progress'
+        return WindowStatusType.process//'In progress'
     }
     const now = new Date().getTime()
-    if (now>=scenario.target.start_timestamp&& now<=scenario.target.end_timestamp){
-        return 'Now open'
+    if (now >= scenario.target.start_timestamp && now < scenario.target.end_timestamp) {
+        return WindowStatusType.open//'Now open'
     }
-    return 'Upcoming'
-
-    //否则按时间段来处理
+    return WindowStatusType.upcoming//'Upcoming'
 }
 
 export function getDuration(windows: any, mode: string) {
@@ -44,16 +68,16 @@ export function getDuration(windows: any, mode: string) {
 }
 
 export function getCountownTime(windows: any, mode: any) {
-    const status = getWindowStatus(windows,mode)
+    const status = getWindowStatus(windows, mode)
     const scenario = getScenario(windows, mode)
     const now = new Date().getTime()
-    switch (status){
-        case 'In progress':
-            return TimeFormatter.countdown(scenario.real.start_timestamp,now)
-        case 'Now open':
-            return TimeFormatter.countdown(scenario.target.start_timestamp,now)
-        case 'Upcoming':
-            return TimeFormatter.countdown(now,scenario.target.start_timestamp)
+    switch (status) {
+        case WindowStatusType.process:
+            return TimeFormatter.countdown(scenario.real.start_timestamp, now)
+        case WindowStatusType.open:
+            return TimeFormatter.countdown(scenario.target.start_timestamp, now)
+        case WindowStatusType.upcoming:
+            return TimeFormatter.countdown(now, scenario.target.start_timestamp)
     }
     return ''
 }

+ 11 - 7
src/features/trackTimeDuration/components/Rings.weapp.tsx

@@ -28,6 +28,7 @@ export type RealRing = {
     durationArc: number;
     radius?: number;
     lineWidth?: number;
+    hideBg?:boolean;
 }
 
 export type TargetRing = {
@@ -239,13 +240,16 @@ export default function Rings(props: {
         //绘制real进度环
         if (props.realRing) {
             if (props.realRing.durationArc <0.01) props.realRing.durationArc=0.01;
-            ctx.beginPath();
-            ctx.arc(center, center, props.realRing!.radius?props.realRing!.radius:radius, props.realRing!.startArc,
-                props.realRing!.startArc + props.realRing!.durationArc);
-            ctx.lineWidth = props.realRing!.lineWidth?props.realRing!.lineWidth+4:lineWidth+4;
-            ctx.strokeStyle = MainColorType.bg;
-            ctx.lineCap = 'round'; // 设置为圆角
-            ctx.stroke();
+            if (!props.realRing.hideBg){
+                ctx.beginPath();
+                ctx.arc(center, center, props.realRing!.radius?props.realRing!.radius:radius, props.realRing!.startArc,
+                    props.realRing!.startArc + props.realRing!.durationArc);
+                ctx.lineWidth = props.realRing!.lineWidth?props.realRing!.lineWidth+4:lineWidth+4;
+                ctx.strokeStyle = MainColorType.bg;
+                ctx.lineCap = 'round'; // 设置为圆角
+                ctx.stroke();
+            }
+            
 
             ctx.beginPath();
             ctx.arc(center, center, props.realRing!.radius?props.realRing!.radius:radius, props.realRing!.startArc,

+ 48 - 22
src/pages/clock/AddMoment.scss

@@ -1,38 +1,64 @@
-.header{
-    display:flex;
-    flex-direction: row;
-    align-items: center;
-    justify-content: center;
 
-}
-
-.time1{
-    padding-left:33px;
-    padding-right:33px;
-    color:#FF751A;
-    font-size: 48px;
-}
 
 .textarea{
     height: 400px;
-    padding-left: 40px;
-    padding-right:40px;
 }
 
 .save{
     background-color: #FF751A;
     color:#fff;
-    width: 582px;
-    margin-left: 46px;
-    margin-top: 100px;
-    height: 88px;
-    border-radius: 44px;
+    width: 360px;
+    left: 195px;
+    height: 96px;
+    border-radius: 28px;
     align-items: center;
     justify-content: center;
     display: flex;
+    position: absolute;
+    bottom: 200px;
+}
+
+.form{
+    display: flex;
+    flex-direction: column;
+    background-color: #fff;
+    padding: 40px;
 }
 
 .cover{
-    width: 200px;
-    height: 200px;
+    width: 128px;
+    height: 128px;
+    background-color: #F5F5F5;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+}
+
+.input_title{
+    padding-bottom: 40px;
+    margin-bottom: 40px;
+    border-bottom: solid 1px #B2B2B299;
+}
+
+.addmoment_header{
+    height: 128px;
+    display: flex;
+    flex-direction: row;
+    border-bottom: solid 1px #B2B2B299;
+    background-color: #fff;
+    align-items: center;
+}
+
+.header_line{
+    width: 1px;
+    height: 36px;
+    background-color: #B2B2B299;
+}
+
+.header_time{
+    display: flex;
+    flex: 1;
+    align-items: center;
+    justify-content: center;
+
 }

+ 86 - 45
src/pages/clock/AddMoment.tsx

@@ -1,4 +1,4 @@
-import { View, Text, Textarea,Image } from "@tarojs/components";
+import { View, Text, Textarea, Image, Input } from "@tarojs/components";
 import Taro, { useRouter } from "@tarojs/taro";
 import { useTranslation } from "react-i18next";
 import './AddMoment.scss'
@@ -12,6 +12,8 @@ import dayjs from "dayjs";
 import TimePicker from "@/features/common/TimePicker";
 import { MainColorType } from "@/context/themes/color";
 import { createMoment } from "@/services/health";
+import { useSelector } from "react-redux";
+import { getThemeColor } from "@/features/health/hooks/health_hooks";
 
 
 let useRoute;
@@ -24,14 +26,17 @@ if (process.env.TARO_ENV == 'rn') {
 
 export default function AddMoment() {
     const [desc, setDesc] = useState('')
+    const [title, setTitle] = useState('')
     const { t } = useTranslation()
     const [imgUrl, setImgUrl] = useState('')
     const [startTime, setStartTime] = useState(0)
     const [endTime, setEndTime] = useState(0)
-    const [showPicker,setShowPicker] = useState(false)
-    const [durationPicker,setDurationPicker] = useState(false)
+    const [showPicker, setShowPicker] = useState(false)
+    const [durationPicker, setDurationPicker] = useState(false)
+
+    const health = useSelector((state: any) => state.health);
+
 
-    
 
     let router
     let navigation;
@@ -46,14 +51,14 @@ export default function AddMoment() {
         router = useRouter()
     }
 
-    const [moment,setMoment] = useState(JSON.parse(router.params.moment))
+    const [moment, setMoment] = useState(JSON.parse(router.params.moment))
 
     useEffect(() => {
         // var obj = JSON.parse(router.params.moment)
         // var start = dayjs(obj.target.timestamp).format('HH:mm')
         // setStartTime(start)
         // setEndTime(obj.target_end_time)
-     }, [])
+    }, [])
 
     function getIntervalHoursAndMinutes(time1, time2) {
         // 将时间字符串转换为 Date 对象
@@ -76,17 +81,20 @@ export default function AddMoment() {
         return { hours, minutes };
     }
 
-    // function duration() {
-    //     const { hours, minutes } = getIntervalHoursAndMinutes(meal.schedule_end_time, meal.schedule_start_time)
-    //     var time = ''
-    //     if (hours > 0) {
-    //         time = hours + '小时'
-    //     }
-    //     if (minutes > 0) {
-    //         time = minutes + '分钟'
-    //     }
-    //     return time
-    // }
+    function duration() {
+        const seconds = moment.target.duration/1000
+        var hours = Math.floor(seconds/3600)
+        var minutes = Math.floor((seconds%3600)/60)
+        // const { hours, minutes } = getIntervalHoursAndMinutes(meal.schedule_end_time, meal.schedule_start_time)
+        var time = ''
+        if (hours > 0) {
+            time = hours + '小时'
+        }
+        if (minutes > 0) {
+            time = minutes + '分钟'
+        }
+        return time
+    }
 
     function tapTime() {
         setShowPicker(true)
@@ -112,34 +120,44 @@ export default function AddMoment() {
     }
 
     function save() {
-        if (desc.length ==0 && imgUrl.length==0){
+        if (desc.length == 0 && imgUrl.length == 0) {
             Taro.showToast({
-                icon:'none',
-                title:'请输入描述或添加图片'
+                icon: 'none',
+                title: '请输入描述或添加图片'
             })
             return
         }
-        var params:any = {
+        var params: any = {
             schedule_id: moment.schedule_id,
-            title:moment.title,
+            title: moment.title,
             description: desc,
             start: {
-                date:dayjs(moment.target.timestamp).format('YYYYMMDD'),
-                timestamp:moment.target.timestamp
+                date: dayjs(moment.target.timestamp).format('YYYYMMDD'),
+                timestamp: moment.target.timestamp
             },
+
             // real_end_time: meal.target_end_time,
         }
 
-        if (imgUrl.length>0){
+        if (title.length>0){
+            params.title = title
+        }
+
+
+
+        if (imgUrl.length > 0) {
             params.media = [{
-                url:imgUrl,
+                url: imgUrl,
                 type: imgUrl.indexOf('mp4') != -1 ? 'video' : 'image',
                 source: 'album'
             }]
         }
-        if (moment.event_id){
+        if (moment.event_id) {
             params.event_id = moment.event_id
         }
+        if (moment.target.duration){
+            params.duration = moment.target.duration
+        }
         createMoment(params).then(res => {
             if (process.env.TARO_ENV == 'weapp') {
                 Taro.navigateBack();
@@ -230,7 +248,7 @@ export default function AddMoment() {
         </Modal>
     }
 
-    function pickerContent(){
+    function pickerContent() {
         const timestamp = moment.target.timestamp
         const strTime = dayjs(timestamp).format('HH:mm')
         return <TimePicker time={strTime}
@@ -244,43 +262,66 @@ export default function AddMoment() {
             }} />
     }
 
-    function confirmPickerTime(e){
+    function confirmPickerTime(e) {
         const list = e.split(':')
         const date = new Date()
         date.setHours(list[0])
         date.setMinutes(list[1])
-        
+
+        console.log(date)
+
         // const duration = meal.target_end_time-meal.target_start_time
         // const endDate = date.getTime()+duration;
         var data = JSON.parse(JSON.stringify(moment))
-        data.target.timestamp = e
+        data.target.timestamp = date.getTime()
         // data.schedule_end_time = dayjs(endDate).format('HH:mm')
-        data.target_start_time = date.getTime()
+        // data.target_starts_time = date.getTime()
         // data.target_end_time = date.getTime()+duration
         setMoment(data)
         setShowPicker(false)
     }
 
-    function durationContent(){
+    function durationContent() {
         return <View></View>
     }
 
 
     return <View>
-        <View className="header">
-            <Text className="time1" onClick={tapTime}>{dayjs(moment.target.timestamp).format('HH:mm')}</Text>
-            {/* <Text className="time1" onClick={tapDuration}>{duration()}</Text> */}
-        </View>
-        <View>
-            <Textarea placeholder="简单描述(选填)" className="textarea" onInput={e => {
-                setDesc(e.detail.value)
-            }} />
-        </View>
         {
-            imgUrl.length>0?<Image src={imgUrl} mode="aspectFill" className="cover" onClick={addImage}/>:<Text onClick={addImage}>添加图片</Text>
+            health.mode != 'FAST' && health.mode != 'SLEEP' &&
+            <View className="addmoment_header">
+                <Text className="header_time" onClick={tapTime} style={{ color: getThemeColor(health.mode) }}>{dayjs(moment.target.timestamp).format('HH:mm')}</Text>
+                {
+                    health.mode == 'EAT' && <View className="header_line"/>
+                }
+                {
+                    health.mode == 'EAT' && <Text className="header_time" style={{ color: getThemeColor(health.mode) }} onClick={tapDuration}>{duration()}</Text>
+                }
+            </View>
         }
-        
-        <View className="save" onClick={save}>Save</View>
+
+        <View className="form">
+            <View>
+                {
+                    (health.mode == 'FAST' || health.mode == 'SLEEP') &&
+                    <Input className="input_title" placeholder="标题文字" onInput={(e: any) => {
+                        setTitle(e.target.value)
+                    }} />
+                }
+                <Textarea placeholder="简单描述(选填)" className="textarea" onInput={e => {
+                    setDesc(e.detail.value)
+                }} />
+            </View>
+            {
+                imgUrl.length > 0 ? <Image src={imgUrl} mode="aspectFill" className="cover" onClick={addImage} /> :
+                    <View className="cover" onClick={addImage}>
+                        <Image src={require('@assets/images/add3.png')} style={{ width: 24, height: 24 }} />
+                    </View>
+            }
+
+        </View>
+
+        <View className="save" style={{ backgroundColor: getThemeColor(health.mode) }} onClick={save}>Save</View>
         {
             showPicker && timeContent()
         }

+ 14 - 0
src/pages/clock/Clock.scss

@@ -6,4 +6,18 @@
     .item{
         margin: 0 20px;
     }
+}
+
+.navi-bar{
+    position: fixed;
+    left: 0;
+    right: 0;
+    top: 0;
+}
+
+.navi-streak{
+    display: flex;
+    flex-direction: row;
+    align-items: center;
+    padding-left: 30px;
 }

+ 63 - 7
src/pages/clock/Clock.tsx

@@ -1,12 +1,16 @@
-import { View } from "@tarojs/components";
+import { View, Text } from "@tarojs/components";
 import './Clock.scss'
 import ClockNew from "./ClockNew";
 import { useEffect, useState } from "react";
 import Taro, { useShareAppMessage } from "@tarojs/taro";
-import { useDispatch } from "react-redux";
+import { useDispatch, useSelector } from "react-redux";
 import { getInfoSuccess } from "@/store/user";
 import { useTranslation } from "react-i18next";
 import { MainColorType } from "@/context/themes/color";
+import { IconStreak } from "@/components/basic/Icons";
+import { getScenario, getThemeColor } from "@/features/health/hooks/health_hooks";
+import Streak from "@/features/health/Streak";
+import Calendar from "@/features/health/calendar";
 
 let useNavigation;
 
@@ -16,8 +20,14 @@ if (process.env.TARO_ENV == 'rn') {
 
 export default function Clock() {
     const dispatch = useDispatch();
-    const [loaded,setLoaded] = useState(false)
+    const [loaded, setLoaded] = useState(false)
     const { t } = useTranslation()
+    const health = useSelector((state: any) => state.health);
+    const [showCalendar, setShowCalendar] = useState(false)
+
+    const systemInfo: any = Taro.getSystemInfoSync();
+    const navigationBarHeight = systemInfo.statusBarHeight + 44;
+
     let navigation;
     if (useNavigation) {
         navigation = useNavigation()
@@ -32,7 +42,7 @@ export default function Clock() {
             }
         })
     }
-    
+
     useEffect(() => {
         if (navigation) {
             navigation.setOptions({
@@ -86,11 +96,57 @@ export default function Clock() {
         }
     }
 
+    function getStreakCount() {
+        const scenario = getScenario(health.windows, health.mode)
+        return scenario.current_streak && scenario.current_streak.days
+    }
+
+    function haveStreaks() {
+        if (health.windows) {
+            const scenario = getScenario(health.windows, health.mode)
+            if (scenario.current_streak) {
+                return true
+            }
+        }
+        return false
+    }
+
     if (!loaded)
         return <View />
-    
-    return <View style={{flex:1}}>
-        <View style={{height:100,backgroundColor:MainColorType.bg,zIndex:1000}}/>
+
+    return <View style={{ flex: 1, position: 'relative' }}>
+        <View style={{ height: navigationBarHeight, backgroundColor: MainColorType.bg }} />
+        <View className="navi-bar" style={{ height: navigationBarHeight, zIndex: 1000, backgroundColor: showCalendar?'#fff':MainColorType.bg }}>
+            {
+                haveStreaks() && <View className="navi-streak"
+                    onClick={() => {
+                        if (showCalendar){
+                            setShowCalendar(false)
+                        }
+                        else {
+                            setShowCalendar(true)
+                        }
+                        
+                    }}
+                    style={{ height: 44, marginTop: systemInfo.statusBarHeight }} >
+                    <IconStreak color={getThemeColor(health.mode)} width={20} />
+                    <Text style={{ color: getThemeColor(health.mode) }}>{getStreakCount()}</Text>
+                </View>
+            }
+
+        </View>
         <ClockNew />
+        {
+            showCalendar && <Streak testInfo={null}
+                dismiss={() => {
+                    setShowCalendar(false)
+                }}
+                confirm={() => { }}>
+                <View style={{display:'flex',flexDirection:'column'}}>
+                    <View style={{height: navigationBarHeight}}/>
+                    <Calendar year={2024} month={9} />
+                </View>
+            </Streak>
+        }
     </View>
 }

+ 3 - 13
src/pages/clock/ClockNew.tsx

@@ -88,9 +88,9 @@ export default function ClockNew() {
         return <View >
             <MainSwiper count={count} pageChanged={pageChanged} typeChanged={typeChanged} />
             <MainConsole type={type} />
-            <View onClick={() => {
+            {/* <View onClick={() => {
                 setShowCalendar(true)
-            }}>Show Calendar</View>
+            }}>Show Calendar</View> */}
             <MainHistory type={type} />
             <View style={{ height: 2200 }} />
         </View>
@@ -132,17 +132,7 @@ export default function ClockNew() {
 
 
         </ScrollView> */}
-        {
-            showCalendar && <Streak testInfo={null}
-                dismiss={() => {
-                    setShowCalendar(false)
-                }}
-                confirm={() => { }}>
-                <View>
-                    <Calendar year={2024} month={9} />
-                </View>
-            </Streak>
-        }
+        
         <TabBar index={0} />
     </View>
 }

+ 6 - 0
src/utils/types.ts

@@ -82,4 +82,10 @@ export enum WindowType {
     night = 'night',
     sleep = 'sleep',
     active = 'active'
+}
+
+export enum WindowStatusType {
+    process = 'process',
+    open = 'open',
+    upcoming = 'upcoming'
 }

Một số tệp đã không được hiển thị bởi vì quá nhiều tập tin thay đổi trong này khác