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:
@@ -4,6 +4,8 @@ import Taro, { useDidShow, useReachBottom } from '@tarojs/taro';
|
||||
import { listConsultations, ConsultationSession } from '../../services/consultation';
|
||||
import { notificationService } from '../../services/notification';
|
||||
import Loading from '../../components/Loading';
|
||||
import GuestGuard from '../../components/GuestGuard';
|
||||
import { useAuthStore } from '../../stores/auth';
|
||||
import './index.scss';
|
||||
|
||||
type MsgTab = 'consultation' | 'notification';
|
||||
@@ -14,9 +16,19 @@ interface NotificationItem {
|
||||
desc: string;
|
||||
time: string;
|
||||
type: string;
|
||||
read?: boolean;
|
||||
}
|
||||
|
||||
const NOTIFY_ICONS: Record<string, { icon: string; bg: string; color: string }> = {
|
||||
appointment: { icon: '约', bg: '#F0DDD4', color: '#C4623A' },
|
||||
alert: { icon: '警', bg: '#FFF3E0', color: '#C4873A' },
|
||||
followup: { icon: '随', bg: '#E8F0E8', color: '#5B7A5E' },
|
||||
points: { icon: '分', bg: '#F0DDD4', color: '#C4623A' },
|
||||
report: { icon: '报', bg: '#E8F0E8', color: '#5B7A5E' },
|
||||
};
|
||||
|
||||
export default function Messages() {
|
||||
const user = useAuthStore((s) => s.user);
|
||||
const [activeTab, setActiveTab] = useState<MsgTab>('consultation');
|
||||
const [sessions, setSessions] = useState<ConsultationSession[]>([]);
|
||||
const [notifications, setNotifications] = useState<NotificationItem[]>([]);
|
||||
@@ -25,10 +37,6 @@ export default function Messages() {
|
||||
const [total, setTotal] = useState(0);
|
||||
const loadingRef = useRef(false);
|
||||
|
||||
useDidShow(() => {
|
||||
loadData(activeTab, 1, true);
|
||||
});
|
||||
|
||||
const loadData = async (tab: MsgTab, pageNum: number = 1, isRefresh = false) => {
|
||||
if (loadingRef.current) return;
|
||||
loadingRef.current = true;
|
||||
@@ -65,6 +73,10 @@ export default function Messages() {
|
||||
}
|
||||
};
|
||||
|
||||
useDidShow(() => {
|
||||
if (user) loadData(activeTab, 1, true);
|
||||
});
|
||||
|
||||
const handleTabChange = (tab: MsgTab) => {
|
||||
setActiveTab(tab);
|
||||
loadData(tab, 1, true);
|
||||
@@ -89,6 +101,12 @@ export default function Messages() {
|
||||
return dateStr.slice(0, 10);
|
||||
};
|
||||
|
||||
if (!user) {
|
||||
return <GuestGuard title='请先登录' desc='登录后即可查看消息和通知' />;
|
||||
}
|
||||
|
||||
const unreadConsultCount = sessions.filter((s) => s.unread_count_patient > 0).length;
|
||||
|
||||
return (
|
||||
<View className='messages-page'>
|
||||
{/* 页头 */}
|
||||
@@ -96,87 +114,110 @@ export default function Messages() {
|
||||
<Text className='messages-title'>消息</Text>
|
||||
</View>
|
||||
|
||||
{/* Tab 切换 */}
|
||||
<View className='msg-tabs'>
|
||||
{/* 分段控件 Tab */}
|
||||
<View className='msg-segment'>
|
||||
<View
|
||||
className={`msg-tab ${activeTab === 'consultation' ? 'msg-tab-active' : ''}`}
|
||||
className={`msg-segment-tab ${activeTab === 'consultation' ? 'msg-segment-active' : ''}`}
|
||||
onClick={() => handleTabChange('consultation')}
|
||||
>
|
||||
<Text className='msg-tab-text'>咨询</Text>
|
||||
<Text className='msg-segment-text'>咨询</Text>
|
||||
{unreadConsultCount > 0 && (
|
||||
<View className='msg-segment-badge'>
|
||||
<Text className='msg-segment-badge-text'>{unreadConsultCount}</Text>
|
||||
</View>
|
||||
)}
|
||||
</View>
|
||||
<View
|
||||
className={`msg-tab ${activeTab === 'notification' ? 'msg-tab-active' : ''}`}
|
||||
className={`msg-segment-tab ${activeTab === 'notification' ? 'msg-segment-active' : ''}`}
|
||||
onClick={() => handleTabChange('notification')}
|
||||
>
|
||||
<Text className='msg-tab-text'>通知</Text>
|
||||
<Text className='msg-segment-text'>通知</Text>
|
||||
</View>
|
||||
</View>
|
||||
<View className='msg-tab-indicator'>
|
||||
<View className={`msg-tab-bar ${activeTab === 'notification' ? 'msg-tab-bar-right' : ''}`} />
|
||||
</View>
|
||||
|
||||
{/* 咨询列表 */}
|
||||
{activeTab === 'consultation' && (
|
||||
<View className='msg-content'>
|
||||
{loading ? (
|
||||
<View className='msg-content'>
|
||||
{/* 咨询列表 */}
|
||||
{activeTab === 'consultation' && (
|
||||
loading ? (
|
||||
<Loading />
|
||||
) : sessions.length === 0 ? (
|
||||
<View className='msg-empty'>
|
||||
<Text className='msg-empty-text'>暂无咨询消息</Text>
|
||||
</View>
|
||||
) : (
|
||||
sessions.map((session) => (
|
||||
<View
|
||||
key={session.id}
|
||||
className='consult-card'
|
||||
onClick={() => Taro.navigateTo({ url: `/pages/consultation/detail/index?id=${session.id}` })}
|
||||
>
|
||||
<View className='consult-info'>
|
||||
<Text className='consult-doctor'>
|
||||
{session.consultation_type === 'online' ? '在线咨询' : '门诊咨询'}
|
||||
</Text>
|
||||
<Text className='consult-preview'>
|
||||
{session.last_message || session.subject || '暂无消息'}
|
||||
</Text>
|
||||
</View>
|
||||
<View className='consult-meta'>
|
||||
<Text className='consult-time'>{formatTime(session.last_message_at)}</Text>
|
||||
{session.unread_count_patient > 0 && (
|
||||
<View className='consult-badge'>
|
||||
<Text className='consult-badge-text'>
|
||||
{session.unread_count_patient > 99 ? '99+' : session.unread_count_patient}
|
||||
</Text>
|
||||
<View className='msg-list'>
|
||||
{sessions.map((session) => {
|
||||
const doctorName = session.last_message?.slice(0, 1) || '医';
|
||||
const hasUnread = session.unread_count_patient > 0;
|
||||
return (
|
||||
<View
|
||||
key={session.id}
|
||||
className={`consult-card ${hasUnread ? '' : 'consult-card-muted'}`}
|
||||
onClick={() => Taro.navigateTo({ url: `/pages/consultation/detail/index?id=${session.id}` })}
|
||||
>
|
||||
<View className={`consult-avatar ${hasUnread ? 'consult-avatar-active' : ''}`}>
|
||||
<Text className='consult-avatar-char'>{doctorName}</Text>
|
||||
</View>
|
||||
)}
|
||||
</View>
|
||||
</View>
|
||||
))
|
||||
)}
|
||||
</View>
|
||||
)}
|
||||
<View className='consult-body'>
|
||||
<View className='consult-row'>
|
||||
<Text className='consult-doctor'>
|
||||
{session.consultation_type === 'online' ? '在线咨询' : '门诊咨询'}
|
||||
</Text>
|
||||
<Text className='consult-time'>{formatTime(session.last_message_at)}</Text>
|
||||
</View>
|
||||
<View className='consult-row'>
|
||||
<Text className='consult-preview'>
|
||||
{session.last_message || session.subject || '暂无消息'}
|
||||
</Text>
|
||||
{hasUnread && (
|
||||
<View className='consult-badge'>
|
||||
<Text className='consult-badge-text'>
|
||||
{session.unread_count_patient > 99 ? '99+' : session.unread_count_patient}
|
||||
</Text>
|
||||
</View>
|
||||
)}
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
})}
|
||||
</View>
|
||||
)
|
||||
)}
|
||||
|
||||
{/* 通知列表 */}
|
||||
{activeTab === 'notification' && (
|
||||
<View className='msg-content'>
|
||||
{loading ? (
|
||||
{/* 通知列表 */}
|
||||
{activeTab === 'notification' && (
|
||||
loading ? (
|
||||
<Loading />
|
||||
) : notifications.length === 0 ? (
|
||||
<View className='msg-empty'>
|
||||
<Text className='msg-empty-text'>暂无新通知</Text>
|
||||
</View>
|
||||
) : (
|
||||
notifications.map((n) => (
|
||||
<View key={n.id} className='notify-card'>
|
||||
<View className='notify-info'>
|
||||
<Text className='notify-title'>{n.title}</Text>
|
||||
<Text className='notify-desc'>{n.desc}</Text>
|
||||
</View>
|
||||
<Text className='notify-time'>{n.time}</Text>
|
||||
</View>
|
||||
))
|
||||
)}
|
||||
</View>
|
||||
)}
|
||||
<View className='msg-list'>
|
||||
{notifications.map((n) => {
|
||||
const cfg = NOTIFY_ICONS[n.type] || NOTIFY_ICONS.report;
|
||||
const isUnread = !n.read;
|
||||
return (
|
||||
<View key={n.id} className={`notify-card ${isUnread ? '' : 'notify-card-muted'}`}>
|
||||
<View className='notify-icon' style={`background:${cfg.bg};`}>
|
||||
<Text className='notify-icon-char' style={`color:${cfg.color};`}>{cfg.icon}</Text>
|
||||
</View>
|
||||
<View className='notify-body'>
|
||||
<View className='notify-row'>
|
||||
<Text className={`notify-title ${isUnread ? 'notify-title-bold' : ''}`}>{n.title}</Text>
|
||||
<Text className='notify-time'>{n.time}</Text>
|
||||
</View>
|
||||
<Text className='notify-desc'>{n.desc}</Text>
|
||||
</View>
|
||||
{isUnread && <View className='notify-dot' />}
|
||||
</View>
|
||||
);
|
||||
})}
|
||||
</View>
|
||||
)
|
||||
)}
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user