leon 1 年間 前
コミット
b7cf78dbfb

+ 0 - 2
src/_health/base/new_durationpicker.tsx

@@ -15,8 +15,6 @@ export default function NewDurationPicker(props: { value?: any, onChange?: any,
     const [items, setItems] = useState<any>([[0]])
     const [values, setValues] = useState<any>([0])
     const [loaded, setLoaded] = useState(false)
-
-    console.log('picker value', props.value)
     useEffect(() => {
         switch (props.type) {
             case DurationPickerType.normal:

+ 3 - 1
src/_health/pages/guide_begin.tsx

@@ -7,6 +7,7 @@ import { useTranslation } from "react-i18next";
 import { useSelector } from "react-redux";
 import Taro from "@tarojs/taro";
 import { useEffect } from "react";
+import { BASE_IMG_URL } from "@/services/http/api";
 
 export default function GuideBegin() {
     const user = useSelector((state: any) => state.user);
@@ -33,7 +34,8 @@ export default function GuideBegin() {
         <View className="h50 bold">{t('health.guide_begin_title')}</View>
         <View className="h30 g01" style={{ marginTop: rpxToPx(12), marginBottom: rpxToPx(72) }}>{t('health.guide_begin_desc')}</View>
         <View style={{ width: rpxToPx(400), height: 1, backgroundColor: '#000', opacity: 0.1, marginBottom: rpxToPx(48) }} />
-        <Image style={{ width: rpxToPx(340), height: rpxToPx(352), marginBottom: rpxToPx(158) }} src={global.language == 'en' ? require('@assets/_health/guide_en.png') : require('@assets/_health/guide_zh.png')} />
+        <Image style={{ width: rpxToPx(340), height: rpxToPx(352), marginBottom: rpxToPx(158) }}
+            src={global.language == 'en' ? BASE_IMG_URL + 'guide_en.png' : BASE_IMG_URL + 'guide_zh.png'} />
         <NewButton
             type={NewButtonType.fill}
             color={MainColorType.fast}

+ 13 - 13
src/app.config.ts

@@ -89,20 +89,20 @@ const appConfig = defineAppConfig({
         'pages/map'
       ]
     },
-    {
-      root: '_record',
-      pages: [
-        'pages/time_record',
-        'pages/log_record',
-      ]
-    }
+    // {
+    //   root: '_record',
+    //   pages: [
+    //     'pages/time_record',
+    //     'pages/log_record',
+    //   ]
+    // }
   ],
-  preloadRule: {
-    "pages/clock/Clock": {
-      "network": "all",
-      "packages": ["_record"]
-    }
-  },
+  // preloadRule: {
+  //   "pages/clock/Clock": {
+  //     "network": "all",
+  //     "packages": ["_record"]
+  //   }
+  // },
   usingComponents: {
     'mysvg': './components/basic/svg',
     // 't-popup':'tdesgin-miniprogram/popup/popup'

BIN
src/assets/_health/guide_en.png


BIN
src/assets/_health/guide_zh.png


+ 1 - 112
src/pages/clock/Clock.tsx

@@ -1,21 +1,14 @@
 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, useSelector } from "react-redux";
 import { getInfoSuccess } from "@/store/user";
 import { useTranslation } from "react-i18next";
-import { MainColorType } from "@/context/themes/color";
+
 import { IconMap, 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";
-import HeaderCircadian from "@/features/health/HeaderCircadian";
 import { rpxToPx } from "@/utils/tools";
-import AddLabel from "@/_health/components/add_label";
-import { jumpPage } from "@/features/trackTimeDuration/hooks/Common";
-import showAlert from "@/components/basic/Alert";
 import { setTitle } from "@/store/health";
 import { globalSetting } from "@/services/common";
 import ClockIndex from "./ClockIndex";
@@ -259,108 +252,4 @@ export default function Clock() {
         return <View />
 
     return <ClockIndex />
-
-    return <View style={{ flex: 1, position: 'relative' }}>
-        <View style={{ height: navigationBarHeight, backgroundColor: MainColorType.g05 }} />
-        <View className="navi-bar" style={{ height: navigationBarHeight, zIndex: 1000, backgroundColor: showCalendar ? '#fff' : MainColorType.g05 }}>
-            <View className="navi-streak" style={{
-                height: 44,
-                marginTop: systemInfo.statusBarHeight,
-                alignItems: 'center',
-                justifyContent: 'center',
-                paddingLeft: 0,
-                fontWeight: 'bold',
-                fontSize: 17
-            }} >{health.title}</View>
-
-            {
-                showStreak && steakIcon()
-            }
-            {
-                locationIcon()
-            }
-
-            <View style={{
-                position: 'absolute',
-                left: 60,
-                right: 60,
-                bottom: 0,
-                height: 44,
-                display: 'flex',
-                alignItems: 'center',
-                justifyContent: 'center'
-            }}>
-                <HeaderCircadian />
-            </View>
-
-
-        </View>
-        {/* <ClockIndex /> */}
-        <ClockNew onScroll={onScroll}>
-            {
-                showCalendar && <Streak testInfo={null}
-                    // top={scrollTop}
-                    top={navigationBarHeight}
-                    dismiss={() => {
-                        setShowCalendar(false)
-                    }}
-                    confirm={() => { }}>
-                    <View style={{ display: 'flex', flexDirection: 'column' }}>
-                        {/* <View style={{ height: navigationBarHeight }} /> */}
-                        <Calendar year={2024} month={new Date().getMonth() + 1} mode={health.mode} />
-                    </View>
-                </Streak>
-            }
-        </ClockNew>
-        {
-            showModal && <AddLabel labels={labels}
-                window='ACTIVE'
-                disMiss={() => setShowModal(false)}
-                op_page='SCHEDULE_ACTIVE_SLEEP'
-                // onlyCheck={true}
-                // schedules={[]}
-                confirm={(res) => {
-
-                    if ((res as any).result) {
-                        global.refreshWindow()
-                        setShowModal(false)
-                        // dispatch(setSchedules((res as any).schedules))
-                        // dispatch(setFooter((res as any).footer))
-                        // setList((res as any).schedules)
-                        // setErrors([])
-                    }
-                    else {
-                        showAlert({
-                            title: t('health.schedule_conflict'),
-                            content: t('health.conflict_desc'),
-                            showCancel: false,
-                            confirmText: t('health.check_conflict'),
-                            confirm: () => {
-                                setTimeout(() => {
-                                    setShowModal(false)
-                                }, 1000)
-
-                                if ((res as any).show_conflict_dialog) {
-                                    jumpPage('/_health/pages/schedules?mode=' + '&error=' + JSON.stringify(res))
-                                }
-                                else {
-                                    jumpPage('/_health/pages/schedules?mode=' + health.mode + '&error=' + JSON.stringify(res))
-                                }
-
-
-                                // jumpPage(`./schedules?mode=&schedules=${JSON.stringify((res as any).schedules)}&errors=${JSON.stringify((res as any).error_messages)}`)
-                            }
-                        })
-
-                        // setList((res as any).schedules)
-                        // dispatch(setFooter((res as any).footer))
-                        // setErrors((res as any).error_messages ? (res as any).error_messages : [])
-                    }
-
-                }}
-                color={MainColorType.active} />
-        }
-
-
-    </View>
 }

+ 271 - 0
src/pages/clock/ClockIndex copy.tsx

@@ -0,0 +1,271 @@
+import { jumpPage } from "@/features/trackTimeDuration/hooks/Common";
+import { View, Image, ScrollView } from "@tarojs/components";
+import './ClockIndex.scss'
+import TabBar from "@/components/navigation/TabBar";
+import { IconClose, IconNext } from "@/components/basic/Icons";
+import { rpxToPx } from "@/utils/tools";
+import { useDispatch, useSelector } from "react-redux";
+import { useEffect, useState } from "react";
+import { homeInfo } from "@/services/health";
+import dayjs from "dayjs";
+import { setActions, setEvents, setTimeData } from "@/store/record";
+import { TimeFormatter } from "@/utils/time_format";
+import { getInfo } from "@/services/user";
+import Taro from "@tarojs/taro";
+import ChooseActions from "./components/choose_actions";
+import NewButton, { NewButtonType } from "@/_health/base/new_button";
+import { BASE_IMG_URL } from "@/services/http/api";
+import { useTranslation } from "react-i18next";
+import NoData from "@/_health/components/no_data";
+
+var timer
+let useNavigation, LinearGradient;
+if (process.env.TARO_ENV == 'rn') {
+    useNavigation = require("@react-navigation/native").useNavigation
+    LinearGradient = require('react-native-linear-gradient').default
+}
+export default function ClockIndex() {
+    const systemInfo: any = Taro.getWindowInfo ? Taro.getWindowInfo() : Taro.getSystemInfoSync();
+    // const navigationBarHeight = systemInfo.statusBarHeight + 44;
+    const screenHeight = systemInfo.screenHeight
+
+    const user = useSelector((state: any) => state.user);
+    const record = useSelector((state: any) => state.record);
+    const [loaded, setLoaded] = useState(false)
+    const [showRetry, setShowRetry] = useState(false)
+    const [home, setHome] = useState<any>(null)
+    const [count, setCount] = useState(0)
+    const [currentTimeData, setCurrentTimeData] = useState<any>(null)
+    const [showChoose, setShowChoose] = useState(false)
+    const [selScenario, setSelScenario] = useState('')
+    const [scenarios, setScenarios] = useState<any>([])
+    const dispatch = useDispatch();
+    const { t } = useTranslation()
+    global.dispatch = dispatch;
+
+    dayjs.locale(global.language == 'en' ? 'en' : 'zh-cn');
+
+    let navigation;
+    if (useNavigation) {
+        navigation = useNavigation()
+    }
+
+    useEffect(() => {
+        getHomeData()
+    }, [user.isLogin])
+
+    function getHomeData() {
+        homeInfo().then(res => {
+            setLoaded(true)
+            setHome(res)
+            setCount(1)
+            setScenarios((res as any).scenarios ?? [])
+            dispatch(setEvents((res as any).events))
+            dispatch(setActions((res as any).actions))
+            setShowRetry(false)
+        }).catch(e => {
+            setShowRetry(true)
+        })
+    }
+
+    useEffect(() => {
+        getInfo()
+        Taro.eventCenter.on('refreshClockIndex', getHomeData)
+        timer = setInterval(() => {
+            setCount(pre => pre + 1)
+        }, 1000)
+        return () => {
+            clearInterval(timer)
+            Taro.eventCenter.off('refreshClockIndex')
+        }
+    }, [])
+
+    useEffect(() => {
+        Taro.eventCenter.trigger('hideTab', showChoose)
+    }, [showChoose])
+    useEffect(() => {
+        getTime()
+    }, [count])
+
+    useEffect(() => {
+        if (currentTimeData) {
+            dispatch(setTimeData(currentTimeData))
+        }
+    }, [currentTimeData])
+
+    function tapItem(index) {
+        if (!user.isLogin) {
+            jumpPage('/_account/pages/ChooseAuth', 'ChooseAuth', navigation)
+            return
+        }
+        var scenarioObj = scenarios[index]
+        switch (scenarioObj.scenario) {
+            case 'MEAL':
+                setShowChoose(true)
+                setSelScenario('MEAL')
+                break;
+            case 'ACTIVITY':
+                setShowChoose(true)
+                setSelScenario('ACTIVITY')
+                break;
+            case 'FAST':
+                jumpPage(`/_record/pages/time_record?scenario=FAST`, 'TimeRecord', navigation, {
+                    scenario: 'FAST'
+                })
+                break
+            case 'SLEEP':
+                jumpPage(`/_record/pages/time_record?scenario=SLEEP`, 'TimeRecord', navigation, {
+                    scenario: 'SLEEP'
+                })
+                break
+            case 'METRIC':
+                jumpPage('/pages/metric/Metric')
+                break
+            case 'MOVE':
+                jumpPage('/_health/pages/move')
+                break;
+        }
+    }
+
+    function getTime() {
+        if (!home) return
+        var now = dayjs().format('HH:mm')
+        for (var i = 0; i < home.time_messages.length; i++) {
+            var obj = home.time_messages[i]
+            if (now >= obj.start_time && now < obj.end_time) {
+                setCurrentTimeData(obj)
+                return
+            }
+        }
+        setCurrentTimeData(home.time_messages[home.time_messages.length - 1])
+    }
+
+    function getBackground() {
+        var time = record.time
+        if (!time) return '#fff'
+        const { background_colors } = time
+        if (!background_colors) {
+            return '#fff'
+        }
+        else if (background_colors.length == 1) {
+            return background_colors[0]
+        }
+        return `linear-gradient(to bottom, ${background_colors[0]}, ${background_colors[1]})`
+    }
+
+    function itemTime(item) {
+        if (item.event && item.event.status == 'OG') {
+            return TimeFormatter.countdown(item.event.time.start_timestamp)
+        }
+        return ''
+    }
+
+
+
+    if (!loaded && showRetry) return <NoData refresh={
+        () => {
+            getHomeData()
+        }
+    } />
+
+    function bgView() {
+        if (process.env.TARO_ENV == 'weapp') {
+            return <View className="main_bg" style={{ background: getBackground() }} />
+        }
+        var time = record.time
+        if (!time) return <View />
+        const { background_colors } = time
+        if (!background_colors) {
+            return <View />
+        }
+        else if (background_colors.length == 1) {
+            return <View />
+        }
+        return <LinearGradient style={{
+            position: 'absolute',
+            left: 0,
+            top: 0,
+
+            width: rpxToPx(750),
+            height: screenHeight,
+            zIndex: 0,
+            pointerEvents: 'none'
+        }}
+            colors={[background_colors[0], background_colors[1]]}
+            start={{ x: 0, y: 0 }}
+            end={{ x: 0, y: 1 }} pointerEvents="none" />
+    }
+
+    if (!loaded) return <View />
+
+    return <View style={{ position: 'relative' }}>
+        {
+            bgView()
+        }
+        <ScrollView style={{ height: process.env.TARO_ENV == 'weapp' ? '100vh' : '100%' }} scrollY><View style={{ position: 'relative', overflow: 'hidden', minHeight: process.env.TARO_ENV == 'weapp' ? '101vh' : '100%' }}>
+
+            <View className="h60 bold" style={{ marginLeft: rpxToPx(52), marginTop: rpxToPx(212) }}>{record.time ? record.time.greeting : ''}</View>
+            <View style={{ display: 'flex', flexDirection: 'row' }}>
+                <View className="h44 bold" style={{ marginLeft: rpxToPx(52), marginTop: rpxToPx(66), marginBottom: rpxToPx(32), position: 'relative', paddingBottom: rpxToPx(20) }}>{t('health.check_in')}
+                    <View className="tab_border_line" />
+                </View>
+            </View>
+
+
+            <View className="operate_panel">
+                {
+                    scenarios.map((item, index) => {
+                        return <NewButton type={NewButtonType.custom}
+                            key={index}
+                            onClick={() => {
+                                tapItem(index)
+
+                            }}>
+                            <View className="operate_card" >
+                                <View className="h36 bold">{item.title}</View>
+                                <View className="h50 bold">{itemTime(item)}</View>
+                                <View style={{ flex: 1 }} />
+                                <View style={{ display: 'flex', flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center', }}>
+                                    <Image src={item.icon} style={{ width: rpxToPx(48), height: rpxToPx(48) }} />
+                                    <Image src={item.action_icon} style={{ width: rpxToPx(36), height: rpxToPx(36) }} />
+                                </View>
+                            </View>
+                        </NewButton>
+                    })
+                }
+            </View>
+            <View style={{ height: 100 }} />
+
+
+        </View>
+        </ScrollView>
+        {
+            showChoose && <ChooseActions
+                close={() => {
+                    setShowChoose(false)
+                }}
+                quick={() => {
+                    jumpPage(`/_record/pages/log_record?scenario=${selScenario}&quick=1`,'LogRecord',navigation,{
+                        scenario:selScenario,
+                        quick:1
+                    })
+                }}
+                chooseImg={(array) => {
+                    jumpPage(`/_record/pages/log_record?scenario=${selScenario}&imgs=${JSON.stringify(array)}`,'LogRecord',navigation,{
+                        scenario:selScenario,
+                        imgs:JSON.stringify(array)
+                    })
+                }}
+                chooseText={() => {
+                    jumpPage(`/_record/pages/log_record?scenario=${selScenario}&only_text=1`,'LogRecord',navigation,{
+                        scenario:selScenario,
+                        only_text:1
+                    })
+                }}
+            />
+        }
+        {
+            process.env.TARO_ENV == 'weapp' && <TabBar index={0} />
+        }
+    </View>
+}

+ 111 - 48
src/pages/clock/ClockIndex.tsx

@@ -1,5 +1,5 @@
 import { jumpPage } from "@/features/trackTimeDuration/hooks/Common";
-import { View, Image, ScrollView } from "@tarojs/components";
+import { View, Image, ScrollView, Swiper, SwiperItem, Block } from "@tarojs/components";
 import './ClockIndex.scss'
 import TabBar from "@/components/navigation/TabBar";
 import { IconClose, IconNext } from "@/components/basic/Icons";
@@ -17,17 +17,27 @@ import NewButton, { NewButtonType } from "@/_health/base/new_button";
 import { BASE_IMG_URL } from "@/services/http/api";
 import { useTranslation } from "react-i18next";
 import NoData from "@/_health/components/no_data";
+import IndexTab from "./components/index_tab";
+import TimeRecord from "@/_record/pages/time_record";
+import RecordTime from "./components/record_time";
+import RecordLog from "./components/record_log";
+import PickerCard from "@/_record/components/picker_card";
 
 var timer
 let useNavigation, LinearGradient;
+let pickerConfirm;
 if (process.env.TARO_ENV == 'rn') {
     useNavigation = require("@react-navigation/native").useNavigation
     LinearGradient = require('react-native-linear-gradient').default
 }
 export default function ClockIndex() {
     const systemInfo: any = Taro.getWindowInfo ? Taro.getWindowInfo() : Taro.getSystemInfoSync();
-    // const navigationBarHeight = systemInfo.statusBarHeight + 44;
+    const navigationBarHeight = systemInfo.statusBarHeight + 44;
     const screenHeight = systemInfo.screenHeight
+    const safeAreaInsets = systemInfo.safeArea; // 获取安全区域信息
+
+    // 计算底部安全区域的高度
+    const bottomSafeAreaHeight = systemInfo.windowHeight - safeAreaInsets.bottom;
 
     const user = useSelector((state: any) => state.user);
     const record = useSelector((state: any) => state.record);
@@ -43,6 +53,13 @@ export default function ClockIndex() {
     const { t } = useTranslation()
     global.dispatch = dispatch;
 
+    const [showDatePicker, setShowDatePicker] = useState(false)
+    const [showDurationPicker, setShowDurationPicker] = useState(false)
+    const [showEndDatePicker, setShowEndDatePicker] = useState(false)
+    const [pickerTitle, setPickerTitle] = useState('')
+    const [pickerValue, setPickerValue] = useState<any>(null)
+    const [pickerFunc, setPickerFunc] = useState<any>(null)
+
     dayjs.locale(global.language == 'en' ? 'en' : 'zh-cn');
 
     let navigation;
@@ -160,6 +177,30 @@ export default function ClockIndex() {
         return ''
     }
 
+    function showPicker(type, title, value, confirm) {
+        setPickerTitle(title)
+        setPickerValue(value)
+        // setPickerFunc(confirm)
+        pickerConfirm = confirm
+        switch (type) {
+            case 'duration':
+                setShowDurationPicker(true)
+                break
+            case 'start':
+                setShowDatePicker(true)
+                break
+            case 'end':
+                setShowEndDatePicker(true)
+                break
+        }
+    }
+
+    function hidePicker() {
+        setShowDatePicker(false)
+        setShowEndDatePicker(false)
+        setShowDurationPicker(false)
+    }
+
 
 
     if (!loaded && showRetry) return <NoData refresh={
@@ -196,74 +237,96 @@ export default function ClockIndex() {
             end={{ x: 0, y: 1 }} pointerEvents="none" />
     }
 
+    function swiperHeight() {
+        return screenHeight - navigationBarHeight - rpxToPx(94) - rpxToPx(112) - bottomSafeAreaHeight+4
+    }
+
     if (!loaded) return <View />
 
     return <View style={{ position: 'relative' }}>
         {
             bgView()
         }
-        <ScrollView style={{ height: process.env.TARO_ENV == 'weapp' ? '100vh' : '100%' }} scrollY><View style={{ position: 'relative', overflow: 'hidden', minHeight: process.env.TARO_ENV == 'weapp' ? '101vh' : '100%' }}>
-
-            <View className="h60 bold" style={{ marginLeft: rpxToPx(52), marginTop: rpxToPx(212) }}>{record.time ? record.time.greeting : ''}</View>
-            <View style={{ display: 'flex', flexDirection: 'row' }}>
-                <View className="h44 bold" style={{ marginLeft: rpxToPx(52), marginTop: rpxToPx(66), marginBottom: rpxToPx(32), position: 'relative', paddingBottom: rpxToPx(20) }}>{t('health.check_in')}
-                    <View className="tab_border_line" />
-                </View>
-            </View>
-
-
-            <View className="operate_panel">
-                {
-                    scenarios.map((item, index) => {
-                        return <NewButton type={NewButtonType.custom}
-                            key={index}
-                            onClick={() => {
-                                tapItem(index)
-
-                            }}>
-                            <View className="operate_card" >
-                                <View className="h36 bold">{item.title}</View>
-                                <View className="h50 bold">{itemTime(item)}</View>
-                                <View style={{ flex: 1 }} />
-                                <View style={{ display: 'flex', flexDirection: 'row', justifyContent: 'space-between', alignItems: 'center', }}>
-                                    <Image src={item.icon} style={{ width: rpxToPx(48), height: rpxToPx(48) }} />
-                                    <Image src={item.action_icon} style={{ width: rpxToPx(36), height: rpxToPx(36) }} />
-                                </View>
-                            </View>
-                        </NewButton>
-                    })
-                }
-            </View>
-            <View style={{ height: 100 }} />
-
-
-        </View>
-        </ScrollView>
+        <View style={{ height: navigationBarHeight }} />
+        <IndexTab />
+        <Swiper style={{ height: swiperHeight(), width: rpxToPx(750) }}>
+            <SwiperItem>
+                <RecordTime scenario="FAST" contentHeight={swiperHeight()} showPicker={showPicker} hidePicker={hidePicker} />
+            </SwiperItem>
+            <SwiperItem>
+                <RecordTime scenario="SLEEP" contentHeight={swiperHeight()} showPicker={showPicker} hidePicker={hidePicker} />
+            </SwiperItem>
+            <SwiperItem>
+                <RecordLog scenario="MEAL" contentHeight={swiperHeight()} />
+            </SwiperItem>
+            <SwiperItem>
+                <RecordLog scenario="ACTIVITY" contentHeight={swiperHeight()} />
+            </SwiperItem>
+        </Swiper>
         {
             showChoose && <ChooseActions
                 close={() => {
                     setShowChoose(false)
                 }}
                 quick={() => {
-                    jumpPage(`/_record/pages/log_record?scenario=${selScenario}&quick=1`,'LogRecord',navigation,{
-                        scenario:selScenario,
-                        quick:1
+                    jumpPage(`/_record/pages/log_record?scenario=${selScenario}&quick=1`, 'LogRecord', navigation, {
+                        scenario: selScenario,
+                        quick: 1
                     })
                 }}
                 chooseImg={(array) => {
-                    jumpPage(`/_record/pages/log_record?scenario=${selScenario}&imgs=${JSON.stringify(array)}`,'LogRecord',navigation,{
-                        scenario:selScenario,
-                        imgs:JSON.stringify(array)
+                    jumpPage(`/_record/pages/log_record?scenario=${selScenario}&imgs=${JSON.stringify(array)}`, 'LogRecord', navigation, {
+                        scenario: selScenario,
+                        imgs: JSON.stringify(array)
                     })
                 }}
                 chooseText={() => {
-                    jumpPage(`/_record/pages/log_record?scenario=${selScenario}&only_text=1`,'LogRecord',navigation,{
-                        scenario:selScenario,
-                        only_text:1
+                    jumpPage(`/_record/pages/log_record?scenario=${selScenario}&only_text=1`, 'LogRecord', navigation, {
+                        scenario: selScenario,
+                        only_text: 1
                     })
                 }}
             />
         }
+        <Block>
+            {
+                showDurationPicker && <PickerCard onClose={() => { setShowDurationPicker(false) }}
+                    value={pickerValue}
+                    type="duration"
+                    title={pickerTitle}
+                    onConfirm={(e) => {
+                        // pickerFunc(e)
+                        pickerConfirm(e)
+                    }}
+                />
+            }
+        </Block>
+        <Block>
+            {
+                showDatePicker && <PickerCard onClose={() => { setShowDatePicker(false) }}
+                    value={pickerValue}
+                    type="datetime"
+                    title={pickerTitle}
+                    onConfirm={(e) => {
+                        // pickerFunc(e)
+                        pickerConfirm(e)
+                    }}
+                />
+            }
+        </Block>
+        <Block>
+            {
+                showEndDatePicker && <PickerCard onClose={() => { setShowEndDatePicker(false) }}
+                    value={pickerValue}
+                    type="datetime"
+                    title={pickerTitle}
+                    onConfirm={(e) => {
+                        // pickerFunc(e)
+                        pickerConfirm(e)
+                    }}
+                />
+            }
+        </Block>
         {
             process.env.TARO_ENV == 'weapp' && <TabBar index={0} />
         }

+ 5 - 0
src/pages/clock/components/index_tab.scss

@@ -0,0 +1,5 @@
+.index_tabbar{
+    width: 750px;
+    height: 94px;
+    background-color: pink;
+}

+ 6 - 0
src/pages/clock/components/index_tab.tsx

@@ -0,0 +1,6 @@
+import { View } from "@tarojs/components";
+import './index_tab.scss'
+
+export default function IndexTab() {
+    return <View className="index_tabbar"></View>
+}

+ 314 - 0
src/pages/clock/components/record_log.scss

@@ -0,0 +1,314 @@
+// page {
+//     background: linear-gradient(to bottom, #F0CABF, #E1CDEE)
+// }
+
+
+
+
+
+.operate_bg {
+    display: flex;
+    flex-direction: column;
+    // flex-wrap: wrap;
+    width: 750px;
+    box-sizing: border-box;
+    padding-left: 40px;
+    margin-top: 26px;
+}
+
+.operate_item {
+    margin-right: 32px;
+    margin-bottom: 26px;
+    width: 670px;
+    height: 156px;
+    display: flex;
+    flex-direction: row;
+    align-items: center;
+    // justify-content: center;
+    border-radius: 42px;
+    padding-right: 50px;
+    box-sizing: border-box;
+}
+
+.first_letter{
+    margin-left:30px;
+    margin-right: 40px;
+    background-color: rgba($color: #fff, $alpha: 0.5);
+    width:108px;
+    height:108px;
+    border-radius: 54px;
+    display:flex;
+    align-items: center;
+    justify-content: center;
+}
+
+.input_form {
+    width: 670px;
+    height: 360px;
+    border-radius: 42px;
+    background-color: rgba($color: #fff, $alpha: 0.25);
+    display: flex;
+    flex-direction: column;
+    align-items: center;
+}
+
+.input_content {
+    width: 578px;
+    height: 128px;
+    border-radius: 21px;
+    margin-top: 60px;
+    box-sizing: border-box;
+    padding-left: 60px;
+    // text-align: center;
+    background-color: #fff;
+}
+
+.input_content_placeholder{
+    text-align: center;
+}
+
+.form_btns {
+    display: flex;
+    flex-direction: row;
+    margin-top: 60px;
+    width: 670px;
+    height: 128px;
+    // background-color: palegoldenrod;
+}
+
+.form_cancel {
+    flex: 1;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+}
+
+.form_confirm {
+    color: rgba($color: #000, $alpha: 0.4);
+}
+
+.sel_tag {
+    height: 64px;
+    border-radius: 32px;
+    background-color: rgba($color: #000, $alpha: 0.05);
+    padding-left: 24px;
+    display: flex;
+    flex-direction: row;
+    align-items: center;
+    padding-right: 16px;
+}
+
+.textarea2 {
+    height: 420px;
+    width: 570px;
+    box-sizing: border-box;
+    // padding: 30px 0px;
+    margin-top: 48px;
+    margin-bottom: 48px;
+}
+
+.cover1 {
+    width: 155px;
+    height: 155px;
+    background-color: rgba($color: #000, $alpha: 0.05);
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    border-radius: 0px;
+    overflow: hidden;
+    margin-right: 20px;
+    margin-bottom: 20px;
+    position: relative;
+    border-radius: 28px;
+}
+
+.cover2 {
+    width: 155px;
+    height: 155px;
+}
+
+.cover_del {
+    position: absolute;
+    right: 0;
+    top: 0;
+    width: 52px;
+    height: 52px;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+}
+
+.cover_del_btn {
+    width: 28px;
+    height: 28px;
+    border-radius: 15px;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+    background-color: #66666680;
+    flex-shrink: 0;
+}
+
+.form2 {
+    display: flex;
+    flex-direction: row;
+    flex-wrap: wrap;
+    width: 100%;
+}
+
+.time_view {
+    position: relative;
+    display: flex;
+    flex-direction: row;
+    align-items: center;
+    margin-left: 56px;
+    padding-right: 52px;
+    height: 108px;
+}
+
+.cardShowAni {
+    animation: showAni 0.3s linear forwards;
+}
+
+.cardHideAni {
+    animation: dismissAni 0.3s linear forwards;
+}
+
+
+
+@keyframes dismissAni {
+    0% {
+        opacity: 1;
+    }
+
+    100% {
+        opacity: 0;
+    }
+}
+
+@keyframes showAni {
+    0% {
+        opacity: 0;
+        transform: translateY(100px);
+    }
+
+    100% {
+        opacity: 1;
+        transform: translateY(0px);
+    }
+}
+
+/* #ifdef weapp */
+.share_bg {
+    position: fixed;
+    left: 0;
+    top: 0;
+    width: 100vw;
+    height: 100vh;
+    z-index: 100;
+    background: linear-gradient(180deg, #000000 0%, #1A1A1A 100%);
+    display: flex;
+    flex-direction: column;
+    align-items: center;
+    justify-content: center;
+}
+
+.navi_bar {
+    position: fixed;
+    left: 0;
+    right: 0;
+    top: 0;
+}
+/* #endif */
+
+/* #ifdef rn */
+.share_bg {
+    position: fixed;
+    left: 0;
+    top: 0;
+    width: 100vw;
+    height: 100vh;
+    z-index: 100;
+    display: flex;
+    flex-direction: column;
+    align-items: center;
+    justify-content: center;
+}
+
+.navi_bar {
+    position: absolute;
+    left: 0;
+    right: 0;
+    top: 0;
+}
+/* #endif */
+
+
+.share_card1 {
+    width: 698px;
+    height: 932px;
+    display: flex;
+    border-radius: 84px;
+    margin-bottom: 26px;
+    flex-direction: column;
+    overflow: hidden;
+    align-items: center;
+    position: relative;
+}
+
+.log_result_success {
+    margin-top: 123px;
+    width: 120px;
+    height: 120px;
+    background-color: #fff;
+    border-radius: 60px;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+}
+
+.share_card_cover {
+    width: 698px;
+    height: 932px;
+    position: absolute;
+    left: 0;
+    top: 0;
+}
+
+.share_card_layer {
+    width: 698px;
+    height: 932px;
+    position: absolute;
+    left: 0;
+    top: 0;
+    background-color: #000;
+    opacity: 0.5;
+}
+
+.share_canvas {
+    position: absolute;
+    top: -1000px;
+}
+
+.content_card {
+    margin-left: 26px;
+    background-color: rgba($color: #fff, $alpha: 0.5);
+    width: 698px;
+    min-height: 875px;
+    border-radius: 84px;
+    padding: 64px;
+    padding-bottom: 44px;
+    box-sizing: border-box;
+    display: flex;
+    flex-direction: column;
+}
+
+.share_btn1{
+    display: flex;
+    flex-direction: row;
+    align-items: center;
+    justify-content: center;
+    width: 698px;
+    height: 108px;
+    border-radius: 28px;
+}

+ 777 - 0
src/pages/clock/components/record_log.tsx

@@ -0,0 +1,777 @@
+import { View, Image, PageMeta, NavigationBar, Input, Textarea, ScrollView, Snapshot, PickerView, PickerViewColumn, Text } from "@tarojs/components";
+import './record_log.scss'
+import { useEffect, useState } from "react";
+import Taro, { useRouter, useShareAppMessage } from "@tarojs/taro";
+import { rpxToPx } from "@/utils/tools";
+import { IconAdd, IconArrow, IconCheck, IconClose } from "@/components/basic/Icons";
+import { MainColorType } from "@/context/themes/color";
+import { useTranslation } from "react-i18next";
+import showAlert from "@/components/basic/Alert";
+import showActionSheet from "@/components/basic/ActionSheet";
+import { BASE_IMG_URL, baseUrl } from "@/services/http/api";
+import { checkAuthorized } from "@/utils/check_authorized";
+import NewButton, { NewButtonType } from "@/_health/base/new_button";
+import dayjs from "dayjs";
+import { TimeFormatter } from "@/utils/time_format";
+import { useSelector } from "react-redux";
+import { addEvents, addUserTag, createMoment, eventDetail, updateMoment, userTags } from "@/services/health";
+import PostMomentTime from "@/_health/components/post_moment_time";
+import ShareBtn from "@/components/basic/ShareBtn";
+import { jumpPage } from "@/features/trackTimeDuration/hooks/Common";
+import ChooseActions from "@/pages/clock/components/choose_actions";
+import FollowInfo from "@/_moment/components/follow_info";
+import NewDateTimePicker from "@/_health/base/new_date_time_picker";
+import { IconCamera, IconClock } from "@/_record/components/record_icon";
+import PickerCard from "@/_record/components/picker_card";
+
+let useRoute;
+let useNavigation;
+let LinearGradient;
+if (process.env.TARO_ENV == 'rn') {
+    useRoute = require("@react-navigation/native").useRoute
+    useNavigation = require("@react-navigation/native").useNavigation
+    LinearGradient = require('react-native-linear-gradient').default
+}
+let useActionSheet;
+if (process.env.TARO_ENV == 'rn') {
+    useActionSheet = require('@expo/react-native-action-sheet').useActionSheet
+}
+
+export default function RecordLog(props: {
+    scenario, contentHeight: number, imgs?: any, only_text?: any, quick?: any, join_id?: any,
+    id?: any,
+    edit?: any,
+    check_in?: any
+
+}) {
+    const systemInfo: any = Taro.getWindowInfo ? Taro.getWindowInfo() : Taro.getSystemInfoSync();
+    const navigationBarHeight = systemInfo.statusBarHeight + 44;
+    const screenHeight = systemInfo.screenHeight
+
+    const scale = '?x-oss-process=image/format,jpg/resize,w_400'
+    const long = useSelector((state: any) => state.long);
+    const health = useSelector((state: any) => state.health);
+    const record = useSelector((state: any) => state.record);
+    const user = useSelector((state: any) => state.user);
+
+    const [title, setTitle] = useState('')
+    const [chooseTitle, setChooseTitle] = useState('')
+    const [desc, setDesc] = useState('')
+    const [step, setStep] = useState(0)
+    const { t } = useTranslation()
+
+    const [time, setTime] = useState(dayjs().format('HH:mm'))
+    const [selDate, setSelDate] = useState(dayjs().format('YYYY-MM-DD'))
+
+    const [enterTimestmap] = useState(new Date().getTime())
+    const [posting, setPosting] = useState(false)
+    const [showTimePicker, setShowTimePicker] = useState(false)
+    const [result, setResult] = useState<any>(null)
+    const [showResult, setShowResult] = useState(false)
+
+    const [tags, setTags] = useState<any>([])
+    const [selPostCount, setPostCount] = useState(1)
+    const [showChoose, setShowChoose] = useState(false)
+    const [selTag, setSelTag] = useState<any>(null)
+    const [loading, setLoading] = useState(false)
+
+
+    let showActionSheetWithOptions;
+    if (process.env.TARO_ENV == 'rn') {
+        showActionSheetWithOptions = useActionSheet()
+    }
+
+    let router
+    let navigation;
+    if (useNavigation) {
+        navigation = useNavigation()
+    }
+
+    if (process.env.TARO_ENV == 'rn') {
+        router = useRoute()
+    }
+    else {
+        router = useRouter()
+    }
+
+    const { scenario, imgs, only_text, quick, join_id } = props
+
+    const [pics, setPics] = useState<any>(imgs ? JSON.parse(imgs) : [])
+    const [focus, setFocus] = useState(only_text ? true : false)
+    const [inputFocus, setInputFocus] = useState(false)
+
+
+    if (process.env.TARO_ENV == 'weapp') {
+
+
+        useShareAppMessage((e) => {
+
+            var path;
+            if (result && result.event_id) {
+                var sharePath = `/_health/pages/timeline_detail?type=recent&fast_type=IF&event_id=${result.event_id}&uid=${user.id}&isfastsleep=${0}&disable_edit=1&enter_type=share`
+                if (user.isLogin) {
+                    sharePath += `&share_uid=${user.id}`
+                }
+                path = sharePath
+            }
+            else {
+                path = 'pages/clock/Clock'
+            }
+            return {
+                title: '分享标题',
+                path: path,
+                // imageUrl: shareUrl
+            }
+        })
+    }
+
+
+    useEffect(() => {
+        global.set_time = new Date().getTime()
+        getTags()
+        if (props.check_in == 1) {
+            setStep(0)
+        }
+        // if (router.params.quick) {
+        //     quickSave()
+        // }
+
+        if (props.edit) {
+            eventDetail(props.id).then(res => {
+                setTitle((res as any).title)
+                setChooseTitle((res as any).title)
+                if ((res as any).moment) {
+                    setDesc((res as any).moment.description)
+                    if ((res as any).moment.media && (res as any).moment.media.length > 0) {
+                        setPics((res as any).moment.media)
+                    }
+                }
+                if ((res as any).time) {
+                    setSelDate(dayjs((res as any).time.start_timestamp).format('YYYY-MM-DD'))
+                    setTime(dayjs((res as any).time.start_timestamp).format('HH:mm'))
+                }
+            })
+        }
+    }, [])
+
+    useEffect(() => {
+        if (step == 1) {
+            setTimeout(() => {
+                setInputFocus(true)
+            }, 300)
+        }
+    }, [step])
+
+    function getTags() {
+        userTags({ scenario: scenario }).then(res => {
+            if (chooseTitle == '' && !props.edit) {
+                var current = dayjs().format('HH:mm');
+                var isFind = false;
+                (res as any).tags.map(item => {
+                    if (item.time_from && item.time_to) {
+                        if (isInTimeRange(current, item.time_from, item.time_to)) {
+                            isFind = true;
+                            setTitle(item.title)
+                            setChooseTitle(item.title)
+                            setPostCount(item.log_count + 1)
+                        }
+                    }
+                })
+                if (!isFind) {
+                    setTitle((res as any).tags[0].title)
+                    setChooseTitle((res as any).tags[0].title)
+                    setPostCount((res as any).tags[0].log_count + 1)
+                }
+            }
+
+            setTags((res as any).tags)
+        })
+    }
+
+    function isInTimeRange(currentTime, startTime, endTime) {
+        // 将时间字符串转换为 Date 对象
+        const current = new Date(`1970-01-01T${currentTime}:00`);
+        const start = new Date(`1970-01-01T${startTime}:00`);
+        const end = new Date(`1970-01-01T${endTime}:00`);
+
+        // 如果结束时间小于开始时间,说明时间段跨越了午夜
+        if (end < start) {
+            end.setDate(end.getDate() + 1); // 将结束时间加一天
+            if (current < start) {
+                current.setDate(current.getDate() + 1); // 如果当前时间在午夜之前,加一天
+            }
+        }
+
+        // 判断当前时间是否在时间段内
+        return current >= start && current < end;
+    }
+
+    function addTag() {
+        addUserTag({ scenario: scenario, title: title }).then(res => {
+            getTags()
+        })
+    }
+
+    function tapPic() {
+        //, t('health.delete')
+        var list = process.env.TARO_ENV == 'weapp' ? [t('health.add_photos'), t('health.camera2'), t('health.import_chat')] :
+            [t('health.add_photos'), t('health.camera2')]
+        showActionSheet({
+            title: '',
+            showActionSheetWithOptions: showActionSheetWithOptions,
+            itemList: list,
+            success: function (res) {
+                switch (res) {
+                    case 0:
+                        addImage(false)
+                        break;
+                    case 1:
+                        addImage(true)
+                        break;
+                    case 2:
+                        Taro.chooseMessageFile({
+                            count: 9 - pics.length,
+                            type: 'image',
+                            success: async function (res) {
+                                const results = await Promise.all(res.tempFiles.map(getImageInfo));
+                                chooseSuccess(results, true)
+                            },
+                            fail(res) {
+                            },
+                        })
+                        break;
+                    case 3:
+                        // setImgUrl('')
+                        break;
+                }
+            }
+        })
+    }
+
+    function addImage(isCamera) {
+        var source: any = isCamera ? ['camera'] : ['album']
+        Taro.chooseImage({
+            count: 9 - pics.length,
+            sizeType: ['compressed'],
+            // mediaType: ['image'],
+            sourceType: source,
+            success: async function (res) {
+                const results = await Promise.all(res.tempFiles.map(getImageInfo));
+
+
+                chooseSuccess(results, true)
+                // checkAuthorized()
+            },
+            fail: function (res) {
+            }
+        })
+    }
+
+    async function chooseSuccess(res, isAlbum) {
+        // const filePaths = res.map(file => file.path
+        //     // process.env.TARO_ENV === 'rn' || isFilePath ? file.path : file.tempFilePath
+        // )
+        Taro.showLoading({
+            title: t('health.uploading')
+        })
+
+        try {
+            const uploadedUrls = await Promise.all(res.map(path => uploadFile2(path, isAlbum ? 'album' : 'camera')))
+            setPics([...pics, ...uploadedUrls])
+            Taro.hideLoading()
+        } catch (error) {
+            console.error('Error uploading files:', error)
+            Taro.hideLoading()
+        }
+    }
+
+    function uploadFile2(obj: any, source: string): Promise<string> {
+        return new Promise((resolve, reject) => {
+            var path = obj.path
+            const dot = path.lastIndexOf('.')
+            const fileExt = dot > 0 ? path.substring(dot) : ''
+            Taro.request({
+                method: 'GET',
+                timeout: 30000,
+                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) {
+                        reject(new Error(t('health.networkError')))
+                        return
+                    }
+                    Taro.uploadFile({
+                        timeout: 30000,
+                        url: rsp.data.upload_url,
+                        filePath: path,
+                        name: 'file',
+                        formData: rsp.data.fields,
+                        success: () => {
+                            var temp = JSON.parse(JSON.stringify(obj))
+                            temp.url = rsp.data.view_url
+                            // resolve(rsp.data.view_url)
+                            resolve(temp)
+                        },
+                        fail: (error) => {
+
+                            reject(error)
+                        }
+                    })
+                },
+                fail: reject
+            })
+        })
+    }
+
+
+    const getImageInfo = (src) => {
+        const { tempFilePath, path } = src
+        return new Promise((resolve) => {
+            Taro.getImageInfo({
+                src: tempFilePath ? tempFilePath : path,
+                success: (result) => resolve({
+                    height: result.height,
+                    width: result.width,
+                    orientation: result.orientation,
+                    path: result.path,
+                    type: result.type
+                }),
+                fail: (error) => resolve({
+                    height: 1024,
+                    width: 1024,
+                    orientation: 'up',
+                    path: tempFilePath,
+                    type: 'unknown'
+                }),
+            });
+        });
+    };
+
+    function quickSave() {
+
+    }
+
+    function save() {
+        if (props.edit) {
+            edit()
+            return
+        }
+        var date = TimeFormatter.stringToDate(selDate, time)
+        date.setMilliseconds(new Date(enterTimestmap).getMilliseconds())
+
+
+        var params: any = {
+            scenario: scenario, //ACTIVITY
+            type: 'POINT',
+            sub_type: scenario,
+            title: chooseTitle,
+
+            time: {
+                start_timestamp: date.getTime()
+            }
+        }
+
+        if (join_id) {
+            params.join_key = join_id
+        }
+
+        var moment: any = {
+            description: desc,
+        }
+        if (pics.length > 0) {
+            var picList: any = []
+            pics.map((item) => {
+                picList.push({
+                    url: item.url,
+                    type: item.url.indexOf('mp4') != -1 ? 'video' : 'image',
+                    source: 'album',
+                    width: item.width,
+                    height: item.height,
+                    format: item.format
+                })
+            })
+            moment.media = picList
+        }
+        params.moment = moment
+
+
+
+        // if (event_id && event_id != 'undefined') {
+        //     params.event_id = event_id
+        // }
+
+        // if (is_temp) {
+        //     params.event = window == 'EAT' ? 'EAT_CUSTOM' : 'ACTIVE_CUSTOM'
+        // }
+        // params.op_page = window == 'EAT' ? 'HOME_EAT' : 'HOME_ACTIVE'
+
+
+        params.extra = {
+            set_time: global.set_time ? global.set_time : new Date().getTime(),
+            confirm_time: new Date().getTime()
+        }
+        if (posting) return
+        setPosting(true)
+        // Taro.showLoading({
+        //     title: t('health.uploading')
+        // })
+        addEvents(params).then(res => {
+            setShowResult(true)
+            setResult(res)
+            setPosting(false)
+            // Taro.hideLoading()
+            Taro.reLaunch({
+                url: '/pages/moment/moment'
+            })
+        }).catch(e => {
+            setPosting(false)
+            // Taro.hideLoading()
+        })
+        // createMoment(params).then(res => {
+        //     // setTimeout(() => {
+        //     setPosting(false)
+        //     Taro.eventCenter.trigger('refreshMoments', '')
+        //     // }, 1000)
+
+        //     if (process.env.TARO_ENV == 'weapp') {
+        //         // Taro.navigateBack();
+        //         Taro.redirectTo({
+        //             url: '/_health/pages/post_result?data=' + JSON.stringify(res)
+        //         })
+        //         // Taro.navigateTo({
+        //         //     url:'/_health/pages/post_result?data=' + JSON.stringify(res)
+        //         // })
+        //     }
+
+        //     // global.refreshWindow()
+        //     // global.refreshHistory()
+
+        //     // if (global.postMomentSuccess) {
+        //     //     global.postMomentSuccess()
+        //     // }
+        // }).catch(e => {
+        //     setPosting(false)
+        // })
+    }
+
+    function edit() {
+        var date = TimeFormatter.stringToDate(selDate, time)
+        date.setMilliseconds(new Date(enterTimestmap).getMilliseconds())
+
+
+        var params: any = {
+            id: router.params.id,
+            scenario: scenario, //ACTIVITY
+            type: 'POINT',
+            sub_type: scenario,
+            title: chooseTitle,
+
+            time: {
+                start_timestamp: date.getTime()
+            }
+        }
+
+        if (join_id) {
+            params.join_key = join_id
+        }
+
+        var moment: any = {
+            description: desc,
+        }
+        if (pics.length > 0) {
+            var picList: any = []
+            pics.map((item) => {
+                picList.push({
+                    url: item.url,
+                    type: item.url.indexOf('mp4') != -1 ? 'video' : 'image',
+                    source: 'album',
+                    width: item.width,
+                    height: item.height,
+                    format: item.format
+                })
+            })
+            moment.media = picList
+        }
+        else {
+            moment.media = []
+        }
+        params.moment = moment
+        params.extra = {
+            set_time: global.set_time ? global.set_time : new Date().getTime(),
+            confirm_time: new Date().getTime()
+        }
+        if (posting) return
+        setPosting(true)
+        // Taro.showLoading({
+        //     title: t('health.uploading')
+        // })
+        addEvents(params).then(res => {
+            setShowResult(true)
+            setResult(res)
+            setPosting(false)
+            Taro.eventCenter.trigger('refresh_timeline', (res as any).feed_item)
+            if (global.refreshHistory) {
+                global.refreshHistory()
+            }
+            process.env.TARO_ENV == 'weapp' ? Taro.navigateBack() : navigation.goBack()
+            // Taro.hideLoading()
+            // Taro.reLaunch({
+            //     url: '/pages/moment/moment'
+            // })
+        }).catch(e => {
+            setPosting(false)
+            // Taro.hideLoading()
+        })
+    }
+
+    function getDate() {
+        var sel = dayjs(selDate)
+        var now = dayjs().format('YYYY-MM-DD')
+        const yesterday = dayjs().subtract(1, 'day');
+        if (sel.format('YYYY-MM-DD') == now) {
+            return ''
+        }
+        if (yesterday.format('YYYY-MM-DD') == sel.format('YYYY-MM-DD')) {
+            return global.language == 'en' ? 'Yesterday ' : '昨天 '
+        }
+        else {
+            return global.language == 'en' ? sel.format('MMM D ') : sel.format('MMMD日 ')
+        }
+    }
+
+    function getBackground() {
+        var time = record.time
+        if (!time) return '#fff'
+        const { background_colors } = time
+        if (!background_colors) {
+            return '#fff'
+        }
+        else if (background_colors.length == 1) {
+            return background_colors[0]
+        }
+        return `linear-gradient(to bottom, ${background_colors[0]}, ${background_colors[1]})`
+    }
+
+    if (quick) {
+        // return <QuickLog tag={chooseTitle} scenario={scenario} updateTag={() => {
+
+        // }} />
+    }
+
+    return <ScrollView style={{ position: 'relative', zIndex: 1000, height: props.contentHeight }} scrollY>
+        <View style={{ position: 'relative', overflow: showChoose ? 'hidden' : 'visible' }}>
+            {
+                step == 0 && <View className="operate_bg">
+                    {
+                        tags.map((item, index) => {
+                            return <NewButton key={index} type={NewButtonType.custom}
+                                onClick={() => {
+                                    setChooseTitle(item.title)
+                                    setPostCount(item.log_count + 1)
+                                    setStep(2)
+                                    setSelTag(item)
+                                    // setShowChoose(true)
+                                }}>
+                                <View key={index} className="operate_item h34"
+                                    style={{ backgroundColor: MainColorType.white_25 }}
+                                >
+                                    <View className="first_letter h36">{item.title.substring(0, 1).toUpperCase()}</View>
+                                    {item.title}
+                                    <View style={{ flex: 1 }} />
+                                    <IconAdd color={MainColorType.g02} width={rpxToPx(36)} />
+                                </View>
+                            </NewButton>
+                        })
+                    }
+
+                    <View className="operate_item h34"
+                        style={{ backgroundColor: MainColorType.white_25, marginBottom: rpxToPx(100) }}
+                        onClick={() => {
+                            setTitle('')
+                            setChooseTitle('')
+                            setPostCount(1)
+                            setStep(1)
+                        }}><View className="first_letter h36">
+                            <Image src={BASE_IMG_URL + 'edit.svg'} style={{ width: rpxToPx(36), height: rpxToPx(36) }} />
+                        </View>{t('health.custom')}</View>
+                </View>
+            }
+            {
+                step == 1 && <View className="cardShowAni" style={{ display: 'flex', flexDirection: 'column', alignItems: 'center', marginTop: rpxToPx(300) }}>
+                    <View className="input_form">
+                        <Input className="input_content h36" placeholder={t('health.custom_name')} value={title}
+                            placeholderClass="input_content_placeholder"
+                            focus={inputFocus}
+                            autoFocus={inputFocus}
+                            maxlength={30}
+                            onBlur={() => {
+                                setInputFocus(false)
+                            }}
+                            onInput={(e: any) => {
+                                setTitle(e.target.value)
+                                setChooseTitle(e.target.value)
+                            }} />
+                        <View className="form_btns">
+                            <View className="form_cancel">
+                                <NewButton btnStyle={{ flex: 1 }} type={NewButtonType.img}
+                                    onClick={() => {
+                                        setStep(0)
+                                    }}
+                                >
+                                    <View className="h30 bold">{t('health.cancel')}</View>
+                                </NewButton>
+                            </View>
+                            <View className="form_cancel">
+                                <NewButton btnStyle={{ flex: 1 }} type={NewButtonType.img}
+                                    onClick={() => {
+                                        if (title.length == 0) return
+                                        setChooseTitle(title)
+                                        setPostCount(1)
+                                        setStep(2)
+                                        addTag()
+                                    }}
+                                >
+                                    <View className={title.length == 0 ? 'form_cancel form_confirm h30 bold' : 'form_cancel h30 bold'}
+
+                                    >{t('health.confirm')}</View>
+                                </NewButton>
+                            </View>
+                        </View>
+                    </View>
+                </View>
+            }
+            {
+                step == 2 && <View className="cardShowAni" style={{ paddingTop: rpxToPx(26) }}>
+
+                    <View className="content_card">
+                        <View style={{ display: 'flex', flexDirection: 'row' }} >
+                            <NewButton type={NewButtonType.custom}
+                                onClick={() => {
+                                    setStep(0)
+                                    setFocus(false)
+                                }}>
+
+                                <View className="sel_tag">
+                                    <View className="h34 bold">{chooseTitle}</View>
+                                    <View style={{ width: rpxToPx(6) }} />
+                                    <IconArrow width={rpxToPx(34)} color='#000' />
+
+                                </View>
+                            </NewButton>
+                        </View>
+
+                        <Textarea placeholder={t('health.add_text')} className="textarea2 h44"
+                            placeholder-style="color:rgba(0,0,0,0.2)"
+                            value={desc}
+                            focus={focus}
+                            onBlur={() => {
+                                setFocus(false)
+                            }}
+                            onInput={e => {
+                                setDesc(e.detail.value)
+                            }} />
+                        <View className="form2">
+
+
+                            {
+                                pics.map((item, index) => {
+                                    return <View className="cover1" key={index}>
+                                        <Image src={item.url + scale} mode="aspectFill" className="cover2" key={index} onClick={() => {
+                                            Taro.previewImage({
+                                                current: pics[index].url,
+                                                urls: pics.map(file => file.url)
+                                            })
+                                        }} />
+                                        <View className="cover_del" onClick={() => {
+                                            showAlert({
+                                                title: t('health.del_title'),
+                                                content: '',
+                                                cancelText: t('health.del_cancel'),
+                                                confirmText: t('health.del_confirm'),
+                                                showCancel: true,
+                                                confirm: () => {
+                                                    var array = JSON.parse(JSON.stringify(pics))
+                                                    array.splice(index, 1)
+                                                    setPics(array)
+                                                }
+                                            })
+                                        }}>
+                                            <View className="cover_del_btn">
+                                                <IconClose width={10} height={10} color="#fff" />
+                                            </View>
+                                        </View>
+                                    </View>
+                                })
+                            }
+                            {
+                                pics.length < 9 && <NewButton
+                                    type={NewButtonType.custom}
+                                    onClick={tapPic}>
+                                    <View className="cover1" style={{}}><IconCamera color="#000" width={rpxToPx(48)} /></View>
+                                </NewButton>
+                            }
+                        </View>
+                    </View>
+
+
+
+
+                    <View className="time_view" onClick={() => {
+                        setShowTimePicker(true)
+                    }}>
+
+                        <IconClock width={rpxToPx(36)} color={MainColorType.black_25} />
+
+
+                        <View className="h30" style={{ opacity: 0.25, marginLeft: rpxToPx(12) }}>{t('health.time')}</View>
+                        <View style={{ flex: 1 }} />
+                        <View className="h30" style={{ opacity: 0.25 }}>{getDate() + time}</View>
+                        <IconArrow width={rpxToPx(34)} color={MainColorType.black_25} />
+                        <View className="border_footer_line" style={{ left: rpxToPx(48) }} />
+                    </View>
+
+                    {
+                        join_id && <FollowInfo user={long.follow} />
+                    }
+
+
+
+
+                    <View style={{ width: 300 }}>
+                    </View>
+                </View>
+
+            }
+            {
+                step == 2 && <View className="main_footer" style={{ backgroundColor: 'transparent' }}>
+                    <NewButton
+                        type={NewButtonType.fill}
+                        color={MainColorType.orange}
+                        width={rpxToPx(646)}
+                        height={rpxToPx(108)}
+                        title={t('health.share_to_moment')}
+                        onClick={save}
+                    />
+                </View>
+            }
+            {
+                showTimePicker && <PickerCard onClose={() => { setShowTimePicker(false) }}
+                    value={new Date(selDate + 'T' + time + ':00').getTime()}
+                    title={global.language == 'en' ? 'Choose Time' : "选择时间"}
+                    type="datetime"
+                    onConfirm={(e) => {
+                        setSelDate(dayjs(e).format('YYYY-MM-DD'))
+                        setTime(dayjs(e).format('HH:mm'))
+                        setShowTimePicker(false)
+                    }}
+                />
+            }
+        </View >
+    </ScrollView>
+}

+ 275 - 0
src/pages/clock/components/record_time.scss

@@ -0,0 +1,275 @@
+
+
+.time_card_bg {
+    display: flex;
+    flex-direction: row;
+    padding-left: 40px;
+    padding-right: 40px;
+    justify-content: space-between;
+    box-sizing: border-box;
+    width: 750px;
+    margin-top: 170px;
+}
+
+.time_card {
+    width: 320px;
+    border-radius: 24px;
+    background-color: rgba($color: #ffffff, $alpha: 0.25);
+    padding: 30px 0;
+    box-sizing: border-box;
+    display: flex;
+    flex-direction: column;
+    align-items: center;
+}
+
+.progress_card {
+    display: flex;
+    flex-direction: column;
+    align-items: center;
+    overflow: hidden;
+    width: 698px;
+    padding-bottom: 60px;
+    border-radius: 84px;
+    background-color: rgba($color: #ffffff, $alpha: 0.25);
+    position: relative;
+    z-index: 1;
+}
+
+.eat_card {
+    width: 698px;
+    height: 156px;
+    background-color: rgba($color: #ffffff, $alpha: 0.25);
+    position: relative;
+    border-radius: 42px;
+    display: flex;
+    flex-direction: column;
+    align-items: center;
+    justify-content: center;
+    margin-top: 26px;
+}
+
+.eat_card_arrow {
+    position: absolute;
+    right: 30px;
+    top: 0;
+    bottom: 0;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+}
+
+.operate_content {
+    display: flex;
+    flex-direction: row;
+    margin-bottom: 40px;
+}
+
+.share_operate_content {
+    display: flex;
+    flex-direction: row;
+    margin-bottom: 40px;
+    height: 172px;
+    margin-top: -40px;
+}
+
+.operate_item_index {
+    display: flex;
+    flex-direction: column;
+    align-items: center;
+    justify-content: center;
+    width: 320px;
+    border-radius: 28px;
+    height: 172px;
+    position: relative;
+}
+
+.card_highlight {
+    position: absolute;
+    left: 0;
+    right: 0;
+    top: 0;
+    bottom: 0;
+    border-radius: 28px;
+}
+
+.start_card_show {
+    background-color: rgba($color: #ffffff, $alpha: 0.5);
+    animation: highlightShowAni 3s linear forwards;
+    opacity: 0;
+}
+
+.end_card_show {
+    background-color: rgba($color: #ffffff, $alpha: 0.25);
+    animation: highlightShowAni 3s linear forwards;
+    opacity: 0;
+}
+
+.start_card_hide {
+    background-color: rgba($color: #ffffff, $alpha: 0.5);
+    animation: highlightHideAni 0.3s 0.1s linear forwards;
+    opacity: 1;
+}
+
+.end_card_hide {
+    background-color: rgba($color: #ffffff, $alpha: 0.25);
+    animation: highlightHideAni 0.3s 0.1s linear forwards;
+    opacity: 1;
+}
+
+@keyframes highlightShowAni {
+    0% {
+        opacity: 0;
+    }
+
+    10% {
+        opacity: 1;
+    }
+
+    90% {
+        opacity: 1;
+    }
+
+    100% {
+        opacity: 0;
+    }
+}
+
+@keyframes highlightHideAni {
+    0% {
+        opacity: 1;
+    }
+
+    100% {
+        opacity: 0;
+    }
+}
+
+.share_icon {
+    position: absolute;
+    right: 48px;
+    top: 48px;
+}
+
+/* #ifdef weapp */
+.share_bg {
+    position: fixed;
+    left: 0;
+    top: 0;
+    width: 100vw;
+    height: 100vh;
+    z-index: 100;
+    background: linear-gradient(180deg, #000000 0%, #1A1A1A 100%);
+    display: flex;
+    flex-direction: column;
+    align-items: center;
+    justify-content: center;
+}
+.navi_bar {
+    position: fixed;
+    left: 0;
+    right: 0;
+    top: 0;
+}
+/* #endif */
+
+/* #ifdef rn */
+.share_bg {
+    position: absolute;
+    left: 0;
+    top: 0;
+    width: 100%;
+    height: 100%;
+    z-index: 100;
+    display: flex;
+    flex-direction: column;
+    align-items: center;
+    justify-content: center;
+}
+.navi_bar {
+    position: absolute;
+    left: 0;
+    right: 0;
+    top: 0;
+}
+/* #endif */
+
+.share_card {
+    width: 698px;
+    height: 932px;
+    display: flex;
+    border-radius: 84px;
+    margin-bottom: 26px;
+    flex-direction: column;
+    overflow: hidden;
+    align-items: center;
+}
+
+.share_canvas {
+    position: absolute;
+    top: -1000px;
+}
+
+.share_card2 {
+    width: 738px;
+    height: 932px;
+    display: flex;
+    border-radius: 84px;
+    margin-bottom: 26px;
+    flex-direction: column;
+    overflow: hidden;
+    align-items: center;
+    animation: heightAni 0.3s 0.1s linear forwards;
+}
+
+@keyframes heightAni {
+    0% {
+        height: 932px;
+    }
+
+    100% {
+        height: 892px;
+    }
+}
+
+.shareBtnAni {
+    opacity: 0;
+    animation: showAni 0.1s 0.1s linear forwards;
+}
+
+.eatBtnAni {
+    opacity: 0;
+    animation: showAni 0.1s 0.2s linear forwards;
+}
+
+.momentBtnAni {
+    opacity: 0;
+    width: 646px;
+    height: 96px;
+    border-radius: 48px;
+    background-color: #fff;
+    color: #000;
+    animation: showAni 0.1s 0.3s linear forwards;
+    display: flex;
+    align-items: center;
+    justify-content: center;
+}
+
+@keyframes showAni {
+    0% {
+        opacity: 0;
+    }
+
+    100% {
+        opacity: 1;
+    }
+}
+
+.share_btn1 {
+    display: flex;
+    flex-direction: row;
+    align-items: center;
+    justify-content: center;
+    width: 698px;
+    height: 108px;
+    border-radius: 28px;
+}

+ 863 - 0
src/pages/clock/components/record_time.tsx

@@ -0,0 +1,863 @@
+import { View, Image, PickerView, PickerViewColumn, Text } from "@tarojs/components";
+import './record_time.scss'
+import Taro, { useRouter, useShareAppMessage } from "@tarojs/taro";
+import { rpxToPx } from "@/utils/tools";
+import { MainColorType } from "@/context/themes/color";
+import NewButton, { NewButtonType } from "@/_health/base/new_button";
+import { useEffect, useRef, useState } from "react";
+import NewDateTimePicker from "@/_health/base/new_date_time_picker";
+import dayjs from "dayjs";
+import NewDurationPicker, { DurationPickerType } from "@/_health/base/new_durationpicker";
+import { IconArrow, IconClose, IconMore2 } from "@/components/basic/Icons";
+import { useSelector } from "react-redux";
+import { TimeFormatter } from "@/utils/time_format";
+import { addEvents, delWindowId } from "@/services/health";
+import Modal from "@/components/layout/Modal.weapp";
+import { getDurationArc, getStartArc } from "@/features/health/hooks/health_hooks";
+import ShareBtn from "@/components/basic/ShareBtn";
+import showActionSheet from "@/components/basic/ActionSheet";
+import { useTranslation } from "react-i18next";
+import FollowInfo from "@/_moment/components/follow_info";
+
+import MomentDetailShare from "@/pages/moment/moment_detail_share";
+import RingProgress from "@/_record/components/ring_progress.weapp";
+import PickerCard from "@/_record/components/picker_card";
+
+
+let timer
+let useRoute;
+let useNavigation, LinearGradient;
+if (process.env.TARO_ENV == 'rn') {
+    useRoute = require("@react-navigation/native").useRoute
+    useNavigation = require("@react-navigation/native").useNavigation
+    LinearGradient = require('react-native-linear-gradient').default
+}
+
+export default function RecordTime(props: { scenario: string, join_id?: string, contentHeight: number, showPicker: any, hidePicker: any }) {
+    const systemInfo: any = Taro.getWindowInfo ? Taro.getWindowInfo() : Taro.getSystemInfoSync();
+    const navigationBarHeight = systemInfo.statusBarHeight + 44;
+    const screenHeight = systemInfo.screenHeight
+
+    const record = useSelector((state: any) => state.record);
+    const long = useSelector((state: any) => state.long);
+    const user = useSelector((state: any) => state.user);
+    const [enterTime] = useState(new Date().getTime())
+
+    const [showDatePicker, setShowDatePicker] = useState(false)
+    const [showDurationPicker, setShowDurationPicker] = useState(false)
+    const [showEndDatePicker, setShowEndDatePicker] = useState(false)
+    const [count, setCount] = useState(0)
+    const [status, setStatus] = useState('WFS')
+    const [info, setInfo] = useState<any>(null)
+    const [loaded, setLoaded] = useState(false)
+    const [showHighlight, setShowHighlight] = useState(false)
+    const [showShare, setShowShare] = useState(false)
+    const [shareUrl, setShareUrl] = useState('')
+    const [showResult, setShowResult] = useState(false)
+
+    const statusRef = useRef(status)
+    const picker1Ref = useRef(showDatePicker)
+    const picker2Ref = useRef(showEndDatePicker)
+    const picker3Ref = useRef(showDurationPicker)
+
+    const [posting, setPosting] = useState(false)
+
+    const [postError, setPostError] = useState('')
+
+    const [pickerTitle, setPickerTitle] = useState('')
+
+    const { t } = useTranslation()
+
+    const scenario = props.scenario
+
+    // let router
+    // let navigation;
+    // if (useNavigation) {
+    //     navigation = useNavigation()
+    // }
+
+    // if (process.env.TARO_ENV == 'rn') {
+    //     router = useRoute()
+    // }
+    // else {
+    //     router = useRouter()
+    // }
+
+
+    if (process.env.TARO_ENV == 'weapp') {
+
+
+        useShareAppMessage((e) => {
+
+            var path;
+            if (info && info.id) {
+                var sharePath = `/_health/pages/timeline_detail?type=recent&fast_type=IF&window_id=${info.id}&uid=${user.id}&isfastsleep=${0}&disable_edit=1&enter_type=share`
+                if (user.isLogin) {
+                    sharePath += `&share_uid=${user.id}`
+                }
+                path = sharePath
+            }
+            else {
+                path = 'pages/clock/Clock'
+            }
+            var title = ''
+            var shareDate = global.language == 'en' ? 'Today\'s' : '今日'
+            if (scenario == 'FAST') {
+                title = t('health.share_check_in', { date: shareDate, type: t('health.fasting') })
+            }
+            else {
+                title = t('health.share_check_in', { date: shareDate, type: t('health.sleep') })
+            }
+            return {
+                title: title,
+                path: path,
+                imageUrl: global.momentdetail_share_url//shareUrl
+            }
+        })
+    }
+
+    useEffect(() => {
+        statusRef.current = status;
+        picker1Ref.current = showDatePicker
+        picker2Ref.current = showEndDatePicker
+        picker3Ref.current = showDurationPicker
+        if (!showDatePicker && !showEndDatePicker) {
+            setPostError('')
+        }
+
+        if (showDatePicker) {
+            setPickerTitle(scenario == 'FAST' ? t('health.adjust_fast_start_time') : t('health.adjust_bed_time'))
+        }
+        else if (showEndDatePicker) {
+            setPickerTitle(scenario == 'FAST' ? t('health.adjust_fast_end_time') : t('health.adjust_wake_up2'))
+        }
+        else if (showDurationPicker) {
+            setPickerTitle(scenario == 'FAST' ? t('health.adjust_fast_goal') : t('health.adjust_sleep_goal'))
+        }
+    }, [status, showDatePicker, showEndDatePicker, showDurationPicker])
+
+
+    useEffect(() => {
+        const { events } = record
+        events.map((item) => {
+            if (item.scenario == scenario) {
+                setInfo(item)
+                setStatus(item.status)
+                setLoaded(true)
+            }
+        })
+        downloadFile()
+        timer = setInterval(() => {
+            if (picker1Ref.current || picker2Ref.current || picker3Ref.current) return
+            if (statusRef.current == 'DONE') return
+            setCount(count => count + 1)
+        }, 1000)
+        return () => {
+            clearInterval(timer)
+        }
+    }, [])
+
+    function downloadFile() {
+        Taro.downloadFile({
+            url: 'https://background-pictures.oss-cn-beijing.aliyuncs.com/inapp/checkmark.png',
+            success: (res) => {
+                if (res.statusCode === 200) {
+                    global.checkImg = res.tempFilePath
+
+                } else {
+                }
+            },
+            fail: (err) => {
+            }
+        });
+    }
+
+    function tapClock() {
+        // if (status == 'WFS')
+        //     setShowDurationPicker(true)
+
+
+        var title = scenario == 'FAST' ? t('health.adjust_fast_goal') : t('health.adjust_sleep_goal')
+        props.showPicker('duration', title, info.time.duration, (e) => {
+            var obj = JSON.parse(JSON.stringify(info))
+            obj.time.duration = e
+            setInfo(obj)
+            if (status == 'WFS') {
+                // setShowDurationPicker(false)
+                props.hidePicker()
+                return
+            }
+            update({
+                duration: e
+            })
+        })
+
+    }
+
+    function tapStartPicker() {
+        var title = scenario == 'FAST' ? t('health.adjust_fast_start_time') : t('health.adjust_bed_time')
+        props.showPicker('start', title, info.time.start_timestamp, (e) => {
+            update({
+                start_timestamp: e
+            })
+        })
+    }
+
+    function tapEndPicker() {
+        var title = scenario == 'FAST' ? t('health.adjust_fast_end_time') : t('health.adjust_wake_up2')
+        props.showPicker('end', title, info.time.end_timestamp, (e) => {
+            update({
+                start_timestamp: e
+            })
+        })
+    }
+
+    function start() {
+        // var date = TimeFormatter.stringToDate(selDate, time)
+        // date.setMilliseconds(new Date(enterTimestmap).getMilliseconds())
+
+
+        var params: any = {
+            scenario: scenario, //ACTIVITY
+            type: 'INTERVAL',
+            sub_type: scenario,
+
+            time: {
+                start_timestamp: new Date().getTime(),
+                duration: info.time.duration
+            }
+        }
+
+        if (props.join_id) {
+            params.join_key = props.join_id
+        }
+
+
+
+
+        // if (event_id && event_id != 'undefined') {
+        //     params.event_id = event_id
+        // }
+
+        // if (is_temp) {
+        //     params.event = window == 'EAT' ? 'EAT_CUSTOM' : 'ACTIVE_CUSTOM'
+        // }
+        // params.op_page = window == 'EAT' ? 'HOME_EAT' : 'HOME_ACTIVE'
+
+
+        params.extra = {
+            set_time: global.set_time ? global.set_time : new Date().getTime(),
+            confirm_time: new Date().getTime()
+        }
+        // console.log('打卡提交数据', params)
+        if (posting) return
+        setPosting(true)
+        addEvents(params).then(res => {
+
+            Taro.eventCenter.trigger('refreshClockIndex')
+            Taro.eventCenter.trigger('refreshMoments', '')
+            setShowHighlight(true)
+            setTimeout(() => {
+                setShowHighlight(false)
+            }, 3300)
+            setInfo((res as any).data)
+            setStatus((res as any).data.status)
+            // Taro.showToast({
+            //     title: '成功',
+            //     icon: 'success'
+            // })
+            setPosting(false)
+        }).catch(e => {
+            setPosting(false)
+        })
+    }
+
+    function end() {
+        var params: any = {
+            scenario: scenario, //ACTIVITY
+            type: 'INTERVAL',
+            sub_type: scenario,
+
+            time: {
+                end_timestamp: new Date().getTime()
+            }
+        }
+
+        if (props.join_id) {
+            params.join_key = props.join_id
+        }
+
+
+        params.extra = {
+            set_time: global.set_time ? global.set_time : new Date().getTime(),
+            confirm_time: new Date().getTime()
+        }
+
+        if (posting) return
+        setPosting(true)
+        addEvents(params).then(res => {
+            setShowHighlight(true)
+            setTimeout(() => {
+                setShowHighlight(false)
+            }, 8000)
+            setTimeout(() => {
+                setShowResult(true)
+            }, 2000)
+            Taro.eventCenter.trigger('refreshClockIndex')
+            Taro.eventCenter.trigger('refreshMoments', '')
+            setInfo((res as any).data)
+            setStatus((res as any).data.status)
+            setShareUrl('')
+            // Taro.showToast({
+            //     title: '成功',
+            //     icon: 'success'
+            // })
+            setPosting(false)
+        }).catch(e => {
+            setPosting(false)
+        })
+    }
+
+    function update(params) {
+        var data: any = {
+            id: info.id,
+            time: {
+                ...params
+            }
+        }
+        if (posting) return
+        setPosting(true)
+        addEvents(data).then(res => {
+            if ((res as any).result == false) {
+                setPostError((res as any).error_messages[0])
+                return
+            }
+            Taro.eventCenter.trigger('refreshClockIndex')
+            Taro.eventCenter.trigger('refreshMoments', '')
+            setInfo((res as any).data)
+            props.hidePicker()
+            setCount((count) => count + 1)
+            setPosting(false)
+        }).catch(e => {
+            setPosting(false)
+        })
+    }
+
+    function more() {
+        showActionSheet({
+            // title: '操作',
+            itemList: [t('health.discard'), t('health.discard_restart')],
+            success: (res) => {
+                switch (res) {
+                    case 0:
+                        delWindowId(info.id).then(res => {
+                            setInfo((res as any).data)
+                            setStatus((res as any).data.status)
+                        })
+                        break
+                    case 1:
+                        delWindowId(info.id).then(res => {
+                            start()
+                        })
+                        break
+                }
+            }
+        })
+    }
+
+
+    function tapStart() {
+        setShowDatePicker(true)
+    }
+
+    function tapGoal() {
+        setShowDurationPicker(true)
+    }
+
+    function ringExtra() {
+        if (status == 'DONE') {
+            return {
+                value: '',
+                footer: '',
+                color: MainColorType.orange,
+                result: TimeFormatter.formateDurationBySeconds(info.time.end_timestamp / 1000 - info.time.start_timestamp / 1000),
+            }
+        }
+        if (status == 'WFS') {
+            const count = info.time.duration / 1000
+            var str = TimeFormatter.formateDurationBySeconds(count)
+
+            return {
+                target: str,
+                goal: t('health.adjust_goal'),
+                color: MainColorType.orange
+            }
+        }
+        var percent = 100 * (new Date().getTime() - info.time.start_timestamp) / info.time.duration
+        percent = parseInt(percent + '')
+        return {
+            progress: TimeFormatter.countdown(info.time.start_timestamp),
+            footer: t('health.elapsed', { percent: percent + '%' }),//`ELAPSED ${percent}%`,
+            color: MainColorType.g02
+        }
+    }
+
+    function getBackground() {
+        var time = record.time
+        if (!time) return '#fff'
+        const { background_colors } = time
+        if (!background_colors) {
+            return '#fff'
+        }
+        else if (background_colors.length == 1) {
+            return background_colors[0]
+        }
+        return `linear-gradient(to bottom, ${background_colors[0]}, ${background_colors[1]})`
+    }
+    if (!loaded) {
+        return <View style={{ display: 'flex', flexDirection: 'column', alignItems: 'center' }}>
+            <View className="main_bg" style={{ background: getBackground() }} />
+        </View>
+    }
+
+    function doneComponent() {
+        if (status == 'DONE') {
+            return <View className="share_bg" style={{ justifyContent: 'flex-start' }}>
+
+                <View className="navi_bar" style={{ height: navigationBarHeight }}>
+                    <View style={{
+                        position: 'absolute',
+                        left: 0,
+                        right: 0,
+                        bottom: 0,
+                        height: 44,
+                        display: 'flex',
+                        alignItems: 'center',
+                        justifyContent: 'center'
+                    }}>
+                        <View style={{
+                            position: 'absolute',
+                            width: rpxToPx(92),
+                            height: rpxToPx(64),
+                            left: 22,
+                            top: 22 - rpxToPx(32)
+                        }}
+                            onClick={() => {
+                                process.env.TARO_ENV == 'weapp' ? Taro.navigateBack() : navigation.goBack()
+                            }}>
+                            <IconClose color="#fff" width={rpxToPx(64)} height={rpxToPx(64)} />
+                        </View>
+                    </View>
+
+                </View>
+                <View className="share_card2" style={{ background: MainColorType.orange, marginTop: rpxToPx(26) + navigationBarHeight }}>
+                    {/* {
+                        shareUrl.length > 0 ? <Image src={shareUrl} style={{ width: rpxToPx(900), height: rpxToPx(720) }} /> :
+                            <View style={{ width: rpxToPx(900), height: rpxToPx(720), backgroundColor: MainColorType.g02 }} />
+                    } */}
+                    <RingProgress
+                        radius={rpxToPx(250)} canvasId={"helloworld_share2" + scenario}
+                        //scale={0.75}
+                        count={count}
+                        bgRing={{
+                            color: 'rgba(255,255,255,1)',
+                            width: rpxToPx(88)
+                        }}
+                        real={{
+                            color: MainColorType.orange,
+                            width: rpxToPx(16),
+                            start: getStartArc(info.time.start_timestamp),
+                            duration: getDurationArc(info.time.start_timestamp, info.time.end_timestamp)
+                        }}
+                        extra={ringExtra()}
+                        shareBg={[MainColorType.orange]}
+                        isCompleted
+                        shareCover={
+                            url => {
+                                setShareUrl(url)
+                            }
+                        }
+                    />
+                    {
+                        <View className="share_operate_content">
+                            {
+                                showResult ? <View style={{ display: 'flex', flex: 1, flexDirection: 'row' }}>
+                                    <NewButton type={NewButtonType.custom} onClick={() => {
+                                        setShowDatePicker(true)
+                                    }}>
+                                        <View className="operate_item" >
+                                            <View className="g02 h24 white_50">{scenario == 'FAST' ? t('health.started') : t('health.bedtime1')}</View>
+                                            <View className="h44 bold white" style={{ marginTop: rpxToPx(8), marginBottom: rpxToPx(8) }}>{TimeFormatter.dateTimeFormate(info.time.start_timestamp, true)}</View>
+                                            <View className="h30 bold white_50">{scenario == 'FAST' ? t('health.adjust_start') : t('health.adjust_bedtime')}</View>
+                                        </View>
+                                    </NewButton>
+                                    <NewButton type={NewButtonType.custom} onClick={() => {
+                                        tapEndPicker()
+                                    }}>
+                                        <View className="operate_item" >
+                                            <View className={showHighlight ? 'card_highlight end_card_show' : ''} />
+                                            <View className="g02 h24 white_50">{scenario == 'FAST' ? t('health.finished') : t('health.wokeup')}</View>
+                                            <View className="h44 bold white" style={{ marginTop: rpxToPx(8), marginBottom: rpxToPx(8) }}>{TimeFormatter.dateTimeFormate(info.time.end_timestamp, true)}</View>
+                                            <View className="h30 bold white_50">{scenario == 'FAST' ? t('health.adjust_end') : t('health.adjust_wake_up')}</View>
+                                        </View>
+                                    </NewButton>
+                                </View> :
+                                    <View className="white bold" style={{ fontSize: rpxToPx(72), display: 'flex', alignItems: 'center', justifyContent: 'center', flex: 1 }}>{scenario == 'FAST' ? t('health.fast_finished') : t('health.sleep_finished')}</View>
+                            }
+
+
+                        </View>
+                    }
+                </View>
+                {/* {
+                    showResult && <View style={{ opacity: 0 }}><View className="shareBtnAni" >
+                        <ShareBtn hideShare={true}>
+                            <NewButton type={NewButtonType.custom}>
+                                <View className="share_btn1" style={{ backgroundColor: MainColorType.success }}>
+                                    <Image src={require('@assets/_health/wechat.png')} style={{ width: rpxToPx(48), height: rpxToPx(48) }} />
+                                    <View className="white bold h34">Send to friends</View>
+                                </View>
+                            </NewButton>
+                        </ShareBtn>
+                    </View>
+                    </View>
+                } */}
+
+                {
+                    showResult && <NewButton type={NewButtonType.custom}
+                        onClick={() => {
+                            Taro.reLaunch({
+                                url: '/pages/moment/moment'
+                            })
+                        }}
+                    ><View
+
+                        className="bold h30 momentBtnAni" style={{ marginTop: rpxToPx(26) }}>{t('health.view_in_moments')}</View>
+                    </NewButton>
+                }
+                {
+                    showResult && <NewButton type={NewButtonType.custom} onClick={() => {
+                        Taro.redirectTo({
+                            url: `./log_record?scenario=MEAL`
+                        })
+                    }}><View
+
+                        className="h30 bold white_75 eatBtnAni" style={{
+                            backgroundColor: MainColorType.black_05,
+                            marginTop: rpxToPx(26),
+                            width: rpxToPx(698),
+                            height: rpxToPx(108),
+                            borderRadius: rpxToPx(28),
+                            display: 'flex',
+                            alignItems: 'center',
+                            justifyContent: 'center'
+                        }}>
+                            {t('health.what_eating')}
+                        </View>
+                    </NewButton>
+
+                }
+
+
+
+            </View>
+        }
+    }
+
+    function naviTitle() {
+        switch (status) {
+            case 'WFS':
+                return scenario == 'FAST' ? t('health.upcoming_fast') : t('health.upcoming_sleep')
+            case 'OG':
+                return scenario == 'FAST' ? t('health.you_are_fasting') : t('health.your_are_sleeping')
+            case 'DONE':
+                return scenario == 'FAST' ? 'Fast completed' : 'Sleep completed'
+        }
+    }
+
+    function bgView() {
+        if (process.env.TARO_ENV == 'weapp') {
+            return <View className="main_bg" style={{ background: getBackground() }} />
+        }
+        var time = record.time
+        if (!time) return <View />
+        const { background_colors } = time
+        if (!background_colors) {
+            return <View />
+        }
+        else if (background_colors.length == 1) {
+            return <View />
+        }
+        return <LinearGradient style={{
+            position: 'absolute',
+            left: 0,
+            top: 0,
+
+            width: rpxToPx(750),
+            height: screenHeight,
+            zIndex: 0,
+            pointerEvents: 'none'
+        }}
+            colors={[background_colors[0], background_colors[1]]}
+            start={{ x: 0, y: 0 }}
+            end={{ x: 0, y: 1 }} pointerEvents="none" />
+    }
+
+    function render() {
+
+        return <View style={{ display: 'flex', flexDirection: 'column', alignItems: 'center' }}>
+            <View style={{ height: rpxToPx(26) }} />
+            <View className="progress_card">
+                <View onClick={() => {
+                    tapClock()
+                    // setShowDurationPicker(true)
+                }}>
+                    <RingProgress
+                        radius={rpxToPx(250)} canvasId={"helloworld" + scenario}
+                        //scale={0.75}
+                        count={count}
+                        bgRing={{
+                            color: 'rgba(255,255,255,0.25)',
+                            width: rpxToPx(88)
+                        }}
+                        target={{
+                            color: 'rgba(255,255,255,0.5)',
+                            width: rpxToPx(88),
+                            start: status == 'WFS' ? getStartArc(new Date().getTime()) : getStartArc(info.time.start_timestamp),
+                            duration: status == 'WFS' ? getDurationArc(new Date().getTime(), new Date().getTime() + info.time.duration) : getDurationArc(info.time.start_timestamp, info.time.start_timestamp + info.time.duration)
+                        }}
+                        real={status == 'WFS' ? null : {
+                            color: MainColorType.orange,
+                            width: rpxToPx(88),
+                            start: getStartArc(info.time.start_timestamp),
+                            duration: getDurationArc(info.time.start_timestamp, new Date().getTime())
+                        }}
+                        extra={ringExtra()}
+                    />
+                </View>
+
+                {
+                    status == 'OG' && <View className="operate_content" style={{ marginTop: -rpxToPx(20) }}>
+                        <NewButton type={NewButtonType.custom} onClick={() => {
+                            tapStartPicker()
+                        }}>
+                            <View className="operate_item_index">
+                                <View className={showHighlight ? 'card_highlight start_card_show' : ''} />
+                                <View className="black_50 h24">{scenario == 'FAST' ? t('health.started') : t('health.bedtime1')}</View>
+                                <View className="h44 bold" style={{ marginTop: rpxToPx(8), marginBottom: rpxToPx(8) }}>{TimeFormatter.abbrTimestampFormat(info.time.start_timestamp)}</View>
+                                <View className="h30 bold" style={{ color: MainColorType.orange }}>{scenario == 'FAST' ? t('health.adjust_start') : t('health.adjust_bedtime')}</View>
+                            </View>
+                        </NewButton>
+                        <NewButton type={NewButtonType.custom} onClick={() => {
+                            tapClock()
+                        }}>
+                            <View className="operate_item_index">
+                                <View className="black_50 h24">{t('health.time_goal', { time: TimeFormatter.formateDurationBySeconds(info.time.duration / 1000) })}</View>
+                                <View className="h44 bold" style={{ marginTop: rpxToPx(8), marginBottom: rpxToPx(8) }}>{TimeFormatter.abbrTimestampFormat(info.time.target_end_timestamp)}</View>
+                                <View className="h30 bold" style={{ color: MainColorType.orange }}>{t('health.adjust_goal')}</View>
+                            </View>
+                        </NewButton>
+                    </View>
+                }
+                {
+                    status == 'WFS' && <NewButton
+                        type={NewButtonType.fill}
+                        title={scenario == 'FAST' ? record.actions.FAST_START : record.actions.SLEEP_BEDTIME}
+                        width={rpxToPx(490)}
+                        height={rpxToPx(108)}
+                        color={MainColorType.orange}
+                        onClick={start}
+                    />
+                }
+
+                {
+                    status == 'OG' && <View style={{ display: 'flex', flexDirection: 'row', alignItems: 'center' }}><NewButton
+                        type={NewButtonType.fill}
+                        title={scenario == 'FAST' ? record.actions.FAST_END : record.actions.SLEEP_WAKE_UP}
+                        width={rpxToPx(490)}
+                        height={rpxToPx(108)}
+                        color={MainColorType.orange}
+                        onClick={end}
+                    />
+                        {/* <NewButton
+                            type={NewButtonType.more}
+                            onClick={more}
+                        /> */}
+                        <NewButton
+                            type={NewButtonType.img}
+                            btnStyle={{ marginRight: -rpxToPx(72) }}
+                            onClick={more}
+                        >
+                            <View style={{ width: rpxToPx(72), height: rpxToPx(72), display: 'flex', alignItems: 'center', justifyContent: 'center' }}>
+                                <IconMore2 width={rpxToPx(36)} color="#000" />
+                            </View>
+
+                        </NewButton>
+                        {/* <View onClick={more}>More</View> */}
+                    </View>
+                }
+                {
+                    status == 'OG' && <View className="share_icon">
+                        <ShareBtn>
+                            <Image onClick={() => {
+                                // setShareUrl('')
+                                // setShowShare(true)
+                            }} src={require('@assets/_health/wechat.png')} style={{
+                                width: rpxToPx(72),
+                                height: rpxToPx(72)
+                            }} />
+                        </ShareBtn>
+                    </View>
+                }
+
+
+            </View>
+            {/* {
+                status == 'WFS' && <View className="eat_card" onClick={() => {
+                    Taro.redirectTo({
+                        url: `./log_record?scenario=${scenario == 'FAST' ? 'MEAL' : 'ACTIVITY'}`
+                    })
+                }}>
+                    <View className="h30 bold">Or share your meals</View>
+                    <View className="h24 g02" style={{ marginTop: rpxToPx(12) }}>if you haven't started fasting yet</View>
+                    <View className="eat_card_arrow">
+                        <IconArrow width={rpxToPx(34)} color={MainColorType.black_25} />
+                    </View>
+                </View>
+            } */}
+
+            {
+                props.join_id && <FollowInfo user={long.follow} timeMode={true} />
+            }
+
+            {/* {
+                status == 'OG' && <View className="eat_card" style={{ height: rpxToPx(120) }} onClick={() => {
+                    Taro.redirectTo({
+                        url: `./log_record?scenario=${scenario == 'FAST' ? 'MEAL' : 'ACTIVITY'}`
+                    })
+                }}>
+                    <View className="h24 g02" style={{ marginTop: rpxToPx(12) }}>How are you feeling?</View>
+                    <View className="eat_card_arrow">
+                        <IconArrow width={rpxToPx(34)} color={MainColorType.g02} />
+                    </View>
+                </View>
+            } */}
+
+
+            {/* {
+            showDurationPicker && <Modal
+                testInfo={null}
+                dismiss={() => {
+                    setShowDurationPicker(false)
+                }}
+                confirm={() => { }}>
+                {
+                    <NewDurationPicker type={DurationPickerType.normal} value={info.time.duration} onChange={(e) => {
+                        console.log(e)
+                        var obj = JSON.parse(JSON.stringify(info))
+                        obj.time.duration = e
+                        setInfo(obj)
+                    }} />
+                }
+            </Modal>
+        } */}
+            {
+                showShare && <View className="share_bg">
+                    {
+                        shareUrl == '' && <View className="share_canvas">
+                            <RingProgress
+                                radius={rpxToPx(250)} canvasId={"helloworld_share" + scenario}
+                                //scale={0.75}
+                                count={count}
+                                bgRing={{
+                                    color: 'rgba(255,255,255,0.25)',
+                                    width: rpxToPx(88)
+                                }}
+                                target={{
+                                    color: 'rgba(255,255,255,0.5)',
+                                    width: rpxToPx(88),
+                                    start: status == 'WFS' ? getStartArc(new Date().getTime()) : getStartArc(info.time.start_timestamp),
+                                    duration: status == 'WFS' ? getDurationArc(new Date().getTime(), new Date().getTime() + info.time.duration) : getDurationArc(info.time.start_timestamp, info.time.start_timestamp + info.time.duration)
+                                }}
+                                real={status == 'WFS' ? null : {
+                                    color: MainColorType.orange,
+                                    width: rpxToPx(88),
+                                    start: getStartArc(info.time.start_timestamp),
+                                    duration: getDurationArc(info.time.start_timestamp, new Date().getTime())
+                                }}
+                                extra={ringExtra()}
+                                shareBg={[record.time.background_colors[0], record.time.background_colors[1]]}
+                                shareCover={
+                                    url => {
+                                        setShareUrl(url)
+                                    }
+                                }
+                            />
+                        </View>
+                    }
+                    <View className="navi_bar" style={{ height: navigationBarHeight }}>
+                        <View style={{
+                            position: 'absolute',
+                            left: 0,
+                            right: 0,
+                            bottom: 0,
+                            height: 44,
+                            display: 'flex',
+                            alignItems: 'center',
+                            justifyContent: 'center'
+                        }}>
+                            <View style={{
+                                position: 'absolute',
+                                width: rpxToPx(92),
+                                height: rpxToPx(64),
+                                left: 22,
+                                top: 22 - rpxToPx(32)
+                            }}
+                                onClick={() => {
+                                    setShowShare(false)
+                                }}>
+                                <IconClose color="#fff" width={rpxToPx(64)} height={rpxToPx(64)} />
+                            </View>
+                        </View>
+                    </View>
+                    <View className="share_card" style={{ background: record.time.background_colors[1] }}>
+                        {
+                            shareUrl.length > 0 ? <Image src={shareUrl} style={{ width: rpxToPx(900), height: rpxToPx(720), flexShrink: 0 }} /> :
+                                <View style={{ width: rpxToPx(900), height: rpxToPx(720), backgroundColor: MainColorType.g02, flexShrink: 0 }} />
+                        }
+                        {
+                            status == 'OG' && <View className="operate_content" style={{ marginTop: -rpxToPx(20) }}>
+                                <View className="operate_item">
+                                    <View className="black_50 h24">STARTED</View>
+                                    <View className="h44 bold" style={{ marginTop: rpxToPx(8), marginBottom: rpxToPx(8) }}>{TimeFormatter.dateTimeFormate(info.time.start_timestamp, true)}</View>
+                                </View>
+                                <View className="operate_item">
+                                    <View className="black_50 h24">{TimeFormatter.formateDurationBySeconds(info.time.duration / 1000)} Goal</View>
+                                    <View className="h44 bold" style={{ marginTop: rpxToPx(8), marginBottom: rpxToPx(8) }}>{TimeFormatter.dateTimeFormate(info.time.target_end_timestamp, true)}</View>
+                                </View>
+                            </View>
+                        }
+                    </View>
+                    <ShareBtn hideShare={true}>
+                        <NewButton type={NewButtonType.custom} btnStyle={{ opacity: 0 }}>
+                            <View className="share_btn1" style={{ backgroundColor: MainColorType.success }}>
+                                <Image src={require('@assets/_health/wechat.png')} style={{ width: rpxToPx(48), height: rpxToPx(48) }} />
+                                <View className="white bold h34">Send to friends</View>
+                            </View>
+                        </NewButton>
+                    </ShareBtn>
+                </View>
+            }
+        </View>
+    }
+    return <View style={{ width: rpxToPx(750), height: 1000, display: 'flex', flexShrink: 0, alignItems: 'center', flexDirection: 'column' }}>
+        {
+            status == 'DONE' ? doneComponent() : render()
+        }
+
+        <MomentDetailShare
+            user={user}
+            canvas_id={"time_record" + scenario}
+            btnColor={MainColorType.orange} />
+    </View>
+}

+ 0 - 2
src/pages/metric/Metric.tsx

@@ -1,7 +1,6 @@
 import { View, Text } from "@tarojs/components";
 import Metric from "@/features/trackSomething/components/Metric";
 import { useDidShow, usePullDownRefresh, useShareAppMessage } from "@tarojs/taro";
-import Tabbar from "@/components/navigation/TabBar";
 import { useTranslation } from "react-i18next";
 
 export default function Page() {
@@ -16,7 +15,6 @@ export default function Page() {
     }
 
     usePullDownRefresh(() => {
-
         global.refreshMetric()
     })
     return <View className="container">