@ledvance/group-ui-biz-bundle 1.0.146 → 1.0.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 +1 -1
- package/src/modules/flags/FlagInfo.tsx +36 -0
- package/src/modules/flags/FlagItem.tsx +125 -63
- package/src/modules/flags/FlagPage.tsx +43 -27
- package/src/modules/mood_new/AddMoodPage.tsx +44 -59
- package/src/modules/mood_new/MoodItem.tsx +165 -116
- package/src/modules/mood_new/RecommendMoodItem.tsx +81 -49
package/package.json
CHANGED
|
@@ -608,6 +608,42 @@ export const defFlagList: FlagUiInfo[] = [
|
|
|
608
608
|
...defFlagConfig,
|
|
609
609
|
colors: [{"h":0,"s":0,"v":100}, {"h":160,"s":80,"v":34}],
|
|
610
610
|
},
|
|
611
|
+
{
|
|
612
|
+
id: 160,
|
|
613
|
+
name: I18n.getLang('country_KR'),
|
|
614
|
+
...defFlagConfig,
|
|
615
|
+
colors: [{"h":0,"s":0,"v":100}, {"h":355,"s":78,"v":80}, {"h":213,"s":93,"v":80}],
|
|
616
|
+
},
|
|
617
|
+
{
|
|
618
|
+
id: 159,
|
|
619
|
+
name: I18n.getLang('country_HT'),
|
|
620
|
+
...defFlagConfig,
|
|
621
|
+
colors: [{"h":228,"s":100,"v":62}, {"h":349,"s":92,"v":82}, {"h":0,"s":0,"v":100}],
|
|
622
|
+
},
|
|
623
|
+
{
|
|
624
|
+
id: 158,
|
|
625
|
+
name: I18n.getLang('country_CW'),
|
|
626
|
+
...defFlagConfig,
|
|
627
|
+
colors: [{"h":220,"s":100,"v":50}, {"h":56,"s":92,"v":98}, {"h":0,"s":0,"v":100}],
|
|
628
|
+
},
|
|
629
|
+
{
|
|
630
|
+
id: 157,
|
|
631
|
+
name: I18n.getLang('country_SN'),
|
|
632
|
+
...defFlagConfig,
|
|
633
|
+
colors: [{"h":148,"s":100,"v":52}, {"h":56,"s":74,"v":99}, {"h":358,"s":88,"v":89}],
|
|
634
|
+
},
|
|
635
|
+
{
|
|
636
|
+
id: 156,
|
|
637
|
+
name: I18n.getLang('country_DZ'),
|
|
638
|
+
...defFlagConfig,
|
|
639
|
+
colors: [{"h":150,"s":100,"v":40}, {"h":0,"s":0,"v":100}, {"h":349,"s":92,"v":82}],
|
|
640
|
+
},
|
|
641
|
+
{
|
|
642
|
+
id: 155,
|
|
643
|
+
name: I18n.getLang('country_UZ'),
|
|
644
|
+
...defFlagConfig,
|
|
645
|
+
colors: [{"h":207,"s":100,"v":81}, {"h":0,"s":0,"v":100}, {"h":109,"s":76,"v":69}],
|
|
646
|
+
},
|
|
611
647
|
]
|
|
612
648
|
|
|
613
649
|
export const def2Pids = ['k588eygmfkdzmpzm']
|
|
@@ -1,81 +1,143 @@
|
|
|
1
|
-
import { StyleSheet, View, Text, ViewStyle, Image, TouchableOpacity } from 'react-native'
|
|
2
|
-
import React from 'react'
|
|
3
|
-
import Card from '@ledvance/base/src/components/Card'
|
|
4
|
-
import { Utils } from 'tuya-panel-kit'
|
|
5
|
-
import MoodColorsLine from '@ledvance/base/src/components/MoodColorsLine'
|
|
6
|
-
import Spacer from '@ledvance/base/src/components/Spacer'
|
|
7
|
-
import ThemeType from '@ledvance/base/src/config/themeType'
|
|
8
|
-
import res from "@ledvance/base/src/res"
|
|
1
|
+
import { StyleSheet, View, Text, ViewStyle, Image, TouchableOpacity, LayoutChangeEvent } from 'react-native';
|
|
2
|
+
import React, { useMemo, useState } from 'react'; // 引入 Hooks
|
|
3
|
+
import Card from '@ledvance/base/src/components/Card';
|
|
4
|
+
import { Utils } from 'tuya-panel-kit';
|
|
5
|
+
import MoodColorsLine from '@ledvance/base/src/components/MoodColorsLine';
|
|
6
|
+
import Spacer from '@ledvance/base/src/components/Spacer';
|
|
7
|
+
import ThemeType from '@ledvance/base/src/config/themeType';
|
|
8
|
+
import res from "@ledvance/base/src/res";
|
|
9
9
|
|
|
10
|
-
const cx = Utils.RatioUtils.convertX
|
|
11
|
-
const { withTheme } = Utils.ThemeUtils
|
|
10
|
+
const cx = Utils.RatioUtils.convertX;
|
|
11
|
+
const { withTheme } = Utils.ThemeUtils;
|
|
12
12
|
|
|
13
|
+
// 1. 将边距定义为常量,便于计算和维护
|
|
14
|
+
const CONTENT_HORIZONTAL_MARGIN = cx(16);
|
|
15
|
+
|
|
16
|
+
// 2. 更新 Props 接口,添加可选的 containerWidth
|
|
13
17
|
interface RecommendMoodItemProps {
|
|
14
|
-
theme?: ThemeType
|
|
15
|
-
enable: boolean
|
|
16
|
-
title: string
|
|
17
|
-
colors: string[]
|
|
18
|
-
icon?: number | string
|
|
19
|
-
style?: ViewStyle
|
|
20
|
-
|
|
21
|
-
|
|
18
|
+
theme?: ThemeType;
|
|
19
|
+
enable: boolean;
|
|
20
|
+
title: string;
|
|
21
|
+
colors: string[];
|
|
22
|
+
icon?: number | string;
|
|
23
|
+
style?: ViewStyle;
|
|
24
|
+
containerWidth?: number; // 变为可选属性
|
|
25
|
+
onPress?: () => void;
|
|
26
|
+
onSwitch: (enable: boolean) => void;
|
|
22
27
|
}
|
|
23
28
|
|
|
29
|
+
// 3. 将样式创建移到外部,提升性能
|
|
30
|
+
const getStyles = (theme?: ThemeType) => StyleSheet.create({
|
|
31
|
+
card: {
|
|
32
|
+
marginHorizontal: cx(24),
|
|
33
|
+
// 移除内边距,以便测量 Card 自身的准确宽度
|
|
34
|
+
paddingHorizontal: 0,
|
|
35
|
+
},
|
|
36
|
+
headline: {
|
|
37
|
+
flexDirection: 'row',
|
|
38
|
+
marginHorizontal: CONTENT_HORIZONTAL_MARGIN,
|
|
39
|
+
justifyContent: 'space-between',
|
|
40
|
+
alignItems: 'center',
|
|
41
|
+
},
|
|
42
|
+
headText: {
|
|
43
|
+
color: theme?.global.fontColor,
|
|
44
|
+
fontSize: cx(16),
|
|
45
|
+
lineHeight: cx(20),
|
|
46
|
+
flex: 1
|
|
47
|
+
},
|
|
48
|
+
checkbox: {
|
|
49
|
+
width: cx(45),
|
|
50
|
+
height: cx(45),
|
|
51
|
+
justifyContent: 'center',
|
|
52
|
+
alignItems: 'flex-end',
|
|
53
|
+
},
|
|
54
|
+
checkboxImage: {
|
|
55
|
+
width: cx(44),
|
|
56
|
+
height: cx(44),
|
|
57
|
+
},
|
|
58
|
+
// 这个容器只负责布局,不负责测量
|
|
59
|
+
lineContainer: {
|
|
60
|
+
marginHorizontal: CONTENT_HORIZONTAL_MARGIN,
|
|
61
|
+
minHeight: cx(24), // 设定最小高度防止布局跳动
|
|
62
|
+
},
|
|
63
|
+
icon: {
|
|
64
|
+
width: cx(60),
|
|
65
|
+
aspectRatio: 2.14,
|
|
66
|
+
marginRight: cx(10),
|
|
67
|
+
},
|
|
68
|
+
titleContainer: {
|
|
69
|
+
flexDirection: 'row',
|
|
70
|
+
flex: 1,
|
|
71
|
+
paddingRight: cx(5),
|
|
72
|
+
justifyContent: 'flex-start',
|
|
73
|
+
alignItems: 'center',
|
|
74
|
+
}
|
|
75
|
+
});
|
|
76
|
+
|
|
77
|
+
|
|
24
78
|
const FlagItem = (props: RecommendMoodItemProps) => {
|
|
25
|
-
const
|
|
79
|
+
const { theme, containerWidth, enable, title, colors, icon: iconProp, style, onPress, onSwitch } = props;
|
|
80
|
+
const styles = useMemo(() => getStyles(theme), [theme]);
|
|
81
|
+
const icon = typeof iconProp === 'number' ? iconProp : { uri: iconProp };
|
|
26
82
|
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
}
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
},
|
|
56
|
-
})
|
|
83
|
+
// 4. 添加 state 用于自我测量
|
|
84
|
+
const [selfMeasuredWidth, setSelfMeasuredWidth] = useState(0);
|
|
85
|
+
|
|
86
|
+
// 5. 创建 onLayout 回调
|
|
87
|
+
const handleLayout = (event: LayoutChangeEvent) => {
|
|
88
|
+
// 如果父组件已提供宽度,则忽略自我测量
|
|
89
|
+
if (containerWidth && containerWidth > 0) {
|
|
90
|
+
return;
|
|
91
|
+
}
|
|
92
|
+
const { width: cardWidth } = event.nativeEvent.layout;
|
|
93
|
+
if (cardWidth > 0 && cardWidth !== selfMeasuredWidth) {
|
|
94
|
+
setSelfMeasuredWidth(cardWidth);
|
|
95
|
+
}
|
|
96
|
+
};
|
|
97
|
+
|
|
98
|
+
// 6. 智能决策最终宽度
|
|
99
|
+
const finalLineWidth = useMemo(() => {
|
|
100
|
+
const cardOuterMargin = cx(24);
|
|
101
|
+
// 优先使用父组件传入的宽度 (用于 FlatList)
|
|
102
|
+
if (containerWidth && containerWidth > 0) {
|
|
103
|
+
return containerWidth - (cardOuterMargin * 2) - (CONTENT_HORIZONTAL_MARGIN * 2);
|
|
104
|
+
}
|
|
105
|
+
// 其次使用自我测量的宽度 (用于普通 View)
|
|
106
|
+
if (selfMeasuredWidth > 0) {
|
|
107
|
+
return selfMeasuredWidth - (CONTENT_HORIZONTAL_MARGIN * 2);
|
|
108
|
+
}
|
|
109
|
+
return 0;
|
|
110
|
+
}, [containerWidth, selfMeasuredWidth]);
|
|
57
111
|
|
|
58
112
|
return (
|
|
113
|
+
// 7. 绑定 onLayout 以启用自我测量
|
|
59
114
|
<Card
|
|
60
|
-
style={[styles.card,
|
|
61
|
-
onPress={
|
|
62
|
-
|
|
63
|
-
|
|
115
|
+
style={[styles.card, style]}
|
|
116
|
+
onPress={onPress}
|
|
117
|
+
onLayout={handleLayout}
|
|
118
|
+
>
|
|
119
|
+
<View accessibilityLabel={'FlagItem'} accessibilityHint={`${title}`}>
|
|
120
|
+
<Spacer height={cx(8)} />
|
|
64
121
|
<View style={styles.headline}>
|
|
65
|
-
<View style={
|
|
66
|
-
{
|
|
67
|
-
{
|
|
122
|
+
<View style={styles.titleContainer}>
|
|
123
|
+
{title ? <Text style={styles.headText}>{title}</Text> : null}
|
|
124
|
+
{iconProp ? <Image source={icon} style={styles.icon} /> : null}
|
|
68
125
|
</View>
|
|
69
|
-
<TouchableOpacity style={styles.checkbox} onPress={() =>
|
|
70
|
-
<Image source={{ uri: res.ic_check}}
|
|
126
|
+
<TouchableOpacity style={styles.checkbox} onPress={() => onSwitch(!enable)}>
|
|
127
|
+
<Image source={{ uri: res.ic_check}} style={[styles.checkboxImage, { tintColor: enable ? theme?.icon.primary : theme?.icon.disable}]} />
|
|
71
128
|
</TouchableOpacity>
|
|
72
129
|
</View>
|
|
73
130
|
<Spacer />
|
|
74
|
-
<View style={styles.
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
131
|
+
<View style={styles.lineContainer}>
|
|
132
|
+
{/* 8. 使用最终计算的宽度进行渲染 */}
|
|
133
|
+
{finalLineWidth > 0 && (
|
|
134
|
+
<MoodColorsLine
|
|
135
|
+
type={'separate'}
|
|
136
|
+
width={finalLineWidth}
|
|
137
|
+
nodeStyle={{ borderColor: theme?.card.border, borderWidth: 1 }}
|
|
138
|
+
colors={colors}
|
|
139
|
+
/>
|
|
140
|
+
)}
|
|
79
141
|
</View>
|
|
80
142
|
<Spacer height={cx(16)} />
|
|
81
143
|
</View>
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import React, {useEffect} from
|
|
1
|
+
import React, { useEffect, useState } from 'react'
|
|
2
2
|
import Page from "@ledvance/base/src/components/Page";
|
|
3
3
|
import {useFlags, useGroupDp, useUAGroupInfo} from "@ledvance/base/src/models/modules/NativePropsSlice";
|
|
4
|
-
import {FlatList, Image, TouchableOpacity, View} from
|
|
4
|
+
import { FlatList, Image, LayoutChangeEvent, TouchableOpacity, View } from 'react-native'
|
|
5
5
|
import Spacer from "@ledvance/base/src/components/Spacer";
|
|
6
6
|
import {Utils} from "tuya-panel-kit";
|
|
7
7
|
import FlagItem from "./FlagItem";
|
|
@@ -60,6 +60,16 @@ const FlagPage = (props: { theme?: ThemeType }) => {
|
|
|
60
60
|
state.filterFlags = state.searchText !== '' ? cloneDeep(state.flags).filter(flag => (flag.name ?? '').toLowerCase().includes(state.searchText.toLowerCase())) : cloneDeep(state.flags)
|
|
61
61
|
}, [state.searchText, state.flags])
|
|
62
62
|
|
|
63
|
+
// 3. 在父组件中创建 state 存储容器宽度
|
|
64
|
+
const [listContainerWidth, setListContainerWidth] = useState(0);
|
|
65
|
+
// 4. 创建 onLayout 回调函数
|
|
66
|
+
const handleLayout = (event: LayoutChangeEvent) => {
|
|
67
|
+
const { width } = event.nativeEvent.layout;
|
|
68
|
+
if (width > 0 && width !== listContainerWidth) {
|
|
69
|
+
setListContainerWidth(width);
|
|
70
|
+
}
|
|
71
|
+
};
|
|
72
|
+
|
|
63
73
|
const getRemoteFlagInfo = async (isRefresh?: boolean) => {
|
|
64
74
|
const defNum = uaGroupInfo.groupDevices.filter(device => !(def2Pids.includes(device.tyPid) || def3Pids.includes(device.tyPid))).length
|
|
65
75
|
const def2Num = uaGroupInfo.groupDevices.filter(device => def2Pids.includes(device.tyPid)).length
|
|
@@ -149,6 +159,7 @@ const FlagPage = (props: { theme?: ThemeType }) => {
|
|
|
149
159
|
}}
|
|
150
160
|
loading={state.loading}
|
|
151
161
|
>
|
|
162
|
+
<View style={{ flex: 1 }} onLayout={handleLayout}>
|
|
152
163
|
<View style={{flexDirection: 'row', display: 'flex', alignItems: 'center'}}>
|
|
153
164
|
<TextField
|
|
154
165
|
value={state.searchText}
|
|
@@ -180,31 +191,36 @@ const FlagPage = (props: { theme?: ThemeType }) => {
|
|
|
180
191
|
style={{ width: cx(24), height: cx(24), tintColor: props.theme?.global.fontColor }}/>
|
|
181
192
|
</TouchableOpacity>
|
|
182
193
|
</View>
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
194
|
+
{listContainerWidth > 0 && (
|
|
195
|
+
<FlatList
|
|
196
|
+
data={state.filterFlags}
|
|
197
|
+
renderItem={({ item }) => <FlagItem
|
|
198
|
+
enable={flagMode?.flagId === item.id && flagMode?.flagMode && switch_led}
|
|
199
|
+
title={item.name}
|
|
200
|
+
icon={item.icon}
|
|
201
|
+
colors={item.colors.map(item => hsv2Hex(item.h, item.s, item.v)).reverse()}
|
|
202
|
+
// 7. 【关键】将测量到的宽度传递给子组件
|
|
203
|
+
containerWidth={listContainerWidth}
|
|
204
|
+
onSwitch={async (enable) => {
|
|
205
|
+
if (enable) {
|
|
206
|
+
state.loading = true
|
|
207
|
+
await setFlag({
|
|
208
|
+
flag: cloneDeep(item),
|
|
209
|
+
})
|
|
210
|
+
state.loading = false
|
|
211
|
+
}
|
|
212
|
+
}}
|
|
213
|
+
onPress={() => {
|
|
214
|
+
navigationRoute('edit', item)
|
|
215
|
+
}}
|
|
216
|
+
/>}
|
|
217
|
+
keyExtractor={item => item.name}
|
|
218
|
+
ListHeaderComponent={() => (<Spacer height={cx(10)} />)}
|
|
219
|
+
ItemSeparatorComponent={() => (<Spacer />)}
|
|
220
|
+
ListFooterComponent={() => (<Spacer />)}
|
|
221
|
+
/>
|
|
222
|
+
)}
|
|
223
|
+
</View>
|
|
208
224
|
</Page>
|
|
209
225
|
)
|
|
210
226
|
}
|
|
@@ -1,39 +1,30 @@
|
|
|
1
|
-
import
|
|
2
|
-
import
|
|
3
|
-
import React, { useCallback } from 'react';
|
|
4
|
-
import { FlatList, StyleSheet, Text, View } from 'react-native';
|
|
1
|
+
import React, { useCallback, useState } from 'react';
|
|
2
|
+
import { FlatList, StyleSheet, Text, View, LayoutChangeEvent } from 'react-native';
|
|
5
3
|
import { Utils } from 'tuya-panel-kit';
|
|
6
|
-
import RecommendMoodItem from './RecommendMoodItem';
|
|
7
|
-
import Spacer from '@ledvance/base/src/components/Spacer';
|
|
8
|
-
import { useReactive } from 'ahooks';
|
|
9
4
|
import { useNavigation, useRoute } from '@react-navigation/native';
|
|
10
|
-
import {
|
|
11
|
-
MoodPageParams,
|
|
12
|
-
MoodNodeTransitionMode,
|
|
13
|
-
MoodUIInfo,
|
|
14
|
-
MoodJumpGradientMode,
|
|
15
|
-
} from './Interface';
|
|
16
|
-
import { Result } from '@ledvance/base/src/models/modules/Result';
|
|
17
|
-
import { ui_biz_routerKey } from '../../navigation/Routers'
|
|
5
|
+
import { useReactive } from 'ahooks';
|
|
18
6
|
import { difference, head, range } from 'lodash';
|
|
7
|
+
import Page from '@ledvance/base/src/components/Page';
|
|
8
|
+
import Spacer from '@ledvance/base/src/components/Spacer';
|
|
9
|
+
import Strings from '@ledvance/base/src/i18n';
|
|
10
|
+
import ThemeType from '@ledvance/base/src/config/themeType';
|
|
11
|
+
import { Result } from '@ledvance/base/src/models/modules/Result';
|
|
12
|
+
import { ui_biz_routerKey } from '../../navigation/Routers';
|
|
13
|
+
import { MoodPageParams, MoodNodeTransitionMode, MoodUIInfo, MoodJumpGradientMode } from './Interface';
|
|
19
14
|
import { RecommendMood, getRecommendMixMoods, getRecommendMoods } from './MoodInfo';
|
|
20
|
-
import
|
|
21
|
-
|
|
15
|
+
import RecommendMoodItem from './RecommendMoodItem';
|
|
22
16
|
const cx = Utils.RatioUtils.convertX;
|
|
23
|
-
const { withTheme } = Utils.ThemeUtils
|
|
24
|
-
|
|
17
|
+
const { withTheme } = Utils.ThemeUtils;
|
|
25
18
|
export interface AddMoodPageParams {
|
|
26
19
|
isStatic: boolean;
|
|
27
20
|
moodIds: number[];
|
|
28
21
|
moduleParams: MoodPageParams;
|
|
29
|
-
nameRepeat: (mood: MoodUIInfo) => boolean
|
|
22
|
+
nameRepeat: (mood: MoodUIInfo) => boolean;
|
|
30
23
|
modDeleteMood: (mode: 'add' | 'edit' | 'del', currentMood: MoodUIInfo) => Promise<Result<any>>;
|
|
31
24
|
}
|
|
32
|
-
|
|
33
25
|
interface AddMoodPageState {
|
|
34
26
|
data: RecommendMood[];
|
|
35
27
|
}
|
|
36
|
-
|
|
37
28
|
const AddMoodPage = (props: { theme?: ThemeType }) => {
|
|
38
29
|
const navigation = useNavigation();
|
|
39
30
|
const routeParams = useRoute().params as AddMoodPageParams;
|
|
@@ -44,31 +35,27 @@ const AddMoodPage = (props: { theme?: ThemeType }) => {
|
|
|
44
35
|
? getRecommendMixMoods(routeParams.isStatic, moduleParams)
|
|
45
36
|
: getRecommendMoods(routeParams.isStatic, moduleParams),
|
|
46
37
|
});
|
|
47
|
-
|
|
48
|
-
const
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
speed: 75,
|
|
56
|
-
},
|
|
57
|
-
};
|
|
38
|
+
// 1. 添加 state 用于存储容器宽度
|
|
39
|
+
const [containerWidth, setContainerWidth] = useState(0);
|
|
40
|
+
// 2. 添加 onLayout 回调函数,用于测量根视图宽度
|
|
41
|
+
const handleLayout = (event: LayoutChangeEvent) => {
|
|
42
|
+
const { width } = event.nativeEvent.layout;
|
|
43
|
+
if (width > 0 && width !== containerWidth) {
|
|
44
|
+
setContainerWidth(width);
|
|
45
|
+
}
|
|
58
46
|
};
|
|
59
|
-
|
|
60
47
|
const onMoodItemClick = useCallback(
|
|
61
48
|
(moodItem: RecommendMood) => {
|
|
62
49
|
const idRange = range(0, 256);
|
|
63
50
|
const mainId: number = head(difference(idRange, routeParams.moodIds)) || 0;
|
|
64
|
-
const secondaryId: number = moduleParams.isCeilingLight ? head(difference(idRange, [...routeParams.moodIds, mainId])) || 0 : 0
|
|
51
|
+
const secondaryId: number = moduleParams.isCeilingLight ? head(difference(idRange, [...routeParams.moodIds, mainId])) || 0 : 0;
|
|
65
52
|
const url = routeParams.isStatic
|
|
66
53
|
? ui_biz_routerKey.group_ui_biz_static_mood_edit
|
|
67
54
|
: !!(moduleParams.isCeilingLight || moduleParams.isMixLight)
|
|
68
55
|
? ui_biz_routerKey.group_ui_biz_dynamic_mix_mood_edit
|
|
69
56
|
: ui_biz_routerKey.group_ui_biz_dynamic_mood_edit;
|
|
70
57
|
const currentMood = moodItem.mainLamp
|
|
71
|
-
? { ...moodItem, id: mainId, mainLamp: { ...moodItem.mainLamp, id: mainId }, secondaryLamp: { ...moodItem.secondaryLamp, id: moodItem.secondaryLamp?.nodes?.length ? secondaryId : -1} }
|
|
58
|
+
? { ...moodItem, id: mainId, mainLamp: { ...moodItem.mainLamp, id: mainId }, secondaryLamp: { ...moodItem.secondaryLamp, id: moodItem.secondaryLamp?.nodes?.length ? secondaryId : -1 } }
|
|
72
59
|
: newMood(mainId, secondaryId, moduleParams.isSupportColor, routeParams.isStatic, moduleParams);
|
|
73
60
|
navigation.navigate(url, {
|
|
74
61
|
...routeParams,
|
|
@@ -78,11 +65,9 @@ const AddMoodPage = (props: { theme?: ThemeType }) => {
|
|
|
78
65
|
},
|
|
79
66
|
[routeParams]
|
|
80
67
|
);
|
|
81
|
-
|
|
82
68
|
const styles = StyleSheet.create({
|
|
83
69
|
root: {
|
|
84
70
|
flex: 1,
|
|
85
|
-
flexDirection: 'column',
|
|
86
71
|
},
|
|
87
72
|
desc: {
|
|
88
73
|
color: props.theme?.global.fontColor,
|
|
@@ -90,8 +75,7 @@ const AddMoodPage = (props: { theme?: ThemeType }) => {
|
|
|
90
75
|
marginHorizontal: cx(24),
|
|
91
76
|
marginTop: cx(12),
|
|
92
77
|
},
|
|
93
|
-
})
|
|
94
|
-
|
|
78
|
+
});
|
|
95
79
|
return (
|
|
96
80
|
<Page
|
|
97
81
|
backText={Strings.getLang('add_new_static_mood_system_back')}
|
|
@@ -101,7 +85,8 @@ const AddMoodPage = (props: { theme?: ThemeType }) => {
|
|
|
101
85
|
: 'add_new_dynamic_mood_headline_text'
|
|
102
86
|
)}
|
|
103
87
|
>
|
|
104
|
-
|
|
88
|
+
{/* 3. 将 onLayout 绑定到根视图 */}
|
|
89
|
+
<View style={styles.root} onLayout={handleLayout}>
|
|
105
90
|
<Text style={styles.desc}>
|
|
106
91
|
{Strings.getLang(
|
|
107
92
|
routeParams.isStatic
|
|
@@ -109,33 +94,33 @@ const AddMoodPage = (props: { theme?: ThemeType }) => {
|
|
|
109
94
|
: 'add_new_dynamic_mood_description_text'
|
|
110
95
|
)}
|
|
111
96
|
</Text>
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
97
|
+
{/* 4. 仅在获取到有效宽度后渲染列表 */}
|
|
98
|
+
{containerWidth > 0 && (
|
|
99
|
+
<FlatList
|
|
100
|
+
style={{ flex: 1 }}
|
|
101
|
+
data={state.data}
|
|
102
|
+
renderItem={({ item }) => (
|
|
117
103
|
<RecommendMoodItem
|
|
118
104
|
title={item.name}
|
|
119
105
|
isMix={isMix}
|
|
120
|
-
mood={
|
|
106
|
+
mood={item}
|
|
121
107
|
deviceTypeOption={moduleParams}
|
|
122
|
-
onPress={() =>
|
|
123
|
-
|
|
124
|
-
}
|
|
108
|
+
onPress={() => onMoodItemClick(item)}
|
|
109
|
+
// 5. 将测量到的宽度传递给子组件
|
|
110
|
+
containerWidth={containerWidth}
|
|
125
111
|
/>
|
|
126
|
-
)
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
112
|
+
)}
|
|
113
|
+
ItemSeparatorComponent={() => <Spacer />}
|
|
114
|
+
ListHeaderComponent={() => <Spacer />}
|
|
115
|
+
ListFooterComponent={() => <Spacer />}
|
|
116
|
+
keyExtractor={item => item.name}
|
|
117
|
+
/>
|
|
118
|
+
)}
|
|
133
119
|
</View>
|
|
134
120
|
</Page>
|
|
135
121
|
);
|
|
136
122
|
};
|
|
137
|
-
|
|
138
|
-
export default withTheme(AddMoodPage)
|
|
123
|
+
export default withTheme(AddMoodPage);
|
|
139
124
|
|
|
140
125
|
const defStripConfig = {
|
|
141
126
|
version: 0,
|
|
@@ -1,171 +1,220 @@
|
|
|
1
|
-
import React, { useMemo } from 'react';
|
|
2
|
-
import { StyleSheet, Text, View, ViewProps, ViewStyle, Image, TouchableOpacity } from 'react-native';
|
|
3
|
-
import { Utils } from 'tuya-panel-kit';
|
|
4
|
-
import { hsv2Hex } from '@ledvance/base/src/utils';
|
|
5
|
-
import { cctToColor } from '@ledvance/base/src/utils/cctUtils';
|
|
6
|
-
import { mapFloatToRange } from '@ledvance/base/src/utils';
|
|
7
1
|
import Card from '@ledvance/base/src/components/Card';
|
|
8
|
-
import Spacer from '@ledvance/base/src/components/Spacer';
|
|
9
2
|
import MoodColorsLine from '@ledvance/base/src/components/MoodColorsLine';
|
|
10
|
-
import
|
|
3
|
+
import Spacer from '@ledvance/base/src/components/Spacer';
|
|
4
|
+
import ThemeType from '@ledvance/base/src/config/themeType';
|
|
11
5
|
import I18n from '@ledvance/base/src/i18n';
|
|
12
6
|
import res from '@ledvance/base/src/res';
|
|
13
|
-
import
|
|
7
|
+
import { hsv2Hex, mapFloatToRange } from '@ledvance/base/src/utils';
|
|
8
|
+
import { cctToColor } from '@ledvance/base/src/utils/cctUtils';
|
|
9
|
+
import React, { useMemo, useState } from 'react';
|
|
10
|
+
import { Image, LayoutChangeEvent, StyleSheet, Text, TouchableOpacity, View, ViewProps, ViewStyle } from 'react-native';
|
|
11
|
+
import { Utils } from 'tuya-panel-kit';
|
|
12
|
+
import { MoodJumpGradientMode, MoodLampInfo, MoodUIInfo } from './Interface';
|
|
14
13
|
|
|
15
14
|
const cx = Utils.RatioUtils.convertX;
|
|
16
|
-
const { withTheme } = Utils.ThemeUtils
|
|
15
|
+
const { withTheme } = Utils.ThemeUtils;
|
|
17
16
|
|
|
18
17
|
interface LightCategory {
|
|
19
|
-
isMixLight?: boolean
|
|
20
|
-
isStripLight?: boolean
|
|
21
|
-
isStringLight?: boolean
|
|
22
|
-
isCeilingLight?: boolean
|
|
18
|
+
isMixLight?: boolean;
|
|
19
|
+
isStripLight?: boolean;
|
|
20
|
+
isStringLight?: boolean;
|
|
21
|
+
isCeilingLight?: boolean;
|
|
23
22
|
}
|
|
24
23
|
|
|
25
24
|
interface MoodItemProps extends ViewProps {
|
|
26
|
-
theme?: ThemeType
|
|
25
|
+
theme?: ThemeType;
|
|
27
26
|
enable: boolean;
|
|
28
27
|
isMix: boolean;
|
|
29
28
|
mood: MoodUIInfo;
|
|
30
29
|
style?: ViewStyle;
|
|
31
|
-
deviceTypeOption?: LightCategory
|
|
30
|
+
deviceTypeOption?: LightCategory;
|
|
32
31
|
onPress?: () => void;
|
|
33
32
|
onSwitch: (enable: boolean) => void;
|
|
34
33
|
}
|
|
35
34
|
|
|
36
35
|
const MoodItem = (props: MoodItemProps) => {
|
|
37
|
-
const { mood, isMix, deviceTypeOption } = props;
|
|
36
|
+
const { mood, isMix, deviceTypeOption, theme } = props;
|
|
38
37
|
const isDynamic = useMemo(() => {
|
|
39
38
|
return mood.mainLamp.nodes?.length > 1 || mood.secondaryLamp.nodes?.length > 1;
|
|
40
39
|
}, [mood.mainLamp.nodes, mood.secondaryLamp.nodes]);
|
|
41
40
|
|
|
42
41
|
const gradientMode = useMemo(() => (
|
|
43
42
|
deviceTypeOption?.isStringLight ? MoodJumpGradientMode.StringGradient : deviceTypeOption?.isStripLight ? MoodJumpGradientMode.StripGradient : MoodJumpGradientMode.SourceGradient
|
|
44
|
-
), [MoodJumpGradientMode, deviceTypeOption])
|
|
45
|
-
|
|
46
|
-
const styles =
|
|
47
|
-
card: {
|
|
48
|
-
marginHorizontal: cx(24),
|
|
49
|
-
},
|
|
50
|
-
headline: {
|
|
51
|
-
flexDirection: 'row',
|
|
52
|
-
marginHorizontal: cx(16),
|
|
53
|
-
alignItems: 'center',
|
|
54
|
-
},
|
|
55
|
-
headText: {
|
|
56
|
-
flex: 1,
|
|
57
|
-
color: props.theme?.global.fontColor,
|
|
58
|
-
fontSize: cx(16),
|
|
59
|
-
// fontFamily: 'helvetica_neue_lt_std_bd',
|
|
60
|
-
lineHeight: cx(20),
|
|
61
|
-
},
|
|
62
|
-
checkbox: {
|
|
63
|
-
width: cx(45),
|
|
64
|
-
height: cx(45),
|
|
65
|
-
marginTop: cx(-5),
|
|
66
|
-
marginBottom: cx(-10),
|
|
67
|
-
},
|
|
68
|
-
moodTypeItem: {
|
|
69
|
-
flexDirection: 'row',
|
|
70
|
-
},
|
|
71
|
-
moodTypeLabel: {
|
|
72
|
-
marginStart: cx(16),
|
|
73
|
-
paddingHorizontal: cx(12.5),
|
|
74
|
-
backgroundColor: props.theme?.tag.background,
|
|
75
|
-
borderRadius: cx(8),
|
|
76
|
-
},
|
|
77
|
-
moodTypeLabelText: {
|
|
78
|
-
height: cx(16),
|
|
79
|
-
color: props.theme?.tag.fontColor,
|
|
80
|
-
fontSize: cx(10),
|
|
81
|
-
textAlignVertical: 'center',
|
|
82
|
-
// fontFamily: 'helvetica_neue_lt_std_roman',
|
|
83
|
-
lineHeight: cx(16),
|
|
84
|
-
},
|
|
85
|
-
});
|
|
43
|
+
), [MoodJumpGradientMode, deviceTypeOption]);
|
|
44
|
+
|
|
45
|
+
const styles = useMemo(() => getStyles(theme), [theme]);
|
|
86
46
|
|
|
87
47
|
return (
|
|
88
48
|
<Card style={[styles.card, props.style]} onPress={props.onPress}>
|
|
89
|
-
<View>
|
|
90
|
-
<
|
|
91
|
-
<View style={styles.headline}>
|
|
49
|
+
<View style={styles.contentContainer}>
|
|
50
|
+
<View style={styles.row}>
|
|
92
51
|
<Text style={styles.headText}>{mood.name}</Text>
|
|
93
52
|
<TouchableOpacity style={styles.checkbox} onPress={() => props.onSwitch(!props.enable)}>
|
|
94
|
-
<Image source={{ uri: res.ic_check
|
|
53
|
+
<Image source={{ uri: res.ic_check }}
|
|
54
|
+
style={[styles.checkboxImage, { tintColor: props.enable ? theme?.icon.primary : theme?.icon.disable }]}/>
|
|
95
55
|
</TouchableOpacity>
|
|
96
56
|
</View>
|
|
97
|
-
<Spacer />
|
|
98
|
-
<MixMoodColorsLine
|
|
99
|
-
|
|
57
|
+
<Spacer height={cx(8)}/>
|
|
58
|
+
<MixMoodColorsLine
|
|
59
|
+
mixSubLight={mood.mainLamp}
|
|
60
|
+
isMix={isMix}
|
|
61
|
+
type={mood.mainLamp.mode === gradientMode && !deviceTypeOption?.isCeilingLight ? 'gradient' : 'separate'}
|
|
62
|
+
/>
|
|
63
|
+
{(deviceTypeOption?.isMixLight || (isMix && !!mood.secondaryLamp.nodes.length)) && (
|
|
100
64
|
<>
|
|
101
|
-
<Spacer height={cx(7)}
|
|
102
|
-
<MixMoodColorsLine
|
|
65
|
+
<Spacer height={cx(7)}/>
|
|
66
|
+
<MixMoodColorsLine
|
|
67
|
+
mixSubLight={mood.secondaryLamp}
|
|
68
|
+
isMix={isMix}
|
|
69
|
+
type={mood.secondaryLamp.mode === (deviceTypeOption?.isMixLight ? MoodJumpGradientMode.SourceGradient : MoodJumpGradientMode.StripGradient) ? 'gradient' : 'separate'}
|
|
70
|
+
/>
|
|
103
71
|
</>
|
|
104
72
|
)}
|
|
105
|
-
<Spacer height={cx(12)}
|
|
106
|
-
<View style={styles.
|
|
73
|
+
<Spacer height={cx(12)}/>
|
|
74
|
+
<View style={styles.row}>
|
|
107
75
|
<View style={styles.moodTypeLabel}>
|
|
108
76
|
<Text style={styles.moodTypeLabelText}>
|
|
109
|
-
{I18n.getLang(
|
|
110
|
-
isDynamic ? 'mood_overview_field_chip_2' : 'mood_overview_field_chip_text'
|
|
111
|
-
)}
|
|
77
|
+
{I18n.getLang(isDynamic ? 'mood_overview_field_chip_2' : 'mood_overview_field_chip_text')}
|
|
112
78
|
</Text>
|
|
113
79
|
</View>
|
|
114
80
|
</View>
|
|
115
|
-
<Spacer height={cx(16)} />
|
|
116
81
|
</View>
|
|
117
82
|
</Card>
|
|
118
83
|
);
|
|
119
84
|
};
|
|
120
85
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
width: cx(24),
|
|
138
|
-
height: cx(24),
|
|
139
|
-
justifyContent: 'center',
|
|
140
|
-
alignItems: 'center',
|
|
141
|
-
backgroundColor: '#aaa',
|
|
142
|
-
borderRadius: cx(8),
|
|
143
|
-
},
|
|
144
|
-
gradientItemIcon: {
|
|
145
|
-
width: cx(16),
|
|
146
|
-
height: cx(16),
|
|
147
|
-
tintColor: '#fff',
|
|
148
|
-
},
|
|
149
|
-
})
|
|
86
|
+
// --- 【核心修正】 ---
|
|
87
|
+
export function MixMoodColorsLine(props: {
|
|
88
|
+
mixSubLight: MoodLampInfo;
|
|
89
|
+
isMix: boolean;
|
|
90
|
+
type: 'gradient' | 'separate';
|
|
91
|
+
width?: number; // 外部传入的宽度(父组件已计算好)
|
|
92
|
+
}) {
|
|
93
|
+
const { mixSubLight, isMix, type, width: propWidth } = props;
|
|
94
|
+
const [measuredWidth, setMeasuredWidth] = useState(0);
|
|
95
|
+
|
|
96
|
+
const handleLayout = (event: LayoutChangeEvent) => {
|
|
97
|
+
const { width } = event.nativeEvent.layout;
|
|
98
|
+
if (width > 0 && width !== measuredWidth) {
|
|
99
|
+
setMeasuredWidth(width);
|
|
100
|
+
}
|
|
101
|
+
};
|
|
150
102
|
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
103
|
+
const lightColors = (mixSubLight.enable && mixSubLight.nodes.length > 0)
|
|
104
|
+
? mixSubLight.nodes.map(n => {
|
|
105
|
+
const s = Math.round(mapFloatToRange(n.s / 100, 30, 100));
|
|
106
|
+
return n.isColorNode
|
|
107
|
+
? hsv2Hex(n.h, s, Math.round(mapFloatToRange(n.v / 100, 50, 100)))
|
|
108
|
+
: cctToColor(n.colorTemp.toFixed());
|
|
109
|
+
})
|
|
110
|
+
: ['#eee'];
|
|
111
|
+
|
|
112
|
+
// 如果父组件传入了精确宽度,直接使用;否则使用内部测量的宽度。
|
|
113
|
+
const finalWidth = propWidth || measuredWidth;
|
|
114
|
+
|
|
115
|
+
// 渲染颜色条的通用逻辑
|
|
116
|
+
const renderColorLine = () => (
|
|
117
|
+
finalWidth > 0 && (
|
|
154
118
|
<MoodColorsLine
|
|
155
119
|
nodeStyle={{ borderColor: '#ccc', borderWidth: 1 }}
|
|
156
|
-
width={
|
|
157
|
-
type={
|
|
120
|
+
width={finalWidth}
|
|
121
|
+
type={type}
|
|
158
122
|
colors={lightColors}
|
|
159
123
|
/>
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
124
|
+
)
|
|
125
|
+
);
|
|
126
|
+
|
|
127
|
+
// 渲染右侧图标的通用逻辑
|
|
128
|
+
const renderIcon = () => (
|
|
129
|
+
isMix && (
|
|
130
|
+
<>
|
|
131
|
+
<Spacer width={cx(7)}/>
|
|
132
|
+
<View style={styles.mixLineIconView}>
|
|
133
|
+
<Image style={styles.mixLineIcon} source={{ uri: mixSubLight.enable ? res.light_on : res.light_off }}/>
|
|
134
|
+
</View>
|
|
135
|
+
</>
|
|
136
|
+
)
|
|
137
|
+
);
|
|
138
|
+
|
|
139
|
+
// Case 1: 父组件已经计算并传入了宽度 (最高效)
|
|
140
|
+
if (propWidth) {
|
|
141
|
+
return (
|
|
142
|
+
<View style={styles.mixLineRow}>
|
|
143
|
+
{renderColorLine()}
|
|
144
|
+
{renderIcon()}
|
|
145
|
+
</View>
|
|
146
|
+
);
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// Case 2: 自我测量模式 (回退方案)
|
|
150
|
+
return (
|
|
151
|
+
<View style={styles.mixLineRow}>
|
|
152
|
+
{/* 这个 View (测量器) 会自动收缩以填充'颜色条'应占的空间 */}
|
|
153
|
+
<View style={{ flex: 1 }} onLayout={handleLayout}>
|
|
154
|
+
{renderColorLine()}
|
|
155
|
+
</View>
|
|
156
|
+
{/* 图标作为测量器的兄弟节点,Flexbox 会先为它分配空间 */}
|
|
157
|
+
{renderIcon()}
|
|
168
158
|
</View>
|
|
169
159
|
);
|
|
170
160
|
}
|
|
171
161
|
|
|
162
|
+
const getStyles = (theme?: ThemeType) => StyleSheet.create({
|
|
163
|
+
card: {
|
|
164
|
+
marginHorizontal: cx(24),
|
|
165
|
+
padding: 0,
|
|
166
|
+
borderRadius: cx(16),
|
|
167
|
+
},
|
|
168
|
+
contentContainer: {
|
|
169
|
+
paddingHorizontal: cx(16),
|
|
170
|
+
paddingTop: cx(8),
|
|
171
|
+
paddingBottom: cx(16),
|
|
172
|
+
},
|
|
173
|
+
row: {
|
|
174
|
+
flexDirection: 'row',
|
|
175
|
+
alignItems: 'center',
|
|
176
|
+
},
|
|
177
|
+
headText: {
|
|
178
|
+
flex: 1,
|
|
179
|
+
color: theme?.global.fontColor,
|
|
180
|
+
fontSize: cx(16),
|
|
181
|
+
lineHeight: cx(20),
|
|
182
|
+
},
|
|
183
|
+
checkbox: {
|
|
184
|
+
width: cx(45),
|
|
185
|
+
height: cx(45),
|
|
186
|
+
marginRight: cx(-10),
|
|
187
|
+
justifyContent: 'center',
|
|
188
|
+
alignItems: 'flex-end',
|
|
189
|
+
},
|
|
190
|
+
checkboxImage: {
|
|
191
|
+
width: cx(44),
|
|
192
|
+
height: cx(44),
|
|
193
|
+
},
|
|
194
|
+
moodTypeLabel: {
|
|
195
|
+
paddingHorizontal: cx(12.5),
|
|
196
|
+
backgroundColor: theme?.tag.background,
|
|
197
|
+
borderRadius: cx(8),
|
|
198
|
+
},
|
|
199
|
+
moodTypeLabelText: {
|
|
200
|
+
height: cx(16),
|
|
201
|
+
color: theme?.tag.fontColor,
|
|
202
|
+
fontSize: cx(10),
|
|
203
|
+
lineHeight: cx(16),
|
|
204
|
+
},
|
|
205
|
+
});
|
|
206
|
+
|
|
207
|
+
const styles = StyleSheet.create({
|
|
208
|
+
mixLineRow: { flexDirection: 'row', alignItems: 'center' },
|
|
209
|
+
mixLineIconView: {
|
|
210
|
+
width: cx(24),
|
|
211
|
+
height: cx(24),
|
|
212
|
+
justifyContent: 'center',
|
|
213
|
+
alignItems: 'center',
|
|
214
|
+
backgroundColor: '#aaa',
|
|
215
|
+
borderRadius: cx(8),
|
|
216
|
+
},
|
|
217
|
+
mixLineIcon: { width: cx(16), height: cx(16), tintColor: '#fff' },
|
|
218
|
+
});
|
|
219
|
+
|
|
220
|
+
export default withTheme(MoodItem);
|
|
@@ -1,85 +1,117 @@
|
|
|
1
1
|
import React, { useMemo } from 'react';
|
|
2
|
+
import { StyleSheet, View } from 'react-native';
|
|
2
3
|
import { Utils } from 'tuya-panel-kit';
|
|
3
4
|
import Card from '@ledvance/base/src/components/Card';
|
|
4
5
|
import { CellContent } from '@ledvance/base/src/components/Cell';
|
|
5
|
-
import { StyleSheet, View } from 'react-native';
|
|
6
6
|
import Spacer from '@ledvance/base/src/components/Spacer';
|
|
7
|
+
import ThemeType from '@ledvance/base/src/config/themeType';
|
|
8
|
+
|
|
7
9
|
import { MoodJumpGradientMode, MoodUIInfo } from './Interface';
|
|
8
|
-
import { MixMoodColorsLine } from './MoodItem';
|
|
9
|
-
import ThemeType from '@ledvance/base/src/config/themeType'
|
|
10
|
+
import { MixMoodColorsLine } from './MoodItem'; // 从 MoodItem.tsx 导入重构后的组件
|
|
10
11
|
|
|
11
12
|
const cx = Utils.RatioUtils.convertX;
|
|
12
|
-
const { withTheme } = Utils.ThemeUtils
|
|
13
|
+
const { withTheme } = Utils.ThemeUtils;
|
|
14
|
+
|
|
15
|
+
// 定义布局中使用的常量,便于维护和计算
|
|
16
|
+
const CARD_MARGIN_HORIZONTAL = cx(24);
|
|
17
|
+
const CONTENT_PADDING_HORIZONTAL = cx(16);
|
|
18
|
+
const ICON_WIDTH = cx(24);
|
|
19
|
+
const ICON_SPACING = cx(7);
|
|
13
20
|
|
|
14
21
|
interface LightCategory {
|
|
15
|
-
isMixLight?: boolean
|
|
16
|
-
isStripLight?: boolean
|
|
17
|
-
isStringLight?: boolean
|
|
18
|
-
isCeilingLight?: boolean
|
|
22
|
+
isMixLight?: boolean;
|
|
23
|
+
isStripLight?: boolean;
|
|
24
|
+
isStringLight?: boolean;
|
|
25
|
+
isCeilingLight?: boolean;
|
|
19
26
|
}
|
|
20
27
|
|
|
21
|
-
interface
|
|
22
|
-
theme?: ThemeType
|
|
28
|
+
interface RecommendMoodItemProps {
|
|
29
|
+
theme?: ThemeType;
|
|
23
30
|
title: string;
|
|
24
31
|
isMix: boolean;
|
|
25
32
|
mood: MoodUIInfo;
|
|
26
|
-
deviceTypeOption?: LightCategory
|
|
33
|
+
deviceTypeOption?: LightCategory;
|
|
27
34
|
onPress: () => void;
|
|
35
|
+
containerWidth: number; // 1. 接收从父组件传来的容器宽度
|
|
28
36
|
}
|
|
29
37
|
|
|
30
|
-
const
|
|
31
|
-
const { mood, isMix, deviceTypeOption } = props;
|
|
38
|
+
const RecommendMoodItem = (props: RecommendMoodItemProps) => {
|
|
39
|
+
const { mood, isMix, deviceTypeOption, theme, containerWidth } = props;
|
|
32
40
|
|
|
33
41
|
const gradientMode = useMemo(() => (
|
|
34
42
|
deviceTypeOption?.isStringLight ? MoodJumpGradientMode.StringGradient : deviceTypeOption?.isStripLight ? MoodJumpGradientMode.StripGradient : MoodJumpGradientMode.SourceGradient
|
|
35
|
-
), [MoodJumpGradientMode, deviceTypeOption])
|
|
43
|
+
), [MoodJumpGradientMode, deviceTypeOption]);
|
|
36
44
|
|
|
45
|
+
// 2. 使用 useMemo 根据传入的 prop 精确计算颜色条的宽度
|
|
46
|
+
const lineWidth = useMemo(() => {
|
|
47
|
+
if (containerWidth <= 0) return 0;
|
|
37
48
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
marginHorizontal: cx(16),
|
|
47
|
-
width: cx(295)
|
|
48
|
-
},
|
|
49
|
-
title: {
|
|
50
|
-
color: props.theme?.global.fontColor,
|
|
51
|
-
fontSize: cx(16),
|
|
52
|
-
// fontFamily: 'helvetica_neue_lt_std_bd',
|
|
53
|
-
},
|
|
54
|
-
lineStyle: {
|
|
55
|
-
alignItems: 'center',
|
|
56
|
-
},
|
|
57
|
-
})
|
|
49
|
+
// 基础宽度 = 容器总宽 - 卡片外边距*2 - 内容内边距*2
|
|
50
|
+
const baseWidth = containerWidth - (CARD_MARGIN_HORIZONTAL * 2) - (CONTENT_PADDING_HORIZONTAL * 2);
|
|
51
|
+
|
|
52
|
+
// 如果是 isMix 模式,还需减去右侧图标的宽度和间距
|
|
53
|
+
return isMix ? baseWidth - ICON_WIDTH - ICON_SPACING : baseWidth;
|
|
54
|
+
}, [containerWidth, isMix]);
|
|
55
|
+
|
|
56
|
+
const styles = useMemo(() => getRecommendStyles(theme), [theme]);
|
|
58
57
|
|
|
59
58
|
return (
|
|
60
|
-
<Card style={styles.
|
|
59
|
+
<Card style={styles.card} onPress={props.onPress}>
|
|
61
60
|
<CellContent
|
|
62
61
|
title={props.title}
|
|
63
62
|
value={''}
|
|
64
63
|
style={styles.content}
|
|
65
64
|
titleStyle={styles.title}
|
|
66
|
-
arrowStyle={{
|
|
67
|
-
color: props.theme?.global.fontColor,
|
|
68
|
-
size: cx(16),
|
|
69
|
-
}}
|
|
65
|
+
arrowStyle={{ color: theme?.global.fontColor, size: cx(16) }}
|
|
70
66
|
/>
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
67
|
+
|
|
68
|
+
{/* 3. 只有在计算出有效宽度后才渲染颜色条 */}
|
|
69
|
+
{lineWidth > 0 && mood.mainLamp && (
|
|
70
|
+
<View style={styles.lineContainer}>
|
|
71
|
+
{/* 主灯颜色条 */}
|
|
72
|
+
<MixMoodColorsLine
|
|
73
|
+
width={lineWidth} // 4. 将计算好的宽度传递下去
|
|
74
|
+
isMix={isMix}
|
|
75
|
+
mixSubLight={mood.mainLamp}
|
|
76
|
+
type={mood.mainLamp.mode === gradientMode ? 'gradient' : 'separate'}
|
|
77
|
+
/>
|
|
78
|
+
{/* 副灯颜色条 (如果需要) */}
|
|
79
|
+
{isMix && mood.secondaryLamp && mood.secondaryLamp.nodes.length > 0 && (
|
|
80
|
+
<>
|
|
81
|
+
<Spacer height={cx(7)} />
|
|
82
|
+
<MixMoodColorsLine
|
|
83
|
+
width={lineWidth} // 同样传递计算好的宽度
|
|
84
|
+
isMix={isMix}
|
|
85
|
+
mixSubLight={mood.secondaryLamp}
|
|
86
|
+
type={mood.secondaryLamp.mode === (deviceTypeOption?.isCeilingLight ? MoodJumpGradientMode.StripGradient : MoodJumpGradientMode.SourceGradient) ? 'gradient' : 'separate'}
|
|
87
|
+
/>
|
|
88
|
+
</>
|
|
89
|
+
)}
|
|
90
|
+
<Spacer height={cx(12)} />
|
|
91
|
+
</View>
|
|
80
92
|
)}
|
|
81
93
|
</Card>
|
|
82
94
|
);
|
|
83
95
|
};
|
|
84
96
|
|
|
85
|
-
|
|
97
|
+
const getRecommendStyles = (theme?: ThemeType) => StyleSheet.create({
|
|
98
|
+
card: {
|
|
99
|
+
marginHorizontal: CARD_MARGIN_HORIZONTAL,
|
|
100
|
+
paddingVertical: 0,
|
|
101
|
+
paddingHorizontal: 0,
|
|
102
|
+
},
|
|
103
|
+
content: {
|
|
104
|
+
paddingHorizontal: CONTENT_PADDING_HORIZONTAL,
|
|
105
|
+
height: cx(56),
|
|
106
|
+
// 移除了硬编码的 width,使其自适应
|
|
107
|
+
},
|
|
108
|
+
title: {
|
|
109
|
+
color: theme?.global.fontColor,
|
|
110
|
+
fontSize: cx(16),
|
|
111
|
+
},
|
|
112
|
+
lineContainer: {
|
|
113
|
+
paddingHorizontal: CONTENT_PADDING_HORIZONTAL,
|
|
114
|
+
},
|
|
115
|
+
});
|
|
116
|
+
|
|
117
|
+
export default withTheme(RecommendMoodItem);
|