leon 1 سال پیش
والد
کامیت
0c414e0d45

+ 1 - 1
src/_health/base/new_timepicker.scss

@@ -2,6 +2,6 @@
     height: 80px !important;
     background-color: #FF2E661A !important;
     border: solid 1px #ffffff00 !important;
-    border-radius: 40px !important;
+    // border-radius: 40px !important;
     color: aqua !important;
 }

+ 39 - 20
src/_health/base/new_timepicker.tsx

@@ -1,41 +1,60 @@
 import { rpxToPx } from "@/utils/tools";
-import { PickerView, PickerViewColumn,Text } from "@tarojs/components";
+import { PickerView, PickerViewColumn, Text,View } from "@tarojs/components";
 import { useEffect, useState } from "react";
 import './new_timepicker.scss'
+import dayjs from "dayjs";
 
-export default function NewTimePicker() {
-    const [items,setItems] = useState<any>([[0],[0]])
-    const [values,setValues] = useState<any>([0,0])
-    useEffect(()=>{
-        var hours:any = []
-        for (var i = 0;i<=23;i++){
-            hours.push(i)
+export default function NewTimePicker(props: { time?: string, onChange?: any }) {
+    const [items, setItems] = useState<any>([[0], [0]])
+    const [values, setValues] = useState<any>([0, 0])
+    const [loaded,setLoaded] = useState(false)
+    useEffect(() => {
+        var hours: any = []
+        var tempValues: any = [0, 0]
+        var str = props.time ? props.time : dayjs().format('HH:mm')
+        var tempHour = str.split(':')[0]
+        var tempMinute = str.split(':')[1]
+        for (var i = 0; i <= 23; i++) {
+            var str = (i + '').padStart(2, '0')
+            if (str == tempHour) {
+                tempValues[0] = i
+            }
+            hours.push(str)
         }
-        var minutes:any = []
-        for (var i = 0;i<=59;i++){
-            minutes.push(i)
+        var minutes: any = []
+        for (var i = 0; i <= 59; i++) {
+            var str = (i + '').padStart(2, '0')
+            if (str == tempMinute) {
+                tempValues[1] = i
+            }
+            minutes.push(str)
         }
-        setItems([hours,minutes])
-    },[])
+        setItems([hours, minutes])
+        setValues(tempValues)
+        setLoaded(true)
+    }, [])
 
-    function onPickerChange(e){
+    function onPickerChange(e) {
         setValues(e.detail.value)
-        console.log(e)
+        if (props.onChange) {
+            props.onChange(e.detail.value)
+        }
     }
 
-    function getColor(i,j){
-        if (i == 0 && j == values[0]){
+    function getColor(i, j) {
+        if (i == 0 && j == values[0]) {
             return true
         }
-        if (i == 1 && j == values[1]){
+        if (i == 1 && j == values[1]) {
             return true
         }
         return false
     }
+    if (!loaded) return <View />
     return <PickerView
         value={values}
         // itemStyle={{ color: '#000' }}
-        style={{ color: '#000', height: rpxToPx(340),width:rpxToPx(670) }}
+        style={{ color: '#000', height: rpxToPx(340), width: rpxToPx(670) }}
         onChange={onPickerChange}
         indicatorClass="pick_sel_item"
         indicatorStyle="background-color: #FF2E661A !important;"
@@ -48,7 +67,7 @@ export default function NewTimePicker() {
                 return <PickerViewColumn key={index}>
                     {item.map((obj, j) => {
                         return (
-                            <Text key={j} style={{ display: 'flex', alignItems: 'center', justifyContent: 'center', color: getColor(index,j)?'#FF2E66':'#000' }}>{obj}</Text>
+                            <Text key={j} style={{ display: 'flex', alignItems: 'center', justifyContent: 'center', color: getColor(index, j) ? '#FF2E66' : '#000' }}>{obj}</Text>
                         );
                     })}
                 </PickerViewColumn>

+ 9 - 4
src/_health/components/add_label.tsx

@@ -9,6 +9,7 @@ import { createSchedule, getLabelsTime, getSchedules } from "@/services/health";
 import Taro from "@tarojs/taro";
 import { useSelector } from "react-redux";
 import { getThemeColor } from "@/features/health/hooks/health_hooks";
+import NewTimePicker from "../base/new_timepicker";
 
 export default function AddLabel(props: { labels: any, defaultValue?: string, disMiss?: any }) {
     const [showTimePicker, setShowTimePicker] = useState(false)
@@ -16,7 +17,7 @@ export default function AddLabel(props: { labels: any, defaultValue?: string, di
     const [value, setValue] = useState(props.defaultValue ?? '')
     const [isFullday, setIsFullday] = useState(false)
     const [timeLabels, setTimeLabels] = useState<any>([])
-    const [strTime, setStrTime] = useState('')
+    const [strTime, setStrTime] = useState(dayjs().format('HH:mm'))
 
     // function timeContent() {
     //     return <Modal
@@ -81,7 +82,7 @@ export default function AddLabel(props: { labels: any, defaultValue?: string, di
             schedules: [{
                 event: health.mode == 'EAT' ? 'EAT_CUSTOM' : 'ACTIVE_CUSTOM',
                 title: value,
-                time: dayjs().format('HH:mm'),
+                time: strTime,
                 is_all_day: isFullday,
                 time_label: isFullday ? strTime : null
             }]
@@ -131,8 +132,12 @@ export default function AddLabel(props: { labels: any, defaultValue?: string, di
                     </View>
                 </View> :
                     <View style={{ flexDirection: 'column', display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
-                        <Text>{dayjs().format('HH:mm')}</Text>
-                        <Text>Picker placeholder</Text>
+                        {/* <Text onClick={}>{dayjs().format('HH:mm')}</Text>
+                        <Text>Picker placeholder</Text> */}
+                        <NewTimePicker onChange={e=>{
+                            console.log(e)
+                            setStrTime((e[0]+'').padStart(2,'0')+':'+(e[1]+'').padStart(2,'0'))
+                        }}/>
                     </View>
             }
             <View className="pop_footer" style={{ backgroundColor: getThemeColor(health.mode) }} onClick={() => done()}>完成</View>

+ 7 - 0
src/_health/pages/add_moment.tsx

@@ -57,6 +57,8 @@ export default function AddMoment() {
     const [moment, setMoment] = useState(JSON.parse(router.params.moment))
 
     useEffect(() => {
+        global.set_time = new Date().getTime()
+
         var obj = JSON.parse(router.params.moment)
         if (obj.target) {
             obj.target.timestamp = new Date().getTime()
@@ -179,6 +181,11 @@ export default function AddMoment() {
         if (moment.target && moment.target.duration) {
             params.duration = durationT//moment.target.duration
         }
+
+        params.extra = {
+            set_time: global.set_time ? global.set_time : new Date().getTime(),
+            confirm_time: new Date().getTime()
+        }
         createMoment(params).then(res => {
             if (process.env.TARO_ENV == 'weapp') {
                 Taro.navigateBack();

+ 161 - 28
src/_health/pages/move.scss

@@ -1,4 +1,4 @@
-.log_btn{
+.log_btn {
     color: #fff;
     display: flex;
     align-items: center;
@@ -8,7 +8,7 @@
     border-radius: 21px;
 }
 
-.move-title{
+.move-title {
     margin-top: 60px;
     margin-bottom: 60px;
     margin-left: 52px;
@@ -17,14 +17,93 @@
     font-weight: bold;
 }
 
-.move-header{
+.move_console {
+    width: 670px;
+    background-color: #fff;
+    margin-left: 40px;
+    border-radius: 24px;
+    display: flex;
+    flex-direction: column;
+    align-items: center;
+    position: relative;
+    padding-top: 70px;
+    padding-bottom: 40px;
+}
+
+.console_duration {
+    color: #999999;
+    font-size: 34px;
+    line-height: 42px;
+}
+
+.console_open_tip{
+    margin-left: 66px;
+    margin-right: 66px;
+    color: #B2B2B2;
+    font-size: 26px;
+    line-height: 40px;
+    text-align: center;
+    margin-top: 10px;
+}
+
+.console_desc {
+    margin-top: 10px;
+    line-height: 72px;
+    font-weight: bold;
+    font-size: 34px;
+}
+
+.summary_status_bg{
+    display: flex;
+    flex-direction: row;
+    align-items: center;
+    position: absolute;
+    left: 20px;
+    top: 20px;
+    padding-left: 5px;
+}
+
+.summary_status_point{
+    width: 12px;
+    height: 12px;
+    border-radius: 6px;
+    margin-right: 6px;
+}
+
+.summary_status_text{
+    font-weight: bold;
+    font-size: 18px;
+}
+
+.console_checkbtn{
+    width: 538px;
+    height: 96px;
+    border-radius: 24px;
+    background-color: #B2B2B233;
+    font-size: 26px;
+    color: #000;
+    font-weight: bold;
+    align-items: center;
+    justify-content: center;
+    display: flex;
+    margin-top: 32px;
+}
+
+.console_footer{
+    font-size: 18px;
+    line-height: 28px;
+    margin-top: 16px;
+    color: #B2B2B2;
+}
+
+.move-header {
     background-color: #fff;
     padding-left: 52px;
     display: flex;
     flex-direction: column;
 }
 
-.header-current{
+.header-current {
     display: flex;
     align-items: center;
     flex-direction: row;
@@ -35,12 +114,12 @@
     padding-right: 40px;
 }
 
-.current_intro{
+.current_intro {
     display: flex;
     flex-direction: column;
 }
 
-.current_footer{
+.current_footer {
     height: 108px;
     display: flex;
     flex-direction: row;
@@ -51,13 +130,12 @@
     font-size: 24px;
 }
 
-.summary{
+.summary {
     margin-top: 36px;
     background-color: #fff;
-
 }
 
-.summary_header{
+.summary_header {
     height: 108px;
     position: relative;
     display: flex;
@@ -69,14 +147,13 @@
     padding-right: 50px;
 }
 
-.summary_footer{
-    height: 222px;
+.summary_footer {
     display: flex;
     align-items: center;
     flex-direction: row;
 }
 
-.summary_footer_item{
+.summary_footer_item {
     display: flex;
     flex-direction: column;
     align-items: center;
@@ -84,20 +161,36 @@
     flex: 1;
 }
 
-.light_desc{
+.move_summary {
+    background-color: #fff;
+    display: flex;
+    flex-direction: column;
+    margin-top: 36px;
+}
+
+.summary_content {
+    padding: 50px;
+
+}
+
+.summary_content2{
+
+}
+
+.light_desc {
     font-size: 24px;
     color: #B2B2B2;
     margin-bottom: 16px;
 }
 
-.history{
+.history {
     background-color: white;
     display: flex;
     flex-direction: column;
     margin-top: 36px;
 }
 
-.history_header{
+.history_header {
     display: flex;
     height: 108px;
     margin-left: 52px;
@@ -106,13 +199,13 @@
     align-items: center;
 }
 
-.history_header_title{
+.history_header_title {
     color: #000;
     font-size: 36px;
     font-weight: bold;
 }
 
-.history_item{
+.history_item {
     padding-left: 44px;
     padding-right: 40px;
     height: 152px;
@@ -122,7 +215,7 @@
     align-items: center;
 }
 
-.history_item_date{
+.history_item_date {
     color: #000;
     font-size: 50px;
     width: 60px;
@@ -133,7 +226,7 @@
     margin-right: 30px;
 }
 
-.history_item_detail_bg{
+.history_item_detail_bg {
     height: 68px;
     flex: 1;
     background-color: #F5F5F5;
@@ -143,12 +236,13 @@
     display: flex;
 }
 
-.history_item_detail_icon{
+.history_item_detail_icon {
     height: 32px;
     width: 32px;
     margin-right: 16px;
 }
-.no_more{
+
+.no_more {
     color: #B2B2B2;
     background-color: #f5f5f5;
     width: 750px;
@@ -158,7 +252,7 @@
     justify-content: center;
 }
 
-.current_history_item{
+.current_history_item {
     height: 140px;
     display: flex;
     flex-direction: row;
@@ -171,17 +265,17 @@
     align-items: center;
 }
 
-.current_item_period{
+.current_item_period {
     font-size: 24px;
     color: #B2B2B2;
 }
 
-.missed{
+.missed {
     color: #B2B2B2;
     font-size: 26px;
 }
 
-.recent_btn_bg{
+.recent_btn_bg {
     display: flex;
     align-items: center;
     justify-content: center;
@@ -190,7 +284,7 @@
     position: relative;
 }
 
-.recent_btn{
+.recent_btn {
     height: 56px;
     border-radius: 28px;
     width: 180px;
@@ -202,13 +296,13 @@
     font-size: 24px;
 }
 
-.arrow_hide{
+.arrow_hide {
     width: 34px;
     height: 34px;
     padding: 10px;
 }
 
-.no_current{
+.no_current {
     display: flex;
     height: 300px;
     color: #B2B2B2;
@@ -216,4 +310,43 @@
     justify-content: center;
     position: relative;
     width: 750px;
+}
+
+.tools{
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    position: relative;
+    height: 108px;
+    color: #5C7099;
+    font-size: 26px;
+    margin-top: 10px;
+}
+
+.upcoming{
+    height: 72px;
+    display: flex;
+    flex-direction: row;
+    align-items: center;
+    justify-content: center;
+}
+
+.calendar{
+    margin-right: 8px;
+    width: 26px;
+    height: 26px;
+}
+
+.more{
+    display: flex;
+    position: absolute;
+    right: 44px;
+    width: 74px;
+    height: 44px;
+    border-radius: 11px;
+    background-color: #B2B2B21A;
+    top: 32px;
+    align-items: center;
+    justify-content: center;
+
 }

+ 247 - 145
src/_health/pages/move.tsx

@@ -3,7 +3,7 @@ import './move.scss'
 import { useDispatch, useSelector } from "react-redux";
 import { getScenario, getThemeColor } from "@/features/health/hooks/health_hooks";
 import { useEffect, useState } from "react";
-import Taro from "@tarojs/taro";
+import Taro, { useDidShow } from "@tarojs/taro";
 import { checkStart, setResult } from "@/store/action_results";
 import RequestType, { thirdPartRequest } from "@/services/thirdPartRequest";
 import { setAuth } from "@/features/trackSomething/hooks/werun";
@@ -12,12 +12,15 @@ import { getActiveMoves, getActiveMovesCurrent, uploadActiveMoves } from "@/serv
 import dayjs from "dayjs";
 import { TimeFormatter } from "@/utils/time_format";
 import { jumpPage } from "@/features/trackTimeDuration/hooks/Common";
+import { rpxToPx } from "@/utils/tools";
+import { IconMore } from "@/components/basic/Icons";
 
 let timer
 export default function Move() {
     const health = useSelector((state: any) => state.health);
     const [allowRun, setAllowRun] = useState(false)
     const [loaded, setLoaded] = useState(false)
+    const [loadAuth, setLoadAuth] = useState(false)
     const [count, setCount] = useState(0)
     const [data, setData] = useState<any>(null)
     const [list, setList] = useState<any>([])
@@ -33,6 +36,22 @@ export default function Move() {
     useEffect(() => {
         getMovesCurrent()
         getMovesHistory()
+        checkAuth()
+        timer = setInterval(() => {
+            setCount((index) => index + 1)
+            const date = new Date()
+            if (date.getMinutes() == 10 && date.getSeconds() == 0) {
+                getMovesCurrent()
+                getMovesHistory()
+            }
+        }, 1000)
+
+        return () => {
+            clearInterval(timer)
+        }
+    }, [])
+
+    function checkAuth() {
         Taro.getSetting({
             success: res => {
                 //第一步,检测是否有授权 - 没有授权
@@ -44,23 +63,16 @@ export default function Move() {
                     setAllowRun(true)
                     Taro.setStorage({ key: 'auth', data: true })
                 }
+                setLoadAuth(true)
             }
         })
-        timer = setInterval(() => {
-            setCount((index) => index + 1)
-            const date = new Date()
-            if (date.getMinutes() == 10 && date.getSeconds() == 0) {
-                getMovesCurrent()
-                getMovesHistory()
-            }
-        }, 1000)
+    }
 
-        return () => {
-            clearInterval(timer)
-        }
-    }, [])
+    useDidShow(() => {
+        checkAuth()
+    })
 
-    global.updateMove = ()=>{
+    global.updateMove = () => {
         getMovesCurrent()
         getMovesHistory()
     }
@@ -101,6 +113,9 @@ export default function Move() {
         // if (getStatus() != 'open') {
         //     return
         // }
+        if (isExtra()) {
+            return;
+        }
         if (allowRun) {
             checkout()
         }
@@ -115,7 +130,6 @@ export default function Move() {
     }
 
     function refuseAuth() {
-        // setTitle('开启');
         Taro.setStorage({ key: 'auth', data: false })
         setAllowRun(false)
     }
@@ -145,15 +159,6 @@ export default function Move() {
         })
 
         thirdPartRequest(RequestType.RequestTypeWXRunData).then(res => {
-            // var params = {
-            //     is_manual: autoCheck ? 0 : 1,
-            //     timestamp: time,
-            //     encryptedData: (res as any).encryptedData,
-            //     iv: (res as any).iv,
-            //     date: strDate,
-            //     cloudID: (res as any).cloudID,
-            // }
-
             uploadActiveMoves({
                 wechat_run: {
                     encryptedData: (res as any).encryptedData,
@@ -193,7 +198,7 @@ export default function Move() {
         const currentHour = currentCheckHour()
         var isChecked = false
         data.hours.map((item) => {
-            if (item.hour == currentHour && item.status != 'NOT_CHECK') {
+            if (item.hour == currentHour && item.status != 'WFS') {
                 isChecked = true
             }
         })
@@ -213,69 +218,6 @@ export default function Move() {
         return 'upcoming'
     }
 
-    function currentFootDesc() {
-        var isChecked = currentHourChecked()
-        const minute = new Date().getMinutes()
-
-        if (isChecked && (minute < 10 || minute >= 50)) {
-            let time = new Date()
-            time.setMinutes(50)
-            time.setSeconds(0)
-            time.setMilliseconds(0)
-
-            // 获取总秒数
-            const totalSeconds = Math.floor((time.getTime() + 3600 * 1000 - new Date().getTime()) / 1000);
-
-            // 计算小时、分钟和秒
-            // const hours = String(Math.floor(totalSeconds / 3600)).padStart(2, '0');
-            const minutes = String(Math.floor((totalSeconds % 3600) / 60)).padStart(2, '0');
-            const seconds = String(totalSeconds % 60).padStart(2, '0');
-
-            // return `${hours}:${minutes}:${seconds}`;
-            return `Opening soon ${minutes}:${seconds}`
-        }
-
-
-        const pre = new Date().getTime()
-        const next = new Date().getTime() + 3600 * 1000
-        if (minute >= 50) {
-            return `Now open ${dayjs(pre).format('HH:50')}-${dayjs(next).format('HH:10')}`
-        }
-        else if (minute < 10) {
-            let time = new Date()
-            time.setMinutes(10)
-            time.setSeconds(0)
-            time.setMilliseconds(0)
-
-            // 获取总秒数
-            const totalSeconds = Math.floor((time.getTime() - new Date().getTime()) / 1000);
-
-            // 计算小时、分钟和秒
-            // const hours = String(Math.floor(totalSeconds / 3600)).padStart(2, '0');
-            const minutes = String(Math.floor((totalSeconds % 3600) / 60)).padStart(2, '0');
-            const seconds = String(totalSeconds % 60).padStart(2, '0');
-
-            // return `${hours}:${minutes}:${seconds}`;
-            return `Closing in ${minutes}:${seconds}`
-        }
-
-        let time = new Date()
-        time.setMinutes(50)
-        time.setSeconds(0)
-        time.setMilliseconds(0)
-
-        // 获取总秒数
-        const totalSeconds = Math.floor((time.getTime() - new Date().getTime()) / 1000);
-
-        // 计算小时、分钟和秒
-        // const hours = String(Math.floor(totalSeconds / 3600)).padStart(2, '0');
-        const minutes = String(Math.floor((totalSeconds % 3600) / 60)).padStart(2, '0');
-        const seconds = String(totalSeconds % 60).padStart(2, '0');
-
-        // return `${hours}:${minutes}:${seconds}`;
-        return `Opening soon ${minutes}:${seconds}`
-    }
-
     function getDuration() {
         let now = new Date()
         let pre = new Date().getTime()
@@ -303,7 +245,7 @@ export default function Move() {
         const hour = new Date(pre).getHours()
         for (var i = 0; i < data.hours.length; i++) {
             if (data.hours[i].hour == hour) {
-                return `${data.hours[i].real_steps}/${data.hours[i].target_steps}`
+                return `${data.hours[i].target_steps}`//`${data.hours[i].real_steps}/${data.hours[i].target_steps}`
             }
         }
     }
@@ -336,41 +278,6 @@ export default function Move() {
         return !isFind
     }
 
-    function currentContent() {
-        const hour = currentHourPeriod()
-        let info: any = null
-        data.hours.map(item => {
-            if (item.hour == hour) {
-                info = item
-            }
-        })
-
-        return <View className="move-header">
-            <View className="header-current">
-                <View className="current_intro">
-                    <View style={{ color: (new Date().getMinutes() < 10 && getStatus() == 'open') ? getThemeColor(health.mode) : '#b2b2b2' }}>{getDuration()}</View>
-                    {
-                        isExtra() ? <Text style={{ color: getThemeColor('SLEEP') }}>is extra</Text> : <Text>{getCurrentTarget()} steps</Text>
-                    }
-
-
-                </View>
-                <View className="log_btn" style={{
-                    backgroundColor: getStatus() == 'open' ? getThemeColor(health.mode) : '#6666661A',
-                    color: getStatus() == 'open' ? '#fff' : '#666666'
-                }} onClick={tapLog}>Log</View>
-                <View className="border_footer_line" />
-            </View>
-            <View className="current_footer">
-                <Text>{currentFootDesc()}</Text>
-                <Text onClick={() => {
-                    jumpPage(`/_health/pages/move_schedule?hours=${JSON.stringify(data.hours)}`)
-                }}>Show More</Text>
-            </View>
-
-        </View>
-    }
-
     function currentHistory(item) {
         var start = item.hour
         var isYesterday = start > new Date().getHours()
@@ -390,7 +297,7 @@ export default function Move() {
                 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 == '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')} />
@@ -405,7 +312,7 @@ export default function Move() {
 
         return <View className="summary">
             <View className="summary_header">
-                <Text>Active Hours</Text>
+                <Text>Last {hours.length} Hour{hours.length == 1 ? '' : 's'}</Text>
                 <Image className="arrow_hide"
                     onClick={() => {
                         setHideCurrentRecent(!hideCurrentRecent)
@@ -416,10 +323,9 @@ export default function Move() {
                 <View className="border_footer_line" />
             </View>
             {
-                !hideCurrentRecent && <View className="summary_content">
+                !hideCurrentRecent && <View className="summary_content2">
                     {
                         hours.map((item, index) => {
-                            // if (item.status == 'NOT_CHECK') return <View key={index} />
                             if (index >= 3 && !moreActive) return <View key={index} />
                             return <View key={index}>
                                 {
@@ -441,40 +347,236 @@ export default function Move() {
             {
                 hours.length == 0 && <View className="no_current">
                     <Text>No Active Hours Yet.</Text>
-                    <View className="border_footer_line"/>
+                    <View className="border_footer_line" />
                 </View>
             }
 
-            <View className="summary_footer">
-                <View className="summary_footer_item">
-                    <Text className="light_desc">Steps</Text>
-                    <Text>{data.stat.real_steps}/{data.stat.target_steps}</Text>
-                </View>
-                <View className="summary_footer_item">
-                    <Text className="light_desc">Calories</Text>
-                    <Text>{data.stat.real_calories}/{data.stat.target_calories}</Text>
+
+        </View>
+    }
+
+    function consoleFooter() {
+        if (getStatus() == 'open') {
+            if (new Date().getMinutes() < 10) {
+                let time = new Date()
+                time.setMinutes(10)
+                time.setSeconds(0)
+                time.setMilliseconds(0)
+
+                // 获取总秒数
+                const totalSeconds = Math.floor((time.getTime() - new Date().getTime()) / 1000);
+
+                // 计算小时、分钟和秒
+                // const hours = String(Math.floor(totalSeconds / 3600)).padStart(2, '0');
+                const minutes = String(Math.floor((totalSeconds % 3600) / 60)).padStart(2, '0');
+                const seconds = String(totalSeconds % 60).padStart(2, '0');
+
+                return <Text className="console_footer" style={{ color: getThemeColor('ACTIVE') }}>Ending soon {minutes}:{seconds}</Text>
+            }
+
+            const pre = new Date().getTime()
+            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()
+        if (isChecked && (minute < 10 || minute >= 50)) {
+            debugger
+            let time = new Date()
+            time.setMinutes(50)
+            time.setSeconds(0)
+            time.setMilliseconds(0)
+
+            // 获取总秒数
+            const totalSeconds = Math.floor((time.getTime() + 3600 * 1000 - new Date().getTime()) / 1000);
+
+            // 计算小时、分钟和秒
+            // const hours = String(Math.floor(totalSeconds / 3600)).padStart(2, '0');
+            const minutes = String(Math.floor((totalSeconds % 3600) / 60)).padStart(2, '0');
+            const seconds = String(totalSeconds % 60).padStart(2, '0');
+
+            // return `${hours}:${minutes}:${seconds}`;
+            return <Text className="console_footer">Opening soon {minutes}:{seconds}</Text>
+        }
+
+        let time = new Date()
+        time.setMinutes(50)
+        time.setSeconds(0)
+        time.setMilliseconds(0)
+
+        // 获取总秒数
+        const totalSeconds = Math.floor((time.getTime() - new Date().getTime()) / 1000);
+
+        // 计算小时、分钟和秒
+        // const hours = String(Math.floor(totalSeconds / 3600)).padStart(2, '0');
+        const minutes = String(Math.floor((totalSeconds % 3600) / 60)).padStart(2, '0');
+        const seconds = String(totalSeconds % 60).padStart(2, '0');
+        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();
+
+        // 将时间差转换为小时,最大为 23 小时
+        const hours2 = Math.min(Math.floor(timeDiff / (1000 * 60 * 60)), 23);
+
+        return hours2 + 1;
+    }
+
+    function hourIndex() {
+        const { start_time, end_time } = data.time_range
+        if (isExtra()) {
+            return hoursPassed(end_time)
+        }
+        return hoursPassed(start_time)
+
+    }
+
+    function becomeTime() {
+        var hour = new Date().getHours()
+        var tHour = parseInt(getDuration().substring(0, 2))
+        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() {
+
+        if (!allowRun) {
+            return <View className="move_console">
+                <Text className="console_duration" style={{ color: '#000', fontSize: rpxToPx(34) }}>你还没有开启步数授权</Text>
+                <Text className="console_open_tip">体验Move More需要您开启微信运动授权,点击下方按钮进行授权</Text>
+                <View className="console_checkbtn"
+                    onClick={tapLog}
+                    style={{ color: '#fff', backgroundColor: getThemeColor('ACTIVE') }}>去授权</View>
+            </View>
+        }
+
+        return <View className="move_console">
+            <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>
+            <Text className="console_duration">{getDuration()}</Text>
+            <Text className="console_desc" style={{
+                color: isExtra() ? getThemeColor('SLEEP') : getThemeColor('ACTIVE'),
+                fontSize: isExtra() ? rpxToPx(48) : rpxToPx(34)
+            }}>{
+                    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"
+                    onClick={tapLog}
+                    style={{ color: '#fff', backgroundColor: getThemeColor('ACTIVE') }}>I Think So!</View> :
+                    <View className="console_checkbtn" onClick={tapLog}>{becomeTime()}</View>
+            }
+            {
+                consoleFooter()
+            }
+
+
+        </View>
+    }
+
+    function tools() {
+        return <View className="tools">
+            <View className="upcoming" onClick={() => {
+                jumpPage(`/_health/pages/move_schedule?hours=${JSON.stringify(data.hours)}`)
+            }}>
+                <Image className="calendar" src={require('@assets/_health/calendar.png')} />
+                <Text>Upcoming{data.hours.length - hours.length > 0 ? `(${data.hours.length - hours.length})` : ''}</Text>
+            </View>
+            <View className="more" onClick={() => {
+                jumpPage('./move_setting_time')
+            }}>
+                <IconMore width={17} color="#b2b2b2" />
+            </View>
+        </View>
+    }
+
+    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' }}>最近更新于 {dayjs(data.last_updated).format('HH:mm')}</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_item">
-                    <Text className="light_desc">Distance</Text>
-                    <Text>{data.stat.real_distance}/{data.stat.target_distance}</Text>
+                <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>
     }
 
-    if (!loaded) return <View />
+    if (!loaded || !loadAuth) return <View />
 
     return <View style={{ display: 'flex', flexDirection: 'column' }}>
         <Text className="move-title">Move Every Hour</Text>
-        <Text onClick={() => {
-            jumpPage('./move_setting_time')
-        }}>Setting</Text>
         {
-            currentContent()
+            console()
+        }
+        {
+            tools()
+        }
+        {
+            summary()
         }
         {
             activeHour()
         }
+
         {
             list.length > 0 && <View className="history">
                 <View className="history_header">

+ 99 - 10
src/_health/pages/move_schedule.tsx

@@ -2,9 +2,10 @@ import { View, Text, Image, Switch } from "@tarojs/components";
 import './move_schedule.scss'
 import { useRouter } from "@tarojs/taro";
 import { useEffect, useState } from "react";
-import { createSchedule, getMoveSchedules } from "@/services/health";
+import { createSchedule, getActiveMovesCurrent, getMoveSchedules } from "@/services/health";
 import { jumpPage } from "@/features/trackTimeDuration/hooks/Common";
 import { getThemeColor } from "@/features/health/hooks/health_hooks";
+import { rpxToPx } from "@/utils/tools";
 
 let useRoute;
 let useNavigation;
@@ -18,6 +19,7 @@ https://fast-dev.oss-cn-beijing.aliyuncs.com/users/b13170f6916a27713c118a5ce75ea
 https://fast-dev.oss-cn-beijing.aliyuncs.com/users/b13170f6916a27713c118a5ce75ea74a/food-journal/2024/88620d96-c898-472f-8783-d425bb91333a_20240910114628_698.png?x-oss-process=image/resize,w_50,limit_0
 */
 
+let timer;
 export default function MoveSchedule() {
     let router
     let navigation;
@@ -32,21 +34,105 @@ export default function MoveSchedule() {
         router = useRouter()
     }
 
-    const [hours, setHours] = useState(JSON.parse(router.params.hours))
+    const [hours, setHours] = useState<any>([])
     const [loaded, setLoaded] = useState(false)
+    const [count, setCount] = useState(1)
 
 
     useEffect(() => {
-        getMoveSchedules().then(res => {
-            console.log(hours)
+        getData()
+        timer = setInterval(() => {
+            setCount((count => count + 1))
+            var now = new Date()
+            if (now.getMinutes() == 10 && now.getSeconds() == 0) {
+                getData()
+            }
+        }, 1000)
+        // getMoveSchedules().then(res => {
+        //     console.log(hours)
+        //     setLoaded(true)
+        // })
+
+        return () => {
+            clearInterval(timer)
+        }
+    }, [])
+
+    function getData() {
+        getActiveMovesCurrent().then(res => {
             setLoaded(true)
+            setHours((res as any).hours)
         })
-    }, [])
+    }
 
     // console.log(JSON.parse(router.params.hours))
     if (!loaded) return <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 firstTime(index) {
+        var firstIndex = -1;
+        var selHour = -1;
+        for (var i = 0; i < hours.length; i++) {
+            if (hours[i].status == 'WFS') {
+                firstIndex = i;
+                selHour = hours[i].hour
+                break;
+            }
+        }
+        if (firstIndex == index) {
+            var endDate = new Date()
+            endDate.setHours(selHour)
+            endDate.setMinutes(10);
+            endDate.setSeconds(0);
+            endDate.setMilliseconds(0);
+            var endTimestamp = endDate.getTime()+3600*1000;
+            if (new Date().getTime()>endTimestamp){
+                endTimestamp += 24*3600*1000
+            }
+
+            if (endTimestamp-new Date().getTime()>20*60*1000){
+                return <Text>countdown {formatTimeInterval(new Date().getTime(),endTimestamp)} </Text>
+            }
+            else if (endTimestamp-new Date().getTime()<10*60*1000){
+                <Text style={{ color: getThemeColor('ACTIVE') }}>Closing soon in {formatTimeInterval(new Date().getTime(),endTimestamp)} </Text>
+            }
+            return <Text>available </Text>
+        }
+        return <Text></Text>
+    }
+
     return <View style={{ display: 'flex', flexDirection: 'column' }}>
+        {
+            hours.length == 0 && <View style={{ display: 'flex', flex: 1, flexDirection: 'column', alignItems: 'center', marginTop: rpxToPx(110) }}>
+                <Image src={require('@assets/_health/sleep2.png')} style={{ width: rpxToPx(64), height: rpxToPx(64) }} />
+                <Text style={{ marginTop: rpxToPx(24), fontSize: rpxToPx(50), fontWeight: 'bold', color: '#B2B2B2' }}>睡个好觉</Text>
+            </View>
+        }
         {
             hours.map((item, index) => {
                 var start = item.hour
@@ -60,7 +146,10 @@ export default function MoveSchedule() {
                 return <View key={index} className="schedule_item">
                     <View className="schedule_item_left">
                         <View>{open}:50</View>
-                        <View className="schedule_item_time">{parseInt(start) < new Date().getHours() ? '明天' : ''}{start}:00-{end}:00</View>
+                        <Text>
+                            {firstTime(index)}
+                            <Text className="schedule_item_time">Check in for {parseInt(start) < new Date().getHours() ? '' : ''}{start}:00-{end}:00</Text>
+                        </Text>
                         {/* <Text className="schedule_item_target">{item.real_steps}/{item.target_steps} steps</Text> */}
                     </View>
                     <View style={{ flex: 1 }} />
@@ -72,13 +161,13 @@ export default function MoveSchedule() {
                                 reminder: e.detail.value
                             }],
                         }).then(res => {
-                            if (global.refreshWindow){
+                            if (global.refreshWindow) {
                                 global.refreshWindow()
                             }
-                            if (global.refreshSchedules){
+                            if (global.refreshSchedules) {
                                 global.refreshSchedules()
                             }
-                            if (global.updateMove){
+                            if (global.updateMove) {
                                 global.updateMove()
                             }
                         })
@@ -93,7 +182,7 @@ export default function MoveSchedule() {
                 </View>
             })
         }
-        <Text className="no_more" onClick={() => {
+        <Text className="no_more" style={{ color: '#5C7099' }} onClick={() => {
             jumpPage('./move_setting_reminder')
         }}>Full schedules</Text>
     </View>

+ 11 - 5
src/_health/pages/schedules_edit.tsx

@@ -40,6 +40,12 @@ export default function SchedulesEdit() {
             case 'ACTIVE':
                 windows = 'ACTIVE,SLEEP';
                 break
+            case 'DAY':
+                windows = 'DAY,NIGHT';
+                break
+            case 'NIGHT':
+                windows = 'ACTIVE,DAY';
+                break
         }
         getSchedules({ window: windows }).then(res => {
             if ((res as any).data && (res as any).data.length > 0) {
@@ -148,11 +154,11 @@ export default function SchedulesEdit() {
                                             }
                                         ]}
                                         onClick={() => {
-                                            if (item.window=='ACTIVE'||item.window=='EAT'){
-                                                if (item.list.length==1){
+                                            if (item.window == 'ACTIVE' || item.window == 'EAT') {
+                                                if (item.list.length == 1) {
                                                     Taro.showToast({
-                                                        title:'至少保留一项',
-                                                        icon:'none'
+                                                        title: '至少保留一项',
+                                                        icon: 'none'
                                                     })
                                                     return;
                                                 }
@@ -224,7 +230,7 @@ export default function SchedulesEdit() {
                                             }
 
 
-                                            {!obj.is_all_day &&<Switch checked={obj.reminder}
+                                            {(!obj.is_all_day || health.mode=='DAY' || health.mode=='NIGHT') && <Switch checked={obj.reminder}
                                                 color={getThemeColor(obj.window)}
                                                 onChange={e => {
                                                     if (process.env.TARO_ENV == 'weapp') {

BIN
src/assets/_health/calendar.png


BIN
src/assets/_health/sleep2.png


+ 0 - 1
src/features/health/HeaderCircadian.tsx

@@ -156,7 +156,6 @@ export default function HeaderCircadian() {
                     }
                     break;
                 case 'ACTIVE':
-                    debugger
                     {
                         setIcon(require('@assets/_health/active.png'))
                         setTitle('Activity logging')

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

@@ -345,6 +345,8 @@ export default function MainConsole(props: { type: WindowType }) {
 
         var duration = 24 * 3600 * 1000//fastData.target.duration
 
+        global.set_time = new Date().getTime()
+
         return <View className="modal_content">
             <ConsolePicker ref={limitPickerRef}
                 themeColor={color}
@@ -379,7 +381,11 @@ export default function MainConsole(props: { type: WindowType }) {
             check_items: [{
                 schedule_id: selItem.schedule_id,
                 date: dayjs(t1).format('YYYYMMDD'),
-                timestamp: t1
+                timestamp: t1,
+                extra: {
+                    set_time: global.set_time?global.set_time:new Date().getTime(),
+                    confirm_time: new Date().getTime()
+                }
             }]
         }).then(res => {
             dispatch(setShowActionTip({

+ 1 - 1
src/pages/account/PhotoWall.tsx

@@ -54,7 +54,7 @@ export default function PhotoWall() {
         })
     }
     return <View style={{ display: 'flex', flexDirection: 'column' }}>
-        {/* <NewTimePicker /> */}
+        <NewTimePicker />
         <View className="photo_wall">
             {
                 list.map((item, index) => {