feat: allow binding device without child

This commit is contained in:
stu2not
2026-06-02 15:10:52 +08:00
parent aea7a3fe42
commit e039ac63be
2 changed files with 55 additions and 34 deletions

View File

@@ -1,6 +1,6 @@
import { View, Text, Image, Input, Button } from '@tarojs/components'
import { useEffect, useRef, useState } from 'react'
import Taro, { useDidShow, useDidHide } from '@tarojs/taro'
import Taro, { useDidShow, useDidHide, useRouter } from '@tarojs/taro'
import { getToken } from '@/services/auth'
import {
@@ -83,6 +83,8 @@ function parseBindingPayload(rawValue: string): { deviceId: string; serialNumber
}
export default function Bind() {
const router = useRouter()
const isAssignMode = String(router.params?.mode || '').trim() === 'assign'
const systemBanner = useSystemBanner()
const [loading, setLoading] = useState(true)
const [submitting, setSubmitting] = useState(false)
@@ -112,12 +114,17 @@ export default function Bind() {
const hasSelectedOrNewChild = selectedChildId !== null || Boolean(newChildName.trim())
const isPendingBinding = Boolean(pendingDeviceId)
const isPollingBind = bindStatus === SESSION_STATUS_PENDING && Boolean(bindToken)
const canEditDeviceFields = hasSelectedOrNewChild && !isPollingBind
const canEditDeviceFields = !isPendingBinding && !isPollingBind
const canSubmit = isPendingBinding ? hasSelectedOrNewChild : !isPollingBind
const goDevicePage = () => {
Taro.reLaunch({ url: '/pages/device/index' })
}
const goManagePage = () => {
Taro.switchTab({ url: '/pages/sleep/index' })
}
const handleBack = () => {
if (getCurrentPages().length > 1) {
Taro.navigateBack()
@@ -167,10 +174,18 @@ export default function Bind() {
if (session.status === SESSION_STATUS_COMPLETED) {
setSelectedBindingDeviceId(session.device_id)
setBindHint(session.card_uuid ? `绑定完成,卡号 ${session.card_uuid}` : '绑定完成')
setBindHint(
session.child_id
? session.card_uuid ? `绑定完成,卡号 ${session.card_uuid}` : '绑定完成'
: session.card_uuid ? `绑定完成,卡号 ${session.card_uuid},稍后可关联孩子` : '绑定完成,稍后可关联孩子'
)
Taro.showToast({ title: '设备绑定成功', icon: 'success' })
setTimeout(() => {
goDevicePage()
if (session.child_id) {
goDevicePage()
} else {
goManagePage()
}
}, 500)
return
}
@@ -209,7 +224,7 @@ export default function Bind() {
setChildren(currentChildren)
setSelectedChildId(currentChild?.child_id || null)
if (activeBinding?.device_id && !activeBinding.child_id) {
if (isAssignMode && activeBinding?.device_id && !activeBinding.child_id) {
setPendingDeviceId(activeBinding.device_id)
setDeviceId(activeBinding.device_id)
} else {
@@ -243,7 +258,7 @@ export default function Bind() {
const handleScanCode = () => {
if (!canEditDeviceFields) {
Taro.showToast({
title: '请先选择或新建儿童资料',
title: isPollingBind ? '绑定确认中,请稍候' : '当前不能扫码',
icon: 'none',
})
return
@@ -283,11 +298,11 @@ export default function Bind() {
try {
const childId = await ensureChildId()
if (!childId) {
throw new Error('请先选择儿童或填写新的儿童昵称')
}
if (isPendingBinding) {
if (!childId) {
throw new Error('请先选择儿童或填写新的儿童昵称')
}
await setBindingChild(pendingDeviceId!, { child_id: childId })
setSelectedBindingDeviceId(pendingDeviceId)
Taro.showToast({ title: '关联完成', icon: 'success' })
@@ -305,15 +320,15 @@ export default function Bind() {
}
resetBindSessionState()
const session = await startNFCBind({
const bindPayload = {
device_id: deviceId.trim(),
serial_number: serialNumber.trim(),
child_id: childId,
})
}
const session = await startNFCBind(childId ? { ...bindPayload, child_id: childId } : bindPayload)
setBindToken(session.bind_token)
setBindStatus(session.status)
setBindHint('已发送绑卡指令,请去设备上贴自己的卡')
setBindHint(childId ? '已发送绑卡指令,请去设备上贴自己的卡' : '已发送绑卡指令,贴卡后设备会先进入待关联状态')
schedulePoll(session.bind_token)
Taro.showToast({ title: '请去设备上贴卡', icon: 'none' })
} catch (error: any) {
@@ -366,11 +381,9 @@ export default function Bind() {
<Image className='control-icon-img' src={require('../../assets/tab-icons/rings.png')} mode='aspectFit' />
</View>
<Text className='scan-text'>
{!canEditDeviceFields ? '请先完成儿童资料' : isScanning ? '正在扫码...' : '点击扫码'}
</Text>
<Text className='scan-hint'>
{!canEditDeviceFields ? '先选儿童,再扫设备二维码' : '扫描设备二维码或条码'}
{isScanning ? '正在扫码...' : '点击扫码'}
</Text>
<Text className='scan-hint'></Text>
</View>
<View className='scan-corners'>
@@ -388,6 +401,18 @@ export default function Bind() {
{children.length > 0 && (
<View className='child-list'>
{!isPendingBinding && (
<View
className={`child-item ${!hasSelectedOrNewChild ? 'selected' : ''}`}
onClick={() => {
setSelectedChildId(null)
setStoredSelectedChildId(null)
setNewChildName('')
}}
>
<Text className='child-item-text'></Text>
</View>
)}
{children.map((child) => (
<View
key={child.child_id}
@@ -408,11 +433,15 @@ export default function Bind() {
)}
<View className='field-item'>
<Text className='field-label'></Text>
<Text className='field-label'>{isPendingBinding ? '新建儿童昵称' : '新建儿童昵称(可选)'}</Text>
<Input
className='field-input'
value={newChildName}
placeholder={children.length > 0 ? '不选已有儿童时,可填写新昵称' : '请输入儿童昵称'}
placeholder={
isPendingBinding
? children.length > 0 ? '不选已有儿童时,可填写新昵称' : '请输入儿童昵称'
: children.length > 0 ? '不选已有儿童时,可填写新昵称' : '可不填,稍后再关联孩子'
}
onInput={(event) => {
setNewChildName(event.detail.value)
if (event.detail.value) {
@@ -429,8 +458,8 @@ export default function Bind() {
? '可以选已有儿童,也可以新建一个儿童后关联当前设备'
: '当前还没有儿童资料,请先创建一个儿童资料后关联设备'
: children.length > 0
? '可以选已有儿童,也可以在这里新建一个儿童后再绑定设备'
: '当前还没有儿童资料,请先创建一个儿童资料'}
? '儿童资料可选;不选择时,设备会先绑定到账号,稍后可关联孩子'
: '也可以先不创建儿童,直接绑定设备,稍后再关联孩子'}
</Text>
</View>
@@ -444,7 +473,7 @@ export default function Bind() {
className='field-input'
value={deviceId}
disabled={!canEditDeviceFields || isPendingBinding}
placeholder={canEditDeviceFields ? '请输入设备号' : '请先选择儿童资料'}
placeholder={canEditDeviceFields ? '请输入设备号' : '绑定确认中'}
onInput={(event) => setDeviceId(event.detail.value)}
/>
</View>
@@ -455,7 +484,7 @@ export default function Bind() {
className='field-input'
value={serialNumber}
disabled={!canEditDeviceFields || isPendingBinding}
placeholder={canEditDeviceFields ? '请输入设备序列号' : '请先选择儿童资料'}
placeholder={canEditDeviceFields ? '请输入设备序列号' : '绑定确认中'}
onInput={(event) => setSerialNumber(event.detail.value)}
/>
</View>
@@ -478,7 +507,7 @@ export default function Bind() {
<Button
className='submit-btn'
loading={submitting}
disabled={submitting || !hasSelectedOrNewChild || isPollingBind}
disabled={submitting || !canSubmit}
onClick={handleSubmit}
>
{isPendingBinding ? '完成儿童关联' : isPollingBind ? '等待贴卡确认' : '发送绑卡指令'}
@@ -489,7 +518,7 @@ export default function Bind() {
<Text className='tips-title'></Text>
<View className='tip-item'>
<Text className='tip-number'>1</Text>
<Text className='tip-text'></Text>
<Text className='tip-text'>{isPendingBinding ? '先选择已有儿童,或者在本页新建一个儿童' : '儿童资料可选,不选择时设备会先进入待关联状态'}</Text>
</View>
<View className='tip-item'>
<Text className='tip-number'>2</Text>

View File

@@ -343,7 +343,7 @@ export default function Sleep() {
if (!targetBinding?.device_id) return
setSelectedBindingDeviceId(targetBinding.device_id)
setShowAssignDeviceModal(false)
Taro.navigateTo({ url: '/pages/bind/index' })
Taro.navigateTo({ url: '/pages/bind/index?mode=assign' })
}
const handleSubmitModal = async () => {
@@ -559,14 +559,6 @@ export default function Sleep() {
}
if (item.name === '绑定设备') {
if (!currentChild) {
handleOpenModal('add')
Taro.showToast({
title: '请先添加儿童资料',
icon: 'none',
})
return
}
Taro.navigateTo({ url: '/pages/bind/index' })
return
}