@ledvance/ui-biz-bundle 1.1.118 → 1.1.120

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/ui-biz-bundle",
5
5
  "pid": [],
6
6
  "uiid": "",
7
- "version": "1.1.118",
7
+ "version": "1.1.120",
8
8
  "scripts": {},
9
9
  "dependencies": {
10
10
  "@ledvance/base": "^1.x",
@@ -1,18 +1,19 @@
1
1
  import React from 'react'
2
- import { SwitchButton, Utils } from "tuya-panel-kit";
2
+ import {Utils} from "tuya-panel-kit";
3
3
  import Page from "@ledvance/base/src/components/Page";
4
4
  import I18n from "@ledvance/base/src/i18n/index";
5
- import { useDeviceInfo, useDp } from "@ledvance/base/src/models/modules/NativePropsSlice";
6
- import { useReactive } from "ahooks";
7
- import Spacer from "@ledvance/base/src/components/Spacer";
8
- import { View, Text, StyleSheet, Image } from "react-native";
9
- import { Result } from "@ledvance/base/src/models/modules/Result";
5
+ import {useDeviceInfo, useDp} from "@ledvance/base/src/models/modules/NativePropsSlice";
6
+ import {useReactive} from "ahooks";
7
+ import {View, Text, StyleSheet, Image} from "react-native";
8
+ import {Result} from "@ledvance/base/src/models/modules/Result";
10
9
  import res from "@ledvance/base/src/res";
11
- import { useParams } from "@ledvance/base/src/hooks/Hooks";
10
+ import {useParams} from "@ledvance/base/src/hooks/Hooks";
12
11
  import ThemeType from '@ledvance/base/src/config/themeType'
12
+ import Card from "@ledvance/base/src/components/Card";
13
+ import LdvSwitch from "@ledvance/base/src/components/ldvSwitch";
13
14
 
14
- const { convertX: cx } = Utils.RatioUtils
15
- const { withTheme } = Utils.ThemeUtils
15
+ const {convertX: cx} = Utils.RatioUtils
16
+ const {withTheme} = Utils.ThemeUtils
16
17
 
17
18
  export interface ChildLockPageParams {
18
19
  childLockCode: string
@@ -44,18 +45,6 @@ const ChildLockPage = (props: { theme?: ThemeType }) => {
44
45
  marginRight: cx(5),
45
46
  tintColor: props.theme?.global.fontColor
46
47
  },
47
- titleBGView: {
48
- flexDirection: 'row',
49
- alignItems: 'center',
50
- paddingHorizontal: cx(16),
51
- marginHorizontal: cx(24),
52
- },
53
- colorBlock: {
54
- width: cx(20),
55
- height: cx(20),
56
- marginStart: cx(12),
57
- borderRadius: cx(4),
58
- },
59
48
  title: {
60
49
  color: props.theme?.global.fontColor,
61
50
  fontSize: cx(14),
@@ -81,22 +70,24 @@ const ChildLockPage = (props: { theme?: ThemeType }) => {
81
70
  headlineText={I18n.getLang('sockets_specific_settings_child_lock')}
82
71
  loading={state.loading}>
83
72
  <View style={styles.tipInfoContainer}>
84
- <Image style={styles.image} source={res.ic_info} />
85
- <Text style={{ color: props.theme?.global.fontColor }}>
73
+ <Image style={styles.image} source={res.ic_info}/>
74
+ <Text style={{color: props.theme?.global.fontColor}}>
86
75
  {params.descriptionText ? params.descriptionText : I18n.getLang('childlock_overview_description_text')}
87
76
  </Text>
88
77
  </View>
89
- <View style={[styles.titleBGView, styles.shadow]}>
90
- <Text style={styles.title}>{I18n.getLang('sockets_specific_settings_child_lock')}</Text>
91
- <View style={[styles.colorBlock, { backgroundColor: 'red', opacity: 0 }]} />
92
- <Spacer style={{ flex: 1 }} height={0} width={0} />
93
- <SwitchButton value={childLock} onValueChange={async v => {
94
- state.loading = true
95
- await setChildLock(v)
96
- params.switchTriggerEvent && params.switchTriggerEvent(v)
97
- state.loading = false
98
- }} />
99
- </View>
78
+ <Card style={{marginHorizontal: cx(24)}}>
79
+ <LdvSwitch
80
+ title={I18n.getLang('sockets_specific_settings_child_lock')}
81
+ colorAlpha={0}
82
+ enable={childLock}
83
+ setEnable={async (v: boolean) => {
84
+ state.loading = true
85
+ await setChildLock(v)
86
+ params.switchTriggerEvent && params.switchTriggerEvent(v)
87
+ state.loading = false
88
+ }}
89
+ />
90
+ </Card>
100
91
  </Page>)
101
92
  }
102
93
 
@@ -203,9 +203,9 @@ const DiySceneEditorPage = (props: { theme?: ThemeType }) => {
203
203
  state.sceneInfo.type !== 'DIY' && <TouchableOpacity onPress={() => {
204
204
  toggleLoveScene(state.sceneInfo.id)
205
205
  }}>
206
- {isLove ? <Image source={res.like} style={{width: cx(24), height: cx(24)}}/>
206
+ {isLove ? <Image source={res.like} style={{width: cx(30), height: cx(26)}}/>
207
207
  : <Image source={res.un_like}
208
- style={{width: cx(24), height: cx(24), tintColor: props.theme?.global.fontColor}}/>}
208
+ style={{width: cx(30), height: cx(26), tintColor: props.theme?.global.fontColor}}/>}
209
209
  </TouchableOpacity>
210
210
  }
211
211
  rightButtonIcon={canSaveMoodData ? res.ic_check : res.ic_uncheck}
@@ -142,3 +142,36 @@ export const exportEnergyCsv = (values: any[][], unit: string) => {
142
142
  const functionName = `${I18n.getLang('consumption_data_annual_bar_chart_system_back_text')}`
143
143
  exportCsvFile(headers, values, functionName)
144
144
  }
145
+
146
+ const EnergyGenerationId = 'EnergyGeneration'
147
+
148
+ export interface EnergyHistory {
149
+ time: number
150
+ generationMode: boolean
151
+ type: 'Consumption' | 'Generation'
152
+ value: number
153
+ }
154
+
155
+ export interface EnergyGeneration {
156
+ generationMode: boolean
157
+ totalElectricity: number
158
+ history: EnergyHistory[]
159
+ }
160
+
161
+ export async function setEnergyGenerationValue(devId: string, data: EnergyGeneration) {
162
+ const res = await NativeApi.putJson(devId, EnergyGenerationId, JSON.stringify(data))
163
+ if (!res.success) {
164
+ console.log('setEnergyGenerationValue failed', res)
165
+ }
166
+ return res.success
167
+ }
168
+
169
+ export async function getEnergyGenerationValue(devId: string): Promise<EnergyGeneration | undefined> {
170
+ const res = await NativeApi.getJson(devId, EnergyGenerationId)
171
+ if (res.success && res.data) {
172
+ return JSON.parse(res.data)
173
+ } else {
174
+ console.log('getEnergyGenerationValue failed', res)
175
+ return undefined
176
+ }
177
+ }
@@ -10,7 +10,7 @@ import Card from "@ledvance/base/src/components/Card";
10
10
  import OverView from "./component/Overview";
11
11
  import {useReactive, useUpdateEffect} from "ahooks";
12
12
  import {getDataWithSpecified, getDpResultByHour} from "@ledvance/base/src/models/TuyaApi";
13
- import {useDeviceId, useEngergyGeneration, useTimeZoneCity} from "@ledvance/base/src/models/modules/NativePropsSlice";
13
+ import {useDeviceId, useSolarPlug, useTimeZoneCity} from "@ledvance/base/src/models/modules/NativePropsSlice";
14
14
  import {cloneDeep, isEmpty} from "lodash";
15
15
  import {OverviewItem, PopupType} from "./EnergyConsumptionPage";
16
16
  import {exchangeNumber, localeNumber, loopsText} from '@ledvance/base/src/utils/common'
@@ -37,7 +37,7 @@ export interface EnergyConsumptionDetailProps {
37
37
  const EnergyConsumptionDetail = (props: {theme?: ThemeType}) => {
38
38
  const params = useRoute().params as EnergyConsumptionDetailProps
39
39
  const devId = useDeviceId()
40
- const isSolarPlug = useEngergyGeneration()
40
+ const isSolarPlug = useSolarPlug()
41
41
  const navigation = useNavigation()
42
42
  const timeZoneCity = useTimeZoneCity()
43
43
  const state = useReactive({
@@ -11,10 +11,17 @@ import Card from "@ledvance/base/src/components/Card";
11
11
  import OverView from "./component/Overview";
12
12
  import { useInterval, useReactive, useUpdateEffect } from "ahooks";
13
13
  import { getDpResultByMonth } from "@ledvance/base/src/models/TuyaApi";
14
- import {useDeviceId, useEngergyGeneration, useTimeZoneCity} from "@ledvance/base/src/models/modules/NativePropsSlice";
14
+ import {useDeviceId, useSolarPlug, useTimeZoneCity} from "@ledvance/base/src/models/modules/NativePropsSlice";
15
15
  import { flattenDeep, isEmpty } from "lodash";
16
- import { exchangeNumber, exportFile, localeNumber, monthFormat, monthFormatShort } from "@ledvance/base/src/utils/common";
17
- import {exportEnergyCsv, updatePrice, useElectricCurrent, usePower, useVoltage} from "./EnergyConsumptionActions";
16
+ import { exchangeNumber, localeNumber, monthFormat, monthFormatShort, showDialog } from "@ledvance/base/src/utils/common";
17
+ import {
18
+ EnergyGeneration, EnergyHistory,
19
+ exportEnergyCsv, getEnergyGenerationValue, setEnergyGenerationValue,
20
+ updatePrice,
21
+ useElectricCurrent,
22
+ usePower,
23
+ useVoltage
24
+ } from "./EnergyConsumptionActions";
18
25
  import {ui_biz_routerKey} from "../../navigation/Routers";
19
26
  import { EnergyConsumptionDetailProps } from "./EnergyConsumptionDetail";
20
27
  import { EnergyConsumptionChartProps } from "./EnergyConsumptionChart";
@@ -22,6 +29,8 @@ import EnergyPopup, { EnergyData, UnitList } from "./component/EnergyModal";
22
29
  import { NativeApi, queryDpIds } from "@ledvance/base/src/api/native";
23
30
  import { carbonDioxideEmission, countryAndRegion } from "./co2Data";
24
31
  import ThemeType from '@ledvance/base/src/config/themeType'
32
+ import LdvSwitch from "@ledvance/base/src/components/ldvSwitch";
33
+ import dayjs from "dayjs";
25
34
 
26
35
  const { convertX: cx } = Utils.RatioUtils
27
36
  const { withTheme } = Utils.ThemeUtils
@@ -39,7 +48,8 @@ export interface OverviewItem {
39
48
  headlineText: string
40
49
  chartTitle: string
41
50
  }
42
- export type PopupType = 'co2' | 'money' | 'unit' | ''
51
+
52
+ export type PopupType = 'co2' | 'money' | 'unit' | 'history' | ''
43
53
 
44
54
  interface EnergyConsumptionState {
45
55
  todayElectricity: string
@@ -50,6 +60,9 @@ interface EnergyConsumptionState {
50
60
  showPopup: boolean
51
61
  popupType: PopupType
52
62
  co2Saved: string
63
+ loading: boolean
64
+ generationMode: boolean
65
+ energyGeneration: EnergyGeneration
53
66
  }
54
67
  const EnergyConsumptionPage = (props: {theme?: ThemeType}) => {
55
68
  const params = useRoute().params as EnergyConsumptionPageProps
@@ -60,7 +73,7 @@ const EnergyConsumptionPage = (props: {theme?: ThemeType}) => {
60
73
  const power = usePower(params.powerDpCode)
61
74
  const voltage = useVoltage(params.voltageDpCode)
62
75
  const electric = useElectricCurrent(params.electricDpCode)
63
- const isSolarPlug = useEngergyGeneration()
76
+ const isSolarPlug = useSolarPlug()
64
77
  const state = useReactive<EnergyConsumptionState>({
65
78
  todayElectricity: '0',
66
79
  totalElectricity: '0',
@@ -69,7 +82,14 @@ const EnergyConsumptionPage = (props: {theme?: ThemeType}) => {
69
82
  unit: UnitList[0],
70
83
  showPopup: false,
71
84
  popupType: '',
72
- co2Saved: '0'
85
+ co2Saved: '0',
86
+ loading: false,
87
+ generationMode: false,
88
+ energyGeneration: {
89
+ generationMode: false,
90
+ totalElectricity: 0,
91
+ history: []
92
+ }
73
93
  })
74
94
  const chartHeadline = useMemo(() => {
75
95
  const len = state.overviewList.length
@@ -89,21 +109,74 @@ const EnergyConsumptionPage = (props: {theme?: ThemeType}) => {
89
109
  useEffect(() => {
90
110
  getElectricity().then()
91
111
  getInitPrice().then()
112
+ getEnergyGeneration().then()
92
113
  }, [])
93
114
 
115
+ const isGeneration = useMemo(() => {
116
+ // 当 isSolarPlug 和 state.generationMode 不相等时返回 true
117
+ return isSolarPlug !== state.generationMode
118
+ }, [isSolarPlug, state.generationMode])
119
+
120
+ const totalElectricity = useMemo(() => {
121
+ return Math.max(Number(state.totalElectricity) - (state.energyGeneration.totalElectricity || 0), 0)
122
+ }, [state.totalElectricity, state.energyGeneration.totalElectricity])
123
+
94
124
  useUpdateEffect(() =>{
95
- if(Number(state.totalElectricity) > 0 && timeZoneCity){
125
+ if(totalElectricity > 0 && timeZoneCity){
96
126
  const letOut = carbonDioxideEmission(timeZoneCity, countryAndRegion) || 0
97
- state.co2Saved = localeNumber((letOut * Number(state.totalElectricity)) / 1000, 4)
127
+ state.co2Saved = localeNumber((letOut * totalElectricity) / 1000, 4)
128
+ }
129
+ }, [totalElectricity, timeZoneCity])
130
+
131
+ const getEnergyGeneration = async () => {
132
+ const data = await getEnergyGenerationValue(devId)
133
+ if (data) {
134
+ state.energyGeneration = data
135
+ state.generationMode = data.generationMode
136
+ } else {
137
+ state.energyGeneration = {
138
+ generationMode: false,
139
+ totalElectricity: 0,
140
+ history: []
141
+ }
98
142
  }
99
- }, [state.totalElectricity, timeZoneCity])
143
+ }
144
+
145
+ const setEnergyGeneration = async (value: boolean) => {
146
+ state.loading = true
147
+ await getElectricity()
148
+ const time = dayjs().startOf('day').valueOf()
149
+ const item: EnergyHistory = {
150
+ time,
151
+ generationMode: value,
152
+ value: Number(state.totalElectricity) || 0,
153
+ type: isSolarPlug ? (value ? 'Consumption' : 'Generation') : (value ? 'Generation' : 'Consumption')
154
+ }
155
+ const data: EnergyGeneration = {
156
+ generationMode: value,
157
+ totalElectricity: Number(state.totalElectricity) || 0,
158
+ history: [item, ...state.energyGeneration.history]
159
+ }
160
+ const res = await setEnergyGenerationValue(devId, data)
161
+ if (res) {
162
+ state.generationMode = value
163
+ state.energyGeneration = data
164
+ } else {
165
+ state.generationMode = !value
166
+ }
167
+ state.loading = false
168
+ }
169
+
170
+ const onceByDay = () => {
171
+ const currentDay = dayjs().startOf('day').valueOf()
172
+ return !!state.energyGeneration.history.find(item => item.time === currentDay)
173
+ }
100
174
 
101
175
  const getElectricity = async () => {
102
176
  const res = await getDpResultByMonth(devId, params.addEleDpCode, 'sum')
103
177
  if (!isEmpty(res)) {
104
178
  state.todayElectricity = res.thisDay
105
179
  state.totalElectricity = res.sum
106
- console.log(res, '< --- res')
107
180
  if (!isEmpty(res.years)) {
108
181
  const yearsList = Object.keys(res.years)
109
182
  yearsList.sort((a, b) => parseInt(b) - parseInt(a))
@@ -142,6 +215,13 @@ const EnergyConsumptionPage = (props: {theme?: ThemeType}) => {
142
215
  state.unit = data.unit
143
216
  }
144
217
 
218
+ const getEnergyModeTitle = (value: boolean) => {
219
+ const generationTitle = I18n.getLang('switchtitle_energygeneration', 'Do you really want to switch to energy generation?')
220
+ const consumptionTitle = I18n.getLang('switchtitle_energyconsumption', 'Do you really want to switch to energy consumption?')
221
+ const titleMapping = isSolarPlug ? [consumptionTitle, generationTitle] : [generationTitle, consumptionTitle]
222
+ return titleMapping[value ? 0 : 1]
223
+ }
224
+
145
225
  const styles = StyleSheet.create({
146
226
  showTip: {
147
227
  marginHorizontal: cx(24)
@@ -222,190 +302,240 @@ const EnergyConsumptionPage = (props: {theme?: ThemeType}) => {
222
302
 
223
303
  const ConsumedEnergyItem = (props: { value: number, unit: string }) => {
224
304
  return (
225
- <View style={styles.subContent}>
226
- <Text style={styles.valueText}>{(props.value) || 0}</Text>
227
- <Spacer height={cx(4)} />
228
- <Text style={styles.titleText}>
229
- {unitDivision(props.unit)[0]}
230
- </Text>
231
- <Text style={styles.titleText}>
232
- {unitDivision(props.unit)[1]}
233
- </Text>
234
- </View>
305
+ <View style={styles.subContent}>
306
+ <Text style={styles.valueText}>{(props.value) || 0}</Text>
307
+ <Spacer height={cx(4)} />
308
+ <Text style={styles.titleText}>
309
+ {unitDivision(props.unit)[0]}
310
+ </Text>
311
+ <Text style={styles.titleText}>
312
+ {unitDivision(props.unit)[1]}
313
+ </Text>
314
+ </View>
235
315
  )
236
316
  }
237
317
 
238
318
  return (
239
- <Page
240
- style={{ position: 'relative' }}
241
- backText={I18n.getLang(isSolarPlug ? 'sockets_headline_power' : 'consumption_data_annual_bar_chart_system_back_text')}
242
- headlineText={I18n.getLang(isSolarPlug ? 'sockets_headline_power' : 'consumption_data_annual_bar_chart_system_back_text')}
243
- headlineIcon={state.overviewList.length ? res.download_icon : undefined}
244
- onHeadlineIconClick={() => {
245
- const values = state.overviewList.map(item => [item.key, item.value, (Number(state.price) * Number(item.value)).toFixed(2)])
246
- exportEnergyCsv(values, state.unit)
247
- }}
248
- showGreenery={isSolarPlug}
249
- greeneryIcon={res.energy_consumption_greenery}
250
- >
251
- <ScrollView nestedScrollEnabled={true}>
252
- <View>
253
- {/* tip */}
319
+ <Page
320
+ style={{ position: 'relative' }}
321
+ backText={I18n.getLang(isGeneration ? 'sockets_headline_power' : 'consumption_data_annual_bar_chart_system_back_text')}
322
+ headlineText={I18n.getLang(isGeneration ? 'sockets_headline_power' : 'consumption_data_annual_bar_chart_system_back_text')}
323
+ headlineIcon={state.overviewList.length ? res.download_icon : undefined}
324
+ onHeadlineIconClick={() => {
325
+ const values = state.overviewList.map(item => [item.key, item.value, (Number(state.price) * Number(item.value)).toFixed(2)])
326
+ exportEnergyCsv(values, state.unit)
327
+ }}
328
+ showGreenery={isGeneration}
329
+ greeneryIcon={res.energy_consumption_greenery}
330
+ loading={state.loading}
331
+ >
332
+ <ScrollView nestedScrollEnabled={true}>
333
+ <View>
334
+ {/* tip */}
335
+ <Spacer height={cx(15)} />
336
+
337
+ <View style={styles.showTip}>
338
+ <Text style={{ fontSize: cx(14), color: props.theme?.global.fontColor, }}>{I18n.getLang(isGeneration ? 'generation_data_description_text' : 'consumption_data_description_text')}</Text>
339
+ </View>
340
+ <Spacer />
341
+ {/* Today */}
342
+ <Card
343
+ style={styles.cardContainer}
344
+ >
345
+ <Text style={styles.cardTitle}>{I18n.getLang('consumption_data_field1_headline_text')}</Text>
254
346
  <Spacer height={cx(15)} />
255
- <View style={styles.showTip}>
256
- <Text style={{ fontSize: cx(14), color: props.theme?.global.fontColor, }}>{I18n.getLang(isSolarPlug ? 'generation_data_description_text' : 'consumption_data_description_text')}</Text>
347
+ <View style={{ flexDirection: 'row', alignItems: 'flex-end' }}>
348
+ <Text style={[styles.consumptionNum, { fontSize: cx(38), marginRight: cx(8) }]}>{localeNumber(state.todayElectricity)}</Text>
349
+ <Text style={[styles.consumptionNum, { fontSize: cx(22), marginBottom: cx(4) }]}>kWh</Text>
257
350
  </View>
258
- <Spacer />
259
- {/* Today */}
260
- <Card
261
- style={styles.cardContainer}
262
- >
263
- <Text style={styles.cardTitle}>{I18n.getLang('consumption_data_field1_headline_text')}</Text>
264
- <Spacer height={cx(15)} />
265
- <View style={{ flexDirection: 'row', alignItems: 'flex-end' }}>
266
- <Text style={[styles.consumptionNum, { fontSize: cx(38), marginRight: cx(8) }]}>{localeNumber(state.todayElectricity)}</Text>
267
- <Text style={[styles.consumptionNum, { fontSize: cx(22), marginBottom: cx(4) }]}>kWh</Text>
268
- </View>
269
- <Spacer height={cx(10)} />
270
- </Card>
271
- <Spacer />
272
- <Card
273
- style={styles.cardContainer}
274
- >
275
- <Text style={styles.cardTitle}>{I18n.getLang('consumption_data_field2_headline_text')}</Text>
276
- <Spacer height={cx(15)} />
277
- <View style={styles.consumedEnergyContent}>
278
- <ConsumedEnergyItem
279
- value={power}
280
- unit={I18n.getLang('consumption_data_field2_value_text1')} />
281
- <ConsumedEnergyItem
282
- value={electric}
283
- unit={I18n.getLang('consumption_data_field2_value_text2')} />
284
- <ConsumedEnergyItem
285
- value={voltage}
286
- unit={I18n.getLang('consumption_data_field2_value_text3')} />
287
- </View>
288
- </Card>
289
- <Spacer />
290
- {/* 365 day */}
291
- <Card
292
- style={styles.cardContainer}
293
- >
351
+ <Spacer height={cx(10)} />
352
+ </Card>
353
+ <Spacer />
354
+ <Card
355
+ style={styles.cardContainer}
356
+ >
357
+ <Text style={styles.cardTitle}>{I18n.getLang('consumption_data_field2_headline_text')}</Text>
358
+ <Spacer height={cx(15)} />
359
+ <View style={styles.consumedEnergyContent}>
360
+ <ConsumedEnergyItem
361
+ value={power}
362
+ unit={I18n.getLang('consumption_data_field2_value_text1')} />
363
+ <ConsumedEnergyItem
364
+ value={electric}
365
+ unit={I18n.getLang('consumption_data_field2_value_text2')} />
366
+ <ConsumedEnergyItem
367
+ value={voltage}
368
+ unit={I18n.getLang('consumption_data_field2_value_text3')} />
369
+ </View>
370
+ </Card>
371
+ <Spacer />
372
+ {/* 365 day */}
373
+ <Card
374
+ style={styles.cardContainer}
375
+ >
376
+ <View style={{ flexDirection: 'row', alignItems: 'center' }}>
294
377
  <Text style={styles.cardTitle}>{I18n.getLang('consumption_data_field3_headline_text')}</Text>
295
- <Spacer height={cx(15)} />
296
- <View style={{ flexDirection: 'row', alignItems: 'flex-end' }}>
297
- <Text style={[styles.consumptionNum, { fontSize: cx(38), marginRight: cx(8) }]}>{localeNumber(state.totalElectricity)}</Text>
298
- <Text style={[styles.consumptionNum, { fontSize: cx(22), marginBottom: cx(4) }]}>kWh</Text>
299
- </View>
300
- <Spacer />
301
- {/* CO2 */}
302
- {isSolarPlug && <>
378
+ { state.energyGeneration.history.length > 0 && <TouchableOpacity
379
+ onPress={() => {
380
+ state.showPopup = true
381
+ state.popupType = 'history'
382
+ }}
383
+ >
384
+ <Image
385
+ source={res.co2Icon}
386
+ resizeMode="contain"
387
+ style={{ height: cx(20), width: cx(20), tintColor: props.theme?.button.primary }}
388
+ />
389
+ </TouchableOpacity>}
390
+ </View>
391
+ <Spacer height={cx(15)} />
392
+ <View style={{ flexDirection: 'row', alignItems: 'flex-end' }}>
393
+ <Text style={[styles.consumptionNum, { fontSize: cx(38), marginRight: cx(8) }]}>{localeNumber(totalElectricity)}</Text>
394
+ <Text style={[styles.consumptionNum, { fontSize: cx(22), marginBottom: cx(4) }]}>kWh</Text>
395
+ </View>
396
+ <Spacer />
397
+ {/* CO2 */}
398
+ {isGeneration && <>
303
399
  <View style={{ flexDirection: 'row', alignItems: 'center' }}>
304
- <View style={styles.priceBg}>
305
- <Image
306
- source={res.energy_consumption_cash}
307
- resizeMode="contain"
308
- style={{ height: cx(20), width: cx(20), tintColor: props.theme?.button.primary }}
309
- />
310
- </View>
311
- <View style={styles.priceNum}>
312
- <View style={{ flexDirection: 'row' }}>
313
- <Text style={{ color: props.theme?.global.secondFontColor, marginRight: cx(5) }}>{I18n.getLang('consumption_data_field3_co2_topic_text')}</Text>
314
- <TouchableOpacity
315
- onPress={() => {
316
- state.showPopup = true
317
- state.popupType = 'co2'
318
- }}
319
- >
400
+ <View style={styles.priceBg}>
320
401
  <Image
321
- source={res.co2Icon}
402
+ source={res.energy_consumption_cash}
322
403
  resizeMode="contain"
323
404
  style={{ height: cx(20), width: cx(20), tintColor: props.theme?.button.primary }}
324
405
  />
325
- </TouchableOpacity>
326
406
  </View>
327
- <Text style={{ color: props.theme?.global.fontColor, fontWeight: 'bold' }}>{`${state.co2Saved} kg`}</Text>
328
- </View>
407
+ <View style={styles.priceNum}>
408
+ <View style={{ flexDirection: 'row' }}>
409
+ <Text style={{ color: props.theme?.global.secondFontColor, marginRight: cx(5) }}>{I18n.getLang('consumption_data_field3_co2_topic_text')}</Text>
410
+ <TouchableOpacity
411
+ onPress={() => {
412
+ state.showPopup = true
413
+ state.popupType = 'co2'
414
+ }}
415
+ >
416
+ <Image
417
+ source={res.co2Icon}
418
+ resizeMode="contain"
419
+ style={{ height: cx(20), width: cx(20), tintColor: props.theme?.button.primary }}
420
+ />
421
+ </TouchableOpacity>
422
+ </View>
423
+ <Text style={{ color: props.theme?.global.fontColor, fontWeight: 'bold' }}>{`${state.co2Saved} kg`}</Text>
424
+ </View>
329
425
  </View>
330
426
  <Spacer height={cx(10)} />
331
- </>}
332
- {/* money */}
333
- <View style={{ flexDirection: 'row', alignItems: 'center' }}>
334
- <View style={styles.priceBg}>
335
- <Image
336
- source={res.energy_consumption_cash}
337
- resizeMode="contain"
338
- style={{ height: cx(20), width: cx(20), tintColor: props.theme?.button.primary }}
339
- />
427
+ </>}
428
+ {/* money */}
429
+ <View style={{ flexDirection: 'row', alignItems: 'center' }}>
430
+ <View style={styles.priceBg}>
431
+ <Image
432
+ source={res.energy_consumption_cash}
433
+ resizeMode="contain"
434
+ style={{ height: cx(20), width: cx(20), tintColor: props.theme?.button.primary }}
435
+ />
436
+ </View>
437
+ <View>
438
+ <View style={styles.priceNum}>
439
+ <Text style={{ color: props.theme?.global.secondFontColor }}>{I18n.getLang(isGeneration ? 'consumption_data_monthly_overview_field1_text2' : 'consumption_data_field3_value_text2')}</Text>
440
+ <Text style={{ color: props.theme?.global.fontColor, fontWeight: 'bold' }}>{state.price ? `${localeNumber(Number(state.price ) * totalElectricity, 2)} ${state.unit}` : '-'}</Text>
340
441
  </View>
341
- <View>
342
- <View style={styles.priceNum}>
343
- <Text style={{ color: props.theme?.global.secondFontColor }}>{I18n.getLang(isSolarPlug ? 'consumption_data_monthly_overview_field1_text2' : 'consumption_data_field3_value_text2')}</Text>
344
- <Text style={{ color: props.theme?.global.fontColor, fontWeight: 'bold' }}>{state.price ? `${localeNumber(Number(state.price ) * Number(state.totalElectricity), 2)} ${state.unit}` : '-'}</Text>
442
+ <TouchableOpacity onPress={() => {
443
+ state.showPopup = true
444
+ state.popupType = 'money'
445
+ }}>
446
+ <View style={styles.priceButton}>
447
+ <Text style={{ color: props.theme?.button.fontColor }}>{I18n.getLang('consumption_data_field3_button_text')}</Text>
345
448
  </View>
346
- <TouchableOpacity onPress={() => {
347
- state.showPopup = true
348
- state.popupType = 'money'
349
- }}>
350
- <View style={styles.priceButton}>
351
- <Text style={{ color: props.theme?.button.fontColor }}>{I18n.getLang('consumption_data_field3_button_text')}</Text>
352
- </View>
353
- </TouchableOpacity>
354
- </View>
449
+ </TouchableOpacity>
355
450
  </View>
356
- </Card>
357
- <Spacer height={cx(30)} />
358
- {/* Annual overview */}
359
- {!!state.price && <OverView
360
- style={{marginHorizontal: cx(24)}}
361
- headlineText={I18n.getLang('consumption_data_field4_headline_text')}
362
- headlineClick={() => {
363
- navigation.navigate(ui_biz_routerKey.ui_biz_energy_consumption_chart, {
364
- headlineText: chartHeadline,
365
- chartData: state.overviewList,
366
- price: state.price,
367
- unit: state.unit,
368
- addEleDpCode: params.addEleDpCode,
369
- date: (new Date()).getFullYear().toString(),
370
- } as EnergyConsumptionChartProps)
371
- }}
372
- overviewItemClick={(item) => {
373
- navigation.navigate(ui_biz_routerKey.ui_biz_energy_consumption_detail, {
374
- addEleDpCode: params.addEleDpCode,
375
- curMonth: item,
376
- price: state.price,
377
- unit: state.unit,
378
- updateEnergyData
379
- } as EnergyConsumptionDetailProps)
380
- }}
381
- overViewList={state.overviewList}
382
- />}
383
- {/* modal */}
384
- <EnergyPopup
385
- visible={!!(state.popupType && state.showPopup)}
386
- popupType={state.popupType || 'co2'}
387
- title={state.popupType === 'co2' ? I18n.getLang('consumption_data_field3_co2_topic_text') : ''}
388
- cancelText={state.popupType === 'co2' ? '' : I18n.getLang("auto_scan_system_cancel")}
389
- confirmText={I18n.getLang(state.popupType === 'co2' ? 'home_screen_home_dialog_yes_con' : 'auto_scan_system_wifi_confirm')}
390
- energyData={{ price: state.price, unit: state.unit }}
391
- onConfirm={(energyData) => {
392
- state.popupType = ''
393
- state.showPopup = false
394
- if(energyData){
395
- updateEnergyData({
396
- ...energyData,
397
- price: exchangeNumber(energyData.price)
398
- })
451
+ </View>
452
+ </Card>
453
+ <Spacer />
454
+ <Card style={{marginHorizontal: cx(24)}}>
455
+ <LdvSwitch
456
+ title={isSolarPlug ? I18n.getLang('plug_energyconsumptionswitch', 'Switch to energy consumption mode') : I18n.getLang('plug_energygenerationswitch', 'Switch to energy generation mode')}
457
+ colorAlpha={0}
458
+ enable={state.generationMode}
459
+ setEnable={async (value: boolean) => {
460
+ if (state.loading) {
461
+ return
462
+ }
463
+ if (onceByDay()) {
464
+ showDialog({
465
+ method: 'alert',
466
+ title: I18n.getLang('switchonlyonce', 'Switching the mode is only allowed once a day.'),
467
+ confirmText: I18n.getLang('home_screen_home_dialog_yes_con'),
468
+ onConfirm: (_, {close}) => {
469
+ close()
470
+ }
471
+ })
472
+ return
473
+ }
474
+ showDialog({
475
+ method: 'confirm',
476
+ title: getEnergyModeTitle(value),
477
+ subTitle: I18n.getLang('switchdescription_energy', 'Note that the total energy data will be resetted and cannot be recovered again.'),
478
+ onConfirm: async (_, { close }) => {
479
+ close()
480
+ await setEnergyGeneration(value)
399
481
  }
400
- }}
401
- onCancel={() => {
402
- state.popupType = ''
403
- state.showPopup = false
404
- }}
482
+ })
483
+ }}
405
484
  />
406
- </View>
407
- </ScrollView>
408
- </Page>
485
+ </Card>
486
+ <Spacer />
487
+ {/* Annual overview */}
488
+ {!!state.price && <OverView
489
+ style={{marginHorizontal: cx(24)}}
490
+ headlineText={I18n.getLang('consumption_data_field4_headline_text')}
491
+ headlineClick={() => {
492
+ navigation.navigate(ui_biz_routerKey.ui_biz_energy_consumption_chart, {
493
+ headlineText: chartHeadline,
494
+ chartData: state.overviewList,
495
+ price: state.price,
496
+ unit: state.unit,
497
+ addEleDpCode: params.addEleDpCode,
498
+ date: (new Date()).getFullYear().toString(),
499
+ } as EnergyConsumptionChartProps)
500
+ }}
501
+ overviewItemClick={(item) => {
502
+ navigation.navigate(ui_biz_routerKey.ui_biz_energy_consumption_detail, {
503
+ addEleDpCode: params.addEleDpCode,
504
+ curMonth: item,
505
+ price: state.price,
506
+ unit: state.unit,
507
+ updateEnergyData
508
+ } as EnergyConsumptionDetailProps)
509
+ }}
510
+ overViewList={state.overviewList}
511
+ />}
512
+ {/* modal */}
513
+ <EnergyPopup
514
+ visible={!!(state.popupType && state.showPopup)}
515
+ popupType={state.popupType || 'co2'}
516
+ title={state.popupType === 'co2' ? I18n.getLang('consumption_data_field3_co2_topic_text') : state.popupType === 'history' ? I18n.getLang('group_energytotal') : ''}
517
+ cancelText={['co2', 'history'].includes(state.popupType) ? '' : I18n.getLang("auto_scan_system_cancel")}
518
+ confirmText={I18n.getLang(['co2', 'history'].includes(state.popupType) ? 'home_screen_home_dialog_yes_con' : 'auto_scan_system_wifi_confirm')}
519
+ energyData={{ price: state.price, unit: state.unit }}
520
+ energyHistory={state.energyGeneration.history}
521
+ onConfirm={(energyData) => {
522
+ state.popupType = ''
523
+ state.showPopup = false
524
+ if(energyData){
525
+ updateEnergyData({
526
+ ...energyData,
527
+ price: exchangeNumber(energyData.price)
528
+ })
529
+ }
530
+ }}
531
+ onCancel={() => {
532
+ state.popupType = ''
533
+ state.showPopup = false
534
+ }}
535
+ />
536
+ </View>
537
+ </ScrollView>
538
+ </Page>
409
539
  )
410
540
  }
411
541
 
@@ -9,6 +9,8 @@ import { Utils, Modal, Popup } from "tuya-panel-kit";
9
9
  import { useReactive, useUpdateEffect } from "ahooks";
10
10
  import { cloneDeep } from "lodash";
11
11
  import ThemeType from '@ledvance/base/src/config/themeType'
12
+ import dayjs from "dayjs";
13
+ import {EnergyHistory} from "../EnergyConsumptionActions";
12
14
 
13
15
  const { convertX: cx, height } = Utils.RatioUtils
14
16
  const { withTheme } = Utils.ThemeUtils
@@ -33,14 +35,16 @@ export interface EnergyData {
33
35
  price: string
34
36
  unit: string
35
37
  }
38
+
36
39
  interface EnergyModalProps {
37
40
  theme?: ThemeType
38
41
  visible: boolean
39
- popupType: 'money' | 'co2' | 'unit'
42
+ popupType: 'money' | 'co2' | 'unit' | 'history'
40
43
  title: string
41
44
  confirmText?: string
42
45
  cancelText?: string
43
46
  energyData?: EnergyData
47
+ energyHistory?: EnergyHistory[]
44
48
  motionType?: 'none'
45
49
  onConfirm?: (data?: EnergyData) => void
46
50
  onCancel?: () => void
@@ -51,29 +55,49 @@ const EnergyModal = (props: EnergyModalProps) => {
51
55
  unitPopup: false
52
56
  })
53
57
 
54
-
55
58
  const openLink = (url: string) => {
56
59
  Linking.openURL(url).catch((error) => console.error('无法打开链接:', error));
57
60
  };
58
61
 
59
62
  const getDescription = (string: string) => {
60
- const separators = /[.:]/;
61
- const text = string.split(separators)
62
- const length = text.length - 1
63
- return <View>
64
- <Text style={{ color: props.theme?.global.fontColor }}>{text[length - 6] + '.'}</Text>
65
- <Spacer />
66
- <Text style={{ color: props.theme?.global.fontColor }}>{text[length - 5] + '.'}</Text>
67
- <Spacer />
68
- <Text style={{ color: props.theme?.global.fontColor }}>{text[length - 4] + text[length - 3] + ':'}</Text>
69
- <Spacer />
70
- <Text
71
- style={{ textDecorationLine: 'underline', color: props.theme?.button.primary }}
72
- onPress={() => openLink(`${text[length - 2]}:${text[length - 1]}${text[length]}`)}
73
- >
74
- {`${text[length - 2]}:${text[length - 1]}${text[length]}`}
75
- </Text>
76
- </View>
63
+ // 首先提取URL
64
+ const urlRegex = /(https?:\/\/[^\s]+)$/;
65
+ const urlMatch = string.match(urlRegex);
66
+ const url = urlMatch ? urlMatch[0] : '';
67
+
68
+ // 移除URL部分以处理正文文本
69
+ const textWithoutUrl = urlMatch
70
+ ? string.slice(0, string.lastIndexOf(url)).trim()
71
+ : string;
72
+
73
+ // 分割文本但保留分隔符
74
+ const sentences:string[] = [];
75
+ const parts = textWithoutUrl.split(/([.:])/);
76
+
77
+ for (let i = 0; i < parts.length - 1; i += 2) {
78
+ if (i + 1 < parts.length) {
79
+ sentences.push(parts[i] + parts[i + 1]);
80
+ }
81
+ }
82
+
83
+ return (
84
+ <View>
85
+ {sentences.map((sentence, index) => (
86
+ <View key={index}>
87
+ <Text style={{ color: props.theme?.global.fontColor }}>{sentence.trim()}</Text>
88
+ <Spacer />
89
+ </View>
90
+ ))}
91
+ {url && (
92
+ <Text
93
+ style={{ textDecorationLine: 'underline', color: props.theme?.button.primary }}
94
+ onPress={() => openLink(url)}
95
+ >
96
+ {url}
97
+ </Text>
98
+ )}
99
+ </View>
100
+ );
77
101
  }
78
102
 
79
103
  useUpdateEffect(() =>{
@@ -196,6 +220,23 @@ const EnergyModal = (props: EnergyModalProps) => {
196
220
  </Card>
197
221
  </View>
198
222
  )
223
+ } else if (props.popupType === 'history') {
224
+ return (
225
+ <View>
226
+ <Spacer />
227
+ <Text style={styles.popupTip}>{I18n.getLang('infobutton_totalenergy', 'The total energy shows only the total consumption/generation data after the last reset.\n' +
228
+ 'The data was switched on the following days:')}</Text>
229
+ <Spacer height={cx(20)} />
230
+ {
231
+ props.energyHistory?.map((item) => (
232
+ <View key={item.time} style={{ backgroundColor: props.theme?.card.background, alignItems: 'center', justifyContent: 'space-between', flexDirection: 'row', }}>
233
+ <Text style={{color: props.theme?.global.fontColor}}>{dayjs(item.time).format('DD/MM/YYYY')}</Text>
234
+ <Text style={{color: props.theme?.global.fontColor}}>{item.type === 'Generation' ? I18n.getLang('plug_energygenerationswitch', 'Switch to energy generation mode') : I18n.getLang('plug_energyconsumptionswitch', 'Switch to energy consumption mode')}</Text>
235
+ </View>
236
+ ))
237
+ }
238
+ </View>
239
+ )
199
240
  } else {
200
241
  return (
201
242
  <View>
@@ -1,17 +1,19 @@
1
1
  import React from 'react'
2
- import {SwitchButton, Utils} from "tuya-panel-kit";
2
+ import {Utils} from "tuya-panel-kit";
3
3
  import Page from "@ledvance/base/src/components/Page";
4
4
  import I18n from "@ledvance/base/src/i18n/index";
5
5
  import {useDeviceInfo, useDp} from "@ledvance/base/src/models/modules/NativePropsSlice";
6
6
  import {useReactive} from "ahooks";
7
7
  import Spacer from "@ledvance/base/src/components/Spacer";
8
- import {StyleSheet, Text, View} from "react-native";
8
+ import {StyleSheet, Text} from "react-native";
9
9
  import {Result} from "@ledvance/base/src/models/modules/Result";
10
10
  import {useParams} from "@ledvance/base/src/hooks/Hooks";
11
11
  import ThemeType from '@ledvance/base/src/config/themeType'
12
+ import Card from "@ledvance/base/src/components/Card";
13
+ import LdvSwitch from "@ledvance/base/src/components/ldvSwitch";
12
14
 
13
- const { convertX: cx } = Utils.RatioUtils
14
- const { withTheme } = Utils.ThemeUtils
15
+ const {convertX: cx} = Utils.RatioUtils
16
+ const {withTheme} = Utils.ThemeUtils
15
17
 
16
18
  export interface OverchargeSwitchPageParams {
17
19
  overchargeSwitchCode: string
@@ -30,55 +32,34 @@ const OverchargeSwitchPage = (props: { theme?: ThemeType }) => {
30
32
  })
31
33
 
32
34
  const styles = StyleSheet.create({
33
- titleBGView: {
34
- flexDirection: 'row',
35
- alignItems: 'center',
36
- paddingHorizontal: cx(16),
37
- marginHorizontal: cx(24),
38
- marginTop: cx(30)
39
- },
40
35
  title: {
41
36
  color: props.theme?.global.fontColor,
42
- fontSize: cx(14),
37
+ fontSize: cx(16),
38
+ fontWeight: 'bold',
43
39
  fontFamily: 'helvetica_neue_lt_std_bd',
44
- paddingVertical: cx(16),
45
- },
46
- desc: {
47
- color: props.theme?.global.fontColor,
48
- },
49
- shadow: {
50
- shadowColor: props.theme?.card.shadowColor,
51
- shadowOpacity: 0.2,
52
- shadowRadius: 8,
53
- elevation:8,
54
- shadowOffset: {
55
- width: 0,
56
- height: 4,
57
- },
58
- backgroundColor: props.theme?.card.background,
59
- borderRadius: 8,
40
+ paddingTop: cx(16),
41
+ paddingHorizontal: cx(16),
60
42
  },
61
43
  })
62
44
 
63
45
  return (<Page
64
46
  backText={devInfo.name}
65
47
  loading={state.loading}>
66
- <View style={[styles.titleBGView, styles.shadow]}>
67
- <View style={{ flex: 7 }}>
68
- <View>
69
- <Text style={styles.title}>{I18n.getLang('switch_overcharge_headline_text')}</Text>
70
- </View>
71
- <View style={{ paddingBottom: cx(16) }}>
72
- <Text style={styles.desc}>{I18n.getLang('switch_overcharge_headline_description')}</Text>
73
- </View>
74
- </View>
75
- <Spacer style={{ flex: 1 }} height={0} width={0} />
76
- <SwitchButton value={overchargeSwitch} onValueChange={async v => {
77
- state.loading = true
78
- await setOverchargeSwitch(v)
79
- state.loading = false
80
- } } />
81
- </View>
48
+ <Spacer height={cx(24)}/>
49
+ <Card style={{marginHorizontal: cx(24)}}>
50
+ <Text style={styles.title}>{I18n.getLang('switch_overcharge_headline_text')}</Text>
51
+ <LdvSwitch
52
+ title={I18n.getLang('switch_overcharge_headline_description')}
53
+ titleStyle={{fontWeight: 'normal', fontSize: cx(14)}}
54
+ colorAlpha={1}
55
+ enable={overchargeSwitch}
56
+ setEnable={async (v) => {
57
+ state.loading = true
58
+ await setOverchargeSwitch(v)
59
+ state.loading = false
60
+ }}
61
+ />
62
+ </Card>
82
63
  </Page>)
83
64
  }
84
65