refactor(mp): 迁移处方列表页 — 使用统一组件库

- PageShell 替代手写 ScrollView + min-height/bg/padding
- SearchSection 替代搜索栏 + SegmentTabs 替代 tabs(预设患者时)
- ContentCard 替代 prescription-card 手写样式
- StatusTag 替代 status-tag 手写样式
- LoadingCard 替代 Loading 组件
- PaginationBar 替代手写分页
- 精简 SCSS:删除 page/search-bar/card/pagination 通用样式,保留业务特有样式
This commit is contained in:
iven
2026-05-16 00:56:02 +08:00
parent 9415807a40
commit 3e88dcaba5
2 changed files with 87 additions and 143 deletions

View File

@@ -1,45 +1,24 @@
@import '../../../styles/variables.scss';
@import '../../../styles/mixins.scss';
.prescription-page {
min-height: 100vh;
background: $bg;
padding-bottom: 120px;
}
.search-bar {
padding: 16px 24px;
background: $card;
}
.search-input {
background: $bg;
border-radius: $r-sm;
padding: 16px 20px;
font-size: var(--tk-font-body-lg);
color: $tx;
}
.prescription-list {
padding: 16px 24px;
}
// PageShell 已接管min-height, background, padding
// SearchSection 已接管search-bar
// ContentCard 已接管prescription-card 背景/圆角/阴影/触摸反馈
// StatusTag 已接管status-tag 标签样式
// PaginationBar 已接管pagination 分页样式
.prescription-count {
font-size: var(--tk-font-h2);
color: $tx3;
padding: 8px 0 16px;
margin-bottom: 16px;
text {
font-size: var(--tk-font-h2);
color: $tx3;
}
}
.prescription-card {
background: $card;
border-radius: $r;
padding: 24px;
margin-bottom: 16px;
box-shadow: $shadow-sm;
&:active {
box-shadow: $shadow-md;
}
.prescription-cards {
display: flex;
flex-direction: column;
gap: var(--tk-gap-md);
}
.prescription-card__header {
@@ -55,20 +34,6 @@
color: $tx;
}
.status-tag {
display: inline-block;
padding: 4px 12px;
border-radius: $r-xs;
font-size: var(--tk-font-body);
background: $bd-l;
color: $tx3;
&--active {
background: $acc-l;
color: $acc;
}
}
.prescription-card__body {
display: flex;
gap: 16px;
@@ -88,31 +53,6 @@
font-variant-numeric: tabular-nums;
}
.pagination {
display: flex;
justify-content: center;
align-items: center;
padding: 16px 0;
gap: 24px;
}
.page-btn {
padding: 12px 24px;
background: $card;
border-radius: $r-sm;
font-size: var(--tk-font-h1);
color: $pri;
&--disabled {
opacity: 0.4;
}
}
.page-info {
font-size: var(--tk-font-h2);
color: $tx2;
}
.fab {
position: fixed;
right: 32px;

View File

@@ -1,15 +1,20 @@
import { useState, useEffect, useCallback, useRef } from 'react';
import { View, Text, Input, ScrollView } from '@tarojs/components';
import { View, Text } from '@tarojs/components';
import Taro, { useRouter } from '@tarojs/taro';
import { usePageData } from '@/hooks/usePageData';
import { listDialysisPrescriptions, type DialysisPrescription } from '@/services/doctor/dialysis';
import { listPatients } from '@/services/doctor/patient';
import Loading from '@/components/Loading';
import PageShell from '@/components/ui/PageShell';
import ContentCard from '@/components/ui/ContentCard';
import StatusTag from '@/components/ui/StatusTag';
import LoadingCard from '@/components/ui/LoadingCard';
import SearchSection from '@/components/patterns/SearchSection';
import PaginationBar from '@/components/patterns/PaginationBar';
import SegmentTabs from '@/components/SegmentTabs';
import ErrorState from '@/components/ErrorState';
import EmptyState from '@/components/EmptyState';
import { useElderClass } from '../../../hooks/useElderClass';
import { safeNavigateTo } from '@/utils/navigate';
import SegmentTabs from '@/components/SegmentTabs';
import './index.scss';
const TABS = [
@@ -18,6 +23,11 @@ const TABS = [
{ key: 'inactive', label: '已停用' },
];
const STATUS_LABEL: Record<string, string> = {
active: '生效中',
inactive: '已停用',
};
export default function PrescriptionList() {
const router = useRouter();
const patientId = router.params.patientId || '';
@@ -82,80 +92,74 @@ export default function PrescriptionList() {
}
};
if (loading && prescriptions.length === 0) return <Loading />;
const handleTab = (key: string) => {
setActiveTab(key);
setPage(1);
};
if (loading && prescriptions.length === 0) return <LoadingCard count={3} />;
if (error) return <ErrorState onRetry={() => loadData(1)} />;
return (
<ScrollView scrollY className={`prescription-page ${modeClass}`}>
{!patientId && (
<View className='search-bar'>
<Input
className='search-input'
placeholder='搜索患者姓名'
value={searchPatient}
onInput={(e) => setSearchPatient(e.detail.value)}
confirmType='search'
onConfirm={handleSearch}
/>
</View>
<PageShell safeBottom className={modeClass}>
{!patientId ? (
<SearchSection
value={searchPatient}
onChange={setSearchPatient}
onSearch={handleSearch}
placeholder="搜索患者姓名"
filters={TABS}
activeFilter={activeTab}
onFilterChange={handleTab}
/>
) : (
<SegmentTabs tabs={TABS} activeKey={activeTab} onChange={handleTab} variant="underline" />
)}
<SegmentTabs tabs={TABS} activeKey={activeTab} onChange={(key) => { setActiveTab(key); setPage(1); }} variant="underline" />
{prescriptions.length === 0 ? (
<EmptyState text='暂无透析处方' />
<EmptyState text="暂无透析处方" />
) : (
<View className='prescription-list'>
<View className='prescription-count'><Text> {total} </Text></View>
{prescriptions.map((p) => (
<View
key={p.id}
className='prescription-card'
onClick={() => safeNavigateTo(`/pages/pkg-doctor-clinical/prescription/detail/index?id=${p.id}`)}
>
<View className='prescription-card__header'>
<Text className='prescription-card__model'>{p.dialyzer_model || '透析处方'}</Text>
<Text className={`status-tag status-tag--${p.status}`}>
{p.status === 'active' ? '生效中' : p.status === 'inactive' ? '已停用' : p.status}
</Text>
</View>
<View className='prescription-card__body'>
{p.frequency_per_week != null && (
<Text className='prescription-card__meta'>{p.frequency_per_week}/</Text>
)}
{p.duration_minutes != null && (
<Text className='prescription-card__meta'>{p.duration_minutes}</Text>
)}
</View>
{(p.effective_from || p.effective_to) && (
<Text className='prescription-card__date'>
{p.effective_from || '...'} ~ {p.effective_to || '...'}
</Text>
)}
</View>
))}
{total > 20 && (
<View className='pagination'>
<View
className={`page-btn ${page <= 1 ? 'page-btn--disabled' : ''}`}
onClick={() => page > 1 && loadData(page - 1)}
<>
<View className="prescription-count">
<Text> {total} </Text>
</View>
<View className="prescription-cards">
{prescriptions.map((p) => (
<ContentCard
key={p.id}
onPress={() => safeNavigateTo(`/pages/pkg-doctor-clinical/prescription/detail/index?id=${p.id}`)}
>
<Text></Text>
</View>
<Text className='page-info'>{page} / {Math.ceil(total / 20)}</Text>
<View
className={`page-btn ${page * 20 >= total ? 'page-btn--disabled' : ''}`}
onClick={() => page * 20 < total && loadData(page + 1)}
>
<Text></Text>
</View>
</View>
)}
</View>
<View className="prescription-card__header">
<Text className="prescription-card__model">{p.dialyzer_model || '透析处方'}</Text>
<StatusTag status={p.status} size="sm">{STATUS_LABEL[p.status] || p.status}</StatusTag>
</View>
<View className="prescription-card__body">
{p.frequency_per_week != null && (
<Text className="prescription-card__meta">{p.frequency_per_week}/</Text>
)}
{p.duration_minutes != null && (
<Text className="prescription-card__meta">{p.duration_minutes}</Text>
)}
</View>
{(p.effective_from || p.effective_to) && (
<Text className="prescription-card__date">
{p.effective_from || '...'} ~ {p.effective_to || '...'}
</Text>
)}
</ContentCard>
))}
</View>
<PaginationBar
current={page}
total={total}
pageSize={20}
onChange={(p) => loadData(p)}
/>
</>
)}
<View
className='fab'
className="fab"
onClick={() => {
if (!currentPatientId) {
Taro.showToast({ title: '请先选择患者', icon: 'none' });
@@ -164,8 +168,8 @@ export default function PrescriptionList() {
safeNavigateTo(`/pages/pkg-doctor-clinical/prescription/create/index?patientId=${currentPatientId}`);
}}
>
<Text className='fab-text'>+</Text>
<Text className="fab-text">+</Text>
</View>
</ScrollView>
</PageShell>
);
}