fix(mp): DevTools 卡死 + 主包 2MB→766KB + 代码质量 4 项全通过

根因:主包 2MB 全量组件注入导致 DevTools 渲染引擎内存渐增,
叠加离线时固定 3s 抑制期后的请求洪泛。

修复:
- app.config.ts 添加 lazyCodeLoading: requiredComponents
  主包 2.0MB→766KB,taro.js 526→131KB,vendors.js 230→28KB
- request.ts 离线抑制改为指数退避(3s→6s→12s→30s cap)
  后端不可达时自动延长抑制,防止请求风暴
- SegmentTabs Tab 接口改为 readonly,修复 TS 编译错误
- AbortController polyfill 补齐小程序运行时缺失
- 健康首页/设备同步/健康档案/报告/设置页 UI 重构
- 文章页公开端点适配游客访问
- 健康首页 Swiper 间隔优化 4s→5s,动画 500→300ms
This commit is contained in:
iven
2026-05-24 11:32:40 +08:00
parent 675f8a4b10
commit 1e59007bd5
58 changed files with 4950 additions and 494 deletions

View File

@@ -13,10 +13,10 @@ import { submitSuggestionFeedback } from '../../services/ai-analysis';
import './index.scss';
const QUICK_ENTRIES = [
{ label: '录入体征', icon: '', path: '/pages/pkg-health/input/index' },
{ label: '健康趋势', icon: '线', path: '/pages/pkg-health/trend/index' },
{ label: '我的报告', icon: '', path: '/pages/pkg-profile/reports/index' },
{ label: '用药记录', icon: '', path: '/pages/pkg-profile/medication/index' },
{ label: '录入体征', icon: '✏', color: 'input', path: '/pages/pkg-health/input/index' },
{ label: '健康趋势', icon: '📈', color: 'trend', path: '/pages/pkg-health/trend/index' },
{ label: '我的报告', icon: '📋', color: 'report', path: '/pages/pkg-profile/reports/index' },
{ label: '健康档案', icon: '健', color: 'med', path: '/pages/pkg-profile/health-records/index' },
] as const;
function statusClass(status?: string): string {
@@ -26,6 +26,14 @@ function statusClass(status?: string): string {
return 'vital-ok';
}
function formatDate(): string {
const d = new Date();
const month = d.getMonth() + 1;
const day = d.getDate();
const weekDays = ['日', '一', '二', '三', '四', '五', '六'];
return `${month}${day}日 周${weekDays[d.getDay()]}`;
}
export default function Health() {
const user = useAuthStore((s) => s.user);
const modeClass = useElderClass();
@@ -59,6 +67,7 @@ export default function Health() {
{ label: '血糖', value: summary.blood_sugar ? `${summary.blood_sugar.value}` : '—', unit: 'mmol/L', status: summary.blood_sugar?.status },
{ label: '体重', value: summary.weight ? `${summary.weight.value}` : '—', unit: 'kg', status: summary.weight?.status },
];
const recordedCount = vitals.filter((v) => v.value !== '—').length;
const getThresholdValue = (type: VitalType): number | null => {
if (!thresholds.length) return null;
@@ -82,10 +91,17 @@ export default function Health() {
<PageShell padding="md" safeBottom={false} scroll className={`health-page ${modeClass}`}>
<View className='health-header'>
<Text className='health-title'></Text>
<Text className='health-date'>{formatDate()}</Text>
</View>
{/* 今日体征摘要 */}
<ContentCard variant="elevated" className='vitals-grid'>
{/* 今日体征 hero 卡片 */}
<View className='vitals-grid'>
<View className='vitals-header'>
<Text className='vitals-title'></Text>
{recordedCount > 0 && (
<Text className='vitals-badge'> {recordedCount} </Text>
)}
</View>
{loading ? <Loading /> : (
<View className='vitals-row'>
{vitals.map((v) => (
@@ -97,9 +113,9 @@ export default function Health() {
))}
</View>
)}
</ContentCard>
</View>
{/* 快捷入口 */}
{/* 快捷入口 — 横排 4 格图标 */}
<View className='quick-entries'>
{QUICK_ENTRIES.map((e) => (
<View
@@ -107,7 +123,7 @@ export default function Health() {
className='quick-entry'
onClick={() => safeNavigateTo(e.path)}
>
<View className='quick-icon'>
<View className={`quick-icon quick-icon--${e.color}`}>
<Text className='quick-icon-text'>{e.icon}</Text>
</View>
<Text className='quick-label'>{e.label}</Text>
@@ -115,10 +131,12 @@ export default function Health() {
))}
</View>
{/* 告警提示 */}
{/* 告警横幅 */}
{alertCount > 0 && (
<ContentCard
variant="elevated"
variant="default"
padding="sm"
margin="none"
className='alert-hint'
onPress={() => safeNavigateTo('/pages/pkg-health/alerts/index')}
>