@ledvance/group-ui-biz-bundle 1.0.151 → 1.0.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 +2 -1
- package/src/modules/biorhythm/BiorhythmPage.tsx +31 -20
- package/src/modules/diyScene/DiyScenePage.tsx +33 -19
- package/src/modules/mood_new/MixMoodColorsLine.tsx +102 -0
- package/src/modules/mood_new/MoodInfo.ts +231 -29
- package/src/modules/mood_new/MoodItem.tsx +177 -173
- package/src/modules/mood_new/MoodPage.tsx +114 -51
- package/src/modules/mood_new/RecommendMoodItem.tsx +1 -1
- package/src/modules/timeSchedule/TimeScheduleDetailPage.tsx +1 -1
package/package.json
CHANGED
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
"name": "@ledvance/group-ui-biz-bundle",
|
|
5
5
|
"pid": [],
|
|
6
6
|
"uiid": "",
|
|
7
|
-
"version": "1.0.
|
|
7
|
+
"version": "1.0.153",
|
|
8
8
|
"scripts": {},
|
|
9
9
|
"dependencies": {
|
|
10
10
|
"@ledvance/base": "^1.x",
|
|
@@ -17,6 +17,7 @@
|
|
|
17
17
|
"prop-types": "^15.6.1",
|
|
18
18
|
"react": "16.8.3",
|
|
19
19
|
"react-native": "0.59.10",
|
|
20
|
+
"react-native-linear-gradient": "2.8.3",
|
|
20
21
|
"react-native-orientation-locker": "^1.7.0",
|
|
21
22
|
"react-native-svg": "5.5.1",
|
|
22
23
|
"tuya-panel-kit": "^4.9.4"
|
|
@@ -245,26 +245,37 @@ const BiorhythmPage = (props: { theme?: ThemeType }) => {
|
|
|
245
245
|
<Page
|
|
246
246
|
backText={uaGroupInfo.name}
|
|
247
247
|
onBackClick={navigation.goBack}
|
|
248
|
-
headlineTopContent={
|
|
249
|
-
<
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
248
|
+
headlineTopContent={
|
|
249
|
+
<View style={{flexDirection: 'row', justifyContent: 'flex-end'}}>
|
|
250
|
+
<TouchableOpacity
|
|
251
|
+
style={{paddingLeft: cx(16)}}
|
|
252
|
+
onPress={() => {
|
|
253
|
+
Dialog.prompt({
|
|
254
|
+
title: I18n.getLang('biorhythm_save_title'),
|
|
255
|
+
placeholder: I18n.getLang('biorhythm_save_placeholder'),
|
|
256
|
+
defaultValue: `${uaGroupInfo.name}`,
|
|
257
|
+
cancelText: I18n.getLang('auto_scan_system_cancel'),
|
|
258
|
+
confirmText: I18n.getLang('auto_scan_system_wifi_confirm'),
|
|
259
|
+
inputWrapperStyle: {backgroundColor: props.theme?.textInput.background, borderRadius: cx(10)},
|
|
260
|
+
autoFocus: true,
|
|
261
|
+
onConfirm: (data, { close }) => {
|
|
262
|
+
saveStorageBiorhythms(data, state.enable, state.gradient, state.repeatPeriod, state.planList)
|
|
263
|
+
close()
|
|
264
|
+
}
|
|
265
|
+
})
|
|
266
|
+
}}
|
|
267
|
+
>
|
|
268
|
+
<Image source={res.icon_save} style={{ width: cx(24), height: cx(24), tintColor: props.theme?.global.fontColor }} />
|
|
269
|
+
</TouchableOpacity>
|
|
270
|
+
<TouchableOpacity
|
|
271
|
+
style={{paddingLeft: cx(16)}}
|
|
272
|
+
onPress={() => {
|
|
273
|
+
setBiorhythmListVisible(true)
|
|
274
|
+
}}
|
|
275
|
+
>
|
|
276
|
+
<Image source={res.icon_import} style={{ width: cx(24), height: cx(24), tintColor: props.theme?.global.fontColor }} />
|
|
277
|
+
</TouchableOpacity>
|
|
278
|
+
</View>}
|
|
268
279
|
headlineText={I18n.getLang('add_new_trigger_time_system_back_text')}
|
|
269
280
|
headlineIconContent={<Switch
|
|
270
281
|
value={state.enable}
|
|
@@ -177,6 +177,9 @@ const DiyScenePage = (props: { theme?: ThemeType }) => {
|
|
|
177
177
|
infoLine: {
|
|
178
178
|
marginHorizontal: cx(24),
|
|
179
179
|
},
|
|
180
|
+
rightIconView: {
|
|
181
|
+
flexDirection: 'row'
|
|
182
|
+
},
|
|
180
183
|
addMoodPopover: {
|
|
181
184
|
position: 'absolute',
|
|
182
185
|
right: cx(60),
|
|
@@ -189,6 +192,9 @@ const DiyScenePage = (props: { theme?: ThemeType }) => {
|
|
|
189
192
|
alignItems: 'flex-start',
|
|
190
193
|
alignSelf: 'flex-start'
|
|
191
194
|
},
|
|
195
|
+
refresh: {
|
|
196
|
+
paddingLeft: cx(24)
|
|
197
|
+
},
|
|
192
198
|
})
|
|
193
199
|
|
|
194
200
|
return (
|
|
@@ -196,10 +202,33 @@ const DiyScenePage = (props: { theme?: ThemeType }) => {
|
|
|
196
202
|
<Page
|
|
197
203
|
backText={uaGroupInfo.name}
|
|
198
204
|
headlineText={I18n.getLang('mood_overview_headline_text')}
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
205
|
+
headlineIconContent={<View style={styles.rightIconView}>
|
|
206
|
+
<TouchableOpacity
|
|
207
|
+
style={styles.refresh}
|
|
208
|
+
onPress={() => {
|
|
209
|
+
showDialog({
|
|
210
|
+
method: 'confirm',
|
|
211
|
+
title: I18n.getLang('mood_resetbutton'),
|
|
212
|
+
subTitle: I18n.getLang('reset_mooddescription'),
|
|
213
|
+
onConfirm: async (_, { close }) => {
|
|
214
|
+
close()
|
|
215
|
+
resetMixScenes().then();
|
|
216
|
+
}
|
|
217
|
+
})
|
|
218
|
+
}}
|
|
219
|
+
>
|
|
220
|
+
<Image source={{uri: res.ic_refresh}} style={{ width: cx(24), height: cx(24), tintColor: props.theme?.global.fontColor }} />
|
|
221
|
+
</TouchableOpacity>
|
|
222
|
+
{state.originScenes.length < MAX_MOOD_COUNT && <TouchableOpacity
|
|
223
|
+
style={styles.refresh}
|
|
224
|
+
onPress={() => {
|
|
225
|
+
navigationRoute('add', cloneDeep(DEFAULT_SCENE))
|
|
226
|
+
}}
|
|
227
|
+
>
|
|
228
|
+
<Image source={{uri: res.add}} style={{ width: cx(24), height: cx(24), tintColor: props.theme?.global.brand }} />
|
|
229
|
+
</TouchableOpacity>
|
|
230
|
+
}
|
|
231
|
+
</View>}
|
|
203
232
|
loading={state.loading}
|
|
204
233
|
>
|
|
205
234
|
<View style={styles.tagLine}>
|
|
@@ -227,21 +256,6 @@ const DiyScenePage = (props: { theme?: ThemeType }) => {
|
|
|
227
256
|
}}
|
|
228
257
|
/>
|
|
229
258
|
</View>
|
|
230
|
-
<TouchableOpacity style={{ alignItems: 'flex-end',paddingRight: cx(24) }}
|
|
231
|
-
onPress={() => {
|
|
232
|
-
showDialog({
|
|
233
|
-
method: 'confirm',
|
|
234
|
-
title: I18n.getLang('mood_resetbutton'),
|
|
235
|
-
subTitle: I18n.getLang('reset_mooddescription'),
|
|
236
|
-
onConfirm: async (_, { close }) => {
|
|
237
|
-
close()
|
|
238
|
-
resetMixScenes().then();
|
|
239
|
-
}
|
|
240
|
-
})
|
|
241
|
-
}}
|
|
242
|
-
>
|
|
243
|
-
<Image source={{uri: res.ic_refresh}} style={{ width: cx(24), height: cx(24), tintColor: props.theme?.global.fontColor }} />
|
|
244
|
-
</TouchableOpacity>
|
|
245
259
|
<Spacer height={cx(10)} />
|
|
246
260
|
{state.originScenes.length >= MAX_MOOD_COUNT && (
|
|
247
261
|
<View style={styles.infoLine}>
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import MoodColorsLine from '@ledvance/base/src/components/MoodColorsLine'
|
|
2
|
+
import Spacer from '@ledvance/base/src/components/Spacer'
|
|
3
|
+
import res from '@ledvance/base/src/res'
|
|
4
|
+
import { hsv2Hex, mapFloatToRange } from '@ledvance/base/src/utils'
|
|
5
|
+
import { cctToColor } from '@ledvance/base/src/utils/cctUtils'
|
|
6
|
+
import React, { useState } from 'react'
|
|
7
|
+
import { Image, LayoutChangeEvent, StyleSheet, View } from 'react-native'
|
|
8
|
+
import { Utils } from 'tuya-panel-kit'
|
|
9
|
+
import { MoodLampInfo } from './Interface'
|
|
10
|
+
|
|
11
|
+
const cx = Utils.RatioUtils.convertX;
|
|
12
|
+
const { withTheme } = Utils.ThemeUtils;
|
|
13
|
+
|
|
14
|
+
const MixMoodColorsLine = (props: {
|
|
15
|
+
mixSubLight: MoodLampInfo;
|
|
16
|
+
isMix: boolean;
|
|
17
|
+
type: 'gradient' | 'separate';
|
|
18
|
+
width?: number; // 外部传入的宽度(父组件已计算好)
|
|
19
|
+
}) => {
|
|
20
|
+
const { mixSubLight, isMix, type, width: propWidth } = props;
|
|
21
|
+
const [measuredWidth, setMeasuredWidth] = useState(0);
|
|
22
|
+
|
|
23
|
+
const handleLayout = (event: LayoutChangeEvent) => {
|
|
24
|
+
const { width } = event.nativeEvent.layout;
|
|
25
|
+
if (width > 0 && width !== measuredWidth) {
|
|
26
|
+
setMeasuredWidth(width);
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
const lightColors = (mixSubLight.enable && mixSubLight.nodes.length > 0)
|
|
31
|
+
? mixSubLight.nodes.map(n => {
|
|
32
|
+
const s = Math.round(mapFloatToRange(n.s / 100, 30, 100));
|
|
33
|
+
return n.isColorNode
|
|
34
|
+
? hsv2Hex(n.h, s, Math.round(mapFloatToRange(n.v / 100, 50, 100)))
|
|
35
|
+
: cctToColor(n.colorTemp.toFixed());
|
|
36
|
+
})
|
|
37
|
+
: ['#eee'];
|
|
38
|
+
|
|
39
|
+
// 如果父组件传入了精确宽度,直接使用;否则使用内部测量的宽度。
|
|
40
|
+
const finalWidth = propWidth || measuredWidth;
|
|
41
|
+
|
|
42
|
+
// 渲染颜色条的通用逻辑
|
|
43
|
+
const renderColorLine = () => (
|
|
44
|
+
finalWidth > 0 && (
|
|
45
|
+
<MoodColorsLine
|
|
46
|
+
nodeStyle={{ borderColor: '#ccc', borderWidth: 1 }}
|
|
47
|
+
width={finalWidth}
|
|
48
|
+
type={type}
|
|
49
|
+
colors={lightColors}
|
|
50
|
+
/>
|
|
51
|
+
)
|
|
52
|
+
);
|
|
53
|
+
|
|
54
|
+
// 渲染右侧图标的通用逻辑
|
|
55
|
+
const renderIcon = () => (
|
|
56
|
+
isMix && (
|
|
57
|
+
<>
|
|
58
|
+
<Spacer width={cx(7)}/>
|
|
59
|
+
<View style={styles.mixLineIconView}>
|
|
60
|
+
<Image style={styles.mixLineIcon} source={{ uri: mixSubLight.enable ? res.light_on : res.light_off }}/>
|
|
61
|
+
</View>
|
|
62
|
+
</>
|
|
63
|
+
)
|
|
64
|
+
);
|
|
65
|
+
|
|
66
|
+
// Case 1: 父组件已经计算并传入了宽度 (最高效)
|
|
67
|
+
if (propWidth) {
|
|
68
|
+
return (
|
|
69
|
+
<View style={styles.mixLineRow}>
|
|
70
|
+
{renderColorLine()}
|
|
71
|
+
{renderIcon()}
|
|
72
|
+
</View>
|
|
73
|
+
);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Case 2: 自我测量模式 (回退方案)
|
|
77
|
+
return (
|
|
78
|
+
<View style={styles.mixLineRow}>
|
|
79
|
+
{/* 这个 View (测量器) 会自动收缩以填充'颜色条'应占的空间 */}
|
|
80
|
+
<View style={{ flex: 1 }} onLayout={handleLayout}>
|
|
81
|
+
{renderColorLine()}
|
|
82
|
+
</View>
|
|
83
|
+
{/* 图标作为测量器的兄弟节点,Flexbox 会先为它分配空间 */}
|
|
84
|
+
{renderIcon()}
|
|
85
|
+
</View>
|
|
86
|
+
);
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
const styles = StyleSheet.create({
|
|
90
|
+
mixLineRow: { flexDirection: 'row', alignItems: 'center' },
|
|
91
|
+
mixLineIconView: {
|
|
92
|
+
width: cx(24),
|
|
93
|
+
height: cx(24),
|
|
94
|
+
justifyContent: 'center',
|
|
95
|
+
alignItems: 'center',
|
|
96
|
+
backgroundColor: '#aaa',
|
|
97
|
+
borderRadius: cx(8),
|
|
98
|
+
},
|
|
99
|
+
mixLineIcon: { width: cx(16), height: cx(16), tintColor: '#fff' },
|
|
100
|
+
});
|
|
101
|
+
|
|
102
|
+
export default withTheme(MixMoodColorsLine)
|
|
@@ -108,14 +108,6 @@ function getRGBWDefSceneList(): RemoteMoodInfo[] {
|
|
|
108
108
|
t: 0,
|
|
109
109
|
e: false,
|
|
110
110
|
},
|
|
111
|
-
{
|
|
112
|
-
n: I18n.getLang('mesh_device_detail_lighting_color_mode'),
|
|
113
|
-
i:
|
|
114
|
-
'05464601000003e803e800000000464601007803e803e80000000046460100f003e803e800000000464601003d03e803e80000000046460100ae03e803e800000000464601011303e803e800000000',
|
|
115
|
-
s: '',
|
|
116
|
-
t: 0,
|
|
117
|
-
e: false,
|
|
118
|
-
},
|
|
119
111
|
{
|
|
120
112
|
n: I18n.getLang('mesh_device_detail_lighting_white_mode'),
|
|
121
113
|
i: '0646460100000000000003e8000046460100000000000003e8019046460100000000000003e803e8',
|
|
@@ -156,13 +148,6 @@ function getRGBDefSceneList(): RemoteMoodInfo[] {
|
|
|
156
148
|
s: '',
|
|
157
149
|
t: 0,
|
|
158
150
|
e: false,
|
|
159
|
-
},
|
|
160
|
-
{
|
|
161
|
-
n: I18n.getLang('mesh_device_detail_lighting_color_mode'),
|
|
162
|
-
i: '05464601000003e803e800000000464601007803e803e80000000046460100f003e803e800000000464601003d03e803e80000000046460100ae03e803e800000000464601011303e803e800000000',
|
|
163
|
-
s: '',
|
|
164
|
-
t: 0,
|
|
165
|
-
e: false,
|
|
166
151
|
},
|
|
167
152
|
...defColorSceneList,
|
|
168
153
|
];
|
|
@@ -198,14 +183,6 @@ function getOnlyRGBDefSceneList(): RemoteMoodInfo[] {
|
|
|
198
183
|
t: 0,
|
|
199
184
|
e: false,
|
|
200
185
|
},
|
|
201
|
-
{
|
|
202
|
-
n: I18n.getLang('mesh_device_detail_lighting_color_mode'),
|
|
203
|
-
i:
|
|
204
|
-
'05464601000003e803e800000000464601007803e803e80000000046460100f003e803e800000000464601003d03e803e80000000046460100ae03e803e800000000464601011303e803e800000000',
|
|
205
|
-
s: '',
|
|
206
|
-
t: 0,
|
|
207
|
-
e: false,
|
|
208
|
-
},
|
|
209
186
|
...defColorSceneList,
|
|
210
187
|
];
|
|
211
188
|
}
|
|
@@ -521,6 +498,55 @@ function getStringLightSceneList(): RemoteMoodInfo[] {
|
|
|
521
498
|
t: 0,
|
|
522
499
|
e: false,
|
|
523
500
|
},
|
|
501
|
+
{
|
|
502
|
+
n: I18n.getLang('strip_lights_mood_text45'),
|
|
503
|
+
i: '0125101e1e0000003700a032000000003700c3640000000037011364000000003700782800000000',
|
|
504
|
+
s: '',
|
|
505
|
+
t: 0,
|
|
506
|
+
e: false,
|
|
507
|
+
},
|
|
508
|
+
{
|
|
509
|
+
n: I18n.getLang('strip_lights_mood_text46'),
|
|
510
|
+
i: '01261014140000002d00174a000000002d002348000000002d001e24000000002d00014000000000',
|
|
511
|
+
s: '',
|
|
512
|
+
t: 0,
|
|
513
|
+
e: false,
|
|
514
|
+
},
|
|
515
|
+
{
|
|
516
|
+
n: I18n.getLang('strip_lights_mood_text47'),
|
|
517
|
+
i: '01270a555500000055012c64000000005500b4640000000055010f51000000005501485c00000000',
|
|
518
|
+
s: '',
|
|
519
|
+
t: 0,
|
|
520
|
+
e: false,
|
|
521
|
+
},
|
|
522
|
+
{
|
|
523
|
+
n: I18n.getLang('strip_lights_mood_text48'),
|
|
524
|
+
i: '01281028280000003700d258000000003700b564000000003700b152000000003700f06400000000',
|
|
525
|
+
s: '',
|
|
526
|
+
t: 0,
|
|
527
|
+
e: false,
|
|
528
|
+
},
|
|
529
|
+
{
|
|
530
|
+
n: I18n.getLang('strip_lights_mood_text49'),
|
|
531
|
+
i: '0129100f0f0000002300784c000000002300504b0000000023005238000000002300545200000000',
|
|
532
|
+
s: '',
|
|
533
|
+
t: 0,
|
|
534
|
+
e: false,
|
|
535
|
+
},
|
|
536
|
+
{
|
|
537
|
+
n: I18n.getLang('strip_lights_mood_text50'),
|
|
538
|
+
i: '012a0a5050000000640000640000000064007864000000006400f0640000000064003c640000000064012c6400000000',
|
|
539
|
+
s: '',
|
|
540
|
+
t: 0,
|
|
541
|
+
e: false,
|
|
542
|
+
},
|
|
543
|
+
{
|
|
544
|
+
n: I18n.getLang('strip_lights_mood_text51'),
|
|
545
|
+
i: '012b1032320000001e00f04e000000001e011364000000001e00b42900000000',
|
|
546
|
+
s: '',
|
|
547
|
+
t: 0,
|
|
548
|
+
e: false,
|
|
549
|
+
},
|
|
524
550
|
];
|
|
525
551
|
}
|
|
526
552
|
|
|
@@ -834,6 +860,55 @@ function getStripLightSceneList(): RemoteMoodInfo[] {
|
|
|
834
860
|
t: 0,
|
|
835
861
|
e: false,
|
|
836
862
|
},
|
|
863
|
+
{
|
|
864
|
+
n: I18n.getLang('strip_lights_mood_text45'),
|
|
865
|
+
i: '013b011e1e6000003700a03200c364011364007828',
|
|
866
|
+
s: '',
|
|
867
|
+
t: 0,
|
|
868
|
+
e: false,
|
|
869
|
+
},
|
|
870
|
+
{
|
|
871
|
+
n: I18n.getLang('strip_lights_mood_text46'),
|
|
872
|
+
i: '013c0114146000002d00174a002348001e24000140',
|
|
873
|
+
s: '',
|
|
874
|
+
t: 0,
|
|
875
|
+
e: false,
|
|
876
|
+
},
|
|
877
|
+
{
|
|
878
|
+
n: I18n.getLang('strip_lights_mood_text47'),
|
|
879
|
+
i: '013d02555560000055012c6400b464010f5101485c',
|
|
880
|
+
s: '',
|
|
881
|
+
t: 0,
|
|
882
|
+
e: false,
|
|
883
|
+
},
|
|
884
|
+
{
|
|
885
|
+
n: I18n.getLang('strip_lights_mood_text48'),
|
|
886
|
+
i: '013e0128286000003700d25800b56400b15200f064',
|
|
887
|
+
s: '',
|
|
888
|
+
t: 0,
|
|
889
|
+
e: false,
|
|
890
|
+
},
|
|
891
|
+
{
|
|
892
|
+
n: I18n.getLang('strip_lights_mood_text49'),
|
|
893
|
+
i: '013f010f0f6000002300784c00504b005238005452',
|
|
894
|
+
s: '',
|
|
895
|
+
t: 0,
|
|
896
|
+
e: false,
|
|
897
|
+
},
|
|
898
|
+
{
|
|
899
|
+
n: I18n.getLang('strip_lights_mood_text50'),
|
|
900
|
+
i: '01400250506000006400006400786400f064003c64012c64',
|
|
901
|
+
s: '',
|
|
902
|
+
t: 0,
|
|
903
|
+
e: false,
|
|
904
|
+
},
|
|
905
|
+
{
|
|
906
|
+
n: I18n.getLang('strip_lights_mood_text51'),
|
|
907
|
+
i: '01410132326000001e00f04e01136400b429',
|
|
908
|
+
s: '',
|
|
909
|
+
t: 0,
|
|
910
|
+
e: false,
|
|
911
|
+
},
|
|
837
912
|
];
|
|
838
913
|
}
|
|
839
914
|
|
|
@@ -1175,6 +1250,55 @@ function getMixCeilingLightList(): RemoteMoodInfo[] {
|
|
|
1175
1250
|
t: 0,
|
|
1176
1251
|
e: false,
|
|
1177
1252
|
},
|
|
1253
|
+
{
|
|
1254
|
+
n: I18n.getLang('strip_lights_mood_text45'),
|
|
1255
|
+
i: '040d0d0100000000000003e800000d0d0100000000000003e80000',
|
|
1256
|
+
s: '013b011e1e6000003700a03200c364011364007828',
|
|
1257
|
+
t: 0,
|
|
1258
|
+
e: false,
|
|
1259
|
+
},
|
|
1260
|
+
{
|
|
1261
|
+
n: I18n.getLang('strip_lights_mood_text46'),
|
|
1262
|
+
i: '040d0d0100000000000003e800000d0d0100000000000003e80000',
|
|
1263
|
+
s: '013c0114146000002d00174a002348001e24000140',
|
|
1264
|
+
t: 0,
|
|
1265
|
+
e: false,
|
|
1266
|
+
},
|
|
1267
|
+
{
|
|
1268
|
+
n: I18n.getLang('strip_lights_mood_text47'),
|
|
1269
|
+
i: '040d0d0100000000000003e800000d0d0100000000000003e80000',
|
|
1270
|
+
s: '013d02555560000055012c6400b464010f5101485c',
|
|
1271
|
+
t: 0,
|
|
1272
|
+
e: false,
|
|
1273
|
+
},
|
|
1274
|
+
{
|
|
1275
|
+
n: I18n.getLang('strip_lights_mood_text48'),
|
|
1276
|
+
i: '040d0d0100000000000003e800000d0d0100000000000003e80000',
|
|
1277
|
+
s: '013e0128286000003700d25800b56400b15200f064',
|
|
1278
|
+
t: 0,
|
|
1279
|
+
e: false,
|
|
1280
|
+
},
|
|
1281
|
+
{
|
|
1282
|
+
n: I18n.getLang('strip_lights_mood_text49'),
|
|
1283
|
+
i: '040d0d0100000000000003e800000d0d0100000000000003e80000',
|
|
1284
|
+
s: '013f010f0f6000002300784c00504b005238005452',
|
|
1285
|
+
t: 0,
|
|
1286
|
+
e: false,
|
|
1287
|
+
},
|
|
1288
|
+
{
|
|
1289
|
+
n: I18n.getLang('strip_lights_mood_text50'),
|
|
1290
|
+
i: '040d0d0100000000000003e800000d0d0100000000000003e80000',
|
|
1291
|
+
s: '01400250506000006400006400786400f064003c64012c64',
|
|
1292
|
+
t: 0,
|
|
1293
|
+
e: false,
|
|
1294
|
+
},
|
|
1295
|
+
{
|
|
1296
|
+
n: I18n.getLang('strip_lights_mood_text51'),
|
|
1297
|
+
i: '040d0d0100000000000003e800000d0d0100000000000003e80000',
|
|
1298
|
+
s: '01410132326000001e00f04e01136400b429',
|
|
1299
|
+
t: 0,
|
|
1300
|
+
e: false,
|
|
1301
|
+
},
|
|
1178
1302
|
];
|
|
1179
1303
|
}
|
|
1180
1304
|
|
|
@@ -1200,12 +1324,6 @@ function getDefMixLightSceneList(): MixRemoteMoodInfo[] {
|
|
|
1200
1324
|
image: '',
|
|
1201
1325
|
value: '00030101020e0d0001f401f40000',
|
|
1202
1326
|
},
|
|
1203
|
-
{
|
|
1204
|
-
name: I18n.getLang('mesh_device_detail_lighting_color_mode'),
|
|
1205
|
-
image: '',
|
|
1206
|
-
value:
|
|
1207
|
-
'00040000010603464601000003e803e8464601007803e803e846460100f003e803e8464601003d03e803e846460100ae03e803e8464601011303e803e8',
|
|
1208
|
-
},
|
|
1209
1327
|
{
|
|
1210
1328
|
name: I18n.getLang('mesh_device_detail_lighting_white_mode'),
|
|
1211
1329
|
image: '',
|
|
@@ -1429,6 +1547,55 @@ const defColorSceneList: RemoteMoodInfo[] = [
|
|
|
1429
1547
|
t: 0,
|
|
1430
1548
|
e: false,
|
|
1431
1549
|
},
|
|
1550
|
+
{
|
|
1551
|
+
n: I18n.getLang('strip_lights_mood_text45'),
|
|
1552
|
+
i: '251e1e0200a001f40226000000001e1e0200c303e80226000000001e1e02011303e80226000000001e1e0200780190022600000000',
|
|
1553
|
+
s: '',
|
|
1554
|
+
t: 0,
|
|
1555
|
+
e: false,
|
|
1556
|
+
},
|
|
1557
|
+
{
|
|
1558
|
+
n: I18n.getLang('strip_lights_mood_text46'),
|
|
1559
|
+
i: '26141402001702e401c200000000141402002302d001c200000000141402001e016801c2000000001414020001028001c200000000',
|
|
1560
|
+
s: '',
|
|
1561
|
+
t: 0,
|
|
1562
|
+
e: false,
|
|
1563
|
+
},
|
|
1564
|
+
{
|
|
1565
|
+
n: I18n.getLang('strip_lights_mood_text47'),
|
|
1566
|
+
i: '27555501012c03e803520000000055550100b403e8035200000000555501010f032a03520000000055550101480398035200000000',
|
|
1567
|
+
s: '',
|
|
1568
|
+
t: 0,
|
|
1569
|
+
e: false,
|
|
1570
|
+
},
|
|
1571
|
+
{
|
|
1572
|
+
n: I18n.getLang('strip_lights_mood_text48'),
|
|
1573
|
+
i: '2828280200d2037002260000000028280200b503e802260000000028280200b1033402260000000028280200f003e8022600000000',
|
|
1574
|
+
s: '',
|
|
1575
|
+
t: 0,
|
|
1576
|
+
e: false,
|
|
1577
|
+
},
|
|
1578
|
+
{
|
|
1579
|
+
n: I18n.getLang('strip_lights_mood_text49'),
|
|
1580
|
+
i: '290f0f02007802f8015e000000000f0f02005002ee015e000000000f0f0200520230015e000000000f0f0200540334015e00000000',
|
|
1581
|
+
s: '',
|
|
1582
|
+
t: 0,
|
|
1583
|
+
e: false,
|
|
1584
|
+
},
|
|
1585
|
+
{
|
|
1586
|
+
n: I18n.getLang('strip_lights_mood_text50'),
|
|
1587
|
+
i: '2a505001000003e803e800000000505001007803e803e80000000050500100f003e803e800000000505001003c03e803e800000000505001012c03e803e800000000',
|
|
1588
|
+
s: '',
|
|
1589
|
+
t: 0,
|
|
1590
|
+
e: false,
|
|
1591
|
+
},
|
|
1592
|
+
{
|
|
1593
|
+
n: I18n.getLang('strip_lights_mood_text51'),
|
|
1594
|
+
i: '2b32320200f0030c012c00000000323202011303e8012c0000000032320200b4019a012c00000000',
|
|
1595
|
+
s: '',
|
|
1596
|
+
t: 0,
|
|
1597
|
+
e: false,
|
|
1598
|
+
},
|
|
1432
1599
|
];
|
|
1433
1600
|
|
|
1434
1601
|
const defMixColorSceneList: MixRemoteMoodInfo[] = [
|
|
@@ -1582,6 +1749,41 @@ const defMixColorSceneList: MixRemoteMoodInfo[] = [
|
|
|
1582
1749
|
image: '',
|
|
1583
1750
|
value: '002400000103034b4b0200d203e803e84b4b0200b403e803e84b4b02009603e803e8',
|
|
1584
1751
|
},
|
|
1752
|
+
{
|
|
1753
|
+
name: I18n.getLang('strip_lights_mood_text45'),
|
|
1754
|
+
image: '',
|
|
1755
|
+
value: '002500000104031e1e0200a001f402261e1e0200c303e802261e1e02011303e802261e1e02007801900226',
|
|
1756
|
+
},
|
|
1757
|
+
{
|
|
1758
|
+
name: I18n.getLang('strip_lights_mood_text46'),
|
|
1759
|
+
image: '',
|
|
1760
|
+
value: '00260000010403141402001702e401c2141402002302d001c2141402001e016801c21414020001028001c2',
|
|
1761
|
+
},
|
|
1762
|
+
{
|
|
1763
|
+
name: I18n.getLang('strip_lights_mood_text47'),
|
|
1764
|
+
image: '',
|
|
1765
|
+
value: '00270000010403555501012c03e8035255550100b403e80352555501010f032a0352555501014803980352',
|
|
1766
|
+
},
|
|
1767
|
+
{
|
|
1768
|
+
name: I18n.getLang('strip_lights_mood_text48'),
|
|
1769
|
+
image: '',
|
|
1770
|
+
value: '0028000001040328280200d20370022628280200b503e8022628280200b10334022628280200f003e80226',
|
|
1771
|
+
},
|
|
1772
|
+
{
|
|
1773
|
+
name: I18n.getLang('strip_lights_mood_text49'),
|
|
1774
|
+
image: '',
|
|
1775
|
+
value: '002900000104030f0f02007802f8015e0f0f02005002ee015e0f0f0200520230015e0f0f0200540334015e',
|
|
1776
|
+
},
|
|
1777
|
+
{
|
|
1778
|
+
name: I18n.getLang('strip_lights_mood_text50'),
|
|
1779
|
+
image: '',
|
|
1780
|
+
value: '002a0000010503505001000003e803e8505001007803e803e850500100f003e803e8505001003c03e803e8505001012c03e803e8',
|
|
1781
|
+
},
|
|
1782
|
+
{
|
|
1783
|
+
name: I18n.getLang('strip_lights_mood_text51'),
|
|
1784
|
+
image: '',
|
|
1785
|
+
value: '002b000001030332320200f0030c012c323202011303e8012c32320200b4019a012c',
|
|
1786
|
+
},
|
|
1585
1787
|
];
|
|
1586
1788
|
|
|
1587
1789
|
export type RecommendMood = {
|
|
@@ -1,15 +1,13 @@
|
|
|
1
|
-
import Card from '@ledvance/base/src/components/Card';
|
|
2
|
-
import MoodColorsLine from '@ledvance/base/src/components/MoodColorsLine';
|
|
3
|
-
import Spacer from '@ledvance/base/src/components/Spacer';
|
|
4
1
|
import ThemeType from '@ledvance/base/src/config/themeType';
|
|
5
2
|
import I18n from '@ledvance/base/src/i18n';
|
|
6
3
|
import res from '@ledvance/base/src/res';
|
|
7
4
|
import { hsv2Hex, mapFloatToRange } from '@ledvance/base/src/utils';
|
|
8
5
|
import { cctToColor } from '@ledvance/base/src/utils/cctUtils';
|
|
9
|
-
import React, { useMemo
|
|
10
|
-
import { Image,
|
|
6
|
+
import React, { useMemo } from 'react';
|
|
7
|
+
import { Image, Platform, StyleSheet, Text, TouchableOpacity, View, ViewProps, ViewStyle } from 'react-native';
|
|
8
|
+
import LinearGradient from 'react-native-linear-gradient';
|
|
11
9
|
import { Utils } from 'tuya-panel-kit';
|
|
12
|
-
import {
|
|
10
|
+
import { MoodLampInfo, MoodUIInfo } from './Interface';
|
|
13
11
|
|
|
14
12
|
const cx = Utils.RatioUtils.convertX;
|
|
15
13
|
const { withTheme } = Utils.ThemeUtils;
|
|
@@ -32,189 +30,195 @@ interface MoodItemProps extends ViewProps {
|
|
|
32
30
|
onSwitch: (enable: boolean) => void;
|
|
33
31
|
}
|
|
34
32
|
|
|
33
|
+
const getGradientColors = (lampInfo: MoodLampInfo, defaultColors: string[]): string[] => {
|
|
34
|
+
if (!lampInfo.enable || lampInfo.nodes.length === 0) {
|
|
35
|
+
return defaultColors;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
const colors = lampInfo.nodes.map(n => {
|
|
39
|
+
const s = Math.round(mapFloatToRange(n.s / 100, 30, 70));
|
|
40
|
+
const v = Math.round(mapFloatToRange(n.v / 100, 80, 100));
|
|
41
|
+
return n.isColorNode ? hsv2Hex(n.h, s, v) : cctToColor(n.colorTemp.toFixed());
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
if (colors.length === 1) {
|
|
45
|
+
return [colors[0], colors[0]];
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
return colors;
|
|
49
|
+
};
|
|
50
|
+
|
|
35
51
|
const MoodItem = (props: MoodItemProps) => {
|
|
36
|
-
const { mood, isMix, deviceTypeOption, theme } = props;
|
|
52
|
+
const { mood, isMix, deviceTypeOption, theme, onPress, onSwitch, enable, style } = props;
|
|
53
|
+
const styles = getStyles(theme);
|
|
54
|
+
|
|
37
55
|
const isDynamic = useMemo(() => {
|
|
38
56
|
return mood.mainLamp.nodes?.length > 1 || mood.secondaryLamp.nodes?.length > 1;
|
|
39
57
|
}, [mood.mainLamp.nodes, mood.secondaryLamp.nodes]);
|
|
40
58
|
|
|
41
|
-
const
|
|
42
|
-
|
|
43
|
-
), [MoodJumpGradientMode, deviceTypeOption]);
|
|
59
|
+
const isDarkMode = theme?.type === 'dark';
|
|
60
|
+
const defaultGreyGradient = isDarkMode ? ['#444444', '#333333'] : ['#E5E5E5', '#DCDCDC'];
|
|
44
61
|
|
|
45
|
-
const
|
|
62
|
+
const mainLampColors = useMemo(
|
|
63
|
+
() => getGradientColors(mood.mainLamp, defaultGreyGradient),
|
|
64
|
+
[mood.mainLamp, defaultGreyGradient]
|
|
65
|
+
);
|
|
66
|
+
const secondaryLampColors = useMemo(
|
|
67
|
+
() => getGradientColors(mood.secondaryLamp, defaultGreyGradient),
|
|
68
|
+
[mood.secondaryLamp, defaultGreyGradient]
|
|
69
|
+
);
|
|
46
70
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
<
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
<Spacer height={cx(12)}/>
|
|
74
|
-
<View style={styles.row}>
|
|
75
|
-
<View style={styles.moodTypeLabel}>
|
|
76
|
-
<Text style={styles.moodTypeLabelText}>
|
|
77
|
-
{I18n.getLang(isDynamic ? 'mood_overview_field_chip_2' : 'mood_overview_field_chip_text')}
|
|
78
|
-
</Text>
|
|
79
|
-
</View>
|
|
71
|
+
const isMixLight = isMix || deviceTypeOption?.isMixLight;
|
|
72
|
+
|
|
73
|
+
const hasMainLampColors = mood.mainLamp.enable && mood.mainLamp.nodes.length > 0;
|
|
74
|
+
const hasSecondaryLampColors = mood.secondaryLamp.enable && mood.secondaryLamp.nodes.length > 0;
|
|
75
|
+
|
|
76
|
+
const renderContent = () => (
|
|
77
|
+
// 关键修改 1: contentContainer 使用 justifyContent: 'space-between'
|
|
78
|
+
<View style={styles.contentContainer}>
|
|
79
|
+
{/* 顶部内容 */}
|
|
80
|
+
<View style={styles.row}>
|
|
81
|
+
<Text style={styles.headText}>{mood.name}</Text>
|
|
82
|
+
{/* checkbox 的 TouchableOpacity 现在也应用了阴影样式 */}
|
|
83
|
+
<TouchableOpacity style={styles.checkbox} onPress={() => onSwitch(!enable)}>
|
|
84
|
+
<Image
|
|
85
|
+
source={{ uri: res.ic_check }}
|
|
86
|
+
style={[styles.checkboxImage, { tintColor: enable ? theme?.icon.primary : theme?.icon.disable }]}
|
|
87
|
+
/>
|
|
88
|
+
</TouchableOpacity>
|
|
89
|
+
</View>
|
|
90
|
+
|
|
91
|
+
{/* 底部内容 (移除了 Spacer) */}
|
|
92
|
+
<View style={styles.row}>
|
|
93
|
+
<View style={styles.moodTypeLabel}>
|
|
94
|
+
<Text style={styles.moodTypeLabelText}>
|
|
95
|
+
{I18n.getLang(isDynamic ? 'mood_overview_field_chip_2' : 'mood_overview_field_chip_text')}
|
|
96
|
+
</Text>
|
|
80
97
|
</View>
|
|
81
98
|
</View>
|
|
82
|
-
</
|
|
99
|
+
</View>
|
|
83
100
|
);
|
|
84
|
-
};
|
|
85
101
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
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);
|
|
102
|
+
const renderBackground = () => {
|
|
103
|
+
if (!isMixLight) {
|
|
104
|
+
return <LinearGradient colors={mainLampColors} style={{ flex: 1 }} start={{ x: 0, y: 0.5 }} end={{ x: 1, y: 0.5 }} />;
|
|
100
105
|
}
|
|
101
|
-
};
|
|
102
106
|
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
const renderColorLine = () => (
|
|
117
|
-
finalWidth > 0 && (
|
|
118
|
-
<MoodColorsLine
|
|
119
|
-
nodeStyle={{ borderColor: '#ccc', borderWidth: 1 }}
|
|
120
|
-
width={finalWidth}
|
|
121
|
-
type={type}
|
|
122
|
-
colors={lightColors}
|
|
123
|
-
/>
|
|
124
|
-
)
|
|
125
|
-
);
|
|
107
|
+
if (hasMainLampColors && hasSecondaryLampColors) {
|
|
108
|
+
return (
|
|
109
|
+
<>
|
|
110
|
+
<LinearGradient colors={mainLampColors} style={{ flex: 1 }} start={{ x: 0, y: 0.5 }} end={{ x: 1, y: 0.5 }} />
|
|
111
|
+
<LinearGradient
|
|
112
|
+
colors={secondaryLampColors}
|
|
113
|
+
style={{ flex: 1 }}
|
|
114
|
+
start={{ x: 0, y: 0.5 }}
|
|
115
|
+
end={{ x: 1, y: 0.5 }}
|
|
116
|
+
/>
|
|
117
|
+
</>
|
|
118
|
+
);
|
|
119
|
+
}
|
|
126
120
|
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
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
|
-
);
|
|
121
|
+
if (hasMainLampColors) {
|
|
122
|
+
return <LinearGradient colors={mainLampColors} style={{ flex: 1 }} start={{ x: 0, y: 0.5 }} end={{ x: 1, y: 0.5 }} />;
|
|
123
|
+
}
|
|
138
124
|
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
}
|
|
125
|
+
if (hasSecondaryLampColors) {
|
|
126
|
+
return (
|
|
127
|
+
<LinearGradient colors={secondaryLampColors} style={{ flex: 1 }} start={{ x: 0, y: 0.5 }} end={{ x: 1, y: 0.5 }} />
|
|
128
|
+
);
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
return <LinearGradient colors={mainLampColors} style={{ flex: 1 }} start={{ x: 0, y: 0.5 }} end={{ x: 1, y: 0.5 }} />;
|
|
132
|
+
};
|
|
148
133
|
|
|
149
|
-
// Case 2: 自我测量模式 (回退方案)
|
|
150
134
|
return (
|
|
151
|
-
<
|
|
152
|
-
|
|
153
|
-
<View style={
|
|
154
|
-
|
|
155
|
-
</View>
|
|
156
|
-
{/* 图标作为测量器的兄弟节点,Flexbox 会先为它分配空间 */}
|
|
157
|
-
{renderIcon()}
|
|
158
|
-
</View>
|
|
135
|
+
<TouchableOpacity activeOpacity={0.8} onPress={onPress} style={[styles.container, style || { marginHorizontal: cx(24) }]}>
|
|
136
|
+
<View style={styles.backgroundWrapper}>{renderBackground()}</View>
|
|
137
|
+
<View style={StyleSheet.absoluteFill}>{renderContent()}</View>
|
|
138
|
+
</TouchableOpacity>
|
|
159
139
|
);
|
|
160
|
-
}
|
|
140
|
+
};
|
|
141
|
+
|
|
142
|
+
const getStyles = (theme?: ThemeType) => {
|
|
143
|
+
const isDarkMode = theme?.type === 'dark';
|
|
144
|
+
const primaryTextColor = isDarkMode ? '#FFFFFF' : '#000000';
|
|
145
|
+
const shadowColor = isDarkMode ? 'rgba(0, 0, 0, 0.7)' : 'rgba(255, 255, 255, 0.7)';
|
|
146
|
+
const tagBackgroundColor = isDarkMode ? 'rgba(0, 0, 0, 0.3)' : 'rgba(255, 255, 255, 0.5)';
|
|
147
|
+
const tagTextColor = isDarkMode ? '#FFFFFF' : '#333333';
|
|
148
|
+
|
|
149
|
+
return StyleSheet.create({
|
|
150
|
+
container: {
|
|
151
|
+
// marginHorizontal: cx(24),
|
|
152
|
+
height: cx(135),
|
|
153
|
+
...Platform.select({
|
|
154
|
+
ios: { shadowColor: '#000', shadowOffset: { width: 0, height: 2 }, shadowOpacity: 0.1, shadowRadius: 8 },
|
|
155
|
+
android: { elevation: 5 },
|
|
156
|
+
}),
|
|
157
|
+
},
|
|
158
|
+
backgroundWrapper: {
|
|
159
|
+
flex: 1,
|
|
160
|
+
borderRadius: cx(16),
|
|
161
|
+
overflow: 'hidden',
|
|
162
|
+
},
|
|
163
|
+
// 关键修改 1: 使用 justifyContent 将内容推向两端
|
|
164
|
+
contentContainer: {
|
|
165
|
+
flex: 1,
|
|
166
|
+
paddingHorizontal: cx(16),
|
|
167
|
+
paddingVertical: cx(12),
|
|
168
|
+
backgroundColor: 'transparent',
|
|
169
|
+
justifyContent: 'space-between', // 使顶部和底部内容分别贴近上下边缘
|
|
170
|
+
},
|
|
171
|
+
row: {
|
|
172
|
+
flexDirection: 'row',
|
|
173
|
+
alignItems: 'center',
|
|
174
|
+
},
|
|
175
|
+
headText: {
|
|
176
|
+
flex: 1,
|
|
177
|
+
fontSize: cx(16),
|
|
178
|
+
lineHeight: cx(20),
|
|
179
|
+
fontWeight: 'bold',
|
|
180
|
+
color: primaryTextColor,
|
|
181
|
+
textShadowColor: shadowColor,
|
|
182
|
+
textShadowOffset: { width: 0, height: 1 },
|
|
183
|
+
textShadowRadius: 3,
|
|
184
|
+
},
|
|
185
|
+
// 关键修改 2: 为 checkbox 添加阴影
|
|
186
|
+
checkbox: {
|
|
187
|
+
width: cx(45),
|
|
188
|
+
height: cx(45),
|
|
189
|
+
marginRight: cx(-10),
|
|
190
|
+
justifyContent: 'center',
|
|
191
|
+
alignItems: 'flex-end',
|
|
192
|
+
// 添加一个通用的深色阴影以提供对比度
|
|
193
|
+
...Platform.select({
|
|
194
|
+
ios: {
|
|
195
|
+
shadowColor: 'rgba(0, 0, 0, 0.4)',
|
|
196
|
+
shadowOffset: { width: 0, height: 1 },
|
|
197
|
+
shadowRadius: 2,
|
|
198
|
+
shadowOpacity: 1,
|
|
199
|
+
},
|
|
200
|
+
android: {
|
|
201
|
+
elevation: 3,
|
|
202
|
+
},
|
|
203
|
+
}),
|
|
204
|
+
},
|
|
205
|
+
checkboxImage: {
|
|
206
|
+
width: cx(44),
|
|
207
|
+
height: cx(44),
|
|
208
|
+
},
|
|
209
|
+
moodTypeLabel: {
|
|
210
|
+
paddingHorizontal: cx(12.5),
|
|
211
|
+
paddingVertical: cx(4),
|
|
212
|
+
borderRadius: cx(8),
|
|
213
|
+
backgroundColor: tagBackgroundColor,
|
|
214
|
+
},
|
|
215
|
+
moodTypeLabelText: {
|
|
216
|
+
fontSize: cx(10),
|
|
217
|
+
lineHeight: cx(12),
|
|
218
|
+
color: tagTextColor,
|
|
219
|
+
fontWeight: '500',
|
|
220
|
+
},
|
|
221
|
+
});
|
|
222
|
+
};
|
|
161
223
|
|
|
162
|
-
|
|
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);
|
|
224
|
+
export default withTheme(MoodItem);
|
|
@@ -9,7 +9,7 @@ import {
|
|
|
9
9
|
useSwitchLed,
|
|
10
10
|
useWorkMode
|
|
11
11
|
} from './MoodActions';
|
|
12
|
-
import {useMoods, useUAGroupInfo,} from '@ledvance/base/src/models/modules/NativePropsSlice'
|
|
12
|
+
import { useIsPad, useMoods, useUAGroupInfo, } from '@ledvance/base/src/models/modules/NativePropsSlice'
|
|
13
13
|
import {useReactive} from 'ahooks';
|
|
14
14
|
import Strings from '@ledvance/base/src/i18n';
|
|
15
15
|
import I18n from '@ledvance/base/src/i18n';
|
|
@@ -30,12 +30,17 @@ import {WorkMode} from '@ledvance/base/src/utils/interface';
|
|
|
30
30
|
import ThemeType from '@ledvance/base/src/config/themeType'
|
|
31
31
|
import {showDialog} from '@ledvance/base/src/utils/common';
|
|
32
32
|
|
|
33
|
-
const cx = Utils.RatioUtils
|
|
33
|
+
const { convertX: cx } = Utils.RatioUtils;
|
|
34
34
|
const { withTheme } = Utils.ThemeUtils
|
|
35
35
|
|
|
36
36
|
const MAX_MOOD_COUNT = 255;
|
|
37
37
|
|
|
38
|
+
// --- 将布局常量移到这里,方便管理 ---
|
|
39
|
+
const OPTIMAL_ITEM_WIDTH = cx(160); // 定义一个在所有设备上看起来都很好的“最佳宽度”
|
|
40
|
+
const GRID_GAP = cx(16);
|
|
41
|
+
|
|
38
42
|
const MoodPage = (props: {theme?: ThemeType}) => {
|
|
43
|
+
// ... (所有 hooks 和 state 定义保持不变) ...
|
|
39
44
|
const params = useParams<MoodPageParams>();
|
|
40
45
|
const uaGroupInfo = useUAGroupInfo()
|
|
41
46
|
const navigation = useNavigation();
|
|
@@ -45,6 +50,7 @@ const MoodPage = (props: {theme?: ThemeType}) => {
|
|
|
45
50
|
const rgbicWorkMode = useRgbicWorkMode()
|
|
46
51
|
const [moods, setMoods] = useMoods();
|
|
47
52
|
const [flagMode, setFlagMode] = useFlagMode();
|
|
53
|
+
const isPad = useIsPad()
|
|
48
54
|
const state = useReactive<MoodPageState>({
|
|
49
55
|
currentMood: undefined,
|
|
50
56
|
staticTagChecked: true,
|
|
@@ -57,6 +63,28 @@ const MoodPage = (props: {theme?: ThemeType}) => {
|
|
|
57
63
|
flag: Symbol(),
|
|
58
64
|
});
|
|
59
65
|
|
|
66
|
+
// --- 关键逻辑 1: 基于 isPad 决定列数,并计算出 FlatList 自身所需的总宽度 ---
|
|
67
|
+
const { numColumns, flatListWidth } = useMemo(() => {
|
|
68
|
+
const cols = isPad ? 3 : 2;
|
|
69
|
+
// FlatList 的总宽度 = (项目数 * 项目宽度) + (项目数 - 1) * 间距
|
|
70
|
+
const listWidth = cols * OPTIMAL_ITEM_WIDTH + (cols - 1) * GRID_GAP;
|
|
71
|
+
return {
|
|
72
|
+
numColumns: cols,
|
|
73
|
+
flatListWidth: listWidth,
|
|
74
|
+
};
|
|
75
|
+
}, [isPad]);
|
|
76
|
+
// --- 关键逻辑 2: 准备幽灵元素数据 (逻辑不变) ---
|
|
77
|
+
const gridData = useMemo(() => {
|
|
78
|
+
const data = state.filterMoods;
|
|
79
|
+
const numToPad = numColumns - (data.length % numColumns);
|
|
80
|
+
if (numToPad === numColumns) {
|
|
81
|
+
return data;
|
|
82
|
+
}
|
|
83
|
+
const ghostItems = Array.from({ length: numToPad }, (_, i) => ({ name: `ghost-${i}`, isGhost: true }));
|
|
84
|
+
return [...data, ...ghostItems];
|
|
85
|
+
}, [state.filterMoods, numColumns]);
|
|
86
|
+
|
|
87
|
+
// ... (所有 useEffect 和函数逻辑保持不变) ...
|
|
60
88
|
const moodIds = useMemo(() => {
|
|
61
89
|
// @ts-ignore
|
|
62
90
|
const mainIds = map(params.isCeilingLight ? (state.originMoods.map(m => m.mainLamp) || []) : state.originMoods, 'id').filter(v => v !== undefined)
|
|
@@ -270,6 +298,9 @@ const MoodPage = (props: {theme?: ThemeType}) => {
|
|
|
270
298
|
infoLine: {
|
|
271
299
|
marginHorizontal: cx(24),
|
|
272
300
|
},
|
|
301
|
+
rightIconView: {
|
|
302
|
+
flexDirection: 'row'
|
|
303
|
+
},
|
|
273
304
|
addMoodPopover: {
|
|
274
305
|
position: 'absolute',
|
|
275
306
|
right: cx(60),
|
|
@@ -281,6 +312,18 @@ const MoodPage = (props: {theme?: ThemeType}) => {
|
|
|
281
312
|
padding: cx(5),
|
|
282
313
|
alignItems: 'flex-start',
|
|
283
314
|
},
|
|
315
|
+
refresh: {
|
|
316
|
+
paddingLeft: cx(24)
|
|
317
|
+
},
|
|
318
|
+
// 重新启用 columnWrapperStyle
|
|
319
|
+
columnWrapperStyle: {
|
|
320
|
+
justifyContent: 'space-between',
|
|
321
|
+
},
|
|
322
|
+
// --- 关键逻辑 3: 新增列表容器样式 ---
|
|
323
|
+
listContainer: {
|
|
324
|
+
flex: 1,
|
|
325
|
+
alignItems: 'center', // 让内部的 FlatList 水平居中
|
|
326
|
+
},
|
|
284
327
|
})
|
|
285
328
|
|
|
286
329
|
return (
|
|
@@ -288,14 +331,37 @@ const MoodPage = (props: {theme?: ThemeType}) => {
|
|
|
288
331
|
<Page
|
|
289
332
|
backText={uaGroupInfo.name}
|
|
290
333
|
headlineText={Strings.getLang('mood_overview_headline_text')}
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
334
|
+
headlineIconContent={<View style={styles.rightIconView}>
|
|
335
|
+
<TouchableOpacity
|
|
336
|
+
style={styles.refresh}
|
|
337
|
+
onPress={() => {
|
|
338
|
+
showDialog({
|
|
339
|
+
method: 'confirm',
|
|
340
|
+
title: I18n.getLang('mood_resetbutton'),
|
|
341
|
+
subTitle: I18n.getLang('reset_mooddescription'),
|
|
342
|
+
onConfirm: (_, { close }) => {
|
|
343
|
+
close()
|
|
344
|
+
getMoodList(true)
|
|
345
|
+
}
|
|
346
|
+
})
|
|
347
|
+
}}
|
|
348
|
+
>
|
|
349
|
+
<Image source={{uri: res.ic_refresh}} style={{ width: cx(24), height: cx(24), tintColor: props.theme?.global.fontColor }} />
|
|
350
|
+
</TouchableOpacity>
|
|
351
|
+
{state.originMoods.length < MAX_MOOD_COUNT && <TouchableOpacity
|
|
352
|
+
style={styles.refresh}
|
|
353
|
+
onPress={() => {
|
|
354
|
+
if (params.isStringLight || params.isStripLight) {
|
|
355
|
+
onAddMoodDialogItemClick(false, 1);
|
|
356
|
+
} else {
|
|
357
|
+
state.showAddMoodPopover = !state.showAddMoodPopover;
|
|
358
|
+
}
|
|
359
|
+
}}
|
|
360
|
+
>
|
|
361
|
+
<Image source={{uri: res.add}} style={{ width: cx(24), height: cx(24), tintColor: props.theme?.global.brand }} />
|
|
362
|
+
</TouchableOpacity>
|
|
297
363
|
}
|
|
298
|
-
}
|
|
364
|
+
</View>}
|
|
299
365
|
loading={state.loading}
|
|
300
366
|
>
|
|
301
367
|
{!params.isStripLight && !params.isStringLight && <View style={styles.tagLine}>
|
|
@@ -315,21 +381,6 @@ const MoodPage = (props: {theme?: ThemeType}) => {
|
|
|
315
381
|
}}
|
|
316
382
|
/>
|
|
317
383
|
</View>}
|
|
318
|
-
<TouchableOpacity style={{ alignItems: 'flex-end',paddingRight: cx(24) }}
|
|
319
|
-
onPress={() => {
|
|
320
|
-
showDialog({
|
|
321
|
-
method: 'confirm',
|
|
322
|
-
title: I18n.getLang('mood_resetbutton'),
|
|
323
|
-
subTitle: I18n.getLang('reset_mooddescription'),
|
|
324
|
-
onConfirm: (_, { close }) => {
|
|
325
|
-
close()
|
|
326
|
-
getMoodList(true)
|
|
327
|
-
}
|
|
328
|
-
})
|
|
329
|
-
}}
|
|
330
|
-
>
|
|
331
|
-
<Image source={{uri: res.ic_refresh}} style={{ width: cx(24), height: cx(24), tintColor: props.theme?.global.fontColor }} />
|
|
332
|
-
</TouchableOpacity>
|
|
333
384
|
<Spacer height={cx(10)} />
|
|
334
385
|
{state.originMoods.length >= MAX_MOOD_COUNT && (
|
|
335
386
|
<View style={styles.infoLine}>
|
|
@@ -342,32 +393,44 @@ const MoodPage = (props: {theme?: ThemeType}) => {
|
|
|
342
393
|
<Spacer height={cx(6)} />
|
|
343
394
|
</View>
|
|
344
395
|
)}
|
|
345
|
-
<
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
396
|
+
<View style={styles.listContainer}>
|
|
397
|
+
<FlatList
|
|
398
|
+
data={gridData}
|
|
399
|
+
numColumns={numColumns}
|
|
400
|
+
key={numColumns}
|
|
401
|
+
style={{ width: flatListWidth }} // 给 FlatList 一个固定的总宽度
|
|
402
|
+
// 移除 contentContainerStyle
|
|
403
|
+
columnWrapperStyle={styles.columnWrapperStyle}
|
|
404
|
+
renderItem={({ item }) => {
|
|
405
|
+
if (item.isGhost) {
|
|
406
|
+
return <View style={{ width: OPTIMAL_ITEM_WIDTH, marginBottom: cx(16) }} />;
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
return (
|
|
410
|
+
<MoodItem
|
|
411
|
+
style={{ width: OPTIMAL_ITEM_WIDTH }} // 所有项目都使用最佳宽度
|
|
412
|
+
enable={getItemEnable(item)}
|
|
413
|
+
isMix={!!(params.isMixLight || params.isCeilingLight)}
|
|
414
|
+
deviceTypeOption={params}
|
|
415
|
+
mood={item}
|
|
416
|
+
onPress={() => {
|
|
417
|
+
navigationRoute(item.mainLamp.nodes.length === 1, 'edit', item);
|
|
418
|
+
}}
|
|
419
|
+
onSwitch={async _ => {
|
|
420
|
+
state.loading = true;
|
|
421
|
+
await modDeleteMood('set', item);
|
|
422
|
+
updateFlagMode();
|
|
423
|
+
state.loading = false;
|
|
424
|
+
}}
|
|
425
|
+
/>
|
|
426
|
+
);
|
|
427
|
+
}}
|
|
428
|
+
ListHeaderComponent={() => <Spacer height={cx(10)} />}
|
|
429
|
+
ItemSeparatorComponent={() => <Spacer />}
|
|
430
|
+
ListFooterComponent={() => <Spacer />}
|
|
431
|
+
keyExtractor={item => `${item.name}`}
|
|
432
|
+
/>
|
|
433
|
+
</View>
|
|
371
434
|
</Page>
|
|
372
435
|
<CustomListDialog
|
|
373
436
|
show={state.showAddMoodPopover}
|
|
@@ -393,4 +456,4 @@ const MoodPage = (props: {theme?: ThemeType}) => {
|
|
|
393
456
|
};
|
|
394
457
|
|
|
395
458
|
|
|
396
|
-
export default withTheme(MoodPage)
|
|
459
|
+
export default withTheme(MoodPage)
|
|
@@ -7,7 +7,7 @@ import Spacer from '@ledvance/base/src/components/Spacer';
|
|
|
7
7
|
import ThemeType from '@ledvance/base/src/config/themeType';
|
|
8
8
|
|
|
9
9
|
import { MoodJumpGradientMode, MoodUIInfo } from './Interface';
|
|
10
|
-
import
|
|
10
|
+
import MixMoodColorsLine from './MixMoodColorsLine';
|
|
11
11
|
|
|
12
12
|
const cx = Utils.RatioUtils.convertX;
|
|
13
13
|
const { withTheme } = Utils.ThemeUtils;
|
|
@@ -410,7 +410,7 @@ const TimeScheduleDetailPage = (props: { theme?: ThemeType }) => {
|
|
|
410
410
|
/>
|
|
411
411
|
<Spacer />
|
|
412
412
|
<Text style={{ ...styles.cardContainer, color: props.theme?.global.fontColor, fontSize: cx(14) }}>
|
|
413
|
-
{loopText(state.timeSchedule.loops.split(''))}
|
|
413
|
+
{loopText(state.timeSchedule.loops.split(''), state.timeSchedule.time)}
|
|
414
414
|
</Text>
|
|
415
415
|
<Spacer height={cx(30)} />
|
|
416
416
|
|