@ledvance/ui-biz-bundle 1.1.151 → 1.1.153

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.151",
7
+ "version": "1.1.153",
8
8
  "scripts": {},
9
9
  "dependencies": {
10
10
  "@ledvance/base": "^1.x",
@@ -559,5 +559,53 @@ export const defFlagList: FlagUiInfo[] = [
559
559
  name: I18n.getLang('country_VE'),
560
560
  ...defFlagConfig,
561
561
  colors: [{ h: 0, s: 100, v: 100 }, { h: 205, s: 100, v: 100 }, { h: 60, s: 100, v: 100 },],
562
- }
562
+ },
563
+ {
564
+ id: 168,
565
+ name: I18n.getLang('country_JO'),
566
+ ...defFlagConfig,
567
+ colors: [{"h":150,"s":100,"v":48}, {"h":0,"s":0,"v":100}, {"h":0,"s":0,"v":0},{"h":353,"s":92,"v":81}],
568
+ },
569
+ {
570
+ id: 167,
571
+ name: I18n.getLang('country_CV'),
572
+ ...defFlagConfig,
573
+ colors: [{"h":49,"s":100,"v":100}, {"h":356,"s":79,"v":94}, {"h":0,"s":0,"v":100},{"h":218,"s":100,"v":65}],
574
+ },
575
+ {
576
+ id: 166,
577
+ name: I18n.getLang('country_PA'),
578
+ ...defFlagConfig,
579
+ colors: [{"h":219,"s":92,"v":34}, {"h":0,"s":0,"v":100}, {"h":358,"s":92,"v":85}],
580
+ },
581
+ {
582
+ id: 165,
583
+ name: I18n.getLang('country_northern_ireland'),
584
+ ...defFlagConfig,
585
+ colors: [{"h":0,"s":0,"v":100}, {"h":0,"s":100,"v":82}, {"h":0,"s":0,"v":100}],
586
+ },
587
+ {
588
+ id: 164,
589
+ name: I18n.getLang('country_wales'),
590
+ ...defFlagConfig,
591
+ colors: [{"h":142,"s":100,"v":69}, {"h":350,"s":92,"v":78}, {"h":0,"s":0,"v":100}],
592
+ },
593
+ {
594
+ id: 163,
595
+ name: I18n.getLang('country_BA'),
596
+ ...defFlagConfig,
597
+ colors: [{"h":0,"s":0,"v":100}, {"h":214,"s":100,"v":42}, {"h":48,"s":100,"v":100}],
598
+ },
599
+ {
600
+ id: 162,
601
+ name: I18n.getLang('country_north_macedonia'),
602
+ ...defFlagConfig,
603
+ colors: [{"h":56,"s":81,"v":97}, {"h":358,"s":85,"v":85}],
604
+ },
605
+ {
606
+ id: 161,
607
+ name: I18n.getLang('country_werder_bremen'),
608
+ ...defFlagConfig,
609
+ colors: [{"h":0,"s":0,"v":100}, {"h":160,"s":80,"v":34}],
610
+ },
563
611
  ]
@@ -46,7 +46,6 @@ function FlagItem(props: RecommendMoodItemProps) {
46
46
  height: cx(45),
47
47
  marginTop: cx(-5),
48
48
  marginBottom: cx(-10),
49
- fontWeight: 'bold',
50
49
  },
51
50
  gradientItem: {
52
51
  alignItems: 'center',
@@ -57,7 +56,7 @@ function FlagItem(props: RecommendMoodItemProps) {
57
56
  <Card
58
57
  style={[styles.card, props.style]}
59
58
  onPress={props.onPress}>
60
- <View>
59
+ <View accessibilityLabel={'FlagItem'} accessibilityHint={`${props.title}`}>
61
60
  <Spacer height={cx(16)} />
62
61
  <View style={styles.headline}>
63
62
  <View style={{ flexDirection: 'row', flex: 1, paddingRight: cx(5), justifyContent: 'flex-start' }}>
@@ -207,7 +207,10 @@ const FlagPage = (props: { theme?: ThemeType }) => {
207
207
  placeholder={I18n.getLang('country_selection_textfield_headline_search')}
208
208
  style={{ marginHorizontal: cx(24), flex: 1 }}
209
209
  />
210
- <TouchableOpacity style={{ paddingRight: cx(24) }}
210
+ <TouchableOpacity
211
+ style={{ paddingRight: cx(24) }}
212
+ accessibilityLabel={'RefreshButton'}
213
+ accessibilityHint={'RefreshButton'}
211
214
  onPress={() => {
212
215
  showDialog({
213
216
  method: 'confirm',
@@ -1,13 +1,15 @@
1
1
  import { useNavigation, useRoute } from '@react-navigation/native'
2
- import React, { useEffect, useState } from 'react'
3
- import { Image, ScrollView, Text, TouchableOpacity, View } from 'react-native'
2
+ import React, { useCallback, useEffect, useMemo, useState } from 'react'
3
+ // 导入 Dimensions API 来获取屏幕宽度
4
+ import { Dimensions, Image, ScrollView, StyleSheet, Text, TouchableOpacity, View } from 'react-native'
4
5
  import { Utils } from 'tuya-panel-kit'
5
6
  import iconList from '../biorhythm/iconListData'
6
7
  import LDVTopBar from '@ledvance/base/src/components/ldvTopBar'
7
8
  import I18n from '@ledvance/base/src/i18n'
8
9
  import ThemeType from '@ledvance/base/src/config/themeType'
10
+ import { xLog } from '@ledvance/base/src/utils'
9
11
 
10
- const cx = Utils.RatioUtils.convertX
12
+ const { convertX: cx } = Utils.RatioUtils
11
13
  const { withTheme } = Utils.ThemeUtils
12
14
 
13
15
  interface SceneDetailPageParams {
@@ -17,6 +19,13 @@ interface SceneDetailPageParams {
17
19
  iconIdList: any
18
20
  }
19
21
 
22
+ // --- 新增:定义常量,方便维护 ---
23
+ const ICON_WIDTH = cx(32)
24
+ const ICON_MARGIN = cx(10)
25
+ const CONTAINER_HORIZONTAL_PADDING = cx(24)
26
+ // 每个图标占据的总宽度(图片宽度 + 左右margin)
27
+ const ITEM_TOTAL_WIDTH = ICON_WIDTH + ICON_MARGIN * 2
28
+
20
29
  function IconSelect(props: { theme?: ThemeType }) {
21
30
  const [list, setList] = useState(iconList)
22
31
  const navigation = useNavigation()
@@ -27,7 +36,7 @@ function IconSelect(props: { theme?: ThemeType }) {
27
36
  ...item,
28
37
  selectStatus: item?.id === id,
29
38
  }
30
- })
39
+ }) as typeof list
31
40
  setList(newList)
32
41
  }
33
42
  useEffect(() => {
@@ -39,26 +48,74 @@ function IconSelect(props: { theme?: ThemeType }) {
39
48
  selectStatus: item?.id === iconId,
40
49
  disabled: iconIdList?.some(val => val === item?.id && val !== iconId),
41
50
  }
42
- })
51
+ }) as typeof list
43
52
  setList(newList)
44
53
  }, [])
