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:
@@ -1,6 +1,7 @@
|
||||
import React, { useState, useCallback, useRef } from 'react';
|
||||
import { View, Text } from '@tarojs/components';
|
||||
import Taro, { useDidShow, usePullDownRefresh } from '@tarojs/taro';
|
||||
import Taro, { usePullDownRefresh } from '@tarojs/taro';
|
||||
import { useThrottledDidShow } from '@/hooks/useThrottledDidShow';
|
||||
import { listPatientAlerts, type Alert } from '@/services/alert';
|
||||
import { useAuthStore } from '@/stores/auth';
|
||||
import Loading from '@/components/Loading';
|
||||
@@ -23,7 +24,7 @@ const STATUS_TABS = [
|
||||
|
||||
export default function PatientAlerts() {
|
||||
const modeClass = useElderClass();
|
||||
const { currentPatient } = useAuthStore();
|
||||
const currentPatient = useAuthStore((s) => s.currentPatient);
|
||||
const [alerts, setAlerts] = useState<Alert[]>([]);
|
||||
const [total, setTotal] = useState(0);
|
||||
const [page, setPage] = useState(1);
|
||||
@@ -60,10 +61,10 @@ export default function PatientAlerts() {
|
||||
[currentPatient],
|
||||
);
|
||||
|
||||
useDidShow(() => {
|
||||
useThrottledDidShow(() => {
|
||||
Taro.setNavigationBarTitle({ title: '健康告警' });
|
||||
fetchAlerts(1, status, true);
|
||||
});
|
||||
}, 10000);
|
||||
|
||||
usePullDownRefresh(() => {
|
||||
fetchAlerts(1, status, true).finally(() => Taro.stopPullDownRefresh());
|
||||
|
||||
@@ -270,7 +270,7 @@
|
||||
|
||||
.dm-submit-text {
|
||||
font-size: var(--tk-font-num);
|
||||
color: white;
|
||||
color: $white;
|
||||
font-weight: bold;
|
||||
letter-spacing: 2px;
|
||||
}
|
||||
|
||||
@@ -60,7 +60,7 @@ const FIELD_LABELS: Record<string, string> = {
|
||||
|
||||
export default function DailyMonitoring() {
|
||||
const modeClass = useElderClass();
|
||||
const { currentPatient } = useAuthStore();
|
||||
const currentPatient = useAuthStore((s) => s.currentPatient);
|
||||
|
||||
const today = formatDate(new Date());
|
||||
const [dateIdx, setDateIdx] = useState(0);
|
||||
|
||||
@@ -226,7 +226,7 @@
|
||||
|
||||
.input-submit-text {
|
||||
font-size: var(--tk-font-num);
|
||||
color: white;
|
||||
color: $white;
|
||||
font-weight: bold;
|
||||
letter-spacing: 2px;
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import { useState } from 'react';
|
||||
import { View, Text, Input, Picker } from '@tarojs/components';
|
||||
import Taro, { useDidShow } from '@tarojs/taro';
|
||||
import Taro from '@tarojs/taro';
|
||||
import { useThrottledDidShow } from '@/hooks/useThrottledDidShow';
|
||||
import { num, validateStr } from '@/utils/validate';
|
||||
import { inputVitalSign, getHealthThresholds, findThreshold, DEFAULT_THRESHOLDS, type HealthThreshold } from '../../../services/health';
|
||||
import { useAuthStore } from '../../../stores/auth';
|
||||
@@ -9,6 +10,7 @@ import { usePointsStore } from '@/stores/points';
|
||||
import { clearRequestCache } from '@/services/request';
|
||||
import { trackEvent } from '@/services/analytics';
|
||||
import { useElderClass } from '../../../hooks/useElderClass';
|
||||
import Loading from '../../../components/Loading';
|
||||
import './index.scss';
|
||||
|
||||
const INDICATORS = [
|
||||
@@ -59,12 +61,14 @@ export default function HealthInput() {
|
||||
const [diastolic, setDiastolic] = useState('');
|
||||
const [note, setNote] = useState('');
|
||||
const [submitting, setSubmitting] = useState(false);
|
||||
const { currentPatient } = useAuthStore();
|
||||
const { clearCache } = useHealthStore();
|
||||
const [loadingThresholds, setLoadingThresholds] = useState(true);
|
||||
const currentPatient = useAuthStore((s) => s.currentPatient);
|
||||
const clearCache = useHealthStore((s) => s.clearCache);
|
||||
|
||||
/** 从 storage 中读取设备同步回传的数据并自动填充表单 */
|
||||
useDidShow(() => {
|
||||
getHealthThresholds().then((t) => { if (t.length > 0) setThresholds(t); });
|
||||
useThrottledDidShow(() => {
|
||||
setLoadingThresholds(true);
|
||||
getHealthThresholds().then((t) => { if (t.length > 0) setThresholds(t); }).finally(() => setLoadingThresholds(false));
|
||||
try {
|
||||
const raw = Taro.getStorageSync('device_sync_result');
|
||||
if (!raw) return;
|
||||
@@ -88,7 +92,7 @@ export default function HealthInput() {
|
||||
} catch {
|
||||
// 解析失败则忽略,不影响正常使用
|
||||
}
|
||||
});
|
||||
}, 10000);
|
||||
|
||||
const handleSubmit = async () => {
|
||||
if (!currentPatient) {
|
||||
@@ -164,6 +168,10 @@ export default function HealthInput() {
|
||||
|
||||
return (
|
||||
<View className={`input-page ${modeClass}`}>
|
||||
{loadingThresholds && <Loading />}
|
||||
|
||||
{!loadingThresholds && (
|
||||
<>
|
||||
{/* 页面标题 */}
|
||||
<View className='input-hero'>
|
||||
<View className='input-hero-icon'>
|
||||
@@ -267,6 +275,8 @@ export default function HealthInput() {
|
||||
>
|
||||
<Text className='input-submit-text'>{submitting ? '提交中...' : '提交录入'}</Text>
|
||||
</View>
|
||||
</>
|
||||
)}
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -65,7 +65,7 @@
|
||||
}
|
||||
|
||||
.trange-tab-text-active {
|
||||
color: white;
|
||||
color: $white;
|
||||
}
|
||||
|
||||
/* ── chart card ── */
|
||||
|
||||
@@ -31,7 +31,7 @@ export default function Trend() {
|
||||
const [range, setRange] = useState('7d');
|
||||
const [points, setPoints] = useState<{ date: string; value: number }[]>([]);
|
||||
const [loading, setLoading] = useState(true);
|
||||
const { getTrend } = useHealthStore();
|
||||
const getTrend = useHealthStore((s) => s.getTrend);
|
||||
|
||||
useEffect(() => {
|
||||
setLoading(true);
|
||||
|
||||
Reference in New Issue
Block a user