refactor(mp): 迁移医生临床详情页 — 使用统一组件库 (4/12)

告警详情、报告详情、处方详情、透析详情页:
- ScrollView → PageShell 替代手写 min-height/bg/padding
- .section / .alert-detail-card → ContentCard 替代手写卡片样式
- 删除通用 page 容器和 card 样式,保留业务布局样式
This commit is contained in:
iven
2026-05-16 01:16:16 +08:00
parent 8d41d5a167
commit 5e230ba1b5
8 changed files with 67 additions and 117 deletions

View File

@@ -1,13 +1,6 @@
@import '../../../../styles/variables.scss'; @import '../../../../styles/variables.scss';
@import '../../../../styles/mixins.scss'; @import '../../../../styles/mixins.scss';
.alert-detail-page {
min-height: 100vh;
background: $bg;
padding: 24px;
padding-bottom: 160px;
}
.alert-detail-header { .alert-detail-header {
margin-bottom: 24px; margin-bottom: 24px;
@@ -77,12 +70,6 @@
} }
.alert-detail-card { .alert-detail-card {
background: $card;
border-radius: $r-lg;
padding: 24px;
margin-bottom: 16px;
box-shadow: $shadow-sm;
&__label { &__label {
font-size: var(--tk-font-h2); font-size: var(--tk-font-h2);
color: $tx2; color: $tx2;

View File

@@ -1,5 +1,5 @@
import { useState, useCallback } from 'react'; import { useState, useCallback } from 'react';
import { View, Text, ScrollView, Button } from '@tarojs/components'; import { View, Text, Button } from '@tarojs/components';
import Taro from '@tarojs/taro'; import Taro from '@tarojs/taro';
import { usePageData } from '@/hooks/usePageData'; import { usePageData } from '@/hooks/usePageData';
import { import {
@@ -7,6 +7,8 @@ import {
type Alert, type Alert,
} from '@/services/doctor/alerts'; } from '@/services/doctor/alerts';
import Loading from '@/components/Loading'; import Loading from '@/components/Loading';
import PageShell from '@/components/ui/PageShell';
import ContentCard from '@/components/ui/ContentCard';
import { useElderClass } from '../../../../hooks/useElderClass'; import { useElderClass } from '../../../../hooks/useElderClass';
import './index.scss'; import './index.scss';
@@ -91,9 +93,9 @@ export default function AlertDetail() {
if (loading) return <Loading />; if (loading) return <Loading />;
if (!alert) { if (!alert) {
return ( return (
<View className={`alert-detail-page ${modeClass}`}> <PageShell className={modeClass}>
<Text></Text> <Text></Text>
</View> </PageShell>
); );
} }
@@ -103,7 +105,7 @@ export default function AlertDetail() {
const isAcknowledged = alert.status === 'acknowledged'; const isAcknowledged = alert.status === 'acknowledged';
return ( return (
<ScrollView scrollY className={`alert-detail-page ${modeClass}`}> <PageShell className={modeClass}>
{/* 顶部状态 */} {/* 顶部状态 */}
<View className='alert-detail-header'> <View className='alert-detail-header'>
<View className='alert-detail-header__tags'> <View className='alert-detail-header__tags'>
@@ -120,55 +122,55 @@ export default function AlertDetail() {
</View> </View>
{/* 告警信息 */} {/* 告警信息 */}
<View className='alert-detail-card'> <ContentCard>
<Text className='alert-detail-card__label'></Text> <Text className='alert-detail-card__label'></Text>
<Text className='alert-detail-card__value'>{alert.title}</Text> <Text className='alert-detail-card__value'>{alert.title}</Text>
</View> </ContentCard>
<View className='alert-detail-card'> <ContentCard>
<Text className='alert-detail-card__label'> ID</Text> <Text className='alert-detail-card__label'> ID</Text>
<Text className='alert-detail-card__value alert-detail-card__value--id'> <Text className='alert-detail-card__value alert-detail-card__value--id'>
{alert.patient_id ? `${alert.patient_id.slice(0, 8)}...` : '-'} {alert.patient_id ? `${alert.patient_id.slice(0, 8)}...` : '-'}
</Text> </Text>
</View> </ContentCard>
<View className='alert-detail-card'> <ContentCard>
<Text className='alert-detail-card__label'></Text> <Text className='alert-detail-card__label'></Text>
<Text className='alert-detail-card__value'>{severity.label}</Text> <Text className='alert-detail-card__value'>{severity.label}</Text>
</View> </ContentCard>
{alert.detail && ( {alert.detail && (
<View className='alert-detail-card'> <ContentCard>
<Text className='alert-detail-card__label'></Text> <Text className='alert-detail-card__label'></Text>
<Text className='alert-detail-card__value alert-detail-card__value--detail'> <Text className='alert-detail-card__value alert-detail-card__value--detail'>
{JSON.stringify(alert.detail, null, 2)} {JSON.stringify(alert.detail, null, 2)}
</Text> </Text>
</View> </ContentCard>
)} )}
{alert.acknowledged_by && ( {alert.acknowledged_by && (
<View className='alert-detail-card'> <ContentCard>
<Text className='alert-detail-card__label'></Text> <Text className='alert-detail-card__label'></Text>
<Text className='alert-detail-card__value'>{alert.acknowledged_by}</Text> <Text className='alert-detail-card__value'>{alert.acknowledged_by}</Text>
</View> </ContentCard>
)} )}
{alert.acknowledged_at && ( {alert.acknowledged_at && (
<View className='alert-detail-card'> <ContentCard>
<Text className='alert-detail-card__label'></Text> <Text className='alert-detail-card__label'></Text>
<Text className='alert-detail-card__value'> <Text className='alert-detail-card__value'>
{new Date(alert.acknowledged_at).toLocaleString('zh-CN')} {new Date(alert.acknowledged_at).toLocaleString('zh-CN')}
</Text> </Text>
</View> </ContentCard>
)} )}
{alert.resolved_at && ( {alert.resolved_at && (
<View className='alert-detail-card'> <ContentCard>
<Text className='alert-detail-card__label'></Text> <Text className='alert-detail-card__label'></Text>
<Text className='alert-detail-card__value'> <Text className='alert-detail-card__value'>
{new Date(alert.resolved_at).toLocaleString('zh-CN')} {new Date(alert.resolved_at).toLocaleString('zh-CN')}
</Text> </Text>
</View> </ContentCard>
)} )}
{/* 操作按钮 */} {/* 操作按钮 */}
@@ -203,6 +205,6 @@ export default function AlertDetail() {
)} )}
</View> </View>
)} )}
</ScrollView> </PageShell>
); );
} }

