@ledvance/ui-biz-bundle 1.1.146 → 1.1.148
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 +2 -1
- package/src/modules/flags/FlagItem.tsx +14 -6
- package/src/newModules/energyConsumption/EnergyConsumptionActions.ts +101 -23
- package/src/newModules/energyConsumption/EnergyConsumptionChart/EmptyDataView.tsx +21 -0
- package/src/newModules/energyConsumption/EnergyConsumptionChart/EnergyChartSection.tsx +75 -0
- package/src/newModules/energyConsumption/EnergyConsumptionChart/LandscapeView.tsx +46 -0
- package/src/newModules/energyConsumption/EnergyConsumptionChart/PortraitView.tsx +43 -0
- package/src/newModules/energyConsumption/EnergyConsumptionChart/PowerChartSection.tsx +42 -0
- package/src/newModules/energyConsumption/EnergyConsumptionChart/index.tsx +33 -0
- package/src/newModules/energyConsumption/EnergyConsumptionChart/styles.ts +95 -0
- package/src/newModules/energyConsumption/EnergyConsumptionChart/useEnergyData.ts +147 -0
- package/src/newModules/energyConsumption/EnergyConsumptionChart.tsx +0 -327
- package/src/newModules/energyConsumption/EnergyConsumptionPage.tsx +42 -33
- package/src/newModules/energyConsumption/Router.ts +1 -1
- package/src/newModules/energyConsumption/component/DateSelectedItem.tsx +1 -1
- package/src/newModules/energyConsumption/component/PowerLineChart.tsx +2 -2
- package/src/newModules/mood/MoodItem.tsx +172 -166
package/package.json
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
"name": "@ledvance/ui-biz-bundle",
|
|
5
5
|
"pid": [],
|
|
6
6
|
"uiid": "",
|
|
7
|
-
"version": "1.1.
|
|
7
|
+
"version": "1.1.148",
|
|
8
8
|
"scripts": {},
|
|
9
9
|
"dependencies": {
|
|
10
10
|
"@ledvance/base": "^1.x",
|
|
@@ -16,6 +16,7 @@
|
|
|
16
16
|
"prop-types": "^15.6.1",
|
|
17
17
|
"react": "16.8.3",
|
|
18
18
|
"react-native": "0.59.10",
|
|
19
|
+
"react-native-orientation-locker": "^1.7.0",
|
|
19
20
|
"react-native-svg": "5.5.1",
|
|
20
21
|
"react-redux": "^7.2.1",
|
|
21
22
|
"tuya-panel-kit": "^4.9.4"
|
|
@@ -1,10 +1,11 @@
|
|
|
1
|
-
import { StyleSheet, View, Text, ViewStyle, Image } from 'react-native'
|
|
1
|
+
import { StyleSheet, View, Text, ViewStyle, Image, TouchableOpacity } from 'react-native'
|
|
2
2
|
import React from 'react'
|
|
3
3
|
import Card from '@ledvance/base/src/components/Card'
|
|
4
|
-
import {
|
|
4
|
+
import { Utils } from 'tuya-panel-kit'
|
|
5
5
|
import MoodColorsLine from '@ledvance/base/src/components/MoodColorsLine'
|
|
6
6
|
import Spacer from '@ledvance/base/src/components/Spacer'
|
|
7
7
|
import ThemeType from '@ledvance/base/src/config/themeType'
|
|
8
|
+
import res from '@ledvance/base/src/res'
|
|
8
9
|
|
|
9
10
|
const cx = Utils.RatioUtils.convertX
|
|
10
11
|
const { withTheme } = Utils.ThemeUtils
|
|
@@ -31,6 +32,7 @@ function FlagItem(props: RecommendMoodItemProps) {
|
|
|
31
32
|
flexDirection: 'row',
|
|
32
33
|
marginHorizontal: cx(16),
|
|
33
34
|
justifyContent: 'space-between',
|
|
35
|
+
alignItems: 'center',
|
|
34
36
|
},
|
|
35
37
|
headText: {
|
|
36
38
|
color: props.theme?.global.fontColor,
|
|
@@ -39,6 +41,13 @@ function FlagItem(props: RecommendMoodItemProps) {
|
|
|
39
41
|
lineHeight: cx(20),
|
|
40
42
|
flex: 1
|
|
41
43
|
},
|
|
44
|
+
checkbox: {
|
|
45
|
+
width: cx(45),
|
|
46
|
+
height: cx(45),
|
|
47
|
+
marginTop: cx(-5),
|
|
48
|
+
marginBottom: cx(-10),
|
|
49
|
+
fontWeight: 'bold',
|
|
50
|
+
},
|
|
42
51
|
gradientItem: {
|
|
43
52
|
alignItems: 'center',
|
|
44
53
|
},
|
|
@@ -55,10 +64,9 @@ function FlagItem(props: RecommendMoodItemProps) {
|
|
|
55
64
|
{props.title ? <Text style={styles.headText}>{props.title}</Text> : undefined}
|
|
56
65
|
{props.icon ? <Image source={icon} style={{ width: cx(60), aspectRatio: 2.14, marginRight: cx(10) }} /> : undefined}
|
|
57
66
|
</View>
|
|
58
|
-
<
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
onValueChange={props.onSwitch} />
|
|
67
|
+
<TouchableOpacity style={styles.checkbox} onPress={() => props.onSwitch(!props.enable)}>
|
|
68
|
+
<Image source={{ uri: res.ic_check}} width={cx(44)} height={cx(44)} style={[styles.checkbox, { tintColor: props.enable ? props.theme?.icon.primary : props.theme?.icon.disable}]} />
|
|
69
|
+
</TouchableOpacity>
|
|
62
70
|
</View>
|
|
63
71
|
<Spacer />
|
|
64
72
|
<View style={styles.gradientItem}>
|
|
@@ -149,31 +149,109 @@ export interface PowerDataItem {
|
|
|
149
149
|
value: number
|
|
150
150
|
}
|
|
151
151
|
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
152
|
+
// 常量抽离(便于维护)
|
|
153
|
+
const DATA_POINT_INTERVAL_SEC = 30; // 预设数据间隔(秒)
|
|
154
|
+
const TIME_UNIT = 'minute' as const; // 时间单位(与 interval 配合)
|
|
155
|
+
const RETRY_CONFIG = {
|
|
156
|
+
maxRetries: 5,
|
|
157
|
+
initialDelay: 1000,
|
|
158
|
+
maxDelay: 30000,
|
|
159
|
+
backoffFactor: 2,
|
|
160
|
+
} as const;
|
|
161
|
+
|
|
162
|
+
/**
|
|
163
|
+
* 创建一个0值填充的数据点
|
|
164
|
+
* @param timePoint Dayjs对象
|
|
165
|
+
* @returns PowerDataItem
|
|
166
|
+
*/
|
|
167
|
+
function createZeroPaddingPoint(timePoint: dayjs.Dayjs): PowerDataItem {
|
|
168
|
+
const timeMs = timePoint.valueOf();
|
|
169
|
+
return {
|
|
170
|
+
key: timePoint.format('HH:mm:ss'),
|
|
171
|
+
chartTitle: timePoint.format('MM/DD/YYYY HH:mm:ss'),
|
|
172
|
+
time: timeMs,
|
|
173
|
+
value: 0,
|
|
174
|
+
};
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* 获取设备功率数据(智能填充,高性能,避免在小间隔内插入0值)
|
|
179
|
+
* @param devId 设备ID
|
|
180
|
+
* @param powerDpCode 功率数据点编码
|
|
181
|
+
* @param interval 时间区间(分钟)
|
|
182
|
+
* @returns 按时间排序的完整数据
|
|
183
|
+
*/
|
|
184
|
+
export async function getPowerData(
|
|
185
|
+
devId: string,
|
|
186
|
+
powerDpCode: string,
|
|
187
|
+
interval: number
|
|
188
|
+
): Promise<PowerDataItem[]> {
|
|
189
|
+
try {
|
|
190
|
+
const now = dayjs();
|
|
191
|
+
const endTime = now;
|
|
192
|
+
const startTime = now.add(-interval, TIME_UNIT);
|
|
193
|
+
const endTimeMs = endTime.valueOf();
|
|
194
|
+
const startTimeMs = startTime.valueOf();
|
|
195
|
+
const paddingIntervalMs = DATA_POINT_INTERVAL_SEC * 1000;
|
|
196
|
+
// 1. 请求已排序的实际数据
|
|
197
|
+
const dpResult = await getSpecifiedTimeDpReportLogs(
|
|
198
|
+
devId,
|
|
199
|
+
[powerDpCode],
|
|
200
|
+
'ASC',
|
|
201
|
+
startTimeMs.toString(),
|
|
202
|
+
endTimeMs.toString(),
|
|
203
|
+
RETRY_CONFIG
|
|
204
|
+
);
|
|
205
|
+
const validDpResult = Array.isArray(dpResult) ? (dpResult as DpReportSataData[]) : [];
|
|
206
|
+
const actualData: PowerDataItem[] = validDpResult
|
|
207
|
+
.map((dp) => {
|
|
208
|
+
const timeMs = dp.timeStamp * 1000;
|
|
209
|
+
if (timeMs < startTimeMs || timeMs > endTimeMs) return null;
|
|
210
|
+
return {
|
|
211
|
+
key: dayjs.unix(dp.timeStamp).format('HH:mm:ss'),
|
|
212
|
+
chartTitle: dayjs.unix(dp.timeStamp).format('MM/DD/YYYY HH:mm:ss'),
|
|
213
|
+
time: timeMs,
|
|
214
|
+
value: parseFloat(dp.value) / 10,
|
|
215
|
+
};
|
|
216
|
+
})
|
|
217
|
+
.filter((item): item is PowerDataItem => item !== null);
|
|
218
|
+
// 如果没有任何真实数据,则生成完整的预设0值数据作为兜底
|
|
219
|
+
if (actualData.length === 0) {
|
|
220
|
+
const finalData: PowerDataItem[] = [];
|
|
221
|
+
let currentTime = startTime;
|
|
222
|
+
while (currentTime.valueOf() < endTimeMs) {
|
|
223
|
+
finalData.push(createZeroPaddingPoint(currentTime));
|
|
224
|
+
currentTime = currentTime.add(DATA_POINT_INTERVAL_SEC, 'second');
|
|
225
|
+
}
|
|
226
|
+
return finalData;
|
|
167
227
|
}
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
228
|
+
// 2. 高性能线性填充
|
|
229
|
+
const finalData: PowerDataItem[] = [];
|
|
230
|
+
let lastTimeMs = startTimeMs; // 游标从查询区间的开始时间算起
|
|
231
|
+
// 遍历所有真实数据点
|
|
232
|
+
actualData.forEach((currentPoint) => {
|
|
233
|
+
// 从上一个点的时间开始,用 while 循环填充,直到下一个真实数据点之前
|
|
234
|
+
let paddingTimeMs = lastTimeMs + paddingIntervalMs;
|
|
235
|
+
while (paddingTimeMs < currentPoint.time) {
|
|
236
|
+
finalData.push(createZeroPaddingPoint(dayjs(paddingTimeMs)));
|
|
237
|
+
paddingTimeMs += paddingIntervalMs;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
// 添加当前的真实数据点
|
|
241
|
+
finalData.push(currentPoint);
|
|
242
|
+
lastTimeMs = currentPoint.time; // 更新游标
|
|
243
|
+
});
|
|
244
|
+
// 3. 填充查询末尾的空白区域(从最后一个真实数据点到查询结束时间)
|
|
245
|
+
let paddingTimeMs = lastTimeMs + paddingIntervalMs;
|
|
246
|
+
while (paddingTimeMs < endTimeMs) {
|
|
247
|
+
finalData.push(createZeroPaddingPoint(dayjs(paddingTimeMs)));
|
|
248
|
+
paddingTimeMs += paddingIntervalMs;
|
|
175
249
|
}
|
|
176
|
-
|
|
250
|
+
return finalData;
|
|
251
|
+
} catch (error) {
|
|
252
|
+
console.error(`[getPowerData] 失败:devId=${devId}, powerDpCode=${powerDpCode}`, error);
|
|
253
|
+
return [];
|
|
254
|
+
}
|
|
177
255
|
}
|
|
178
256
|
|
|
179
257
|
export const exportEnergyCsv = (values: any[][], unit: string) => {
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import InfoText from '@ledvance/base/src/components/InfoText'
|
|
2
|
+
import Spacer from '@ledvance/base/src/components/Spacer'
|
|
3
|
+
import res from '@ledvance/base/src/res'
|
|
4
|
+
import React from 'react'
|
|
5
|
+
import { Image, View } from 'react-native'
|
|
6
|
+
import { Utils } from 'tuya-panel-kit'
|
|
7
|
+
|
|
8
|
+
const { convertX: cx } = Utils.RatioUtils
|
|
9
|
+
|
|
10
|
+
export const EmptyDataView = ({ text, theme, styles, height }) => (
|
|
11
|
+
<View style={[styles.listEmptyView, { height }]}>
|
|
12
|
+
<Image style={styles.listEmptyImage} source={{ uri: res.ldv_timer_empty }}/>
|
|
13
|
+
<Spacer height={cx(5)}/>
|
|
14
|
+
<InfoText
|
|
15
|
+
text={text}
|
|
16
|
+
icon={res.ic_info}
|
|
17
|
+
textStyle={styles.listEmptyText}
|
|
18
|
+
contentColor={theme?.global.fontColor}
|
|
19
|
+
/>
|
|
20
|
+
</View>
|
|
21
|
+
)
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import I18n from '@ledvance/base/src/i18n'
|
|
2
|
+
import res from '@ledvance/base/src/res'
|
|
3
|
+
import React, { useCallback } from 'react'
|
|
4
|
+
import { Image, TouchableOpacity, View } from 'react-native'
|
|
5
|
+
import { Utils } from 'tuya-panel-kit'
|
|
6
|
+
import DateSelectedItem from '../component/DateSelectedItem'
|
|
7
|
+
import DateSwitch from '../component/DateSwitch'
|
|
8
|
+
import { DateType } from '../co2Data'
|
|
9
|
+
import DateTypeItem from '../component/DateTypeItem'
|
|
10
|
+
import NewBarChart from '../component/NewBarChart'
|
|
11
|
+
import { EmptyDataView } from './EmptyDataView'
|
|
12
|
+
import { exportEnergyCsv } from '../EnergyConsumptionActions'
|
|
13
|
+
|
|
14
|
+
const { convertX: cx } = Utils.RatioUtils
|
|
15
|
+
|
|
16
|
+
export const EnergyChartSection = ({ isLandscape, state, actions, params, styles, theme, chartHeight }) => {
|
|
17
|
+
const getEmptyDataTip = useCallback(() => {
|
|
18
|
+
if (state.over365Days && state.dateType !== DateType.Day) {
|
|
19
|
+
return I18n.getLang('energyconsumption_Daylimit')
|
|
20
|
+
}
|
|
21
|
+
if (state.dateType === DateType.Day && state.over7Days) {
|
|
22
|
+
return I18n.getLang('energyconsumption_hourlylimit')
|
|
23
|
+
}
|
|
24
|
+
return I18n.getLang('energyconsumption_emptydata')
|
|
25
|
+
}, [state.dateType, state.over365Days, state.over7Days])
|
|
26
|
+
|
|
27
|
+
const isDataEmpty = state.chartData.length <= 0
|
|
28
|
+
|
|
29
|
+
const handleExportCsv = useCallback(() => {
|
|
30
|
+
const values = state.chartData.map(item => [item.key, item.value, (Number(params.price) * Number(item.value)).toFixed(2)])
|
|
31
|
+
exportEnergyCsv(values, params.unit)
|
|
32
|
+
}, [state.chartData, params.price, params.unit])
|
|
33
|
+
|
|
34
|
+
return (
|
|
35
|
+
<>
|
|
36
|
+
{!isLandscape && (
|
|
37
|
+
<View style={styles.dateSwitchContainer}>
|
|
38
|
+
<DateSwitch
|
|
39
|
+
style={{ flex: 1 }}
|
|
40
|
+
date={state.date}
|
|
41
|
+
dateType={state.dateType}
|
|
42
|
+
headlineText={state.headlineText}
|
|
43
|
+
onDateChange={actions.setDate}
|
|
44
|
+
/>
|
|
45
|
+
<TouchableOpacity style={{ width: cx(30) }} onPress={handleExportCsv}>
|
|
46
|
+
<Image
|
|
47
|
+
style={styles.downloadIcon}
|
|
48
|
+
source={{ uri: !isDataEmpty ? res.download_icon : undefined }}
|
|
49
|
+
/>
|
|
50
|
+
</TouchableOpacity>
|
|
51
|
+
</View>
|
|
52
|
+
)}
|
|
53
|
+
<View style={styles.dateTypeContainer}>
|
|
54
|
+
<DateTypeItem
|
|
55
|
+
style={{ flex: 1, marginHorizontal: cx(5) }}
|
|
56
|
+
dateType={state.dateType}
|
|
57
|
+
onDateTypeChange={actions.setDateType}
|
|
58
|
+
/>
|
|
59
|
+
<DateSelectedItem
|
|
60
|
+
style={{ flex: 1 }}
|
|
61
|
+
dateType={state.dateType}
|
|
62
|
+
date={state.date}
|
|
63
|
+
onDateChange={actions.setDate}
|
|
64
|
+
/>
|
|
65
|
+
</View>
|
|
66
|
+
|
|
67
|
+
{(state.loading || isDataEmpty) ? (
|
|
68
|
+
<EmptyDataView text={getEmptyDataTip()} theme={theme} styles={styles}/>
|
|
69
|
+
) : (
|
|
70
|
+
!state.loading &&
|
|
71
|
+
<NewBarChart height={chartHeight} data={state.chartData} price={state.price} unit={params.unit}/>
|
|
72
|
+
)}
|
|
73
|
+
</>
|
|
74
|
+
)
|
|
75
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import I18n from '@ledvance/base/src/i18n'
|
|
2
|
+
import res from '@ledvance/base/src/res'
|
|
3
|
+
import React from 'react'
|
|
4
|
+
import { Image, Text, TouchableOpacity, View } from 'react-native'
|
|
5
|
+
import { Utils } from 'tuya-panel-kit'
|
|
6
|
+
import { ChartType } from '../co2Data'
|
|
7
|
+
import { EnergyChartSection } from './EnergyChartSection'
|
|
8
|
+
import { PowerChartSection } from './PowerChartSection'
|
|
9
|
+
|
|
10
|
+
const { convertX: cx, width } = Utils.RatioUtils
|
|
11
|
+
|
|
12
|
+
export const LandscapeView = ({ state, actions, params, styles, theme }) => (
|
|
13
|
+
<View style={styles.landscapeContainer}>
|
|
14
|
+
<View style={styles.landscapeHeader}>
|
|
15
|
+
<Text style={styles.landscapeTitle}>
|
|
16
|
+
{I18n.getLang(state.chartType === ChartType.kWh ? 'chartdisplay_energy' : 'chartdisplay_power')}
|
|
17
|
+
</Text>
|
|
18
|
+
<TouchableOpacity onPress={actions.toggleLandscape}>
|
|
19
|
+
<Image source={{ uri: res.screen_normal }} style={styles.normalScreenIcon}/>
|
|
20
|
+
</TouchableOpacity>
|
|
21
|
+
</View>
|
|
22
|
+
|
|
23
|
+
<View style={styles.landscapeContent}>
|
|
24
|
+
{state.chartType === ChartType.kWh ? (
|
|
25
|
+
<EnergyChartSection
|
|
26
|
+
isLandscape={true}
|
|
27
|
+
state={state}
|
|
28
|
+
actions={actions}
|
|
29
|
+
params={params}
|
|
30
|
+
styles={styles}
|
|
31
|
+
theme={theme}
|
|
32
|
+
chartHeight={cx(width - 110)}
|
|
33
|
+
/>
|
|
34
|
+
) : (
|
|
35
|
+
<PowerChartSection
|
|
36
|
+
isLandscape={true}
|
|
37
|
+
state={state}
|
|
38
|
+
actions={actions}
|
|
39
|
+
styles={styles}
|
|
40
|
+
theme={theme}
|
|
41
|
+
chartHeight={cx(width - 110)}
|
|
42
|
+
/>
|
|
43
|
+
)}
|
|
44
|
+
</View>
|
|
45
|
+
</View>
|
|
46
|
+
)
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import Page from '@ledvance/base/src/components/Page'
|
|
2
|
+
import Segmented from '@ledvance/base/src/components/Segmented'
|
|
3
|
+
import Spacer from '@ledvance/base/src/components/Spacer'
|
|
4
|
+
import I18n from '@ledvance/base/src/i18n'
|
|
5
|
+
import res from '@ledvance/base/src/res'
|
|
6
|
+
import React from 'react'
|
|
7
|
+
import { ScrollView } from 'react-native'
|
|
8
|
+
import { Utils } from 'tuya-panel-kit'
|
|
9
|
+
import { ChartType } from '../co2Data'
|
|
10
|
+
import { EnergyChartSection } from './EnergyChartSection'
|
|
11
|
+
import { PowerChartSection } from './PowerChartSection'
|
|
12
|
+
|
|
13
|
+
const { convertX: cx } = Utils.RatioUtils
|
|
14
|
+
|
|
15
|
+
export const PortraitView = ({ state, actions, params, styles, theme }) => (
|
|
16
|
+
<Page
|
|
17
|
+
backText={params.backTitle}
|
|
18
|
+
showGreenery={false}
|
|
19
|
+
loading={state.loading}
|
|
20
|
+
greeneryIcon={res.energy_consumption_greenery}
|
|
21
|
+
rightButtonIcon={state.isSupportLandscape ? res.screen_full : undefined}
|
|
22
|
+
rightButtonStyle={styles.fullScreenIcon}
|
|
23
|
+
rightButtonIconClick={actions.toggleLandscape}
|
|
24
|
+
>
|
|
25
|
+
<ScrollView nestedScrollEnabled={true} style={styles.scrollViewContent}>
|
|
26
|
+
<Spacer/>
|
|
27
|
+
<Segmented
|
|
28
|
+
options={[
|
|
29
|
+
{ label: I18n.getLang('chartdisplay_energy'), value: ChartType.kWh },
|
|
30
|
+
{ label: I18n.getLang('chartdisplay_power'), value: ChartType.Watt },
|
|
31
|
+
]}
|
|
32
|
+
value={state.chartType}
|
|
33
|
+
onChange={actions.setChartType}
|
|
34
|
+
/>
|
|
35
|
+
{state.chartType === ChartType.kWh ? (
|
|
36
|
+
<EnergyChartSection state={state} actions={actions} params={params} styles={styles} theme={theme}
|
|
37
|
+
chartHeight={cx(400)}/>
|
|
38
|
+
) : (
|
|
39
|
+
<PowerChartSection state={state} actions={actions} styles={styles} theme={theme} chartHeight={cx(400)}/>
|
|
40
|
+
)}
|
|
41
|
+
</ScrollView>
|
|
42
|
+
</Page>
|
|
43
|
+
)
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import I18n from '@ledvance/base/src/i18n'
|
|
2
|
+
import React from 'react'
|
|
3
|
+
import { Text, TouchableOpacity, View } from 'react-native'
|
|
4
|
+
import PowerLineChart from '../component/PowerLineChart'
|
|
5
|
+
import { EmptyDataView } from './EmptyDataView'
|
|
6
|
+
|
|
7
|
+
const INTERVAL_OPTIONS = [
|
|
8
|
+
{ label: 'charttime_type1', value: 24 * 60 },
|
|
9
|
+
{ label: 'charttime_type2', value: 6 * 60 },
|
|
10
|
+
{ label: 'charttime_type3', value: 60 },
|
|
11
|
+
{ label: 'charttime_type4', value: 5 },
|
|
12
|
+
]
|
|
13
|
+
|
|
14
|
+
export const PowerChartSection = ({ isLandscape, state, actions, styles, theme, chartHeight }) => {
|
|
15
|
+
const isDataEmpty = state.powerData.length <= 0
|
|
16
|
+
|
|
17
|
+
return (
|
|
18
|
+
<>
|
|
19
|
+
{isDataEmpty ? (
|
|
20
|
+
<EmptyDataView text={I18n.getLang('power_chart_empty')} theme={theme} styles={styles} height={chartHeight}/>
|
|
21
|
+
) : (
|
|
22
|
+
<PowerLineChart height={chartHeight} data={state.powerData}/>
|
|
23
|
+
)}
|
|
24
|
+
<View style={[styles.intervalContainer, isLandscape && styles.landscapeIntervalContainer]}>
|
|
25
|
+
{INTERVAL_OPTIONS.map(({ label, value }) => {
|
|
26
|
+
const isChecked = value === state.interval
|
|
27
|
+
return (
|
|
28
|
+
<TouchableOpacity
|
|
29
|
+
key={value}
|
|
30
|
+
style={[styles.intervalItem, isChecked && styles.intervalItemChecked]}
|
|
31
|
+
onPress={() => actions.setInterval(value)}
|
|
32
|
+
>
|
|
33
|
+
<Text style={isChecked ? styles.intervalTextChecked : styles.intervalText}>
|
|
34
|
+
{I18n.getLang(label)}
|
|
35
|
+
</Text>
|
|
36
|
+
</TouchableOpacity>
|
|
37
|
+
)
|
|
38
|
+
})}
|
|
39
|
+
</View>
|
|
40
|
+
</>
|
|
41
|
+
)
|
|
42
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import ThemeType from '@ledvance/base/src/config/themeType'
|
|
2
|
+
import { useDeviceId } from '@ledvance/base/src/models/modules/NativePropsSlice'
|
|
3
|
+
import { useRoute } from '@react-navigation/core'
|
|
4
|
+
import React, { useMemo } from 'react'
|
|
5
|
+
import { Utils } from 'tuya-panel-kit'
|
|
6
|
+
import { EnergyConsumptionChartProps } from '../EnergyConsumptionChart'
|
|
7
|
+
import { LandscapeView } from './LandscapeView'
|
|
8
|
+
import { PortraitView } from './PortraitView'
|
|
9
|
+
import { getStyles } from './styles'
|
|
10
|
+
|
|
11
|
+
import { useEnergyData } from './useEnergyData'
|
|
12
|
+
|
|
13
|
+
const { withTheme } = Utils.ThemeUtils
|
|
14
|
+
|
|
15
|
+
const EnergyConsumptionChartComponent = (props: { theme?: ThemeType }) => {
|
|
16
|
+
const devId = useDeviceId()
|
|
17
|
+
const params = useRoute().params as EnergyConsumptionChartProps
|
|
18
|
+
|
|
19
|
+
// Use the custom Hook to get all state and logic
|
|
20
|
+
const { state, actions } = useEnergyData(params, devId)
|
|
21
|
+
|
|
22
|
+
// Use useMemo to prevent re-creating styles on every render
|
|
23
|
+
const styles = useMemo(() => getStyles(props.theme), [props.theme])
|
|
24
|
+
|
|
25
|
+
// Conditionally render the correct view based on orientation state
|
|
26
|
+
if (state.isLandscape) {
|
|
27
|
+
return <LandscapeView state={state} actions={actions} params={params} styles={styles} theme={props.theme}/>
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
return <PortraitView state={state} actions={actions} params={params} styles={styles} theme={props.theme}/>
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export default withTheme(EnergyConsumptionChartComponent)
|
|
@@ -0,0 +1,95 @@
|
|
|
1
|
+
import ThemeType from '@ledvance/base/src/config/themeType'
|
|
2
|
+
import { StyleSheet } from 'react-native'
|
|
3
|
+
import { Utils } from 'tuya-panel-kit'
|
|
4
|
+
|
|
5
|
+
const { convertX: cx, width, height } = Utils.RatioUtils
|
|
6
|
+
|
|
7
|
+
export const getStyles = (theme: ThemeType | undefined) => StyleSheet.create({
|
|
8
|
+
listEmptyView: {
|
|
9
|
+
alignItems: 'center',
|
|
10
|
+
justifyContent: 'center',
|
|
11
|
+
},
|
|
12
|
+
listEmptyImage: {
|
|
13
|
+
width: cx(180),
|
|
14
|
+
height: cx(180),
|
|
15
|
+
},
|
|
16
|
+
listEmptyText: {
|
|
17
|
+
flex: 0,
|
|
18
|
+
},
|
|
19
|
+
downloadIcon: {
|
|
20
|
+
width: cx(24),
|
|
21
|
+
height: cx(24),
|
|
22
|
+
tintColor: theme?.global.brand,
|
|
23
|
+
position: 'absolute',
|
|
24
|
+
right: 0,
|
|
25
|
+
top: cx(10),
|
|
26
|
+
},
|
|
27
|
+
intervalContainer: {
|
|
28
|
+
flexDirection: 'row',
|
|
29
|
+
justifyContent: 'center',
|
|
30
|
+
alignItems: 'center',
|
|
31
|
+
marginTop: cx(-10),
|
|
32
|
+
},
|
|
33
|
+
intervalItem: {
|
|
34
|
+
flex: 1,
|
|
35
|
+
marginHorizontal: cx(5),
|
|
36
|
+
padding: cx(5),
|
|
37
|
+
borderWidth: cx(1),
|
|
38
|
+
borderRadius: cx(25),
|
|
39
|
+
borderColor: theme?.icon.normal,
|
|
40
|
+
},
|
|
41
|
+
intervalItemChecked: {
|
|
42
|
+
borderColor: theme?.icon.primary,
|
|
43
|
+
},
|
|
44
|
+
intervalText: {
|
|
45
|
+
textAlign: 'center',
|
|
46
|
+
color: theme?.global.fontColor,
|
|
47
|
+
},
|
|
48
|
+
intervalTextChecked: {
|
|
49
|
+
textAlign: 'center',
|
|
50
|
+
color: theme?.icon.primary,
|
|
51
|
+
},
|
|
52
|
+
landscapeContainer: {
|
|
53
|
+
backgroundColor: theme?.global.background,
|
|
54
|
+
width: cx(height + 20), // In landscape, width is screen height
|
|
55
|
+
// height: width, // In landscape, height is screen width
|
|
56
|
+
paddingVertical: cx(30),
|
|
57
|
+
paddingHorizontal: cx(20),
|
|
58
|
+
},
|
|
59
|
+
landscapeHeader: {
|
|
60
|
+
flexDirection: 'row',
|
|
61
|
+
justifyContent: 'space-between',
|
|
62
|
+
alignItems: 'center',
|
|
63
|
+
},
|
|
64
|
+
landscapeTitle: {
|
|
65
|
+
color: theme?.global.fontColor,
|
|
66
|
+
fontSize: cx(16),
|
|
67
|
+
fontWeight: 'bold',
|
|
68
|
+
},
|
|
69
|
+
landscapeContent: {
|
|
70
|
+
height: cx(width - 120),
|
|
71
|
+
},
|
|
72
|
+
landscapeIntervalContainer: {
|
|
73
|
+
marginTop: cx(-10),
|
|
74
|
+
},
|
|
75
|
+
dateSwitchContainer: {
|
|
76
|
+
width: '100%',
|
|
77
|
+
flexDirection: 'row',
|
|
78
|
+
marginVertical: cx(15),
|
|
79
|
+
},
|
|
80
|
+
dateTypeContainer: {
|
|
81
|
+
flexDirection: 'row',
|
|
82
|
+
marginBottom: cx(15),
|
|
83
|
+
},
|
|
84
|
+
scrollViewContent: {
|
|
85
|
+
marginHorizontal: cx(24),
|
|
86
|
+
},
|
|
87
|
+
fullScreenIcon: {
|
|
88
|
+
tintColor: theme?.global.brand,
|
|
89
|
+
},
|
|
90
|
+
normalScreenIcon: {
|
|
91
|
+
width: cx(50),
|
|
92
|
+
height: cx(50),
|
|
93
|
+
tintColor: theme?.icon.normal
|
|
94
|
+
}
|
|
95
|
+
})
|