集成家长短信通知
This commit is contained in:
@@ -44,6 +44,7 @@
|
||||
|
||||
.user-info {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
|
||||
.user-name {
|
||||
font-size: 34px;
|
||||
@@ -51,6 +52,15 @@
|
||||
color: #1A1A1A;
|
||||
display: block;
|
||||
}
|
||||
|
||||
.user-phone {
|
||||
display: block;
|
||||
margin-top: 8px;
|
||||
font-size: 24px;
|
||||
line-height: 1.4;
|
||||
color: #666666;
|
||||
word-break: break-all;
|
||||
}
|
||||
}
|
||||
|
||||
.verified-badge {
|
||||
@@ -64,6 +74,32 @@
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
|
||||
.phone-auth-btn {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
flex-shrink: 0;
|
||||
width: 150px;
|
||||
height: 60px;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
border-radius: 14px;
|
||||
background: #FF8C42;
|
||||
color: #FFFFFF;
|
||||
font-size: 24px;
|
||||
font-weight: 600;
|
||||
line-height: 60px;
|
||||
|
||||
&::after {
|
||||
border: none;
|
||||
}
|
||||
|
||||
&[disabled] {
|
||||
background: #CBD5E1;
|
||||
color: #FFFFFF;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
.menu-card {
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { View, Text, Image, Input, Button } from '@tarojs/components'
|
||||
import { useState } from 'react'
|
||||
import Taro, { useDidShow, useShareAppMessage } from '@tarojs/taro'
|
||||
import { clearToken, getToken } from '@/services/auth'
|
||||
import { authorizeParentPhone, clearToken, getParent, getToken, Parent } from '@/services/auth'
|
||||
import { DEVICE_UNAVAILABLE_MESSAGE } from '@/services/api'
|
||||
import { getCurrentUserId } from '@/services/session'
|
||||
import {
|
||||
@@ -72,7 +72,8 @@ export default function Sleep() {
|
||||
const [modalType, setModalType] = useState<'add' | 'edit'>('add')
|
||||
const [childName, setChildName] = useState('')
|
||||
const [editingChildId, setEditingChildId] = useState<number | null>(null)
|
||||
const [parentInfo, setParentInfo] = useState<{ nickname?: string; avatar_url?: string }>({})
|
||||
const [parentInfo, setParentInfo] = useState<Parent | null>(null)
|
||||
const [isAuthorizingPhone, setIsAuthorizingPhone] = useState(false)
|
||||
|
||||
useDidShow(() => {
|
||||
void loadData()
|
||||
@@ -95,12 +96,14 @@ export default function Sleep() {
|
||||
setLoading(true)
|
||||
try {
|
||||
const context = await loadCurrentChildBindingContext()
|
||||
const currentUserId = getCurrentUserId()
|
||||
const currentParent = currentUserId ? await loadCurrentParent(currentUserId) : null
|
||||
const nextBinding = (context.currentBinding as BindingListItem | null) || null
|
||||
setChildren(context.children)
|
||||
setBindings(context.bindings as BindingListItem[])
|
||||
setCurrentChild(context.currentChild)
|
||||
setBinding(nextBinding)
|
||||
setParentInfo(Taro.getStorageSync('userInfo') || {})
|
||||
setParentInfo(currentParent)
|
||||
void loadFirmwareStatus(nextBinding?.device_id)
|
||||
void loadRoleData(nextBinding?.device_id)
|
||||
void loadFamilyData(nextBinding?.device_id)
|
||||
@@ -115,6 +118,15 @@ export default function Sleep() {
|
||||
}
|
||||
}
|
||||
|
||||
const loadCurrentParent = async (userId: number): Promise<Parent | null> => {
|
||||
try {
|
||||
return await getParent(userId)
|
||||
} catch (error) {
|
||||
console.warn('[manage] parent profile load failed:', error)
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
const currentChildName = currentChild?.child_name || '未设置'
|
||||
|
||||
const loadFirmwareStatus = async (deviceId?: string | null) => {
|
||||
@@ -526,6 +538,45 @@ export default function Sleep() {
|
||||
})
|
||||
}
|
||||
|
||||
const handleAuthorizePhone = async (event: any) => {
|
||||
if (isAuthorizingPhone) return
|
||||
const phoneCode = String(event?.detail?.code || '').trim()
|
||||
if (!phoneCode) {
|
||||
const errMsg = String(event?.detail?.errMsg || '')
|
||||
Taro.showToast({
|
||||
title: errMsg.includes('deny') || errMsg.includes('fail') ? '未授权手机号' : '未获取到手机号授权',
|
||||
icon: 'none',
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
setIsAuthorizingPhone(true)
|
||||
Taro.showLoading({ title: '保存中...' })
|
||||
try {
|
||||
const parent = await authorizeParentPhone(phoneCode)
|
||||
setParentInfo(parent)
|
||||
const cachedUserInfo = (Taro.getStorageSync('userInfo') || {}) as {
|
||||
nickname?: string
|
||||
avatar_url?: string
|
||||
}
|
||||
Taro.setStorageSync('userInfo', {
|
||||
...cachedUserInfo,
|
||||
nickname: parent.nickname || cachedUserInfo.nickname,
|
||||
avatar_url: parent.avatar_url || cachedUserInfo.avatar_url,
|
||||
})
|
||||
Taro.showToast({ title: '短信通知已开启', icon: 'success' })
|
||||
} catch (error: any) {
|
||||
console.error('[manage] phone authorization failed:', error)
|
||||
Taro.showToast({
|
||||
title: error?.message || '手机号保存失败',
|
||||
icon: 'none',
|
||||
})
|
||||
} finally {
|
||||
Taro.hideLoading()
|
||||
setIsAuthorizingPhone(false)
|
||||
}
|
||||
}
|
||||
|
||||
const handleMenuClick = (item: MenuItem) => {
|
||||
if (item.disabled) return
|
||||
|
||||
@@ -589,7 +640,9 @@ export default function Sleep() {
|
||||
)
|
||||
}
|
||||
|
||||
const parentDisplayName = parentInfo.nickname?.trim() || '家长'
|
||||
const parentDisplayName = parentInfo?.nickname?.trim() || '家长'
|
||||
const parentPhone = String(parentInfo?.phone || '').trim()
|
||||
const parentPhoneLabel = parentPhone ? `${parentPhone.slice(0, 3)}****${parentPhone.slice(-4)}` : '未开启'
|
||||
const unassignedBindings = bindings.filter((item) => item.device_id && item.child_id === null)
|
||||
const currentFirmwareLabel = firmwareStatus?.current_version || '--'
|
||||
const latestFirmwareLabel = firmwareStatus?.latest_version || '--'
|
||||
@@ -694,7 +747,7 @@ export default function Sleep() {
|
||||
|
||||
<View className='user-card'>
|
||||
<View className='user-avatar'>
|
||||
{parentInfo.avatar_url ? (
|
||||
{parentInfo?.avatar_url ? (
|
||||
<Image className='avatar-img image-avatar' src={parentInfo.avatar_url} mode='aspectFill' />
|
||||
) : (
|
||||
<Text className='avatar-img'>👤</Text>
|
||||
@@ -702,10 +755,23 @@ export default function Sleep() {
|
||||
</View>
|
||||
<View className='user-info'>
|
||||
<Text className='user-name'>{parentDisplayName}</Text>
|
||||
<Text className='user-phone'>短信通知:{parentPhoneLabel}</Text>
|
||||
</View>
|
||||
<View className='verified-badge'>
|
||||
<Text>{currentChild ? '已选择当前孩子' : '未选择孩子'}</Text>
|
||||
</View>
|
||||
{parentPhone ? (
|
||||
<View className='verified-badge'>
|
||||
<Text>已开启</Text>
|
||||
</View>
|
||||
) : (
|
||||
<Button
|
||||
className='phone-auth-btn'
|
||||
openType='getPhoneNumber'
|
||||
loading={isAuthorizingPhone}
|
||||
disabled={isAuthorizingPhone}
|
||||
onGetPhoneNumber={handleAuthorizePhone}
|
||||
>
|
||||
开启短信
|
||||
</Button>
|
||||
)}
|
||||
</View>
|
||||
|
||||
<View className='summary-card'>
|
||||
|
||||
Reference in New Issue
Block a user