Leon 1 year ago
parent
commit
0382568bad

+ 7 - 6
src/app.config.ts

@@ -4,15 +4,16 @@ const appConfig = defineAppConfig({
   pages: [
     // 'pages/clock/Index',
     'pages/clock/Clock',
-    'pages/clock/Suggest',
-    'pages/clock/SetGoal',
-    'pages/demo',
-    'pages/food/Food',
+    // 'pages/clock/Suggest',
+    // 'pages/clock/SetGoal',
+    'pages/clock/AddEat',
+    // 'pages/demo',
+    // 'pages/food/Food',
     'pages/account/Login',
     'pages/account/Auth',
     'pages/account/ChooseAuth',
-    'pages/clock/ChooseScenario',
-    'pages/clock/SetSchedule',
+    // 'pages/clock/ChooseScenario',
+    // 'pages/clock/SetSchedule',
     'pages/common/H5',
     'pages/account/Profile',
     'pages/metric/Metric',

+ 5 - 0
src/features/trackTimeDuration/components/MainCard.scss

@@ -48,6 +48,11 @@
     color: #CACACA;
 }
 
+.fast_log_eat_btn{
+    color:#FE810C;
+    background-color: rgba(254, 129, 12, 0.2);
+}
+
 .schedule {
     display: flex;
     flex-direction: column;

+ 125 - 10
src/features/trackTimeDuration/components/MainFastEatCard.tsx

@@ -7,13 +7,14 @@ import Rings, { RingCommon, BgRing, TargetRing, CurrentDot } from "@/features/tr
 import dayjs from "dayjs";
 import moment from 'moment-timezone'
 import { MainColorType } from "@/context/themes/color";
-import { fastWindow } from "@/services/trackTimeDuration";
+import { fastWindow, setSchedule, updateRecord } from "@/services/trackTimeDuration";
 import { useSelector } from "react-redux";
 import { jumpPage } from "../hooks/Common";
 import ConsolePicker from "./ConsolePicker";
 import { endFast, startFast } from "../actions/TrackTimeActions";
 import formatMilliseconds from "@/utils/format_time";
-
+import TimePicker from "@/features/common/TimePicker";
+import showAlert from "@/components/basic/Alert";
 let useNavigation;
 
 let Linking, PushNotification;
@@ -46,7 +47,8 @@ export default function MainFastEatCard(props: { count: any }) {
     const [startTime, setStartTime] = useState<any>(null)
     const [endTime, setEndTime] = useState<any>(null)
     const [status, setStatus] = useState<any>('upcoming')
-
+    const [showPicker, setShowPicker] = useState(false)
+    const [isStart, setIsStart] = useState(true)
     const [loaded, setLoaded] = useState(false)
 
     const user = useSelector((state: any) => state.user);
@@ -85,6 +87,10 @@ export default function MainFastEatCard(props: { count: any }) {
         }
     }, [props.count])
 
+    global.refreshFastEat = () => {
+        refresh()
+    }
+
     function refresh() {
         fastWindow().then(res => {
             const { eat_win, fast_win } = res
@@ -367,15 +373,17 @@ export default function MainFastEatCard(props: { count: any }) {
     }
 
     function tapFastStart() {
-
+        setIsStart(true)
+        setShowPicker(true)
     }
 
     function tapFastEnd() {
-
+        setIsStart(false)
+        setShowPicker(true)
     }
 
     function tapStartLog() {
-        if (status == 'upcoming'){
+        if (status == 'upcoming') {
             return;
         }
         if (!user.isLogin) {
@@ -416,6 +424,19 @@ export default function MainFastEatCard(props: { count: any }) {
         </Modal>
     }
 
+    function timeContent() {
+        return <Modal
+            testInfo={null}
+            dismiss={() => {
+                setShowPicker(false)
+            }}
+            confirm={() => { }}>
+            {
+                pickerContent()
+            }
+        </Modal>
+    }
+
     function timePickerContent() {
         var title = operateType == 'endFast' ? '结束断食' : '开始断食'
         var color = MainColorType.fast
@@ -450,6 +471,70 @@ export default function MainFastEatCard(props: { count: any }) {
         </View>
     }
 
+    function pickerContent() {
+        const timestamp = isStart ? fastData.fast.target_start_time : fastData.fast.target_end_time
+        const strTime = dayjs(timestamp).format('HH:mm')
+        return <TimePicker time={strTime}
+            color={MainColorType.fast}
+            title={isStart ? '开始断食' : '结束断食'}
+            confirm={(e) => {
+                confirmPickerTime(e)
+            }}
+            cancel={() => {
+                setShowPicker(false)
+            }} />
+    }
+
+    function confirmPickerTime(strTime) {
+        if (status != 'process') {
+            setSchedule({
+                code: isStart ? 'FAST_START' : 'FAST_END',
+                time: strTime
+            }).then(res => {
+                refresh()
+                setShowPicker(false)
+            })
+        }
+        else {
+            var startTime = dayjs(fastData.fast.target_start_time).format('HH:mm:ss')
+            var endTime = strTime + ':00'
+            console.log(startTime, endTime)
+            if (startTime == endTime) {
+                showAlert({
+                    title: '',
+                    content: '开始时间不能与结束时间相同'
+                })
+                return;
+            }
+
+            updateRecord({
+                fast: {
+                    target_duration: getIntervalSeconds(startTime, endTime) * 1000
+                }
+            }, fastData.id).then(res => {
+                refresh()
+                setShowPicker(false)
+            })
+        }
+    }
+
+    function getIntervalSeconds(time1, time2) {
+        // 将时间字符串转换为 Date 对象
+        const date1 = new Date(`2000-01-01T${time1}Z`);
+        const date2 = new Date(`2000-01-01T${time2}Z`);
+
+        // 计算两个 Date 对象之间的时间差
+        let intervalMs = date2.getTime() - date1.getTime();
+
+        // 如果 time2 比 time1 小, 说明跨天了, 需要加上一天的毫秒数
+        if (date2 < date1) {
+            intervalMs += 24 * 60 * 60 * 1000;
+        }
+
+        // 返回间隔秒数
+        return Math.floor(intervalMs / 1000);
+    }
+
     function pickerConfirm(t1: number, event: any) {
         if (btnDisable) {
             return
@@ -484,6 +569,13 @@ export default function MainFastEatCard(props: { count: any }) {
         }
     }
 
+    function goAddEat(index) {
+        if (status == 'process') {
+            return;
+        }
+        jumpPage('/pages/clock/AddEat?meal=' + JSON.stringify(eatData.meals[index]))
+    }
+
     if (!loaded) {
         return <View />
     }
@@ -505,7 +597,7 @@ export default function MainFastEatCard(props: { count: any }) {
                 <Text className="date1">{global.language == 'en' ? formatTime('dddd, MMM D') : formatTime('MMMD日 dddd')}</Text>
             </View>
         </View>
-        <View>{isFastMode?formatMilliseconds(fastData.fast.target_duration):formatMilliseconds(eatData.target_end_time-eatData.target_start_time)}</View>
+        <View>{isFastMode ? formatMilliseconds(fastData.fast.target_duration) : formatMilliseconds(eatData.target_end_time - eatData.target_start_time)}</View>
         {
             isFastMode && <View>
                 <View className="log_row">
@@ -521,8 +613,8 @@ export default function MainFastEatCard(props: { count: any }) {
                     }
 
                     {
-                        status == 'process' ? <View className="schedule_time">{dayjs(fastData.fast.target_start_time).format('HH:mm')}</View> : 
-                        <View onClick={tapStartLog} className={status == 'new' ? "fast_log_btn" : "fast_log_btn fast_log_btn_disable"}>Log{status == 'new' && <View className="badge" />}</View>
+                        status == 'process' ? <View className="schedule_time">{dayjs(fastData.fast.target_start_time).format('HH:mm')}</View> :
+                            <View onClick={tapStartLog} className={status == 'new' ? "fast_log_btn" : "fast_log_btn fast_log_btn_disable"}>Log{status == 'new' && <View className="badge" />}</View>
                     }
 
 
@@ -533,13 +625,33 @@ export default function MainFastEatCard(props: { count: any }) {
                             Fast ends:
                         </Text>
                         <Text className="schedule_time">
-                            {status == 'process' ?dayjs(fastData.fast.target_end_time).format('HH:mm'):endScheduleTime}
+                            {status == 'process' ? dayjs(fastData.fast.target_end_time).format('HH:mm') : endScheduleTime}
                         </Text>
                     </View>
                     <View onClick={tapEndLog} className={status == 'process' ? "fast_log_btn" : "fast_log_btn fast_log_btn_disable"}>Log</View>
                 </View>
             </View>
         }
+        {
+            !isFastMode && <View>
+                {
+                    eatData.meals.map((item, index) => {
+                        return <View className="log_row" key={index}>
+                            <View className="schedule">
+                                <Text className="schedule_name">{item.name}</Text>
+                                <Text className="schedule_time">
+                                    {item.schedule_start_time}
+                                </Text>
+                            </View>
+                            {
+                                item.real_start_time ? <View className="fast_log_btn fast_log_btn_disable">已上传</View> : <View onClick={() => goAddEat(index)} className={status == 'process' ? "fast_log_btn fast_log_btn_disable" : "fast_log_btn fast_log_eat_btn"}>Add</View>
+                            }
+
+                        </View>
+                    })
+                }
+            </View>
+        }
 
 
 
@@ -552,5 +664,8 @@ export default function MainFastEatCard(props: { count: any }) {
         {
             showTimePicker && modalContent()
         }
+        {
+            showPicker && timeContent()
+        }
     </View>
 }

+ 38 - 0
src/pages/clock/AddEat.scss

@@ -0,0 +1,38 @@
+.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;
+    align-items: center;
+    justify-content: center;
+    display: flex;
+}
+
+.cover{
+    width: 200px;
+    height: 200px;
+}

+ 208 - 0
src/pages/clock/AddEat.tsx

@@ -0,0 +1,208 @@
+import { View, Text, Textarea,Image } from "@tarojs/components";
+import Taro, { useRouter } from "@tarojs/taro";
+import { useTranslation } from "react-i18next";
+import './AddEat.scss'
+import { useEffect, useState } from "react";
+import { saveFoodCache } from "@/features/food/hooks/ExtraData";
+import { baseUrl } from "@/services/http/api";
+import { checkAuthorized } from "@/utils/check_authorized";
+import { eatMeals } from "@/services/trackSomething";
+
+
+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 AddEat() {
+    const [desc, setDesc] = useState('')
+    const { t } = useTranslation()
+    const [imgUrl, setImgUrl] = useState('')
+    const [startTime, setStartTime] = useState(0)
+    const [endTime, setEndTime] = useState(0)
+
+    let router
+    let navigation;
+    if (useNavigation) {
+        navigation = useNavigation()
+    }
+
+    if (process.env.TARO_ENV == 'rn') {
+        router = useRoute()
+    }
+    else {
+        router = useRouter()
+    }
+
+    const meal = JSON.parse(router.params.meal)
+
+    useEffect(() => {
+        setStartTime(meal.target_start_time)
+        setEndTime(meal.target_end_time)
+     }, [])
+
+    function getIntervalHoursAndMinutes(time1, time2) {
+        // 将时间字符串转换为 Date 对象
+        const date1 = new Date(`2000-01-01T${time1}Z`);
+        const date2 = new Date(`2000-01-02T${time2}Z`);
+
+        // 计算两个 Date 对象之间的时间差
+        let intervalMs = Math.abs(date2.getTime() - date1.getTime());
+
+        // 如果 time2 比 time1 小, 说明跨天了, 需要加上一天的毫秒数
+        if (date2 < date1) {
+            intervalMs += 24 * 60 * 60 * 1000;
+        }
+
+        // 计算小时和分钟差
+        const intervalSeconds = Math.floor(intervalMs / 1000);
+        const hours = Math.floor(intervalSeconds / 3600);
+        const minutes = Math.floor((intervalSeconds % 3600) / 60);
+
+        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 tapTime() {
+
+    }
+
+    function tapDuration() {
+
+    }
+
+    function addImage() {
+        Taro.chooseMedia({
+            count: 1,
+            sizeType: ['compressed'],
+            mediaType: ['image'],
+            sourceType: ['album'],
+            success: function (res) {
+                chooseSuccess(res, true)
+                checkAuthorized()
+            },
+            fail: function (res) {
+            }
+        })
+    }
+
+    function save() {
+        eatMeals({
+            code: meal.code,
+            description: desc,
+            real_start_time: startTime,
+            real_end_time: endTime,
+            media: [{
+                imgUrl,
+                type: imgUrl.indexOf('mp4') != -1 ? 'video' : 'image',
+                source: 'album'
+            }],
+        }).then(res => {
+            if (process.env.TARO_ENV == 'weapp') {
+                Taro.navigateBack();
+            }
+
+            global.refreshFastEat()
+        })
+    }
+
+    async function chooseSuccess(res, isAlbum) {
+        var params = {
+            event: 'add_a_picture',
+            value: isAlbum ? 'choose_from_album_confirm' : 'use_camera_confirm',
+        }
+
+        saveFoodCache('create', params)
+
+        var savedFilePath = process.env.TARO_ENV == 'rn' ? res.tempFiles[0].path : res.tempFiles[0].tempFilePath
+
+        // var savedFilePath = res.savedFilePath
+        await Taro.setStorage({ key: 'pic', data: savedFilePath })
+        setImgUrl(savedFilePath as any)
+
+        uploadFile(savedFilePath, isAlbum ? 'album' : 'camera')
+
+
+    }
+
+    function uploadFile(path, source) {
+        Taro.showLoading({
+            title: '加载中'
+        })
+        var dot = path.lastIndexOf('.')
+        var fileExt = dot > 0 ? path.substring(dot) : ''
+        Taro.request({
+            method: 'GET',
+            url: `${baseUrl}/api/thirdparty/aliyun/oss-form-upload`,
+            header: {
+                'Authorization': 'bearer ' + global.token
+            },
+            data: {
+                type: 'FOOD_JOURNAL',
+                file_ext: fileExt
+            },
+            success: (rsp) => {
+                if (rsp.statusCode != 200) {
+                    Taro.showToast({
+                        title: '操作失败,请检查网络',
+                        icon: 'none'
+                    })
+                    return
+                }
+                Taro.uploadFile({
+                    url: rsp.data.upload_url,
+                    filePath: path,
+                    name: 'file',
+                    formData: rsp.data.fields,
+                    success: rlt => {
+                        setImgUrl(rsp.data.view_url)
+                        Taro.hideLoading()
+                        // createData(rsp.data.view_url, source)
+
+                        // uploadAvatar(rsp.data.view_url)
+                        // _this.changeAvatar(rsp.data.view_url);
+                    },
+                    fail: rlt => {
+                        Taro.hideLoading()
+                        Taro.showModal({
+                            content: rlt.errMsg
+                        })
+                    }
+                })
+            }
+        })
+    }
+
+
+    return <View>
+        <View className="header">
+            <Text className="time1" onClick={tapTime}>{meal.schedule_start_time}</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>
+        }
+        
+        <View className="save" onClick={save}>Save</View>
+        {/* <Text> add eat detail</Text> */}
+    </View>
+}

+ 2 - 0
src/services/http/api.js

@@ -53,6 +53,8 @@ export const API_CLOCK_RECORD_UPDATE = `${baseUrl}/api/clock/records`
 export const API_CLOCK_STREAKS = `${baseUrl}/api/clock/streaks`
 export const API_FAST_WINDOW = `${baseUrl}/api/fast/fast-eat-windows`
 export const API_SLEEP_WINDOW = `${baseUrl}/api/fast/sleep-active-windows`
+export const API_SET_SCHEDULE = `${baseUrl}/api/fast/schedules`
+export const API_EAT_MEALS = `${baseUrl}/api/eat/meals`
 
 
 

+ 11 - 1
src/services/trackSomething.tsx

@@ -1,5 +1,5 @@
 
-import { API_ACTIVITY_CARDS, API_ACTIVITY_RECORDS, API_METRIC_CARDS, API_METRIC_FOLLOWS, API_METRIC_GROUPS, API_METRIC_RECORDS, API_METRIC_STANDARD, API_UPLOAD_STEPS } from './http/api';
+import { API_ACTIVITY_CARDS, API_ACTIVITY_RECORDS, API_EAT_MEALS, API_METRIC_CARDS, API_METRIC_FOLLOWS, API_METRIC_GROUPS, API_METRIC_RECORDS, API_METRIC_STANDARD, API_UPLOAD_STEPS } from './http/api';
 import { request } from './http/request';
 
 export const uploadSteps = (params) => {
@@ -173,3 +173,13 @@ export const metricFollows = (params) => {
     })
 }
 
+export const eatMeals = (params) =>{
+    return new Promise((resolve) => {
+        request({
+            url: API_EAT_MEALS, method: 'POST', data: { ...params }
+        }).then(res => {
+            resolve(res);
+            // dispatch(loginSuccess(res));
+        })
+    })
+}

+ 11 - 1
src/services/trackTimeDuration.tsx

@@ -1,6 +1,6 @@
 
 
-import { API_FAST_PLANS, API_FAST_CHECKS, API_FAST_CLOCKS, API_CLOCK_RECORDS, API_CLOCK_HOME, API_CLOCK_STATS, API_CLOCK_SUMMARY_RECORDS, API_CLOCK_RECORD_UPDATE, API_CLOCK_STREAKS, API_EAT_WAKES, API_FAST_WINDOW, API_SLEEP_WINDOW } from './http/api'
+import { API_FAST_PLANS, API_FAST_CHECKS, API_FAST_CLOCKS, API_CLOCK_RECORDS, API_CLOCK_HOME, API_CLOCK_STATS, API_CLOCK_SUMMARY_RECORDS, API_CLOCK_RECORD_UPDATE, API_CLOCK_STREAKS, API_EAT_WAKES, API_FAST_WINDOW, API_SLEEP_WINDOW, API_SET_SCHEDULE } from './http/api'
 import { request } from './http/request';
 import { getLocalPush } from '@/features/trackTimeDuration/actions/TrackTimeActions';
 
@@ -179,4 +179,14 @@ export const sleepWindow = () => {
             resolve(res);
         })
     })
+}
+
+export const setSchedule = (params: any) => {
+    return new Promise((resolve) => {
+        request({
+            url: API_SET_SCHEDULE, method: 'POST', data: { ...params }
+        }).then(res => {
+            resolve(res);
+        })
+    })
 }