@ledvance/ui-biz-bundle 1.1.160 → 1.1.162

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -4,7 +4,7 @@
4
4
  "name": "@ledvance/ui-biz-bundle",
5
5
  "pid": [],
6
6
  "uiid": "",
7
- "version": "1.1.160",
7
+ "version": "1.1.162",
8
8
  "scripts": {},
9
9
  "dependencies": {
10
10
  "@ledvance/base": "^1.x",
@@ -7,7 +7,7 @@ import { MixLightBean } from '../timeSchedule/mix/MixLightBean'
7
7
  import { Formatter } from '@tuya/tuya-panel-lamp-sdk'
8
8
  import { DreamMusicDataType, dreamMusicData } from './MusicDataBean'
9
9
  import { Result } from '@ledvance/base/src/models/modules/Result'
10
- import { getFeature, NativeApi } from '@ledvance/base/src/api/native'
10
+ import { getFeature, putFeature, NativeApi } from '@ledvance/base/src/api/native'
11
11
  import { parseJSON } from '@tuya/tuya-panel-lamp-sdk/lib/utils'
12
12
  import { WorkMode } from '@ledvance/base/src/utils/interface'
13
13
  const DreamMusicFormatter = new Formatter.DreamLightMicMusicFormatter()
@@ -32,7 +32,16 @@ export const useWorkMode = (dpKey: string, isFeatureMode?: boolean): [WorkMode,
32
32
  useUpdateEffect(() =>{
33
33
  setMode(workMode)
34
34
  }, [workMode])
35
- return [mode, setWorkMode]
35
+
36
+ const setWorkModeWithFeature = async (v: WorkMode): Promise<Result<any>> => {
37
+ const result = await setWorkMode(v)
38
+ if (isFeatureMode) {
39
+ await putFeature(devId, 'work_mode', v)
40
+ }
41
+ return result
42
+ }
43
+
44
+ return [mode, setWorkModeWithFeature]
36
45
  }
37
46
 
38
47
  export const useMixRgbcw = (mixRgbcwDp: string, mixSwitchDp: string) => {
@@ -8,18 +8,19 @@ import { hex2Int, spliceByStep } from '@ledvance/base/src/utils/common'
8
8
  import { to16 } from '@tuya/tuya-panel-lamp-sdk/lib/utils'
9
9
  import { useUpdateEffect } from 'ahooks'
10
10
  import dayjs from 'dayjs'
11
- import { padStart } from 'lodash'
11
+ import { cloneDeep, padStart } from 'lodash'
12
12
  import { useCallback, useEffect, useRef, useState } from 'react'
13
+ import { AsyncStorage } from 'react-native'
13
14
  import {
14
15
  BiorhythmBean,
15
16
  BiorhythmGradientType,
16
17
  BiorhythmGradientTypeMap,
17
18
  BiorhythmGradientTypeMap2,
18
19
  colorTemperatureValue,
19
- colorTempPercent,
20
- Plan,
20
+ colorTempPercent, Plan,
21
21
  RemoteBiorhythmBean,
22
22
  } from './BiorhythmBean'
23
+ import iconList from './iconListData'
23
24
 
24
25
  type UseBiorhythmType = (dpKey: string, disabledFeature?: boolean) => [BiorhythmBean, SetBiorhythmType];
25
26
  type SetBiorhythmType = (biorhythmObj: BiorhythmBean, pushFeature?: boolean) => Promise<Result<any>>;
@@ -401,3 +402,91 @@ function dto2Vo(remoteBiorhythmBean: RemoteBiorhythmBean): BiorhythmBean {
401
402
  }),
402
403
  }
403
404
  }