View File

@@ -1,21 +1,6 @@
@import '../../../../styles/variables.scss'; @import '../../../../styles/variables.scss';
@import '../../../../styles/mixins.scss'; @import '../../../../styles/mixins.scss';
.dialysis-detail {
min-height: 100vh;
background: $bg;
padding: 24px;
padding-bottom: 200px;
}
.section {
background: $card;
border-radius: $r;
padding: 24px;
margin-bottom: 16px;
box-shadow: $shadow-sm;
}
.section-title { .section-title {
font-size: var(--tk-font-body-lg); font-size: var(--tk-font-body-lg);
font-weight: bold; font-weight: bold;

View File

@@ -1,5 +1,5 @@
import { useState, useCallback } from 'react'; import { useState, useCallback } from 'react';
import { View, Text, ScrollView } from '@tarojs/components'; import { View, Text } from '@tarojs/components';
import Taro, { useRouter } from '@tarojs/taro'; import Taro, { useRouter } from '@tarojs/taro';
import { usePageData } from '@/hooks/usePageData'; import { usePageData } from '@/hooks/usePageData';
import { import {
@@ -8,6 +8,8 @@ import {
type DialysisRecord, type DialysisRecord,
} from '@/services/doctor/dialysis'; } from '@/services/doctor/dialysis';
import Loading from '@/components/Loading'; import Loading from '@/components/Loading';
import PageShell from '@/components/ui/PageShell';
import ContentCard from '@/components/ui/ContentCard';
import { useElderClass } from '../../../../hooks/useElderClass'; import { useElderClass } from '../../../../hooks/useElderClass';
import { useSafeTimeout } from '@/hooks/useSafeTimeout'; import { useSafeTimeout } from '@/hooks/useSafeTimeout';
import './index.scss'; import './index.scss';
@@ -93,15 +95,15 @@ export default function DialysisDetail() {
}; };
if (loading) return <Loading />; if (loading) return <Loading />;
if (!record) return <View className={`error-text ${modeClass}`}><Text></Text></View>; if (!record) return <PageShell className={modeClass}><View className='error-text'><Text></Text></View></PageShell>;
const canComplete = record.status === 'draft'; const canComplete = record.status === 'draft';
const canReview = record.status === 'completed'; const canReview = record.status === 'completed';
return ( return (
<ScrollView scrollY className={`dialysis-detail ${modeClass}`}> <PageShell className={modeClass}>
{/* 状态头部 */} {/* 状态头部 */}
<View className='section'> <ContentCard>
<View className='record-header'> <View className='record-header'>
<Text className='record-header__title'>{record.dialysis_date}</Text> <Text className='record-header__title'>{record.dialysis_date}</Text>
<Text className={`record-header__status record-header__status--${record.status}`}> <Text className={`record-header__status record-header__status--${record.status}`}>
@@ -112,10 +114,10 @@ export default function DialysisDetail() {
{(record.dialysis_type === 'HD' ? '血液透析' : record.dialysis_type === 'HDF' ? '血液透析滤过' : record.dialysis_type === 'HF' ? '血液滤过' : record.dialysis_type)} {(record.dialysis_type === 'HD' ? '血液透析' : record.dialysis_type === 'HDF' ? '血液透析滤过' : record.dialysis_type === 'HF' ? '血液滤过' : record.dialysis_type)}
</Text> </Text>
{record.reviewed_at && <Text className='review-info'> {record.reviewed_at}</Text>} {record.reviewed_at && <Text className='review-info'> {record.reviewed_at}</Text>}
</View> </ContentCard>
{/* 基本信息 */} {/* 基本信息 */}
<View className='section'> <ContentCard>
<Text className='section-title'></Text> <Text className='section-title'></Text>
<Row label='透析日期' value={record.dialysis_date} /> <Row label='透析日期' value={record.dialysis_date} />
<Row label='开始时间' value={record.start_time} /> <Row label='开始时间' value={record.start_time} />
@@ -123,10 +125,10 @@ export default function DialysisDetail() {
<Row label='透析时长' value={record.dialysis_duration} unit=' 分钟' /> <Row label='透析时长' value={record.dialysis_duration} unit=' 分钟' />
<Row label='血流速' value={record.blood_flow_rate} unit=' ml/min' /> <Row label='血流速' value={record.blood_flow_rate} unit=' ml/min' />
<Row label='超滤量' value={record.ultrafiltration_volume} unit=' ml' /> <Row label='超滤量' value={record.ultrafiltration_volume} unit=' ml' />
</View> </ContentCard>
{/* 体重与血压 */} {/* 体重与血压 */}
<View className='section'> <ContentCard>
<Text className='section-title'></Text> <Text className='section-title'></Text>
<Row label='干体重' value={record.dry_weight} unit=' kg' /> <Row label='干体重' value={record.dry_weight} unit=' kg' />
<Row label='透前体重' value={record.pre_weight} unit=' kg' /> <Row label='透前体重' value={record.pre_weight} unit=' kg' />
@@ -139,17 +141,17 @@ export default function DialysisDetail() {
)} )}
<Row label='透前心率' value={record.pre_heart_rate} unit=' bpm' /> <Row label='透前心率' value={record.pre_heart_rate} unit=' bpm' />
<Row label='透后心率' value={record.post_heart_rate} unit=' bpm' /> <Row label='透后心率' value={record.post_heart_rate} unit=' bpm' />
</View> </ContentCard>
{/* 症状与并发症 */} {/* 症状与并发症 */}
{(record.symptoms || record.complication_notes) && ( {(record.symptoms || record.complication_notes) && (
<View className='section'> <ContentCard>
<Text className='section-title'></Text> <Text className='section-title'></Text>
{record.symptoms && ( {record.symptoms && (
<Row label='症状' value={JSON.stringify(record.symptoms)} /> <Row label='症状' value={JSON.stringify(record.symptoms)} />
)} )}
<Row label='并发症备注' value={record.complication_notes} /> <Row label='并发症备注' value={record.complication_notes} />
</View> </ContentCard>
)} )}
{/* 操作按钮 */} {/* 操作按钮 */}
@@ -175,6 +177,6 @@ export default function DialysisDetail() {
<Text className='action-btn__text'></Text> <Text className='action-btn__text'></Text>
</View> </View>
</View> </View>
</ScrollView> </PageShell>
); );
} }

