合入家庭共享与设备消息能力

This commit is contained in:
stu2not
2026-05-26 17:12:51 +08:00
parent 555a2ddc16
commit 050bc17f79
40 changed files with 3072 additions and 165 deletions

View File

@@ -0,0 +1,3 @@
export default definePageConfig({
navigationBarTitleText: '家庭邀请',
})

View File

@@ -0,0 +1,76 @@
.family-invite-page {
min-height: 100%;
background: #F5F7FA;
padding: 96px 24px 48px;
box-sizing: border-box;
}
.invite-panel {
background: #FFFFFF;
border-radius: 20px;
padding: 32px 28px;
box-shadow: 0 2px 12px rgba(0, 0, 0, 0.04);
}
.invite-title {
display: block;
font-size: 38px;
font-weight: 700;
color: #1A1A1A;
margin-bottom: 24px;
}
.invite-desc {
display: block;
font-size: 28px;
line-height: 1.6;
color: #666666;
&.danger {
color: #E5484D;
}
}
.invite-row {
display: flex;
align-items: center;
justify-content: space-between;
gap: 24px;
padding: 22px 0;
border-bottom: 1px solid #F2F2F2;
}
.invite-label {
flex-shrink: 0;
font-size: 28px;
color: #666666;
}
.invite-value {
min-width: 0;
font-size: 29px;
color: #1A1A1A;
font-weight: 600;
text-align: right;
word-break: break-all;
}
.invite-action {
height: 92px;
margin-top: 36px;
border-radius: 18px;
background: #FF8C42;
display: flex;
align-items: center;
justify-content: center;
&.disabled {
background: #CBD5E1;
}
}
.invite-action-text {
font-size: 30px;
color: #FFFFFF;
font-weight: 700;
}

View File

@@ -0,0 +1,103 @@
import { View, Text } from '@tarojs/components'
import Taro, { useDidShow, useRouter } from '@tarojs/taro'
import { useState } from 'react'
import { getToken } from '@/services/auth'
import { acceptFamilyInvitation, FamilyInvitation, getFamilyInvitation } from '@/services/family'
import './index.scss'
export default function FamilyInvite() {
const router = useRouter()
const inviteToken = String(router.params?.token || '').trim()
const redirectUrl = `/pages/family-invite/index?token=${inviteToken}`
const [loading, setLoading] = useState(true)
const [joining, setJoining] = useState(false)
const [invitation, setInvitation] = useState<FamilyInvitation | null>(null)
const [errorText, setErrorText] = useState('')
useDidShow(() => {
void loadInvitation()
})
const loadInvitation = async () => {
if (!getToken()) {
Taro.setStorageSync('postLoginRedirect', redirectUrl)
Taro.reLaunch({ url: '/pages/login/index' })
return
}
if (!inviteToken) {
setErrorText('邀请无效')
setLoading(false)
return
}
setLoading(true)
setErrorText('')
try {
const nextInvitation = await getFamilyInvitation(inviteToken)
setInvitation(nextInvitation)
} catch (error: any) {
setInvitation(null)
setErrorText(error?.message || '邀请加载失败')
} finally {
setLoading(false)
}
}
const handleAccept = async () => {
if (!inviteToken || joining) return
setJoining(true)
try {
await acceptFamilyInvitation(inviteToken)
Taro.showToast({ title: '已加入家庭', icon: 'success' })
setTimeout(() => {
Taro.switchTab({ url: '/pages/device/index' })
}, 600)
} catch (error: any) {
Taro.showToast({
title: error?.message || '加入失败',
icon: 'none',
})
} finally {
setJoining(false)
}
}
const targetName = invitation?.child_name || invitation?.device_id || '--'
const statusText = invitation?.status === 1 ? '等待确认加入' : '邀请已失效'
return (
<View className='family-invite-page'>
<View className='invite-panel'>
<Text className='invite-title'></Text>
{loading ? (
<Text className='invite-desc'>...</Text>
) : errorText ? (
<Text className='invite-desc danger'>{errorText}</Text>
) : (
<>
<View className='invite-row'>
<Text className='invite-label'></Text>
<Text className='invite-value'>{targetName}</Text>
</View>
<View className='invite-row'>
<Text className='invite-label'></Text>
<Text className='invite-value'>{invitation?.device_id || '--'}</Text>
</View>
<View className='invite-row'>
<Text className='invite-label'></Text>
<Text className='invite-value'>{statusText}</Text>
</View>
<View
className={`invite-action ${invitation?.status === 1 && !joining ? '' : 'disabled'}`}
onClick={invitation?.status === 1 && !joining ? handleAccept : undefined}
>
<Text className='invite-action-text'>{joining ? '加入中...' : '加入家庭'}</Text>
</View>
</>
)}
</View>
</View>
)
}