405
+
406
+ export const replaceImg = (img) => {
407
+ const item = iconList?.find(val => val.id === Number(img))
408
+ switch (img) {
409
+ case 'rhythm_icon1':
410
+ case '31':
411
+ return { icon: res.biorhythom_icon1, iconId: 1 }
412
+ case 'rhythm_icon2':
413
+ case '33':
414
+ return { icon: res.biorhythom_icon5, iconId: 5 }
415
+ case 'rhythm_icon3':
416
+ case '35':
417
+ return { icon: res.biorhythom_icon2, iconId: 2 }
418
+ case 'rhythm_icon4':
419
+ case '32':
420
+ return { icon: res.biorhythom_icon9, iconId: 9 }
421
+ case 'rhythm_icon12':
422
+ case '39':
423
+ return { icon: res.biorhythom_icon3, iconId: 3 }
424
+ default:
425
+ return { icon: item?.icon, iconId: item?.id }
426
+ }
427
+ }
428
+
429
+ export function useStorageBiorhythm(): [[], (key, enable: boolean, gradient: BiorhythmGradientType, weeks: number[], planList: Plan[]) => void, (key) => void, (key) => {
430
+ weeks: number[];
431
+ planList: Plan[];
432
+ enable: boolean;
433
+ gradient: BiorhythmGradientType
434
+ }] {
435
+ const [storageBiorhythms, setStorageBiorhythms] = useState([])
436
+ useEffect(() => {
437
+ AsyncStorage.getItem('BIORHYTHM_STORAGE').then(res => {
438
+ if (res) {
439
+ const storageBiorhythms = JSON.parse(res)
440
+ setStorageBiorhythms(storageBiorhythms)
441
+ xLog('AsyncStorage getBiorhythm', storageBiorhythms)
442
+ }
443
+ })
444
+ }, [])
445
+
446
+ const saveBiorhythm = useCallback((key, enable: boolean, gradient: BiorhythmGradientType, weeks: number[], planList: Plan[]) => {
447
+ const newPlanList = planList?.map(item => {
448
+ return { ...item, icon: `${item.icon}` }
449
+ }).sort((a, b) => a.time - b.time)
450
+ const biorhythm = cloneDeep({
451
+ key: key,
452
+ enable: enable,
453
+ weeks: weeks,
454
+ gradient: gradient,
455
+ planList: newPlanList,
456
+ })
457
+ const newStorageBiorhythms = storageBiorhythms.filter(item => item.key !== key)
458
+ newStorageBiorhythms.unshift(biorhythm)
459
+ setStorageBiorhythms(newStorageBiorhythms)
460
+ xLog('AsyncStorage saveBiorhythm', newStorageBiorhythms)
461
+ AsyncStorage.setItem('BIORHYTHM_STORAGE', JSON.stringify(newStorageBiorhythms), (error) => {
462
+ xLog('AsyncStorage saveBiorhythm error', error)
463
+ }).then()
464
+ }, [storageBiorhythms])
465
+
466
+ const removeBiorhythm = useCallback((key) => {
467
+ const newStorageBiorhythms = storageBiorhythms.filter(item => item.key !== key)
468
+ setStorageBiorhythms(newStorageBiorhythms)
469
+ AsyncStorage.setItem('BIORHYTHM_STORAGE', JSON.stringify(newStorageBiorhythms)).then(res => {
470
+ xLog('AsyncStorage removeBiorhythm res', res)
471
+ })
472
+ }, [storageBiorhythms])
473
+
474
+ const applyBiorhythm = useCallback((key) => {
475
+ const newBiorhythm = storageBiorhythms.find(item => item.key === key)
476
+ const planList = newBiorhythm.planList?.map((it, index) => {
477
+ return {
478
+ ...it,
479
+ index: it.index ?? index,
480
+ icon: replaceImg(it?.iconId || it?.icon)?.icon,
481
+ iconId: replaceImg(it?.iconId || it?.icon)?.iconId,
482
+ }
483
+ })
484
+ return {
485
+ weeks: newBiorhythm.weeks,
486
+ planList: planList,
487
+ enable: newBiorhythm.enable,
488
+ gradient: newBiorhythm.gradient,
489
+ }
490
+ }, [storageBiorhythms])
491
+ return [storageBiorhythms, saveBiorhythm, removeBiorhythm, applyBiorhythm]
492
+ }
@@ -1,5 +1,6 @@
1
1
  import Card from '@ledvance/base/src/components/Card'
2
2
  import DeleteButton from '@ledvance/base/src/components/DeleteButton'
3
+ import InfoText from '@ledvance/base/src/components/InfoText'
3
4
  import Page from '@ledvance/base/src/components/Page'
4
5
  import Spacer from '@ledvance/base/src/components/Spacer'
5
6
  import LdvWeekView from '@ledvance/base/src/components/weekSelect'