View File

@@ -1,21 +1,6 @@
@import '../../../../styles/variables.scss'; @import '../../../../styles/variables.scss';
@import '../../../../styles/mixins.scss'; @import '../../../../styles/mixins.scss';
.prescription-detail {
min-height: 100vh;
background: $bg;
padding: 24px;
padding-bottom: 200px;
}
.section {
background: $card;
border-radius: $r;
padding: 24px;
margin-bottom: 16px;
box-shadow: $shadow-sm;
}
.section-title { .section-title {
font-size: var(--tk-font-body-lg); font-size: var(--tk-font-body-lg);
font-weight: bold; font-weight: bold;

View File

@@ -1,5 +1,5 @@
import { useState, useCallback } from 'react'; import { useState, useCallback } from 'react';
import { View, Text, ScrollView } from '@tarojs/components'; import { View, Text } from '@tarojs/components';
import Taro, { useRouter } from '@tarojs/taro'; import Taro, { useRouter } from '@tarojs/taro';
import { usePageData } from '@/hooks/usePageData'; import { usePageData } from '@/hooks/usePageData';
import { import {
@@ -7,6 +7,8 @@ import {
type DialysisPrescription, type DialysisPrescription,
} from '@/services/doctor/dialysis'; } from '@/services/doctor/dialysis';
import Loading from '@/components/Loading'; import Loading from '@/components/Loading';
import PageShell from '@/components/ui/PageShell';
import ContentCard from '@/components/ui/ContentCard';
import { useElderClass } from '../../../../hooks/useElderClass'; import { useElderClass } from '../../../../hooks/useElderClass';
import { useSafeTimeout } from '@/hooks/useSafeTimeout'; import { useSafeTimeout } from '@/hooks/useSafeTimeout';
import './index.scss'; import './index.scss';
@@ -83,12 +85,12 @@ export default function PrescriptionDetail() {
}; };
if (loading) return <Loading />; if (loading) return <Loading />;
if (!rx) return <View className={`error-text ${modeClass}`}><Text></Text></View>; if (!rx) return <PageShell className={modeClass}><View className='error-text'><Text></Text></View></PageShell>;
return ( return (
<ScrollView scrollY className={`prescription-detail ${modeClass}`}> <PageShell className={modeClass}>
{/* 状态头部 */} {/* 状态头部 */}
<View className='section'> <ContentCard>
<View className='rx-header'> <View className='rx-header'>
<Text className='rx-header__title'>{rx.dialyzer_model || '透析处方'}</Text> <Text className='rx-header__title'>{rx.dialyzer_model || '透析处方'}</Text>
<Text className={`rx-header__status rx-header__status--${rx.status}`}> <Text className={`rx-header__status rx-header__status--${rx.status}`}>
@@ -98,10 +100,10 @@ export default function PrescriptionDetail() {
{(rx.effective_from || rx.effective_to) && ( {(rx.effective_from || rx.effective_to) && (
<Text className='rx-sub'>{rx.effective_from || '...'} ~ {rx.effective_to || '...'}</Text> <Text className='rx-sub'>{rx.effective_from || '...'} ~ {rx.effective_to || '...'}</Text>
)} )}
</View> </ContentCard>
{/* 基本参数 */} {/* 基本参数 */}
<View className='section'> <ContentCard>
<Text className='section-title'></Text> <Text className='section-title'></Text>
<Row label='透析器型号' value={rx.dialyzer_model} /> <Row label='透析器型号' value={rx.dialyzer_model} />
<Row label='膜面积' value={rx.membrane_area != null ? `${rx.membrane_area}` : null} unit=' m²' /> <Row label='膜面积' value={rx.membrane_area != null ? `${rx.membrane_area}` : null} unit=' m²' />
@@ -109,47 +111,47 @@ export default function PrescriptionDetail() {
<Row label='透析液流量' value={rx.dialysate_flow_rate} unit=' ml/min' /> <Row label='透析液流量' value={rx.dialysate_flow_rate} unit=' ml/min' />
<Row label='频率' value={rx.frequency_per_week != null ? `${rx.frequency_per_week} 次/周` : null} /> <Row label='频率' value={rx.frequency_per_week != null ? `${rx.frequency_per_week} 次/周` : null} />
<Row label='每次时长' value={rx.duration_minutes} unit=' 分钟' /> <Row label='每次时长' value={rx.duration_minutes} unit=' 分钟' />
</View> </ContentCard>
{/* 透析液配比 */} {/* 透析液配比 */}
<View className='section'> <ContentCard>
<Text className='section-title'></Text> <Text className='section-title'></Text>
<Row label='钾浓度' value={rx.dialysate_potassium} unit=' mmol/L' /> <Row label='钾浓度' value={rx.dialysate_potassium} unit=' mmol/L' />
<Row label='钙浓度' value={rx.dialysate_calcium} unit=' mmol/L' /> <Row label='钙浓度' value={rx.dialysate_calcium} unit=' mmol/L' />
<Row label='碳酸氢盐' value={rx.dialysate_bicarbonate} unit=' mmol/L' /> <Row label='碳酸氢盐' value={rx.dialysate_bicarbonate} unit=' mmol/L' />
</View> </ContentCard>
{/* 抗凝方案 */} {/* 抗凝方案 */}
<View className='section'> <ContentCard>
<Text className='section-title'></Text> <Text className='section-title'></Text>
<Row label='抗凝类型' value={rx.anticoagulation_type} /> <Row label='抗凝类型' value={rx.anticoagulation_type} />
<Row label='抗凝剂量' value={rx.anticoagulation_dose} /> <Row label='抗凝剂量' value={rx.anticoagulation_dose} />
</View> </ContentCard>
{/* 血管通路 */} {/* 血管通路 */}
{(rx.vascular_access_type || rx.vascular_access_location) && ( {(rx.vascular_access_type || rx.vascular_access_location) && (
<View className='section'> <ContentCard>
<Text className='section-title'></Text> <Text className='section-title'></Text>
<Row label='通路类型' value={rx.vascular_access_type} /> <Row label='通路类型' value={rx.vascular_access_type} />
<Row label='通路位置' value={rx.vascular_access_location} /> <Row label='通路位置' value={rx.vascular_access_location} />
</View> </ContentCard>
)} )}
{/* 超滤目标 */} {/* 超滤目标 */}
{(rx.target_ultrafiltration_ml != null || rx.target_dry_weight != null) && ( {(rx.target_ultrafiltration_ml != null || rx.target_dry_weight != null) && (
<View className='section'> <ContentCard>
<Text className='section-title'></Text> <Text className='section-title'></Text>
<Row label='目标超滤量' value={rx.target_ultrafiltration_ml} unit=' ml' /> <Row label='目标超滤量' value={rx.target_ultrafiltration_ml} unit=' ml' />
<Row label='目标干体重' value={rx.target_dry_weight} unit=' kg' /> <Row label='目标干体重' value={rx.target_dry_weight} unit=' kg' />
</View> </ContentCard>
)} )}
{/* 备注 */} {/* 备注 */}
{rx.notes && ( {rx.notes && (
<View className='section'> <ContentCard>
<Text className='section-title'></Text> <Text className='section-title'></Text>
<Text className='notes-text'>{rx.notes}</Text> <Text className='notes-text'>{rx.notes}</Text>
</View> </ContentCard>
)} )}
{/* 操作按钮 */} {/* 操作按钮 */}
@@ -163,6 +165,6 @@ export default function PrescriptionDetail() {
<Text className='action-btn__text'></Text> <Text className='action-btn__text'></Text>
</View> </View>
</View> </View>
</ScrollView> </PageShell>
); );
} }

View File

@@ -1,21 +1,6 @@
@import '../../../../styles/variables.scss'; @import '../../../../styles/variables.scss';
@import '../../../../styles/mixins.scss'; @import '../../../../styles/mixins.scss';
.report-detail {
min-height: 100vh;
background: $bg;
padding: 24px;
padding-bottom: 120px;
}
.section {
background: $card;
border-radius: $r-lg;
padding: 28px;
margin-bottom: 20px;
box-shadow: $shadow-sm;
}
.section-title { .section-title {
@include section-title; @include section-title;
} }

View File

@@ -1,9 +1,11 @@
import { useState, useCallback } from 'react'; import { useState, useCallback } from 'react';
import { View, Text, Textarea, ScrollView } from '@tarojs/components'; import { View, Text, Textarea } from '@tarojs/components';
import Taro, { useRouter } from '@tarojs/taro'; import Taro, { useRouter } from '@tarojs/taro';
import { usePageData } from '@/hooks/usePageData'; import { usePageData } from '@/hooks/usePageData';
import { getLabReport, reviewLabReport, type LabReportDetail } from '@/services/doctor/labReport'; import { getLabReport, reviewLabReport, type LabReportDetail } from '@/services/doctor/labReport';
import Loading from '@/components/Loading'; import Loading from '@/components/Loading';
import PageShell from '@/components/ui/PageShell';
import ContentCard from '@/components/ui/ContentCard';
import { useElderClass } from '../../../../hooks/useElderClass'; import { useElderClass } from '../../../../hooks/useElderClass';
import './index.scss'; import './index.scss';
@@ -53,12 +55,12 @@ export default function ReportDetail() {
const formatDate = (d: string) => new Date(d).toLocaleDateString('zh-CN'); const formatDate = (d: string) => new Date(d).toLocaleDateString('zh-CN');
if (loading) return <Loading />; if (loading) return <Loading />;
if (!report) return <View className={`error-text ${modeClass}`}><Text></Text></View>; if (!report) return <PageShell className={modeClass}><View className='error-text'><Text></Text></View></PageShell>;
return ( return (
<ScrollView scrollY className={`report-detail ${modeClass}`}> <PageShell className={modeClass}>
{/* 基本信息 */} {/* 基本信息 */}
<View className='section'> <ContentCard>
<View className='report-header'> <View className='report-header'>
<Text className='report-header__type'>{report.report_type}</Text> <Text className='report-header__type'>{report.report_type}</Text>
<Text className={`report-header__status report-header__status--${report.status}`}> <Text className={`report-header__status report-header__status--${report.status}`}>
@@ -69,11 +71,11 @@ export default function ReportDetail() {
{report.reviewed_at && ( {report.reviewed_at && (
<Text className='review-info'>: {formatDate(report.reviewed_at)}</Text> <Text className='review-info'>: {formatDate(report.reviewed_at)}</Text>
)} )}
</View> </ContentCard>
{/* 指标列表 */} {/* 指标列表 */}
{report.items && report.items.length > 0 && ( {report.items && report.items.length > 0 && (
<View className='section'> <ContentCard>
<Text className='section-title'></Text> <Text className='section-title'></Text>
<View className='indicator-table'> <View className='indicator-table'>
<View className='indicator-row indicator-row--header'> <View className='indicator-row indicator-row--header'>
@@ -102,11 +104,11 @@ export default function ReportDetail() {
</View> </View>
))} ))}
</View> </View>
</View> </ContentCard>
)} )}
{/* 医生注释 */} {/* 医生注释 */}
<View className='section'> <ContentCard>
<Text className='section-title'></Text> <Text className='section-title'></Text>
{report.status === 'reviewed' && report.doctor_notes ? ( {report.status === 'reviewed' && report.doctor_notes ? (
<View className='notes-display'> <View className='notes-display'>
@@ -129,7 +131,7 @@ export default function ReportDetail() {
</View> </View>
</View> </View>
)} )}
</View> </ContentCard>
</ScrollView> </PageShell>
); );
} }