@ledvance/group-ui-biz-bundle 1.0.71 → 1.0.73

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.
@@ -0,0 +1,517 @@
1
+ import React, { useEffect, useMemo } from 'react';
2
+ import { ScrollView, StyleSheet, View, Text, Image, TouchableOpacity } from 'react-native';
3
+ import { useNavigation } from '@react-navigation/core';
4
+ import { useRoute } from '@react-navigation/core';
5
+ import Page from '@ledvance/base/src/components/Page';
6
+ import res from '@ledvance/base/src/res';
7
+ import I18n from '@ledvance/base/src/i18n';
8
+ import { Utils } from 'tuya-panel-kit';
9
+ import Spacer from '@ledvance/base/src/components/Spacer';
10
+ import Card from '@ledvance/base/src/components/Card';
11
+ import OverView from './component/Overview';
12
+ import { useReactive, useUpdateEffect } from 'ahooks';
13
+ import { ui_biz_routerKey } from '../../navigation/Routers'
14
+ import { DpResultByMonthResData, getDpResultByMonth } from '@ledvance/base/src/models/TuyaApi';
15
+ import { useTimeZoneCity } from '@ledvance/base/src/models/modules/NativePropsSlice';
16
+ import { flattenDeep, isNumber, sumBy } from 'lodash';
17
+ import {
18
+ exchangeNumber,
19
+ exportFile,
20
+ localeNumber,
21
+ monthFormat,
22
+ monthFormatShort,
23
+ } from '@ledvance/base/src/utils/common';
24
+ import { unitDivision, useEnergyConsumption } from './EnergyConsumptionActions';
25
+ import EnergyPopup, { EnergyData } from './component/EnergyModal';
26
+ import { carbonDioxideEmission, countryAndRegion } from './co2Data';
27
+ import { EnergyConsumptionDetailProps } from './EnergyConsumptionDetail';
28
+ import { EnergyConsumptionChartProps } from './EnergyConsumptionChart';
29
+ import SegmentControl from '@ledvance/base/src/components/segmentControl';
30
+
31
+ const { convertX: cx } = Utils.RatioUtils;
32
+ const { withTheme } = Utils.ThemeUtils;
33
+
34
+ export interface EnergyConsumptionPageProps {
35
+ solarCurrentSum: number
36
+ solarPowerSum: number
37
+ wifiCurrentSum: number
38
+ wifiPowerSum: number
39
+ addEleDpCode: string;
40
+ solarPlugGroup: string[];
41
+ wifiPlugGroup: string[]
42
+ backText?: string
43
+ }
44
+
45
+ export interface OverviewItem {
46
+ key: string;
47
+ value: number | string;
48
+ headlineText: string;
49
+ chartTitle: string;
50
+ }
51
+ export type PopupType = 'co2' | 'money' | 'unit' | '';
52
+
53
+ interface EnergyConsumptionState {
54
+ solarTodayElectricity: string
55
+ solarTotalElectricity: string
56
+ wifiTodayElectricity: string
57
+ wifiTotalElectricity: string
58
+ solarOverviewList: OverviewItem[];
59
+ wifiOverviewList: OverviewItem[];
60
+ price: string;
61
+ unit: string;
62
+ showPopup: boolean;
63
+ popupType: PopupType;
64
+ co2Saved: string;
65
+ isSolarMode: boolean
66
+ loading: boolean
67
+ collapsed: boolean
68
+ }
69
+
70
+ const EnergyConsumptionPage = (props: { theme?: any }) => {
71
+ const params = useRoute().params as EnergyConsumptionPageProps;
72
+ const navigation = useNavigation();
73
+ const timeZoneCity = useTimeZoneCity();
74
+ const [energyData, setEnergyData] = useEnergyConsumption()
75
+ const state = useReactive<EnergyConsumptionState>({
76
+ solarTodayElectricity: '0',
77
+ solarTotalElectricity: '0',
78
+ wifiTodayElectricity: '0',
79
+ wifiTotalElectricity: '0',
80
+ solarOverviewList: [],
81
+ wifiOverviewList: [],
82
+ price: energyData.price,
83
+ unit: energyData.unit,
84
+ showPopup: false,
85
+ popupType: '',
86
+ co2Saved: '0',
87
+ isSolarMode: !params.wifiPlugGroup.length,
88
+ loading: true,
89
+ collapsed: true
90
+ });
91
+
92
+ useUpdateEffect(() => {
93
+ state.price = energyData.price
94
+ state.unit = energyData.unit
95
+ }, [JSON.stringify(energyData)])
96
+
97
+ const chartHeadline = useMemo(() => {
98
+ const overview = state.isSolarMode ? state.solarOverviewList : state.wifiOverviewList
99
+ const len = overview.length;
100
+ return overview?.length
101
+ ? `${overview[len - 1].key} - ${overview[0].key}`
102
+ : '';
103
+ }, [state.solarOverviewList, state.wifiOverviewList, state.isSolarMode]);
104
+
105
+ useEffect(() => {
106
+ getElectricity().then();
107
+ }, []);
108
+
109
+ useUpdateEffect(() => {
110
+ if (Number(state.solarTotalElectricity) > 0 && timeZoneCity) {
111
+ const letOut = carbonDioxideEmission(timeZoneCity, countryAndRegion) || 0;
112
+ state.co2Saved = localeNumber((letOut * Number(state.solarTotalElectricity)) / 1000, 4);
113
+ }
114
+ }, [state.solarTotalElectricity, timeZoneCity]);
115
+
116
+ const getElectricity = async () => {
117
+ const solarPromise = params.solarPlugGroup.map(devId =>
118
+ getDpResultByMonth(devId, params.addEleDpCode, 'sum').catch(error => ({ error }))
119
+ );
120
+ const wifiPromise = params.wifiPlugGroup.map(devId =>
121
+ getDpResultByMonth(devId, params.addEleDpCode, 'sum').catch(error => ({ error }))
122
+ );
123
+ state.loading = true
124
+ const solarRes = await Promise.all(solarPromise);
125
+ const wifiRes = await Promise.all(wifiPromise)
126
+ state.loading = false
127
+ // @ts-ignore
128
+ const solarGroup: DpResultByMonthResData[] = solarRes.filter(v => !v.error);
129
+ // @ts-ignore
130
+ const wifiGroup: DpResultByMonthResData[] = wifiRes.filter(v => !v.error);
131
+ // 初始化合并后的对象
132
+ const solarData = getMergeData(solarGroup)
133
+ const wifiData = getMergeData(wifiGroup)
134
+ state.solarTodayElectricity = solarData.thisDay
135
+ state.solarTotalElectricity = solarData.sum
136
+ state.solarOverviewList = flattenDeep(solarData.overviewList)
137
+ state.wifiTodayElectricity = wifiData.thisDay
138
+ state.wifiTotalElectricity = wifiData.sum
139
+ state.wifiOverviewList = flattenDeep(wifiData.overviewList)
140
+ };
141
+
142
+ const getMergeData = (monthDataGroup: DpResultByMonthResData[]) => {
143
+ const mergedData = {
144
+ sum: sumBy(monthDataGroup, item => parseFloat(item.sum)).toFixed(2) ?? 0,
145
+ thisDay: sumBy(monthDataGroup, item => parseFloat(item.thisDay)).toFixed(2) ?? 0,
146
+ years: {},
147
+ overviewList: [] as any[]
148
+ };
149
+ monthDataGroup.forEach(item => {
150
+ // 合并 years 数据
151
+ Object.keys(item.years).forEach(year => {
152
+ if (!mergedData.years[year]) {
153
+ // 如果当前年不存在,初始化该年
154
+ mergedData.years[year] = {};
155
+ }
156
+ // 遍历每一年的月份
157
+ Object.keys(item.years[year]).forEach(month => {
158
+ if (!mergedData.years[year][month]) {
159
+ // 如果当前月份不存在,初始化该月份
160
+ mergedData.years[year][month] = 0;
161
+ }
162
+ const monthNum = Number(item.years[year][month]);
163
+ // 累加月份的数据
164
+ mergedData.years[year][month] += Number(isNumber(monthNum) ? monthNum : 0);
165
+ });
166
+ });
167
+ });
168
+
169
+ const yearsList = Object.keys(mergedData.years);
170
+ yearsList.sort((a, b) => parseInt(b) - parseInt(a));
171
+ const monthList = yearsList.map(year => {
172
+ const curMonth = mergedData.years[year];
173
+ const curMonthList = Object.keys(curMonth).sort((a, b) => parseInt(b) - parseInt(a));
174
+ const monthData = curMonthList.map(month => {
175
+ return {
176
+ key: `${monthFormat(month)} ${year}`,
177
+ value: (Number(curMonth[month]) || 0).toFixed(2),
178
+ headlineText: `${year}${month}`,
179
+ chartTitle: `${monthFormatShort(month)}\n${year}`,
180
+ };
181
+ });
182
+ return monthData;
183
+ });
184
+ return {
185
+ sum: mergedData.sum,
186
+ thisDay: mergedData.thisDay,
187
+ overviewList: monthList
188
+ }
189
+ }
190
+
191
+ const updateEnergyData = (data: EnergyData) => {
192
+ setEnergyData(data).then()
193
+ state.price = data.price;
194
+ state.unit = data.unit;
195
+ };
196
+
197
+ const styles = StyleSheet.create({
198
+ showTip: {
199
+ marginHorizontal: cx(24),
200
+ },
201
+ cardContainer: {
202
+ flex: 1,
203
+ marginHorizontal: cx(24),
204
+ padding: cx(16),
205
+ },
206
+ cardTitle: {
207
+ color: props.theme.global.fontColor,
208
+ fontSize: cx(16),
209
+ fontWeight: 'bold',
210
+ },
211
+ consumedEnergyContent: {
212
+ flex: 1,
213
+ flexDirection: 'row',
214
+ },
215
+ consumptionNum: {
216
+ color: props.theme.global.secondBrand,
217
+ fontFamily: 'helvetica_neue_lt_std_bd',
218
+ },
219
+ subContent: {
220
+ flex: 1,
221
+ alignItems: 'center',
222
+ marginBottom: cx(9),
223
+ },
224
+ valueText: {
225
+ fontSize: cx(24),
226
+ fontWeight: 'bold',
227
+ color: props.theme.global.secondBrand,
228
+ },
229
+ titleText: {
230
+ fontFamily: 'helvetica_neue_lt_std_roman',
231
+ fontSize: cx(14),
232
+ color: props.theme.global.secondFontColor,
233
+ textAlign: 'center',
234
+ },
235
+ priceBg: {
236
+ height: cx(40),
237
+ width: cx(40),
238
+ borderRadius: cx(40),
239
+ backgroundColor: props.theme.global.thirdBrand,
240
+ justifyContent: 'center',
241
+ alignItems: 'center',
242
+ marginRight: cx(10),
243
+ },
244
+ priceNum: {
245
+ marginRight: cx(10),
246
+ },
247
+ priceButton: {
248
+ borderRadius: cx(5),
249
+ paddingHorizontal: cx(10),
250
+ paddingVertical: cx(8),
251
+ marginTop: cx(8),
252
+ backgroundColor: props.theme.button.active,
253
+ alignItems: 'center',
254
+ justifyContent: 'center',
255
+ },
256
+ unitItem: {
257
+ flexDirection: 'row',
258
+ justifyContent: 'space-between',
259
+ paddingHorizontal: cx(10),
260
+ alignItems: 'center',
261
+ height: cx(40),
262
+ },
263
+ });
264
+
265
+ const ConsumedEnergyItem = (props: { value: number; unit: string }) => {
266
+ return (
267
+ <View style={styles.subContent}>
268
+ <Text style={styles.valueText}>{props.value || 0}</Text>
269
+ <Spacer height={cx(4)} />
270
+ <Text style={styles.titleText}>{unitDivision(props.unit)[0]}</Text>
271
+ <Text style={styles.titleText}>{unitDivision(props.unit)[1]}</Text>
272
+ </View>
273
+ );
274
+ };
275
+
276
+ return (
277
+ <Page
278
+ style={{ position: 'relative' }}
279
+ backText={params.backText || I18n.getLang('sockets_ce')}
280
+ headlineText={I18n.getLang(
281
+ state.isSolarMode
282
+ ? 'sockets_headline_power'
283
+ : 'consumption_data_annual_bar_chart_system_back_text'
284
+ )}
285
+ headlineIcon={(state.isSolarMode ? state.solarOverviewList : state.wifiOverviewList).length ? res.download_icon : undefined}
286
+ onHeadlineIconClick={() => {
287
+ exportFile(state.isSolarMode ? state.solarOverviewList : state.wifiOverviewList);
288
+ }}
289
+ showGreenery={state.isSolarMode}
290
+ greeneryIcon={res.energy_consumption_greenery}
291
+ headlineTopContent={
292
+ (params.solarPlugGroup.length && params.wifiPlugGroup.length) ?
293
+ <View style={{ marginBottom: cx(5) }}>
294
+ <SegmentControl
295
+ title1={I18n.getLang('consumption_data_annual_bar_chart_system_back_text')}
296
+ title2={I18n.getLang('sockets_headline_power')}
297
+ isFirst={!state.isSolarMode}
298
+ tabStyle={{paddingHorizontal: cx(15)}}
299
+ setIsFirst={(v) => {
300
+ state.isSolarMode = !v
301
+ }} />
302
+ </View> : undefined
303
+ }
304
+ loading={state.loading}
305
+ >
306
+ <ScrollView nestedScrollEnabled={true}>
307
+ <View>
308
+ {/* tip */}
309
+ <Spacer height={cx(15)} />
310
+ <View style={styles.showTip}>
311
+ <Text style={{ fontSize: cx(14) }}>
312
+ {I18n.getLang(
313
+ state.isSolarMode
314
+ ? 'generation_data_description_text'
315
+ : 'consumption_data_description_text'
316
+ )}
317
+ </Text>
318
+ </View>
319
+ <Spacer />
320
+ {/* Today */}
321
+ <Card style={styles.cardContainer}>
322
+ <Text style={styles.cardTitle}>
323
+ {I18n.getLang('consumption_data_field1_headline_text')}
324
+ </Text>
325
+ <Spacer height={cx(15)} />
326
+ <View style={{ flexDirection: 'row', alignItems: 'flex-end' }}>
327
+ <Text style={[styles.consumptionNum, { fontSize: cx(38), marginRight: cx(8) }]}>
328
+ {localeNumber(state.isSolarMode ? state.solarTodayElectricity : state.wifiTodayElectricity)}
329
+ </Text>
330
+ <Text style={[styles.consumptionNum, { fontSize: cx(22), marginBottom: cx(4) }]}>
331
+ kWh
332
+ </Text>
333
+ </View>
334
+ <Spacer height={cx(10)} />
335
+ </Card>
336
+ <Spacer />
337
+ <Card style={styles.cardContainer}>
338
+ <Text style={styles.cardTitle}>
339
+ {I18n.getLang('consumption_data_field2_headline_text')}
340
+ </Text>
341
+ <Spacer height={cx(15)} />
342
+ <View style={styles.consumedEnergyContent}>
343
+ <ConsumedEnergyItem
344
+ value={state.isSolarMode ? params.solarPowerSum : params.wifiPowerSum}
345
+ unit={I18n.getLang('consumption_data_field2_value_text1')}
346
+ />
347
+ <ConsumedEnergyItem
348
+ value={state.isSolarMode ? params.solarCurrentSum : params.wifiCurrentSum}
349
+ unit={I18n.getLang('consumption_data_field2_value_text2')}
350
+ />
351
+ </View>
352
+ </Card>
353
+ <Spacer />
354
+ {/* 365 day */}
355
+ <Card style={styles.cardContainer}>
356
+ <Text style={styles.cardTitle}>
357
+ {I18n.getLang('consumption_data_field3_headline_text')}
358
+ </Text>
359
+ <Spacer height={cx(15)} />
360
+ <View style={{ flexDirection: 'row', alignItems: 'flex-end' }}>
361
+ <Text style={[styles.consumptionNum, { fontSize: cx(38), marginRight: cx(8) }]}>
362
+ {localeNumber(state.isSolarMode ? state.solarTotalElectricity : state.wifiTotalElectricity)}
363
+ </Text>
364
+ <Text style={[styles.consumptionNum, { fontSize: cx(22), marginBottom: cx(4) }]}>
365
+ kWh
366
+ </Text>
367
+ </View>
368
+ <Spacer />
369
+ {/* CO2 */}
370
+ {state.isSolarMode && (
371
+ <>
372
+ <View style={{ flexDirection: 'row', alignItems: 'center' }}>
373
+ <View style={styles.priceBg}>
374
+ <Image
375
+ source={res.energy_consumption_cash}
376
+ resizeMode="contain"
377
+ style={{
378
+ height: cx(20),
379
+ width: cx(20),
380
+ tintColor: props.theme.button.active,
381
+ }}
382
+ />
383
+ </View>
384
+ <View style={styles.priceNum}>
385
+ <View style={{ flexDirection: 'row' }}>
386
+ <Text
387
+ style={{ color: props.theme.global.secondFontColor, marginRight: cx(5) }}
388
+ >
389
+ {I18n.getLang('consumption_data_field3_co2_topic_text')}
390
+ </Text>
391
+ <TouchableOpacity
392
+ onPress={() => {
393
+ state.showPopup = true;
394
+ state.popupType = 'co2';
395
+ }}
396
+ >
397
+ <Image
398
+ source={res.co2Icon}
399
+ resizeMode="contain"
400
+ style={{
401
+ height: cx(20),
402
+ width: cx(20),
403
+ tintColor: props.theme.button.active,
404
+ }}
405
+ />
406
+ </TouchableOpacity>
407
+ </View>
408
+ <Text
409
+ style={{ color: props.theme.global.fontColor, fontWeight: 'bold' }}
410
+ >{`${state.co2Saved} kg`}</Text>
411
+ </View>
412
+ </View>
413
+ <Spacer height={cx(10)} />
414
+ </>
415
+ )}
416
+ {/* money */}
417
+ <View style={{ flexDirection: 'row', alignItems: 'center' }}>
418
+ <View style={styles.priceBg}>
419
+ <Image
420
+ source={res.energy_consumption_cash}
421
+ resizeMode="contain"
422
+ style={{ height: cx(20), width: cx(20), tintColor: props.theme.button.active }}
423
+ />
424
+ </View>
425
+ <View>
426
+ <View style={styles.priceNum}>
427
+ <Text style={{ color: props.theme.global.secondFontColor }}>
428
+ {I18n.getLang(
429
+ state.isSolarMode
430
+ ? 'consumption_data_monthly_overview_field1_text2'
431
+ : 'consumption_data_field3_value_text2'
432
+ )}
433
+ </Text>
434
+ <Text style={{ color: props.theme.global.fontColor, fontWeight: 'bold' }}>
435
+ {state.price
436
+ ? `${localeNumber(Number(state.price) * Number(state.isSolarMode ? state.solarTotalElectricity : state.wifiTotalElectricity), 2)} ${state.unit
437
+ }`
438
+ : '-'}
439
+ </Text>
440
+ </View>
441
+ <TouchableOpacity
442
+ onPress={() => {
443
+ state.showPopup = true;
444
+ state.popupType = 'money';
445
+ }}
446
+ >
447
+ <View style={styles.priceButton}>
448
+ <Text style={{ color: props.theme.button.fontColor }}>
449
+ {I18n.getLang('consumption_data_field3_button_text')}
450
+ </Text>
451
+ </View>
452
+ </TouchableOpacity>
453
+ </View>
454
+ </View>
455
+ </Card>
456
+ <Spacer height={cx(30)} />
457
+ {/* Annual overview */}
458
+ <OverView
459
+ style={{ marginHorizontal: cx(24) }}
460
+ headlineText={I18n.getLang('consumption_data_field4_headline_text')}
461
+ headlineClick={() => {
462
+ navigation.navigate(ui_biz_routerKey.group_ui_biz_energy_consumption_chart, {
463
+ headlineText: chartHeadline,
464
+ chartData: state.isSolarMode ? state.solarOverviewList : state.wifiOverviewList
465
+ } as EnergyConsumptionChartProps)
466
+ }}
467
+ overviewItemClick={(item) => {
468
+ navigation.navigate(ui_biz_routerKey.group_ui_biz_energy_consumption_detail, {
469
+ addEleDpCode: params.addEleDpCode,
470
+ curMonth: item,
471
+ price: state.price,
472
+ unit: state.unit,
473
+ isSolarMode: state.isSolarMode,
474
+ deviceIdGroup: state.isSolarMode ? params.solarPlugGroup : params.wifiPlugGroup,
475
+ updateEnergyData
476
+ } as EnergyConsumptionDetailProps)
477
+ }}
478
+ overViewList={state.isSolarMode ? state.solarOverviewList : state.wifiOverviewList}
479
+ />
480
+ {/* modal */}
481
+ <EnergyPopup
482
+ visible={!!(state.popupType && state.showPopup)}
483
+ popupType={state.popupType || 'co2'}
484
+ title={
485
+ state.popupType === 'co2'
486
+ ? I18n.getLang('consumption_data_field3_co2_topic_text')
487
+ : ''
488
+ }
489
+ cancelText={state.popupType === 'co2' ? '' : I18n.getLang('auto_scan_system_cancel')}
490
+ confirmText={I18n.getLang(
491
+ state.popupType === 'co2'
492
+ ? 'home_screen_home_dialog_yes_con'
493
+ : 'auto_scan_system_wifi_confirm'
494
+ )}
495
+ energyData={{ price: state.price, unit: state.unit }}
496
+ onConfirm={energyData => {
497
+ state.popupType = '';
498
+ state.showPopup = false;
499
+ if (energyData) {
500
+ updateEnergyData({
501
+ ...energyData,
502
+ price: exchangeNumber(energyData.price),
503
+ });
504
+ }
505
+ }}
506
+ onCancel={() => {
507
+ state.popupType = '';
508
+ state.showPopup = false;
509
+ }}
510
+ />
511
+ </View>
512
+ </ScrollView>
513
+ </Page>
514
+ );
515
+ };
516
+
517
+ export default withTheme(EnergyConsumptionPage);
@@ -0,0 +1,34 @@
1
+ import {NavigationRoute} from "tuya-panel-kit";
2
+ import EnergyConsumptionPage from "./EnergyConsumptionPage";
3
+ import EnergyConsumptionDetail from "./EnergyConsumptionDetail";
4
+ import EnergyConsumptionChart from "./EnergyConsumptionChart";
5
+ import {ui_biz_routerKey} from "../../navigation/Routers";
6
+
7
+ const EnergyConsumptionPageRouters: NavigationRoute[] = [
8
+ {
9
+ name: ui_biz_routerKey.group_ui_biz_energy_consumption,
10
+ component: EnergyConsumptionPage,
11
+ options:{
12
+ hideTopbar: true,
13
+ showOfflineView: false,
14
+ }
15
+ },
16
+ {
17
+ name: ui_biz_routerKey.group_ui_biz_energy_consumption_detail,
18
+ component: EnergyConsumptionDetail,
19
+ options:{
20
+ hideTopbar: true,
21
+ showOfflineView: false,
22
+ }
23
+ },
24
+ {
25
+ name: ui_biz_routerKey.group_ui_biz_energy_consumption_chart,
26
+ component: EnergyConsumptionChart,
27
+ options:{
28
+ hideTopbar: true,
29
+ showOfflineView: false,
30
+ }
31
+ },
32
+ ]
33
+
34
+ export default EnergyConsumptionPageRouters