Compare commits

...

4 Commits

Author SHA1 Message Date
ozh fda1f144fc feat(mine): 游客态菜单展示配置设备入口,与白名单放行保持一致
上一轮 BLE 配网页已入游客白名单,但个人中心游客态菜单仍收起
「配置设备」,入口不可达;现游客态照常展示配置设备与当前版本,
场所管理与完整功能引导登录。
2026-10-12 16:18:42 +08:00
ozh 1a56f49205 chore(device): 设备卡片暂只保留网络配置按钮
参数配置/服务器配置尚未接入,先从卡片隐藏,原键值以注释保留待后续恢复。
2026-10-12 16:15:40 +08:00
ozh 3791865419 feat(auth): BLE 配网页加入游客白名单,配置设备无需登录
个人中心「配置设备」链路为纯本地蓝牙操作(扫描/连接/配网),
不依赖账号体系,游客放行供设备试连体验;对应守卫测试补充断言。
2026-10-12 16:10:17 +08:00
ozh c84ca3f1b7 feat(index): 游客首页改功能展示形态,登录引导降为底部非阻断卡
微信审核驳回整改:原游客态首屏是「登录后查看家人动态」空态墙,
审核认定为未体验功能即要求授权登录。改为家庭圈入口 + 三张功能
介绍卡(健康雷达/跌倒告警/家庭圈)可自由浏览,登录引导收进底部
卡片,用户体验后自主选择登录。
2026-10-12 16:03:31 +08:00
6 changed files with 144 additions and 11 deletions

View File

@ -2,9 +2,11 @@ import type { BleDevice } from '@/ble/hooks/useBle'
// 设备型号 → 启用的按钮 key 列表。 // 设备型号 → 启用的按钮 key 列表。
// 后续如需让其他型号裁剪按钮,只需新增映射条目。 // 后续如需让其他型号裁剪按钮,只需新增映射条目。
// 参数配置/服务器配置尚未接入,先从卡片隐藏(原 ['WIFI', 'PARAMS', 'SERVER'])
export const DEVICE_ACTION_KEYS: Record<string, readonly string[]> = Object.freeze({ export const DEVICE_ACTION_KEYS: Record<string, readonly string[]> = Object.freeze({
EP832: ['WIFI'], EP832: ['WIFI'],
DEFAULT: ['WIFI', 'PARAMS', 'SERVER'] DEFAULT: ['WIFI']
// DEFAULT: ['WIFI', 'PARAMS', 'SERVER']
}) })
// 按钮注册表:label / 忙态文案。 // 按钮注册表:label / 忙态文案。

View File