@@ -8,17 +9,18 @@ import { useParams } from '@ledvance/base/src/hooks/Hooks'
8
9
  import I18n from '@ledvance/base/src/i18n'
9
10
  import { useDeviceInfo, useSystemTimeFormate, } from '@ledvance/base/src/models/modules/NativePropsSlice'
10
11
  import res from '@ledvance/base/src/res'
12
+ import { xLog } from '@ledvance/base/src/utils'
11
13
  import { cctToColor } from '@ledvance/base/src/utils/cctUtils'
12
- import { convertMinutesTo12HourFormat, loopText, showDialog as showCommonDialog } from '@ledvance/base/src/utils/common'
14
+ import { convertMinutesTo12HourFormat, loopText, showDialog } from '@ledvance/base/src/utils/common'
13
15
  import { useNavigation } from '@react-navigation/native'
14
16
  import { useDebounceFn, useReactive, useUpdateEffect } from 'ahooks'
15
17
  import { useConflictTask } from 'hooks/DeviceDpStateHooks'
16
18
  import { cloneDeep, sortBy } from 'lodash'
17
- import React, { useCallback, useMemo } from 'react'
18
- import { FlatList, Image, Linking, ScrollView, Switch, Text, TouchableOpacity, View } from 'react-native'
19
+ import React, { useCallback, useEffect, useMemo, useState } from 'react'
20
+ import { FlatList, Image, Linking, ScrollView, Switch, Text, TouchableOpacity, View, AsyncStorage } from 'react-native'
19
21
  import { Dialog, Modal, Utils } from 'tuya-panel-kit'
20
22
  import { ui_biz_routerKey } from '../../navigation/Routers'
