新增小程序后端代码,包括数据库、路由、服务层等。
小程序前后端打通,包括登录、注册、绑定设备、查询绑定信息,修改小朋友名称等功能。
This commit is contained in:
@@ -1,27 +1,59 @@
|
||||
import { View, Text, Button, Image } from '@tarojs/components'
|
||||
import { View, Text, Button, Image, Input } from '@tarojs/components'
|
||||
import { useState, useEffect } from 'react'
|
||||
import Taro from '@tarojs/taro'
|
||||
import { getToken } from '@/services/api'
|
||||
import { getCurrentBinding, directBind } from '@/services/binding'
|
||||
import { getChildren, createChild } from '@/services/child'
|
||||
import './index.scss'
|
||||
|
||||
interface Child {
|
||||
child_id: number
|
||||
child_name: string
|
||||
}
|
||||
|
||||
export default function Bind() {
|
||||
const [isScanning, setIsScanning] = useState(false)
|
||||
const [loading, setLoading] = useState(true)
|
||||
const [children, setChildren] = useState<Child[]>([])
|
||||
const [newChildName, setNewChildName] = useState('')
|
||||
const [saving, setSaving] = useState(false)
|
||||
|
||||
useEffect(() => {
|
||||
// 检查是否已登录
|
||||
const isLoggedIn = Taro.getStorageSync('isLoggedIn')
|
||||
if (!isLoggedIn) {
|
||||
// 未登录,跳转到登录页面
|
||||
checkAuth()
|
||||
}, [])
|
||||
|
||||
const checkAuth = async () => {
|
||||
const token = getToken()
|
||||
if (!token) {
|
||||
Taro.redirectTo({ url: '/pages/login/index' })
|
||||
return
|
||||
}
|
||||
|
||||
// 检查是否已绑定设备
|
||||
const hasDevice = Taro.getStorageSync('hasDevice')
|
||||
if (hasDevice) {
|
||||
// 已绑定设备,跳转到首页
|
||||
Taro.reLaunch({ url: '/pages/device/index' })
|
||||
try {
|
||||
const childRes = await getChildren()
|
||||
setChildren(childRes.items || [])
|
||||
|
||||
if (childRes.items?.length === 0) {
|
||||
setLoading(false)
|
||||
return
|
||||
}
|
||||
} catch (err) {
|
||||
console.error('Get children failed:', err)
|
||||
}
|
||||
}, [])
|
||||
|
||||
try {
|
||||
const binding = await getCurrentBinding()
|
||||
if (binding) {
|
||||
Taro.showToast({ title: '已有设备绑定', icon: 'none' })
|
||||
setTimeout(() => {
|
||||
Taro.reLaunch({ url: '/pages/device/index' })
|
||||
}, 1000)
|
||||
return
|
||||
}
|
||||
} catch (err) {
|
||||
}
|
||||
setLoading(false)
|
||||
}
|
||||
|
||||
const handleScanCode = () => {
|
||||
setIsScanning(true)
|
||||
@@ -29,56 +61,57 @@ export default function Bind() {
|
||||
Taro.scanCode({
|
||||
onlyFromCamera: true,
|
||||
scanType: ['qrCode', 'barCode'],
|
||||
success: (res) => {
|
||||
success: async (res) => {
|
||||
setIsScanning(false)
|
||||
console.log('扫码结果:', res.result)
|
||||
const deviceId = res.result
|
||||
|
||||
// 模拟绑定设备
|
||||
Taro.showLoading({ title: '绑定中...' })
|
||||
|
||||
setTimeout(() => {
|
||||
Taro.hideLoading()
|
||||
|
||||
// 保存设备信息
|
||||
Taro.setStorageSync('hasDevice', true)
|
||||
Taro.setStorageSync('deviceInfo', {
|
||||
id: res.result || 'DEVICE_' + Date.now(),
|
||||
name: '小夏的伴伴',
|
||||
status: 'online',
|
||||
battery: 82,
|
||||
bindTime: new Date().toLocaleString()
|
||||
})
|
||||
|
||||
Taro.showToast({
|
||||
title: '设备绑定成功',
|
||||
icon: 'success',
|
||||
duration: 2000,
|
||||
complete: () => {
|
||||
// 绑定成功,跳转到首页
|
||||
setTimeout(() => {
|
||||
Taro.reLaunch({ url: '/pages/device/index' })
|
||||
}, 500)
|
||||
}
|
||||
})
|
||||
}, 1500)
|
||||
if (!deviceId) {
|
||||
Taro.showToast({ title: '无效的设备码', icon: 'none' })
|
||||
return
|
||||
}
|
||||
|
||||
await doBind(deviceId)
|
||||
},
|
||||
fail: (err) => {
|
||||
setIsScanning(false)
|
||||
console.error('扫码失败:', err)
|
||||
|
||||
// 用户取消扫码,不显示错误
|
||||
if (err.errMsg && err.errMsg.includes('cancel')) {
|
||||
return
|
||||
}
|
||||
|
||||
Taro.showToast({
|
||||
title: '扫码失败,请重试',
|
||||
icon: 'none'
|
||||
})
|
||||
Taro.showToast({ title: '扫码失败', icon: 'none' })
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const doBind = async (deviceId: string) => {
|
||||
if (children.length === 0) {
|
||||
Taro.showToast({ title: '请先添加儿童', icon: 'none' })
|
||||
return
|
||||
}
|
||||
|
||||
Taro.showLoading({ title: '绑定中...' })
|
||||
|
||||
try {
|
||||
await directBind({
|
||||
device_id: deviceId,
|
||||
child_id: children[0].child_id
|
||||
})
|
||||
Taro.hideLoading()
|
||||
Taro.showToast({
|
||||
title: '设备绑定成功',
|
||||
icon: 'success',
|
||||
duration: 1500,
|
||||
complete: () => {
|
||||
setTimeout(() => {
|
||||
Taro.reLaunch({ url: '/pages/device/index' })
|
||||
}, 500)
|
||||
}
|
||||
})
|
||||
} catch (err: any) {
|
||||
Taro.hideLoading()
|
||||
Taro.showToast({ title: err.message || '绑定失败', icon: 'none' })
|
||||
}
|
||||
}
|
||||
|
||||
const handleManualInput = () => {
|
||||
Taro.showModal({
|
||||
title: '手动输入设备码',
|
||||
@@ -86,37 +119,60 @@ export default function Bind() {
|
||||
placeholderText: '请输入设备底部的设备码',
|
||||
success: (res) => {
|
||||
if (res.confirm && res.content) {
|
||||
// 模拟绑定设备
|
||||
Taro.showLoading({ title: '绑定中...' })
|
||||
|
||||
setTimeout(() => {
|
||||
Taro.hideLoading()
|
||||
|
||||
Taro.setStorageSync('hasDevice', true)
|
||||
Taro.setStorageSync('deviceInfo', {
|
||||
id: res.content,
|
||||
name: '小夏的伴伴',
|
||||
status: 'online',
|
||||
battery: 82,
|
||||
bindTime: new Date().toLocaleString()
|
||||
})
|
||||
|
||||
Taro.showToast({
|
||||
title: '设备绑定成功',
|
||||
icon: 'success',
|
||||
duration: 2000,
|
||||
complete: () => {
|
||||
setTimeout(() => {
|
||||
Taro.reLaunch({ url: '/pages/device/index' })
|
||||
}, 500)
|
||||
}
|
||||
})
|
||||
}, 1500)
|
||||
doBind(res.content)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
const handleAddChild = async () => {
|
||||
if (!newChildName.trim()) {
|
||||
Taro.showToast({ title: '请输入儿童姓名', icon: 'none' })
|
||||
return
|
||||
}
|
||||
setSaving(true)
|
||||
try {
|
||||
await createChild({ child_name: newChildName.trim() })
|
||||
Taro.showToast({ title: '添加成功', icon: 'success' })
|
||||
const childRes = await getChildren()
|
||||
setChildren(childRes.items || [])
|
||||
} catch (err) {
|
||||
Taro.showToast({ title: '添加失败', icon: 'none' })
|
||||
} finally {
|
||||
setSaving(false)
|
||||
}
|
||||
}
|
||||
|
||||
if (loading) {
|
||||
return (
|
||||
<View className='bind-page'>
|
||||
<View className='loading'>加载中...</View>
|
||||
</View>
|
||||
)
|
||||
}
|
||||
|
||||
if (children.length === 0) {
|
||||
return (
|
||||
<View className='bind-page'>
|
||||
<View className='empty-tip'>
|
||||
<Text className='empty-title'>请先添加儿童</Text>
|
||||
<Text className='empty-desc'>绑定设备前需要先添加儿童信息</Text>
|
||||
<View className='empty-input-wrap'>
|
||||
<Input
|
||||
className='empty-input'
|
||||
placeholder='请输入儿童姓名'
|
||||
value={newChildName}
|
||||
onInput={(e) => setNewChildName(e.detail.value)}
|
||||
/>
|
||||
</View>
|
||||
<View className='empty-btn' onClick={handleAddChild}>
|
||||
<Text className='empty-btn-text'>{saving ? '保存中...' : '确认添加'}</Text>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<View className='bind-page'>
|
||||
<View className='bind-header'>
|
||||
@@ -170,4 +226,4 @@ export default function Bind() {
|
||||
</View>
|
||||
</View>
|
||||
)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user