@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 +1 -1
- package/src/modules/energyConsumption/EditPriceSegmentContract.tsx +108 -0
- package/src/modules/energyConsumption/EditPriceSegmentScreen.tsx +89 -0
- package/src/modules/energyConsumption/EnergyConsumptionActions.ts +8 -2
- package/src/modules/energyConsumption/EnergyConsumptionChart/ChartSection.tsx +35 -6
- package/src/modules/energyConsumption/EnergyConsumptionPage.tsx +462 -293
- package/src/modules/energyConsumption/Router.ts +9 -0
- package/src/modules/energyConsumption/SetSegmentPricesContract.tsx +461 -0
- package/src/modules/energyConsumption/SetSegmentedPricesScreen.tsx +201 -0
- package/src/modules/energyConsumption/component/EnergyModal.tsx +73 -13
- package/src/modules/energyConsumption/component/NewBarChart.tsx +71 -3
- package/src/modules/energyConsumption/component/VerticalDailyPricesIndicator.tsx +175 -0
- package/src/modules/fixedTimeForPlug/FixedTimeForPlugPage.tsx +40 -38
- package/src/modules/fixedTimingForLight/FixedTimingForLightPage.tsx +40 -38
- package/src/modules/randomTimeForPlug/RandomTimeForPlugPage.tsx +41 -38
- package/src/modules/randomTimingForLight/RandomTimingForLightPage.tsx +40 -38
- package/src/modules/timeSchedule/TimeSchedulePage.tsx +13 -13
- package/src/navigation/Routers.ts +1 -0
package/package.json
CHANGED
|
@@ -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
|
|
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',
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
)}
|