21
- import { useBiorhythm } from './BiorhythmActions'
23
+ import { replaceImg, useBiorhythm, useStorageBiorhythm } from './BiorhythmActions'
22
24
  import {
23
25
  BiorhythmBean,
24
26
  BiorhythmGradientType,
@@ -64,6 +66,8 @@ const BiorhythmPage = (props: { theme?: ThemeType }) => {
64
66
  const is24Hour = useSystemTimeFormate()
65
67
  const devicesJudge = pIdList.some(val => val === productId)
66
68
  const [checkConflict, resolveConflict] = useConflictTask(params.conflictDps)
69
+ const [storageBiorhythms, saveStorageBiorhythms, removeStorageBiorhythm, applyStorageBiorhythm] = useStorageBiorhythm()
70
+ const [biorhythmListVisible, setBiorhythmListVisible] = useState(false)
67
71
 
68
72
  const state = useReactive<UIState>({
69
73
  ...biorhythm,
@@ -163,29 +167,6 @@ const BiorhythmPage = (props: { theme?: ThemeType }) => {
163
167
  run()
164
168
  }, [state.flag])
165
169
 
166
- const replaceImg = (img) => {
167
- const item = iconList?.find(val => val.id === Number(img))
168
- switch (img) {
169
- case 'rhythm_icon1':
170
- case '31':
171
- return { icon: res.biorhythom_icon1, iconId: 1 }
172
- case 'rhythm_icon2':
173
- case '33':
174
- return { icon: res.biorhythom_icon5, iconId: 5 }
175
- case 'rhythm_icon3':
176
- case '35':
177
- return { icon: res.biorhythom_icon2, iconId: 2 }
178
- case 'rhythm_icon4':
179
- case '32':
180
- return { icon: res.biorhythom_icon9, iconId: 9 }
181
- case 'rhythm_icon12':
182
- case '39':
183
- return { icon: res.biorhythom_icon3, iconId: 3 }
184
- default:
185
- return { icon: item?.icon, iconId: item?.id }
186
- }
187
- }
188
-
189
170
  useUpdateEffect(() => {
190
171
  const planList = biorhythm.planList?.map(item => {
191
172
  return {
@@ -261,6 +242,26 @@ const BiorhythmPage = (props: { theme?: ThemeType }) => {
261
242
  <Page
262
243
  backText={deviceInfo.name}
263
244
  onBackClick={navigation.goBack}
245
+ headlineTopContent={<View style={{ flexDirection: 'row', width: '100%', justifyContent: 'space-between' }}>
246
+ <DeleteButton style={{flex: 1}} text={I18n.getLang('biorhythm_save_as')} onPress={() => {
247
+ Dialog.prompt({
248
+ title: I18n.getLang('biorhythm_save_title'),
249
+ placeholder: I18n.getLang('biorhythm_save_placeholder'),
250
+ defaultValue: `${deviceInfo.name}`,
251
+ cancelText: I18n.getLang('auto_scan_system_cancel'),
252
+ confirmText: I18n.getLang('auto_scan_system_wifi_confirm'),
253
+ inputWrapperStyle: {backgroundColor: props.theme?.textInput.background, borderRadius: cx(10)},
254
+ autoFocus: true,
255
+ onConfirm: (data, { close }) => {
256
+ saveStorageBiorhythms(data, state.enable, state.gradient, state.weeks, state.planList)
257
+ close()
258
+ }
259
+ })
260
+ }} />
261
+ <DeleteButton style={{flex: 1}} text={I18n.getLang('biorhythm_load')} onPress={() => {
262
+ setBiorhythmListVisible(true)
263
+ }} />
264
+ </View>}
264
265
  headlineText={I18n.getLang('add_new_trigger_time_system_back_text')}
265
266
  headlineIconContent={<Switch
266
267
  value={state.enable}
@@ -275,7 +276,7 @@ const BiorhythmPage = (props: { theme?: ThemeType }) => {
275
276
  channel: 1
276
277
  }
277
278
  if (enable && checkConflict(biorhythmTask)) {
278
- return showCommonDialog({
279
+ return showDialog({
279
280
  method: 'confirm',
280
281
  title: I18n.getLang('conflict_dialog_active_item_bio_rhythm_titel'),
281
282
  subTitle: I18n.getLang('conflict_dialog_active_item_bio_rhythm_description'),
@@ -580,7 +581,7 @@ const BiorhythmPage = (props: { theme?: ThemeType }) => {
580
581
  // fontFamily: 'helvetica_neue_lt_std_roman',
581
582
  }}
582
583
  onPress={() => {
583
- showCommonDialog({
584
+ showDialog({
584
585
  method: 'confirm',
585
586
  title: I18n.getLang('bio_ryhthm_reset_description_text'),
586
587
  onConfirm: (_, { close }) => {
@@ -645,6 +646,54 @@ const BiorhythmPage = (props: { theme?: ThemeType }) => {
645
646
  showGradientTypeSelectModal(false)
646
647
  }}
647
648
  />
649
+ <Modal visible={biorhythmListVisible} onMaskPress={() => {setBiorhythmListVisible(false)}}>
650
+ <View style={{ height: cx(300), padding: cx(16), backgroundColor: props.theme?.card.background }}>
651
+ {
652
+ storageBiorhythms.length === 0 ? (
653
+ <View style={{ flex: 1, justifyContent: 'center', alignItems: 'center' }}>
654
+ <InfoText
655
+ textStyle={{ flex: undefined }}
656
+ icon={res.ic_info}
657
+ text={I18n.getLang('energyconsumption_emptydata')}
658
+ />
659
+ </View>
660
+ ) : (
661
+ <FlatList
662
+ data={storageBiorhythms}
663
+ renderItem={({ item }) => {
664
+ return <View style={{ padding: cx(5), flexDirection: 'row' }}>
665
+ <Text style={{ flex: 1, color: props.theme?.global.fontColor }}>{item.key}</Text>
666
+ <TouchableOpacity style={{width: cx(24), height: cx(24), marginRight: cx(20)}} onPress={() => {
667
+ showDialog({
668
+ method: 'confirm',
669
+ title: I18n.getLang('biorhythm_delete_tips'),
670
+ onConfirm: (_, { close }) => {
671
+ removeStorageBiorhythm(item.key)
672
+ close()
673
+ }
674
+ })
675
+ }}>
676
+ <Image source={{ uri: res.delete}} style={{width: cx(24), height: cx(24), tintColor: props.theme?.global.warning}} />
677
+ </TouchableOpacity>
678
+ <TouchableOpacity style={{width: cx(24), height: cx(24), marginRight: cx(10)}} onPress={() => {
679
+ const newBiorhythm = applyStorageBiorhythm(item.key)
680
+ state.enable = newBiorhythm.enable
681
+ state.gradient = newBiorhythm.gradient
682
+ state.weeks = newBiorhythm.weeks
683
+ state.planList = newBiorhythm.planList
684
+ run()
685
+ setBiorhythmListVisible(false)
686
+ }}>
687
+ <Image source={{ uri:res.ic_checked}} style={{width: cx(24), height: cx(24), tintColor: props.theme?.icon.normal}} />
688
+ </TouchableOpacity>
689
+ </View>
690
+ }}
691
+ keyExtractor={(item) => `${item.key}`}
692
+ />
693
+ )
694
+ }
695
+ </View>
696
+ </Modal>
648
697
  </>
649
698
  )
650
699
  }
@@ -65,6 +65,7 @@ export const getStyles = (theme: ThemeType | undefined) => StyleSheet.create({
65
65
  flexDirection: 'row',
66
66
  justifyContent: 'space-between',
67
67
  alignItems: 'center',
68
+ paddingHorizontal: cx(28),
68
69
  },
69
70
  landscapeTitle: {
70
71
  color: theme?.global.fontColor,
@@ -5,10 +5,57 @@ import React, { useRef } from 'react'
5
5
  import { View } from 'react-native'
6
6
  import { Utils } from 'tuya-panel-kit'
7
7
  import { PowerDataItem } from '../EnergyConsumptionActions'
8
+ import dayjs from 'dayjs'
8
9
 
9
10
  const { withTheme } = Utils.ThemeUtils
10
11
  const cx = Utils.RatioUtils.convertX
11
12
 
13
+ /** 相邻两点时间差超过该阈值(ms)时插入补充点 */
14
+ const GAP_THRESHOLD_MS = 5 * 1000 // 5 秒
15
+
16
+ /**
17
+ * 在时间间隔较大的相邻点之间插入一个"间隙补充点",模拟阶梯式折线效果。
18
+ *
19
+ * 插入规则(基于值的变化方向):
20
+ * - 值下降(cur > next):在 cur.time + 1s 处插入 next.value
21
+ * → 表示值在前点之后立刻跌落,后续一直保持新低值直到 next 点
22
+ * - 值上升(cur <= next):在 next.time - 1s 处插入 cur.value
23
+ * → 表示值一直保持旧值平稳,在 next 点前 1 秒才结束
24
+ */
25
+ function fillGaps(items: PowerDataItem[]): PowerDataItem[] {
26
+ if (!items || items.length < 2) return items
27
+ const result: PowerDataItem[] = []
28
+ for (let i = 0; i < items.length; i++) {
29
+ result.push(items[i])
30
+ if (i < items.length - 1) {
31
+ const cur = items[i]
32
+ const next = items[i + 1]
33
+ const diffMs = next.time - cur.time
34
+ if (diffMs > GAP_THRESHOLD_MS) {
35
+ let gapTime: number
36
+ let gapValue: number
37
+ if (cur.value > next.value) {
38
+ // 值下降:立刻跌落 —— 在前点后 1 秒插入 next.value
39
+ gapTime = cur.time + 1000
40
+ gapValue = next.value
41
+ } else {
42
+ // 值上升(或相等):保持旧值 —— 在后点前 1 秒插入 cur.value
43
+ gapTime = next.time - 1000
44
+ gapValue = cur.value
45
+ }
46
+ const gapDayjs = dayjs(gapTime)
47
+ result.push({
48
+ key: gapDayjs.format('YYYY-MM-DD HH:mm:ss'),
49
+ chartTitle: gapDayjs.format('MM/DD/YYYY HH:mm:ss'),
50
+ time: gapTime,
51
+ value: gapValue,
52
+ })
53
+ }
54
+ }
55
+ }
56
+ return result
57
+ }
58
+
12
59
  interface PowerLineChartProps {
13
60
  theme?: ThemeType
14
61
  data: PowerDataItem[],
@@ -19,7 +66,7 @@ const PowerLineChart = (props: PowerLineChartProps) => {
19
66
  const echarts = useRef()
20
67
  const { data, height } = props
21
68
 
22
- const values = data?.map(item => ([item.time, item.value]))
69
+ const values = fillGaps(data)?.map(item => ([item.time, item.value]))
23
70
 
24
71
  const option = {
25
72
  tooltip: {