@ledvance/group-ui-biz-bundle 1.0.72 → 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.
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.72",
7
+ "version": "1.0.73",
8
8
  "scripts": {},
9
9
  "dependencies": {
10
10
  "@ledvance/base": "^1.x",
@@ -0,0 +1,18 @@
1
+ import { useFeatureHook } from "@ledvance/base/src/models/modules/NativePropsSlice";
2
+ import {EnergyData, UnitList} from "./component/EnergyModal";
3
+
4
+ interface LightConfig {
5
+ energyConsumption?: EnergyData
6
+ }
7
+
8
+ export const useEnergyConsumption = () =>{
9
+ return useFeatureHook<LightConfig, EnergyData>('energyConsumption', {unit: UnitList[0], price: ''})
10
+ }
11
+
12
+ export const unitDivision = (str: string) => {
13
+ if (!str) { return ['', ''] }
14
+ const strIndex = str.indexOf('(' || '(')
15
+ const unit = str.substring(strIndex)
16
+ const name = str.split(unit)[0]
17
+ return [name, unit]
18
+ }
@@ -0,0 +1,82 @@
1
+ import React from "react";
2
+ import { Platform, View, StyleSheet, Image } from "react-native";
3
+ import { useRoute } from '@react-navigation/core'
4
+ import Page from "@ledvance/base/src/components/Page";
5
+ import res from "@ledvance/base/src/res";
6
+ import I18n from "@ledvance/base/src/i18n";
7
+ import { Utils } from "tuya-panel-kit";
8
+ import BarChart from "./component/BarChart";
9
+ import { exportFile } from "@ledvance/base/src/utils/common";
10
+ import { OverviewItem } from "./EnergyConsumptionPage";
11
+ import Spacer from "@ledvance/base/src/components/Spacer";
12
+ import InfoText from "@ledvance/base/src/components/InfoText";
13
+
14
+ const { convertX: cx, height, width } = Utils.RatioUtils
15
+
16
+ export interface EnergyConsumptionChartProps {
17
+ addEleDpCode: string
18
+ headlineText: string
19
+ chartData: OverviewItem[],
20
+ over365Days?: boolean
21
+ over7Days?: boolean
22
+ }
23
+
24
+ const EnergyConsumptionChart = () => {
25
+ const props = useRoute().params as EnergyConsumptionChartProps
26
+ const computeNum = Platform.OS === 'ios' && 180 || 130
27
+ return (
28
+ <Page
29
+ backText={I18n.getLang('consumption_data_annual_bar_chart_system_back_text')}
30
+ headlineText={props.headlineText}
31
+ headlineIcon={props.chartData?.length ? res.download_icon : undefined}
32
+ onHeadlineIconClick={() => {
33
+ exportFile(props.chartData)
34
+ }}
35
+ showGreenery={false}
36
+ greeneryIcon={res.energy_consumption_greenery}
37
+ >
38
+ <View style={{ marginHorizontal: cx(24) }}>
39
+ {
40
+ props.over7Days ? (
41
+ <View style={styles.listEmptyView}>
42
+ <Spacer height={cx(26)} />
43
+ <Image
44
+ style={styles.listEmptyImage}
45
+ source={{ uri: res.ldv_timer_empty }} />
46
+ <Spacer height={cx(14)} />
47
+ <InfoText
48
+ text={I18n.getLang('energyconsumption_hourlylimit')}
49
+ icon={res.ic_info}
50
+ contentColor='#000'
51
+ />
52
+ </View>
53
+ ) : (
54
+ <BarChart height={height - cx(computeNum)} data={props.chartData} width={width - cx(80)} />
55
+ )
56
+ }
57
+ </View>
58
+ </Page>
59
+ )
60
+ }
61
+
62
+ const styles = StyleSheet.create({
63
+ listEmptyView: {
64
+ alignItems: 'center',
65
+ },
66
+ listEmptyImage: {
67
+ width: cx(200),
68
+ height: cx(200),
69
+ },
70
+ listEmptyTextIcon: {
71
+ width: cx(16),
72
+ height: cx(16),
73
+ tintColor: '#000',
74
+ },
75
+ listEmptyText: {
76
+ color: '#000',
77
+ fontSize: cx(12),
78
+ fontFamily: 'helvetica_neue_lt_std_roman',
79
+ },
80
+ });
81
+
82
+ export default EnergyConsumptionChart
@@ -0,0 +1,350 @@
1
+ import React, { useEffect } from "react";
2
+ import { Image, ScrollView, StyleSheet, Text, TouchableOpacity, View } from "react-native";
3
+ import { useNavigation, useRoute } from '@react-navigation/core'
4
+ import Page from "@ledvance/base/src/components/Page";
5
+ import res from "@ledvance/base/src/res";
6
+ import I18n from "@ledvance/base/src/i18n";
7
+ import { Utils } from "tuya-panel-kit";
8
+ import Spacer from "@ledvance/base/src/components/Spacer";
9
+ import Card from "@ledvance/base/src/components/Card";
10
+ import OverView from "./component/Overview";
11
+ import { useReactive, useUpdateEffect } from "ahooks";
12
+ import { DpResultByDataWithSpecifiedResData, getDataWithSpecified, getDpResultByHour } from "@ledvance/base/src/models/TuyaApi";
13
+ import { useTimeZoneCity } from "@ledvance/base/src/models/modules/NativePropsSlice";
14
+ import { cloneDeep, isNumber } from "lodash";
15
+ import { OverviewItem, PopupType } from "./EnergyConsumptionPage";
16
+ import { exchangeNumber, exportFile, localeNumber, loopsText } from '@ledvance/base/src/utils/common'
17
+ import dayjs from "dayjs";
18
+ import { ui_biz_routerKey } from '../../navigation/Routers'
19
+ import { EnergyConsumptionChartProps } from "./EnergyConsumptionChart";
20
+ import { overDays } from "@ledvance/base/src/utils";
21
+ import EnergyPopup, { EnergyData } from "./component/EnergyModal";
22
+ import { carbonDioxideEmission, countryAndRegion } from "./co2Data";
23
+
24
+ const { convertX: cx } = Utils.RatioUtils
25
+ const { withTheme } = Utils.ThemeUtils
26
+
27
+ export interface EnergyConsumptionDetailProps {
28
+ addEleDpCode: string
29
+ curMonth: OverviewItem
30
+ price: string
31
+ unit: string
32
+ deviceIdGroup: string[];
33
+ isSolarMode: boolean
34
+ updateEnergyData: (data: EnergyData) => {}
35
+ }
36
+
37
+ const EnergyConsumptionDetail = (props: { theme?: any }) => {
38
+ const params = useRoute().params as EnergyConsumptionDetailProps
39
+ const navigation = useNavigation()
40
+ const timeZoneCity = useTimeZoneCity()
41
+ const state = useReactive({
42
+ todayElectricity: '0',
43
+ totalElectricity: '0',
44
+ co2Saved: '0',
45
+ price: '',
46
+ unit: '',
47
+ overviewList: [] as OverviewItem[],
48
+ loading: false,
49
+ over365Days: false,
50
+ over7Days: false,
51
+ showPopup: false,
52
+ popupType: '' as PopupType,
53
+ })
54
+
55
+ useEffect(() => {
56
+ state.price = params.price
57
+ state.unit = params.unit
58
+ getElectricity().then()
59
+ }, [])
60
+
61
+ useUpdateEffect(() => {
62
+ if (Number(state.totalElectricity) > 0 && timeZoneCity) {
63
+ const letOut = carbonDioxideEmission(timeZoneCity, countryAndRegion) || 0
64
+ state.co2Saved = localeNumber((letOut * Number(state.totalElectricity)) / 1000, 4)
65
+ }
66
+ }, [state.totalElectricity, timeZoneCity])
67
+
68
+ const getElectricity = async () => {
69
+ const startDay = params.curMonth.headlineText + '01'
70
+ const endDay = params.curMonth.headlineText + dayjs(params.curMonth.headlineText + '01').daysInMonth()
71
+ state.over365Days = overDays(startDay, 365)
72
+ if (state.over365Days) {
73
+ state.overviewList = []
74
+ return
75
+ }
76
+ const promiseGroup = params.deviceIdGroup.map(devId => getDataWithSpecified(devId, params.addEleDpCode, startDay, endDay, 'sum').catch(error => ({ error })))
77
+ state.loading = true
78
+ const res = await Promise.all(promiseGroup);
79
+ state.loading = false
80
+ // @ts-ignore
81
+ const successGroup: DpResultByDataWithSpecifiedResData[] = res.filter(v => !v.error);
82
+ const mergedData = {}
83
+ successGroup.forEach(item => {
84
+ Object.keys(item.result).forEach(day => {
85
+ if (mergedData[day] === undefined) {
86
+ mergedData[day] = 0
87
+ }
88
+ const dayNum = Number(item.result[day])
89
+ mergedData[day] += Number(isNumber(dayNum) ? dayNum : 0)
90
+ })
91
+ })
92
+ const dayData = Object.keys(mergedData).filter(v => Number(mergedData[v]) > 0).map(time => {
93
+ // 提取年、月和日
94
+ const year = time.slice(0, 4);
95
+ const month = time.slice(4, 6);
96
+ const day = time.slice(6, 8);
97
+
98
+ // 格式化为 'YYYY/MM/DD' 格式
99
+ const formattedDate = `${year}/${month}/${day}`
100
+ const dateStr = `${day}/${month}/${year}`
101
+ const dateObj = dayjs(formattedDate, "YYYY/MM/DD");
102
+ const dayOfWeek = dateObj.day() % 7;
103
+ const key = `${dateStr} (${loopsText[dayOfWeek]})`
104
+ return {
105
+ key,
106
+ value: mergedData[time].toFixed(2),
107
+ headlineText: formattedDate,
108
+ chartTitle: `${Number(key?.split('/')[0])}\n${loopsText[dayOfWeek]}`
109
+ }
110
+ })
111
+
112
+ state.overviewList = cloneDeep(dayData).reverse()
113
+ }
114
+
115
+ const getHourList = async (item: OverviewItem) => {
116
+ const value = item?.key?.split(' ')[0]?.split('/')
117
+ const date = `${value[2]}${value[1]}${value[0]}`
118
+ state.over7Days = overDays(date, 7)
119
+ if (state.over7Days) {
120
+ return []
121
+ }
122
+ const promiseGroup = params.deviceIdGroup.map(devId => getDpResultByHour(devId, params.addEleDpCode, date, 'sum').catch(error => ({ error })))
123
+ const res = await Promise.all(promiseGroup);
124
+ // @ts-ignore
125
+ const successGroup: DpResultByDataWithSpecifiedResData[] = res.filter(v => !v.error);
126
+ const mergedData = {}
127
+ successGroup.forEach(item => {
128
+ Object.keys(item).forEach(day => {
129
+ if (mergedData[day] === undefined) {
130
+ mergedData[day] = 0
131
+ }
132
+ const dayNum = Number(item[day])
133
+ mergedData[day] += Number(isNumber(dayNum) ? dayNum : 0)
134
+ })
135
+ })
136
+ const list: Array<object> = []
137
+ const resData = Object.keys(mergedData)?.map(val => {
138
+ return { key: Number(val?.slice(8, 10)), value: Number(mergedData[val]) }
139
+ })
140
+ for (let i = 0; i <= 23; i++) {
141
+ const hourData = resData?.find(val => val?.key === i)
142
+ const hourKey = hourData?.key || i
143
+ const hourValue = Number(hourData?.value) || 0
144
+ list.push({
145
+ key: `${hourKey.toString().padStart(2, '0')}:00`, value: hourValue, chartTitle: `${hourKey}:00`
146
+ })
147
+ }
148
+ return list
149
+ }
150
+
151
+ const styles = StyleSheet.create({
152
+ showTip: {
153
+ marginHorizontal: cx(24)
154
+ },
155
+ cardContainer: {
156
+ flex: 1,
157
+ marginHorizontal: cx(24),
158
+ padding: cx(16)
159
+ },
160
+ cardTitle: {
161
+ color: props.theme.global.fontColor,
162
+ fontSize: cx(16),
163
+ fontWeight: 'bold'
164
+ },
165
+ consumedEnergyContent: {
166
+ flex: 1,
167
+ flexDirection: 'row',
168
+ },
169
+ consumptionNum: {
170
+ color: props.theme.global.secondBrand,
171
+ fontFamily: 'helvetica_neue_lt_std_bd',
172
+ },
173
+ subContent: {
174
+ flex: 1,
175
+ alignItems: 'center',
176
+ marginBottom: cx(9)
177
+ },
178
+ valueText: {
179
+ fontSize: cx(24),
180
+ fontWeight: 'bold',
181
+ color: props.theme.global.secondBrand,
182
+ },
183
+ titleText: {
184
+ fontFamily: 'helvetica_neue_lt_std_roman',
185
+ fontSize: cx(14),
186
+ color: props.theme.global.secondFontColor,
187
+ textAlign: 'center',
188
+ },
189
+ priceBg: {
190
+ height: cx(40),
191
+ width: cx(40),
192
+ borderRadius: cx(40),
193
+ backgroundColor: props.theme.global.thirdBrand,
194
+ justifyContent: 'center',
195
+ alignItems: 'center',
196
+ marginRight: cx(10)
197
+ },
198
+ priceNum: {
199
+ marginRight: cx(10)
200
+ },
201
+ priceButton: {
202
+ borderRadius: cx(5),
203
+ paddingHorizontal: cx(10),
204
+ paddingVertical: cx(8),
205
+ marginTop: cx(8),
206
+ backgroundColor: props.theme.button.active,
207
+ alignItems: 'center',
208
+ justifyContent: 'center'
209
+ }
210
+ })
211
+
212
+ return (
213
+ <Page
214
+ backText={I18n.getLang('consumption_data_annual_bar_chart_system_back_text')}
215
+ headlineText={params.curMonth.key}
216
+ headlineIcon={state.overviewList.length ? res.download_icon : undefined}
217
+ onHeadlineIconClick={() => {
218
+ exportFile(state.overviewList)
219
+ }}
220
+ showGreenery={false}
221
+ greeneryIcon={res.energy_consumption_greenery}
222
+ loading={state.loading}
223
+ >
224
+ <ScrollView nestedScrollEnabled={true}>
225
+ <View>
226
+ <Spacer height={cx(15)} />
227
+ {/* 365 day */}
228
+ <Card
229
+ style={styles.cardContainer}
230
+ >
231
+ <Text style={styles.cardTitle}>{params.curMonth.key}</Text>
232
+ <Spacer height={cx(15)} />
233
+ <View style={{ flexDirection: 'row', alignItems: 'flex-end' }}>
234
+ <Text style={[styles.consumptionNum, { fontSize: cx(38), marginRight: cx(8) }]}>{params.curMonth.value}</Text>
235
+ <Text style={[styles.consumptionNum, { fontSize: cx(22), marginBottom: cx(4) }]}>kWh</Text>
236
+ </View>
237
+ <Spacer />
238
+ {/* CO2 */}
239
+ {params.isSolarMode && <>
240
+ <View style={{ flexDirection: 'row', alignItems: 'center' }}>
241
+ <View style={styles.priceBg}>
242
+ <Image
243
+ source={res.energy_consumption_cash}
244
+ resizeMode="contain"
245
+ style={{ height: cx(20), width: cx(20), tintColor: props.theme.button.active }}
246
+ />
247
+ </View>
248
+ <View style={styles.priceNum}>
249
+ <View style={{ flexDirection: 'row' }}>
250
+ <Text style={{ color: props.theme.global.secondFontColor, marginRight: cx(5) }}>{I18n.getLang('consumption_data_field3_co2_topic_text')}</Text>
251
+ <TouchableOpacity
252
+ onPress={() => {
253
+ state.showPopup = true
254
+ state.popupType = 'co2'
255
+ }}
256
+ >
257
+ <Image
258
+ source={res.co2Icon}
259
+ resizeMode="contain"
260
+ style={{ height: cx(20), width: cx(20), tintColor: props.theme.button.action }}
261
+ />
262
+ </TouchableOpacity>
263
+ </View>
264
+ <Text style={{ color: props.theme.global.fontColor, fontWeight: 'bold' }}>{`${state.co2Saved} kg`}</Text>
265
+ </View>
266
+ </View>
267
+ <Spacer height={cx(10)} />
268
+ </>}
269
+ {/* money */}
270
+ <View style={{ flexDirection: 'row', alignItems: 'center' }}>
271
+ <View style={styles.priceBg}>
272
+ <Image
273
+ source={res.energy_consumption_cash}
274
+ resizeMode="contain"
275
+ style={{ height: cx(20), width: cx(20), tintColor: props.theme.button.active }}
276
+ />
277
+ </View>
278
+ <View>
279
+ <View style={styles.priceNum}>
280
+ <Text style={{ color: props.theme.global.secondFontColor }}>{I18n.getLang('consumption_data_field3_value_text2')}</Text>
281
+ <Text style={{ color: props.theme.global.fontColor, fontWeight: 'bold' }}>{state.price ? `${localeNumber(Number(state.price) * Number(params.curMonth.value), 2)} ${state.unit}` : '-'}</Text>
282
+ </View>
283
+ <TouchableOpacity onPress={() => {
284
+ state.showPopup = true
285
+ state.popupType = 'money'
286
+ }}>
287
+ <View style={styles.priceButton}>
288
+ <Text style={{ color: props.theme.button.fontColor }}>{I18n.getLang('consumption_data_field3_button_text')}</Text>
289
+ </View>
290
+ </TouchableOpacity>
291
+ </View>
292
+ </View>
293
+ </Card>
294
+ <Spacer height={cx(30)} />
295
+ {/* Annual overview */}
296
+ <OverView
297
+ style={{ marginHorizontal: cx(24) }}
298
+ headlineText={I18n.getLang('consumption_data_monthly_overview_field2_headline_text')}
299
+ headlineClick={() => {
300
+ navigation.navigate(ui_biz_routerKey.group_ui_biz_energy_consumption_chart, {
301
+ headlineText: params.curMonth.key,
302
+ chartData: state.overviewList,
303
+ over365Days: state.over365Days
304
+ } as EnergyConsumptionChartProps)
305
+ }}
306
+ overviewItemClick={async (item) => {
307
+ state.loading = true
308
+ const res = await getHourList(item)
309
+ state.loading = false
310
+ navigation.navigate(ui_biz_routerKey.group_ui_biz_energy_consumption_chart, {
311
+ headlineText: item.key,
312
+ chartData: res,
313
+ over7Days: state.over7Days,
314
+ addEleDpCode: params.addEleDpCode
315
+ } as EnergyConsumptionChartProps)
316
+ }}
317
+ overViewList={state.overviewList}
318
+ />
319
+ {/* modal */}
320
+ <EnergyPopup
321
+ visible={(!!state.popupType && state.showPopup)}
322
+ popupType={state.popupType || 'co2'}
323
+ title={state.popupType === 'co2' ? I18n.getLang('consumption_data_field3_co2_topic_text') : ''}
324
+ cancelText={state.popupType === 'co2' ? '' : I18n.getLang("auto_scan_system_cancel")}
325
+ confirmText={I18n.getLang(state.popupType === 'co2' ? 'home_screen_home_dialog_yes_con' : 'auto_scan_system_wifi_confirm')}
326
+ energyData={{ price: state.price, unit: state.unit }}
327
+ onConfirm={(energyData) => {
328
+ state.popupType = ''
329
+ state.showPopup = false
330
+ if (energyData) {
331
+ params.updateEnergyData({
332
+ ...energyData,
333
+ price: exchangeNumber(energyData.price)
334
+ })
335
+ state.price = energyData.price
336
+ state.unit = energyData.unit
337
+ }
338
+ }}
339
+ onCancel={() => {
340
+ state.popupType = ''
341
+ state.showPopup = false
342
+ }}
343
+ />
344
+ </View>
345
+ </ScrollView>
346
+ </Page>
347
+ )
348
+ }
349
+
350
+ export default withTheme(EnergyConsumptionDetail)