diff --git a/banban-mini/src/app.config.ts b/banban-mini/src/app.config.ts index 7309acc..dce7b8b 100644 --- a/banban-mini/src/app.config.ts +++ b/banban-mini/src/app.config.ts @@ -1,8 +1,8 @@ export default defineAppConfig({ pages: [ + "pages/device/index", "pages/login/index", "pages/bind/index", - "pages/device/index", "pages/notification/index", "pages/chat/index", "pages/chat/detail/index", diff --git a/banban-mini/src/pages/chat/index.scss b/banban-mini/src/pages/chat/index.scss index 663e425..2e71362 100644 --- a/banban-mini/src/pages/chat/index.scss +++ b/banban-mini/src/pages/chat/index.scss @@ -25,6 +25,53 @@ overflow: hidden; } +.guest-card { + margin: 0 24px; + padding: 44px 32px; + border-radius: 20px; + background: #FFFFFF; + box-shadow: 0 2px 12px rgba(0, 0, 0, 0.04); + text-align: center; +} + +.guest-card-icon { + display: block; + font-size: 56px; + line-height: 1; +} + +.guest-card-title { + display: block; + margin-top: 18px; + font-size: 34px; + font-weight: 700; + color: #1A1A1A; +} + +.guest-card-desc { + display: block; + margin-top: 14px; + font-size: 26px; + line-height: 1.6; + color: #666666; +} + +.guest-card-btn { + display: flex; + align-items: center; + justify-content: center; + height: 84px; + margin-top: 30px; + border-radius: 16px; + background: #07C160; +} + +.guest-card-btn-text { + font-size: 30px; + font-weight: 700; + color: #FFFFFF; +} + .conversation-item { background: #FFFFFF; border-radius: 20px; diff --git a/banban-mini/src/pages/chat/index.tsx b/banban-mini/src/pages/chat/index.tsx index 3e08ae7..5fdcfaf 100644 --- a/banban-mini/src/pages/chat/index.tsx +++ b/banban-mini/src/pages/chat/index.tsx @@ -10,6 +10,7 @@ export default function Chat() { const systemBanner = useSystemBanner() const [conversations, setConversations] = useState([]) const [loading, setLoading] = useState(true) + const [isGuest, setIsGuest] = useState(false) useDidShow(() => { void loadConversations() @@ -17,10 +18,13 @@ export default function Chat() { const loadConversations = async () => { if (!getToken()) { - Taro.reLaunch({ url: '/pages/login/index' }) + setConversations([]) + setIsGuest(true) + setLoading(false) return } + setIsGuest(false) setLoading(true) try { const data = await getConversations() @@ -55,13 +59,27 @@ export default function Chat() { }) } + const handleGuestLogin = () => { + Taro.setStorageSync('postLoginRedirect', '/pages/chat/index') + Taro.navigateTo({ url: '/pages/login/index' }) + } + return ( 玩伴 - {loading ? ( + {isGuest ? ( + + 💬 + 亲子互动会话 + 绑定设备后,可以在这里查看孩子、家长和 AI 玩伴之间的消息记录。 + + 登录后查看 + + + ) : loading ? ( 加载中... diff --git a/banban-mini/src/pages/device/index.scss b/banban-mini/src/pages/device/index.scss index 39c6d4d..f392573 100644 --- a/banban-mini/src/pages/device/index.scss +++ b/banban-mini/src/pages/device/index.scss @@ -18,6 +18,119 @@ } } +.guest-hero { + padding: 88px 40px 32px; + background: #FFFFFF; + text-align: center; +} + +.guest-device-icon { + display: flex; + align-items: center; + justify-content: center; + width: 132px; + height: 132px; + margin: 0 auto 24px; + border-radius: 32px; + background: #FEF3E8; + + .robot-img { + width: 72px; + height: 72px; + } +} + +.guest-title { + display: block; + font-size: 44px; + line-height: 1.2; + font-weight: 700; + color: #1A1A1A; +} + +.guest-subtitle { + display: block; + margin-top: 18px; + font-size: 28px; + line-height: 1.6; + color: #666666; +} + +.guest-feature-grid { + display: grid; + grid-template-columns: 1fr 1fr; + gap: 18px; + padding: 24px; +} + +.guest-feature-card { + min-height: 204px; + padding: 26px 22px; + border-radius: 20px; + background: #FFFFFF; + box-shadow: 0 2px 12px rgba(0, 0, 0, 0.04); +} + +.guest-feature-icon { + width: 54px; + height: 54px; + margin-bottom: 18px; +} + +.guest-feature-title { + display: block; + font-size: 28px; + font-weight: 700; + color: #1A1A1A; +} + +.guest-feature-desc { + display: block; + margin-top: 10px; + font-size: 24px; + line-height: 1.45; + color: #666666; +} + +.guest-action-card { + margin: 0 24px 24px; + padding: 30px 26px; + border-radius: 20px; + background: #FFFFFF; + box-shadow: 0 2px 12px rgba(0, 0, 0, 0.04); +} + +.guest-action-title { + display: block; + font-size: 30px; + font-weight: 700; + color: #1A1A1A; +} + +.guest-action-desc { + display: block; + margin-top: 10px; + font-size: 25px; + line-height: 1.5; + color: #666666; +} + +.guest-primary-btn { + display: flex; + align-items: center; + justify-content: center; + height: 88px; + margin-top: 24px; + border-radius: 16px; + background: #07C160; +} + +.guest-primary-btn-text { + font-size: 30px; + font-weight: 700; + color: #FFFFFF; +} + .status-card { position: relative; overflow: hidden; diff --git a/banban-mini/src/pages/device/index.tsx b/banban-mini/src/pages/device/index.tsx index f69c48e..617cea1 100644 --- a/banban-mini/src/pages/device/index.tsx +++ b/banban-mini/src/pages/device/index.tsx @@ -62,6 +62,11 @@ function formatSleepRange(status: DeviceStatus | null): string { return `${start}-${end}` } +function goLoginWithRedirect(url = '/pages/device/index') { + Taro.setStorageSync('postLoginRedirect', url) + Taro.navigateTo({ url: '/pages/login/index' }) +} + function formatAlarmTime(value?: string | null): string { if (!value) return '--' const date = new Date(value) @@ -129,6 +134,7 @@ export default function Device() { const [isSavingVolume, setIsSavingVolume] = useState(false) const [sleepWakePending, setSleepWakePending] = useState<'on' | 'off' | null>(null) const [volumeValue, setVolumeValue] = useState(0) + const [isGuest, setIsGuest] = useState(false) const volumeCommandInFlightRef = useRef(false) useDidShow(() => { @@ -137,11 +143,18 @@ export default function Device() { const loadDeviceInfo = async () => { if (!getToken()) { - Taro.reLaunch({ url: '/pages/login/index' }) + setBinding(null) + setChild(null) + setDeviceStatus(null) + setDeviceAlarms([]) + setVolumeValue(0) + setIsGuest(true) + setIsLoading(false) return } setIsLoading(true) + setIsGuest(false) try { const context = await loadCurrentChildBindingContext() setChild(context.currentChild) @@ -264,6 +277,52 @@ export default function Device() { ) } + if (isGuest) { + return ( + + + + + + 伴伴儿童陪伴设备 + 查看设备状态、定位轨迹、亲子消息和休眠管理。浏览功能后,可在需要绑定设备时登录。 + + + + + + 设备状态 + 查看电量、信号、版本和告警记录。 + + + + 定位展示 + 绑定后可查看设备当前位置。 + + + + 亲子互动 + 查看孩子和设备的消息会话。 + + + + 设备管理 + 管理孩子资料、绑定设备和休眠设置。 + + + + + 准备绑定或管理设备 + 登录后可创建孩子资料、绑定设备并开启完整功能。 + goLoginWithRedirect('/pages/device/index')}> + 登录后继续 + + + {systemBanner} + + ) + } + if (!child) { return ( diff --git a/banban-mini/src/pages/location/index.scss b/banban-mini/src/pages/location/index.scss index 74d393f..25b902a 100644 --- a/banban-mini/src/pages/location/index.scss +++ b/banban-mini/src/pages/location/index.scss @@ -56,6 +56,10 @@ margin-bottom: 24px; } +.guest-map-section { + height: 42vh; +} + .map { width: 100%; height: 100%; diff --git a/banban-mini/src/pages/location/index.tsx b/banban-mini/src/pages/location/index.tsx index 55385eb..7820f53 100644 --- a/banban-mini/src/pages/location/index.tsx +++ b/banban-mini/src/pages/location/index.tsx @@ -135,6 +135,7 @@ export default function Location() { const [emptyState, setEmptyState] = useState<{ title: string; desc: string; actionText: string; actionUrl: string } | null>( null ) + const [isGuest, setIsGuest] = useState(false) useDidShow(() => { void loadLocation(true) @@ -144,10 +145,18 @@ export default function Location() { const targetMode = nextMode || trajectoryMode if (!getToken()) { - Taro.reLaunch({ url: '/pages/login/index' }) + setDeviceLocation(null) + setTrajectory([]) + setSelectedPoint(null) + setCoordinates(DEFAULT_COORDINATES) + setEmptyState(null) + setLocationNotice(null) + setIsGuest(true) + setLoading(false) return } + setIsGuest(false) if (showToast) { Taro.showLoading({ title: '刷新中...' }) } @@ -372,7 +381,36 @@ export default function Location() { 设备定位 - {emptyState ? ( + {isGuest ? ( + <> + + { + console.error('[location] guest map error:', event.detail) + }} + /> + + + + 设备定位与轨迹 + 绑定设备后,可在这里查看孩子设备的位置、更新时间和定位精度。 + { + Taro.setStorageSync('postLoginRedirect', '/pages/location/index') + Taro.navigateTo({ url: '/pages/login/index' }) + }} + > + 登录后查看 + + + + + ) : emptyState ? ( {emptyState.title} diff --git a/banban-mini/src/pages/login/index.scss b/banban-mini/src/pages/login/index.scss index 1a6f4b4..dc85522 100644 --- a/banban-mini/src/pages/login/index.scss +++ b/banban-mini/src/pages/login/index.scss @@ -72,30 +72,26 @@ } } -.nickname-card { +.login-info-card { background: #FFFFFF; border-radius: 20px; padding: 28px 24px; margin-bottom: 32px; box-shadow: 0 2px 12px rgba(0, 0, 0, 0.04); - .nickname-label { + .login-info-title { display: block; font-size: 28px; color: #1A1A1A; font-weight: 600; - margin-bottom: 18px; } - .nickname-input { - width: 100%; - height: 88px; - box-sizing: border-box; - padding: 0 24px; - border-radius: 16px; - background: #F5F7FA; - font-size: 30px; - color: #1A1A1A; + .login-info-desc { + display: block; + margin-top: 12px; + font-size: 25px; + line-height: 1.5; + color: #666666; } } diff --git a/banban-mini/src/pages/login/index.tsx b/banban-mini/src/pages/login/index.tsx index 3304e95..9223009 100644 --- a/banban-mini/src/pages/login/index.tsx +++ b/banban-mini/src/pages/login/index.tsx @@ -1,4 +1,4 @@ -import { View, Text, Button, Input, Checkbox, CheckboxGroup } from '@tarojs/components' +import { View, Text, Button, Checkbox, CheckboxGroup } from '@tarojs/components' import { useEffect, useState } from 'react' import Taro from '@tarojs/taro' import { getToken, wechatLogin } from '@/services/auth' @@ -6,7 +6,6 @@ import './index.scss' export default function Login() { const [submitting, setSubmitting] = useState(false) - const [nickname, setNickname] = useState('') const [agreementAccepted, setAgreementAccepted] = useState(false) useEffect(() => { @@ -21,12 +20,6 @@ export default function Login() { return } - const cachedUserInfo = (Taro.getStorageSync('userInfo') || {}) as { - nickname?: string - } - if (cachedUserInfo.nickname) { - setNickname(cachedUserInfo.nickname) - } }, []) const handleLogin = async () => { @@ -39,12 +32,6 @@ export default function Login() { return } - const cachedUserInfo = (Taro.getStorageSync('userInfo') || {}) as { - nickname?: string - avatar_url?: string - } - const normalizedNickname = nickname.trim() || cachedUserInfo.nickname?.trim() || '' - setSubmitting(true) Taro.showLoading({ title: '登录中...' }) @@ -56,24 +43,18 @@ export default function Login() { const session = await wechatLogin({ code: loginRes.code, - nickname: normalizedNickname || undefined, - avatar_url: cachedUserInfo.avatar_url, }) const nextUserInfo = { - nickname: session.nickname || normalizedNickname, - avatar_url: cachedUserInfo.avatar_url, + nickname: session.nickname || '', } - if (nextUserInfo.nickname || nextUserInfo.avatar_url) { + if (nextUserInfo.nickname) { Taro.setStorageSync('userInfo', { nickname: nextUserInfo.nickname, - avatar_url: nextUserInfo.avatar_url, }) - setNickname(nextUserInfo.nickname || '') } else { Taro.removeStorageSync('userInfo') - setNickname('') } Taro.showToast({ title: '登录成功', icon: 'success' }) @@ -170,22 +151,15 @@ export default function Login() { - - 家长昵称 - setNickname(event.detail.value)} - /> + + 登录后启用设备管理 + 仅在绑定设备、查看定位和管理孩子资料时需要登录。 diff --git a/banban-mini/src/pages/sleep/index.scss b/banban-mini/src/pages/sleep/index.scss index 4c94b73..e9acbe3 100644 --- a/banban-mini/src/pages/sleep/index.scss +++ b/banban-mini/src/pages/sleep/index.scss @@ -102,6 +102,85 @@ } } +.guest-manage-card { + margin: 0 24px 24px; + padding: 44px 32px; + border-radius: 20px; + background: #FFFFFF; + box-shadow: 0 2px 12px rgba(0, 0, 0, 0.04); + text-align: center; +} + +.guest-manage-icon { + display: block; + font-size: 58px; + line-height: 1; +} + +.guest-manage-title { + display: block; + margin-top: 18px; + font-size: 34px; + font-weight: 700; + color: #1A1A1A; +} + +.guest-manage-desc { + display: block; + margin-top: 14px; + font-size: 26px; + line-height: 1.6; + color: #666666; +} + +.guest-manage-btn { + display: flex; + align-items: center; + justify-content: center; + height: 84px; + margin-top: 30px; + border-radius: 16px; + background: #07C160; +} + +.guest-manage-btn-text { + font-size: 30px; + font-weight: 700; + color: #FFFFFF; +} + +.guest-manage-list { + margin: 0 24px 24px; + border-radius: 20px; + overflow: hidden; + background: #FFFFFF; + box-shadow: 0 2px 12px rgba(0, 0, 0, 0.04); +} + +.guest-manage-item { + padding: 26px 24px; + border-bottom: 1px solid #F0F0F0; + + &:last-child { + border-bottom: none; + } +} + +.guest-manage-item-title { + display: block; + font-size: 30px; + font-weight: 700; + color: #1A1A1A; +} + +.guest-manage-item-desc { + display: block; + margin-top: 8px; + font-size: 25px; + line-height: 1.5; + color: #666666; +} + .menu-card { background: #FFFFFF; border-radius: 20px; diff --git a/banban-mini/src/pages/sleep/index.tsx b/banban-mini/src/pages/sleep/index.tsx index 7f6117f..90aae1f 100644 --- a/banban-mini/src/pages/sleep/index.tsx +++ b/banban-mini/src/pages/sleep/index.tsx @@ -106,6 +106,7 @@ export default function Sleep() { const [familyMemberDisplayName, setFamilyMemberDisplayName] = useState('') const [parentInfo, setParentInfo] = useState(null) const [wechatMpStatus, setWechatMpStatus] = useState(null) + const [isGuest, setIsGuest] = useState(false) const [isLoadingWechatMp, setIsLoadingWechatMp] = useState(false) const [isStartingWechatMpBind, setIsStartingWechatMpBind] = useState(false) const [isAuthorizingPhone, setIsAuthorizingPhone] = useState(false) @@ -135,10 +136,23 @@ export default function Sleep() { const loadData = async () => { if (!getToken()) { - Taro.reLaunch({ url: '/pages/login/index' }) + setChildren([]) + setBindings([]) + setBinding(null) + setCurrentChild(null) + setParentInfo(null) + setFirmwareStatus(null) + setDeviceRole(null) + setRoles([]) + setCurrentCardGroup(null) + setFamilyInfo(null) + setWechatMpStatus(null) + setIsGuest(true) + setLoading(false) return } + setIsGuest(false) setLoading(true) try { const context = await loadCurrentChildBindingContext() @@ -798,7 +812,7 @@ export default function Sleep() { clearSelectedBindingDeviceId() clearSelectedChildId() Taro.removeStorageSync('userInfo') - Taro.reLaunch({ url: '/pages/login/index' }) + void loadData() }, }) } @@ -951,6 +965,47 @@ export default function Sleep() { ) } + if (isGuest) { + return ( + + + 设置与管理 + + + + ⚙️ + 管理孩子资料和设备 + 登录后可创建孩子资料、绑定伴伴设备、管理卡片、设置休眠时间和家庭成员。 + { + Taro.setStorageSync('postLoginRedirect', '/pages/sleep/index') + Taro.navigateTo({ url: '/pages/login/index' }) + }} + > + 登录后管理 + + + + + + 孩子资料 + 为不同孩子维护独立设备和会话。 + + + 设备绑定 + 扫码或 NFC 绑定伴伴设备。 + + + 休眠设置 + 设置设备休眠时间和远程控制。 + + + {systemBanner} + + ) + } + const parentDisplayName = parentInfo?.nickname?.trim() || '家长' const parentPhone = String(parentInfo?.phone || '').trim() const parentPhoneLabel = parentPhone ? `${parentPhone.slice(0, 3)}****${parentPhone.slice(-4)}` : '未开启'