fix(mp): T40 UI 审计修复 — 28 项设计系统合规 + 安全加固 + 讨论记录

T40 UI 审计修复(60 页面全覆盖):
- 新增 $acc-d/$wrn-d 渐变中间色变量,修复首页轮播渐变硬编码
- 替换 8 处裸 white 为 $white 设计变量(5 个 SCSS 文件)
- 修复 7 处触摸目标 40/44px → 48px(健康/消息/咨询/预约/首页)
- 3 页面新增 Loading 状态(体征录入/个人中心/就诊人添加)
- statusTag 移除硬编码布局值,改用 SCSS mixin 控制
- 医生端 14 页面架构 Hook 层补充(useThrottledDidShow 替换 useEffect)
- 移除 action-inbox 未使用 import

安全 P0 修复:
- JWT 中间件加固:token 类型校验 + 过期预检 + 类型别名简化
- 速率限制增强:滑动窗口 + 暴力破解防护
- analytics handler 错误处理完善

文档:
- T40 审计报告(24 PASS / 36 PASS_WITH_ISSUES / 0 NEEDS_WORK)
- 5 份 DevTools/性能审计讨论记录
- wiki 症状导航 + 小程序章节更新
This commit is contained in:
iven
2026-05-14 23:12:54 +08:00
parent 447126b6c5
commit 8f353946e1
90 changed files with 2089 additions and 830 deletions

View File

@@ -1,9 +1,12 @@
import { View, Text } from '@tarojs/components';
import Taro, { useDidShow } from '@tarojs/taro';
import Taro from '@tarojs/taro';
import { useState } from 'react';
import { useAuthStore } from '../../stores/auth';
import { usePointsStore } from '../../stores/points';
import { useUIStore } from '../../stores/ui';
import { navigateToLogin } from '../../utils/navigate';
import { useThrottledDidShow } from '@/hooks/useThrottledDidShow';
import Loading from '../../components/Loading';
import './index.scss';
interface MenuItem {
@@ -76,16 +79,23 @@ const GUEST_GROUPS: MenuGroup[] = [
];
export default function Profile() {
const { user, logout } = useAuthStore();
const { account: pointsAccount, checkinStatus: checkinInfo, refresh: refreshPoints } = usePointsStore();
const user = useAuthStore((s) => s.user);
const logout = useAuthStore((s) => s.logout);
const pointsAccount = usePointsStore((s) => s.account);
const checkinInfo = usePointsStore((s) => s.checkinStatus);
const refreshPoints = usePointsStore((s) => s.refresh);
const mode = useUIStore((s) => s.mode);
const modeClass = mode === 'elder' ? 'elder-mode' : '';
const isGuest = !user;
const groups = isGuest ? GUEST_GROUPS : LOGGED_IN_GROUPS;
const [pointsLoading, setPointsLoading] = useState(false);
useDidShow(() => {
if (!isGuest) refreshPoints();
});
useThrottledDidShow(() => {
if (!isGuest) {
setPointsLoading(true);
refreshPoints().finally(() => setPointsLoading(false));
}
}, 5000);
const handleMenuClick = (item: MenuItem) => {
if (item.isSwitchTab) {
@@ -139,6 +149,9 @@ export default function Profile() {
</View>
{/* 积分 + 打卡 */}
{pointsLoading ? (
<Loading />
) : (
<View className='profile-stats-row'>
<View className='stat-card'>
<Text className='stat-value stat-pri'>{(pointsAccount?.balance ?? 0).toLocaleString()}</Text>
@@ -149,6 +162,7 @@ export default function Profile() {
<Text className='stat-label'></Text>
</View>
</View>
)}
</>
)}