feat(miniprogram): 访客模式 + 长辈模式 + MCP 自动化脚本
访客模式: - 未登录用户可见首页(轮播图+健康资讯+登录引导)和"我的"页面 - 健康和消息 tab 显示 GuestGuard 登录拦截 - 登录页增加"暂不登录,先看看"跳过入口 - 401 拦截器增加 hasToken 检查,避免访客被重定向到登录页 - 退出登录后 reLaunch 到首页而非登录页 长辈模式: - 新增 stores/ui.ts 管理显示模式(标准/长辈) - 长辈模式放大字体 ×1.3、间距 ×1.2、按钮加大 - "我的 → 账号 → 长辈模式"切换页 - 设置持久化到 Storage 修复: - Health/Messages 页面 Hooks 顺序违规(条件 return 在 hooks 之间) 导致访客模式下页面白屏,所有 hooks 移到条件判断之前 工程: - scripts/mpsync.sh/ps1 自动清理残留 DevTools 进程 - project.config.json 默认关闭域名校验
This commit is contained in:
@@ -1,51 +1,96 @@
|
||||
import React from 'react';
|
||||
import { View, Text } from '@tarojs/components';
|
||||
import Taro, { useDidShow } from '@tarojs/taro';
|
||||
import { useAuthStore } from '../../stores/auth';
|
||||
import { usePointsStore } from '../../stores/points';
|
||||
import { useUIStore } from '../../stores/ui';
|
||||
import './index.scss';
|
||||
|
||||
const MENU_ITEMS = [
|
||||
{ label: '就诊人管理', icon: '家', bg: 'pri-l' },
|
||||
{ label: '我的报告', icon: '报', bg: 'acc-l' },
|
||||
{ label: '健康记录', icon: '健', bg: 'pri-l' },
|
||||
{ label: '诊断记录', icon: '诊', bg: 'acc-l' },
|
||||
{ label: '我的随访', icon: '随', bg: 'pri-l' },
|
||||
{ label: '我的预约', icon: '约', bg: 'acc-l' },
|
||||
{ label: '用药记录', icon: '药', bg: 'pri-l' },
|
||||
{ label: '透析记录', icon: '透', bg: 'acc-l' },
|
||||
{ label: '知情同意', icon: '知', bg: 'pri-l' },
|
||||
{ label: '线下活动', icon: '活', bg: 'acc-l' },
|
||||
{ label: '在线咨询', icon: '问', bg: 'pri-l' },
|
||||
{ label: '设置', icon: '设', bg: 'surface-alt' },
|
||||
interface MenuItem {
|
||||
label: string;
|
||||
icon: string;
|
||||
bg: string;
|
||||
color: string;
|
||||
path: string;
|
||||
isSwitchTab?: boolean;
|
||||
}
|
||||
|
||||
interface MenuGroup {
|
||||
title: string;
|
||||
items: MenuItem[];
|
||||
}
|
||||
|
||||
const LOGGED_IN_GROUPS: MenuGroup[] = [
|
||||
{
|
||||
title: '健康管理',
|
||||
items: [
|
||||
{ label: '健康记录', icon: '健', bg: 'pri-l', color: 'pri', path: '/pages/pkg-profile/health-records/index' },
|
||||
{ label: '我的报告', icon: '报', bg: 'acc-l', color: 'acc', path: '/pages/pkg-profile/reports/index' },
|
||||
{ label: 'AI 分析', icon: '智', bg: 'pri-l', color: 'pri', path: '/pages/ai-report/list/index' },
|
||||
{ label: '诊断记录', icon: '诊', bg: 'acc-l', color: 'acc', path: '/pages/pkg-profile/diagnoses/index' },
|
||||
{ label: '用药记录', icon: '药', bg: 'pri-l', color: 'pri', path: '/pages/pkg-profile/medication/index' },
|
||||
],
|
||||
},
|
||||
{
|
||||
title: '就诊服务',
|
||||
items: [
|
||||
{ label: '我的预约', icon: '约', bg: 'pri-l', color: 'pri', path: '/pages/appointment/index' },
|
||||
{ label: '我的随访', icon: '随', bg: 'acc-l', color: 'acc', path: '/pages/pkg-profile/followups/index' },
|
||||
{ label: '在线咨询', icon: '问', bg: 'pri-l', color: 'pri', path: '/pages/consultation/index' },
|
||||
],
|
||||
},
|
||||
{
|
||||
title: '透析管理',
|
||||
items: [
|
||||
{ label: '透析记录', icon: '透', bg: 'pri-l', color: 'pri', path: '/pages/pkg-profile/dialysis-records/index' },
|
||||
{ label: '透析处方', icon: '方', bg: 'acc-l', color: 'acc', path: '/pages/pkg-profile/dialysis-prescriptions/index' },
|
||||
{ label: '知情同意', icon: '知', bg: 'pri-l', color: 'pri', path: '/pages/pkg-profile/consents/index' },
|
||||
],
|
||||
},
|
||||
{
|
||||
title: '生活服务',
|
||||
items: [
|
||||
{ label: '积分商城', icon: '礼', bg: 'pri-l', color: 'pri', path: '/pages/mall/index', isSwitchTab: true },
|
||||
{ label: '线下活动', icon: '活', bg: 'acc-l', color: 'acc', path: '/pages/events/index' },
|
||||
],
|
||||
},
|
||||
{
|
||||
title: '账号',
|
||||
items: [
|
||||
{ label: '就诊人管理', icon: '家', bg: 'pri-l', color: 'pri', path: '/pages/pkg-profile/family/index' },
|
||||
{ label: '长辈模式', icon: '老', bg: 'acc-l', color: 'acc', path: '/pages/pkg-profile/elder-mode/index' },
|
||||
{ label: '设备同步', icon: '设', bg: 'surface-alt', color: 'tx3', path: '/pages/device-sync/index' },
|
||||
{ label: '设置', icon: '齿', bg: 'surface-alt', color: 'tx3', path: '/pages/pkg-profile/settings/index' },
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
const MENU_PATHS: Record<string, string> = {
|
||||
'就诊人管理': '/pages/pkg-profile/family/index',
|
||||
'我的报告': '/pages/pkg-profile/reports/index',
|
||||
'健康记录': '/pages/pkg-profile/health-records/index',
|
||||
'诊断记录': '/pages/pkg-profile/diagnoses/index',
|
||||
'我的随访': '/pages/pkg-profile/followups/index',
|
||||
'我的预约': '/pages/appointment/index',
|
||||
'用药记录': '/pages/pkg-profile/medication/index',
|
||||
'透析记录': '/pages/pkg-profile/dialysis-records/index',
|
||||
'知情同意': '/pages/pkg-profile/consents/index',
|
||||
'线下活动': '/pages/events/index',
|
||||
'在线咨询': '/pages/consultation/index',
|
||||
'设置': '/pages/pkg-profile/settings/index',
|
||||
};
|
||||
const GUEST_GROUPS: MenuGroup[] = [
|
||||
{
|
||||
title: '设置',
|
||||
items: [
|
||||
{ label: '长辈模式', icon: '老', bg: 'acc-l', color: 'acc', path: '/pages/pkg-profile/elder-mode/index' },
|
||||
{ label: '设置', icon: '齿', bg: 'surface-alt', color: 'tx3', path: '/pages/pkg-profile/settings/index' },
|
||||
],
|
||||
},
|
||||
];
|
||||
|
||||
export default function Profile() {
|
||||
const { user, logout } = useAuthStore();
|
||||
const { account: pointsAccount, checkinStatus: checkinInfo, refresh: refreshPoints } = usePointsStore();
|
||||
const mode = useUIStore((s) => s.mode);
|
||||
const modeClass = mode === 'elder' ? 'elder-mode' : '';
|
||||
const isGuest = !user;
|
||||
|
||||
useDidShow(() => {
|
||||
refreshPoints();
|
||||
if (!isGuest) refreshPoints();
|
||||
});
|
||||
|
||||
const handleMenuClick = (label: string) => {
|
||||
const path = MENU_PATHS[label];
|
||||
if (path) Taro.navigateTo({ url: path });
|
||||
const handleMenuClick = (item: MenuItem) => {
|
||||
if (item.isSwitchTab) {
|
||||
Taro.switchTab({ url: item.path });
|
||||
} else {
|
||||
Taro.navigateTo({ url: item.path });
|
||||
}
|
||||
};
|
||||
|
||||
const handleLogout = () => {
|
||||
@@ -59,54 +104,84 @@ export default function Profile() {
|
||||
});
|
||||
};
|
||||
|
||||
const groups = isGuest ? GUEST_GROUPS : LOGGED_IN_GROUPS;
|
||||
|
||||
return (
|
||||
<View className='profile-page'>
|
||||
<View className={`profile-page ${modeClass}`}>
|
||||
{/* 用户信息卡片 */}
|
||||
<View className='profile-user-card'>
|
||||
<View className='profile-avatar'>
|
||||
<Text className='profile-avatar-icon'>
|
||||
{(user?.display_name || '访').charAt(0)}
|
||||
</Text>
|
||||
</View>
|
||||
<View className='profile-user-info'>
|
||||
<Text className='profile-name'>{user?.display_name || '未登录'}</Text>
|
||||
<Text className='profile-phone'>{user?.phone || ''}</Text>
|
||||
</View>
|
||||
</View>
|
||||
|
||||
{/* 积分 + 打卡 — 两个独立卡片并排 */}
|
||||
<View className='profile-stats-row'>
|
||||
<View className='stat-card'>
|
||||
<Text className='stat-value stat-pri'>{(pointsAccount?.balance ?? 0).toLocaleString()}</Text>
|
||||
<Text className='stat-label'>健康积分</Text>
|
||||
</View>
|
||||
<View className='stat-card'>
|
||||
<Text className='stat-value stat-acc'>{checkinInfo?.consecutive_days ?? 0}天</Text>
|
||||
<Text className='stat-label'>连续打卡</Text>
|
||||
</View>
|
||||
</View>
|
||||
|
||||
{/* 菜单 */}
|
||||
<View className='profile-menu'>
|
||||
{MENU_ITEMS.map((item) => (
|
||||
<View
|
||||
className='menu-item'
|
||||
key={item.label}
|
||||
onClick={() => handleMenuClick(item.label)}
|
||||
>
|
||||
<View className={`menu-icon menu-icon--${item.bg}`}>
|
||||
<Text className='menu-icon-text'>{item.icon}</Text>
|
||||
</View>
|
||||
<Text className='menu-label'>{item.label}</Text>
|
||||
<Text className='menu-arrow'>›</Text>
|
||||
{isGuest ? (
|
||||
<View className='profile-user-card' onClick={() => Taro.navigateTo({ url: '/pages/login/index' })}>
|
||||
<View className='profile-avatar profile-avatar--guest'>
|
||||
<Text className='profile-avatar-char'>?</Text>
|
||||
</View>
|
||||
<View className='profile-user-info'>
|
||||
<Text className='profile-name'>未登录</Text>
|
||||
<Text className='profile-phone'>点击登录,开启健康管理之旅</Text>
|
||||
</View>
|
||||
<Text className='profile-arrow'>›</Text>
|
||||
</View>
|
||||
) : (
|
||||
<>
|
||||
<View className='profile-user-card'>
|
||||
<View className='profile-avatar'>
|
||||
<Text className='profile-avatar-char'>{(user?.display_name || '访').charAt(0)}</Text>
|
||||
</View>
|
||||
<View className='profile-user-info'>
|
||||
<Text className='profile-name'>{user?.display_name || '访客'}</Text>
|
||||
<Text className='profile-phone'>
|
||||
{user?.phone ? `${user.phone.slice(0, 3)}****${user.phone.slice(-4)}` : ''}
|
||||
</Text>
|
||||
</View>
|
||||
<Text className='profile-arrow'>›</Text>
|
||||
</View>
|
||||
))}
|
||||
</View>
|
||||
|
||||
{/* 退出登录 */}
|
||||
<View className='profile-logout' onClick={handleLogout}>
|
||||
<Text className='logout-text'>退出登录</Text>
|
||||
</View>
|
||||
{/* 积分 + 打卡 */}
|
||||
<View className='profile-stats-row'>
|
||||
<View className='stat-card'>
|
||||
<Text className='stat-value stat-pri'>{(pointsAccount?.balance ?? 0).toLocaleString()}</Text>
|
||||
<Text className='stat-label'>健康积分</Text>
|
||||
</View>
|
||||
<View className='stat-card'>
|
||||
<Text className='stat-value stat-acc'>{checkinInfo?.consecutive_days ?? 0}<Text className='stat-unit'>天</Text></Text>
|
||||
<Text className='stat-label'>连续打卡</Text>
|
||||
</View>
|
||||
</View>
|
||||
</>
|
||||
)}
|
||||
|
||||
{/* 分组菜单 */}
|
||||
{groups.map((group) => (
|
||||
<View className='menu-group' key={group.title}>
|
||||
<Text className='menu-group-title'>{group.title}</Text>
|
||||
<View className='menu-group-card'>
|
||||
{group.items.map((item, idx) => (
|
||||
<View
|
||||
className='menu-item'
|
||||
key={item.label}
|
||||
onClick={() => handleMenuClick(item)}
|
||||
>
|
||||
<View className={`menu-icon menu-icon--${item.bg}`}>
|
||||
<Text className={`menu-icon-text menu-icon-text--${item.color}`}>{item.icon}</Text>
|
||||
</View>
|
||||
<Text className='menu-label'>{item.label}</Text>
|
||||
{idx < group.items.length - 1 && <View className='menu-divider' />}
|
||||
<Text className='menu-arrow'>›</Text>
|
||||
</View>
|
||||
))}
|
||||
</View>
|
||||
</View>
|
||||
))}
|
||||
|
||||
{/* 退出登录 / 登录 */}
|
||||
{isGuest ? (
|
||||
<View className='profile-logout' onClick={() => Taro.navigateTo({ url: '/pages/login/index' })}>
|
||||
<Text className='logout-text logout-text--pri'>登录账号</Text>
|
||||
</View>
|
||||
) : (
|
||||
<View className='profile-logout' onClick={handleLogout}>
|
||||
<Text className='logout-text'>退出登录</Text>
|
||||
</View>
|
||||
)}
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user