@ -34,6 +34,99 @@
color: #ffffff; color: #ffffff;
} }
// ===== 游客首页:功能介绍卡 + 底部登录引导(先浏览体验,后自主选择登录) =====
.guestSection {
padding: 0 24px;
}
.guestCard {
margin-bottom: 24px;
padding: 28px;
background: $color-bg-white;
border-radius: $radius-lg;
box-shadow: $shadow-card;
display: flex;
align-items: center;
}
.guestCardIcon {
flex-shrink: 0;
width: 72px;
height: 72px;
border-radius: $radius-md;
background: $color-primary-light-bg;
display: flex;
align-items: center;
justify-content: center;
margin-right: 20px;
}
.guestCardIconText {
font-size: 32px;
}
.guestCardBody {
flex: 1;
min-width: 0;
}
.guestCardTitle {
font-size: 30px;
font-weight: 600;
color: $color-text;
}
.guestCardDesc {
display: block;
margin-top: 8px;
font-size: 24px;
color: $color-text-secondary;
}
// 底部登录引导卡:非阻断,用户可继续浏览其他 tab
.guestLoginCard {
margin: 8px 24px 0;
padding: 32px 28px;
background: $color-bg-white;
border-radius: $radius-lg;
box-shadow: $shadow-card;
display: flex;
align-items: center;
}
.guestLoginTexts {
flex: 1;
min-width: 0;
margin-right: 24px;
}
.guestLoginTitle {
font-size: 30px;
font-weight: 600;
color: $color-text;
}
.guestLoginSub {
display: block;
margin-top: 8px;
font-size: 24px;
line-height: 1.5;
color: $color-text-secondary;
}
.guestLoginCard .guestLoginBtn {
margin-top: 0;
padding: 0 36px;
height: 72px;
flex-shrink: 0;
}
.guestLoginCard .guestLoginBtnText {
font-size: 26px;
font-weight: 600;
color: #ffffff;
}
// ===== 家庭圈入口:渐变横幅卡 ===== // ===== 家庭圈入口:渐变横幅卡 =====
.familyEntry { .familyEntry {
margin: 24px; margin: 24px;

View File

@ -68,12 +68,7 @@ export default function Index() {
{loading ? ( {loading ? (
<View className={styles.stateWrap}><Loading /></View> <View className={styles.stateWrap}><Loading /></View>
) : guest ? ( ) : guest ? (
<View className={styles.stateWrap}> <GuestHome onLogin={() => navigateTo(ROUTES.LOGIN)} />
<Empty text="登录后查看家人动态" mode="message" subText="浏览功能无需登录,需要查看告警与设备时再登录" />
<View className={styles.guestLoginBtn} hoverClass={styles.guestLoginBtnPressed} onClick={() => navigateTo(ROUTES.LOGIN)}>
<Text className={styles.guestLoginText}>去登录</Text>
</View>
</View>
) : loadError ? ( ) : loadError ? (
<StateView status="error" onRetry={loadAlerts} /> <StateView status="error" onRetry={loadAlerts} />
) : ( ) : (
@ -115,6 +110,42 @@ function FamilyEntry({ onClick }: { onClick: () => void }) {
) )
} }
// 游客首页:功能介绍可自由浏览,登录引导放底部非阻断(微信审核:先体验后自主选择登录)
function GuestHome({ onLogin }: { onLogin: () => void }) {
const features = [
{ icon: '◉', title: '健康雷达守护', desc: '睡眠呼吸、心率监测,异常实时提醒' },
{ icon: '⚠', title: '跌倒自动告警', desc: '跌倒检测雷达全天值守,紧急联系人速达' },
{ icon: '👪', title: '家庭圈共享', desc: '家人共同守护长辈,告警动态一起看' }
]
return (
<>
<FamilyEntry onClick={onLogin} />
<View className={styles.guestSection}>
{features.map((f) => (
<View key={f.title} className={styles.guestCard}>
<View className={styles.guestCardIcon}>
<Text className={styles.guestCardIconText}>{f.icon}</Text>
</View>
<View className={styles.guestCardBody}>
<Text className={styles.guestCardTitle}>{f.title}</Text>
<Text className={styles.guestCardDesc}>{f.desc}</Text>
</View>
</View>
))}
</View>
<View className={styles.guestLoginCard}>
<View className={styles.guestLoginTexts}>
<Text className={styles.guestLoginTitle}>登录后开启守护</Text>
<Text className={styles.guestLoginSub}>绑定设备、查看告警动态需要登录,可随时在下方自主选择</Text>
</View>
<View className={styles.guestLoginBtn} hoverClass={styles.guestLoginBtnPressed} onClick={onLogin}>
<Text className={styles.guestLoginBtnText}>微信登录</Text>
</View>
</View>
</>
)
}
function UnhandledAlert({ alert, onClick }: { alert: HomeAlarm; onClick: () => void }) { function UnhandledAlert({ alert, onClick }: { alert: HomeAlarm; onClick: () => void }) {
return ( return (
<View className={styles.unhandledCard} onClick={onClick}> <View className={styles.unhandledCard} onClick={onClick}>

View File

@ -47,7 +47,10 @@ export default function Mine() {
<View className={styles.menuCard}> <View className={styles.menuCard}>
{loggedOut ? ( {loggedOut ? (
<ListRowGroup> <ListRowGroup>
{/* 游客态:菜单收起,只留一个登录入口(微信审核要求不强制授权) */} {/* 游客态:配置设备纯蓝牙操作无需登录照常展示;场所管理依赖账号数据,引导登录 */}
<ListRow label="配置设备" arrow onClick={() => navigateTo(ROUTES.DEVICE_BLE_CONFIG)} />
<ListRow label="场所管理" arrow onClick={goLogin} />
<ListRow label="当前版本" value="2.8.44" />
<ListRow label="登录后使用完整功能" arrow onClick={goLogin} /> <ListRow label="登录后使用完整功能" arrow onClick={goLogin} />
</ListRowGroup> </ListRowGroup>
) : ( ) : (

View File

@ -7,14 +7,16 @@ import { ROUTES } from '@/constants/routes'
// 游客可浏览页面(微信审核要求:进入首页即要求授权登录会被拒, // 游客可浏览页面(微信审核要求:进入首页即要求授权登录会被拒,
// tab 页与登录页放开,操作型页面按需登录;协议全文页游客可读—— // tab 页与登录页放开,操作型页面按需登录;协议全文页游客可读——
// 登录前需可查看协议内容才能完成勾选同意) // 登录前需可查看协议内容才能完成勾选同意;
// BLE 配网页纯本地蓝牙操作不依赖账号,游客放行供设备试连体验)
export const PUBLIC_PAGES = new Set<string>([ export const PUBLIC_PAGES = new Set<string>([
ROUTES.LOGIN, ROUTES.LOGIN,
ROUTES.HOME, ROUTES.HOME,
ROUTES.DEVICE_LIST, ROUTES.DEVICE_LIST,
ROUTES.EVENT, ROUTES.EVENT,
ROUTES.MINE, ROUTES.MINE,
ROUTES.AGREEMENT ROUTES.AGREEMENT,
ROUTES.DEVICE_BLE_CONFIG
]) ])
// 登录页路径(navigation 守卫改投目标) // 登录页路径(navigation 守卫改投目标)

View File

@ -88,12 +88,14 @@ describe('navigation 登录守卫', () => {
}) })
describe('isProtectedPage', () => { describe('isProtectedPage', () => {
it('登录页与四个 tab 页返回 false(游客可浏览),其余页面返回 true', () => { it('登录页、四个 tab 页与协议/BLE 配网页返回 false(游客可浏览),其余页面返回 true', () => {
expect(isProtectedPage(ROUTES.LOGIN)).toBe(false) expect(isProtectedPage(ROUTES.LOGIN)).toBe(false)
expect(isProtectedPage(ROUTES.HOME)).toBe(false) expect(isProtectedPage(ROUTES.HOME)).toBe(false)
expect(isProtectedPage(ROUTES.DEVICE_LIST)).toBe(false) expect(isProtectedPage(ROUTES.DEVICE_LIST)).toBe(false)
expect(isProtectedPage(ROUTES.EVENT)).toBe(false) expect(isProtectedPage(ROUTES.EVENT)).toBe(false)
expect(isProtectedPage(ROUTES.MINE)).toBe(false) expect(isProtectedPage(ROUTES.MINE)).toBe(false)
expect(isProtectedPage(ROUTES.AGREEMENT)).toBe(false)
expect(isProtectedPage(ROUTES.DEVICE_BLE_CONFIG)).toBe(false)
expect(isProtectedPage(ROUTES.FAMILY)).toBe(true) expect(isProtectedPage(ROUTES.FAMILY)).toBe(true)
expect(isProtectedPage(`${ROUTES.LOGIN}?from=xxx`)).toBe(false) expect(isProtectedPage(`${ROUTES.LOGIN}?from=xxx`)).toBe(false)
}) })