Compare commits

...

8 Commits

Author SHA1 Message Date
iven
c6bffd4019 refactor(mp): 迁移个人中心 12 个页面 — 统一组件库
线下活动、健康档案、报告列表、随访列表、透析记录、
透析处方、诊断列表、用药列表、家庭成员、添加家庭成员、
设置、知情同意共 12 个页面迁移:
- 最外层容器 → PageShell
- SCSS 删除 min-height/background 通用样式
2026-05-16 01:34:05 +08:00
iven
466b6567d1 refactor(mp): 迁移商城+AI报告+预约列表+文章详情页 — 统一组件库
商城订单/积分兑换/积分明细、AI 报告列表、预约列表、
文章详情共 6 个页面迁移:
- 最外层容器 → PageShell
- SCSS 删除 min-height/background 通用样式
2026-05-16 01:33:42 +08:00
iven
37327a4da4 refactor(mp): 迁移医护+健康页面 — 使用 PageShell + ContentCard 统一组件库
行动收件箱、医护工作台、健康趋势、患者告警、体征录入、
日常监测、设备同步共 7 个页面迁移:
- 最外层容器 → PageShell
- 卡片元素 → ContentCard
- SCSS 删除 min-height/background/box-shadow 通用样式
2026-05-16 01:33:24 +08:00
iven
4dd5a1b4d9 refactor(mp): 迁移创建页 — 使用 PageShell + ContentCard 统一组件库
处方创建、透析记录创建、预约创建三个表单页迁移:
- 最外层容器 ScrollView/View → PageShell
- 表单分组 section View → ContentCard
- SCSS 删除 min-height/background/box-shadow 通用样式
2026-05-16 01:33:06 +08:00
iven
900c9babc3 refactor(mp): 迁移预约+AI报告详情页 — 使用统一组件库 (12/12)
预约详情页:
- View.detail-page → PageShell 替代手写 page 容器
- 手写 detail-header → PageHeader 组件
- .status-card / .info-section / .tips-card → ContentCard
- 删除通用 page 容器、header 和 card 样式

AI 报告详情页:
- View.detail-page → PageShell 替代手写 page 容器
- .detail-card / .content-card → ContentCard
- 删除通用 page 容器和 card 样式,保留 RichText 内部样式
2026-05-16 01:17:04 +08:00
iven
61f1061092 refactor(mp): 迁移患者端详情页 — 使用统一组件库 (10/12)
报告详情、随访详情、透析记录详情、透析处方详情页:
- View.detail-page → PageShell 替代手写 page 容器
- .detail-card → ContentCard 替代手写卡片样式
- 删除通用 page 容器和 card 样式,保留业务布局样式
2026-05-16 01:16:49 +08:00
iven
85701ddeb2 refactor(mp): 迁移医生核心详情页 — 使用统一组件库 (6/12)
随访详情、患者详情页:
- ScrollView → PageShell 替代手写 page 容器
- .section → ContentCard 替代手写卡片样式
- 删除通用 page 容器和 card 样式,保留业务布局样式
2026-05-16 01:16:32 +08:00
iven
5e230ba1b5 refactor(mp): 迁移医生临床详情页 — 使用统一组件库 (4/12)
告警详情、报告详情、处方详情、透析详情页:
- ScrollView → PageShell 替代手写 min-height/bg/padding
- .section / .alert-detail-card → ContentCard 替代手写卡片样式
- 删除通用 page 容器和 card 样式,保留业务布局样式
2026-05-16 01:16:16 +08:00
80 changed files with 415 additions and 734 deletions

View File

