@ledvance/group-ui-biz-bundle 1.0.39 → 1.0.40

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/group-ui-biz-bundle",
5
5
  "pid": [],
6
6
  "uiid": "",
7
- "version": "1.0.39",
7
+ "version": "1.0.40",
8
8
  "scripts": {},
9
9
  "dependencies": {
10
10
  "@ledvance/base": "^1.x",
@@ -0,0 +1,114 @@
1
+ /**
2
+ * 循环定时:最大 10 个定时功能。每 10 个字节代表含义如下:
3
+ *
4
+ * 第 1 个字节:通道号。bit0 代表开关,bit7-bit1 代表通道号。
5
+ * 第 2 个字节:星期。00:单次,01:周日,02:周一,04:周二,08:周三,10:周四,20:周五,40:周六。
6
+ * 第 3 个字节和第 4 个字节:起始时间。单位:分钟。
7
+ * 第 5 个字节和第 6 个字节:结束时间。单位:分钟。
8
+ * 第 7 个字节和第 8 个字节:开启时间。单位:分钟。
9
+ * 第 9 个字节和第 10 个字节:关闭时间。单位:分钟。
10
+ */
11
+
12
+ import {useFeatureHook} from "@ledvance/base/src/models/modules/NativePropsSlice";
13
+ import { Buffer } from "buffer";
14
+
15
+
16
+ export interface FixedTimeParam {
17
+ channels: string[],
18
+ is24Hour?: boolean
19
+ }
20
+
21
+ export interface FixedTimeItem {
22
+ enable: boolean
23
+ channel?: number
24
+ weeks: number[]
25
+ startTime: number
26
+ endTime: number
27
+ onTime: number
28
+ offTime: number
29
+ name?: string
30
+ index?: number
31
+ }
32
+
33
+ interface PlugConfig {
34
+ cycleTime: FixedTimeItem[]
35
+ }
36
+
37
+ export function useFixedTime() {
38
+ return useFeatureHook<PlugConfig, FixedTimeItem[]>('cycleTime', [], (val) => fixedTimeItemsToBase64(val))
39
+ }
40
+ // @ts-ignore
41
+ const fixedTimeItemsFromBase64 = (base64Str: string): FixedTimeItem[] => {
42
+ const buffer = Buffer.from(base64Str, 'base64');
43
+
44
+ const items: FixedTimeItem[] = [];
45
+
46
+ for (let i = 0; i < buffer.length; i += 10) {
47
+ const enable = (buffer[i] & 0x01) === 1;
48
+ const channel = (buffer[i] >> 1) & 0x7F;
49
+
50
+ const week = buffer[i + 1];
51
+ const weeks: number[] = [
52
+ (week & 0b00000001) >> 0,
53
+ (week & 0b00000010) >> 1,
54
+ (week & 0b00000100) >> 2,
55
+ (week & 0b00001000) >> 3,
56
+ (week & 0b00010000) >> 4,
57
+ (week & 0b00100000) >> 5,
58
+ (week & 0b01000000) >> 6,
59
+ (week & 0b10000000) >> 7,
60
+ ];
61
+
62
+ const startTime = (buffer[i + 2] << 8) | buffer[i + 3];
63
+ const endTime = (buffer[i + 4] << 8) | buffer[i + 5];
64
+ const onTime = (buffer[i + 6] << 8) | buffer[i + 7];
65
+ const offTime = (buffer[i + 8] << 8) | buffer[i + 9];
66
+
67
+ items.push({
68
+ enable,
69
+ channel,
70
+ weeks,
71
+ startTime,
72
+ endTime,
73
+ onTime,
74
+ offTime,
75
+ name: `FixedTime ${items.length}`,
76
+ index: items.length
77
+ });
78
+ }
79
+
80
+ return items;
81
+ }
82
+
83
+ export const fixedTimeItemsToBase64 = (items: FixedTimeItem[]): string => {
84
+ const buffer = new Uint8Array(10 * items.length);
85
+
86
+ items.forEach((item, index) => {
87
+ const offset = index * 10;
88
+
89
+ // Byte 1: Channel (bit0: enable, bit7-bit1: channel)
90
+ buffer[offset] = (item.enable ? 1 : 0) | ((item.channel ?? 0) << 1);
91
+
92
+ // Byte 2: Weeks
93
+ buffer[offset + 1] = item.weeks.reduce((acc, bit, i) => acc | (bit << i), 0);
94
+
95
+ // Byte 3-4: Start time
96
+ buffer[offset + 2] = (item.startTime >> 8) & 0xFF;
97
+ buffer[offset + 3] = item.startTime & 0xFF;
98
+
99
+ // Byte 5-6: End time
100
+ buffer[offset + 4] = (item.endTime >> 8) & 0xFF;
101
+ buffer[offset + 5] = item.endTime & 0xFF;
102
+
103
+ // Byte 7-8: On time
104
+ buffer[offset + 6] = (item.onTime >> 8) & 0xFF;
105
+ buffer[offset + 7] = item.onTime & 0xFF;
106
+
107
+ // Byte 9-10: Off time
108
+ buffer[offset + 8] = (item.offTime >> 8) & 0xFF;
109
+ buffer[offset + 9] = item.offTime & 0xFF;
110
+ });
111
+
112
+ return Buffer.from(buffer).toString('base64');
113
+ }
114
+
@@ -0,0 +1,350 @@
1
+ import React, {useEffect, useMemo} from "react";
2
+ import Page from "@ledvance/base/src/components/Page";
3
+ import {TimerPicker, Utils} from "tuya-panel-kit";
4
+ import dayjs from "dayjs";
5
+ import {FixedTimeItem, FixedTimeParam} from "./FixedTimeForPlugActon";
6
+ import {useReactive, useUpdateEffect} from "ahooks";
7
+ import {cloneDeep, isEqual} from "lodash";
8
+ import {loopText, showDialog} from "@ledvance/base/src/utils/common";
9
+ import I18n from "@ledvance/base/src/i18n/index";
10
+ import {useNavigation, useRoute} from '@react-navigation/native'
11
+ import res from "@ledvance/base/src/res/index";
12
+ import {Image, ScrollView, StyleSheet, Text, TouchableOpacity, View} from "react-native";
13
+ import LdvTopName from "@ledvance/base/src/components/ldvTopName";
14
+ import TextField from "@ledvance/base/src/components/TextField";
15
+ import LdvWeekView from "@ledvance/base/src/components/weekSelect";
16
+ import Spacer from "@ledvance/base/src/components/Spacer";
17
+ import Summary from "./Summary";
18
+ import {parseHour12} from "@tuya/tuya-panel-lamp-sdk/lib/utils";
19
+ import DeleteButton from "@ledvance/base/src/components/DeleteButton";
20
+ import LdvPickerView from "@ledvance/base/src/components/ldvPickerView";
21
+
22
+ const {parseTimer} = Utils.TimeUtils
23
+ const {convertX: cx} = Utils.RatioUtils;
24
+
25
+ interface FixedTimeDetailParam extends FixedTimeParam {
26
+ mode: 'add' | 'edit',
27
+ item: FixedTimeItem,
28
+ onPost: (mode: 'add' | 'edit' | 'del', item: FixedTimeItem, goBack?: boolean) => Promise<void>
29
+ }
30
+
31
+ const newFixedTimeItem = (): FixedTimeItem => {
32
+ const startTime = dayjs().hour() * 60 + dayjs().minute()
33
+ return {
34
+ enable: true,
35
+ channel: 0,
36
+ weeks: [0, 0, 0, 0, 0, 0, 0, 0],
37
+ startTime: startTime,
38
+ endTime: startTime + 60,
39
+ onTime: 1,
40
+ offTime: 1
41
+ }
42
+ }
43
+
44
+ const FixedTimeForPlugDetailPage = () => {
45
+ const navigation = useNavigation()
46
+ const params = useRoute().params as FixedTimeDetailParam
47
+ const state = useReactive({
48
+ item: params.mode === 'add' ? newFixedTimeItem() : cloneDeep(params.item),
49
+ onHour: '00',
50
+ onMinute: '01',
51
+ offHour: '00',
52
+ offMinute: '01',
53
+ loading: false
54
+ })
55
+
56
+ useEffect(() => {
57
+ const { onTime, offTime } = state.item
58
+ state.onHour = formatTime(Math.floor(onTime / 60))
59
+ state.onMinute = formatTime(onTime % 60)
60
+ state.offHour = formatTime(Math.floor(offTime / 60))
61
+ state.offMinute = formatTime(offTime % 60)
62
+ }, []);
63
+
64
+ useUpdateEffect(() => {
65
+ if (state.onHour === '00' && state.onMinute === '00') {
66
+ state.onMinute = '01'
67
+ }
68
+ if (state.offHour === '00' && state.offMinute === '00') {
69
+ state.offMinute = '01'
70
+ }
71
+ }, [state.onHour, state.onMinute, state.offHour, state.offMinute])
72
+
73
+ const formatTime = (v: number) => {
74
+ return v.toString().padStart(2, '0')
75
+ }
76
+
77
+ const parseTime = (h: string, m: string) => {
78
+ return Number(h) * 60 + Number(m)
79
+ }
80
+
81
+ const isTimeEffective = useMemo(() => {
82
+ const { startTime, endTime } = state.item
83
+ const timeInterval = endTime - startTime + ((startTime > endTime) ? 1440 : 0) // 跨天要加1440
84
+ const workTime = parseTime(state.onHour, state.onMinute) + parseTime(state.offHour, state.offMinute)
85
+ return timeInterval >= workTime
86
+ }, [state.item.startTime, state.item.endTime, state.onHour, state.onMinute, state.offHour, state.offMinute])
87
+
88
+ const canSave = useMemo(() => {
89
+ return state.item.name && state.item.name.length < 33 && state.item.channel !== undefined && isTimeEffective
90
+ }, [state.item.name, state.item.channel, isTimeEffective])
91
+
92
+ const showConfirm = useMemo(() => {
93
+ return params.mode === 'edit' && !isEqual(state.item, params.item)
94
+ }, [JSON.stringify(state.item), JSON.stringify(params.item)])
95
+
96
+ const onSave = async () => {
97
+ if (!canSave) {
98
+ return
99
+ }
100
+ const item = cloneDeep(state.item)
101
+ item.onTime = parseTime(state.onHour, state.onMinute)
102
+ item.offTime = parseTime(state.offHour, state.offMinute)
103
+ await params.onPost(params.mode, item, true);
104
+ }
105
+
106
+ const onDelete = () => {
107
+ return showDialog({
108
+ method: 'confirm',
109
+ title: I18n.getLang('cancel_dialog_delete_item_fixedtimecycle_titel'),
110
+ subTitle: I18n.getLang('cancel_dialog_delete_item_fixedtimecycle_description'),
111
+ onConfirm: async (_, {close}) => {
112
+ close()
113
+ await params.onPost('del', state.item, true)
114
+ }
115
+ })
116
+ }
117
+ return (
118
+ <Page
119
+ backText={I18n.getLang('fixedTimeCycle_socket_headline')}
120
+ onBackClick={!showConfirm ? navigation.goBack : undefined}
121
+ rightButtonIcon={canSave ? res.ic_check : res.ic_uncheck}
122
+ rightButtonDisabled={state.loading}
123
+ loading={state.loading}
124
+ showBackDialog={showConfirm}
125
+ backDialogTitle={I18n.getLang('cancel_dialog_leave_unsaved_titel')}
126
+ backDialogContent={I18n.getLang('cancel_dialog_delete_item_fixedtimecycle_description')}
127
+ rightButtonIconClick={onSave}
128
+ >
129
+ <ScrollView nestedScrollEnabled={true}>
130
+ <LdvTopName
131
+ title={I18n.getLang(params.mode === 'add' ? 'add_fixedtimecycle_headline_text' : 'edit_fixedtimecycle_headline_text')}/>
132
+ <TextField
133
+ style={styles.cardContainer}
134
+ value={state.item.name}
135
+ showError={(state.item.name?.length || 0) > 32}
136
+ maxLength={33}
137
+ errorText={I18n.getLang('add_new_dynamic_mood_alert_text')}
138
+ placeholder={I18n.getLang('add_new_trigger_time_inputfield_value_text')}
139
+ onChangeText={(t: string) => {
140
+ state.item.name = t;
141
+ }}
142
+ />
143
+ {/* pick */}
144
+ <TimerPicker
145
+ itemTextColor='#aeadb5'
146
+ style={{paddingVertical: cx(0), marginVertical: cx(0)}}
147
+ is12Hours={!params.is24Hour}
148
+ startTime={state.item.startTime}
149
+ endTime={state.item.endTime}
150
+ onTimerChange={(startTime, endTime) => {
151
+ state.item.startTime = startTime
152
+ state.item.endTime = endTime
153
+ }}/>
154
+ <LdvWeekView
155
+ value={state.item.weeks}
156
+ style={styles.cardContainer}
157
+ onSelect={(index: number) => {
158
+ const rawIndex = index - 1
159
+ state.item.weeks[rawIndex] = state.item.weeks[rawIndex] === 1 ? 0 : 1
160
+ }}/>
161
+ <Spacer/>
162
+ <Text style={styles.cardContainer}>{loopText(state.item.weeks, parseTimer(state.item.startTime * 60))}</Text>
163
+ <Spacer/>
164
+ {/*Apply for */}
165
+ <View style={styles.cardContainer}>
166
+ <Text style={styles.itemTitle}>{I18n.getLang('timeschedule_add_schedule_subheadline_text')}</Text>
167
+ <Spacer height={cx(10)}/>
168
+ <View style={styles.applyContent}>
169
+ {state.item.channel === undefined ?
170
+ <Text>{I18n.getLang('timer_ceiling_fan_selectionfield_no_components_text')}</Text> :
171
+ <View style={[styles.applyItem, {marginBottom: cx(10), borderRadius: 4}]}>
172
+ <Text style={{color: '#000'}}>{params.channels[state.item.channel]}</Text>
173
+ {params.channels.length > 1 && <TouchableOpacity
174
+ onPress={() => {
175
+ state.item.channel = undefined
176
+ }}
177
+ style={{paddingHorizontal: cx(5)}}>
178
+ <Image style={{width: cx(16), height: cx(16)}} source={res.ic_arrows_nav_clear}/>
179
+ </TouchableOpacity>}
180
+ </View>
181
+ }
182
+ </View>
183
+ {params.channels.map((item: string, index: number) => {
184
+ if (state.item.channel === index) {
185
+ return null
186
+ }
187
+ return (
188
+ <TouchableOpacity
189
+ style={styles.applyItem}
190
+ key={item}
191
+ onPress={() => {
192
+ state.item.channel = index
193
+ }}
194
+ >
195
+ <Text style={{color: '#000'}}>{item}</Text>
196
+ <Image style={{width: cx(16), height: cx(16)}} source={res.device_panel_timer_add}/>
197
+ </TouchableOpacity>
198
+ )
199
+ })}
200
+ </View>
201
+ <Spacer/>
202
+ <View style={styles.cardContainer}>
203
+ <Text style={styles.itemTitle}>{I18n.getLang('timeschedule_add_schedule_subheadline4_text')}</Text>
204
+ {!isTimeEffective && <View style={{ flexDirection: 'row', alignItems: 'center' }}>
205
+ <Image style={{ width: cx(16), height: cx(16), tintColor: 'rgb(255,149,0)' }} source={res.ic_warning_amber} />
206
+ <Text style={{ fontSize: cx(12), color: 'rgb(255,149,0)' }}>{I18n.getLang('addTimeCycle_warning_text')}</Text>
207
+ </View>}
208
+ <View >
209
+ <Text style={{ color: '#000', marginVertical: cx(10) }}>{I18n.getLang('addTimeCycle_settings_sec_text')}</Text>
210
+ <LdvPickerView
211
+ hour={state.onHour}
212
+ minute={state.onMinute}
213
+ unit={['h', 'min']}
214
+ setHour={(h: string) => state.onHour = h}
215
+ setMinute={(m: string) => state.onMinute = m}
216
+ />
217
+ </View>
218
+ <View>
219
+ <Text style={{ color: '#000', marginVertical: cx(10) }}>{I18n.getLang('addTimeCycle_settings_sec_text2')}</Text>
220
+ <LdvPickerView
221
+ hour={state.offHour}
222
+ minute={state.offMinute}
223
+ unit={['h', 'min']}
224
+ setHour={(h: string) => state.offHour = h}
225
+ setMinute={(m: string) => state.offMinute = m}
226
+ />
227
+ </View>
228
+ </View>
229
+ <Spacer/>
230
+ {/* summary */}
231
+ <Summary
232
+ frequency={loopText(state.item.weeks)}
233
+ time={params.is24Hour ?
234
+ `${parseTimer(state.item.startTime * 60)} - ${parseTimer(state.item.endTime * 60)}`
235
+ :
236
+ `${parseHour12(state.item.startTime * 60)} - ${parseHour12(state.item.endTime * 60)}`
237
+ }
238
+ actions={<View style={{ flex: 1 }}>
239
+ <>
240
+ <View>
241
+ <Text style={{ fontSize: cx(12), color: '#000000' }}>
242
+ {I18n.formatValue('feature_summary_action_txt_4', `${Number(state.onHour)}`, `${Number(state.onMinute)}`)}
243
+ </Text>
244
+ </View>
245
+ <View style={{ flexDirection: 'row' }}>
246
+ <View style={styles.filletCorner}>
247
+ <Text style={styles.rightTitle}>{params.channels?.length > 0 && params.channels[0]}</Text>
248
+ </View>
249
+ </View>
250
+ </>
251
+ <>
252
+ <View>
253
+ <Text style={{ fontSize: cx(12), color: '#000000' }}>
254
+ {I18n.formatValue('feature_summary_action_txt_6', `${Number(state.offHour)}`, `${Number(state.offMinute)}`)}
255
+ </Text>
256
+ </View>
257
+ <View style={{ flexDirection: 'row' }}>
258
+ <View style={styles.filletCorner}>
259
+ <Text style={styles.rightTitle}>{params.channels?.length > 0 && params.channels[0]}</Text>
260
+ </View>
261
+ </View>
262
+ </>
263
+ </View>}
264
+ />
265
+ <Spacer/>
266
+ {params.mode === 'edit' &&
267
+ <View style={{marginHorizontal: cx(24)}}>
268
+ <DeleteButton
269
+ text={I18n.getLang('edit_fixedtimecycle_bttn_text')}
270
+ onPress={onDelete}/>
271
+ </View>
272
+ }
273
+ <Spacer/>
274
+ </ScrollView>
275
+ </Page>
276
+ )
277
+ }
278
+
279
+ const styles = StyleSheet.create({
280
+ cardContainer: {
281
+ marginHorizontal: cx(24)
282
+ },
283
+ itemTitle: {
284
+ color: '#000',
285
+ fontSize: cx(16),
286
+ fontWeight: 'bold',
287
+ fontFamily: 'helvetica_neue_lt_std_bd',
288
+ },
289
+ applyContent: {
290
+ backgroundColor: '#f6f6f6',
291
+ borderRadius: 4,
292
+ minHeight: cx(55),
293
+ flex: 1,
294
+ justifyContent: 'center',
295
+ paddingHorizontal: cx(10),
296
+ paddingTop: cx(10)
297
+ },
298
+ applyItem: {
299
+ paddingLeft: cx(5),
300
+ flexDirection: 'row',
301
+ justifyContent: 'space-between',
302
+ alignItems: 'center',
303
+ backgroundColor: '#fff',
304
+ height: cx(35),
305
+ },
306
+ summaryContainer: {
307
+ flexDirection: 'row',
308
+ justifyContent: 'flex-start',
309
+ marginBottom: cx(10)
310
+ },
311
+ summaryLeft: {
312
+ flexDirection: 'row',
313
+ justifyContent: 'flex-start',
314
+ alignItems: 'center',
315
+ },
316
+ summaryImg: {
317
+ width: cx(12),
318
+ height: cx(12),
319
+ marginRight: cx(6)
320
+ },
321
+ leftTitle: {
322
+ fontSize: cx(14),
323
+ color: '#000'
324
+ },
325
+ summaryRight: {
326
+ flexDirection: 'column',
327
+ marginLeft: cx(21),
328
+ borderRadius: cx(16),
329
+ backgroundColor: '#cbcbcb',
330
+ },
331
+ rightItem: {
332
+ paddingHorizontal: cx(12),
333
+ color: '#000',
334
+ },
335
+ rightTitle: {
336
+ paddingLeft: cx(12),
337
+ paddingRight: cx(12),
338
+ fontSize: cx(10),
339
+ textAlign: 'center',
340
+ alignSelf: 'flex-start'
341
+ },
342
+ filletCorner: {
343
+ flexDirection: 'row',
344
+ backgroundColor: '#cbcbcb',
345
+ borderRadius: cx(16),
346
+ alignSelf: 'flex-start'
347
+ }
348
+ })
349
+
350
+ export default FixedTimeForPlugDetailPage
@@ -0,0 +1,194 @@
1
+ import React, {useCallback, useMemo} from "react";
2
+ import {FlatList, Image, ScrollView, StyleSheet, Text, TouchableOpacity, View} from 'react-native'
3
+ import {useNavigation, useRoute} from '@react-navigation/native'
4
+ import Page from "@ledvance/base/src/components/Page";
5
+ import I18n from "@ledvance/base/src/i18n";
6
+ import res from "@ledvance/base/src/res";
7
+ import {useDeviceInfo} from "@ledvance/base/src/models/modules/NativePropsSlice";
8
+ import {FixedTimeItem, FixedTimeParam, useFixedTime} from "./FixedTimeForPlugActon";
9
+ import {useReactive} from "ahooks";
10
+ import {ui_biz_routerKey} from "../../navigation/Routers";
11
+ import {cloneDeep} from "lodash";
12
+ import Spacer from "@ledvance/base/src/components/Spacer";
13
+ import {Utils} from "tuya-panel-kit";
14
+ import ItemCard from "./ItemCard";
15
+
16
+ const {convertX: cx, topBarHeight} = Utils.RatioUtils;
17
+ const MAX_NUM = 10
18
+
19
+ const FixedTimeForPlugPage = () => {
20
+ const devInfo = useDeviceInfo()
21
+ const navigation = useNavigation()
22
+ const params = useRoute().params as FixedTimeParam
23
+ const [fixedTimeList, setFixedTimeList] = useFixedTime()
24
+ const state = useReactive({
25
+ loading: false
26
+ })
27
+
28
+ const isMaxNum = useMemo(() => {
29
+ return fixedTimeList.length >= MAX_NUM
30
+ }, [fixedTimeList])
31
+
32
+ const onAddOrEditItem = (mode: 'add' | 'edit', item?: FixedTimeItem) => {
33
+ const path = ui_biz_routerKey.group_ui_biz_fixed_time_plug_detail
34
+ navigation.navigate(path, {
35
+ ...params,
36
+ mode,
37
+ item,
38
+ onPost
39
+ })
40
+ }
41
+
42
+ const onPost = useCallback(async (mode: 'add' | 'edit' | 'del', fixedTime: FixedTimeItem, goBack?: boolean) => {
43
+ state.loading = true
44
+ const cloneFixedTimeList = cloneDeep(fixedTimeList)
45
+ const idx = fixedTimeList.findIndex(it => it.index === fixedTime.index)
46
+ if (mode === 'edit') {
47
+ cloneFixedTimeList.splice(idx, 1, fixedTime)
48
+ }
49
+ if (mode === 'del') cloneFixedTimeList.splice(idx, 1)
50
+ const newFixedTimeList = mode === 'add' ? [...fixedTimeList, {
51
+ ...fixedTime,
52
+ index: fixedTimeList.length
53
+ }] : cloneFixedTimeList
54
+ const res = await setFixedTimeList(newFixedTimeList)
55
+ state.loading = false
56
+ if (res.success && goBack) {
57
+ navigation.goBack()
58
+ }
59
+ }, [fixedTimeList])
60
+
61
+ const renderList = () => {
62
+ return (
63
+ <ScrollView nestedScrollEnabled={true}>
64
+ <Text style={{
65
+ color: '#000',
66
+ marginLeft: cx(24),
67
+ }}>{I18n.getLang('timeschedule_overview_description_text')}</Text>
68
+ <Spacer height={cx(10)}/>
69
+ {isMaxNum && <View style={{marginHorizontal: cx(24), flexDirection: 'row'}}>
70
+ <Image style={{width: cx(16), height: cx(16), tintColor: '#ff9500'}} source={res.ic_warning_amber}/>
71
+ <Text>{I18n.getLang('randomtimecycle_warning_max_number_text')}</Text>
72
+ </View>}
73
+ <FlatList
74
+ data={fixedTimeList}
75
+ renderItem={({item}) => (
76
+ <ItemCard
77
+ item={item}
78
+ is24Hour={params.is24Hour}
79
+ onSwitch={async (v) => {
80
+ await onPost('edit', {
81
+ ...item,
82
+ enable: v,
83
+ })
84
+ }}
85
+ onPress={() => {
86
+ onAddOrEditItem('edit', item)
87
+ }}
88
+ />
89
+ )}
90
+ keyExtractor={(item: any) => `${item?.index}`}
91
+ ItemSeparatorComponent={() => <Spacer/>}
92
+ ListHeaderComponent={<Spacer height={cx(10)}/>}
93
+ ListFooterComponent={<Spacer/>}
94
+ />
95
+ </ScrollView>
96
+ )
97
+ }
98
+ const renderEmpty = () => {
99
+ return (
100
+ <View style={styles.emptyListCon}>
101
+ <Image
102
+ style={styles.emptyImage}
103
+ source={{uri: res.ldv_timer_empty}}
104
+ resizeMode="contain"/>
105
+ <View
106
+ style={{
107
+ marginHorizontal: cx(24),
108
+ marginTop: cx(30),
109
+ flexDirection: 'row',
110
+ alignItems: 'center',
111
+ }}
112
+ >
113
+ <Image
114
+ source={{uri: res.device_panel_schedule_alert}}
115
+ style={{width: cx(16), height: cx(16)}}
116
+ />
117
+ <Text style={styles.emptyNoTime}>{I18n.getLang('fixedTimeCycle_information_text')}</Text>
118
+ </View>
119
+ <View
120
+ style={{
121
+ height: cx(36),
122
+ width: cx(150),
123
+ marginVertical: cx(25),
124
+ marginHorizontal: cx(24),
125
+ borderRadius: cx(6),
126
+ backgroundColor: '#f60',
127
+ }}
128
+ >
129
+ <TouchableOpacity
130
+ style={{
131
+ flex: 1,
132
+ justifyContent: 'center',
133
+ alignItems: 'center',
134
+ }}
135
+ onPress={() => onAddOrEditItem('add')}
136
+ >
137
+ <Text style={{fontSize: cx(12), fontWeight: 'bold', color: '#fff', textAlign: 'center'}}>
138
+ {I18n.getLang('fixedTimeCycle_bttn_text')}
139
+ </Text>
140
+ </TouchableOpacity>
141
+ </View>
142
+ </View>
143
+ )
144
+ }
145
+
146
+ return (
147
+ <Page
148
+ backText={devInfo.name}
149
+ onBackClick={() => navigation.goBack()}
150
+ headlineText={I18n.getLang('fixedTimeCycle_socket_headline')}
151
+ headlineIcon={!isMaxNum && res.device_panel_schedule_add || undefined}
152
+ onHeadlineIconClick={() => onAddOrEditItem('add')}
153
+ loading={state.loading}
154
+ >
155
+ {fixedTimeList.length > 0 ? renderList() : renderEmpty()}
156
+ </Page>
157
+ )
158
+ }
159
+
160
+ const styles = StyleSheet.create({
161
+ bg: {
162
+ marginTop: topBarHeight,
163
+ flex: 1,
164
+ },
165
+ emptyListCon: {
166
+ justifyContent: 'center',
167
+ alignItems: 'center',
168
+ marginTop: cx(30),
169
+ },
170
+ emptyImage: {
171
+ width: cx(225),
172
+ height: cx(198)
173
+ },
174
+ emptyNoTime: {
175
+ fontSize: cx(12),
176
+ color: '#000'
177
+ },
178
+ emptyTimeTip: {
179
+ fontSize: cx(12),
180
+ lineHeight: cx(17),
181
+ marginTop: cx(6),
182
+ width: '76%',
183
+ textAlign: 'center'
184
+ },
185
+ listContainer: {
186
+ bottom: cx(74)
187
+ },
188
+ categoryList: {
189
+ marginHorizontal: cx(24),
190
+ marginBottom: cx(12),
191
+ },
192
+ })
193
+
194
+ export default FixedTimeForPlugPage