feat(小程序): 支持多设备绑定与管理
This commit is contained in:
@@ -1,97 +1,126 @@
|
||||
import Taro from '@tarojs/taro'
|
||||
import { request } from './api'
|
||||
|
||||
const SELECTED_BINDING_DEVICE_ID_KEY = 'selectedBindingDeviceId'
|
||||
|
||||
function buildQuery(params: Record<string, string | number | undefined | null>): string {
|
||||
return Object.entries(params)
|
||||
.filter(([, value]) => value !== undefined && value !== null && value !== '')
|
||||
.map(([key, value]) => `${encodeURIComponent(key)}=${encodeURIComponent(String(value))}`)
|
||||
.join('&')
|
||||
}
|
||||
|
||||
export interface Binding {
|
||||
device_id: string
|
||||
child_id: number | null
|
||||
child_name?: string | null
|
||||
status: number
|
||||
bound_at: string
|
||||
}
|
||||
|
||||
export interface BindStartResponse {
|
||||
bind_token: string
|
||||
expires_at: string
|
||||
}
|
||||
export interface BindingListItem extends Binding {}
|
||||
|
||||
export interface BindHistoryItem {
|
||||
device_id: string
|
||||
child_id: number | null
|
||||
bound_at: string
|
||||
unbound_at?: string
|
||||
}
|
||||
|
||||
export interface BindHistoryResponse {
|
||||
items: BindHistoryItem[]
|
||||
export interface BindingListResponse {
|
||||
items: BindingListItem[]
|
||||
total: number
|
||||
next_cursor?: string
|
||||
next_cursor?: number | null
|
||||
}
|
||||
|
||||
export interface DirectBindPayload {
|
||||
device_id: string
|
||||
serial_number: string
|
||||
child_id?: number
|
||||
}
|
||||
|
||||
export function getSelectedBindingDeviceId(): string | null {
|
||||
const value = Taro.getStorageSync(SELECTED_BINDING_DEVICE_ID_KEY)
|
||||
const deviceId = String(value || '').trim()
|
||||
return deviceId || null
|
||||
}
|
||||
|
||||
export function setSelectedBindingDeviceId(deviceId?: string | null) {
|
||||
const normalizedDeviceId = String(deviceId || '').trim()
|
||||
if (!normalizedDeviceId) {
|
||||
Taro.removeStorageSync(SELECTED_BINDING_DEVICE_ID_KEY)
|
||||
return
|
||||
}
|
||||
Taro.setStorageSync(SELECTED_BINDING_DEVICE_ID_KEY, normalizedDeviceId)
|
||||
}
|
||||
|
||||
export function clearSelectedBindingDeviceId() {
|
||||
Taro.removeStorageSync(SELECTED_BINDING_DEVICE_ID_KEY)
|
||||
}
|
||||
|
||||
export function resolveBindingSelection<T extends { device_id: string }>(items: T[]): T | null {
|
||||
const selectedDeviceId = getSelectedBindingDeviceId()
|
||||
const selectedBinding = selectedDeviceId ? items.find((item) => item.device_id === selectedDeviceId) || null : null
|
||||
const activeBinding = selectedBinding || items[0] || null
|
||||
|
||||
if (activeBinding) {
|
||||
setSelectedBindingDeviceId(activeBinding.device_id)
|
||||
} else {
|
||||
clearSelectedBindingDeviceId()
|
||||
}
|
||||
|
||||
return activeBinding
|
||||
}
|
||||
|
||||
// 获取当前绑定信息
|
||||
export async function getCurrentBinding(): Promise<Binding | null> {
|
||||
try {
|
||||
return await request<Binding>('/bindings/current')
|
||||
} catch (err: any) {
|
||||
if (err.status === 404) {
|
||||
return null
|
||||
}
|
||||
throw err
|
||||
} catch (error: any) {
|
||||
if (error?.status === 404) return null
|
||||
throw error
|
||||
}
|
||||
}
|
||||
|
||||
// 开始绑定设备
|
||||
export async function startBind(data: {
|
||||
device_id: string
|
||||
child_id?: number
|
||||
}): Promise<BindStartResponse> {
|
||||
return request<BindStartResponse>('/bindings/start', {
|
||||
method: 'POST',
|
||||
data,
|
||||
export async function getBindings(cursor?: number, limit: number = 20): Promise<BindingListResponse> {
|
||||
const query = buildQuery({
|
||||
cursor,
|
||||
limit,
|
||||
})
|
||||
return request<BindingListResponse>(`/bindings?${query}`)
|
||||
}
|
||||
|
||||
// 确认绑定设备
|
||||
export async function confirmBind(data: {
|
||||
bind_token: string
|
||||
challenge_code: string
|
||||
}): Promise<{ device_id: string; child_id: number | null }> {
|
||||
return request('/bindings/confirm', {
|
||||
method: 'POST',
|
||||
data,
|
||||
})
|
||||
}
|
||||
|
||||
// 获取设备绑定信息
|
||||
export async function getBinding(deviceId: string): Promise<Binding | null> {
|
||||
// If no deviceId, get the first binding for this user
|
||||
if (!deviceId) {
|
||||
try {
|
||||
// Try to get binding list - for now return null if no specific device
|
||||
return null
|
||||
} catch {
|
||||
return null
|
||||
}
|
||||
try {
|
||||
return await request<Binding>(`/bindings/${deviceId}`)
|
||||
} catch (error: any) {
|
||||
if (error?.status === 404) return null
|
||||
throw error
|
||||
}
|
||||
return request<Binding>(`/bindings/${deviceId}`)
|
||||
}
|
||||
|
||||
// 解绑设备
|
||||
export async function unbindDevice(deviceId: string): Promise<void> {
|
||||
return request(`/bindings/${deviceId}`, {
|
||||
method: 'DELETE',
|
||||
})
|
||||
export async function resolveActiveBinding(): Promise<Binding | null> {
|
||||
const selectedDeviceId = getSelectedBindingDeviceId()
|
||||
|
||||
if (selectedDeviceId) {
|
||||
const selectedBinding = await getBinding(selectedDeviceId)
|
||||
if (selectedBinding) {
|
||||
setSelectedBindingDeviceId(selectedBinding.device_id)
|
||||
return selectedBinding
|
||||
}
|
||||
|
||||
clearSelectedBindingDeviceId()
|
||||
}
|
||||
|
||||
const currentBinding = await getCurrentBinding()
|
||||
if (currentBinding?.device_id) {
|
||||
setSelectedBindingDeviceId(currentBinding.device_id)
|
||||
} else {
|
||||
clearSelectedBindingDeviceId()
|
||||
}
|
||||
|
||||
return currentBinding
|
||||
}
|
||||
|
||||
// 直接绑定设备(手动输入设备码)
|
||||
export async function directBind(data: {
|
||||
device_id: string
|
||||
child_id?: number
|
||||
}): Promise<{ device_id: string; child_id: number | null }> {
|
||||
export async function directBind(data: DirectBindPayload): Promise<{ device_id: string; child_id: number | null }> {
|
||||
return request('/bindings/direct', {
|
||||
method: 'POST',
|
||||
data,
|
||||
})
|
||||
}
|
||||
|
||||
// Bind child profile after device has been bound to parent account.
|
||||
export async function setBindingChild(
|
||||
deviceId: string,
|
||||
data: { child_id: number }
|
||||
@@ -102,15 +131,8 @@ export async function setBindingChild(
|
||||
})
|
||||
}
|
||||
|
||||
// 获取绑定历史
|
||||
export async function getBindHistory(
|
||||
deviceId: string,
|
||||
cursor?: string,
|
||||
limit: number = 20
|
||||
): Promise<BindHistoryResponse> {
|
||||
const params = new URLSearchParams()
|
||||
if (cursor) params.append('cursor', cursor)
|
||||
params.append('limit', String(limit))
|
||||
|
||||
return request<BindHistoryResponse>(`/bindings/history/${deviceId}?${params.toString()}`)
|
||||
export async function unbindDevice(deviceId: string): Promise<void> {
|
||||
return request(`/bindings/${deviceId}`, {
|
||||
method: 'DELETE',
|
||||
})
|
||||
}
|
||||
|
||||
@@ -1,5 +1,12 @@
|
||||
import { request } from './api'
|
||||
|
||||
function buildQuery(params: Record<string, string | number | undefined | null>): string {
|
||||
return Object.entries(params)
|
||||
.filter(([, value]) => value !== undefined && value !== null && value !== '')
|
||||
.map(([key, value]) => `${encodeURIComponent(key)}=${encodeURIComponent(String(value))}`)
|
||||
.join('&')
|
||||
}
|
||||
|
||||
export interface Child {
|
||||
child_id: number
|
||||
child_name: string
|
||||
@@ -11,10 +18,9 @@ export interface Child {
|
||||
export interface ChildListResponse {
|
||||
items: Child[]
|
||||
total: number
|
||||
next_cursor?: number
|
||||
next_cursor?: number | null
|
||||
}
|
||||
|
||||
// 创建小孩档案
|
||||
export async function createChild(data: {
|
||||
child_name: string
|
||||
child_gender?: number
|
||||
@@ -26,24 +32,18 @@ export async function createChild(data: {
|
||||
})
|
||||
}
|
||||
|
||||
// 获取小孩列表
|
||||
export async function getChildren(
|
||||
cursor?: number,
|
||||
limit: number = 20
|
||||
): Promise<ChildListResponse> {
|
||||
const params = new URLSearchParams()
|
||||
if (cursor) params.append('cursor', String(cursor))
|
||||
params.append('limit', String(limit))
|
||||
|
||||
return request<ChildListResponse>(`/children?${params.toString()}`)
|
||||
export async function getChildren(cursor?: number, limit: number = 20): Promise<ChildListResponse> {
|
||||
const query = buildQuery({
|
||||
cursor,
|
||||
limit,
|
||||
})
|
||||
return request<ChildListResponse>(`/children?${query}`)
|
||||
}
|
||||
|
||||
// 获取单个小孩信息
|
||||
export async function getChild(childId: number): Promise<Child> {
|
||||
return request<Child>(`/children/${childId}`)
|
||||
}
|
||||
|
||||
// 更新小孩信息
|
||||
export async function updateChild(
|
||||
childId: number,
|
||||
data: { child_name?: string; child_gender?: number; child_birthday?: string }
|
||||
@@ -52,4 +52,4 @@ export async function updateChild(
|
||||
method: 'PATCH',
|
||||
data,
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user