iphone-xudale 0.2.9
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/.eslintrc.json +3 -0
- package/app/(pages)/options/page.tsx +720 -0
- package/app/(pages)/popup/page.tsx +166 -0
- package/app/(pages)/tips/page.tsx +40 -0
- package/app/components/DropListBox.tsx +157 -0
- package/app/components/SVGPlay.tsx +14 -0
- package/app/favicon.ico +0 -0
- package/app/globals.css +27 -0
- package/app/layout.tsx +15 -0
- package/app/page.tsx +3 -0
- package/app/scripts/content/checkoutSteps.ts +341 -0
- package/app/scripts/content/doFroApplePages.ts +206 -0
- package/app/scripts/content/getPageInitInfo.ts +49 -0
- package/app/scripts/content/getStoreCanPickInfo.ts +251 -0
- package/app/scripts/content/goOrderSteps.ts +179 -0
- package/app/scripts/content/index.ts +56 -0
- package/app/scripts/content/playSystemNotifacation.ts +39 -0
- package/app/scripts/content/sendSelfNotificatioin.ts +27 -0
- package/app/scripts/inject/index.ts +18 -0
- package/app/shared/constants.ts +236 -0
- package/app/shared/interface.ts +25 -0
- package/app/shared/location/city.json +1774 -0
- package/app/shared/location/county.json +17115 -0
- package/app/shared/location/province.json +94 -0
- package/app/shared/util.ts +93 -0
- package/buildAfter.js +86 -0
- package/bunBuild.ts +54 -0
- package/extension/content-script.js +7 -0
- package/extension/favicon.ico +0 -0
- package/extension/icons/icon128.png +0 -0
- package/extension/icons/icon16.png +0 -0
- package/extension/icons/icon19-disable.png +0 -0
- package/extension/icons/icon19.png +0 -0
- package/extension/icons/icon32.png +0 -0
- package/extension/icons/icon38.png +0 -0
- package/extension/icons/icon48.png +0 -0
- package/extension/inject-script.js +1 -0
- package/extension/manifest.json +38 -0
- package/extension/service-worker.js +62 -0
- package/extension.next.config.js +25 -0
- package/icon_generator.py +30 -0
- package/middleware.ts +21 -0
- package/next.config.js +15 -0
- package/package.json +67 -0
- package/postcss.config.js +6 -0
- package/public/assets/images/SCR-20230916-nbkz.png +0 -0
- package/public/assets/images/SCR-20230916-nbyv.png +0 -0
- package/public/assets/images/SCR-20230916-ncte.png +0 -0
- package/public/assets/images/SCR-20230916-ndgw.png +0 -0
- package/public/assets/images/SCR-20230916-ndks.png +0 -0
- package/public/assets/images/SCR-20230916-neaa.png +0 -0
- package/public/assets/images/SCR-20230916-neeq.jpeg +0 -0
- package/public/assets/images/SCR-20230916-nfkt.png +0 -0
- package/public/assets/images/SCR-20230919-ulfn.png +0 -0
- package/public/assets/images/SCR-20230919-ulzd.png +0 -0
- package/public/assets/images/SCR-20230919-uocr.png +0 -0
- package/public/icon_original.png +0 -0
- package/public/next.svg +1 -0
- package/public/vercel.svg +1 -0
- package/tailwind.config.ts +20 -0
- package/tsconfig.json +27 -0
- package/types/global.d.ts +19 -0
@@ -0,0 +1,720 @@
|
|
1
|
+
'use client'
|
2
|
+
import { restoreFromStorage, saveToStorage, sleep } from '@/app/shared/util'
|
3
|
+
import {
|
4
|
+
storeKeys,
|
5
|
+
billItemList,
|
6
|
+
defaultiPhoneOrderConfig,
|
7
|
+
billTypeKeys,
|
8
|
+
defaultPayinstallmentTotal,
|
9
|
+
} from '@/app/shared/constants'
|
10
|
+
import DropListBox from '@/app/components/DropListBox'
|
11
|
+
import { IPHONEORDER_CONFIG } from '@/app/shared/interface'
|
12
|
+
import { useCallback, useEffect, useState, useRef, useMemo } from 'react'
|
13
|
+
import { filter as _filter, map as _map, find as _find, findIndex as _findIndex, sortBy as _sortBy } from 'lodash'
|
14
|
+
import city from '@/app/shared/location/city.json'
|
15
|
+
import province from '@/app/shared/location/province.json'
|
16
|
+
import county from '@/app/shared/location/county.json'
|
17
|
+
|
18
|
+
const defaultItem = { id: '', name: '' }
|
19
|
+
|
20
|
+
type VoiceType = { lang?: string; voiceName?: string; id: string; name: string }
|
21
|
+
|
22
|
+
export default function Options() {
|
23
|
+
const [config, setConfig] = useState<IPHONEORDER_CONFIG>(defaultiPhoneOrderConfig)
|
24
|
+
const [payinstallmentList, setpayinstallmentList] = useState([defaultItem])
|
25
|
+
const [provinceList, setProvinceList] = useState(province)
|
26
|
+
const [selectedProvinceIndex, setSelectedProvinceIndex] = useState(0)
|
27
|
+
const [cityList, setCityList] = useState([defaultItem])
|
28
|
+
const [selectedCityIndex, setSelectedCityIndex] = useState(0)
|
29
|
+
const [districtList, setDistrictList] = useState([defaultItem])
|
30
|
+
const [selectedDistrictIndex, setSelectedDistrictIndex] = useState(0)
|
31
|
+
const [voiceList, setVoiceList] = useState<VoiceType[]>([defaultItem])
|
32
|
+
const firstNameRef = useRef<HTMLInputElement>(null)
|
33
|
+
const lastNameRef = useRef<HTMLInputElement>(null)
|
34
|
+
const last4codeRef = useRef<HTMLInputElement>(null)
|
35
|
+
const mobileRef = useRef<HTMLInputElement>(null)
|
36
|
+
const appleidRef = useRef<HTMLInputElement>(null)
|
37
|
+
const passwordRef = useRef<HTMLInputElement>(null)
|
38
|
+
const stepWaitRef = useRef<HTMLInputElement>(null)
|
39
|
+
const beforeReloadCountRef = useRef<HTMLInputElement>(null)
|
40
|
+
const voiceTimesRef = useRef<HTMLInputElement>(null)
|
41
|
+
const voiceTextRef = useRef<HTMLInputElement>(null)
|
42
|
+
const notiAPIRef = useRef<HTMLInputElement>(null)
|
43
|
+
|
44
|
+
useEffect(() => {
|
45
|
+
restoreFromStorage(storeKeys.orderConfig).then(data => {
|
46
|
+
if (data) {
|
47
|
+
console.log(`restoreFromStorage`, data)
|
48
|
+
setConfig(config => {
|
49
|
+
return {
|
50
|
+
...config,
|
51
|
+
...(data as IPHONEORDER_CONFIG),
|
52
|
+
}
|
53
|
+
})
|
54
|
+
}
|
55
|
+
})
|
56
|
+
|
57
|
+
const syncFun = async () => {
|
58
|
+
const voiceList = await getVoices()
|
59
|
+
setVoiceList(voiceList)
|
60
|
+
}
|
61
|
+
syncFun()
|
62
|
+
}, [])
|
63
|
+
|
64
|
+
const voiceSelected = useMemo(() => {
|
65
|
+
let _index = -1
|
66
|
+
const { lang, voiceName } = config?.voiceInfo || {}
|
67
|
+
if (lang && voiceName && voiceList.length) {
|
68
|
+
_index = _findIndex(voiceList, v => {
|
69
|
+
return v.lang == lang && v.voiceName == voiceName
|
70
|
+
})
|
71
|
+
}
|
72
|
+
if (_index < 0) _index = 0
|
73
|
+
return _index
|
74
|
+
}, [voiceList, config.voiceInfo])
|
75
|
+
// ************ 更新选中支付方式 ************
|
76
|
+
const billSelected = useMemo(() => {
|
77
|
+
return (
|
78
|
+
(config?.payBill &&
|
79
|
+
_findIndex(billItemList, _b => {
|
80
|
+
return _b.id == config.payBill
|
81
|
+
})) ||
|
82
|
+
0
|
83
|
+
)
|
84
|
+
}, [config.payBill])
|
85
|
+
|
86
|
+
// ************ 更新选中分期笔数 ************
|
87
|
+
const payinstallmentSelected = useMemo(() => {
|
88
|
+
return (
|
89
|
+
(config?.payInstallment &&
|
90
|
+
_findIndex(payinstallmentList, _b => {
|
91
|
+
return _b.id == String(config.payInstallment)
|
92
|
+
})) ||
|
93
|
+
0
|
94
|
+
)
|
95
|
+
}, [config.payInstallment, payinstallmentList])
|
96
|
+
|
97
|
+
// ************ 👇下拉菜单联动👇 ************
|
98
|
+
useEffect(() => {
|
99
|
+
const newPayinstallmentList = _map(
|
100
|
+
_filter(defaultPayinstallmentTotal, item => {
|
101
|
+
if (item.id == 0) return true
|
102
|
+
if (item?.includes && Number(item?.includes?.length) > 0) {
|
103
|
+
return item.includes.includes(config.payBill)
|
104
|
+
}
|
105
|
+
return false
|
106
|
+
}),
|
107
|
+
(_item: any) => {
|
108
|
+
return {
|
109
|
+
id: _item.id,
|
110
|
+
name: _item.name,
|
111
|
+
}
|
112
|
+
}
|
113
|
+
)
|
114
|
+
|
115
|
+
setpayinstallmentList(newPayinstallmentList)
|
116
|
+
// 当原来的分期笔数不存在时
|
117
|
+
if (
|
118
|
+
!_find(newPayinstallmentList, _t => {
|
119
|
+
return _t.id == config.payInstallment
|
120
|
+
})
|
121
|
+
) {
|
122
|
+
setConfig({
|
123
|
+
...config,
|
124
|
+
payInstallment: Number(newPayinstallmentList[0].id) || 0,
|
125
|
+
})
|
126
|
+
}
|
127
|
+
}, [config.payBill])
|
128
|
+
|
129
|
+
useEffect(() => {
|
130
|
+
if (config.provinceName) {
|
131
|
+
let provinceIndex: number = _findIndex(province, item => {
|
132
|
+
return item.name == config.provinceName
|
133
|
+
})
|
134
|
+
provinceIndex = provinceIndex > -1 ? provinceIndex : 0
|
135
|
+
|
136
|
+
setSelectedProvinceIndex(provinceIndex)
|
137
|
+
const provinceId = province[provinceIndex].id
|
138
|
+
// @ts-ignore
|
139
|
+
const newCityList = city[provinceId]
|
140
|
+
setCityList(newCityList)
|
141
|
+
if (
|
142
|
+
!_find(newCityList, _t => {
|
143
|
+
return _t.name == config.cityName
|
144
|
+
})
|
145
|
+
) {
|
146
|
+
setConfig({
|
147
|
+
...config,
|
148
|
+
cityName: newCityList[0].name,
|
149
|
+
})
|
150
|
+
}
|
151
|
+
// if (provinceList.length < 1) {
|
152
|
+
// setProvinceList(province)
|
153
|
+
// }
|
154
|
+
}
|
155
|
+
}, [config.provinceName])
|
156
|
+
|
157
|
+
useEffect(() => {
|
158
|
+
if (config.cityName) {
|
159
|
+
let cityIndex: number = _findIndex(cityList, item => {
|
160
|
+
return item.name == config.cityName
|
161
|
+
})
|
162
|
+
cityIndex = cityIndex > -1 ? cityIndex : 0
|
163
|
+
setSelectedCityIndex(cityIndex)
|
164
|
+
const cityId: string = cityList[cityIndex].id
|
165
|
+
// @ts-ignore
|
166
|
+
const newDistrictList = county[cityId]
|
167
|
+
// @ts-ignore
|
168
|
+
console.log(`cityIndex`, cityIndex, cityList[cityIndex], county[cityId])
|
169
|
+
if (newDistrictList) {
|
170
|
+
setDistrictList(newDistrictList)
|
171
|
+
if (
|
172
|
+
!_find(newDistrictList, _t => {
|
173
|
+
return _t.name == config.districtName
|
174
|
+
})
|
175
|
+
) {
|
176
|
+
setConfig({
|
177
|
+
...config,
|
178
|
+
districtName: newDistrictList[0].name,
|
179
|
+
})
|
180
|
+
}
|
181
|
+
}
|
182
|
+
}
|
183
|
+
}, [config.cityName, cityList])
|
184
|
+
|
185
|
+
useEffect(() => {
|
186
|
+
// setSelectedDistrictIndex
|
187
|
+
let districtIndex: number = _findIndex(districtList, item => {
|
188
|
+
return item.name == config.districtName
|
189
|
+
})
|
190
|
+
districtIndex = districtIndex > -1 ? districtIndex : 0
|
191
|
+
setSelectedDistrictIndex(districtIndex)
|
192
|
+
}, [config.districtName, districtList])
|
193
|
+
// ************ 👆下拉菜单联动👆 ************
|
194
|
+
|
195
|
+
// ************ 支付方式 ************
|
196
|
+
const handleSelectPayType = (payItem: Record<string, any>) => {
|
197
|
+
setConfig(prev => {
|
198
|
+
return {
|
199
|
+
...prev,
|
200
|
+
payBill: payItem.id,
|
201
|
+
}
|
202
|
+
})
|
203
|
+
}
|
204
|
+
|
205
|
+
// ************ 分期笔数 ************
|
206
|
+
const handleSelectPayinstallment = (payinstallmentItem: Record<string, any>) => {
|
207
|
+
setConfig(prev => {
|
208
|
+
return {
|
209
|
+
...prev,
|
210
|
+
payInstallment: payinstallmentItem.id,
|
211
|
+
}
|
212
|
+
})
|
213
|
+
}
|
214
|
+
|
215
|
+
// ************ 选中省份 ************
|
216
|
+
const handleSelectProvince = (provinceItem: Record<string, any>) => {
|
217
|
+
setConfig(prev => {
|
218
|
+
return {
|
219
|
+
...prev,
|
220
|
+
provinceName: provinceItem.name,
|
221
|
+
}
|
222
|
+
})
|
223
|
+
}
|
224
|
+
// ************ 选中城市 ************
|
225
|
+
const handleSelectCity = (cityItem: Record<string, any>) => {
|
226
|
+
setConfig(prev => {
|
227
|
+
return {
|
228
|
+
...prev,
|
229
|
+
cityName: cityItem.name,
|
230
|
+
}
|
231
|
+
})
|
232
|
+
}
|
233
|
+
|
234
|
+
// ************ 选中区域 ************
|
235
|
+
const handleSelectDistrict = (districtItem: Record<string, any>) => {
|
236
|
+
setConfig(prev => {
|
237
|
+
return {
|
238
|
+
...prev,
|
239
|
+
districtName: districtItem.name,
|
240
|
+
}
|
241
|
+
})
|
242
|
+
}
|
243
|
+
|
244
|
+
const handleSelectVoice = (voiceItem: Record<string, any>) => {
|
245
|
+
setConfig(prev => {
|
246
|
+
return {
|
247
|
+
...prev,
|
248
|
+
voiceInfo: {
|
249
|
+
...prev.voiceInfo,
|
250
|
+
lang: voiceItem.lang,
|
251
|
+
voiceName: voiceItem.voiceName,
|
252
|
+
},
|
253
|
+
}
|
254
|
+
})
|
255
|
+
}
|
256
|
+
const handleSave = useCallback(() => {
|
257
|
+
const saveConfig: IPHONEORDER_CONFIG = {
|
258
|
+
...config,
|
259
|
+
firstName: firstNameRef.current?.value,
|
260
|
+
lastName: lastNameRef.current?.value,
|
261
|
+
last4code: last4codeRef.current?.value,
|
262
|
+
mobile: mobileRef.current?.value,
|
263
|
+
appleId: appleidRef.current?.value,
|
264
|
+
password: passwordRef.current?.value,
|
265
|
+
stepWait: Number(stepWaitRef.current?.value) || config.stepWait || 10,
|
266
|
+
afterCountThenReload: Number(beforeReloadCountRef.current?.value) || config.afterCountThenReload || 50,
|
267
|
+
selfNotiAPI: notiAPIRef.current?.value || '',
|
268
|
+
voiceInfo: {
|
269
|
+
...config.voiceInfo,
|
270
|
+
text: voiceTextRef.current?.value || defaultiPhoneOrderConfig.voiceInfo.text,
|
271
|
+
voiceName: config.voiceInfo.voiceName || '',
|
272
|
+
lang: config.voiceInfo.lang || '',
|
273
|
+
times: Number(voiceTimesRef.current?.value) || defaultiPhoneOrderConfig.voiceInfo.times,
|
274
|
+
},
|
275
|
+
}
|
276
|
+
console.log(saveConfig)
|
277
|
+
saveAsync(saveConfig)
|
278
|
+
}, [config])
|
279
|
+
|
280
|
+
const handleCancel = useCallback(() => {
|
281
|
+
window.close()
|
282
|
+
}, [])
|
283
|
+
|
284
|
+
const handlePlaySound = () => {
|
285
|
+
const _text = voiceTextRef.current?.value || defaultiPhoneOrderConfig.voiceInfo.text,
|
286
|
+
_voiceName = config.voiceInfo.voiceName || '',
|
287
|
+
_lang = config.voiceInfo.lang || ''
|
288
|
+
let voiceOptions = {}
|
289
|
+
if (config.voiceInfo.voiceName && config.voiceInfo.lang) {
|
290
|
+
voiceOptions = {
|
291
|
+
lang: config.voiceInfo.lang,
|
292
|
+
voiceName: config.voiceInfo.voiceName,
|
293
|
+
}
|
294
|
+
}
|
295
|
+
if (typeof chrome !== 'undefined' && chrome?.tts) {
|
296
|
+
chrome.tts.speak(_text, voiceOptions)
|
297
|
+
}
|
298
|
+
}
|
299
|
+
|
300
|
+
const inputClass = `px-3 block w-full rounded-md border-0 py-1.5 text-gray-900 shadow-sm ring-1 ring-inset ring-gray-300 placeholder:text-gray-400 focus:ring-2 focus:ring-inset focus:ring-indigo-600 sm:text-sm sm:leading-6`
|
301
|
+
const labelClass = `block text-sm font-medium leading-6 text-gray-900`
|
302
|
+
return (
|
303
|
+
<main className="flex min-h-screen flex-col items-center justify-between p-24">
|
304
|
+
<div className="space-y-12">
|
305
|
+
<div className="border-b border-gray-900/10 pb-12">
|
306
|
+
<h2 className="text-base font-semibold leading-7 text-gray-900">配置信息</h2>
|
307
|
+
<p className="mt-1 text-sm leading-6 text-gray-600">以下信息用于抢购iPhone时自动填入</p>
|
308
|
+
<div className="mt-10 grid grid-cols-1 gap-x-6 gap-y-8 sm:grid-cols-6">
|
309
|
+
<div className="sm:col-span-3">
|
310
|
+
<label htmlFor="last-name" className={labelClass}>
|
311
|
+
姓氏
|
312
|
+
</label>
|
313
|
+
<div className="mt-2">
|
314
|
+
<input
|
315
|
+
ref={lastNameRef}
|
316
|
+
type="text"
|
317
|
+
name="last-name"
|
318
|
+
id="last-name"
|
319
|
+
autoComplete="family-name"
|
320
|
+
className={inputClass}
|
321
|
+
defaultValue={config.lastName}
|
322
|
+
/>
|
323
|
+
</div>
|
324
|
+
</div>
|
325
|
+
|
326
|
+
<div className="sm:col-span-3">
|
327
|
+
<label htmlFor="first-name" className={labelClass}>
|
328
|
+
名字
|
329
|
+
</label>
|
330
|
+
<div className="mt-2">
|
331
|
+
<input
|
332
|
+
ref={firstNameRef}
|
333
|
+
type="text"
|
334
|
+
name="first-name"
|
335
|
+
id="first-name"
|
336
|
+
autoComplete="given-name"
|
337
|
+
className={inputClass}
|
338
|
+
defaultValue={config.firstName}
|
339
|
+
/>
|
340
|
+
</div>
|
341
|
+
</div>
|
342
|
+
|
343
|
+
<div className="sm:col-span-3">
|
344
|
+
<label htmlFor="mobile-number" className={labelClass}>
|
345
|
+
手机号
|
346
|
+
</label>
|
347
|
+
<div className="mt-2">
|
348
|
+
<input
|
349
|
+
type="tel"
|
350
|
+
ref={mobileRef}
|
351
|
+
name="mobile-number"
|
352
|
+
id="mobile-number"
|
353
|
+
className={inputClass}
|
354
|
+
defaultValue={config.mobile}
|
355
|
+
/>
|
356
|
+
</div>
|
357
|
+
</div>
|
358
|
+
|
359
|
+
<div className="sm:col-span-3">
|
360
|
+
<label htmlFor="last-code" className={labelClass}>
|
361
|
+
身份证后四位
|
362
|
+
</label>
|
363
|
+
<div className="mt-2">
|
364
|
+
<input
|
365
|
+
type="text"
|
366
|
+
ref={last4codeRef}
|
367
|
+
name="last-code"
|
368
|
+
id="last-code"
|
369
|
+
className={inputClass}
|
370
|
+
defaultValue={config.last4code}
|
371
|
+
/>
|
372
|
+
</div>
|
373
|
+
</div>
|
374
|
+
|
375
|
+
<div className="sm:col-span-3">
|
376
|
+
<label htmlFor="email" className={labelClass}>
|
377
|
+
邮箱/Apple ID
|
378
|
+
</label>
|
379
|
+
<div className="mt-2">
|
380
|
+
<input
|
381
|
+
ref={appleidRef}
|
382
|
+
id="email"
|
383
|
+
name="email"
|
384
|
+
type="email"
|
385
|
+
autoComplete="email"
|
386
|
+
className={inputClass}
|
387
|
+
defaultValue={config.appleId}
|
388
|
+
/>
|
389
|
+
</div>
|
390
|
+
</div>
|
391
|
+
|
392
|
+
<div className="sm:col-span-3">
|
393
|
+
<label htmlFor="password" className={labelClass}>
|
394
|
+
登录密码(可选,不填则以访客模式下单)
|
395
|
+
</label>
|
396
|
+
<div className="mt-2">
|
397
|
+
<input
|
398
|
+
ref={passwordRef}
|
399
|
+
id="password"
|
400
|
+
name="password"
|
401
|
+
type="password"
|
402
|
+
autoComplete="password"
|
403
|
+
className={inputClass}
|
404
|
+
defaultValue={config.password}
|
405
|
+
/>
|
406
|
+
</div>
|
407
|
+
</div>
|
408
|
+
|
409
|
+
<div className="sm:col-span-3">
|
410
|
+
<label htmlFor="pay-type" className={labelClass}>
|
411
|
+
支付方式
|
412
|
+
</label>
|
413
|
+
<div className="mt-2">
|
414
|
+
<DropListBox
|
415
|
+
itemList={billItemList}
|
416
|
+
domID={'pay-type'}
|
417
|
+
selectedIndex={billSelected}
|
418
|
+
callback={handleSelectPayType}
|
419
|
+
/>
|
420
|
+
</div>
|
421
|
+
</div>
|
422
|
+
|
423
|
+
<div className="sm:col-span-3">
|
424
|
+
<label htmlFor="pay-installment" className={labelClass}>
|
425
|
+
分期笔数
|
426
|
+
</label>
|
427
|
+
<div className="mt-2">
|
428
|
+
<DropListBox
|
429
|
+
itemList={payinstallmentList}
|
430
|
+
domID={'pay-installment'}
|
431
|
+
selectedIndex={payinstallmentSelected}
|
432
|
+
callback={handleSelectPayinstallment}
|
433
|
+
/>
|
434
|
+
</div>
|
435
|
+
</div>
|
436
|
+
|
437
|
+
<div className="sm:col-span-2 sm:col-start-1">
|
438
|
+
<label htmlFor="province-list" className={labelClass}>
|
439
|
+
省份
|
440
|
+
</label>
|
441
|
+
<div className="mt-2">
|
442
|
+
<DropListBox
|
443
|
+
itemList={provinceList}
|
444
|
+
selectedIndex={selectedProvinceIndex}
|
445
|
+
domID={'province-list'}
|
446
|
+
callback={handleSelectProvince}
|
447
|
+
/>
|
448
|
+
</div>
|
449
|
+
</div>
|
450
|
+
|
451
|
+
<div className="sm:col-span-2">
|
452
|
+
<label htmlFor="city-list" className={labelClass}>
|
453
|
+
城市
|
454
|
+
</label>
|
455
|
+
<div className="mt-2">
|
456
|
+
<DropListBox
|
457
|
+
itemList={cityList}
|
458
|
+
domID={'city-list'}
|
459
|
+
callback={handleSelectCity}
|
460
|
+
selectedIndex={selectedCityIndex}
|
461
|
+
/>
|
462
|
+
</div>
|
463
|
+
</div>
|
464
|
+
|
465
|
+
<div className="sm:col-span-2">
|
466
|
+
<label htmlFor="district-list" className={labelClass}>
|
467
|
+
区名
|
468
|
+
</label>
|
469
|
+
<div className="mt-2">
|
470
|
+
<DropListBox
|
471
|
+
itemList={districtList}
|
472
|
+
selectedIndex={selectedDistrictIndex}
|
473
|
+
domID={'district-list'}
|
474
|
+
callback={handleSelectDistrict}
|
475
|
+
/>
|
476
|
+
</div>
|
477
|
+
</div>
|
478
|
+
</div>
|
479
|
+
</div>
|
480
|
+
|
481
|
+
<div className="border-b border-gray-900/10 pb-12">
|
482
|
+
<h2 className="text-base font-semibold leading-7 text-gray-900">系统设置</h2>
|
483
|
+
<h4 className="text-sm font-medium leading-7 text-gray-900">
|
484
|
+
如果你不了解以下配置应该如何设置,保持默认值即可。
|
485
|
+
</h4>
|
486
|
+
<p className="mt-1 text-sm leading-6 text-gray-600"></p>
|
487
|
+
<div className="mt-10 grid grid-cols-1 gap-x-6 gap-y-8 sm:grid-cols-6">
|
488
|
+
<div className="sm:col-span-3">
|
489
|
+
<label htmlFor="step-wait" className={labelClass}>
|
490
|
+
StepWait (步频等待秒数。默认10秒,不建议设置过短,会封IP)
|
491
|
+
</label>
|
492
|
+
<div className="mt-2">
|
493
|
+
<input
|
494
|
+
ref={stepWaitRef}
|
495
|
+
id={'step-wait'}
|
496
|
+
min={1}
|
497
|
+
max={20}
|
498
|
+
step={0.5}
|
499
|
+
type="number"
|
500
|
+
defaultValue={config.stepWait}
|
501
|
+
className={inputClass}
|
502
|
+
/>
|
503
|
+
</div>
|
504
|
+
</div>
|
505
|
+
<div className="sm:col-span-3">
|
506
|
+
<label htmlFor="beforereload-count" className={labelClass}>
|
507
|
+
每周期重试次数 (默认50,后台请求达到该次数才会刷新页面,防止签名过期)
|
508
|
+
</label>
|
509
|
+
<div className="mt-2">
|
510
|
+
<input
|
511
|
+
ref={beforeReloadCountRef}
|
512
|
+
id={'beforereload-count'}
|
513
|
+
min={1}
|
514
|
+
max={100}
|
515
|
+
step={1}
|
516
|
+
type="number"
|
517
|
+
defaultValue={config.afterCountThenReload}
|
518
|
+
className={inputClass}
|
519
|
+
/>
|
520
|
+
</div>
|
521
|
+
</div>
|
522
|
+
</div>
|
523
|
+
|
524
|
+
<div className="mt-10 grid grid-cols-1 gap-x-6 gap-y-8 sm:grid-cols-6">
|
525
|
+
<div className="sm:col-span-3">
|
526
|
+
<label htmlFor="voice-list" className={labelClass}>
|
527
|
+
提示音声音
|
528
|
+
</label>
|
529
|
+
<div className="mt-2">
|
530
|
+
<DropListBox
|
531
|
+
itemList={voiceList}
|
532
|
+
selectedIndex={voiceSelected}
|
533
|
+
domID={'voice-list'}
|
534
|
+
callback={handleSelectVoice}
|
535
|
+
/>
|
536
|
+
</div>
|
537
|
+
</div>
|
538
|
+
<div className="sm:col-span-3">
|
539
|
+
<label htmlFor="voice-times" className={labelClass}>
|
540
|
+
播放次数
|
541
|
+
</label>
|
542
|
+
<div className="mt-2">
|
543
|
+
<input
|
544
|
+
ref={voiceTimesRef}
|
545
|
+
id="voice-times"
|
546
|
+
name="voice-times"
|
547
|
+
type="number"
|
548
|
+
min={1}
|
549
|
+
max={15}
|
550
|
+
step={1}
|
551
|
+
className={inputClass}
|
552
|
+
defaultValue={config.voiceInfo.times}
|
553
|
+
/>
|
554
|
+
</div>
|
555
|
+
</div>
|
556
|
+
|
557
|
+
<div className="sm:col-span-5">
|
558
|
+
<label htmlFor="voice-text" className={labelClass}>
|
559
|
+
提示音文本
|
560
|
+
</label>
|
561
|
+
<div className="mt-2">
|
562
|
+
<input
|
563
|
+
type="text"
|
564
|
+
ref={voiceTextRef}
|
565
|
+
name="voice-text"
|
566
|
+
id="voice-text"
|
567
|
+
className={inputClass}
|
568
|
+
defaultValue={config.voiceInfo.text || ''}
|
569
|
+
/>
|
570
|
+
</div>
|
571
|
+
</div>
|
572
|
+
<div className="sm:col-span-1 align-bottom flex items-end justify-end">
|
573
|
+
<div className="flex w-1/2 items-end justify-end">
|
574
|
+
<button
|
575
|
+
type="submit"
|
576
|
+
className="rounded-md w-full bg-indigo-600 px-3 py-2 text-sm font-semibold text-white shadow-sm hover:bg-indigo-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600"
|
577
|
+
onClick={handlePlaySound}
|
578
|
+
>
|
579
|
+
播放
|
580
|
+
</button>
|
581
|
+
</div>
|
582
|
+
</div>
|
583
|
+
</div>
|
584
|
+
</div>
|
585
|
+
|
586
|
+
<div className="border-b border-gray-900/10 pb-12">
|
587
|
+
<h2 className="text-base font-semibold leading-7 text-gray-900">实验性功能</h2>
|
588
|
+
<h4 className="text-sm font-medium leading-7 text-gray-900">
|
589
|
+
如果你不了解以下配置的功能,可以忽略它们。
|
590
|
+
</h4>
|
591
|
+
<p className="mt-1 text-sm leading-6 text-gray-600"></p>
|
592
|
+
<div className="mt-10 space-y-10">
|
593
|
+
<div className="sm:col-span-6">
|
594
|
+
<label htmlFor="noti-api" className={labelClass}>
|
595
|
+
自定义消息API (由于跨域限制,仅支持GET请求,将以图片 src 方式引入并调用)
|
596
|
+
</label>
|
597
|
+
<div className="mt-2">
|
598
|
+
<input
|
599
|
+
type="text"
|
600
|
+
ref={notiAPIRef}
|
601
|
+
name="noti-api"
|
602
|
+
id="noti-api"
|
603
|
+
className={inputClass}
|
604
|
+
defaultValue={config.selfNotiAPI || ''}
|
605
|
+
/>
|
606
|
+
</div>
|
607
|
+
</div>
|
608
|
+
</div>
|
609
|
+
</div>
|
610
|
+
|
611
|
+
{/* <div className="border-b border-gray-900/10 pb-12">
|
612
|
+
<h2 className="text-base font-semibold leading-7 text-gray-900">Notifications</h2>
|
613
|
+
<p className="mt-1 text-sm leading-6 text-gray-600"></p>
|
614
|
+
|
615
|
+
<div className="mt-10 space-y-10">
|
616
|
+
<fieldset>
|
617
|
+
<legend className="text-sm font-semibold leading-6 text-gray-900">By Email</legend>
|
618
|
+
<div className="mt-6 space-y-6">
|
619
|
+
<div className="relative flex gap-x-3">
|
620
|
+
<div className="flex h-6 items-center">
|
621
|
+
<input
|
622
|
+
id="comments"
|
623
|
+
name="comments"
|
624
|
+
type="checkbox"
|
625
|
+
className="h-4 w-4 rounded border-gray-300 text-indigo-600 focus:ring-indigo-600"
|
626
|
+
/>
|
627
|
+
</div>
|
628
|
+
<div className="text-sm leading-6">
|
629
|
+
<label htmlFor="comments" className="font-medium text-gray-900">
|
630
|
+
Comments
|
631
|
+
</label>
|
632
|
+
<p className="text-gray-500">
|
633
|
+
Get notified when someones posts a comment on a posting.
|
634
|
+
</p>
|
635
|
+
</div>
|
636
|
+
</div>
|
637
|
+
|
638
|
+
</div>
|
639
|
+
</fieldset>
|
640
|
+
<fieldset>
|
641
|
+
<legend className="text-sm font-semibold leading-6 text-gray-900">
|
642
|
+
Push Notifications
|
643
|
+
</legend>
|
644
|
+
<p className="mt-1 text-sm leading-6 text-gray-600">
|
645
|
+
These are delivered via SMS to your mobile phone.
|
646
|
+
</p>
|
647
|
+
<div className="mt-6 space-y-6">
|
648
|
+
<div className="flex items-center gap-x-3">
|
649
|
+
<input
|
650
|
+
id="push-everything"
|
651
|
+
name="push-notifications"
|
652
|
+
type="radio"
|
653
|
+
className="h-4 w-4 border-gray-300 text-indigo-600 focus:ring-indigo-600"
|
654
|
+
/>
|
655
|
+
<label htmlFor="push-everything" className={labelClass}>
|
656
|
+
Everything
|
657
|
+
</label>
|
658
|
+
</div>
|
659
|
+
|
660
|
+
</div>
|
661
|
+
</fieldset>
|
662
|
+
</div>
|
663
|
+
</div> */}
|
664
|
+
</div>
|
665
|
+
|
666
|
+
<div className="mt-6 flex items-center justify-end gap-x-6">
|
667
|
+
<button type="button" className="text-sm font-semibold leading-6 text-gray-900" onClick={handleCancel}>
|
668
|
+
取消
|
669
|
+
</button>
|
670
|
+
<button
|
671
|
+
type="submit"
|
672
|
+
className="rounded-md bg-indigo-600 px-3 py-2 text-sm font-semibold text-white shadow-sm hover:bg-indigo-500 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-indigo-600"
|
673
|
+
onClick={handleSave}
|
674
|
+
>
|
675
|
+
保存
|
676
|
+
</button>
|
677
|
+
</div>
|
678
|
+
</main>
|
679
|
+
)
|
680
|
+
}
|
681
|
+
|
682
|
+
const saveAsync = async (config: IPHONEORDER_CONFIG) => {
|
683
|
+
await saveToStorage(config, storeKeys.orderConfig)
|
684
|
+
await sleep(1)
|
685
|
+
window.close()
|
686
|
+
}
|
687
|
+
|
688
|
+
const validateVoices = ['zh-cn', 'zh-tw', 'zh-hk', 'en-us', 'en-gb']
|
689
|
+
const getVoices = async (): Promise<VoiceType[]> => {
|
690
|
+
let voiceList: VoiceType[] = [defaultItem]
|
691
|
+
if (typeof chrome !== 'undefined' && chrome?.tts) {
|
692
|
+
return new Promise((resolve, reject) => {
|
693
|
+
chrome.tts.getVoices(
|
694
|
+
// @ts-ignore
|
695
|
+
function (voices) {
|
696
|
+
voiceList = []
|
697
|
+
_map(voices, v => {
|
698
|
+
const { lang, voiceName } = v || {}
|
699
|
+
const prefixlang = lang && lang.toLowerCase()
|
700
|
+
if (validateVoices.includes(prefixlang)) {
|
701
|
+
voiceList.push({
|
702
|
+
lang: lang,
|
703
|
+
voiceName: voiceName,
|
704
|
+
name: `${lang} - ${voiceName}`,
|
705
|
+
id: `${lang} - ${voiceName}`,
|
706
|
+
})
|
707
|
+
}
|
708
|
+
})
|
709
|
+
voiceList = _sortBy(voiceList, function (o) {
|
710
|
+
const x = !o?.lang ? 9999 : validateVoices.indexOf(o.lang.toLowerCase())
|
711
|
+
return x
|
712
|
+
})
|
713
|
+
resolve(voiceList)
|
714
|
+
}
|
715
|
+
)
|
716
|
+
})
|
717
|
+
}
|
718
|
+
|
719
|
+
return voiceList
|
720
|
+
}
|