refactor(mp): 分包策略优化 — 合并单页分包 + doctor 拆包 + consultation 移出主包

- 合并 4 个单页分包:report→pkg-profile/reports, followup→pkg-profile/followups,
  events→pkg-profile/events, device-sync→pkg-health
- consultation/detail 移出主包到 pkg-consultation 分包(减少主包体积)
- doctor 18 页拆分为 pkg-doctor-core(8页) + pkg-doctor-clinical(10页)
- 全部导航路径和 import 路径同步更新
- 分包 10→8 个,主包页面 13→12
This commit is contained in:
iven
2026-05-15 07:53:00 +08:00
parent 5baa518516
commit 4c38fcd89d
58 changed files with 71 additions and 78 deletions

View File

@@ -5,7 +5,6 @@ export default defineAppConfig({
'pages/health/index',
'pages/messages/index',
'pages/consultation/index',
'pages/consultation/detail/index',
'pages/mall/index',
'pages/profile/index',
'pages/appointment/index',
@@ -17,19 +16,24 @@ export default defineAppConfig({
subPackages: [
{
root: 'pages/pkg-health',
pages: ['trend/index', 'input/index', 'daily-monitoring/index', 'alerts/index'],
pages: ['trend/index', 'input/index', 'daily-monitoring/index', 'alerts/index', 'device-sync/index'],
},
{
root: 'pages/doctor',
root: 'pages/pkg-doctor-core',
pages: [
'index', 'patients/index', 'patients/detail/index',
'consultation/index', 'consultation/detail/index',
'followup/index', 'followup/detail/index',
'report/index', 'report/detail/index',
'alerts/index', 'alerts/detail/index',
'action-inbox/index',
],
},
{
root: 'pages/pkg-doctor-clinical',
pages: [
'dialysis/index', 'dialysis/detail/index', 'dialysis/create/index',
'prescription/index', 'prescription/detail/index', 'prescription/create/index',
'report/index', 'report/detail/index',
'alerts/index', 'alerts/detail/index',
],
},
{
@@ -39,12 +43,12 @@ export default defineAppConfig({
{
root: 'pages/pkg-profile',
pages: [
'family/index', 'family-add/index', 'reports/index',
'followups/index', 'medication/index', 'settings/index',
'family/index', 'family-add/index', 'reports/index', 'reports/detail/index',
'followups/index', 'followups/detail/index', 'medication/index', 'settings/index',
'dialysis-records/index', 'dialysis-records/detail/index',
'dialysis-prescriptions/index', 'dialysis-prescriptions/detail/index',
'consents/index', 'health-records/index', 'diagnoses/index',
'elder-mode/index',
'elder-mode/index', 'events/index',
],
},
{
@@ -56,21 +60,9 @@ export default defineAppConfig({
pages: ['index', 'detail/index'],
},
{
root: 'pages/report',
root: 'pages/pkg-consultation',
pages: ['detail/index'],
},
{
root: 'pages/followup',
pages: ['detail/index'],
},
{
root: 'pages/events',
pages: ['index'],
},
{
root: 'pages/device-sync',
pages: ['index'],
},
],
tabBar: {
color: '#A8A29E',

View File

@@ -22,7 +22,7 @@ export default function DeviceCard({ deviceName, deviceType, lastSyncAt, status
const statusClass = status === 'connected' ? 'connected' : 'idle';
const handleSync = () => {
Taro.navigateTo({ url: '/pages/device-sync/index' });
Taro.navigateTo({ url: '/pages/pkg-health/device-sync/index' });
};
return (

View File

@@ -82,7 +82,7 @@ export default function Consultation() {
});
const handleTapSession = (session: ConsultationSession) => {
Taro.navigateTo({ url: `/pages/consultation/detail/index?id=${session.id}` });
Taro.navigateTo({ url: `/pages/pkg-consultation/detail/index?id=${session.id}` });
};
return (

View File

@@ -251,7 +251,7 @@ function HomeDashboard({ modeClass }: { modeClass: string }) {
className={`reminder-item ${i > 0 ? 'reminder-item-border' : ''}`}
onClick={() => {
if (r.type === 'appointment') Taro.navigateTo({ url: '/pages/appointment/index' });
else if (r.type === 'followup') Taro.navigateTo({ url: `/pages/followup/detail/index?id=${r.id}` });
else if (r.type === 'followup') Taro.navigateTo({ url: `/pages/pkg-profile/followups/detail/index?id=${r.id}` });
}}
>
<Text className='reminder-tag'>{r.tag}</Text>

View File

@@ -21,7 +21,7 @@ export default function Login() {
const navigateAfterLogin = () => {
if (isMedicalStaff()) {
Taro.reLaunch({ url: '/pages/doctor/index' });
Taro.reLaunch({ url: '/pages/pkg-doctor-core/index' });
} else {
Taro.switchTab({ url: '/pages/index/index' });
}

View File

@@ -156,7 +156,7 @@ export default function Messages() {
<View
key={session.id}
className={`consult-card ${hasUnread ? '' : 'consult-card-muted'}`}
onClick={() => Taro.navigateTo({ url: `/pages/consultation/detail/index?id=${session.id}` })}
onClick={() => Taro.navigateTo({ url: `/pages/pkg-consultation/detail/index?id=${session.id}` })}
>
<View className={`consult-avatar ${hasUnread ? 'consult-avatar-active' : ''}`}>
<Text className='consult-avatar-char'>{doctorName}</Text>

View File

@@ -74,7 +74,7 @@ export default function AlertList() {
};
const handleAlertClick = (alert: Alert) => {
safeNavigateTo(`/pages/doctor/alerts/detail/index?id=${alert.id}`);
safeNavigateTo(`/pages/pkg-doctor-clinical/alerts/detail/index?id=${alert.id}`);
};
const formatTime = (dateStr: string) => {

View File

@@ -166,7 +166,7 @@ export default function DialysisDetail() {
)}
{record.status === 'draft' && (
<View className='action-btn action-btn--secondary' onClick={() => Taro.navigateTo({
url: `/pages/doctor/dialysis/create/index?id=${id}&version=${record.version}`,
url: `/pages/pkg-doctor-clinical/dialysis/create/index?id=${id}&version=${record.version}`,
})}>
<Text className='action-btn__text'></Text>
</View>

View File

@@ -127,7 +127,7 @@ export default function DialysisList() {
<View
key={r.id}
className='record-card'
onClick={() => safeNavigateTo(`/pages/doctor/dialysis/detail/index?id=${r.id}`)}
onClick={() => safeNavigateTo(`/pages/pkg-doctor-clinical/dialysis/detail/index?id=${r.id}`)}
>
<View className='record-card__header'>
<Text className={`type-tag type-tag--${(r.dialysis_type || 'hd').toLowerCase()}`}>
@@ -175,7 +175,7 @@ export default function DialysisList() {
Taro.showToast({ title: '请先选择患者', icon: 'none' });
return;
}
safeNavigateTo(`/pages/doctor/dialysis/create/index?patientId=${currentPatientId}`);
safeNavigateTo(`/pages/pkg-doctor-clinical/dialysis/create/index?patientId=${currentPatientId}`);
}}
>
<Text className='fab-text'>+</Text>

View File

@@ -115,7 +115,7 @@ export default function PrescriptionList() {
<View
key={p.id}
className='prescription-card'
onClick={() => safeNavigateTo(`/pages/doctor/prescription/detail/index?id=${p.id}`)}
onClick={() => safeNavigateTo(`/pages/pkg-doctor-clinical/prescription/detail/index?id=${p.id}`)}
>
<View className='prescription-card__header'>
<Text className='prescription-card__model'>{p.dialyzer_model || '透析处方'}</Text>
@@ -165,7 +165,7 @@ export default function PrescriptionList() {
Taro.showToast({ title: '请先选择患者', icon: 'none' });
return;
}
safeNavigateTo(`/pages/doctor/prescription/create/index?patientId=${currentPatientId}`);
safeNavigateTo(`/pages/pkg-doctor-clinical/prescription/create/index?patientId=${currentPatientId}`);
}}
>
<Text className='fab-text'>+</Text>

View File

@@ -95,7 +95,7 @@ export default function ReportList() {
key={r.id}
className='report-card'
onClick={() => Taro.navigateTo({
url: `/pages/doctor/report/detail/index?patientId=${currentPatientId}&id=${r.id}`,
url: `/pages/pkg-doctor-clinical/report/detail/index?patientId=${currentPatientId}&id=${r.id}`,
})}
>
<View className='report-card__header'>

View File

@@ -91,7 +91,7 @@ export default function ConsultationList() {
<View
key={s.id}
className='session-card'
onClick={() => safeNavigateTo(`/pages/doctor/consultation/detail/index?id=${s.id}`)}
onClick={() => safeNavigateTo(`/pages/pkg-doctor-core/consultation/detail/index?id=${s.id}`)}
>
<View className='session-card__top'>
<Text className='session-card__subject'>{s.subject || '在线咨询'}</Text>

View File

@@ -98,7 +98,7 @@ export default function FollowUpList() {
<View
key={task.id}
className='task-card'
onClick={() => Taro.navigateTo({ url: `/pages/doctor/followup/detail/index?id=${task.id}` })}
onClick={() => Taro.navigateTo({ url: `/pages/pkg-doctor-core/followup/detail/index?id=${task.id}` })}
>
<View className='task-card__header'>
<Text className='task-card__type'>{getTypeLabel(task.follow_up_type)}</Text>

View File

@@ -17,15 +17,15 @@ interface CardConfig {
}
const ALL_CARDS: CardConfig[] = [
{ key: 'total_patients', label: '我的患者', initial: '患', route: '/pages/doctor/patients/index' },
{ key: 'unread_messages', label: '未读消息', initial: '消', route: '/pages/doctor/consultation/index' },
{ key: 'pending_follow_ups', label: '待处理随访', initial: '随', route: '/pages/doctor/followup/index', roles: ['doctor', 'nurse', 'health_manager'] },
{ key: 'today_consultations', label: '今日咨询', initial: '诊', route: '/pages/doctor/consultation/index', roles: ['doctor', 'health_manager'] },
{ key: 'total_patients', label: '我的患者', initial: '患', route: '/pages/pkg-doctor-core/patients/index' },
{ key: 'unread_messages', label: '未读消息', initial: '消', route: '/pages/pkg-doctor-core/consultation/index' },
{ key: 'pending_follow_ups', label: '待处理随访', initial: '随', route: '/pages/pkg-doctor-core/followup/index', roles: ['doctor', 'nurse', 'health_manager'] },
{ key: 'today_consultations', label: '今日咨询', initial: '诊', route: '/pages/pkg-doctor-core/consultation/index', roles: ['doctor', 'health_manager'] },
];
const ALL_HEALTH_CARDS: CardConfig[] = [
{ key: 'pending_lab_review', label: '待审化验', initial: '化', route: '/pages/doctor/report/index', roles: ['doctor'] },
{ key: 'today_appointments', label: '今日预约', initial: '约', route: '/pages/doctor/patients/index' },
{ key: 'pending_lab_review', label: '待审化验', initial: '化', route: '/pages/pkg-doctor-clinical/report/index', roles: ['doctor'] },
{ key: 'today_appointments', label: '今日预约', initial: '约', route: '/pages/pkg-doctor-core/patients/index' },
];
interface QuickAction {
@@ -36,13 +36,13 @@ interface QuickAction {
}
const ALL_QUICK_ACTIONS: QuickAction[] = [
{ label: '化验审核', initial: '审', route: '/pages/doctor/report/index', roles: ['doctor'] },
{ label: '患者查询', initial: '查', route: '/pages/doctor/patients/index', roles: ['doctor', 'nurse', 'health_manager'] },
{ label: '随访记录', initial: '随', route: '/pages/doctor/followup/index', roles: ['doctor', 'nurse', 'health_manager'] },
{ label: '告警中心', initial: '警', route: '/pages/doctor/alerts/index', roles: ['doctor', 'nurse', 'health_manager'] },
{ label: '透析管理', initial: '透', route: '/pages/doctor/dialysis/index', roles: ['doctor'] },
{ label: '处方管理', initial: '方', route: '/pages/doctor/prescription/index', roles: ['doctor'] },
{ label: '行动收件箱', initial: '行', route: '/pages/doctor/action-inbox/index', roles: ['doctor', 'nurse', 'health_manager'] },
{ label: '化验审核', initial: '审', route: '/pages/pkg-doctor-clinical/report/index', roles: ['doctor'] },
{ label: '患者查询', initial: '查', route: '/pages/pkg-doctor-core/patients/index', roles: ['doctor', 'nurse', 'health_manager'] },
{ label: '随访记录', initial: '随', route: '/pages/pkg-doctor-core/followup/index', roles: ['doctor', 'nurse', 'health_manager'] },
{ label: '告警中心', initial: '警', route: '/pages/pkg-doctor-clinical/alerts/index', roles: ['doctor', 'nurse', 'health_manager'] },
{ label: '透析管理', initial: '透', route: '/pages/pkg-doctor-clinical/dialysis/index', roles: ['doctor'] },
{ label: '处方管理', initial: '方', route: '/pages/pkg-doctor-clinical/prescription/index', roles: ['doctor'] },
{ label: '行动收件箱', initial: '行', route: '/pages/pkg-doctor-core/action-inbox/index', roles: ['doctor', 'nurse', 'health_manager'] },
];
const ROLE_LABELS: Record<string, string> = {
@@ -122,7 +122,7 @@ export default function DoctorHome() {
<View className='doctor-home__alert'>
<Text className='doctor-home__alert-icon'>!</Text>
<Text className='doctor-home__alert-text'>{alertCount} </Text>
<Text className='doctor-home__alert-link' onClick={() => Taro.navigateTo({ url: '/pages/doctor/alerts/index' })}> </Text>
<Text className='doctor-home__alert-link' onClick={() => Taro.navigateTo({ url: '/pages/pkg-doctor-clinical/alerts/index' })}> </Text>
</View>
)}
@@ -130,7 +130,7 @@ export default function DoctorHome() {
<Input
className='doctor-home__search-input'
placeholder='搜索患者姓名...'
onFocus={() => Taro.navigateTo({ url: '/pages/doctor/patients/index' })}
onFocus={() => Taro.navigateTo({ url: '/pages/pkg-doctor-core/patients/index' })}
/>
</View>

View File

@@ -125,7 +125,7 @@ export default function PatientDetail() {
<Text className='section-title'></Text>
<View
className='lab-item'
onClick={() => Taro.navigateTo({ url: `/pages/doctor/report/detail/index?patientId=${patientId}&id=${summary.latest_lab_report!.id}` })}
onClick={() => Taro.navigateTo({ url: `/pages/pkg-doctor-clinical/report/detail/index?patientId=${patientId}&id=${summary.latest_lab_report!.id}` })}
>
<View className='lab-item__header'>
<Text className='lab-item__type'>{summary.latest_lab_report.report_type}</Text>
@@ -142,10 +142,10 @@ export default function PatientDetail() {
<View className='section'>
<Text className='section-title'></Text>
<View className='action-buttons'>
<View className='action-btn' onClick={() => Taro.navigateTo({ url: `/pages/doctor/report/index?patientId=${patientId}` })}>
<View className='action-btn' onClick={() => Taro.navigateTo({ url: `/pages/pkg-doctor-clinical/report/index?patientId=${patientId}` })}>
<Text></Text>
</View>
<View className='action-btn' onClick={() => Taro.navigateTo({ url: `/pages/doctor/followup/index?patientId=${patientId}` })}>
<View className='action-btn' onClick={() => Taro.navigateTo({ url: `/pages/pkg-doctor-core/followup/index?patientId=${patientId}` })}>
<Text>访</Text>
</View>
</View>

View File

@@ -146,7 +146,7 @@ export default function PatientList() {
<View
key={p.id}
className='patient-card'
onClick={() => Taro.navigateTo({ url: `/pages/doctor/patients/detail/index?id=${p.id}` })}
onClick={() => Taro.navigateTo({ url: `/pages/pkg-doctor-core/patients/detail/index?id=${p.id}` })}
>
<View className='patient-card__header'>
<Text className='patient-card__name'>{p.name}</Text>

View File

@@ -1,5 +1,5 @@
@import '../../styles/variables.scss';
@import '../../styles/mixins.scss';
@import '../../../styles/variables.scss';
@import '../../../styles/mixins.scss';
.device-sync-page {
min-height: 100vh;

View File

@@ -11,7 +11,7 @@ import { DataSyncScheduler } from '@/services/ble/DataSyncScheduler';
import { uploadReadings } from '@/services/device-sync';
import { useAuthStore } from '@/stores/auth';
import type { BLEDevice, NormalizedReading } from '@/services/ble/types';
import { useElderClass } from '../../hooks/useElderClass';
import { useElderClass } from '@/hooks/useElderClass';
import './index.scss';
/** liveReadings 最大保留条数,防止内存无限增长 */

View File

@@ -191,7 +191,7 @@ export default function HealthInput() {
</View>
{/* 从设备同步入口 */}
<View className='input-sync-entry' onClick={() => Taro.navigateTo({ url: '/pages/device-sync/index?returnTo=input' })}>
<View className='input-sync-entry' onClick={() => Taro.navigateTo({ url: '/pages/pkg-health/device-sync/index?returnTo=input' })}>
<Text className='input-sync-entry-text'></Text>
<Text className='input-sync-entry-hint'></Text>
</View>

View File

@@ -1,5 +1,5 @@
@import '../../styles/variables.scss';
@import '../../styles/mixins.scss';
@import '../../../styles/variables.scss';
@import '../../../styles/mixins.scss';
.events-page {
min-height: 100vh;

View File

@@ -4,7 +4,7 @@ import Taro from '@tarojs/taro';
import * as pointsApi from '@/services/points';
import Loading from '@/components/Loading';
import EmptyState from '@/components/EmptyState';
import { useElderClass } from '../../hooks/useElderClass';
import { useElderClass } from '@/hooks/useElderClass';
import { usePageData } from '@/hooks/usePageData';
import './index.scss';

View File

@@ -1,5 +1,5 @@
@import '../../../styles/variables.scss';
@import '../../../styles/mixins.scss';
@import '../../../../styles/variables.scss';
@import '../../../../styles/mixins.scss';
.detail-page {
min-height: 100vh;

View File

@@ -2,13 +2,13 @@ import React, { useState, useCallback } from 'react';
import { View, Text, Textarea } from '@tarojs/components';
import Taro, { useRouter } from '@tarojs/taro';
import { usePageData } from '@/hooks/usePageData';
import { getTaskDetail, submitRecord } from '../../../services/followup';
import type { FollowUpTask } from '../../../services/followup';
import { getTaskDetail, submitRecord } from '@/services/followup';
import type { FollowUpTask } from '@/services/followup';
import { TEMPLATE_IDS } from '@/services/wechat-templates';
import { trackEvent } from '@/services/analytics';
import Loading from '../../../components/Loading';
import ErrorState from '../../../components/ErrorState';
import { useElderClass } from '../../../hooks/useElderClass';
import Loading from '@/components/Loading';
import ErrorState from '@/components/ErrorState';
import { useElderClass } from '@/hooks/useElderClass';
import './index.scss';
export default function FollowUpDetail() {

View File

@@ -40,7 +40,7 @@ export default function MyFollowUps() {
};
const goToDetail = (id: string) => {
Taro.navigateTo({ url: `/pages/followup/detail/index?id=${id}` });
Taro.navigateTo({ url: `/pages/pkg-profile/followups/detail/index?id=${id}` });
};
const getStatusClass = (status: string) => {

View File

@@ -1,5 +1,5 @@
@import '../../../styles/variables.scss';
@import '../../../styles/mixins.scss';
@import '../../../../styles/variables.scss';
@import '../../../../styles/mixins.scss';
.detail-page {
min-height: 100vh;

View File

@@ -2,10 +2,10 @@ import React, { useState, useCallback } from 'react';
import { View, Text } from '@tarojs/components';
import Taro, { useRouter } from '@tarojs/taro';
import { usePageData } from '@/hooks/usePageData';
import { getReportDetail, LabReport } from '../../../services/report';
import Loading from '../../../components/Loading';
import { useElderClass } from '../../../hooks/useElderClass';
import { useAuthStore } from '../../../stores/auth';
import { getReportDetail, LabReport } from '@/services/report';
import Loading from '@/components/Loading';
import { useElderClass } from '@/hooks/useElderClass';
import { useAuthStore } from '@/stores/auth';
import './index.scss';
interface IndicatorItem {

View File

@@ -47,7 +47,7 @@ export default function MyReports() {
});
const goToDetail = (id: string) => {
Taro.navigateTo({ url: `/pages/report/detail/index?id=${id}` });
Taro.navigateTo({ url: `/pages/pkg-profile/reports/detail/index?id=${id}` });
};
const formatStatus = (report: LabReport) => {

View File

@@ -54,7 +54,7 @@ const LOGGED_IN_GROUPS: MenuGroup[] = [
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' },
{ label: '线下活动', icon: '活', bg: 'acc-l', color: 'acc', path: '/pages/pkg-profile/events/index' },
],
},
{
@@ -62,7 +62,7 @@ const LOGGED_IN_GROUPS: MenuGroup[] = [
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-health/device-sync/index' },
{ label: '设置', icon: '齿', bg: 'surface-alt', color: 'tx3', path: '/pages/pkg-profile/settings/index' },
],
},

View File

@@ -907,6 +907,7 @@ node scripts/audit-pages.mjs --role doctor --batch-size 8
| 日期 | 变更 |
|------|------|
| 2026-05-15 | **架构重构 P4分包策略优化**:合并 4 个单页分包report→pkg-profile/reports、followup→pkg-profile/followups、events→pkg-profile/events、device-sync→pkg-healthconsultation/detail 移出主包到 pkg-consultation 分包doctor 18 页拆分为 pkg-doctor-core8 页:工作台+患者+咨询+随访)+ pkg-doctor-clinical10 页:透析+处方+报告+告警);分包 10→8 个,主包页面 13→12 |
| 2026-05-15 | **架构重构 P3长轮询通用化 useLongPolling**:抽取 `useLongPolling` hookgeneration counter + useDidShow/Hide 可见性 + 失败退避 + enabled 守卫);患者端 + 医生端 consultation/detail 接入,删除 ~80 行重复代码;架构建议 #2 全部完成 ✅ |
| 2026-05-15 | **架构重构 P2request.ts 模块级状态收编 + AbortSignal + Analytics 受控**:提取 `ConcurrencyLimiter` 类(并发限制)、`ResponseCache` 类(缓存+去重+patientId 绑定);新增 `resetForTesting()` 测试隔离函数;`api.get/post/put/delete` 支持 `AbortSignal` 请求取消app.tsx Analytics 定时器改为 `useDidShow`/`useDidHide` 控制后台暂停;构建通过 + 测试 74/75 |
| 2026-05-15 | **患者端登录后卡死深度审查3 专家组)**:根因 — 全局并发请求超微信 10 限制排队阻塞;端点可达性验证 33/33 全部存在Tab 切换请求链路分析(最坏 13 并发);修复 HIGH×3doRefresh 状态清理 + 401 跳转登录页 + 全局并发限制 MAX_CONCURRENT=8+ MEDIUM×3长轮询 generation counter + 首页/健康页 loadingRef 防重入 + refreshToday 去重) |