leon 1 rok temu
rodzic
commit
2188d232b0

+ 10 - 2
src/_health/components/add_label.tsx

@@ -70,7 +70,12 @@ export default function AddLabel(props: { labels: any, defaultValue?: string, di
             }]
         }).then(res => {
             global.refreshWindow()
-            global.refreshSchedules()
+            if (global.refreshSchedules) {
+                global.refreshSchedules()
+            }
+            if (global.refreshSchedules2) {
+                global.refreshSchedules2()
+            }
 
             // if (process.env.TARO_ENV == 'weapp') {
             //     Taro.navigateBack()
@@ -96,6 +101,9 @@ export default function AddLabel(props: { labels: any, defaultValue?: string, di
                 if (global.refreshSchedules) {
                     global.refreshSchedules()
                 }
+                if (global.refreshSchedules2) {
+                    global.refreshSchedules2()
+                }
             }
             else {
                 showAlert({
@@ -103,7 +111,7 @@ export default function AddLabel(props: { labels: any, defaultValue?: string, di
                     content: '冲突描述',
                     showCancel: false,
                     confirm: () => {
-                        jumpPage(`./schedules_conflict?schedules=${JSON.stringify((res as any).schedules)}&errors=${JSON.stringify((res as any).error_mesages)}`)
+                        jumpPage(`./schedules_conflict?schedules=${JSON.stringify((res as any).schedules)}&errors=${JSON.stringify((res as any).error_messages)}`)
                     }
                 })
             }

+ 11 - 0
src/_health/pages/move.scss

@@ -349,4 +349,15 @@
     align-items: center;
     justify-content: center;
 
+}
+
+.detail_date{
+    height: 106px;
+    display: flex;
+    flex-direction: row;
+    align-items: center;
+    margin-left: 52px;
+    margin-top: 36px;
+    font-size: 48px;
+    font-weight: bold;
 }

+ 72 - 40
src/_health/pages/move.tsx

@@ -70,6 +70,8 @@ export default function Move() {
 
     useDidShow(() => {
         checkAuth()
+        getMovesCurrent()
+        getMovesHistory()
     })
 
     global.updateMove = () => {
@@ -286,7 +288,7 @@ export default function Move() {
         end = (end + '').padStart(2, '0')
         return <View className="current_history_item">
             <View style={{ display: 'flex', flexDirection: 'column' }}>
-                <Text className="current_item_period">{isYesterday ? '昨天' : ''}{start}:00-{end}:00</Text>
+                <Text className="current_item_period">{start}:00-{end}:00</Text>
                 {
                     item.is_extra && <Text style={{ color: getThemeColor('SLEEP') }}>is Extra</Text>
                 }
@@ -313,13 +315,13 @@ export default function Move() {
         return <View className="summary">
             <View className="summary_header">
                 <Text>Last {hours.length} Hour{hours.length == 1 ? '' : 's'}</Text>
-                <Image className="arrow_hide"
+                {/* <Image className="arrow_hide"
                     onClick={() => {
                         setHideCurrentRecent(!hideCurrentRecent)
                     }}
                     src={hideCurrentRecent ? require('@assets/_health/arrow_down.png') : require('@assets/_health/arrow_up.png')} />
                 <View style={{ flex: 1 }} />
-                <Text style={{ color: getThemeColor(health.mode) }}>{process()}</Text>
+                <Text style={{ color: getThemeColor(health.mode) }}>{process()}</Text> */}
                 <View className="border_footer_line" />
             </View>
             {
@@ -355,7 +357,45 @@ export default function Move() {
         </View>
     }
 
+    function formatTimeInterval(startTimestamp, endTimestamp) {
+        // 计算时间间隔(以秒为单位)
+        const intervalSeconds = Math.floor((endTimestamp - startTimestamp) / 1000);
+
+        // 处理负值的情况
+        if (intervalSeconds < 0) {
+            throw new Error("End timestamp must be greater than start timestamp");
+        }
+
+        // 如果时间间隔大于1小时
+        if (intervalSeconds >= 3600) {
+            const hours = Math.floor(intervalSeconds / 3600);
+            const minutes = Math.floor((intervalSeconds % 3600) / 60);
+            const seconds = intervalSeconds % 60;
+
+            return `${String(hours).padStart(2, '0')}:${String(minutes).padStart(2, '0')}:${String(seconds).padStart(2, '0')}`;
+        } else {
+            // 如果时间间隔小于1小时
+            const minutes = Math.floor(intervalSeconds / 60);
+            const seconds = intervalSeconds % 60;
+
+            return `${String(minutes).padStart(2, '0')}:${String(seconds).padStart(2, '0')}`;
+        }
+    }
+
     function consoleFooter() {
+        if (isExtra()) {
+            var now = new Date()
+            var target = new Date()
+            target.setHours(data.hours[0].hour)
+            target.setMinutes(50)
+            target.setSeconds(0)
+            target.setMilliseconds(0)
+            var timestamp = target.getTime()
+            if (now.getTime()>timestamp){
+                timestamp += 24*3600*1000
+            }
+            return <Text className="console_footer">{formatTimeInterval(new Date().getTime(),timestamp)}</Text>
+        }
         if (getStatus() == 'open') {
             if (new Date().getMinutes() < 10) {
                 let time = new Date()
@@ -378,9 +418,6 @@ export default function Move() {
             const next = new Date().getTime() + 3600 * 1000
             return <Text className="console_footer">Check In available {dayjs(pre).format('HH:50')}-{dayjs(next).format('HH:10')}</Text>
         }
-        else if (isExtra()) {
-            return <Text className="console_footer">to do 倒计时</Text>
-        }
 
         const minute = new Date().getMinutes()
         var isChecked = currentHourChecked()
@@ -418,49 +455,39 @@ export default function Move() {
         return <Text className="console_footer">Opening soon {minutes}:{seconds}</Text>
     }
 
-    function hoursPassed(timeStr) {
-        // 获取当前时间
-        const now = new Date();
-
-        // 解析输入时间字符串
-        const [hours, minutes] = timeStr.split(':').map(Number);
-
-        // 创建一个 Date 对象,设置为今天的输入时间
-        const inputTime = new Date(now.getFullYear(), now.getMonth(), now.getDate(), hours, minutes);
-
-        // 如果输入时间在当前时间之后,假设它是前一天的时间
-        if (inputTime > now) {
-            inputTime.setDate(inputTime.getDate() - 1);
-        }
-
-        // 计算时间差(以毫秒为单位)
-        const timeDiff = now.getTime() - inputTime.getTime();
+    function hourIndex() {
+        const { time_slots } = data.period
+        const str = getDuration()
+        const hour = parseInt(str.substring(0, 2))
 
-        // 将时间差转换为小时,最大为 23 小时
-        const hours2 = Math.min(Math.floor(timeDiff / (1000 * 60 * 60)), 23);
+        var selItem: any = null
 
-        return hours2 + 1;
-    }
+        time_slots.map((item) => {
+            if (item.hour == hour) {
+                selItem = item;
+            }
+        })
 
-    function hourIndex() {
-        const { start_time, end_time } = data.time_range
-        if (isExtra()) {
-            return hoursPassed(end_time)
-        }
-        return hoursPassed(start_time)
+        return <View className="summary_status_bg">
+            <View className="summary_status_point" style={{ backgroundColor: selItem.type == 'SLEEP' ? getThemeColor('SLEEP') : getThemeColor('ACTIVE') }} />
+            <Text className="summary_status_text">{selItem.type == 'SLEEP' ? 'SLEEP HOUR ' : 'WAKING HOUR '}{selItem.index}</Text>
+        </View>
 
     }
 
     function becomeTime() {
         var hour = new Date().getHours()
         var tHour = parseInt(getDuration().substring(0, 2))
+        if (isExtra()){
+            tHour = data.hours[0].hour
+        }
         if (hour > tHour) {
             return `Check in begins at ${getDuration().substring(0, 2)}:50 tomorrow`
         }
         return `Check in begins at ${getDuration().substring(0, 2)}:50`
     }
 
-    function console() {
+    function consolePanel() {
 
         if (!allowRun) {
             return <View className="move_console">
@@ -473,10 +500,11 @@ export default function Move() {
         }
 
         return <View className="move_console">
-            <View className="summary_status_bg">
+            {/* <View className="summary_status_bg">
                 <View className="summary_status_point" style={{ backgroundColor: isExtra() ? getThemeColor('SLEEP') : getThemeColor('ACTIVE') }} />
                 <Text className="summary_status_text">{isExtra() ? 'SLEEP HOUR ' : 'WAKING HOUR '}{hourIndex()}</Text>
-            </View>
+            </View> */}
+            {hourIndex()}
             <Text className="console_duration">{getDuration()}</Text>
             <Text className="console_desc" style={{
                 color: isExtra() ? getThemeColor('SLEEP') : getThemeColor('ACTIVE'),
@@ -485,7 +513,7 @@ export default function Move() {
                     isExtra() ? 'Sleep well.' : getStatus() == 'open' ? `Think you've hit ${getCurrentTarget()} steps this hour?` : `Hit ${getCurrentTarget()} steps within the hour`
                 }</Text>
             {
-                getStatus() == 'open' ? <View className="console_checkbtn"
+                isExtra() ?<View className="console_checkbtn">{becomeTime()}</View>:getStatus() == 'open' ? <View className="console_checkbtn"
                     onClick={tapLog}
                     style={{ color: '#fff', backgroundColor: getThemeColor('ACTIVE') }}>I Think So!</View> :
                     <View className="console_checkbtn" onClick={tapLog}>{becomeTime()}</View>
@@ -560,12 +588,16 @@ export default function Move() {
         </View>
     }
 
+    function goDetail(item) {
+        jumpPage('./move_detail?id='+item.id)
+    }
+
     if (!loaded || !loadAuth) return <View />
 
     return <View style={{ display: 'flex', flexDirection: 'column' }}>
         <Text className="move-title">Move Every Hour</Text>
         {
-            console()
+            consolePanel()
         }
         {
             tools()
@@ -585,11 +617,11 @@ export default function Move() {
                 </View>
                 {
                     list.map((item, index) => {
-                        return <View key={index} className="history_item">
+                        return <View key={index} className="history_item" onClick={() => goDetail(item)}>
                             <Text className="history_item_date">{(item.date + '').substring(6, 8)}</Text>
                             <View className="history_item_detail_bg">
                                 <Image className="history_item_detail_icon" src={require('@assets/_health/walk.png')} />
-                                <Text>每小时活动 {item.real_hours}/{item.target_hours} · Steps {item.stat.real_steps}/{item.stat.target_steps}</Text>
+                                <Text> Hours Active {item.active_hours} · Total Steps {item.stat.real_steps}</Text>
                             </View>
                             <View className="border_footer_line" />
                         </View>

+ 162 - 0
src/_health/pages/move_detail.tsx

@@ -0,0 +1,162 @@
+import { getActiveMoveDetail } from "@/services/health";
+import { View, Text, Image } from "@tarojs/components";
+import { useRouter } from "@tarojs/taro";
+import { useEffect, useState } from "react";
+import './move.scss'
+import { getThemeColor } from "@/features/health/hooks/health_hooks";
+import { rpxToPx } from "@/utils/tools";
+import dayjs from "dayjs";
+
+let useRoute;
+let useNavigation;
+let scenario = '';
+if (process.env.TARO_ENV == 'rn') {
+    useRoute = require("@react-navigation/native").useRoute
+    useNavigation = require("@react-navigation/native").useNavigation
+}
+export default function MoveDetail() {
+    let router
+    let navigation;
+    if (useNavigation) {
+        navigation = useNavigation()
+    }
+
+    if (process.env.TARO_ENV == 'rn') {
+        router = useRoute()
+    }
+    else {
+        router = useRouter()
+    }
+
+    const [loaded, setLoaded] = useState(false)
+    const [data, setData] = useState<any>(null)
+    const [moreActive, setMoreActive] = useState(false)
+
+    useEffect(() => {
+        getActiveMoveDetail(router.params.id).then(res => {
+            setLoaded(true)
+            setData(res)
+        })
+    }, [])
+
+    function summary() {
+        return <View className="move_summary">
+
+            <View className="summary_header">
+                <Text>Summary</Text>
+                <View style={{ flex: 1 }} />
+                {
+                    data.last_updated && <Text style={{ color: '#b2b2b2', fontSize: 10, fontWeight: 'normal' }}></Text>
+                }
+
+                <View className="border_footer_line" />
+            </View>
+            <View className="summary_content">
+                <View className="summary_footer">
+                    <View className="summary_footer_item">
+                        <Text className="light_desc">Hours Sedentary</Text>
+                        <Text>{data.stat.sedentary_hours}/{data.stat.waking_hours}</Text>
+                    </View>
+                    <View className="summary_footer_item">
+                        <Text className="light_desc">Hours Active</Text>
+                        <Text style={{ color: getThemeColor('ACTIVE') }}>{data.stat.active_hours}/{data.stat.waking_hours}</Text>
+                    </View>
+                    <View className="summary_footer_item">
+                        <Text className="light_desc">Hours Missed</Text>
+                        <Text>{data.stat.missed_hours}/{data.stat.waking_hours}</Text>
+                    </View>
+                </View>
+                <View className="summary_footer" style={{ marginTop: rpxToPx(48) }}>
+                    <View className="summary_footer_item">
+                        <Text className="light_desc">Total Steps</Text>
+                        <Text>{data.stat.real_steps}/{data.stat.target_steps}</Text>
+                    </View>
+                    <View className="summary_footer_item">
+                        <Text className="light_desc">Total Calories</Text>
+                        <Text>{data.stat.real_calories}/{data.stat.target_calories}</Text>
+                    </View>
+                    <View className="summary_footer_item">
+                        <Text className="light_desc">Total Distance</Text>
+                        <Text>{data.stat.real_distance}/{data.stat.target_distance}</Text>
+                    </View>
+                </View>
+            </View>
+
+        </View>
+    }
+
+    function activeHour() {
+
+        const hours = data.hours
+        return <View className="summary">
+            <View className="summary_header">
+                <Text>Hourly</Text>
+                <View className="border_footer_line" />
+            </View>
+            <View className="summary_content2">
+                {
+                    hours.map((item, index) => {
+                        if (index >= 3 && !moreActive) return <View key={index} />
+                        return <View key={index}>
+                            {
+                                currentHistory(item)
+                            }
+                        </View>
+                    })
+                }
+                {
+                    hours.length > 3 && <View className="recent_btn_bg">
+                        {
+                            moreActive ? <View className="recent_btn" onClick={() => setMoreActive(false)}>收起</View> : <View className="recent_btn" onClick={() => setMoreActive(true)}>展开剩余{hours.length - 3}条</View>
+                        }
+                        <View className="border_footer_line" />
+                    </View>
+                }
+            </View>
+
+
+
+        </View>
+    }
+
+    function currentHistory(item) {
+        var start = item.hour
+        var isYesterday = start > new Date().getHours()
+        var end = start + 1
+        start = (start + '').padStart(2, '0')
+        end = (end + '').padStart(2, '0')
+        return <View className="current_history_item">
+            <View style={{ display: 'flex', flexDirection: 'column' }}>
+                <Text className="current_item_period">{start}:00-{end}:00</Text>
+                {
+                    item.is_extra && <Text style={{ color: getThemeColor('SLEEP') }}>is Extra</Text>
+                }
+                <Text>{item.real_steps}/{item.target_steps}</Text>
+            </View>
+
+            {
+                item.status == 'MISSED' && <Text className="missed">Missed</Text>
+            }
+            {
+                item.status == 'SEDENTARY' && <Image className="history_item_detail_icon" src={require('@assets/_health/sit.png')} />
+            }
+            {
+                item.status == 'ACTIVE' && <Image className="history_item_detail_icon" src={require('@assets/_health/walk.png')} />
+            }
+            <View className="border_footer_line" />
+        </View>
+    }
+
+    if (!loaded) return <View />
+
+    const date = data.start_date+''
+    return <View>
+        <View className="detail_date">{date.substring(0,4)}年{date.substring(4,6)}月{date.substring(6,8)}日</View>
+        {
+            summary()
+        }
+        {
+            activeHour()
+        }
+    </View>
+}

+ 1 - 0
src/_health/pages/move_setting_time.tsx

@@ -40,6 +40,7 @@ export default function MoveSettingTime() {
                                         value={item.goal}
                                         autoFocus={true}
                                         focus={true}
+                                        type="number"
                                         onBlur={() => {
                                             setSelIndex(-1)
                                             createSchedule({

+ 6 - 27
src/_health/pages/schedules_conflict.tsx

@@ -39,8 +39,6 @@ export default function SchedulesConflict() {
     const [showTimePicker, setShowTimePicker] = useState(false)
     const [selItem, setSelItem] = useState<any>(null)
     const [selIndex, setSelIndex] = useState(-1)
-    const [showErrorTip, setShowErrorTip] = useState(true)
-    debugger
 
     function modalContent() {
         const strTime = selItem.time
@@ -74,34 +72,15 @@ export default function SchedulesConflict() {
             setSchedules((res as any).schedules)
             setErrors((res as any).error_messages?(res as any).error_messages:[])
             if ((res as any).result) {
-                setShowErrorTip(false)
                 global.refreshWindow()
-                global.refreshSchedules()
-            }
-            else {
-                setShowErrorTip(true)
-            }
-        })
-    }
-
-    function done() {
-        createSchedule({
-            schedules: schedules
-        }).then(res => {
-            if ((res as any).result) {
-                global.refreshWindow()
-                global.refreshSchedules()
-                Taro.navigateBack({
-                    delta: router.params.isAdd == 1 ? 1 : 2
-                })
+                if (global.refreshSchedules) {
+                    global.refreshSchedules()
+                }
+                if (global.refreshSchedules2) {
+                    global.refreshSchedules2()
+                }
             }
             else {
-                setSchedules((res as any).schedules)
-                showAlert({
-                    title: '弹窗标题',
-                    content: '冲突描述',
-                    showCancel: false,
-                })
             }
         })
     }

+ 7 - 2
src/_health/pages/schedules_edit.tsx

@@ -145,7 +145,12 @@ export default function SchedulesEdit() {
         }).then(res => {
             if ((res as any).result) {
                 global.refreshWindow()
-                global.refreshSchedules()
+                if (global.refreshSchedules) {
+                    global.refreshSchedules()
+                }
+                if (global.refreshSchedules2) {
+                    global.refreshSchedules2()
+                }
                 if (process.env.TARO_ENV == 'weapp') {
                     Taro.navigateBack()
                 }
@@ -156,7 +161,7 @@ export default function SchedulesEdit() {
                     content: '冲突描述',
                     showCancel: false,
                     confirm: () => {
-                        jumpPage(`./schedules_conflict?schedules=${JSON.stringify((res as any).schedules)}&errors=${JSON.stringify((res as any).error_mesages)}`)
+                        jumpPage(`./schedules_conflict?schedules=${JSON.stringify((res as any).schedules)}&errors=${JSON.stringify((res as any).error_messages)}`)
                     }
                 })
 

+ 1 - 1
src/_health/pages/schedules_list.tsx

@@ -60,7 +60,7 @@ export default function SchedulesList() {
         schedules()
     }, [])
 
-    global.refreshSchedules = () => {
+    global.refreshSchedules2 = () => {
         schedules()
     }
 

+ 1 - 0
src/app.config.ts

@@ -62,6 +62,7 @@ const appConfig = defineAppConfig({
         'pages/timeline_detail',
         'pages/archive',
         'pages/move',
+        'pages/move_detail',
         'pages/move_schedule',
         'pages/move_setting',
         'pages/move_setting_reminder',

+ 9 - 1
src/features/health/MainConsole.tsx

@@ -408,7 +408,7 @@ export default function MainConsole(props: { type: WindowType }) {
         switch (health.mode) {
             case 'DAY':
             case 'NIGHT':
-                list = ['设置提醒']
+                list = ['设置提醒','设置位置']
                 break;
             case 'FAST':
             case 'SLEEP':
@@ -515,6 +515,10 @@ export default function MainConsole(props: { type: WindowType }) {
                         case 'ACTIVE':
                             jumpPage('/_health/pages/schedules_list?mode=' + health.mode)
                             break;
+                        case 'DAY':
+                        case 'NIGHT':
+                            chooseLocation()
+                            break;
                     }
                 }
                 break;
@@ -669,6 +673,10 @@ export default function MainConsole(props: { type: WindowType }) {
     }
 
     function chooseLocation() {
+        if (!user.isLogin) {
+            jumpPage('/pages/account/ChooseAuth', 'ChooseAuth', navigation)
+            return
+        }
         Taro.chooseLocation({
             // latitude: authInfo && authInfo.lat ? authInfo.lat : undefined,
             // longitude: authInfo && authInfo.lat ? authInfo.lng : undefined,

+ 13 - 3
src/features/health/MainFastEatCard.tsx

@@ -43,7 +43,7 @@ export default function MainFastEatCard(props: {
     onClick: Function,
     scale: number
 }) {
-    const [isFastMode, setIsFastMode] = useState(true)
+
 
     const [showModal, setShowModal] = useState(false)
     const [showTimePicker, setShowTimePicker] = useState(false);
@@ -63,6 +63,14 @@ export default function MainFastEatCard(props: {
     const dispatch = useDispatch()
 
 
+    const { fast, eat } = health.windows.fast_eat
+    const t = new Date().getTime()
+    const isTempFast = (fast.status == 'WAIT_FOR_END' ||
+        fast.target.start_time <= t && fast.target.end_time >= t) ||
+        isCurrentTimeInRange(fast.period.start_time, fast.period.end_time)
+    const [isFastMode, setIsFastMode] = useState(isTempFast)
+
+
     useEffect(() => {
         if (health.mode == 'FAST') {
             setIsFastMode(true)
@@ -77,9 +85,11 @@ export default function MainFastEatCard(props: {
         var now = new Date().getTime()
         if ((fast.status == 'WAIT_FOR_END' || fast.target.start_time <= now && fast.target.end_time >= now) || isCurrentTimeInRange(fast.period.start_time, fast.period.end_time)) {
             setIsFastMode(true)
+            dispatch(setMode('FAST'))
         }
         else {
             setIsFastMode(false)
+            dispatch(setMode('EAT'))
         }
 
         setEatData(eat)
@@ -191,7 +201,7 @@ export default function MainFastEatCard(props: {
             const status = getWindowStatus(health.windows, isFastMode ? 'FAST' : 'EAT')
 
             return {
-                color: status == WindowStatusType.upcoming?'#B2B2B2':isFastMode ? MainColorType.fast : MainColorType.eat,
+                color: status == WindowStatusType.upcoming ? '#B2B2B2' : isFastMode ? MainColorType.fast : MainColorType.eat,
                 lineWidth: 4,
                 borderColor: '#F5F5F5',
                 offset: 0
@@ -200,7 +210,7 @@ export default function MainFastEatCard(props: {
         return null;
     }
 
-    function showDotAnimation(){
+    function showDotAnimation() {
         if (health.mode == 'FAST' || health.mode == 'EAT') {
             const status = getWindowStatus(health.windows, isFastMode ? 'FAST' : 'EAT')
             if (status == WindowStatusType.process) return true;

+ 6 - 2
src/features/health/MainSwiper.tsx

@@ -3,7 +3,7 @@ import './MainCard.scss';
 import MainDayNightCard from "./MainDayNightCard";
 import MainFastEatCard from "./MainFastEatCard";
 import MainSleepActiveCard from "./MainSleepActiveCard";
-import { useState } from "react";
+import { useEffect, useState } from "react";
 import { WindowType } from "@/utils/types";
 import { useDispatch, useSelector } from "react-redux";
 import { setMode, setTab } from "@/store/health";
@@ -14,7 +14,7 @@ import dayjs from "dayjs";
 
 let scrollPage = 0;
 export default function MainSwiper(props: { count: number, pageChanged: Function, typeChanged: Function }) {
-    const [current, setCurrent] = useState(0)
+    const [current, setCurrent] = useState(1)
     const [type, setType] = useState(WindowType.day)
     const health = useSelector((state: any) => state.health);
 
@@ -23,6 +23,10 @@ export default function MainSwiper(props: { count: number, pageChanged: Function
     const [scale2, setScale2] = useState(0.8)
     const dispatch = useDispatch()
 
+    useEffect(()=>{
+        dispatch(setTab(1))
+    },[])
+
     function pageChanged(e) {
         const page = e.detail.current
         console.log(page)