@ledvance/group-ui-biz-bundle 1.0.84 → 1.0.86
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/EnergyConsumptionActions.ts +146 -2
- package/src/modules/energyConsumption/EnergyConsumptionChart.tsx +157 -37
- package/src/modules/energyConsumption/EnergyConsumptionDetail.tsx +12 -3
- package/src/modules/energyConsumption/EnergyConsumptionPage.tsx +14 -9
- package/src/modules/energyConsumption/co2Data.ts +6 -0
- package/src/modules/energyConsumption/component/DateSelectedItem.tsx +168 -0
- package/src/modules/energyConsumption/component/DateTypeItem.tsx +89 -0
- package/src/modules/energyConsumption/component/NewBarChart.tsx +141 -0
package/package.json
CHANGED
|
@@ -1,4 +1,11 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {useGroupEzvizConfig} from "@ledvance/base/src/models/modules/NativePropsSlice";
|
|
2
|
+
import {getDataWithSpecified, getDpResultByHour, getDpResultByMonth} from "@ledvance/base/src/models/TuyaApi";
|
|
3
|
+
import {overDays} from "@ledvance/base/src/utils";
|
|
4
|
+
import {loopsText, monthFormat, monthFormatShort} from "@ledvance/base/src/utils/common";
|
|
5
|
+
import {DateType} from "@ledvance/group-ui-biz-bundle/src/modules/energyConsumption/co2Data";
|
|
6
|
+
import {OverviewItem} from "@ledvance/group-ui-biz-bundle/src/modules/energyConsumption/EnergyConsumptionPage";
|
|
7
|
+
import dayjs from "dayjs";
|
|
8
|
+
import {isNumber} from "lodash";
|
|
2
9
|
import {EnergyData, UnitList} from "./component/EnergyModal";
|
|
3
10
|
|
|
4
11
|
interface LightConfig {
|
|
@@ -6,7 +13,7 @@ interface LightConfig {
|
|
|
6
13
|
}
|
|
7
14
|
|
|
8
15
|
export const useEnergyConsumption = () =>{
|
|
9
|
-
return
|
|
16
|
+
return useGroupEzvizConfig<LightConfig, EnergyData>('energyConsumption', {unit: UnitList[0], price: ''})
|
|
10
17
|
}
|
|
11
18
|
|
|
12
19
|
export const unitDivision = (str: string) => {
|
|
@@ -16,3 +23,140 @@ export const unitDivision = (str: string) => {
|
|
|
16
23
|
const name = str.split(unit)[0]
|
|
17
24
|
return [name, unit]
|
|
18
25
|
}
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
export async function getElectricity(devIdGroup: string[], addEleDpCode: string, date: string, dateType: DateType): Promise<OverviewItem[]> {
|
|
29
|
+
let res: OverviewItem[] = []
|
|
30
|
+
switch (dateType) {
|
|
31
|
+
case DateType.Year:
|
|
32
|
+
res = await getDpResultByYear(devIdGroup, addEleDpCode, date)
|
|
33
|
+
break
|
|
34
|
+
case DateType.Month:
|
|
35
|
+
res = await getDpResultByYearMonth(devIdGroup, addEleDpCode, date)
|
|
36
|
+
break
|
|
37
|
+
case DateType.Day:
|
|
38
|
+
res = await getDpResultByDate(devIdGroup, addEleDpCode, date);
|
|
39
|
+
break
|
|
40
|
+
}
|
|
41
|
+
return res
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
const getDpResultByYear = async (devIdGroup: string[], addEleDpCode: string, dateStr: string): Promise<OverviewItem[]> => {
|
|
45
|
+
const year = dateStr;
|
|
46
|
+
const promiseGroup = devIdGroup.map(devId =>
|
|
47
|
+
getDpResultByMonth(devId, addEleDpCode, 'sum').catch(error => ({error}))
|
|
48
|
+
);
|
|
49
|
+
const res = await Promise.all(promiseGroup);
|
|
50
|
+
// @ts-ignore
|
|
51
|
+
const successGroup: DpResultByMonthResData[] = res.filter(v => !v.error);
|
|
52
|
+
const mergedData = {}
|
|
53
|
+
successGroup.forEach(item => {
|
|
54
|
+
const monthData = item.years[year];
|
|
55
|
+
if (monthData) {
|
|
56
|
+
Object.keys(monthData).forEach(month => {
|
|
57
|
+
if (mergedData[month] === undefined) {
|
|
58
|
+
mergedData[month] = 0
|
|
59
|
+
}
|
|
60
|
+
const monthNum = Number(monthData[month])
|
|
61
|
+
mergedData[month] += Number(isNumber(monthNum) ? monthNum : 0)
|
|
62
|
+
})
|
|
63
|
+
}
|
|
64
|
+
})
|
|
65
|
+
const curMonthList = Object.keys(mergedData).sort((a, b) => parseInt(b) - parseInt(a));
|
|
66
|
+
return curMonthList.map(month => {
|
|
67
|
+
return {
|
|
68
|
+
key: `${monthFormat(month)} ${year}`,
|
|
69
|
+
value: (Number(mergedData[month]) || 0).toFixed(2),
|
|
70
|
+
headlineText: `${year}${month}`,
|
|
71
|
+
chartTitle: `${monthFormatShort(month)}\n${year}`
|
|
72
|
+
}
|
|
73
|
+
})
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
const getDpResultByYearMonth = async (deviceIdGroup: string[], addEleDpCode: string, dateStr: string): Promise<OverviewItem[]> => {
|
|
78
|
+
const date = dayjs(dateStr)
|
|
79
|
+
const startDay = date.startOf('month').format('YYYYMMDD')
|
|
80
|
+
const endDay = date.endOf('month').format('YYYYMMDD')
|
|
81
|
+
if (overDays(startDay, 365)) {
|
|
82
|
+
console.log("getDpResultByYearMonth overDays true")
|
|
83
|
+
return []
|
|
84
|
+
}
|
|
85
|
+
const promiseGroup = deviceIdGroup.map(devId => getDataWithSpecified(devId, addEleDpCode, startDay, endDay, 'sum').catch(error => ({error})))
|
|
86
|
+
const res = await Promise.all(promiseGroup);
|
|
87
|
+
// @ts-ignore
|
|
88
|
+
const successGroup: DpResultByDataWithSpecifiedResData[] = res.filter(v => !v.error);
|
|
89
|
+
const mergedData = {}
|
|
90
|
+
successGroup.forEach(item => {
|
|
91
|
+
if (item.result) {
|
|
92
|
+
Object.keys(item.result).forEach(day => {
|
|
93
|
+
if (mergedData[day] === undefined) {
|
|
94
|
+
mergedData[day] = 0
|
|
95
|
+
}
|
|
96
|
+
const dayNum = Number(item.result[day])
|
|
97
|
+
mergedData[day] += Number(isNumber(dayNum) ? dayNum : 0)
|
|
98
|
+
})
|
|
99
|
+
}
|
|
100
|
+
})
|
|
101
|
+
return Object.keys(mergedData).filter(v => Number(mergedData[v]) > 0).map(time => {
|
|
102
|
+
// 提取年、月和日
|
|
103
|
+
const year = time.slice(0, 4);
|
|
104
|
+
const month = time.slice(4, 6);
|
|
105
|
+
const day = time.slice(6, 8);
|
|
106
|
+
|
|
107
|
+
// 格式化为 'YYYY/MM/DD' 格式
|
|
108
|
+
const formattedDate = `${year}/${month}/${day}`
|
|
109
|
+
const dateStr = `${day}/${month}/${year}`
|
|
110
|
+
const dateObj = dayjs(formattedDate, "YYYY/MM/DD");
|
|
111
|
+
const dayOfWeek = dateObj.day() % 7;
|
|
112
|
+
const key = `${dateStr} (${loopsText[dayOfWeek]})`
|
|
113
|
+
return {
|
|
114
|
+
key,
|
|
115
|
+
value: Number(mergedData[time] || 0).toFixed(2),
|
|
116
|
+
headlineText: formattedDate,
|
|
117
|
+
chartTitle: `${Number(key?.split('/')[0])}\n${loopsText[dayOfWeek]}`
|
|
118
|
+
}
|
|
119
|
+
})
|
|
120
|
+
}
|
|
121
|
+
|
|
122
|
+
|
|
123
|
+
const getDpResultByDate = async (deviceIdGroup: string[], addEleDpCode: string, date: string): Promise<OverviewItem[]> => {
|
|
124
|
+
if (overDays(date, 7)) {
|
|
125
|
+
console.log("getDpResultByDate overDays true")
|
|
126
|
+
return []
|
|
127
|
+
}
|
|
128
|
+
const promiseGroup = deviceIdGroup.map(devId => getDpResultByHour(devId, addEleDpCode, date, 'sum').catch(error => ({error})))
|
|
129
|
+
const res = await Promise.all(promiseGroup);
|
|
130
|
+
// @ts-ignore
|
|
131
|
+
const successGroup: DpResultByDataWithSpecifiedResData[] = res.filter(v => !v.error);
|
|
132
|
+
const mergedData = {}
|
|
133
|
+
successGroup.forEach(item => {
|
|
134
|
+
if (item) {
|
|
135
|
+
Object.keys(item).forEach(day => {
|
|
136
|
+
if (mergedData[day] === undefined) {
|
|
137
|
+
mergedData[day] = 0
|
|
138
|
+
}
|
|
139
|
+
const dayNum = Number(item[day])
|
|
140
|
+
mergedData[day] += Number(isNumber(dayNum) ? dayNum : 0)
|
|
141
|
+
})
|
|
142
|
+
}
|
|
143
|
+
})
|
|
144
|
+
|
|
145
|
+
|
|
146
|
+
const list: Array<OverviewItem> = []
|
|
147
|
+
const resData = Object.keys(mergedData)?.map(val => {
|
|
148
|
+
return {key: Number(val?.slice(8, 10)), value: Number(mergedData[val])}
|
|
149
|
+
})
|
|
150
|
+
for (let i = 0; i <= 23; i++) {
|
|
151
|
+
const hourData = resData?.find(val => val?.key === i)
|
|
152
|
+
const hourKey = hourData?.key || i
|
|
153
|
+
const hourValue = Number(hourData?.value) || 0
|
|
154
|
+
list.push({
|
|
155
|
+
key: `${hourKey.toString().padStart(2, '0')}:00`,
|
|
156
|
+
value: hourValue,
|
|
157
|
+
chartTitle: `${hourKey}:00`,
|
|
158
|
+
headlineText: `${hourKey}:00`
|
|
159
|
+
})
|
|
160
|
+
}
|
|
161
|
+
return list
|
|
162
|
+
}
|
|
@@ -1,16 +1,23 @@
|
|
|
1
|
-
import React from "react";
|
|
1
|
+
import React, { useCallback } from "react";
|
|
2
2
|
import { Platform, View, StyleSheet, Image } from "react-native";
|
|
3
3
|
import { useRoute } from '@react-navigation/core'
|
|
4
4
|
import Page from "@ledvance/base/src/components/Page";
|
|
5
5
|
import res from "@ledvance/base/src/res";
|
|
6
6
|
import I18n from "@ledvance/base/src/i18n";
|
|
7
7
|
import { Utils } from "tuya-panel-kit";
|
|
8
|
-
import
|
|
9
|
-
import { exportFile } from "@ledvance/base/src/utils/common";
|
|
8
|
+
import { exportFile, loopsText, monthFormat } from "@ledvance/base/src/utils/common";
|
|
10
9
|
import { OverviewItem } from "./EnergyConsumptionPage";
|
|
11
10
|
import Spacer from "@ledvance/base/src/components/Spacer";
|
|
12
11
|
import InfoText from "@ledvance/base/src/components/InfoText";
|
|
13
12
|
import ThemeType from '@ledvance/base/src/config/themeType'
|
|
13
|
+
import {overDays} from "@ledvance/base/src/utils/index";
|
|
14
|
+
import dayjs from "dayjs";
|
|
15
|
+
import { DateType } from "@ledvance/group-ui-biz-bundle/src/modules/energyConsumption/co2Data";
|
|
16
|
+
import { useReactive, useUpdateEffect } from "ahooks";
|
|
17
|
+
import DateTypeItem from "@ledvance/group-ui-biz-bundle/src/modules/energyConsumption/component/DateTypeItem";
|
|
18
|
+
import DateSelectedItem from "@ledvance/group-ui-biz-bundle/src/modules/energyConsumption/component/DateSelectedItem";
|
|
19
|
+
import NewBarChart from "@ledvance/group-ui-biz-bundle/src/modules/energyConsumption/component/NewBarChart";
|
|
20
|
+
import { getElectricity } from "@ledvance/group-ui-biz-bundle/src/modules/energyConsumption/EnergyConsumptionActions";
|
|
14
21
|
|
|
15
22
|
const { convertX: cx, height, width } = Utils.RatioUtils
|
|
16
23
|
const { withTheme } = Utils.ThemeUtils
|
|
@@ -20,12 +27,16 @@ export interface EnergyConsumptionChartProps {
|
|
|
20
27
|
headlineText: string
|
|
21
28
|
chartData: OverviewItem[],
|
|
22
29
|
over365Days?: boolean
|
|
23
|
-
over7Days?: boolean
|
|
30
|
+
over7Days?: boolean,
|
|
31
|
+
deviceIdGroup: string[];
|
|
32
|
+
price: string,
|
|
33
|
+
unit: string,
|
|
34
|
+
date: string,
|
|
24
35
|
}
|
|
25
36
|
|
|
26
37
|
const EnergyConsumptionChart = (props: { theme?: ThemeType }) => {
|
|
27
38
|
const params = useRoute().params as EnergyConsumptionChartProps
|
|
28
|
-
const
|
|
39
|
+
const {price, unit, date, addEleDpCode,deviceIdGroup,over365Days, over7Days} = params;
|
|
29
40
|
|
|
30
41
|
const styles = StyleSheet.create({
|
|
31
42
|
listEmptyView: {
|
|
@@ -35,40 +46,149 @@ const EnergyConsumptionChart = (props: { theme?: ThemeType }) => {
|
|
|
35
46
|
width: cx(200),
|
|
36
47
|
height: cx(200),
|
|
37
48
|
},
|
|
38
|
-
|
|
49
|
+
listEmptyText: {
|
|
50
|
+
flex: 0
|
|
51
|
+
},
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
const getDateType = useCallback((date: string) => {
|
|
55
|
+
const datejs = dayjs(date);
|
|
56
|
+
if (datejs.isValid()) {
|
|
57
|
+
if (datejs.format('YYYY') === date) {
|
|
58
|
+
return DateType.Year;
|
|
59
|
+
} else if (datejs.format('YYYYMM') === date) {
|
|
60
|
+
return DateType.Month;
|
|
61
|
+
} else if (datejs.format('YYYY/MM/DD') === date) {
|
|
62
|
+
return DateType.Day;
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
return DateType.Day;
|
|
66
|
+
}, []);
|
|
67
|
+
|
|
68
|
+
const dateType = getDateType(date);
|
|
69
|
+
const state = useReactive({
|
|
70
|
+
loading: false,
|
|
71
|
+
dateType: dateType,
|
|
72
|
+
date: date,
|
|
73
|
+
headlineText: dateType === DateType.Year ? date : params.headlineText,
|
|
74
|
+
chartData: params.chartData.filter((item) => {
|
|
75
|
+
return dateType !== DateType.Year || item.headlineText.startsWith(date)
|
|
76
|
+
}),
|
|
77
|
+
price: isNaN(Number(price)) ? 0 : Number(price),
|
|
78
|
+
over365Days: over365Days,
|
|
79
|
+
over7Days: over7Days,
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
useUpdateEffect(() => {
|
|
83
|
+
state.over365Days = overDays(state.date, 365);
|
|
84
|
+
state.over7Days = overDays(state.date, 7);
|
|
85
|
+
updateHeadlineText(dayjs(state.date));
|
|
86
|
+
state.loading = true;
|
|
87
|
+
getElectricity(deviceIdGroup, addEleDpCode, state.date, state.dateType).then((res) => {
|
|
88
|
+
state.chartData = res;
|
|
89
|
+
state.loading = false;
|
|
90
|
+
})
|
|
91
|
+
}, [state.date]);
|
|
92
|
+
|
|
93
|
+
useUpdateEffect(() => {
|
|
94
|
+
const date = dayjs();
|
|
95
|
+
const year = date.year().toString();
|
|
96
|
+
const month = (date.month() + 1).toString().padStart(2, '0');
|
|
97
|
+
const day = date.date().toString().padStart(2, '0');
|
|
98
|
+
switch (state.dateType) {
|
|
99
|
+
case DateType.Year:
|
|
100
|
+
state.date = year;
|
|
101
|
+
break
|
|
102
|
+
case DateType.Month:
|
|
103
|
+
state.date = `${year}${month}`
|
|
104
|
+
break
|
|
105
|
+
case DateType.Day:
|
|
106
|
+
state.date = `${year}${month}${day}`
|
|
107
|
+
break
|
|
108
|
+
}
|
|
109
|
+
}, [state.dateType]);
|
|
110
|
+
|
|
111
|
+
const updateHeadlineText = useCallback((date: dayjs.Dayjs) => {
|
|
112
|
+
const year = date.year().toString();
|
|
113
|
+
const month = (date.month() + 1).toString().padStart(2, '0');
|
|
114
|
+
const day = date.date().toString().padStart(2, '0');
|
|
115
|
+
const dayOfWeek = date.day() % 7;
|
|
116
|
+
switch (state.dateType) {
|
|
117
|
+
case DateType.Year:
|
|
118
|
+
state.headlineText = year;
|
|
119
|
+
break
|
|
120
|
+
case DateType.Month:
|
|
121
|
+
state.headlineText = `${monthFormat(month)} ${year}`;
|
|
122
|
+
break
|
|
123
|
+
case DateType.Day:
|
|
124
|
+
state.headlineText = `${day}/${month}/${year}\n${loopsText[dayOfWeek]}`;
|
|
125
|
+
break
|
|
126
|
+
}
|
|
127
|
+
}, [state.dateType, state.headlineText]);
|
|
128
|
+
|
|
129
|
+
const getEmptyDataTip = useCallback(() => {
|
|
130
|
+
if (state.over365Days) {
|
|
131
|
+
return I18n.getLang('energyconsumption_Daylimit')
|
|
132
|
+
}
|
|
133
|
+
if (state.dateType === DateType.Day && state.over7Days) {
|
|
134
|
+
return I18n.getLang('energyconsumption_hourlylimit')
|
|
135
|
+
}
|
|
136
|
+
return I18n.getLang('energyconsumption_emptydata')
|
|
137
|
+
}, [state.dateType, state.over365Days, state.over7Days]);
|
|
39
138
|
|
|
40
139
|
return (
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
{
|
|
53
|
-
|
|
54
|
-
<
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
140
|
+
<Page
|
|
141
|
+
backText={I18n.getLang('consumption_data_annual_bar_chart_system_back_text')}
|
|
142
|
+
headlineText={state.headlineText}
|
|
143
|
+
headlineIcon={state.chartData?.length ? res.download_icon : undefined}
|
|
144
|
+
onHeadlineIconClick={() => {
|
|
145
|
+
exportFile(state.chartData,params.price,params.unit)
|
|
146
|
+
}}
|
|
147
|
+
showGreenery={false}
|
|
148
|
+
greeneryIcon={res.energy_consumption_greenery}
|
|
149
|
+
loading={state.loading}
|
|
150
|
+
>
|
|
151
|
+
<View style={{ marginHorizontal: cx(24) }}>
|
|
152
|
+
<View style={{flexDirection: 'row'}}>
|
|
153
|
+
<DateTypeItem
|
|
154
|
+
style={{flex: 1}}
|
|
155
|
+
dateType={state.dateType}
|
|
156
|
+
onDateTypeChange={(dateType) => {
|
|
157
|
+
state.dateType = dateType;
|
|
158
|
+
}}/>
|
|
159
|
+
<DateSelectedItem
|
|
160
|
+
style={{flex: 1, marginStart: cx(10),marginBottom:cx(15)}}
|
|
161
|
+
dateType={state.dateType}
|
|
162
|
+
date={state.date}
|
|
163
|
+
onDateChange={date => {
|
|
164
|
+
state.date = date;
|
|
165
|
+
}}
|
|
166
|
+
/>
|
|
167
|
+
|
|
168
|
+
</View>
|
|
169
|
+
{
|
|
170
|
+
(state.chartData.length <= 0) ? (
|
|
171
|
+
<View style={styles.listEmptyView}>
|
|
172
|
+
<Spacer height={cx(26)} />
|
|
173
|
+
<Image
|
|
174
|
+
style={styles.listEmptyImage}
|
|
175
|
+
source={{ uri: res.ldv_timer_empty }} />
|
|
176
|
+
<Spacer height={cx(14)} />
|
|
177
|
+
<InfoText
|
|
178
|
+
text={getEmptyDataTip()}
|
|
179
|
+
icon={res.ic_info}
|
|
180
|
+
textStyle={styles.listEmptyText}
|
|
181
|
+
contentColor={props.theme?.global.fontColor}
|
|
182
|
+
/>
|
|
183
|
+
</View>
|
|
184
|
+
) : (
|
|
185
|
+
state.chartData.length > 0 && !state.loading &&
|
|
186
|
+
<NewBarChart height={400} data={state.chartData} price={state.price}
|
|
187
|
+
unit={unit}/>
|
|
188
|
+
)
|
|
189
|
+
}
|
|
190
|
+
</View>
|
|
191
|
+
</Page>
|
|
72
192
|
)
|
|
73
193
|
}
|
|
74
194
|
|
|
@@ -216,7 +216,7 @@ const EnergyConsumptionDetail = (props: { theme?: ThemeType }) => {
|
|
|
216
216
|
headlineText={params.curMonth.key}
|
|
217
217
|
headlineIcon={state.overviewList.length ? res.download_icon : undefined}
|
|
218
218
|
onHeadlineIconClick={() => {
|
|
219
|
-
exportFile(state.overviewList)
|
|
219
|
+
exportFile(state.overviewList, params.price, params.unit);
|
|
220
220
|
}}
|
|
221
221
|
showGreenery={false}
|
|
222
222
|
greeneryIcon={res.energy_consumption_greenery}
|
|
@@ -301,7 +301,12 @@ const EnergyConsumptionDetail = (props: { theme?: ThemeType }) => {
|
|
|
301
301
|
navigation.navigate(ui_biz_routerKey.group_ui_biz_energy_consumption_chart, {
|
|
302
302
|
headlineText: params.curMonth.key,
|
|
303
303
|
chartData: state.overviewList,
|
|
304
|
-
over365Days: state.over365Days
|
|
304
|
+
over365Days: state.over365Days,
|
|
305
|
+
price:state.price,
|
|
306
|
+
unit:state.unit,
|
|
307
|
+
addEleDpCode:params.addEleDpCode,
|
|
308
|
+
date:params.curMonth.headlineText,
|
|
309
|
+
deviceIdGroup: params.deviceIdGroup,
|
|
305
310
|
} as EnergyConsumptionChartProps)
|
|
306
311
|
}}
|
|
307
312
|
overviewItemClick={async (item) => {
|
|
@@ -312,7 +317,11 @@ const EnergyConsumptionDetail = (props: { theme?: ThemeType }) => {
|
|
|
312
317
|
headlineText: item.key,
|
|
313
318
|
chartData: res,
|
|
314
319
|
over7Days: state.over7Days,
|
|
315
|
-
addEleDpCode: params.addEleDpCode
|
|
320
|
+
addEleDpCode: params.addEleDpCode,
|
|
321
|
+
price:state.price,
|
|
322
|
+
unit:state.unit,
|
|
323
|
+
date:item.headlineText,
|
|
324
|
+
deviceIdGroup: params.deviceIdGroup,
|
|
316
325
|
} as EnergyConsumptionChartProps)
|
|
317
326
|
}}
|
|
318
327
|
overViewList={state.overviewList}
|
|
@@ -285,7 +285,7 @@ const EnergyConsumptionPage = (props: { theme?: ThemeType }) => {
|
|
|
285
285
|
)}
|
|
286
286
|
headlineIcon={(state.isSolarMode ? state.solarOverviewList : state.wifiOverviewList).length ? res.download_icon : undefined}
|
|
287
287
|
onHeadlineIconClick={() => {
|
|
288
|
-
exportFile(state.isSolarMode ? state.solarOverviewList : state.wifiOverviewList);
|
|
288
|
+
exportFile(state.isSolarMode ? state.solarOverviewList : state.wifiOverviewList, state.price, state.unit);
|
|
289
289
|
}}
|
|
290
290
|
showGreenery={state.isSolarMode}
|
|
291
291
|
greeneryIcon={res.energy_consumption_greenery}
|
|
@@ -457,14 +457,19 @@ const EnergyConsumptionPage = (props: { theme?: ThemeType }) => {
|
|
|
457
457
|
<Spacer height={cx(30)} />
|
|
458
458
|
{/* Annual overview */}
|
|
459
459
|
<OverView
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
460
|
+
style={{marginHorizontal: cx(24)}}
|
|
461
|
+
headlineText={I18n.getLang('consumption_data_field4_headline_text')}
|
|
462
|
+
headlineClick={() => {
|
|
463
|
+
navigation.navigate(ui_biz_routerKey.group_ui_biz_energy_consumption_chart, {
|
|
464
|
+
headlineText: chartHeadline,
|
|
465
|
+
chartData: state.isSolarMode ? state.solarOverviewList : state.wifiOverviewList,
|
|
466
|
+
price: state.price,
|
|
467
|
+
unit: state.unit,
|
|
468
|
+
addEleDpCode: params.addEleDpCode,
|
|
469
|
+
date: (new Date()).getFullYear().toString(),
|
|
470
|
+
deviceIdGroup: state.isSolarMode ? params.solarPlugGroup : params.wifiPlugGroup,
|
|
471
|
+
} as EnergyConsumptionChartProps)
|
|
472
|
+
}}
|
|
468
473
|
overviewItemClick={(item) => {
|
|
469
474
|
navigation.navigate(ui_biz_routerKey.group_ui_biz_energy_consumption_detail, {
|
|
470
475
|
addEleDpCode: params.addEleDpCode,
|
|
@@ -23653,3 +23653,9 @@ export const carbonDioxideEmission = (name, list) => {
|
|
|
23653
23653
|
const order = emissionData?.find(item => item.nation === nationCode)?.value
|
|
23654
23654
|
return order || emissionData?.find(item => item.nation === 'World')?.value
|
|
23655
23655
|
}
|
|
23656
|
+
|
|
23657
|
+
export enum DateType {
|
|
23658
|
+
Year = 'Year',
|
|
23659
|
+
Month = 'Month',
|
|
23660
|
+
Day = 'Day',
|
|
23661
|
+
}
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
import ThemeType from "@ledvance/base/src/config/themeType";
|
|
2
|
+
import React, {PropsWithChildren, useCallback, useEffect} from "react";
|
|
3
|
+
import {StyleSheet, Text, TouchableOpacity, View, ViewProps} from "react-native";
|
|
4
|
+
import {DatePicker, Modal, Utils} from "tuya-panel-kit";
|
|
5
|
+
import {useReactive} from "ahooks";
|
|
6
|
+
import I18n from "@ledvance/base/src/i18n/index";
|
|
7
|
+
import dayjs from "dayjs";
|
|
8
|
+
import {DateType} from "@ledvance/group-ui-biz-bundle/src/modules/energyConsumption/co2Data";
|
|
9
|
+
|
|
10
|
+
const {convertX: cx} = Utils.RatioUtils
|
|
11
|
+
const {withTheme} = Utils.ThemeUtils
|
|
12
|
+
|
|
13
|
+
interface DateSelectedItemProps extends PropsWithChildren<ViewProps> {
|
|
14
|
+
theme?: ThemeType
|
|
15
|
+
date: string,
|
|
16
|
+
dateType: DateType,
|
|
17
|
+
onDateChange: (string) => void
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const getModalMode = (dateType) => {
|
|
21
|
+
switch (dateType) {
|
|
22
|
+
case DateType.Year:
|
|
23
|
+
return 'year';
|
|
24
|
+
case DateType.Month:
|
|
25
|
+
return 'month';
|
|
26
|
+
case DateType.Day:
|
|
27
|
+
default:
|
|
28
|
+
return 'date';
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
const getDateForDateType = (year, month, day, dateType: DateType) => {
|
|
33
|
+
const monthOfYear = month.toString().padStart(2, '0');
|
|
34
|
+
const dayOfMonth = day.toString().padStart(2, '0');
|
|
35
|
+
switch (dateType) {
|
|
36
|
+
case DateType.Year:
|
|
37
|
+
return `${year}`
|
|
38
|
+
case DateType.Month:
|
|
39
|
+
return `${year}${monthOfYear}`;
|
|
40
|
+
case DateType.Day:
|
|
41
|
+
return `${year}${monthOfYear}${dayOfMonth}`;
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export default withTheme(function DateSelectedItem(props: DateSelectedItemProps) {
|
|
46
|
+
const {dateType, date, onDateChange, theme} = props;
|
|
47
|
+
const state = useReactive({
|
|
48
|
+
showDateModal: false,
|
|
49
|
+
date: date,
|
|
50
|
+
selectedDate: date,
|
|
51
|
+
dateType: dateType,
|
|
52
|
+
modalMode: getModalMode(dateType),
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
const styles = StyleSheet.create({
|
|
56
|
+
root: {
|
|
57
|
+
width: '100%',
|
|
58
|
+
flexDirection: 'row',
|
|
59
|
+
borderRadius: cx(4),
|
|
60
|
+
backgroundColor: props.theme?.textInput.background,
|
|
61
|
+
alignItems: 'center',
|
|
62
|
+
height: cx(44),
|
|
63
|
+
borderBottomWidth: cx(1),
|
|
64
|
+
borderBottomColor: props.theme?.textInput.line,
|
|
65
|
+
},
|
|
66
|
+
date: {
|
|
67
|
+
fontSize: cx(16),
|
|
68
|
+
textAlign: 'center',
|
|
69
|
+
flex: 1,
|
|
70
|
+
color: props.theme?.textInput.fontColor,
|
|
71
|
+
fontFamily: 'helvetica_neue_lt_std_roman',
|
|
72
|
+
},
|
|
73
|
+
modalRoot: {paddingTop: cx(20), backgroundColor: theme?.popup.cellBg},
|
|
74
|
+
modalButtonParent: {flex: 1, height: cx(48)},
|
|
75
|
+
modalButton: {
|
|
76
|
+
height: '100%',
|
|
77
|
+
alignItems: 'center',
|
|
78
|
+
justifyContent: 'center'
|
|
79
|
+
},
|
|
80
|
+
modalConfirm: {
|
|
81
|
+
textAlign: 'center',
|
|
82
|
+
color: theme?.popup.confirmFontColor,
|
|
83
|
+
fontWeight: 'bold',
|
|
84
|
+
fontSize: cx(16)
|
|
85
|
+
},
|
|
86
|
+
modalCancel: {
|
|
87
|
+
textAlign: 'center',
|
|
88
|
+
color: theme?.popup.cancelFontColor,
|
|
89
|
+
fontSize: cx(16)
|
|
90
|
+
}
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
useEffect(() => {
|
|
94
|
+
state.dateType = dateType;
|
|
95
|
+
const datejs = dayjs(date)
|
|
96
|
+
switch (dateType) {
|
|
97
|
+
case DateType.Year:
|
|
98
|
+
state.date = `${datejs.year()}`;
|
|
99
|
+
break
|
|
100
|
+
case DateType.Month:
|
|
101
|
+
state.date = `${datejs.month() + 1}/${datejs.year()}`;
|
|
102
|
+
break
|
|
103
|
+
case DateType.Day:
|
|
104
|
+
state.date = `${datejs.date()}/${datejs.month() + 1}/${datejs.year()}`;
|
|
105
|
+
}
|
|
106
|
+
state.modalMode = getModalMode(dateType);
|
|
107
|
+
}, [dateType, date]);
|
|
108
|
+
|
|
109
|
+
return (<View style={props.style}>
|
|
110
|
+
<TouchableOpacity
|
|
111
|
+
style={{width: '100%',}}
|
|
112
|
+
onPress={() => {
|
|
113
|
+
state.showDateModal = true;
|
|
114
|
+
}}
|
|
115
|
+
>
|
|
116
|
+
<View style={styles.root}>
|
|
117
|
+
<Text style={styles.date}>{state.date}</Text>
|
|
118
|
+
</View>
|
|
119
|
+
</TouchableOpacity>
|
|
120
|
+
|
|
121
|
+
<Modal
|
|
122
|
+
visible={state.showDateModal}
|
|
123
|
+
onCancel={() => {
|
|
124
|
+
state.showDateModal = false;
|
|
125
|
+
}}>
|
|
126
|
+
<View style={styles.modalRoot}>
|
|
127
|
+
<DatePicker
|
|
128
|
+
defaultDate={dayjs(date).toDate()}
|
|
129
|
+
mode={state.modalMode}
|
|
130
|
+
dateSortKeys={['day', 'month', 'year']}
|
|
131
|
+
maxDate={new Date()}
|
|
132
|
+
style={{backgroundColor: theme?.popup.cellBg}}
|
|
133
|
+
pickerFontColor={theme?.global.fontColor}
|
|
134
|
+
onDateChange={date => {
|
|
135
|
+
if (date) {
|
|
136
|
+
state.selectedDate = getDateForDateType(
|
|
137
|
+
date.getFullYear(),
|
|
138
|
+
date.getMonth() + 1,
|
|
139
|
+
date.getDate(),
|
|
140
|
+
dateType,
|
|
141
|
+
)
|
|
142
|
+
}
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
/>
|
|
146
|
+
<View style={{flexDirection: 'row'}}>
|
|
147
|
+
<TouchableOpacity style={styles.modalButtonParent} onPress={() => {
|
|
148
|
+
state.showDateModal = false;
|
|
149
|
+
}}>
|
|
150
|
+
<View style={styles.modalButton}>
|
|
151
|
+
<Text style={styles.modalCancel}>{I18n.getLang('auto_scan_system_cancel')}</Text>
|
|
152
|
+
</View>
|
|
153
|
+
</TouchableOpacity>
|
|
154
|
+
<TouchableOpacity style={styles.modalButtonParent} onPress={() => {
|
|
155
|
+
state.showDateModal = false;
|
|
156
|
+
onDateChange(state.selectedDate);
|
|
157
|
+
}}>
|
|
158
|
+
<View style={styles.modalButton}>
|
|
159
|
+
<Text style={styles.modalConfirm}>{I18n.getLang('auto_scan_system_wifi_confirm')}</Text>
|
|
160
|
+
</View>
|
|
161
|
+
</TouchableOpacity>
|
|
162
|
+
</View>
|
|
163
|
+
</View>
|
|
164
|
+
</Modal>
|
|
165
|
+
|
|
166
|
+
</View>)
|
|
167
|
+
})
|
|
168
|
+
|
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
import ThemeType from "@ledvance/base/src/config/themeType";
|
|
2
|
+
import React, {PropsWithChildren} from "react";
|
|
3
|
+
import {Text, TouchableOpacity, View, ViewProps} from "react-native";
|
|
4
|
+
import {Modal, Utils} from "tuya-panel-kit";
|
|
5
|
+
import {useReactive, useUpdateEffect} from "ahooks";
|
|
6
|
+
import I18n from "@ledvance/base/src/i18n/index";
|
|
7
|
+
import { DateType } from "@ledvance/group-ui-biz-bundle/src/modules/energyConsumption/co2Data";
|
|
8
|
+
|
|
9
|
+
const {convertX: cx} = Utils.RatioUtils
|
|
10
|
+
const {withTheme} = Utils.ThemeUtils
|
|
11
|
+
|
|
12
|
+
interface DateTypeItemProps extends PropsWithChildren<ViewProps> {
|
|
13
|
+
theme?: ThemeType
|
|
14
|
+
dateType: string,
|
|
15
|
+
onDateTypeChange: (dateType: DateType) => void
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
export default withTheme(function DateTypeItem(props: DateTypeItemProps) {
|
|
19
|
+
const {dateType} = props;
|
|
20
|
+
const state = useReactive({
|
|
21
|
+
showDateTypeModal: false,
|
|
22
|
+
dateType: dateType,
|
|
23
|
+
});
|
|
24
|
+
useUpdateEffect(() => {
|
|
25
|
+
state.dateType = dateType;
|
|
26
|
+
}, [dateType])
|
|
27
|
+
return (<View style={props.style}>
|
|
28
|
+
<TouchableOpacity
|
|
29
|
+
style={{width: '100%',}}
|
|
30
|
+
onPress={() => {
|
|
31
|
+
state.showDateTypeModal = true;
|
|
32
|
+
}}
|
|
33
|
+
>
|
|
34
|
+
<View
|
|
35
|
+
style={{
|
|
36
|
+
width: '100%',
|
|
37
|
+
flexDirection: 'row',
|
|
38
|
+
borderRadius: cx(4),
|
|
39
|
+
backgroundColor: props.theme?.textInput.background,
|
|
40
|
+
alignItems: 'center',
|
|
41
|
+
height: cx(44),
|
|
42
|
+
borderBottomWidth: cx(1),
|
|
43
|
+
borderBottomColor: props.theme?.textInput.line,
|
|
44
|
+
}}
|
|
45
|
+
>
|
|
46
|
+
<Text style={{
|
|
47
|
+
fontSize: cx(16),
|
|
48
|
+
textAlign: 'center',
|
|
49
|
+
flex: 1,
|
|
50
|
+
color: props.theme?.textInput.fontColor,
|
|
51
|
+
fontFamily: 'helvetica_neue_lt_std_roman',
|
|
52
|
+
}}>{state.dateType}</Text>
|
|
53
|
+
</View>
|
|
54
|
+
</TouchableOpacity>
|
|
55
|
+
|
|
56
|
+
<Modal.List
|
|
57
|
+
type={'radio'}
|
|
58
|
+
visible={state.showDateTypeModal}
|
|
59
|
+
value={state.dateType}
|
|
60
|
+
dataSource={[
|
|
61
|
+
{
|
|
62
|
+
title: I18n.getLang('day'),
|
|
63
|
+
key: DateType.Day,
|
|
64
|
+
value: DateType.Day,
|
|
65
|
+
},
|
|
66
|
+
{
|
|
67
|
+
title: I18n.getLang('month'),
|
|
68
|
+
key: DateType.Month,
|
|
69
|
+
value: DateType.Month,
|
|
70
|
+
},
|
|
71
|
+
{
|
|
72
|
+
title: I18n.getLang('year'),
|
|
73
|
+
key: DateType.Year,
|
|
74
|
+
value: DateType.Year,
|
|
75
|
+
}
|
|
76
|
+
]}
|
|
77
|
+
onCancel={() => {
|
|
78
|
+
state.showDateTypeModal = false;
|
|
79
|
+
}}
|
|
80
|
+
title={I18n.getLang('date_type')}
|
|
81
|
+
cancelText={I18n.getLang('auto_scan_system_cancel')}
|
|
82
|
+
confirmText={I18n.getLang('auto_scan_system_wifi_confirm')}
|
|
83
|
+
onConfirm={(item: DateType) => {
|
|
84
|
+
state.showDateTypeModal = false;
|
|
85
|
+
props.onDateTypeChange(item)
|
|
86
|
+
}}
|
|
87
|
+
/>
|
|
88
|
+
</View>)
|
|
89
|
+
})
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import React, {useRef} from 'react';
|
|
2
|
+
import {View} from 'react-native';
|
|
3
|
+
import ECharts from '@ledvance/react-native-echarts-pro';
|
|
4
|
+
import I18n from "@ledvance/base/src/i18n";
|
|
5
|
+
import ThemeType from "@ledvance/base/src/config/themeType";
|
|
6
|
+
import {Utils} from "tuya-panel-kit";
|
|
7
|
+
import {OverviewItem} from "../EnergyConsumptionPage";
|
|
8
|
+
|
|
9
|
+
const {withTheme} = Utils.ThemeUtils
|
|
10
|
+
|
|
11
|
+
interface BarChartProps {
|
|
12
|
+
theme?: ThemeType
|
|
13
|
+
data: OverviewItem[],
|
|
14
|
+
price: number,
|
|
15
|
+
unit: string,
|
|
16
|
+
height: number
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
const BarChartWithTouch = (props: BarChartProps) => {
|
|
20
|
+
const echarts = useRef();
|
|
21
|
+
const {data, height, price, unit, theme} = props;
|
|
22
|
+
const dataX = data?.map(item => {
|
|
23
|
+
return item.chartTitle;
|
|
24
|
+
});
|
|
25
|
+
const dataKwhY = data?.map(item => {
|
|
26
|
+
return Number(item.value);
|
|
27
|
+
});
|
|
28
|
+
const dataPriceY = data?.map(item => {
|
|
29
|
+
return ((isNaN(Number(item.value)) ? 0 : Number(item.value)) * price).toFixed(2);
|
|
30
|
+
});
|
|
31
|
+
const maxValue = Math.max(...dataKwhY);
|
|
32
|
+
const option = {
|
|
33
|
+
tooltip: {
|
|
34
|
+
show: true,
|
|
35
|
+
triggerOn: 'click',
|
|
36
|
+
trigger: 'axis',
|
|
37
|
+
},
|
|
38
|
+
legend: {
|
|
39
|
+
show: true,
|
|
40
|
+
data: ['kWh', unit],
|
|
41
|
+
textStyle: {
|
|
42
|
+
color: theme?.global.fontColor,
|
|
43
|
+
}
|
|
44
|
+
},
|
|
45
|
+
xAxis: {
|
|
46
|
+
data: dataX,
|
|
47
|
+
axisTick: {
|
|
48
|
+
show: false,
|
|
49
|
+
},
|
|
50
|
+
axisLabel: {
|
|
51
|
+
show: true,
|
|
52
|
+
color: props.theme?.global.secondFontColor,
|
|
53
|
+
interval: 0,
|
|
54
|
+
}
|
|
55
|
+
},
|
|
56
|
+
yAxis: [{
|
|
57
|
+
type: 'value',
|
|
58
|
+
name: I18n.getLang('consumption_data_annual_bar_chart_text'),
|
|
59
|
+
max: Math.ceil(maxValue),
|
|
60
|
+
min: 0,
|
|
61
|
+
position: 'left',
|
|
62
|
+
axisLabel: {
|
|
63
|
+
formatter: function (value) {
|
|
64
|
+
return parseFloat(value).toFixed(2);
|
|
65
|
+
},
|
|
66
|
+
color:props.theme?.global.secondFontColor,
|
|
67
|
+
},
|
|
68
|
+
nameTextStyle: {
|
|
69
|
+
fontSize: 14,
|
|
70
|
+
color: props.theme?.global.secondFontColor,
|
|
71
|
+
},
|
|
72
|
+
}, {
|
|
73
|
+
type: 'value',
|
|
74
|
+
name: I18n.formatValue('format_unit', unit),
|
|
75
|
+
position: 'right',
|
|
76
|
+
alignTicks: true,
|
|
77
|
+
max: Math.ceil(price ? maxValue * price * 1.4 : 0),
|
|
78
|
+
min: 0,
|
|
79
|
+
minInterval: 1,
|
|
80
|
+
axisLabel: {
|
|
81
|
+
formatter: function (value) {
|
|
82
|
+
return parseFloat(value).toFixed(1);
|
|
83
|
+
},
|
|
84
|
+
color:props.theme?.global.secondFontColor,
|
|
85
|
+
},
|
|
86
|
+
nameTextStyle: {
|
|
87
|
+
fontSize: 14,
|
|
88
|
+
color: props.theme?.global.secondFontColor,
|
|
89
|
+
},
|
|
90
|
+
}],
|
|
91
|
+
series: [
|
|
92
|
+
{
|
|
93
|
+
name: 'kWh',
|
|
94
|
+
type: 'bar',
|
|
95
|
+
data: dataKwhY,
|
|
96
|
+
itemStyle: {
|
|
97
|
+
emphasis: {
|
|
98
|
+
color: '#FFC2A9', // Color when bar is clicked
|
|
99
|
+
},
|
|
100
|
+
color: '#FFC2A9',
|
|
101
|
+
borderRadius: 2,
|
|
102
|
+
},
|
|
103
|
+
barMaxWidth: 10,
|
|
104
|
+
select: {
|
|
105
|
+
itemStyle: {
|
|
106
|
+
borderColor: '#FFC2A9',
|
|
107
|
+
}
|
|
108
|
+
},
|
|
109
|
+
selectedMode: 'single',
|
|
110
|
+
},
|
|
111
|
+
{
|
|
112
|
+
name: unit,
|
|
113
|
+
type: 'line',
|
|
114
|
+
data: dataPriceY,
|
|
115
|
+
yAxisIndex: 1,
|
|
116
|
+
smooth: true,
|
|
117
|
+
showSymbol: false,
|
|
118
|
+
color: '#F49431',
|
|
119
|
+
selectedMode: "single",
|
|
120
|
+
}
|
|
121
|
+
],
|
|
122
|
+
dataZoom: {
|
|
123
|
+
start: 0,
|
|
124
|
+
type: "inside",
|
|
125
|
+
maxValueSpan: 5,
|
|
126
|
+
zoomLock: true,
|
|
127
|
+
},
|
|
128
|
+
customMapData: {}
|
|
129
|
+
};
|
|
130
|
+
return (
|
|
131
|
+
<View style={{flex: 1}}>
|
|
132
|
+
<ECharts
|
|
133
|
+
option={option}
|
|
134
|
+
ref={echarts}
|
|
135
|
+
height={height}
|
|
136
|
+
/>
|
|
137
|
+
</View>
|
|
138
|
+
);
|
|
139
|
+
};
|
|
140
|
+
|
|
141
|
+
export default withTheme(BarChartWithTouch)
|