@@ -1,21 +1,6 @@
@import '../../../styles/variables.scss';
@import '../../../styles/mixins.scss';
.detail-page {
min-height: 100vh;
background: $bg;
padding: 24px;
padding-bottom: 40px;
}
.detail-card {
background: $card;
border-radius: $r;
padding: 28px;
margin-bottom: 20px;
box-shadow: $shadow-sm;
}
.detail-type {
@include section-title;
margin-bottom: 12px;
@@ -31,12 +16,7 @@
color: $tx3;
}
.content-card {
background: $card;
border-radius: $r;
padding: 28px;
box-shadow: $shadow-sm;
.content-card-wrap {
// RichText 内部样式
h1, h2, h3 {
font-weight: bold;

View File

@@ -4,6 +4,8 @@ import Taro, { useRouter } from '@tarojs/taro';
import { usePageData } from '@/hooks/usePageData';
import { getAiAnalysisDetail, type AiAnalysisItem } from '@/services/ai-analysis';
import Loading from '@/components/Loading';
import PageShell from '@/components/ui/PageShell';
import ContentCard from '@/components/ui/ContentCard';
import { sanitizeHtml } from '@/utils/sanitize-html';
import { useElderClass } from '../../../hooks/useElderClass';
import './index.scss';
@@ -54,9 +56,9 @@ export default function AiReportDetail() {
if (!analysis) {
return (
<View className={`detail-page ${modeClass}`}>
<PageShell className={modeClass}>
<Text className='empty-text'></Text>
</View>
</PageShell>
);
}
@@ -68,8 +70,8 @@ export default function AiReportDetail() {
const isAutoAnalysis = (analysis.result_metadata as Record<string, unknown>)?.auto_analysis === true;
return (
<View className={`detail-page ${modeClass}`}>
<View className='detail-card'>
<PageShell className={modeClass}>
<ContentCard>
<Text className='detail-type'>{TYPE_LABELS[analysis.analysis_type] || analysis.analysis_type}</Text>
<View className='detail-meta'>
<Text className='meta-item'>: {analysis.model_used}</Text>
@@ -80,7 +82,7 @@ export default function AiReportDetail() {
<Text className='auto-badge-text'></Text>
</View>
)}
</View>
</ContentCard>
{isTrendAnalysis && (
<View className='trend-tip-card'>
@@ -90,9 +92,9 @@ export default function AiReportDetail() {
</View>
)}
<View className='content-card'>
<ContentCard className='content-card-wrap'>
<RichText className='report-content' nodes={htmlContent} />
</View>
</View>
</ContentCard>
</PageShell>
);
}

View File

@@ -1,12 +1,7 @@
@import '../../../styles/variables.scss';
@import '../../../styles/mixins.scss';
.ai-report-page {
min-height: 100vh;
background: $bg;
padding: 24px;
padding-bottom: 40px;
}
// PageShell 已接管min-height, background, padding
.page-title {
@include section-title;

View File

@@ -6,6 +6,7 @@ import { listAiAnalysis, type AiAnalysisItem } from '@/services/ai-analysis';
import Loading from '@/components/Loading';
import EmptyState from '@/components/EmptyState';
import { useElderClass } from '../../../hooks/useElderClass';
import PageShell from '@/components/ui/PageShell';
import './index.scss';
const TYPE_LABELS: Record<string, string> = {
@@ -61,14 +62,14 @@ export default function AiReportList() {
if (list.length === 0) {
return (
<View className={`ai-report-page ${modeClass}`}>
<PageShell className={modeClass}>
<EmptyState text='暂无 AI 分析报告' />
</View>
</PageShell>
);
}
return (
<View className={`ai-report-page ${modeClass}`}>
<PageShell className={modeClass}>
<View className='page-title'>AI </View>
<ScrollView scrollY className='report-scroll' onScrollToLower={loadMore}>
{list.map((item) => {
@@ -93,6 +94,6 @@ export default function AiReportList() {
{loading && <Loading />}
{!hasMore && list.length > 0 && <Text className='no-more'></Text>}
</ScrollView>
</View>
</PageShell>
);
}

View File

@@ -1,9 +1,9 @@
@import '../../../styles/variables.scss';
@import '../../../styles/mixins.scss';
.create-page {
min-height: 100vh;
background: $bg;
// PageShell 已接管min-height, background
.create-page-shell {
padding-bottom: 160px;
}
@@ -132,11 +132,7 @@
/* 确认卡片 (step 3 医生信息) */
.confirm-card {
background: $card;
border-radius: $r;
padding: 24px 28px;
margin-bottom: 24px;
box-shadow: $shadow-sm;
}
.confirm-row {

View File

@@ -7,6 +7,8 @@ import { TEMPLATE_IDS } from '@/services/wechat-templates';
import { trackEvent } from '@/services/analytics';
import StepIndicator from '../../../components/StepIndicator';
import WeekCalendar from '../../../components/WeekCalendar';
import PageShell from '@/components/ui/PageShell';
import ContentCard from '@/components/ui/ContentCard';
import { useElderClass } from '../../../hooks/useElderClass';
import { useSafeTimeout } from '@/hooks/useSafeTimeout';
import './index.scss';
@@ -153,7 +155,7 @@ export default function AppointmentCreate() {
};
return (
<View className={`create-page ${modeClass}`}>
<PageShell padding="none" safeBottom={false} scroll={false} className={modeClass}>
<StepIndicator
steps={[{ label: '选科室' }, { label: '选医生' }, { label: '选时段' }]}
current={currentStep}
@@ -222,7 +224,7 @@ export default function AppointmentCreate() {
<View className='step-content'>
<Text className='step-title'></Text>
<View className='confirm-card'>
<ContentCard className='confirm-card'>
<View className='confirm-row'>
<View className='confirm-icon-wrap'>
<Text className='confirm-icon-serif'></Text>
@@ -235,7 +237,7 @@ export default function AppointmentCreate() {
<Text className='confirm-dept-text'>{department}</Text>
</View>
</View>
</View>
</ContentCard>
<WeekCalendar
scheduledDates={scheduledDates}
@@ -295,6 +297,6 @@ export default function AppointmentCreate() {
</View>
)}
</View>
</View>
</PageShell>
);
}

View File

@@ -1,55 +1,13 @@
@import '../../../styles/variables.scss';
@import '../../../styles/mixins.scss';
.detail-page {
min-height: 100vh;
background: $bg;
padding-bottom: 160px;
}
/* 顶部导航 */
.detail-header {
display: flex;
align-items: center;
justify-content: space-between;
padding: 32px;
padding-top: 48px;
background: $card;
box-shadow: $shadow-sm;
}
.back-btn {
padding: 8px 0;
}
.back-text {
font-size: var(--tk-font-body-lg);
color: $pri;
font-weight: 500;
}
.header-title {
font-family: 'Georgia', 'Times New Roman', serif;
font-size: var(--tk-font-num-lg);
font-weight: bold;
color: $tx;
}
.header-placeholder {
width: 60px;
}
/* 状态卡片 */
.status-card {
background: $card;
border-radius: $r-lg;
padding: 40px 32px;
margin: 20px 24px 24px;
box-shadow: $shadow-md;
.status-card-wrap {
display: flex;
flex-direction: column;
align-items: center;
gap: 12px;
margin: 20px 24px 24px;
}
.status-tag {
@@ -97,12 +55,8 @@
}
/* 详情信息 */
.info-section {
.info-section-wrap {
margin: 0 24px 24px;
background: $card;
border-radius: $r;
padding: 28px;
box-shadow: $shadow-sm;
}
.section-title {
@@ -171,11 +125,9 @@
}
/* 温馨提示 */
.tips-card {
.tips-card-wrap {
margin: 0 24px 24px;
background: $wrn-l;
border-radius: $r;
padding: 24px 28px;
}
.tips-title {

View File

@@ -5,6 +5,9 @@ import { getAppointment, cancelAppointment } from '../../../services/appointment
import type { Appointment } from '../../../services/appointment';
import Loading from '../../../components/Loading';
import ErrorState from '../../../components/ErrorState';
import PageShell from '@/components/ui/PageShell';
import ContentCard from '@/components/ui/ContentCard';
import PageHeader from '@/components/patterns/PageHeader';
import { useElderClass } from '../../../hooks/useElderClass';
import { useSafeTimeout } from '@/hooks/useSafeTimeout';
import './index.scss';
@@ -65,53 +68,39 @@ export default function AppointmentDetail() {
}
};
const goBack = () => Taro.navigateBack();
if (loading) {
return (
<View className={`detail-page ${modeClass}`}>
<View className='detail-header'>
<View className='back-btn' onClick={goBack}><Text className='back-text'></Text></View>
<Text className='header-title'></Text>
<View className='header-placeholder' />
</View>
<PageShell padding='none' scroll={false} className={modeClass}>
<PageHeader title='预约详情' />
<Loading />
</View>
</PageShell>
);
}
if (error || !appointment) {
return (
<View className={`detail-page ${modeClass}`}>
<View className='detail-header'>
<View className='back-btn' onClick={goBack}><Text className='back-text'></Text></View>
<Text className='header-title'></Text>
<View className='header-placeholder' />
</View>
<PageShell padding='none' scroll={false} className={modeClass}>
<PageHeader title='预约详情' />
<ErrorState text='未找到预约信息' />
</View>
</PageShell>
);
}
return (
<View className={`detail-page ${modeClass}`}>
<View className='detail-header'>
<View className='back-btn' onClick={goBack}><Text className='back-text'></Text></View>
<Text className='header-title'></Text>
<View className='header-placeholder' />
</View>
<PageShell padding='none' scroll={false} className={modeClass}>
<PageHeader title='预约详情' />
{/* 状态卡片 */}
<View className='status-card'>
<ContentCard variant='elevated' className='status-card-wrap'>
<View className={`status-tag ${status.className}`}>
<Text className='status-tag-text'>{status.label}</Text>
</View>
<Text className='status-doctor'>{appointment.doctor_name}</Text>
<Text className='status-dept'>{appointment.department || ''}</Text>
</View>
</ContentCard>
{/* 预约信息 */}
<View className='info-section'>
<ContentCard className='info-section-wrap'>
<Text className='section-title'></Text>
<View className='info-item'>
@@ -145,14 +134,14 @@ export default function AppointmentDetail() {
</View>
<Text className='info-value info-id'>{appointment.id}</Text>
</View>
</View>
</ContentCard>
{/* 温馨提示 */}
{(appointment.status === 'pending' || appointment.status === 'confirmed') && (
<View className='tips-card'>
<ContentCard className='tips-card-wrap'>
<Text className='tips-title'></Text>
<Text className='tips-text'>15</Text>
</View>
</ContentCard>
)}
{/* 取消按钮 */}
@@ -166,6 +155,6 @@ export default function AppointmentDetail() {
</View>
</View>
)}
</View>
</PageShell>
);
}

View File

@@ -1,9 +1,8 @@
@import '../../styles/variables.scss';
@import '../../styles/mixins.scss';
// PageShell 已接管min-height, background, padding
.appointment-page {
min-height: 100vh;
background: $bg;
padding-bottom: 160px;
}

View File

@@ -7,6 +7,7 @@ import type { Appointment } from '../../services/appointment';
import EmptyState from '../../components/EmptyState';
import Loading from '../../components/Loading';
import { useElderClass } from '../../hooks/useElderClass';
import PageShell from '@/components/ui/PageShell';
import './index.scss';
const STATUS_MAP: Record<string, { label: string; className: string }> = {
@@ -80,7 +81,7 @@ export default function AppointmentList() {
};
return (
<View className={`appointment-page ${modeClass}`}>
<PageShell className={modeClass}>
{/* 页面标题 */}
<View className='page-header'>
<Text className='page-title'></Text>
@@ -146,6 +147,6 @@ export default function AppointmentList() {
<View className='fab-btn' onClick={goCreate}>
<Text className='fab-text'></Text>
</View>
</View>
</PageShell>
);
}

View File

@@ -1,11 +1,7 @@
@import '../../../styles/variables.scss';
@import '../../../styles/mixins.scss';
.article-detail-page {
min-height: 100vh;
background: $bg;
padding-bottom: 40px;
}
// PageShell 已接管min-height, background, padding
.article-header {
background: $card;

View File

@@ -7,6 +7,7 @@ import { trackEvent } from '@/services/analytics';
import { sanitizeHtml } from '@/utils/sanitize-html';
import { useElderClass } from '../../../hooks/useElderClass';
import { useAuthStore } from '../../../stores/auth';
import PageShell from '@/components/ui/PageShell';
import './index.scss';
export default function ArticleDetail() {
@@ -44,26 +45,26 @@ export default function ArticleDetail() {
if (loading) {
return (
<View className={`article-detail-page ${modeClass}`}>
<PageShell className={modeClass}>
<View className='loading-state'>
<Text className='loading-text'>...</Text>
</View>
</View>
</PageShell>
);
}
if (!article) {
return (
<View className={`article-detail-page ${modeClass}`}>
<PageShell className={modeClass}>
<View className='empty-state'>
<Text className='empty-text'></Text>
</View>
</View>
</PageShell>
);
}
return (
<View className={`article-detail-page ${modeClass}`}>
<PageShell className={modeClass}>
{/* 文章头部 */}
<View className='article-header'>
<Text className='article-title'>{article.title}</Text>
@@ -93,6 +94,6 @@ export default function ArticleDetail() {
nodes={sanitizeHtml(article.content || '')}
/>
</View>
</View>
</PageShell>
);
}

View File

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

View File

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

View File

@@ -1,19 +1,12 @@
@import '../../../../styles/variables.scss';
@import '../../../../styles/mixins.scss';
.create-page {
min-height: 100vh;
background: $bg;
padding: 24px;
padding-bottom: 200px;
}
// PageShell 已接管min-height, background, padding
// ContentCard 已接管background, border-radius, padding, box-shadow
/* ─── 表单分组间距ContentCard 外层补充) ─── */
.section {
background: $card;
border-radius: $r;
padding: 24px;
margin-bottom: 16px;
box-shadow: $shadow-sm;
}
.section-title {

View File

@@ -1,10 +1,12 @@
import { useState, useEffect } from 'react';
import { View, Text, Input, Textarea, Picker, ScrollView } from '@tarojs/components';
import { View, Text, Input, Textarea, Picker } from '@tarojs/components';
import Taro, { useRouter } from '@tarojs/taro';
import {
getDialysisRecord, updateDialysisRecord, createDialysisRecord,
} from '@/services/doctor/dialysis';
import Loading from '@/components/Loading';
import PageShell from '@/components/ui/PageShell';
import ContentCard from '@/components/ui/ContentCard';
import { useElderClass } from '../../../../hooks/useElderClass';
import { useSafeTimeout } from '@/hooks/useSafeTimeout';
import './index.scss';
@@ -173,8 +175,8 @@ export default function DialysisCreate() {
);
return (
<ScrollView scrollY className={`create-page ${modeClass}`}>
<View className='section'>
<PageShell safeBottom={false} className={modeClass}>
<ContentCard className='section'>
<Text className='section-title'></Text>
<View className='form-row'>
<Text className='form-label'></Text>
@@ -208,16 +210,16 @@ export default function DialysisCreate() {
</View>
<InputField label='透析时长' field='dialysis_duration' placeholder='分钟' type='number' />
<InputField label='血流速' field='blood_flow_rate' placeholder='ml/min' type='number' />
</View>
</ContentCard>
<View className='section'>
<ContentCard className='section'>
<Text className='section-title'></Text>
<InputField label='干体重' field='dry_weight' placeholder='kg' />
<InputField label='透前体重' field='pre_weight' placeholder='kg' />
<InputField label='透后体重' field='post_weight' placeholder='kg' />
</View>
</ContentCard>
<View className='section'>
<ContentCard className='section'>
<Text className='section-title'></Text>
<InputField label='透前收缩压' field='pre_bp_systolic' placeholder='mmHg' type='number' />
<InputField label='透前舒张压' field='pre_bp_diastolic' placeholder='mmHg' type='number' />
@@ -225,9 +227,9 @@ export default function DialysisCreate() {
<InputField label='透后舒张压' field='post_bp_diastolic' placeholder='mmHg' type='number' />
<InputField label='透前心率' field='pre_heart_rate' placeholder='bpm' type='number' />
<InputField label='透后心率' field='post_heart_rate' placeholder='bpm' type='number' />
</View>
</ContentCard>
<View className='section'>
<ContentCard className='section'>
<Text className='section-title'></Text>
<InputField label='超滤量' field='ultrafiltration_volume' placeholder='ml' type='number' />
<View className='form-row form-row--textarea'>
@@ -240,11 +242,11 @@ export default function DialysisCreate() {
maxlength={500}
/>
</View>
</View>
</ContentCard>
<View className={`submit-btn ${submitting ? 'submit-btn--disabled' : ''}`} onClick={handleSubmit}>
<Text className='submit-btn__text'>{submitting ? '提交中...' : isEdit ? '更新记录' : '创建记录'}</Text>
</View>
</ScrollView>
</PageShell>
);
}

View File

@@ -1,21 +1,6 @@
@import '../../../../styles/variables.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 {
font-size: var(--tk-font-body-lg);
font-weight: bold;

View File

@@ -1,5 +1,5 @@
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 { usePageData } from '@/hooks/usePageData';
import {
@@ -8,6 +8,8 @@ import {
type DialysisRecord,
} from '@/services/doctor/dialysis';
import Loading from '@/components/Loading';
import PageShell from '@/components/ui/PageShell';
import ContentCard from '@/components/ui/ContentCard';
import { useElderClass } from '../../../../hooks/useElderClass';
import { useSafeTimeout } from '@/hooks/useSafeTimeout';
import './index.scss';
@@ -93,15 +95,15 @@ export default function DialysisDetail() {
};
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 canReview = record.status === 'completed';
return (
<ScrollView scrollY className={`dialysis-detail ${modeClass}`}>
<PageShell className={modeClass}>
{/* 状态头部 */}
<View className='section'>
<ContentCard>
<View className='record-header'>
<Text className='record-header__title'>{record.dialysis_date}</Text>
<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)}
</Text>
{record.reviewed_at && <Text className='review-info'> {record.reviewed_at}</Text>}
</View>
</ContentCard>
{/* 基本信息 */}
<View className='section'>
<ContentCard>
<Text className='section-title'></Text>
<Row label='透析日期' value={record.dialysis_date} />
<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.blood_flow_rate} unit=' ml/min' />
<Row label='超滤量' value={record.ultrafiltration_volume} unit=' ml' />
</View>
</ContentCard>
{/* 体重与血压 */}
<View className='section'>
<ContentCard>
<Text className='section-title'></Text>
<Row label='干体重' value={record.dry_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.post_heart_rate} unit=' bpm' />
</View>
</ContentCard>
{/* 症状与并发症 */}
{(record.symptoms || record.complication_notes) && (
<View className='section'>
<ContentCard>
<Text className='section-title'></Text>
{record.symptoms && (
<Row label='症状' value={JSON.stringify(record.symptoms)} />
)}
<Row label='并发症备注' value={record.complication_notes} />
</View>
</ContentCard>
)}
{/* 操作按钮 */}
@@ -175,6 +177,6 @@ export default function DialysisDetail() {
<Text className='action-btn__text'></Text>
</View>
</View>
</ScrollView>
</PageShell>
);
}

View File

@@ -1,19 +1,12 @@
@import '../../../../styles/variables.scss';
@import '../../../../styles/mixins.scss';
.create-page {
min-height: 100vh;
background: $bg;
padding: 24px;
padding-bottom: 200px;
}
// PageShell 已接管min-height, background, padding
// ContentCard 已接管background, border-radius, padding, box-shadow
/* ─── 表单分组间距ContentCard 外层补充) ─── */
.section {
background: $card;
border-radius: $r;
padding: 24px;
margin-bottom: 16px;
box-shadow: $shadow-sm;
}
.section-title {

View File

@@ -1,8 +1,10 @@
import { useState } from 'react';
import { View, Text, Input, Textarea, Picker, ScrollView } from '@tarojs/components';
import { View, Text, Input, Textarea, Picker } from '@tarojs/components';
import Taro, { useRouter } from '@tarojs/taro';
import { createDialysisPrescription } from '@/services/doctor/dialysis';
import Loading from '@/components/Loading';
import PageShell from '@/components/ui/PageShell';
import ContentCard from '@/components/ui/ContentCard';
import { useElderClass } from '../../../../hooks/useElderClass';
import { useSafeTimeout } from '@/hooks/useSafeTimeout';
import './index.scss';
@@ -118,31 +120,31 @@ export default function PrescriptionCreate() {
);
return (
<ScrollView scrollY className={`create-page ${modeClass}`}>
<PageShell safeBottom={false} className={modeClass}>
{/* 透析器 */}
<View className='section'>
<ContentCard className='section'>
<Text className='section-title'></Text>
<InputField label='透析器型号' field='dialyzer_model' placeholder='请输入型号' type='text' />
<InputField label='膜面积' field='membrane_area' placeholder='m²' />
</View>
</ContentCard>
{/* 透析液 */}
<View className='section'>
<ContentCard className='section'>
<Text className='section-title'></Text>
<InputField label='钾浓度' field='dialysate_potassium' placeholder='mmol/L' />
<InputField label='钙浓度' field='dialysate_calcium' placeholder='mmol/L' />
<InputField label='碳酸氢盐' field='dialysate_bicarbonate' placeholder='mmol/L' />
</View>
</ContentCard>
{/* 抗凝 */}
<View className='section'>
<ContentCard className='section'>
<Text className='section-title'></Text>
<InputField label='抗凝类型' field='anticoagulation_type' placeholder='请输入' type='text' />
<InputField label='抗凝剂量' field='anticoagulation_dose' placeholder='请输入' type='text' />
</View>
</ContentCard>
{/* 参数 */}
<View className='section'>
<ContentCard className='section'>
<Text className='section-title'></Text>
<InputField label='目标超滤量' field='target_ultrafiltration_ml' placeholder='ml' type='number' />
<InputField label='目标干体重' field='target_dry_weight' placeholder='kg' />
@@ -150,17 +152,17 @@ export default function PrescriptionCreate() {
<InputField label='透析液流量' field='dialysate_flow_rate' placeholder='ml/min' type='number' />
<InputField label='每周频次' field='frequency_per_week' placeholder='次/周' type='number' />
<InputField label='每次时长' field='duration_minutes' placeholder='分钟' type='number' />
</View>
</ContentCard>
{/* 血管通路 */}
<View className='section'>
<ContentCard className='section'>
<Text className='section-title'></Text>
<InputField label='通路类型' field='vascular_access_type' placeholder='请输入' type='text' />
<InputField label='通路位置' field='vascular_access_location' placeholder='请输入' type='text' />
</View>
</ContentCard>
{/* 生效日期 */}
<View className='section'>
<ContentCard className='section'>
<Text className='section-title'></Text>
<View className='form-row'>
<Text className='form-label'></Text>
@@ -178,10 +180,10 @@ export default function PrescriptionCreate() {
</Text>
</Picker>
</View>
</View>
</ContentCard>
{/* 备注 */}
<View className='section'>
<ContentCard className='section'>
<Text className='section-title'></Text>
<Textarea
className='form-textarea'
@@ -190,11 +192,11 @@ export default function PrescriptionCreate() {
onInput={(e) => updateField('notes', e.detail.value)}
maxlength={500}
/>
</View>
</ContentCard>
<View className={`submit-btn ${submitting ? 'submit-btn--disabled' : ''}`} onClick={handleSubmit}>
<Text className='submit-btn__text'>{submitting ? '提交中...' : '创建处方'}</Text>
</View>
</ScrollView>
</PageShell>
);
}

View File

@@ -1,21 +1,6 @@
@import '../../../../styles/variables.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 {
font-size: var(--tk-font-body-lg);
font-weight: bold;

View File

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

View File

@@ -1,21 +1,6 @@
@import '../../../../styles/variables.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 {
@include section-title;
}

View File

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

View File

@@ -1,10 +1,8 @@
@import '../../../styles/variables.scss';
@import '../../../styles/mixins.scss';
@import '../../styles/variables.scss';
@import '../../styles/mixins.scss';
.action-inbox-page {
min-height: 100vh;
background: $bg;
}
// PageShell 已接管min-height, background
// ContentCard 已接管inbox-card 背景/圆角/阴影/触摸反馈
.inbox-list {
height: calc(100vh - 50px);
@@ -12,11 +10,7 @@
}
.inbox-card {
background: $card;
border-radius: $r-sm;
padding: 14px 16px;
margin-bottom: 10px;
box-shadow: $shadow-sm;
.inbox-card-header {
display: flex;

View File

@@ -13,6 +13,8 @@ import Loading from '@/components/Loading';
import ErrorState from '@/components/ErrorState';
import EmptyState from '@/components/EmptyState';
import SegmentTabs from '@/components/SegmentTabs';
import PageShell from '@/components/ui/PageShell';
import ContentCard from '@/components/ui/ContentCard';
import { useElderClass } from '../../../hooks/useElderClass';
import './index.scss';
@@ -119,7 +121,7 @@ export default function ActionInboxPage() {
};
return (
<View className={`action-inbox-page ${modeClass}`}>
<PageShell padding="none" className={modeClass}>
<SegmentTabs tabs={STATUS_TABS} activeKey={activeTab} onChange={handleTabChange} variant="underline" />
{error ? (
@@ -129,10 +131,11 @@ export default function ActionInboxPage() {
) : (
<ScrollView scrollY className="inbox-list">
{items.map((item) => (
<View
<ContentCard
key={item.id}
className="inbox-card"
onClick={() => handleItemClick(item)}
activeFeedback="opacity"
onPress={() => handleItemClick(item)}
>
<View className="inbox-card-header">
<Text
@@ -146,7 +149,7 @@ export default function ActionInboxPage() {
{item.patient_name} ·{' '}
{new Date(item.created_at).toLocaleDateString('zh-CN')}
</Text>
</View>
</ContentCard>
))}
{loading && <Loading />}
{!loading && items.length >= total && total > 0 && (
@@ -208,6 +211,6 @@ export default function ActionInboxPage() {
)}
</View>
)}
</View>
</PageShell>
);
}

View File

@@ -1,21 +1,6 @@
@import '../../../../styles/variables.scss';
@import '../../../../styles/mixins.scss';
.followup-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 {
@include section-title;
}

View File

@@ -1,5 +1,5 @@
import { useState, useCallback } from 'react';
import { View, Text, Textarea, ScrollView, Picker } from '@tarojs/components';
import { View, Text, Textarea, Picker } from '@tarojs/components';
import Taro, { useRouter } from '@tarojs/taro';
import { usePageData } from '@/hooks/usePageData';
import {
@@ -8,6 +8,8 @@ import {
type FollowUpTask, type FollowUpRecord,
} from '@/services/doctor/followup';
import Loading from '@/components/Loading';
import PageShell from '@/components/ui/PageShell';
import ContentCard from '@/components/ui/ContentCard';
import { useElderClass } from '../../../../hooks/useElderClass';
import './index.scss';
@@ -93,13 +95,13 @@ export default function FollowUpDetail() {
const formatDate = (d: string) => new Date(d).toLocaleDateString('zh-CN');
if (loading) return <Loading />;
if (!task) return <View className={`error-text ${modeClass}`}><Text></Text></View>;
if (!task) return <PageShell className={modeClass}><View className='error-text'><Text></Text></View></PageShell>;
const canSubmit = task.status === 'in_progress' || task.status === 'pending' || task.status === 'overdue';
return (
<ScrollView scrollY className={`followup-detail ${modeClass}`}>
<View className='section'>
<PageShell className={modeClass}>
<ContentCard>
<View className='task-header'>
<Text className='task-header__title'>访</Text>
<Text className={`task-header__status task-header__status--${task.status}`}>
@@ -126,11 +128,11 @@ export default function FollowUpDetail() {
<Text className='task-template__text'>{task.content_template}</Text>
</View>
)}
</View>
</ContentCard>
{/* 历史记录 */}
{records.length > 0 && (
<View className='section'>
<ContentCard>
<Text className='section-title'></Text>
{records.map((r) => (
<View key={r.id} className='record-item'>
@@ -140,12 +142,12 @@ export default function FollowUpDetail() {
{r.medical_advice && <Text className='record-item__text'>: {r.medical_advice}</Text>}
</View>
))}
</View>
</ContentCard>
)}
{/* 填写表单 */}
{canSubmit && (
<View className='section'>
<ContentCard>
<Text className='section-title'>访</Text>
{(task.status === 'pending' || task.status === 'overdue') && (
<View className='start-btn' onClick={handleStartTask}>
@@ -193,8 +195,8 @@ export default function FollowUpDetail() {
<View className={`submit-btn ${submitting ? 'submit-btn--disabled' : ''}`} onClick={handleSubmit}>
<Text className='submit-btn__text'>{submitting ? '提交中...' : '提交记录'}</Text>
</View>
</View>
</ContentCard>
)}
</ScrollView>
</PageShell>
);
}

View File

@@ -1,11 +1,9 @@
@import '../../styles/variables.scss';
@import '../../styles/mixins.scss';
// PageShell 已接管min-height, background, padding
.doctor-home {
min-height: 100vh;
background: $bg;
padding: 32px;
padding-bottom: calc(160px + env(safe-area-inset-bottom));
&__header {
margin-bottom: 40px;

View File

@@ -1,11 +1,12 @@
import { useState, useMemo, useCallback } from 'react';
import { View, Text, Input, ScrollView } from '@tarojs/components';
import { View, Text, Input } from '@tarojs/components';
import Taro from '@tarojs/taro';
import { useAuthStore } from '@/stores/auth';
import { useElderClass } from '../../hooks/useElderClass';
import { usePageData } from '@/hooks/usePageData';
import { getDashboard, type DoctorDashboard } from '@/services/doctor/dashboard';
import Loading from '@/components/Loading';
import PageShell from '@/components/ui/PageShell';
import './index.scss';
interface CardConfig {
@@ -107,7 +108,7 @@ export default function DoctorHome() {
if (loading) return <Loading />;
return (
<ScrollView scrollY className={`doctor-home ${modeClass}`}>
<PageShell safeBottom={false} className={`doctor-home ${modeClass}`}>
<View className='doctor-home__header'>
<Text className='doctor-home__title'></Text>
<Text className='doctor-home__greeting'>
@@ -192,6 +193,6 @@ export default function DoctorHome() {
<View className='doctor-home__footer'>
<Text className='doctor-home__logout' onClick={handleLogout}>退</Text>
</View>
</ScrollView>
</PageShell>
);
}

View File

@@ -1,21 +1,6 @@
@import '../../../../styles/variables.scss';
@import '../../../../styles/mixins.scss';
.patient-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 {
@include section-title;
}

View File

@@ -1,9 +1,11 @@
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 { usePageData } from '@/hooks/usePageData';
import { getPatient, getHealthSummary, type PatientDetail, type HealthSummary } from '@/services/doctor/patient';
import Loading from '@/components/Loading';
import PageShell from '@/components/ui/PageShell';
import ContentCard from '@/components/ui/ContentCard';
import { useElderClass } from '../../../../hooks/useElderClass';
import './index.scss';
@@ -42,12 +44,12 @@ export default function PatientDetail() {
};
if (loading) return <Loading />;
if (!patient) return <View className={`error-text ${modeClass}`}><Text></Text></View>;
if (!patient) return <PageShell className={modeClass}><View className='error-text'><Text></Text></View></PageShell>;
return (
<ScrollView scrollY className={`patient-detail ${modeClass}`}>
<PageShell className={modeClass}>
{/* 基本信息 */}
<View className='section'>
<ContentCard>
<View className='section-header'>
<Text className='section-title'></Text>
</View>
@@ -57,11 +59,11 @@ export default function PatientDetail() {
<View className='info-item'><Text className='info-label'></Text><Text className='info-value'>{calcAge(patient.birth_date)}</Text></View>
{patient.blood_type && <View className='info-item'><Text className='info-label'></Text><Text className='info-value'>{patient.blood_type}</Text></View>}
</View>
</View>
</ContentCard>
{/* 医疗信息 */}
{(patient.allergy_history || patient.medical_history_summary) && (
<View className='section'>
<ContentCard>
<Text className='section-title'></Text>
{patient.allergy_history && (
<View className='warning-card'>
@@ -75,12 +77,12 @@ export default function PatientDetail() {
<Text className='info-block-text'>{patient.medical_history_summary}</Text>
</View>
)}
</View>
</ContentCard>
)}
{/* 健康摘要 */}
{summary && (
<View className='section'>
<ContentCard>
<Text className='section-title'></Text>
{summary.latest_vital_signs && (
<View className='vitals-grid'>
@@ -116,12 +118,12 @@ export default function PatientDetail() {
<Text className='stat-value stat-value--warn'>{summary.pending_follow_ups} </Text>
</View>
)}
</View>
</ContentCard>
)}
{/* 近期化验 */}
{summary?.latest_lab_report && (
<View className='section'>
<ContentCard>
<Text className='section-title'></Text>
<View
className='lab-item'
@@ -135,11 +137,11 @@ export default function PatientDetail() {
<Text className='lab-item__abnormal'>{summary.latest_lab_report.abnormal_count} </Text>
)}
</View>
</View>
</ContentCard>
)}
{/* 快捷操作 */}
<View className='section'>
<ContentCard>
<Text className='section-title'></Text>
<View className='action-buttons'>
<View className='action-btn' onClick={() => Taro.navigateTo({ url: `/pages/pkg-doctor-clinical/report/index?patientId=${patientId}` })}>
@@ -149,7 +151,7 @@ export default function PatientDetail() {
<Text>访</Text>
</View>
</View>
</View>
</ScrollView>
</ContentCard>
</PageShell>
);
}

View File

@@ -1,11 +1,8 @@
@import '../../../styles/variables.scss';
@import '../../../styles/mixins.scss';
.alerts-page {
min-height: 100vh;
background: $bg;
padding-bottom: env(safe-area-inset-bottom);
}
// PageShell 已接管min-height, background, safe-bottom
// ContentCard 已接管alert-card 背景/圆角/阴影
.alerts-tabs {
display: flex;
@@ -39,11 +36,7 @@
}
.alert-card {
background: $card;
border-radius: $r;
padding: 24px;
margin-bottom: 16px;
box-shadow: $shadow-sm;
}
.alert-header {

View File

@@ -8,6 +8,8 @@ import Loading from '@/components/Loading';
import ErrorState from '@/components/ErrorState';
import EmptyState from '@/components/EmptyState';
import SegmentTabs from '@/components/SegmentTabs';
import PageShell from '@/components/ui/PageShell';
import ContentCard from '@/components/ui/ContentCard';
import { useElderClass } from '../../../hooks/useElderClass';
import './index.scss';
@@ -79,23 +81,23 @@ export default function PatientAlerts() {
if (!currentPatient) {
return (
<View className={`alerts-page ${modeClass}`}>
<PageShell padding="none" className={modeClass}>
<ErrorState text='请先完善个人档案' onRetry={() => Taro.navigateTo({ url: '/pages/pkg-profile/family-add/index' })} />
</View>
</PageShell>
);
}
if (error) {
return (
<View className={`alerts-page ${modeClass}`}>
<PageShell padding="none" className={modeClass}>
<SegmentTabs tabs={STATUS_TABS} activeKey={status} onChange={handleTabChange} variant="pill" />
<ErrorState onRetry={() => fetchAlerts(1, status, true)} />
</View>
</PageShell>
);
}
return (
<View className={`alerts-page ${modeClass}`}>
<PageShell padding="none" className={modeClass}>
<SegmentTabs tabs={STATUS_TABS} activeKey={status} onChange={handleTabChange} variant="pill" />
{alerts.length === 0 && !loading ? (
@@ -105,7 +107,7 @@ export default function PatientAlerts() {
{alerts.map((item) => {
const sev = SEVERITY_MAP[item.severity] || SEVERITY_MAP.warning;
return (
<View className='alert-card' key={item.id}>
<ContentCard className='alert-card' key={item.id}>
<View className='alert-header'>
<View className={`alert-badge ${sev.className}`}>
<Text className='alert-badge-text'>{sev.label}</Text>
@@ -115,7 +117,7 @@ export default function PatientAlerts() {
</Text>
</View>
<Text className='alert-title'>{item.title}</Text>
</View>
</ContentCard>
);
})}
{loading && <Loading />}
@@ -124,6 +126,6 @@ export default function PatientAlerts() {
)}
</View>
)}
</View>
</PageShell>
);
}

View File

@@ -1,11 +1,8 @@
@import '../../../styles/variables.scss';
@import '../../../styles/mixins.scss';
.dm-page {
min-height: 100vh;
background: $bg;
padding: 0 0 60px;
}
// PageShell 已接管min-height, background
// ContentCard 已接管dm-card/dm-group 背景/圆角/阴影
/* ── hero ── */
.dm-hero {
@@ -44,10 +41,6 @@
/* ── card (standalone, used for date picker) ── */
.dm-card {
background: $card;
border-radius: $r;
box-shadow: $shadow-md;
padding: 28px;
margin: 0 24px 20px;
}
@@ -98,9 +91,6 @@
/* ── collapsible group ── */
.dm-group {
background: $card;
border-radius: $r;
box-shadow: $shadow-md;
margin: 0 24px 20px;
overflow: hidden;
}

View File

@@ -10,6 +10,8 @@ import { clearRequestCache } from '@/services/request';
import { trackEvent } from '@/services/analytics';
import { useSafeTimeout } from '@/hooks/useSafeTimeout';
import { useElderClass } from '@/hooks/useElderClass';
import PageShell from '@/components/ui/PageShell';
import ContentCard from '@/components/ui/ContentCard';
import {
BP_RANGE, WEIGHT_RANGE, SUGAR_RANGE, VOLUME_RANGE,
checkAbnormal, formatDate, FIELD_LABELS,
@@ -218,7 +220,7 @@ export default function DailyMonitoring() {
const bloodSugarAbnormal = checkAbnormal(bloodSugar, 'bloodSugar');
return (
<View className={`dm-page ${modeClass}`}>
<PageShell padding="none" safeBottom className={modeClass}>
{/* 页面标题 */}
<View className='dm-hero'>
<View className='dm-hero-icon'>
@@ -229,7 +231,7 @@ export default function DailyMonitoring() {
</View>
{/* 日期选择 (standalone card) */}
<View className='dm-card'>
<ContentCard className='dm-card'>
<View className='dm-card-header'>
<Text className='dm-card-title'></Text>
{isToday && (
@@ -247,10 +249,10 @@ export default function DailyMonitoring() {
<Text className='dm-date-arrow'>V</Text>
</View>
</Picker>
</View>
</ContentCard>
{/* ── Group 1: 晨间体征 (default open) ── */}
<View className={`dm-group${collapsed.morning ? ' dm-group-collapsed' : ''}`}>
<ContentCard className={`dm-group${collapsed.morning ? ' dm-group-collapsed' : ''}`}>
<View className='dm-group-header' onClick={() => toggleSection('morning')}>
<Text className='dm-group-title'></Text>
<Text className={`dm-group-arrow${collapsed.morning ? '' : ' dm-group-arrow-open'}`}>&#9656;</Text>
@@ -295,10 +297,10 @@ export default function DailyMonitoring() {
</View>
<Text className='dm-field-unit'>mmHg</Text>
</View>
</View>
</ContentCard>
{/* ── Group 2: 晚间体征 (default open) ── */}
<View className={`dm-group${collapsed.evening ? ' dm-group-collapsed' : ''}`}>
<ContentCard className={`dm-group${collapsed.evening ? ' dm-group-collapsed' : ''}`}>
<View className='dm-group-header' onClick={() => toggleSection('evening')}>
<Text className='dm-group-title'></Text>
<Text className={`dm-group-arrow${collapsed.evening ? '' : ' dm-group-arrow-open'}`}>&#9656;</Text>
@@ -343,10 +345,10 @@ export default function DailyMonitoring() {
</View>
<Text className='dm-field-unit'>mmHg</Text>
</View>
</View>
</ContentCard>
{/* ── Group 3: 其他指标 (default collapsed) ── */}
<View className={`dm-group${collapsed.other ? ' dm-group-collapsed' : ''}`}>
<ContentCard className={`dm-group${collapsed.other ? ' dm-group-collapsed' : ''}`}>
<View className='dm-group-header' onClick={() => toggleSection('other')}>
<Text className='dm-group-title'></Text>
<Text className={`dm-group-arrow${collapsed.other ? '' : ' dm-group-arrow-open'}`}>&#9656;</Text>
@@ -428,7 +430,7 @@ export default function DailyMonitoring() {
/>
</View>
</View>
</View>
</ContentCard>
{/* 提交 */}
<View
@@ -442,6 +444,6 @@ export default function DailyMonitoring() {
<View className='dm-reset' onClick={resetForm}>
<Text className='dm-reset-text'></Text>
</View>
</View>
</PageShell>
);
}

View File

@@ -1,11 +1,8 @@
@import '../../../styles/variables.scss';
@import '../../../styles/mixins.scss';
.device-sync-page {
min-height: 100vh;
background: $bg;
padding-bottom: env(safe-area-inset-bottom);
}
// PageShell 已接管min-height, background, safe-bottom
// ContentCard 已接管sync-status-card/sync-result-card 背景/圆角/阴影
.sync-header {
background: $pri;
@@ -130,11 +127,7 @@
.sync-status-card {
display: flex;
align-items: center;
background: $card;
border-radius: $r-sm;
padding: 24px;
margin-bottom: 16px;
box-shadow: $shadow-sm;
}
.sync-status-dot {
@@ -225,9 +218,6 @@
display: flex;
flex-direction: column;
align-items: center;
background: $card;
border-radius: $r;
padding: 48px 24px;
margin-bottom: 24px;
box-shadow: $shadow-sm;
}

View File

@@ -12,6 +12,8 @@ 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 PageShell from '@/components/ui/PageShell';
import ContentCard from '@/components/ui/ContentCard';
import './index.scss';
/** liveReadings 最大保留条数,防止内存无限增长 */
@@ -232,10 +234,10 @@ export default function DeviceSync() {
const renderConnected = () => (
<View className="sync-section">
<View className="sync-status-card">
<ContentCard className="sync-status-card">
<Text className="sync-status-dot sync-status-dot--connected" />
<Text className="sync-status-text">: {selectedDevice?.name}</Text>
</View>
</ContentCard>
{liveReadings.length > 0 && (
<View className="sync-readings-panel">
@@ -276,11 +278,11 @@ export default function DeviceSync() {
const renderDone = () => (
<View className="sync-section">
<View className="sync-result-card">
<ContentCard className="sync-result-card">
<Text className="sync-result-icon">V</Text>
<Text className="sync-result-title"></Text>
<Text className="sync-result-count"> {syncCount} </Text>
</View>
</ContentCard>
<View className="sync-action" onClick={() => {
handleDisconnect();
if (returnTo === 'input') {
@@ -293,7 +295,7 @@ export default function DeviceSync() {
);
return (
<View className={`device-sync-page ${modeClass}`}>
<PageShell padding="none" className={modeClass}>
<View className="sync-header">
<Text className="sync-header-title"></Text>
</View>
@@ -317,6 +319,6 @@ export default function DeviceSync() {
{(pageState === 'idle' || pageState === 'error') && renderIdle()}
{pageState === 'connected' && renderConnected()}
{pageState === 'done' && renderDone()}
</View>
</PageShell>
);
}

View File

@@ -1,11 +1,8 @@
@import '../../../styles/variables.scss';
@import '../../../styles/mixins.scss';
.input-page {
min-height: 100vh;
background: $bg;
padding: 0 0 60px;
}
// PageShell 已接管min-height, background
// ContentCard 已接管input-card 背景/圆角/阴影
/* ── hero ── */
.input-hero {
@@ -47,10 +44,6 @@
display: flex;
align-items: center;
justify-content: space-between;
background: $card;
border-radius: $r;
box-shadow: $shadow-sm;
padding: 24px 28px;
margin: 0 24px 20px;
border: 1px dashed $pri;
@@ -72,10 +65,6 @@
/* ── card ── */
.input-card {
background: $card;
border-radius: $r;
box-shadow: $shadow-md;
padding: 28px;
margin: 0 24px 20px;
}

View File

@@ -12,6 +12,8 @@ import { useSafeTimeout } from '@/hooks/useSafeTimeout';
import { trackEvent } from '@/services/analytics';
import { useElderClass } from '../../../hooks/useElderClass';
import Loading from '../../../components/Loading';
import PageShell from '@/components/ui/PageShell';
import ContentCard from '@/components/ui/ContentCard';
import './index.scss';
const INDICATORS = [
@@ -176,7 +178,7 @@ export default function HealthInput() {
const indicatorInitial = INDICATORS[indicatorIdx].label.charAt(0);
return (
<View className={`input-page ${modeClass}`}>
<PageShell padding="none" safeBottom className={modeClass}>
{loadingThresholds && <Loading />}
{!loadingThresholds && (
@@ -197,7 +199,7 @@ export default function HealthInput() {
</View>
{/* 指标类型选择 */}
<View className='input-card'>
<ContentCard className='input-card'>
<View className='input-card-header'>
<View className='input-card-indicator'>
<Text className='input-card-indicator-char'>{indicatorInitial}</Text>
@@ -215,11 +217,11 @@ export default function HealthInput() {
<Text className='input-picker-arrow'>V</Text>
</View>
</Picker>
</View>
</ContentCard>
{/* 数值输入 */}
{BP_INDICATORS.includes(INDICATORS[indicatorIdx].value) ? (
<View className='input-card'>
<ContentCard className='input-card'>
<Text className='input-section-title'></Text>
<View className='input-bp-group'>
<View className='input-bp-field'>
@@ -249,9 +251,9 @@ export default function HealthInput() {
</View>
</View>
<Text className='input-field-unit'>mmHg</Text>
</View>
</ContentCard>
) : (
<View className='input-card'>
<ContentCard className='input-card'>
<Text className='input-section-title'></Text>
<Input
type='digit'
@@ -263,11 +265,11 @@ export default function HealthInput() {
<Text className='input-field-unit'>
{INDICATORS[indicatorIdx].label.match(/\((.+)\)/)?.[1] || ''}
</Text>
</View>
</ContentCard>
)}
{/* 备注 */}
<View className='input-card'>
<ContentCard className='input-card'>
<Text className='input-section-title'></Text>
<Input
className='input-field-box input-field-full'
@@ -275,7 +277,7 @@ export default function HealthInput() {
value={note}
onInput={(e) => setNote(e.detail.value)}
/>
</View>
</ContentCard>
{/* 提交 */}
<View
@@ -286,6 +288,6 @@ export default function HealthInput() {
</View>
</>
)}
</View>
</PageShell>
);
}

View File

@@ -1,9 +1,10 @@
@import '../../../styles/variables.scss';
@import '../../../styles/mixins.scss';
.trend-page {
min-height: 100vh;
background: $bg;
// PageShell 已接管min-height, background
// ContentCard 已接管trend-chart-card 背景/圆角/阴影
.trend-page-shell {
padding-bottom: 60px;
}
@@ -48,10 +49,6 @@
/* ── chart card ── */
.trend-chart-card {
margin: 0 24px 20px;
background: $card;
border-radius: $r;
box-shadow: $shadow-md;
padding: 24px;
min-height: 300px;
overflow: hidden;
}
@@ -59,9 +56,6 @@
/* ── reference card ── */
.trend-ref-card {
margin: 0 24px 20px;
background: $acc-l;
border-radius: $r;
padding: 20px 24px;
display: flex;
align-items: center;
gap: 16px;

View File

@@ -8,6 +8,8 @@ import Loading from '@/components/Loading';
import ErrorState from '@/components/ErrorState';
import EmptyState from '@/components/EmptyState';
import SegmentTabs from '@/components/SegmentTabs';
import PageShell from '@/components/ui/PageShell';
import ContentCard from '@/components/ui/ContentCard';
import { useElderClass } from '../../../hooks/useElderClass';
import './index.scss';
@@ -67,7 +69,7 @@ export default function Trend() {
};
return (
<View className={`trend-page ${modeClass}`}>
<PageShell padding="none" className={modeClass}>
{/* 页面标题 */}
<View className='trend-hero'>
<View className='trend-hero-icon'>
@@ -81,36 +83,36 @@ export default function Trend() {
{/* ECharts 折线图 */}
{loading ? (
<View className='trend-chart-card'>
<ContentCard className='trend-chart-card'>
<Loading />
</View>
</ContentCard>
) : error ? (
<View className='trend-chart-card'>
<ContentCard className='trend-chart-card'>
<ErrorState onRetry={fetchTrend} />
</View>
</ContentCard>
) : points.length === 0 ? (
<View className='trend-chart-card'>
<ContentCard className='trend-chart-card'>
<EmptyState text='暂无趋势数据' />
</View>
</ContentCard>
) : (
<View className='trend-chart-card'>
<ContentCard className='trend-chart-card'>
<TrendChart
data={points}
referenceMin={meta.refMin}
referenceMax={meta.refMax}
unit={meta.unit}
/>
</View>
</ContentCard>
)}
{/* 参考区间 */}
{!loading && points.length > 0 && meta.refMin !== undefined && meta.refMax !== undefined && (
<View className='trend-ref-card'>
<ContentCard variant="outlined" className='trend-ref-card'>
<Text className='trend-ref-label'></Text>
<Text className='trend-ref-value'>
{meta.refMin} ~ {meta.refMax} {meta.unit}
</Text>
</View>
</ContentCard>
)}
{/* 数据列表 */}
@@ -135,6 +137,6 @@ export default function Trend() {
})}
</View>
)}
</View>
</PageShell>
);
}

View File

@@ -1,11 +1,7 @@
@import '../../../styles/variables.scss';
@import '../../../styles/mixins.scss';
.detail-page {
min-height: 100vh;
background: $bg;
padding-bottom: 40px;
}
// PageShell 已接管min-height, background, padding
/* ===== 余额卡片 ===== */
.balance-card {

View File

@@ -8,6 +8,7 @@ import { usePointsStore } from '../../../stores/points';
import EmptyState from '../../../components/EmptyState';
import Loading from '../../../components/Loading';
import { useElderClass } from '../../../hooks/useElderClass';
import PageShell from '@/components/ui/PageShell';
import './index.scss';
const TYPE_TABS = [
@@ -109,7 +110,7 @@ export default function PointsDetail() {
const balance = account?.balance ?? 0;
return (
<View className={`detail-page ${modeClass}`}>
<PageShell className={modeClass}>
{/* 余额卡片 */}
<View className='balance-card'>
<Text className='balance-label'></Text>
@@ -177,6 +178,6 @@ export default function PointsDetail() {
)}
</View>
)}
</View>
</PageShell>
);
}

View File

@@ -1,9 +1,8 @@
@import '../../../styles/variables.scss';
@import '../../../styles/mixins.scss';
// PageShell 已接管min-height, background, padding
.exchange-page {
min-height: 100vh;
background: $bg;
padding-bottom: 140px;
}

View File

@@ -11,6 +11,7 @@ import { usePointsStore } from '../../../stores/points';
import Loading from '../../../components/Loading';
import { useElderClass } from '../../../hooks/useElderClass';
import { useSafeTimeout } from '@/hooks/useSafeTimeout';
import PageShell from '@/components/ui/PageShell';
import './index.scss';
const TYPE_INITIAL: Record<string, string> = {
@@ -128,9 +129,9 @@ export default function ExchangeConfirm() {
if (loading) {
return (
<View className={`exchange-page ${modeClass}`}>
<PageShell className={modeClass}>
<Loading />
</View>
</PageShell>
);
}
@@ -140,7 +141,7 @@ export default function ExchangeConfirm() {
const iconCls = TYPE_CLASS[productType] || 'product-icon-wrap--service';
return (
<View className={`exchange-page ${modeClass}`}>
<PageShell className={modeClass}>
{/* 商品预览卡片 */}
<View className='product-card'>
<View className={`product-icon-wrap ${iconCls}`}>
@@ -216,6 +217,6 @@ export default function ExchangeConfirm() {
</Text>
</View>
</View>
</View>
</PageShell>
);
}

View File

@@ -1,11 +1,8 @@
@import '../../../styles/variables.scss';
@import '../../../styles/mixins.scss';
.orders-page {
min-height: 100vh;
background: $bg;
padding-bottom: 40px;
}
// PageShell 已接管min-height, background, safe-bottom
// ContentCard 已接管order-card 背景/圆角/阴影
/* ===== 订单列表 ===== */
.order-list {
@@ -13,11 +10,8 @@
}
.order-card {
background: $card;
border-radius: $r;
margin-bottom: 16px;
overflow: hidden;
box-shadow: $shadow-sm;
}
.order-header {

View File

@@ -8,6 +8,8 @@ import EmptyState from '../../../components/EmptyState';
import ErrorState from '../../../components/ErrorState';
import Loading from '../../../components/Loading';
import SegmentTabs from '../../../components/SegmentTabs';
import PageShell from '@/components/ui/PageShell';
import ContentCard from '@/components/ui/ContentCard';
import { useElderClass } from '../../../hooks/useElderClass';
import './index.scss';
@@ -111,7 +113,7 @@ export default function MallOrders() {
};
return (
<View className={`orders-page ${modeClass}`}>
<PageShell padding="none" className={modeClass}>
{/* 状态筛选标签 */}
<SegmentTabs tabs={STATUS_TABS} activeKey={activeTab} onChange={handleTabChange} variant="underline" />
@@ -131,7 +133,7 @@ export default function MallOrders() {
{orders.map((order) => {
const statusCfg = getStatusConfig(order.status);
return (
<View className='order-card' key={order.id}>
<ContentCard className='order-card' key={order.id}>
<View className='order-header'>
<Text className='order-product'> {order.product_id.slice(0, 8)}</Text>
<View
@@ -162,7 +164,7 @@ export default function MallOrders() {
</View>
)}
</View>
</View>
</ContentCard>
);
})}
{loading && <Loading />}
@@ -171,6 +173,6 @@ export default function MallOrders() {
)}
</View>
)}
</View>
</PageShell>
);
}

View File

@@ -1,12 +1,7 @@
@import '../../../styles/variables.scss';
@import '../../../styles/mixins.scss';
.consents-page {
min-height: 100vh;
background: $bg;
padding: 32px 24px;
padding-bottom: 40px;
}
// PageShell 已接管min-height, background, padding
.page-title {
@include section-title;

View File

@@ -8,6 +8,7 @@ import type { Consent } from '@/services/consent';
import EmptyState from '@/components/EmptyState';
import Loading from '@/components/Loading';
import { useElderClass } from '../../../hooks/useElderClass';
import PageShell from '@/components/ui/PageShell';
import './index.scss';
const CONSENT_TYPE_MAP: Record<string, string> = {
@@ -82,7 +83,7 @@ export default function ConsentList() {
};
return (
<View className={`consents-page ${modeClass}`}>
<PageShell className={modeClass}>
<Text className='page-title'></Text>
<View className='consent-list'>
@@ -123,6 +124,6 @@ export default function ConsentList() {
)}
{loading && <Loading />}
</View>
</PageShell>
);
}

View File

@@ -1,12 +1,7 @@
@import '../../../styles/variables.scss';
@import '../../../styles/mixins.scss';
.diagnoses-page {
min-height: 100vh;
background: $bg;
padding: 32px 24px;
padding-bottom: 40px;
}
// PageShell 已接管min-height, background, padding
.page-title {
font-family: 'Georgia', 'Times New Roman', serif;

View File

@@ -7,6 +7,7 @@ import { listDiagnoses, Diagnosis } from '../../../services/health-record';
import EmptyState from '../../../components/EmptyState';
import Loading from '../../../components/Loading';
import { useElderClass } from '../../../hooks/useElderClass';
import PageShell from '@/components/ui/PageShell';
import './index.scss';
const TYPE_MAP: Record<string, { label: string; cls: string }> = {
@@ -60,7 +61,7 @@ export default function Diagnoses() {
});
return (
<View className={`diagnoses-page ${modeClass}`}>
<PageShell className={modeClass}>
<Text className='page-title'></Text>
<View className='diagnosis-list'>
@@ -95,6 +96,6 @@ export default function Diagnoses() {
)}
{loading && <Loading />}
</View>
</PageShell>
);
}

View File

@@ -1,28 +1,11 @@
@import '../../../../styles/variables.scss';
@import '../../../../styles/mixins.scss';
.detail-page {
min-height: 100vh;
background: $bg;
padding: 24px;
padding-bottom: 40px;
}
.detail-card {
background: $card;
border-radius: $r;
padding: 28px;
margin-bottom: 20px;
box-shadow: $shadow-sm;
}
.header-card {
.header-row {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 8px;
}
.header-row {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 8px;
}
.detail-title {

View File

@@ -5,6 +5,8 @@ import { usePageData } from '@/hooks/usePageData';
import { getDialysisPrescription } from '@/services/dialysis';
import type { DialysisPrescription } from '@/services/dialysis';
import Loading from '@/components/Loading';
import PageShell from '@/components/ui/PageShell';
import ContentCard from '@/components/ui/ContentCard';
import { useElderClass } from '../../../../hooks/useElderClass';
import './index.scss';
@@ -36,8 +38,8 @@ export default function DialysisPrescriptionDetail() {
usePageData(fetchDetail, { throttleMs: 60000 });
if (loading) return <View className={`detail-page ${modeClass}`}><Loading /></View>;
if (!rx) return <View className={`detail-page ${modeClass}`}><View className='empty-state'><Text className='empty-text'></Text></View></View>;
if (loading) return <PageShell className={modeClass}><Loading /></PageShell>;
if (!rx) return <PageShell className={modeClass}><View className='empty-state'><Text className='empty-text'></Text></View></PageShell>;
const si = STATUS_MAP[rx.status] || { label: rx.status, cls: '' };
@@ -52,9 +54,9 @@ export default function DialysisPrescriptionDetail() {
};
return (
<View className={`detail-page ${modeClass}`}>
<PageShell className={modeClass}>
{/* 状态头部 */}
<View className='detail-card header-card'>
<ContentCard>
<View className='header-row'>
<Text className='detail-title'>{rx.dialyzer_model || '透析处方'}</Text>
<Text className={`status-tag ${si.cls}`}>{si.label}</Text>
@@ -62,10 +64,10 @@ export default function DialysisPrescriptionDetail() {
{(rx.effective_from || rx.effective_to) && (
<Text className='header-sub'>{rx.effective_from || '...'} ~ {rx.effective_to || '...'}</Text>
)}
</View>
</ContentCard>
{/* 基本参数 */}
<View className='detail-card'>
<ContentCard>
<Text className='section-title'></Text>
<Row label='透析器型号' value={rx.dialyzer_model} />
<Row label='膜面积' value={rx.membrane_area != null ? `${rx.membrane_area}` : null} />
@@ -73,48 +75,48 @@ export default function DialysisPrescriptionDetail() {
<Row label='透析液流量' value={rx.dialysate_flow_rate != null ? `${rx.dialysate_flow_rate} ml/min` : null} />
<Row label='频率' value={rx.frequency_per_week != null ? `${rx.frequency_per_week} 次/周` : null} />
<Row label='每次时长' value={rx.duration_minutes != null ? `${rx.duration_minutes} 分钟` : null} />
</View>
</ContentCard>
{/* 透析液配比 */}
<View className='detail-card'>
<ContentCard>
<Text className='section-title'></Text>
<Row label='钾浓度' value={rx.dialysate_potassium != null ? `${rx.dialysate_potassium} mmol/L` : null} />
<Row label='钙浓度' value={rx.dialysate_calcium != null ? `${rx.dialysate_calcium} mmol/L` : null} />
<Row label='碳酸氢盐浓度' value={rx.dialysate_bicarbonate != null ? `${rx.dialysate_bicarbonate} mmol/L` : null} />
</View>
</ContentCard>
{/* 抗凝方案 */}
<View className='detail-card'>
<ContentCard>
<Text className='section-title'></Text>
<Row label='抗凝类型' value={rx.anticoagulation_type} />
<Row label='抗凝剂量' value={rx.anticoagulation_dose} />
</View>
</ContentCard>
{/* 血管通路 */}
{(rx.vascular_access_type || rx.vascular_access_location) && (
<View className='detail-card'>
<ContentCard>
<Text className='section-title'></Text>
<Row label='通路类型' value={rx.vascular_access_type} />
<Row label='通路位置' value={rx.vascular_access_location} />
</View>
</ContentCard>
)}
{/* 超滤与干体重 */}
{(rx.target_ultrafiltration_ml != null || rx.target_dry_weight != null) && (
<View className='detail-card'>
<ContentCard>
<Text className='section-title'></Text>
<Row label='目标超滤量' value={rx.target_ultrafiltration_ml != null ? `${rx.target_ultrafiltration_ml} ml` : null} />
<Row label='目标干体重' value={rx.target_dry_weight != null ? `${rx.target_dry_weight} kg` : null} />
</View>
</ContentCard>
)}
{/* 备注 */}
{rx.notes && (
<View className='detail-card'>
<ContentCard>
<Text className='section-title'></Text>
<Text className='notes-text'>{rx.notes}</Text>
</View>
</ContentCard>
)}
</View>
</PageShell>
);
}

View File

@@ -1,12 +1,7 @@
@import '../../../styles/variables.scss';
@import '../../../styles/mixins.scss';
.dialysis-prescriptions-page {
min-height: 100vh;
background: $bg;
padding: 32px 24px;
padding-bottom: 40px;
}
// PageShell 已接管min-height, background, padding
.page-title {
@include section-title;

View File

@@ -8,6 +8,7 @@ import type { DialysisPrescription } from '@/services/dialysis';
import EmptyState from '@/components/EmptyState';
import Loading from '@/components/Loading';
import { useElderClass } from '../../../hooks/useElderClass';
import PageShell from '@/components/ui/PageShell';
import './index.scss';
const STATUS_MAP: Record<string, { label: string; cls: string }> = {
@@ -57,7 +58,7 @@ export default function DialysisPrescriptionList() {
const statusInfo = (s: string) => STATUS_MAP[s] || { label: s, cls: '' };
return (
<View className={`dialysis-prescriptions-page ${modeClass}`}>
<PageShell className={modeClass}>
<Text className='page-title'></Text>
<View className='prescription-list'>
@@ -96,6 +97,6 @@ export default function DialysisPrescriptionList() {
)}
{loading && <Loading />}
</View>
</PageShell>
);
}

View File

@@ -1,28 +1,11 @@
@import '../../../../styles/variables.scss';
@import '../../../../styles/mixins.scss';
.detail-page {
min-height: 100vh;
background: $bg;
padding: 24px;
padding-bottom: 40px;
}
.detail-card {
background: $card;
border-radius: $r;
padding: 28px;
margin-bottom: 20px;
box-shadow: $shadow-sm;
}
.header-card {
.header-row {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 8px;
}
.header-row {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 8px;
}
.detail-title {

View File

@@ -5,6 +5,8 @@ import { usePageData } from '@/hooks/usePageData';
import { getDialysisRecord } from '@/services/dialysis';
import type { DialysisRecord } from '@/services/dialysis';
import Loading from '@/components/Loading';
import PageShell from '@/components/ui/PageShell';
import ContentCard from '@/components/ui/ContentCard';
import { useElderClass } from '../../../../hooks/useElderClass';
import './index.scss';
@@ -42,25 +44,25 @@ export default function DialysisRecordDetail() {
usePageData(fetchDetail, { throttleMs: 60000 });
if (loading) return <View className={`detail-page ${modeClass}`}><Loading /></View>;
if (!record) return <View className={`detail-page ${modeClass}`}><View className='empty-state'><Text className='empty-text'></Text></View></View>;
if (loading) return <PageShell className={modeClass}><Loading /></PageShell>;
if (!record) return <PageShell className={modeClass}><View className='empty-state'><Text className='empty-text'></Text></View></PageShell>;
const si = STATUS_MAP[record.status] || { label: record.status, cls: '' };
return (
<View className={`detail-page ${modeClass}`}>
<PageShell className={modeClass}>
{/* 状态头部 */}
<View className='detail-card header-card'>
<ContentCard>
<View className='header-row'>
<Text className='detail-title'>{record.dialysis_date}</Text>
<Text className={`status-tag ${si.cls}`}>{si.label}</Text>
</View>
<Text className='header-sub'>{TYPE_MAP[record.dialysis_type] || record.dialysis_type}</Text>
{record.reviewed_at && <Text className='review-info'> {record.reviewed_at}</Text>}
</View>
</ContentCard>
{/* 基本信息 */}
<View className='detail-card'>
<ContentCard>
<Text className='section-title'></Text>
{record.start_time && (
<View className='detail-row'><Text className='detail-label'></Text><Text className='detail-value'>{record.start_time}</Text></View>
@@ -77,10 +79,10 @@ export default function DialysisRecordDetail() {
{record.ultrafiltration_volume != null && (
<View className='detail-row'><Text className='detail-label'></Text><Text className='detail-value'>{record.ultrafiltration_volume} ml</Text></View>
)}
</View>
</ContentCard>
{/* 体重与血压 */}
<View className='detail-card'>
<ContentCard>
<Text className='section-title'></Text>
{record.dry_weight != null && (
<View className='detail-row'><Text className='detail-label'></Text><Text className='detail-value'>{record.dry_weight} kg</Text></View>
@@ -103,11 +105,11 @@ export default function DialysisRecordDetail() {
{record.post_heart_rate != null && (
<View className='detail-row'><Text className='detail-label'></Text><Text className='detail-value'>{record.post_heart_rate} bpm</Text></View>
)}
</View>
</ContentCard>
{/* 症状与并发症 */}
{(record.symptoms || record.complication_notes) && (
<View className='detail-card'>
<ContentCard>
<Text className='section-title'></Text>
{record.symptoms && Object.keys(record.symptoms).length > 0 && (
<View className='detail-row'><Text className='detail-label'></Text><Text className='detail-value'>{JSON.stringify(record.symptoms)}</Text></View>
@@ -115,8 +117,8 @@ export default function DialysisRecordDetail() {
{record.complication_notes && (
<View className='detail-row'><Text className='detail-label'></Text><Text className='detail-value'>{record.complication_notes}</Text></View>
)}
</View>
</ContentCard>
)}
</View>
</PageShell>
);
}

View File

@@ -1,12 +1,7 @@
@import '../../../styles/variables.scss';
@import '../../../styles/mixins.scss';
.dialysis-records-page {
min-height: 100vh;
background: $bg;
padding: 32px 24px;
padding-bottom: 40px;
}
// PageShell 已接管min-height, background, padding
.page-title {
@include section-title;

View File

@@ -8,6 +8,7 @@ import type { DialysisRecord } from '@/services/dialysis';
import EmptyState from '@/components/EmptyState';
import Loading from '@/components/Loading';
import { useElderClass } from '../../../hooks/useElderClass';
import PageShell from '@/components/ui/PageShell';
import './index.scss';
const TYPE_MAP: Record<string, { label: string; cls: string }> = {
@@ -64,7 +65,7 @@ export default function DialysisRecordList() {
const statusInfo = (s: string) => STATUS_MAP[s] || { label: s, cls: '' };
return (
<View className={`dialysis-records-page ${modeClass}`}>
<PageShell className={modeClass}>
<Text className='page-title'></Text>
<View className='record-list'>
@@ -101,6 +102,6 @@ export default function DialysisRecordList() {
)}
{loading && <Loading />}
</View>
</PageShell>
);
}

View File

@@ -1,9 +1,8 @@
@import '../../../styles/variables.scss';
@import '../../../styles/mixins.scss';
// PageShell 已接管min-height, background, padding
.events-page {
min-height: 100vh;
background: $bg;
padding-bottom: 120px;
}

View File

@@ -6,6 +6,7 @@ import Loading from '@/components/Loading';
import EmptyState from '@/components/EmptyState';
import { useElderClass } from '@/hooks/useElderClass';
import { usePageData } from '@/hooks/usePageData';
import PageShell from '@/components/ui/PageShell';
import './index.scss';
const STATUS_MAP: Record<string, { label: string; className: string }> = {
@@ -60,7 +61,7 @@ export default function EventsPage() {
if (loading) return <Loading />;
return (
<ScrollView scrollY className={`events-page ${modeClass}`}>
<PageShell className={modeClass}>
<View className='events-header'>
<Text className='events-header__title'>线</Text>
<Text className='events-header__subtitle'></Text>
@@ -111,6 +112,6 @@ export default function EventsPage() {
})}
</View>
)}
</ScrollView>
</PageShell>
);
}

View File

@@ -1,10 +1,8 @@
@import '../../../styles/variables.scss';
@import '../../../styles/mixins.scss';
// PageShell 已接管min-height, background, padding
.family-add-page {
min-height: 100vh;
background: $bg;
padding: 32px 24px;
padding-bottom: 160px;
}

View File

@@ -4,6 +4,7 @@ import Taro, { useRouter } from '@tarojs/taro';
import { createPatient, updatePatient, Patient } from '../../../services/patient';
import { useElderClass } from '../../../hooks/useElderClass';
import { useSafeTimeout } from '@/hooks/useSafeTimeout';
import PageShell from '@/components/ui/PageShell';
import './index.scss';
const RELATION_OPTIONS = ['本人', '配偶', '父母', '子女', '其他'];
@@ -66,7 +67,7 @@ export default function FamilyAdd() {
};
return (
<View className={`family-add-page ${modeClass}`}>
<PageShell className={modeClass}>
<Text className='page-title'>{editId ? '编辑就诊人' : '添加就诊人'}</Text>
<View className='form-card'>
@@ -134,6 +135,6 @@ export default function FamilyAdd() {
>
<Text className='submit-text'>{submitting ? '提交中...' : editId ? '保存修改' : '确认添加'}</Text>
</View>
</View>
</PageShell>
);
}

View File

@@ -1,10 +1,8 @@
@import '../../../styles/variables.scss';
@import '../../../styles/mixins.scss';
// PageShell 已接管min-height, background, padding
.family-page {
min-height: 100vh;
background: $bg;
padding: 32px 24px;
padding-bottom: 160px;
}

View File

@@ -6,6 +6,7 @@ import { listPatients, Patient } from '../../../services/patient';
import { useAuthStore } from '../../../stores/auth';
import EmptyState from '../../../components/EmptyState';
import { useElderClass } from '../../../hooks/useElderClass';
import PageShell from '@/components/ui/PageShell';
import './index.scss';
export default function FamilyList() {
@@ -60,7 +61,7 @@ export default function FamilyList() {
};
return (
<View className={`family-page ${modeClass}`}>
<PageShell className={modeClass}>
<Text className='family-page-title'></Text>
<View className='family-list'>
@@ -103,6 +104,6 @@ export default function FamilyList() {
<View className='family-add-btn' onClick={goToAdd}>
<Text className='family-add-text'></Text>
</View>
</View>
</PageShell>
);
}

View File

@@ -1,21 +1,6 @@
@import '../../../../styles/variables.scss';
@import '../../../../styles/mixins.scss';
.detail-page {
min-height: 100vh;
background: $bg;
padding: 24px;
padding-bottom: 40px;
}
.detail-card {
background: $card;
border-radius: $r;
padding: 28px;
margin-bottom: 20px;
box-shadow: $shadow-sm;
}
.detail-title {
@include section-title;
font-size: var(--tk-font-num-lg);
@@ -79,13 +64,6 @@
line-height: 1.6;
}
.submit-card {
background: $card;
border-radius: $r;
padding: 28px;
box-shadow: $shadow-sm;
}
.section-title {
@include section-title;
margin-bottom: 16px;

View File

@@ -8,6 +8,8 @@ import { TEMPLATE_IDS } from '@/services/wechat-templates';
import { trackEvent } from '@/services/analytics';
import Loading from '@/components/Loading';
import ErrorState from '@/components/ErrorState';
import PageShell from '@/components/ui/PageShell';
import ContentCard from '@/components/ui/ContentCard';
import { useElderClass } from '@/hooks/useElderClass';
import './index.scss';
@@ -89,25 +91,25 @@ export default function FollowUpDetail() {
if (loading) {
return (
<View className={`detail-page ${modeClass}`}>
<PageShell className={modeClass}>
<Loading />
</View>
</PageShell>
);
}
if (error || !task) {
return (
<View className={`detail-page ${modeClass}`}>
<PageShell className={modeClass}>
<ErrorState text='任务不存在' />
</View>
</PageShell>
);
}
const isCompleted = task.status === 'completed';
return (
<View className={`detail-page ${modeClass}`}>
<View className='detail-card'>
<PageShell className={modeClass}>
<ContentCard>
<Text className='detail-title'>{task.follow_up_type}</Text>
<View className='detail-row'>
<Text className='detail-label'></Text>
@@ -132,10 +134,10 @@ export default function FollowUpDetail() {
<Text className='detail-desc-text'>{task.content_template}</Text>
</View>
)}
</View>
</ContentCard>
{!isCompleted && (
<View className='submit-card'>
<ContentCard>
<Text className='section-title'>访</Text>
<Textarea
className='submit-textarea'
@@ -152,8 +154,8 @@ export default function FollowUpDetail() {
{submitting ? '提交中...' : '提交'}
</Text>
</View>
</View>
</ContentCard>
)}
</View>
</PageShell>
);
}

View File

@@ -1,10 +1,7 @@
@import '../../../styles/variables.scss';
@import '../../../styles/mixins.scss';
.my-followups-page {
min-height: 100vh;
background: $bg;
}
// PageShell 已接管min-height, background, padding
.tab-bar {
display: flex;

View File

@@ -7,6 +7,7 @@ import EmptyState from '../../../components/EmptyState';
import ErrorState from '../../../components/ErrorState';
import Loading from '../../../components/Loading';
import { useElderClass } from '../../../hooks/useElderClass';
import PageShell from '@/components/ui/PageShell';
import './index.scss';
const TABS = [
@@ -60,7 +61,7 @@ export default function MyFollowUps() {
};
return (
<View className={`my-followups-page ${modeClass}`}>
<PageShell className={modeClass}>
<View className='tab-bar'>
{TABS.map((tab) => (
<View
@@ -109,6 +110,6 @@ export default function MyFollowUps() {
)}
</>
)}
</View>
</PageShell>
);
}

View File

@@ -1,12 +1,7 @@
@import '../../../styles/variables.scss';
@import '../../../styles/mixins.scss';
.health-records-page {
min-height: 100vh;
background: $bg;
padding: 32px 24px;
padding-bottom: 40px;
}
// PageShell 已接管min-height, background, padding
.page-title {
font-family: 'Georgia', 'Times New Roman', serif;

View File

@@ -7,6 +7,7 @@ import { listHealthRecords, HealthRecord } from '../../../services/health-record
import EmptyState from '../../../components/EmptyState';
import Loading from '../../../components/Loading';
import { useElderClass } from '../../../hooks/useElderClass';
import PageShell from '@/components/ui/PageShell';
import './index.scss';
const TYPE_MAP: Record<string, string> = {
@@ -54,7 +55,7 @@ export default function HealthRecords() {
});
return (
<View className={`health-records-page ${modeClass}`}>
<PageShell className={modeClass}>
<Text className='page-title'></Text>
<View className='record-list'>
@@ -84,6 +85,6 @@ export default function HealthRecords() {
)}
{loading && <Loading />}
</View>
</PageShell>
);
}

View File

@@ -1,10 +1,8 @@
@import '../../../styles/variables.scss';
@import '../../../styles/mixins.scss';
// PageShell 已接管min-height, background, padding
.medication-page {
min-height: 100vh;
background: $bg;
padding: 32px 24px;
padding-bottom: 160px;
}

View File

@@ -12,6 +12,7 @@ import {
type MedicationReminder,
} from '../../../services/medication-reminder';
import { useElderClass } from '../../../hooks/useElderClass';
import PageShell from '@/components/ui/PageShell';
import './index.scss';
export default function MedicationReminder() {
@@ -100,17 +101,17 @@ export default function MedicationReminder() {
if (loading) {
return (
<View className={`medication-page ${modeClass}`}>
<PageShell className={modeClass}>
<Text className='page-title'></Text>
<View className='medication-loading'>
<Text className='medication-loading-text'>...</Text>
</View>
</View>
</PageShell>
);
}
return (
<View className={`medication-page ${modeClass}`}>
<PageShell className={modeClass}>
<Text className='page-title'></Text>
<View className='reminder-list'>
@@ -199,6 +200,6 @@ export default function MedicationReminder() {
<Text className='add-text'></Text>
</View>
)}
</View>
</PageShell>
);
}

View File

@@ -1,21 +1,6 @@
@import '../../../../styles/variables.scss';
@import '../../../../styles/mixins.scss';
.detail-page {
min-height: 100vh;
background: $bg;
padding: 24px;
padding-bottom: 40px;
}
.detail-card {
background: $card;
border-radius: $r;
padding: 28px;
margin-bottom: 20px;
box-shadow: $shadow-sm;
}
.detail-title {
font-family: 'Georgia', 'Times New Roman', serif;
font-size: var(--tk-font-num-lg);
@@ -51,13 +36,6 @@
margin-left: 24px;
}
.indicators-card {
background: $card;
border-radius: $r;
padding: 28px;
box-shadow: $shadow-sm;
}
.section-title {
font-family: 'Georgia', 'Times New Roman', serif;
font-size: var(--tk-font-num);

View File

@@ -4,6 +4,8 @@ import Taro, { useRouter } from '@tarojs/taro';
import { usePageData } from '@/hooks/usePageData';
import { getReportDetail, LabReport } from '@/services/report';
import Loading from '@/components/Loading';
import PageShell from '@/components/ui/PageShell';
import ContentCard from '@/components/ui/ContentCard';
import { useElderClass } from '@/hooks/useElderClass';
import { useAuthStore } from '@/stores/auth';
import './index.scss';
@@ -62,26 +64,26 @@ export default function ReportDetail() {
if (loading) {
return (
<View className={`detail-page ${modeClass}`}>
<PageShell className={modeClass}>
<Loading />
</View>
</PageShell>
);
}
if (!report) {
return (
<View className={`detail-page ${modeClass}`}>
<PageShell className={modeClass}>
<View className='empty-state'>
<Text className='empty-text'></Text>
</View>
</View>
</PageShell>
);
}
return (
<View className={`detail-page ${modeClass}`}>
<PageShell className={modeClass}>
{/* 基本信息 */}
<View className='detail-card'>
<ContentCard>
<Text className='detail-title'>{report.report_type}</Text>
<View className='detail-row'>
<Text className='detail-label'></Text>
@@ -93,10 +95,10 @@ export default function ReportDetail() {
<Text className='detail-value'>{report.doctor_interpretation}</Text>
</View>
)}
</View>
</ContentCard>
{/* 指标列表 */}
<View className='indicators-card'>
<ContentCard>
<Text className='section-title'></Text>
{indicators.map((item) => {
const statusInfo = getStatusInfo(item.status);
@@ -122,7 +124,7 @@ export default function ReportDetail() {
</View>
);
})}
</View>
</View>
</ContentCard>
</PageShell>
);
}

View File

@@ -1,12 +1,7 @@
@import '../../../styles/variables.scss';
@import '../../../styles/mixins.scss';
.my-reports-page {
min-height: 100vh;
background: $bg;
padding: 32px 24px;
padding-bottom: 40px;
}
// PageShell 已接管min-height, background, padding
.page-title {
@include section-title;

View File

@@ -7,6 +7,7 @@ import { listReports, LabReport } from '../../../services/report';
import EmptyState from '../../../components/EmptyState';
import Loading from '../../../components/Loading';
import { useElderClass } from '../../../hooks/useElderClass';
import PageShell from '@/components/ui/PageShell';
import './index.scss';
export default function MyReports() {
@@ -64,7 +65,7 @@ export default function MyReports() {
};
return (
<View className={`my-reports-page ${modeClass}`}>
<PageShell className={modeClass}>
<Text className='page-title'></Text>
<View className='report-list'>
@@ -100,6 +101,6 @@ export default function MyReports() {
{loading && (
<Loading />
)}
</View>
</PageShell>
);
}

View File

@@ -1,11 +1,7 @@
@import '../../../styles/variables.scss';
@import '../../../styles/mixins.scss';
.settings-page {
min-height: 100vh;
background: $bg;
padding: 32px 24px;
}
// PageShell 已接管min-height, background, padding
.page-title {
@include section-title;

View File

@@ -4,6 +4,7 @@ import Taro from '@tarojs/taro';
import { useAuthStore } from '../../../stores/auth';
import { invalidateHeadersCache, clearRequestCache } from '@/services/request';
import { useElderClass } from '../../../hooks/useElderClass';
import PageShell from '@/components/ui/PageShell';
import './index.scss';
export default function Settings() {
@@ -65,7 +66,7 @@ export default function Settings() {
};
return (
<View className={`settings-page ${modeClass}`}>
<PageShell className={modeClass}>
<Text className='page-title'></Text>
<View className='settings-group'>
@@ -97,6 +98,6 @@ export default function Settings() {
<Text className='settings-label logout-label'>退</Text>
</View>
</View>
</View>
</PageShell>
);
}