@ledvance/group-ui-biz-bundle 1.0.159 → 1.0.161
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 +153 -20
- 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/modules/timer/TimerPage.tsx +1 -1
- 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
|
)}
|
|
@@ -27,14 +27,20 @@ import {
|
|
|
27
27
|
monthFormat,
|
|
28
28
|
monthFormatShort,
|
|
29
29
|
} from '@ledvance/base/src/utils/common';
|
|
30
|
-
import {
|
|
31
|
-
|
|
30
|
+
import {
|
|
31
|
+
getEnergyGenerationValue,
|
|
32
|
+
unitDivision,
|
|
33
|
+
useDynamicPrice,
|
|
34
|
+
useEnergyConsumption
|
|
35
|
+
} from './EnergyConsumptionActions';
|
|
36
|
+
import EnergyPopup, {EnergyData, EnergyType, calculateAveragePrice, PriceSegment, DynamicEnergyData} from './component/EnergyModal';
|
|
32
37
|
import { carbonDioxideEmission, countryAndRegion } from './co2Data';
|
|
33
38
|
import { EnergyConsumptionDetailProps } from './EnergyConsumptionDetail';
|
|
34
39
|
import { EnergyConsumptionChartProps } from './EnergyConsumptionChart';
|
|
35
40
|
import SegmentControl from '@ledvance/base/src/components/segmentControl';
|
|
36
41
|
import ThemeType from '@ledvance/base/src/config/themeType'
|
|
37
42
|
import { exportEnergyCsv } from "./EnergyConsumptionActions";
|
|
43
|
+
import { SetSegmentedPricesParams } from './SetSegmentedPricesScreen';
|
|
38
44
|
|
|
39
45
|
const { convertX: cx } = Utils.RatioUtils;
|
|
40
46
|
const { withTheme } = Utils.ThemeUtils;
|
|
@@ -71,6 +77,7 @@ interface EnergyConsumptionState {
|
|
|
71
77
|
popupType: PopupType;
|
|
72
78
|
co2Saved: string;
|
|
73
79
|
isSolarMode: boolean
|
|
80
|
+
pricingType: EnergyType,
|
|
74
81
|
loading: boolean
|
|
75
82
|
collapsed: boolean
|
|
76
83
|
}
|
|
@@ -80,6 +87,36 @@ const EnergyConsumptionPage = (props: { theme?: ThemeType }) => {
|
|
|
80
87
|
const navigation = useNavigation();
|
|
81
88
|
const timeZoneCity = useTimeZoneCity();
|
|
82
89
|
const [energyData, setEnergyData] = useEnergyConsumption()
|
|
90
|
+
const [dynamicData, setDynamicData] = useDynamicPrice()
|
|
91
|
+
|
|
92
|
+
const parsePrice = (dynamicCosts: PriceSegment[], dynamicGenerates: PriceSegment[], fixedModel?: any): string => {
|
|
93
|
+
const type = fixedModel?.energyType ?? 'fixed'
|
|
94
|
+
const usingPrice = state.isSolarMode ? (fixedModel?.generatePrice ?? fixedModel?.price) : fixedModel?.price;
|
|
95
|
+
if (type === 'dynamic') {
|
|
96
|
+
const usingIntervals = state.isSolarMode ? dynamicGenerates : dynamicCosts;
|
|
97
|
+
const dynamicPrice = usingIntervals && usingIntervals.length > 0
|
|
98
|
+
? String(calculateAveragePrice(usingIntervals))
|
|
99
|
+
: usingPrice;
|
|
100
|
+
return String(dynamicPrice ?? '0');
|
|
101
|
+
} else {
|
|
102
|
+
return String(usingPrice ?? '0');
|
|
103
|
+
}
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
const initialPrice = useMemo(() => {
|
|
107
|
+
const type = energyData.energyType ?? 'fixed'
|
|
108
|
+
const usingPrice = !params.wifiPlugGroup.length ? (energyData.generatePrice ?? energyData.price) : energyData.price;
|
|
109
|
+
if (type === 'dynamic') {
|
|
110
|
+
const usingIntervals = !params.wifiPlugGroup.length ? (dynamicData?.generatePrices || []) : (dynamicData?.intervalPrices || []);
|
|
111
|
+
const dynamicPrice = usingIntervals && usingIntervals.length > 0
|
|
112
|
+
? String(calculateAveragePrice(usingIntervals))
|
|
113
|
+
: usingPrice;
|
|
114
|
+
return String(dynamicPrice ?? '0');
|
|
115
|
+
} else {
|
|
116
|
+
return String(usingPrice ?? '0');
|
|
117
|
+
}
|
|
118
|
+
}, [energyData, dynamicData, params.wifiPlugGroup.length])
|
|
119
|
+
|
|
83
120
|
const state = useReactive<EnergyConsumptionState>({
|
|
84
121
|
solarTodayElectricity: '0',
|
|
85
122
|
solarTotalElectricity: '0',
|
|
@@ -87,8 +124,9 @@ const EnergyConsumptionPage = (props: { theme?: ThemeType }) => {
|
|
|
87
124
|
wifiTotalElectricity: '0',
|
|
88
125
|
solarOverviewList: [],
|
|
89
126
|
wifiOverviewList: [],
|
|
90
|
-
price:
|
|
127
|
+
price: initialPrice,
|
|
91
128
|
unit: energyData.unit,
|
|
129
|
+
pricingType: energyData.energyType ?? "fixed",
|
|
92
130
|
showPopup: false,
|
|
93
131
|
popupType: '',
|
|
94
132
|
co2Saved: '0',
|
|
@@ -98,9 +136,11 @@ const EnergyConsumptionPage = (props: { theme?: ThemeType }) => {
|
|
|
98
136
|
});
|
|
99
137
|
|
|
100
138
|
useUpdateEffect(() => {
|
|
101
|
-
state.
|
|
139
|
+
state.pricingType = energyData.energyType ?? 'fixed'
|
|
140
|
+
state.price = parsePrice(dynamicData?.intervalPrices || [], dynamicData?.generatePrices || [], energyData)
|
|
102
141
|
state.unit = energyData.unit
|
|
103
|
-
|
|
142
|
+
actions.setPriceAndUnit(state.price, state.unit)
|
|
143
|
+
}, [JSON.stringify(energyData), JSON.stringify(dynamicData), state.isSolarMode])
|
|
104
144
|
|
|
105
145
|
const chartHeadline = useMemo(() => {
|
|
106
146
|
const overview = state.isSolarMode ? state.solarOverviewList : state.wifiOverviewList
|
|
@@ -123,8 +163,10 @@ const EnergyConsumptionPage = (props: { theme?: ThemeType }) => {
|
|
|
123
163
|
displayMode: state.isSolarMode ? 'generation' : 'consumption',
|
|
124
164
|
consumptionDeviceIds: params.wifiPlugGroup,
|
|
125
165
|
generationDeviceIds: params.solarPlugGroup,
|
|
166
|
+
energyType: state.pricingType,
|
|
167
|
+
dynamicData: dynamicData,
|
|
126
168
|
}
|
|
127
|
-
}, [backTitle, chartHeadline, state.price, state.unit, params.addEleDpCode, state.isSolarMode, params.wifiPlugGroup, params.solarPlugGroup])
|
|
169
|
+
}, [backTitle, chartHeadline, state.price, state.unit, params.addEleDpCode, state.isSolarMode, params.wifiPlugGroup, params.solarPlugGroup, state.pricingType, dynamicData])
|
|
128
170
|
|
|
129
171
|
const { state: chartState, actions } = useEnergyData(chartParams);
|
|
130
172
|
const chartStyles = useMemo(() => getStyles(props.theme), [props.theme]);
|
|
@@ -226,10 +268,18 @@ const EnergyConsumptionPage = (props: { theme?: ThemeType }) => {
|
|
|
226
268
|
}
|
|
227
269
|
|
|
228
270
|
const updateEnergyData = (data: EnergyData) => {
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
271
|
+
const internationalPrice = exchangeNumber(data.price)
|
|
272
|
+
const newEnergyData = {
|
|
273
|
+
...energyData,
|
|
274
|
+
unit: data.unit,
|
|
275
|
+
energyType: 'fixed',
|
|
276
|
+
[state.isSolarMode ? 'generatePrice' : 'price']: internationalPrice,
|
|
277
|
+
}
|
|
278
|
+
setEnergyData(newEnergyData).then()
|
|
279
|
+
state.price = internationalPrice || '0'
|
|
280
|
+
state.unit = data.unit
|
|
281
|
+
state.pricingType = 'fixed'
|
|
282
|
+
actions.setPriceAndUnit(state.price, data.unit)
|
|
233
283
|
};
|
|
234
284
|
|
|
235
285
|
const backTitle = useMemo(() => {
|
|
@@ -336,6 +386,7 @@ const EnergyConsumptionPage = (props: { theme?: ThemeType }) => {
|
|
|
336
386
|
(params.solarPlugGroup.length && params.wifiPlugGroup.length) ?
|
|
337
387
|
<View style={{ marginBottom: cx(5) }}>
|
|
338
388
|
<SegmentControl
|
|
389
|
+
marginHorizontal={cx(0)}
|
|
339
390
|
title1={I18n.getLang('consumption_data_annual_bar_chart_system_back_text')}
|
|
340
391
|
title2={I18n.getLang('sockets_headline_power')}
|
|
341
392
|
isFirst={!state.isSolarMode}
|
|
@@ -404,6 +455,41 @@ const EnergyConsumptionPage = (props: { theme?: ThemeType }) => {
|
|
|
404
455
|
</View>
|
|
405
456
|
</Card>
|
|
406
457
|
<Spacer />
|
|
458
|
+
{/* pricing radio button*/}
|
|
459
|
+
<View style={{ marginBottom: cx(5) }}>
|
|
460
|
+
<SegmentControl
|
|
461
|
+
marginHorizontal={cx(0)}
|
|
462
|
+
title1={I18n.getLang('consumption_energy_type_fixed')}
|
|
463
|
+
title2={I18n.getLang('consumption_energy_type_dynamic')}
|
|
464
|
+
isFirst={state.pricingType === 'fixed'}
|
|
465
|
+
tabStyle={{paddingHorizontal: cx(15)}}
|
|
466
|
+
setIsFirst={(v) => {
|
|
467
|
+
const value = v ? 'fixed' : 'dynamic'
|
|
468
|
+
const fixedModel = energyData ?? {
|
|
469
|
+
price: '0',
|
|
470
|
+
unit: '$',
|
|
471
|
+
energyType: 'fixed',
|
|
472
|
+
generatePrice: '0',
|
|
473
|
+
}
|
|
474
|
+
state.loading = true
|
|
475
|
+
setEnergyData({
|
|
476
|
+
...fixedModel,
|
|
477
|
+
energyType: value,
|
|
478
|
+
}).then((res) => {
|
|
479
|
+
state.loading = false
|
|
480
|
+
if (res && res.success) {
|
|
481
|
+
state.pricingType = value
|
|
482
|
+
if (value === 'fixed') {
|
|
483
|
+
state.price = state.isSolarMode ? (fixedModel.generatePrice || fixedModel.price) : fixedModel.price
|
|
484
|
+
} else {
|
|
485
|
+
const segments = state.isSolarMode ? (dynamicData?.generatePrices || []) : (dynamicData?.intervalPrices || [])
|
|
486
|
+
state.price = String(calculateAveragePrice(segments))
|
|
487
|
+
}
|
|
488
|
+
actions.setPriceAndUnit(state.price, state.unit)
|
|
489
|
+
}
|
|
490
|
+
})
|
|
491
|
+
}} />
|
|
492
|
+
</View>
|
|
407
493
|
{/* 365 day */}
|
|
408
494
|
<Card style={styles.cardContainer}>
|
|
409
495
|
<View style={{ flexDirection: 'row', alignItems: 'center' }}>
|
|
@@ -503,11 +589,11 @@ const EnergyConsumptionPage = (props: { theme?: ThemeType }) => {
|
|
|
503
589
|
: '-'}
|
|
504
590
|
</Text>
|
|
505
591
|
</View>
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
592
|
+
{state.pricingType === 'fixed' &&<TouchableOpacity
|
|
593
|
+
onPress={() => {
|
|
594
|
+
state.showPopup = true;
|
|
595
|
+
state.popupType = 'money';
|
|
596
|
+
}}
|
|
511
597
|
>
|
|
512
598
|
<View style={styles.priceButton}>
|
|
513
599
|
<Text style={{ color: props.theme?.button.fontColor }}>
|
|
@@ -515,6 +601,56 @@ const EnergyConsumptionPage = (props: { theme?: ThemeType }) => {
|
|
|
515
601
|
</Text>
|
|
516
602
|
</View>
|
|
517
603
|
</TouchableOpacity>
|
|
604
|
+
}
|
|
605
|
+
{state.pricingType === 'dynamic' && (
|
|
606
|
+
<TouchableOpacity
|
|
607
|
+
onPress={() => {
|
|
608
|
+
navigation.navigate(ui_biz_routerKey.group_ui_biz_set_segmented_prices, {
|
|
609
|
+
backTitle: backTitle,
|
|
610
|
+
dynamicData: dynamicData ?? {
|
|
611
|
+
intervalPrices: [],
|
|
612
|
+
generatePrices: [],
|
|
613
|
+
},
|
|
614
|
+
priceUnit: state.unit,
|
|
615
|
+
isGenerate: state.isSolarMode,
|
|
616
|
+
onSetPriceSements: async (segments: PriceSegment[], currency: string): Promise<boolean> => {
|
|
617
|
+
console.log("onSetPriceSements ", JSON.stringify(segments), currency)
|
|
618
|
+
const fixedModel = energyData ?? {
|
|
619
|
+
price: '0',
|
|
620
|
+
unit: '$',
|
|
621
|
+
energyType: 'fixed',
|
|
622
|
+
generatePrice: '0',
|
|
623
|
+
}
|
|
624
|
+
const dynamicModel = {
|
|
625
|
+
...dynamicData,
|
|
626
|
+
[state.isSolarMode ? 'generatePrices' : 'intervalPrices']: segments
|
|
627
|
+
}
|
|
628
|
+
const [fixedRes, dynamicRes] = await Promise.all([
|
|
629
|
+
setEnergyData({ ...fixedModel, energyType: 'dynamic', unit: currency }),
|
|
630
|
+
setDynamicData(dynamicModel)
|
|
631
|
+
])
|
|
632
|
+
const isDynamicSuccess = dynamicRes && dynamicRes.success
|
|
633
|
+
const isRegularSuccess = fixedRes && fixedRes.success
|
|
634
|
+
const isAllSuccess = isDynamicSuccess && isRegularSuccess
|
|
635
|
+
if (isAllSuccess) {
|
|
636
|
+
state.price = String(calculateAveragePrice(segments)) // 价格
|
|
637
|
+
state.pricingType = 'dynamic'
|
|
638
|
+
state.unit = currency
|
|
639
|
+
actions.setPriceAndUnit(state.price, currency)
|
|
640
|
+
}
|
|
641
|
+
return !!isAllSuccess
|
|
642
|
+
}
|
|
643
|
+
} as SetSegmentedPricesParams)
|
|
644
|
+
}}
|
|
645
|
+
>
|
|
646
|
+
<View style={styles.priceButton}>
|
|
647
|
+
<Text style={{ color: props.theme?.button.fontColor }}>
|
|
648
|
+
{I18n.getLang('consumption_data_field_dynamic_money')}
|
|
649
|
+
</Text>
|
|
650
|
+
</View>
|
|
651
|
+
</TouchableOpacity>
|
|
652
|
+
)}
|
|
653
|
+
|
|
518
654
|
</View>
|
|
519
655
|
</View>
|
|
520
656
|
</Card>
|
|
@@ -581,15 +717,12 @@ const EnergyConsumptionPage = (props: { theme?: ThemeType }) => {
|
|
|
581
717
|
? 'home_screen_home_dialog_yes_con'
|
|
582
718
|
: 'auto_scan_system_wifi_confirm'
|
|
583
719
|
)}
|
|
584
|
-
energyData={{ price: state.price, unit: state.unit }}
|
|
720
|
+
energyData={{ price: state.popupType === 'money' ? (state.isSolarMode ? (energyData?.generatePrice ?? '0') : (energyData?.price ?? '0')) : state.price, unit: state.unit }}
|
|
585
721
|
onConfirm={energyData => {
|
|
586
722
|
state.popupType = '';
|
|
587
723
|
state.showPopup = false;
|
|
588
724
|
if (energyData) {
|
|
589
|
-
updateEnergyData(
|
|
590
|
-
...energyData,
|
|
591
|
-
price: exchangeNumber(energyData.price),
|
|
592
|
-
});
|
|
725
|
+
updateEnergyData(energyData);
|
|
593
726
|
}
|
|
594
727
|
}}
|
|
595
728
|
onCancel={() => {
|
|
@@ -2,6 +2,7 @@ import {NavigationRoute} from "tuya-panel-kit";
|
|
|
2
2
|
import EnergyConsumptionPage from "./EnergyConsumptionPage";
|
|
3
3
|
import EnergyConsumptionDetail from "./EnergyConsumptionDetail";
|
|
4
4
|
import EnergyConsumptionChart from "./EnergyConsumptionChart/index";
|
|
5
|
+
import SetSegmentedPricesScreen from "./SetSegmentedPricesScreen";
|
|
5
6
|
import {ui_biz_routerKey} from "../../navigation/Routers";
|
|
6
7
|
|
|
7
8
|
const EnergyConsumptionPageRouters: NavigationRoute[] = [
|
|
@@ -29,6 +30,14 @@ const EnergyConsumptionPageRouters: NavigationRoute[] = [
|
|
|
29
30
|
showOfflineView: false,
|
|
30
31
|
}
|
|
31
32
|
},
|
|
33
|
+
{
|
|
34
|
+
name: ui_biz_routerKey.group_ui_biz_set_segmented_prices,
|
|
35
|
+
component: SetSegmentedPricesScreen,
|
|
36
|
+
options:{
|
|
37
|
+
hideTopbar: true,
|
|
38
|
+
showOfflineView: false,
|
|
39
|
+
}
|
|
40
|
+
},
|
|
32
41
|
]
|
|
33
42
|
|
|
34
43
|
export default EnergyConsumptionPageRouters
|