@ledvance/group-ui-biz-bundle 1.0.160 → 1.0.163

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/group-ui-biz-bundle",
5
5
  "pid": [],
6
6
  "uiid": "",
7
- "version": "1.0.160",
7
+ "version": "1.0.163",
8
8
  "scripts": {},
9
9
  "dependencies": {
10
10
  "@ledvance/base": "^1.x",
@@ -0,0 +1,108 @@
1
+ import React, { useState } from "react";
2
+ import ThemeType from "@ledvance/base/src/config/themeType";
3
+ import { parseMinutes, PriceSegment } from "./component/EnergyModal";
4
+ import { Utils } from 'tuya-panel-kit'
5
+ import { StyleSheet } from "react-native";
6
+ import {exchangeNumber} from "@ledvance/base/src/utils/common";
7
+
8
+ export const useEditPriceSegment = (segment: PriceSegment | undefined,
9
+ onSegmentSet: (s: PriceSegment) => void, theme?: ThemeType,) => {
10
+ const cx = Utils.RatioUtils.convertX
11
+ const startMin = segment?.startMinute ?? 0;
12
+ const endMin = segment?.endMinute ?? 0;
13
+
14
+ const startTime = parseMinutes(startMin);
15
+ const endTime = parseMinutes(endMin);
16
+
17
+ const [priceStr, setPriceStr] = useState<string>(() => {
18
+ if (segment?.price === undefined) {
19
+ return '0';
20
+ }
21
+ return segment.price.toString();
22
+ });
23
+
24
+ const handleTimeChange = (type: 'startHour' | 'startMinute' | 'endHour' | 'endMinute', value: string) => {
25
+ let startH = Math.floor(startMin / 60);
26
+ let startM = startMin % 60;
27
+ let endH = Math.floor(endMin / 60);
28
+ let endM = endMin % 60;
29
+
30
+ const intVal = parseInt(value, 10) || 0;
31
+ if (type === 'startHour') startH = intVal;
32
+ if (type === 'startMinute') startM = intVal;
33
+ if (type === 'endHour') endH = intVal;
34
+ if (type === 'endMinute') endM = intVal;
35
+
36
+ const updatedSegment: PriceSegment = {
37
+ ...(segment ?? {}),
38
+ startMinute: startH * 60 + startM,
39
+ endMinute: endH * 60 + endM,
40
+ } as PriceSegment;
41
+
42
+ onSegmentSet(updatedSegment);
43
+ };
44
+
45
+ const handlePriceChange = (t: string) => {
46
+ const cleaned = exchangeNumber(t)
47
+ const parsed = parseFloat(cleaned);
48
+ let numericVal = isNaN(parsed) ? 0 : parsed;
49
+ if (numericVal > 999999) {
50
+ numericVal = 999999;
51
+ setPriceStr('999999');
52
+ } else {
53
+ setPriceStr(t);
54
+ }
55
+
56
+ onSegmentSet({
57
+ ...(segment ?? {}),
58
+ price: numericVal
59
+ } as PriceSegment);
60
+ };
61
+
62
+ const style = StyleSheet.create({
63
+ timerHeader: {
64
+ fontSize: cx(16),
65
+ fontWeight: 'bold',
66
+ color: theme?.global.fontColor,
67
+ },
68
+ timerDivider: {
69
+ fontSize: cx(20),
70
+ fontWeight: 'bold',
71
+ color: theme?.global.fontColor, paddingTop: cx(16),
72
+ },
73
+ inputCardViewContainer: {
74
+ flexDirection: 'row',
75
+ paddingHorizontal: cx(16),
76
+ paddingVertical: cx(8),
77
+ alignItems: 'center',
78
+ justifyContent: 'space-between',
79
+ },
80
+ inputSecondaryContainer: {
81
+ flexDirection: 'row',
82
+ borderRadius: cx(4),
83
+ backgroundColor: theme?.textInput.background,
84
+ alignItems: 'center',
85
+ flex: 0.4,
86
+ },
87
+ textInput: {
88
+ height: cx(44),
89
+ marginStart: cx(16),
90
+ marginEnd: cx(6),
91
+ fontSize: cx(16),
92
+ color: theme?.textInput.fontColor,
93
+ flex:1,
94
+ },
95
+ textInputSpacer: {
96
+ height: 1,
97
+ position: 'absolute',
98
+ start: cx(4),
99
+ end: cx(4),
100
+ bottom: 0,
101
+ backgroundColor: theme?.textInput.line,
102
+ }
103
+
104
+ })
105
+ return {
106
+ style, startTime, endTime, currentPrice: priceStr, handleTimeChange, handlePriceChange,
107
+ }
108
+ }
@@ -0,0 +1,89 @@
1
+ import React, { } from 'react';
2
+ import ThemeType from "@ledvance/base/src/config/themeType";
3
+ import { PriceSegment } from "./component/EnergyModal";
4
+ import { Utils } from 'tuya-panel-kit'
5
+ import { Text, TextInput, View } from "react-native";
6
+ import Spacer from '@ledvance/base/src/components/Spacer';
7
+ import Card from '@ledvance/base/src/components/Card';
8
+ import LdvPickerView from '@ledvance/base/src/components/LdvPickerView';
9
+ import I18n from '@ledvance/base/src/i18n';
10
+ import { useEditPriceSegment } from './EditPriceSegmentContract';
11
+
12
+ const { withTheme } = Utils.ThemeUtils
13
+ const cx = Utils.RatioUtils.convertX
14
+
15
+ const EditPriceSegmentScreen = (props: EditPriceProps) => {
16
+ const {style, startTime, endTime, currentPrice, handleTimeChange, handlePriceChange } = useEditPriceSegment(
17
+ props.segment,
18
+ props.onSegmentSet,
19
+ props.theme,
20
+ )
21
+ return (
22
+ <View >
23
+ <Spacer />
24
+ {/* Timer Picker */}
25
+ <View style={{ flexDirection: 'row' }}>
26
+ {/* Start Time */}
27
+ <View style={{ flex: 0.45, alignItems: 'center' }}>
28
+ <Text style={style.timerHeader}>{I18n.getLang('add_randomtimecycle_timestart_topic')}</Text>
29
+ <LdvPickerView
30
+ hour={startTime.hour}
31
+ minute={startTime.minute}
32
+ setHour={(hour) => {
33
+ handleTimeChange('startHour', hour)
34
+ }}
35
+ setMinute={(minute) => {
36
+ handleTimeChange('startMinute', minute)
37
+ }}
38
+ minuteLoop={false}
39
+ minutesStep={30}
40
+ />
41
+ </View>
42
+ {/* Divider */}
43
+ <View style={{ flex: 0.1, justifyContent: 'center', alignItems: 'center' }}>
44
+ <Text style={style.timerDivider}>-</Text>
45
+ </View>
46
+ {/* End Time */}
47
+ <View style={{ flex: 0.45, alignItems: 'center' }}>
48
+ <Text style={style.timerHeader}>{I18n.getLang('add_randomtimecycle_timeend_topic')}</Text>
49
+ <LdvPickerView
50
+ hour={endTime.hour}
51
+ minute={endTime.minute}
52
+ setHour={(hour) => {
53
+ handleTimeChange('endHour', hour)
54
+ }}
55
+ setMinute={(minute) => {
56
+ handleTimeChange('endMinute', minute)
57
+ }}
58
+ minuteLoop={false}
59
+ minutesStep={30}
60
+ isAdd59MinsWhen23Clock={true}
61
+ />
62
+ </View>
63
+ </View>
64
+ <Spacer />
65
+ {/* Price Input */}
66
+ <Card style={{ elevation: cx(1) }}
67
+ containerStyle={style.inputCardViewContainer}>
68
+ <Text style={style.timerHeader}>{I18n.getLang('consumption_data_price_per_kwh_headline_text')}</Text>
69
+ <View style={style.inputSecondaryContainer}>
70
+ <TextInput
71
+ value={currentPrice.toString()}
72
+ onChangeText={handlePriceChange}
73
+ style={style.textInput}
74
+ keyboardType="numeric"
75
+ />
76
+ <View style={style.textInputSpacer} />
77
+ </View>
78
+ </Card>
79
+ </View>
80
+ )
81
+
82
+ }
83
+
84
+ export default withTheme(EditPriceSegmentScreen)
85
+ export interface EditPriceProps {
86
+ segment?: PriceSegment,
87
+ theme?: ThemeType,
88
+ onSegmentSet: (segment: PriceSegment) => void,
89
+ }
@@ -11,16 +11,22 @@ import { DateType } from "./co2Data";
11
11
  import { OverviewItem } from "./EnergyConsumptionPage";
12
12
  import dayjs from "dayjs";
13
13
  import { isNumber } from "lodash";
14
- import { EnergyData, UnitList } from "./component/EnergyModal";
14
+ import {DynamicEnergyData, EnergyData, PriceSegment, UnitList} from "./component/EnergyModal";
15
15
  import {NativeApi} from "@ledvance/base/src/api/native";
16
16
  import {xLog, retryWithBackoff} from "@ledvance/base/src/utils"
17
17
 
18
18
  interface LightConfig {
19
19
  energyConsumption?: EnergyData
20
+ dynamicenergyprice?: DynamicEnergyData
20
21
  }
21
22
 
22
23
  export const useEnergyConsumption = () => {
23
- return useGroupEzvizConfig<LightConfig, EnergyData>('energyConsumption', { unit: UnitList[0], price: '' })
24
+ return useGroupEzvizConfig<LightConfig, EnergyData>('energyConsumption',
25
+ { unit: UnitList[0], price: '' , energyType: 'fixed', generatePrice:''})
26
+ }
27
+ export const useDynamicPrice = () => {
28
+ return useGroupEzvizConfig<LightConfig, DynamicEnergyData>('dynamicenergyprice',
29
+ {intervalPrices: [] as PriceSegment[], generatePrices: [] as PriceSegment[]})
24
30
  }
25
31
 
26
32
  export const unitDivision = (str: string) => {
@@ -4,11 +4,12 @@ import { Image, TouchableOpacity, View } from 'react-native'
4
4
  import { DateType } from '../co2Data'
5
5
  import DateTypeItem from '../component/DateTypeItem'
6
6
  import DateSwitch from '../component/DateSwitch'
7
- import NewBarChart from '../component/NewBarChart'
7
+ import NewBarChart, { calculateIntervalAveragePrice } from '../component/NewBarChart'
8
8
  import { EmptyDataView } from './EmptyDataView'
9
9
  import { exportEnergyCsv } from '../EnergyConsumptionActions'
10
10
  import { Utils } from 'tuya-panel-kit'
11
11
  import res from '@ledvance/base/src/res'
12
+ import { calculateAveragePrice } from '../component/EnergyModal'
12
13
 
13
14
  const { convertX: cx } = Utils.RatioUtils
14
15
 
@@ -42,6 +43,27 @@ export const ChartSection = ({ isLandscape, state, actions, params, styles, them
42
43
  const rows = []
43
44
  let i = 0 // 指向 consumedData 的指针
44
45
  let j = 0 // 指向 generatedData 的指针
46
+
47
+ const getDynamicPrice = (key: string, isGen: boolean) => {
48
+ let itemPrice = Number(price);
49
+ if (params.energyType === 'dynamic' && params.dynamicData) {
50
+ const intervals = isGen ? params.dynamicData.generatePrices : params.dynamicData.intervalPrices;
51
+ if (intervals && intervals.length > 0) {
52
+ if (state.dateType === DateType.Day) {
53
+ const hour = parseInt(key.split(':')[0]);
54
+ if (!isNaN(hour)) {
55
+ itemPrice = calculateIntervalAveragePrice(intervals, hour * 60, (hour + 1) * 60);
56
+ } else {
57
+ itemPrice = calculateAveragePrice(intervals);
58
+ }
59
+ } else {
60
+ itemPrice = calculateAveragePrice(intervals);
61
+ }
62
+ }
63
+ }
64
+ return itemPrice;
65
+ }
66
+
45
67
  // 2. 双指针合并算法
46
68
  while (i < consumedData.length || j < generatedData.length) {
47
69
  const consumedItem = consumedData[i]
@@ -55,10 +77,12 @@ export const ChartSection = ({ isLandscape, state, actions, params, styles, them
55
77
  const consumedValue = Number(consumedItem.value)
56
78
  const generatedValue = Number(generatedItem.value)
57
79
  if (displayMode === 'consumption' || displayMode === 'both') {
58
- rowData.push(consumedValue.toFixed(2), (consumedValue * price).toFixed(2))
80
+ const itemPrice = getDynamicPrice(consumedKey, false)
81
+ rowData.push(consumedValue.toFixed(2), (consumedValue * itemPrice).toFixed(2))
59
82
  }
60
83
  if (displayMode === 'generation' || displayMode === 'both') {
61
- rowData.push(generatedValue.toFixed(2), (generatedValue * price).toFixed(2))
84
+ const itemPrice = getDynamicPrice(generatedKey, true)
85
+ rowData.push(generatedValue.toFixed(2), (generatedValue * itemPrice).toFixed(2))
62
86
  }
63
87
  i++
64
88
  j++
@@ -66,7 +90,8 @@ export const ChartSection = ({ isLandscape, state, actions, params, styles, them
66
90
  dateKey = consumedItem.key
67
91
  const consumedValue = Number(consumedItem.value)
68
92
  if (displayMode === 'consumption' || displayMode === 'both') {
69
- rowData.push(consumedValue.toFixed(2), (consumedValue * price).toFixed(2))
93
+ const itemPrice = getDynamicPrice(consumedKey, false)
94
+ rowData.push(consumedValue.toFixed(2), (consumedValue * itemPrice).toFixed(2))
70
95
  }
71
96
  if (displayMode === 'generation' || displayMode === 'both') {
72
97
  // 在 'generation' 或 'both' 模式下,为缺失的产生数据补0
@@ -81,7 +106,8 @@ export const ChartSection = ({ isLandscape, state, actions, params, styles, them
81
106
  rowData.push('0.00', '0.00')
82
107
  }
83
108
  if (displayMode === 'generation' || displayMode === 'both') {
84
- rowData.push(generatedValue.toFixed(2), (generatedValue * price).toFixed(2))
109
+ const itemPrice = getDynamicPrice(generatedKey, true)
110
+ rowData.push(generatedValue.toFixed(2), (generatedValue * itemPrice).toFixed(2))
85
111
  }
86
112
  j++
87
113
  }
@@ -91,7 +117,7 @@ export const ChartSection = ({ isLandscape, state, actions, params, styles, them
91
117
  }
92
118
 
93
119
  exportEnergyCsv(header, rows)
94
- }, [state.displayMode, state.consumptionChartData, state.generationChartData, params.price, params.unit])
120
+ }, [state.displayMode, state.consumptionChartData, state.generationChartData, params.price, params.unit, params.energyType, params.dynamicData, state.dateType])
95
121
 
96
122
  const isDataEmpty = state.consumptionChartData.length <= 0 && state.generationChartData.length <= 0
97
123
 
@@ -133,6 +159,9 @@ export const ChartSection = ({ isLandscape, state, actions, params, styles, them
133
159
  generatedData={state.generationChartData}
134
160
  price={state.price}
135
161
  unit={params.unit}
162
+ energyType={params.energyType}
163
+ dynamicData={params.dynamicData}
164
+ dateType={state.dateType}
136
165
  />
137
166
  </>
138
167
  )}