45
- useEffect(() =>{
46
- console.log(list, '< --- listttt')
47
- }, [list])
54
+
55
+ const getStyles = useCallback((theme?: ThemeType) => StyleSheet.create({
56
+ container: { flex: 1, flexDirection: 'column' },
57
+ scrollView: { marginHorizontal: CONTAINER_HORIZONTAL_PADDING },
58
+ titleView: { marginTop: cx(40), marginBottom: cx(20) },
59
+ title: { fontSize: cx(24), color: theme?.global.brand },
60
+ iconContainer: {
61
+ flexDirection: 'row',
62
+ flex: 1,
63
+ flexWrap: 'wrap',
64
+ justifyContent: 'space-between',
65
+ alignItems: 'center'
66
+ },
67
+ ghostItem: {
68
+ width: ICON_WIDTH,
69
+ height: 0,
70
+ margin: ICON_MARGIN,
71
+ }
72
+ }), [CONTAINER_HORIZONTAL_PADDING, ICON_WIDTH, ICON_MARGIN])
73
+
74
+ const styles = useMemo(() => getStyles(props.theme), [props.theme])
75
+
76
+ // --- 新增:动态计算需要渲染的幽灵元素数量 ---
77
+ const ghostElements = useMemo(() => {
78
+
79
+ // 1. 获取容器的可用宽度
80
+ const containerWidth = cx(Dimensions.get('window').width) - CONTAINER_HORIZONTAL_PADDING * 2
81
+
82
+ // 2. 计算每行可以容纳多少个元素
83
+ const itemsPerRow = Math.ceil(containerWidth / ITEM_TOTAL_WIDTH)
84
+ // 如果无法容纳任何元素,则不渲染幽灵元素
85
+ if (itemsPerRow <= 0) {
86
+ xLog('No ghost elements needed.')
87
+ return null
88
+ }
89
+
90
+ // 3. 计算需要补充的幽灵元素数量
91
+ const numberOfItems = list.length
92
+ const itemsInLastRow = numberOfItems % itemsPerRow
93
+
94
+ // 如果最后一行为空或已满,则不需要幽灵元素
95
+ if (itemsInLastRow === 0) {
96
+ return null
97
+ }
98
+
99
+ const numberOfGhosts = itemsPerRow - itemsInLastRow
100
+
101
+ // 4. 返回一个包含正确数量幽灵元素的数组
102
+ return Array.from({ length: numberOfGhosts }).map((_, index) => (
103
+ <View key={`ghost-${index}`} style={styles.ghostItem}/>
104
+ ))
105
+ }, [list.length]) // 依赖项:当图标总数变化时重新计算
106
+
48
107
  return (
49
- <View style={{ flex: 1, flexDirection: 'column' }}>
108
+ <View style={styles.container}>
50
109
  <LDVTopBar
51
110
  title={params.backText ?? I18n.getLang('add_new_trigger_time_system_back_text')}
52
- onBackPress={() => {
53
- navigation.goBack()
54
- }}
111
+ onBackPress={() => navigation.goBack()}
55
112
  />
56
- <ScrollView nestedScrollEnabled={true} style={{ marginHorizontal: cx(24) }}>
57
- <View style={{ marginTop: cx(40), marginBottom: cx(20) }}>
58
- <Text style={{ fontSize: cx(24), color: props.theme?.global.brand }}>{I18n.getLang('add_new_trigger_time_icon_selection_headline_text')}</Text>
113
+ <ScrollView nestedScrollEnabled={true} style={styles.scrollView}>
114
+ <View style={styles.titleView}>
115
+ <Text style={styles.title}>{I18n.getLang('add_new_trigger_time_icon_selection_headline_text')}</Text>
59
116
  </View>
60
- <View
61
- style={{ flexDirection: 'row', flex: 1, flexWrap: 'wrap', justifyContent: 'space-between', alignItems: 'flex-start' }}>
117
+ <View style={styles.iconContainer}>
118
+ {/* 渲染真实的图标 */}
62
119
  {list?.map(item => {
63
120
  return <TouchableOpacity
64
121
  onPress={() => {
@@ -71,14 +128,17 @@ function IconSelect(props: { theme?: ThemeType }) {
71
128
  <Image
72
129
  source={{ uri: item?.icon }}
73
130
  style={{
74
- width: cx(32),
75
- height: cx(32),
76
- margin: cx(10),
131
+ width: ICON_WIDTH,
132
+ height: cx(32), // 高度保持不变
133
+ margin: ICON_MARGIN,
77
134
  tintColor: item?.selectStatus ? props.theme?.icon.primary : item?.disabled && props.theme?.icon.disable || props.theme?.icon.normal,
78
135
  }}
79
136
  />
80
137
  </TouchableOpacity>
81
138
  })}
139
+
140
+ {/* 渲染精确计算出的幽灵元素 */}
141
+ {ghostElements}
82
142
  </View>
83
143
  </ScrollView>
84
144
  </View>
@@ -61,13 +61,8 @@ export async function getElectricity(devId: string, addEleDpCode: string, date:
61
61
  return res
62
62
  }
63
63
 
64
- let dpResultByMonthCache: DpResultByMonthResData | undefined = undefined
65
64
  const getDpResultByYear = async (devId: string, addEleDpCode: string, dateStr: string): Promise<OverviewItem[]> => {
66
- let res: DpResultByMonthResData | undefined
67
- if (!dpResultByMonthCache) {
68
- dpResultByMonthCache = await getDpResultByMonth(devId, addEleDpCode, 'sum')
69
- }
70
- res = dpResultByMonthCache
65
+ const res = await getDpResultByMonth(devId, addEleDpCode, 'sum')
71
66
  if (!isEmpty(res)) {
72
67
  if (!isEmpty(res.years)) {
73
68
  const year = dateStr
@@ -75,7 +70,7 @@ const getDpResultByYear = async (devId: string, addEleDpCode: string, dateStr: s
75
70
  if (!curMonth) {
76
71
  return []
77
72
  }
78
- const curMonthList = Object.keys(curMonth).sort((a, b) => parseInt(b) - parseInt(a))
73
+ const curMonthList = Object.keys(curMonth).sort((a, b) => parseInt(a) - parseInt(b))
79
74
  return curMonthList.map(month => {
80
75
  return {
81
76
  key: `${monthFormat(month)} ${year}`,
@@ -98,7 +98,7 @@ const EnergyConsumptionCard = (props: EnergyConsumptionProp) => {
98
98
  consumedEnergyItemUnit: {
99
99
  color: props.theme?.global.secondFontColor,
100
100
  fontSize: cx(14),
101
- fontFamily: 'helvetica_neue_lt_std_roman',
101
+ // fontFamily: 'helvetica_neue_lt_std_roman',
102
102
  textAlign: 'center'
103
103
  },
104
104
  subContent: {
@@ -112,13 +112,13 @@ const EnergyConsumptionCard = (props: EnergyConsumptionProp) => {
112
112
  color: props.theme?.global.secondBrand,
113
113
  },
114
114
  titleText: {
115
- fontFamily: 'helvetica_neue_lt_std_roman',
115
+ // fontFamily: 'helvetica_neue_lt_std_roman',
116
116
  fontSize: cx(14),
117
117
  color: props.theme?.global.secondFontColor,
118
118
  textAlign: 'center',
119
119
  },
120
120
  unitText: {
121
- fontFamily: 'helvetica_neue_lt_std_roman',
121
+ // fontFamily: 'helvetica_neue_lt_std_roman',
122
122
  fontSize: cx(14),
123
123
  color: props.theme?.global.secondFontColor,
124
124
  },
@@ -3,7 +3,6 @@ import res from '@ledvance/base/src/res'
3
3
  import React, { useCallback } from 'react'
4
4
  import { Image, TouchableOpacity, View } from 'react-native'
5
5
  import { Utils } from 'tuya-panel-kit'
6
- import DateSelectedItem from '../component/DateSelectedItem'
7
6
  import DateSwitch from '../component/DateSwitch'
8
7
  import { DateType } from '../co2Data'
9
8
  import DateTypeItem from '../component/DateTypeItem'
@@ -33,30 +32,13 @@ export const EnergyChartSection = ({ isLandscape, state, actions, params, styles
33
32
 
34
33
  return (
35
34
  <>
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
35
  <View style={styles.dateTypeContainer}>
54
36
  <DateTypeItem
55
- style={{ flex: 1, marginHorizontal: cx(5) }}
37
+ style={{ flex: 0.5 }}
56
38
  dateType={state.dateType}
57
39
  onDateTypeChange={actions.setDateType}
58
40
  />
59
- <DateSelectedItem
41
+ <DateSwitch
60
42
  style={{ flex: 1 }}
61
43
  dateType={state.dateType}
62
44
  date={state.date}
@@ -67,8 +49,18 @@ export const EnergyChartSection = ({ isLandscape, state, actions, params, styles
67
49
  {(state.loading || isDataEmpty) ? (
68
50
  <EmptyDataView text={getEmptyDataTip()} theme={theme} styles={styles}/>
69
51
  ) : (
70
- !state.loading &&
71
- <NewBarChart height={chartHeight} data={state.chartData} price={state.price} unit={params.unit}/>
52
+ !state.loading && <>
53
+ {!isLandscape && <View style={styles.downloadContainer}>
54
+ <TouchableOpacity
55
+ style={{ width: cx(30) }}
56
+ onPress={handleExportCsv}>
57
+ <Image
58
+ style={styles.downloadIcon}
59
+ source={{ uri: res.download_icon }}/>
60
+ </TouchableOpacity>
61
+ </View>}
62
+ <NewBarChart height={chartHeight} data={state.chartData} price={state.price} unit={params.unit}/>
63
+ </>
72
64
  )}
73
65
  </>
74
66
  )
@@ -16,13 +16,17 @@ export const getStyles = (theme: ThemeType | undefined) => StyleSheet.create({
16
16
  listEmptyText: {
17
17
  flex: 0,
18
18
  },
19
+ downloadContainer: {
20
+ flexDirection: 'row',
21
+ justifyContent: 'flex-end',
22
+ // marginHorizontal: cx(24),
23
+ marginTop: cx(15),
24
+ marginBottom: cx(-15)
25
+ },
19
26
  downloadIcon: {
20
27
  width: cx(24),
21
28
  height: cx(24),
22
29
  tintColor: theme?.global.brand,
23
- position: 'absolute',
24
- right: 0,
25
- top: cx(10),
26
30
  },
27
31
  intervalContainer: {
28
32
  flexDirection: 'row',
@@ -79,7 +83,8 @@ export const getStyles = (theme: ThemeType | undefined) => StyleSheet.create({
79
83
  },
80
84
  dateTypeContainer: {
81
85
  flexDirection: 'row',
82
- marginBottom: cx(15),
86
+ marginTop: cx(15),
87
+ // marginBottom: cx(15),
83
88
  },
84
89
  scrollViewContent: {
85
90
  marginHorizontal: cx(24),
@@ -9,10 +9,12 @@ import { useCallback, useEffect } from 'react'
9
9
  import { ChartType, DateType } from '../co2Data'
10
10
  import { getElectricity, getPowerData, PowerDataItem } from '../EnergyConsumptionActions'
11
11
  import { EnergyConsumptionChartProps } from '../EnergyConsumptionChart'
12
+ import {useIsPad} from "@ledvance/base/src/models/modules/NativePropsSlice"
12
13
 
13
14
  export const useEnergyData = (params: EnergyConsumptionChartProps, devId: string) => {
14
15
  const isFocused = useIsFocused()
15
- const { addEleDpCode, powerDpCode, price, date, over365Days, over7Days, chartData } = params
16
+ const isPad = useIsPad()
17
+ const { addEleDpCode, powerDpCode, price, unit, date, over365Days, over7Days, chartData } = params
16
18
 
17
19
  const getDateType = useCallback((d: string) => {
18
20
  const datejs = dayjs(d)
@@ -26,7 +28,7 @@ export const useEnergyData = (params: EnergyConsumptionChartProps, devId: string
26
28
 
27
29
  const state = useReactive({
28
30
  loading: false,
29
- isSupportLandscape: OrientationService.isSupported(),
31
+ isSupportLandscape: OrientationService.isSupported() && !isPad,
30
32
  isLandscape: false,
31
33
  chartType: ChartType.kWh as ChartType,
32
34
  dateType: getDateType(date),
@@ -34,6 +36,7 @@ export const useEnergyData = (params: EnergyConsumptionChartProps, devId: string
34
36
  headlineText: params.headlineText,
35
37
  chartData: chartData.filter((item) => getDateType(date) !== DateType.Year || item.headlineText.startsWith(date)),
36
38
  price: isNaN(Number(price)) ? 0 : Number(price),
39
+ unit: unit,
37
40
  over365Days: over365Days,
38
41
  over7Days: over7Days,
39
42
  interval: 5,
@@ -49,7 +52,7 @@ export const useEnergyData = (params: EnergyConsumptionChartProps, devId: string
49
52
  }, 5000, { immediate: true })
50
53
 
51
54
  // 副作用:获取数据
52
- useUpdateEffect(() => {
55
+ useEffect(() => {
53
56
  state.loading = true
54
57
  if (state.chartType === ChartType.kWh) {
55
58
  getElectricity(devId, addEleDpCode, state.date, state.dateType)
@@ -143,6 +146,10 @@ export const useEnergyData = (params: EnergyConsumptionChartProps, devId: string
143
146
  OrientationService.lockToPortrait()
144
147
  }
145
148
  }, []),
149
+ setPriceAndUnit: useCallback((newPrice: string, newUnit: string) => {
150
+ state.price = isNaN(Number(newPrice)) ? 0 : Number(newPrice)
151
+ state.unit = newUnit
152
+ }, []),
146
153
  }
147
154
 
148
155
  return { state, actions }
@@ -147,7 +147,7 @@ const EnergyConsumptionDetail = (props: {theme?: ThemeType}) => {
147
147
  },
148
148
  consumptionNum: {
149
149
  color: props.theme?.global.secondBrand,
150
- fontFamily: 'helvetica_neue_lt_std_bd',
150
+ // fontFamily: 'helvetica_neue_lt_std_bd',
151
151
  },
152
152
  subContent: {
153
153
  flex: 1,
@@ -160,7 +160,7 @@ const EnergyConsumptionDetail = (props: {theme?: ThemeType}) => {
160
160
  color: props.theme?.global.secondBrand,
161
161
  },
162
162
  titleText: {
163
- fontFamily: 'helvetica_neue_lt_std_roman',
163
+ // fontFamily: 'helvetica_neue_lt_std_roman',
164
164
  fontSize: cx(14),
165
165
  color: props.theme?.global.secondFontColor,
166
166
  textAlign: 'center',
@@ -1,3 +1,9 @@
1
+ import Segmented from '@ledvance/base/src/components/Segmented'
2
+ import { ChartType } from './co2Data'
3
+ import { EnergyChartSection } from './EnergyConsumptionChart/EnergyChartSection'
4
+ import { PowerChartSection } from './EnergyConsumptionChart/PowerChartSection'
5
+ import { getStyles } from './EnergyConsumptionChart/styles'
6
+ import { useEnergyData } from './EnergyConsumptionChart/useEnergyData'
1
7
  import React, { useEffect, useMemo } from "react";
2
8
  import { ScrollView, StyleSheet, View, Text, Image, TouchableOpacity } from "react-native";
3
9
  import { useNavigation, useIsFocused } from '@react-navigation/core'
@@ -95,6 +101,18 @@ const EnergyConsumptionPage = (props: {theme?: ThemeType}) => {
95
101
  const len = state.overviewList.length
96
102
  return state.overviewList?.length ? `${state.overviewList[len - 1].key} - ${state.overviewList[0].key}` : ''
97
103
  }, [state.overviewList])
104
+ const chartStyles = useMemo(() => getStyles(props.theme), [props.theme])
105
+ const chartParams = useMemo(() => ({
106
+ backTitle,
107
+ headlineText: chartHeadline,
108
+ chartData: state.overviewList,
109
+ price: state.price,
110
+ unit: state.unit,
111
+ addEleDpCode: params.addEleDpCode,
112
+ powerDpCode: params.powerDpCode,
113
+ date: (new Date()).getFullYear().toString(),
114
+ }), [backTitle, chartHeadline, state.overviewList, state.price, state.unit, params.addEleDpCode, params.powerDpCode])
115
+ const { state: chartState, actions } = useEnergyData(chartParams, devId)
98
116
 
99
117
  useInterval(() => {
100
118
  if (isFocused) {
@@ -204,6 +222,7 @@ const EnergyConsumptionPage = (props: {theme?: ThemeType}) => {
204
222
  const v = JSON.parse(res.data)
205
223
  state.price = v?.price || '0'
206
224
  state.unit = v?.unit
225
+ actions.setPriceAndUnit(v?.price, v?.unit || UnitList[0])
207
226
  } else {
208
227
  state.price = '0'
209
228
  }
@@ -213,6 +232,7 @@ const EnergyConsumptionPage = (props: {theme?: ThemeType}) => {
213
232
  updatePrice(devId, data).then()
214
233
  state.price = data.price || '0'
215
234
  state.unit = data.unit
235
+ actions.setPriceAndUnit(data.price, data.unit)
216
236
  }
217
237
 
218
238
  const getEnergyModeTitle = (value: boolean) => {
@@ -246,7 +266,7 @@ const EnergyConsumptionPage = (props: {theme?: ThemeType}) => {
246
266
  },
247
267
  consumptionNum: {
248
268
  color: props.theme?.global.secondBrand,
249
- fontFamily: 'helvetica_neue_lt_std_bd',
269
+ // fontFamily: 'helvetica_neue_lt_std_bd',
250
270
  },
251
271
  subContent: {
252
272
  flex: 1,
@@ -259,7 +279,7 @@ const EnergyConsumptionPage = (props: {theme?: ThemeType}) => {
259
279
  color: props.theme?.global.secondBrand,
260
280
  },
261
281
  titleText: {
262
- fontFamily: 'helvetica_neue_lt_std_roman',
282
+ // fontFamily: 'helvetica_neue_lt_std_roman',
263
283
  fontSize: cx(14),
264
284
  color: props.theme?.global.secondFontColor,
265
285
  textAlign: 'center',
@@ -511,6 +531,23 @@ const EnergyConsumptionPage = (props: {theme?: ThemeType}) => {
511
531
  }}
512
532
  />
513
533
  </Card>
534
+ <Spacer/>
535
+ <ScrollView style={{marginHorizontal: cx(24)}}>
536
+ <Segmented
537
+ options={[
538
+ { label: I18n.getLang('chartdisplay_energy'), value: ChartType.kWh },
539
+ { label: I18n.getLang('chartdisplay_power'), value: ChartType.Watt },
540
+ ]}
541
+ value={chartState.chartType}
542
+ onChange={actions.setChartType}
543
+ />
544
+ {chartState.chartType === ChartType.kWh ? (
545
+ <EnergyChartSection state={chartState} actions={actions} params={chartParams} styles={chartStyles} theme={props.theme}
546
+ chartHeight={cx(400)}/>
547
+ ) : (
548
+ <PowerChartSection state={chartState} actions={actions} styles={chartStyles} theme={props.theme} chartHeight={cx(400)}/>
549
+ )}
550
+ </ScrollView>
514
551
  <Spacer />
515
552
  {/* Annual overview */}
516
553
  {!!state.price && <OverView
@@ -68,7 +68,7 @@ export default withTheme(function DateSelectedItem(props: DateSelectedItemProps)
68
68
  textAlign: 'center',
69
69
  flex: 1,
70
70
  color: props.theme?.textInput.fontColor,
71
- fontFamily: 'helvetica_neue_lt_std_roman',
71
+ // fontFamily: 'helvetica_neue_lt_std_roman',
72
72
  },
73
73
  modalRoot: {paddingTop: cx(20), backgroundColor: theme?.popup.cellBg},
74
74
  modalButtonParent: {flex: 1, height: cx(48)},
@@ -1,6 +1,7 @@
1
1
  import ThemeType from "@ledvance/base/src/config/themeType";
2
+ import DateSelectedItem from './DateSelectedItem'
2
3
  import React, {PropsWithChildren, useCallback, useEffect} from "react";
3
- import {Image, Text, TouchableOpacity, View, ViewProps} from "react-native";
4
+ import {Image, TouchableOpacity, View, ViewProps} from "react-native";
4
5
  import {Utils} from "tuya-panel-kit";
5
6
  import {useReactive} from "ahooks";
6
7
  import {DateType} from "../co2Data";
@@ -79,33 +80,34 @@ export default withTheme(function DateSwitch(props: DateSwitchProps) {
79
80
 
80
81
  return (<View style={[props.style, {flexDirection: 'row', alignItems: 'center', justifyContent: 'center'}]}>
81
82
  <TouchableOpacity
82
- disabled={state.disableArrowLeft}
83
- onPress={() => {
84
- changeDate(false);
85
- }}>
83
+ disabled={state.disableArrowLeft}
84
+ onPress={() => {
85
+ changeDate(false);
86
+ }}>
86
87
  <Image
87
- source={{ uri: res.arrow_left}}
88
- style={{tintColor: state.disableArrowLeft ? theme?.global.disabledFontColor : theme?.global.brand}}
89
- height={cx(36)}
90
- width={cx(36)}/>
88
+ source={{ uri: res.arrow_left}}
89
+ style={{tintColor: state.disableArrowLeft ? theme?.global.disabledFontColor : theme?.global.brand}}
90
+ height={cx(36)}
91
+ width={cx(36)}/>
91
92
  </TouchableOpacity>
92
- <Text style={{
93
- fontSize: cx(24),
94
- minWidth: cx(200),
95
- textAlign: 'center',
96
- color: theme?.global.brand,
97
- fontFamily: 'helvetica_neue_lt_std_roman',
98
- }}>{state.headlineText}</Text>
93
+ <DateSelectedItem
94
+ style={{flex: 1}}
95
+ dateType={state.dateType}
96
+ date={state.date}
97
+ onDateChange={date => {
98
+ props.onDateChange(date)
99
+ }}
100
+ />
99
101
  <TouchableOpacity
100
- disabled={state.disableArrowRight}
101
- onPress={() => {
102
- changeDate(true);
103
- }}>
102
+ disabled={state.disableArrowRight}
103
+ onPress={() => {
104
+ changeDate(true);
105
+ }}>
104
106
  <Image
105
- source={{ uri: res.arrow_right}}
106
- style={{tintColor: state.disableArrowRight ? theme?.global.disabledFontColor : theme?.global.brand}}
107
- height={cx(36)}
108
- width={cx(36)}/>
107
+ source={{ uri: res.arrow_right}}
108
+ style={{tintColor: state.disableArrowRight ? theme?.global.disabledFontColor : theme?.global.brand}}
109
+ height={cx(36)}
110
+ width={cx(36)}/>
109
111
  </TouchableOpacity>
110
112
  </View>)
111
113
  })
@@ -61,7 +61,7 @@ export default withTheme(function DateTypeItem(props: DateTypeItemProps) {
61
61
  textAlign: 'center',
62
62
  flex: 1,
63
63
  color: props.theme?.textInput.fontColor,
64
- fontFamily: 'helvetica_neue_lt_std_roman',
64
+ // fontFamily: 'helvetica_neue_lt_std_roman',
65
65
  }}>{state.dateTypeTitle}</Text>
66
66
  </View>
67
67
  </TouchableOpacity>
@@ -117,7 +117,7 @@ const EnergyModal = (props: EnergyModalProps) => {
117
117
  marginEnd: cx(6),
118
118
  fontSize: cx(16),
119
119
  color: props.theme?.textInput.fontColor,
120
- fontFamily: 'helvetica_neue_lt_std_roman',
120
+ // fontFamily: 'helvetica_neue_lt_std_roman',
121
121
  },
122
122
  textInputGroup: {
123
123
  flexDirection: 'row',
@@ -159,7 +159,7 @@ const EnergyModal = (props: EnergyModalProps) => {
159
159
  <View style={{ flexDirection: 'row', alignItems: 'center' }}>
160
160
  <View style={{ flex: 3 }}>
161
161
  <Spacer height={cx(4)} />
162
- <Text style={{ color: props.theme?.global.secondFontColor, marginStart: cx(13), fontFamily: 'helvetica_neue_lt_std_bd' }}>{I18n.getLang('consumption_data_price_per_kwh_headline_text')}</Text>
162
+ <Text style={{ color: props.theme?.global.secondFontColor, marginStart: cx(13), }}>{I18n.getLang('consumption_data_price_per_kwh_headline_text')}</Text>
163
163
  <View style={styles.textInputGroup}>
164
164
  <TextInput
165
165
  value={state.energyData?.price}
@@ -11,24 +11,22 @@ const cx = Utils.RatioUtils.convertX
11
11
 
12
12
  interface BarChartProps {
13
13
  theme?: ThemeType
14
- data: OverviewItem[],
15
- price: number,
16
- unit: string,
17
- height: number,
14
+ data: OverviewItem[]
15
+ price: number
16
+ unit: string
17
+ height: number
18
18
  }
19
19
 
20
20
  const BarChartWithTouch = (props: BarChartProps) => {
21
- const echarts = useRef()
21
+ const echarts = useRef<any>()
22
22
  const { data, height, price, unit, theme } = props
23
- const dataX = data?.map(item => {
24
- return item.chartTitle
25
- })
26
- const dataKwhY = data?.map(item => {
27
- return Number(item.value)
28
- })
29
- const dataPriceY = data?.map(item => {
30
- return ((isNaN(Number(item.value)) ? 0 : Number(item.value)) * price).toFixed(2)
31
- })
23
+
24
+ const dataX = data?.map(item => item.chartTitle)
25
+ const dataKwhY = data?.map(item => Number(item.value))
26
+ const dataPriceY = data?.map(item =>
27
+ ((isNaN(Number(item.value)) ? 0 : Number(item.value)) * price).toFixed(2)
28
+ )
29
+
32
30
  const maxValue = useMemo(() => {
33
31
  let max = Math.max(...dataKwhY)
34
32
  if (max < 0.1) {
@@ -48,134 +46,130 @@ const BarChartWithTouch = (props: BarChartProps) => {
48
46
  const max = Math.max(...dataPriceY.map(it => Number(it)))
49
47
  return max > 999 ? '15%' : '10%'
50
48
  }, [dataPriceY])
51
- const option = {
52
- tooltip: {
53
- show: true,
54
- triggerOn: 'click',
55
- trigger: 'axis',
56
- },
57
- legend: {
58
- show: true,
59
- data: ['kWh', unit],
60
- textStyle: {
61
- color: theme?.global.fontColor,
62
- }
63
- },
64
- grid: {
65
- right: gridRight,
66
- },
67
- xAxis: {
68
- data: dataX,
69
- axisTick: {
70
- show: false,
71
- },
72
- axisLabel: {
49
+
50
+ const chartKey = useMemo(() => {
51
+ return `chart-${price}-${unit}`
52
+ }, [price, unit])
53
+
54
+ /** --- option 保持 useMemo,避免不必要重建 ---**/
55
+ const option = useMemo(() => {
56
+ return {
57
+ tooltip: {
73
58
  show: true,
74
- color: props.theme?.global.secondFontColor,
75
- interval: 'auto',
76
- rotate: 45,
77
- align: 'right',
78
- verticalAlign: 'top',
79
- showMinLabel: true,
80
- showMaxLabel: true,
81
- }
82
- },
83
- yAxis: [{
84
- type: 'value',
85
- name: I18n.getLang('consumption_data_annual_bar_chart_text'),
86
- max: Math.max(maxValue, 0.02),
87
- min: 0,
88
- position: 'left',
89
- axisLabel: {
90
- formatter: function (value) {
91
- const kwh = parseFloat(value)
92
- let toFixed = 2
93
- if (kwh >= 100) {
94
- toFixed = 0
95
- } else if (kwh >= 10) {
96
- toFixed = 1
97
- }
98
- return kwh.toFixed(toFixed)
99
- },
100
- color: props.theme?.global.secondFontColor,
59
+ triggerOn: 'click',
60
+ trigger: 'axis',
101
61
  },
102
- nameTextStyle: {
103
- fontSize: 14,
104
- align: 'left',
105
- padding: [0, 0, 0, cx(-30)],
106
- color: props.theme?.global.secondFontColor,
107
- },
108
- }, {
109
- type: 'value',
110
- name: I18n.formatValue('format_unit', unit),
111
- position: 'right',
112
- alignTicks: true,
113
- max: Math.ceil(price ? maxValue * price * 1.4 : 0),
114
- min: 0,
115
- minInterval: 1,
116
- axisLabel: {
117
- formatter: function (value) {
118
- const price = parseFloat(value)
119
- let toFixed = 2
120
- if (price >= 100) {
121
- toFixed = 0
122
- } else if (price >= 10) {
123
- toFixed = 1
124
- }
125
- return price.toFixed(toFixed)
126
- },
127
- color: props.theme?.global.secondFontColor,
62
+ legend: {
63
+ show: true,
64
+ data: ['kWh', unit],
65
+ top: '5%',
66
+ textStyle: {
67
+ color: theme?.global?.fontColor,
68
+ }
128
69
  },
129
- nameTextStyle: {
130
- fontSize: 14,
131
- align: 'right',
132
- padding: [0, cx(-30), 0, 0],
133
- color: props.theme?.global.secondFontColor,
70
+ grid: { right: gridRight },
71
+ xAxis: {
72
+ data: dataX,
73
+ axisTick: { show: false },
74
+ axisLabel: {
75
+ show: true,
76
+ color: theme?.global?.secondFontColor,
77
+ interval: 'auto',
78
+ rotate: 45,
79
+ align: 'right',
80
+ verticalAlign: 'top',
81
+ showMinLabel: true,
82
+ showMaxLabel: true,
83
+ }
134
84
  },
135
- }],
136
- series: [
137
- {
138
- name: 'kWh',
139
- type: 'bar',
140
- data: dataKwhY,
141
- itemStyle: {
142
- emphasis: {
143
- color: '#FFC2A9', // Color when bar is clicked
85
+ yAxis: [
86
+ {
87
+ type: 'value',
88
+ name: I18n.getLang('consumption_data_annual_bar_chart_text'),
89
+ max: Math.max(maxValue, 0.02),
90
+ min: 0,
91
+ position: 'left',
92
+ axisLabel: {
93
+ formatter(value) {
94
+ const kwh = parseFloat(value)
95
+ let toFixed = 2
96
+ if (kwh >= 100) toFixed = 0
97
+ else if (kwh >= 10) toFixed = 1
98
+ return kwh.toFixed(toFixed)
99
+ },
100
+ color: theme?.global?.secondFontColor,
101
+ },
102
+ nameTextStyle: {
103
+ fontSize: 14,
104
+ align: 'left',
105
+ padding: [0, 0, 0, cx(-30)],
106
+ color: theme?.global?.secondFontColor,
144
107
  },
145
- color: '#FFC2A9',
146
- borderRadius: 2,
147
108
  },
148
- barMaxWidth: 10,
149
- select: {
109
+ {
110
+ type: 'value',
111
+ name: I18n.formatValue('format_unit', unit),
112
+ position: 'right',
113
+ alignTicks: true,
114
+ max: Math.ceil(price ? maxValue * price * 1.4 : 0),
115
+ min: 0,
116
+ minInterval: 1,
117
+ axisLabel: {
118
+ formatter(value) {
119
+ const priceValue = parseFloat(value)
120
+ let toFixed = 2
121
+ if (priceValue >= 100) toFixed = 0
122
+ else if (priceValue >= 10) toFixed = 1
123
+ return priceValue.toFixed(toFixed)
124
+ },
125
+ color: theme?.global?.secondFontColor,
126
+ },
127
+ nameTextStyle: {
128
+ fontSize: 14,
129
+ align: 'right',
130
+ padding: [0, cx(-30), 0, 0],
131
+ color: theme?.global?.secondFontColor,
132
+ },
133
+ }
134
+ ],
135
+ series: [
136
+ {
137
+ name: 'kWh',
138
+ type: 'bar',
139
+ data: dataKwhY,
150
140
  itemStyle: {
151
- borderColor: '#FFC2A9',
152
- }
141
+ color: '#FFC2A9',
142
+ emphasis: { color: '#FFC2A9' },
143
+ borderRadius: 2,
144
+ },
145
+ barMaxWidth: 10,
146
+ selectedMode: 'single',
153
147
  },
154
- selectedMode: 'single',
155
- },
156
- {
157
- name: unit,
158
- type: 'line',
159
- data: dataPriceY,
160
- yAxisIndex: 1,
161
- smooth: true,
162
- showSymbol: false,
163
- color: '#F49431',
164
- selectedMode: 'single',
148
+ {
149
+ name: unit,
150
+ type: 'line',
151
+ data: dataPriceY,
152
+ yAxisIndex: 1,
153
+ smooth: true,
154
+ showSymbol: false,
155
+ color: '#F49431',
156
+ selectedMode: 'single',
157
+ }
158
+ ],
159
+ dataZoom: {
160
+ start: 0,
161
+ type: 'inside',
162
+ zoomLock: true,
165
163
  }
166
- ],
167
- dataZoom: {
168
- start: 0,
169
- type: 'inside',
170
- zoomLock: true,
171
- },
172
- customMapData: {}
173
- }
164
+ }
165
+ }, [dataX, dataKwhY, dataPriceY, price, unit, maxValue, gridRight, theme])
166
+
174
167
  return (
175
168
  <View style={{ flex: 1 }}>
176
169
  <ECharts
177
- option={option}
170
+ key={chartKey}
178
171
  ref={echarts}
172
+ option={option}
179
173
  height={height}
180
174
  />
181
175
  </View>
@@ -40,7 +40,7 @@ const OverView = (props: OverViewProps) => {
40
40
  listEmptyText: {
41
41
  color: props.theme?.global.fontColor,
42
42
  fontSize: cx(12),
43
- fontFamily: 'helvetica_neue_lt_std_roman',
43
+ // fontFamily: 'helvetica_neue_lt_std_roman',
44
44
  },
45
45
  overviewItemText: {
46
46
  color: props.theme?.global.fontColor,
@@ -65,7 +65,6 @@ const MoodItem = (props: MoodItemProps) => {
65
65
  height: cx(45),
66
66
  marginTop: cx(-5),
67
67
  marginBottom: cx(-10),
68
- fontWeight: 'bold',
69
68
  },
70
69
  moodTypeItem: {
71
70
  flexDirection: 'row',