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:
@@ -1,45 +1,24 @@
|
|||||||
@import '../../../styles/variables.scss';
|
@import '../../../styles/variables.scss';
|
||||||
@import '../../../styles/mixins.scss';
|
|
||||||
|
|
||||||
.prescription-page {
|
// PageShell 已接管:min-height, background, padding
|
||||||
min-height: 100vh;
|
// SearchSection 已接管:search-bar
|
||||||
background: $bg;
|
// ContentCard 已接管:prescription-card 背景/圆角/阴影/触摸反馈
|
||||||
padding-bottom: 120px;
|
// StatusTag 已接管:status-tag 标签样式
|
||||||
}
|
// PaginationBar 已接管:pagination 分页样式
|
||||||
|
|
||||||
.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;
|
|
||||||
}
|
|
||||||
|
|
||||||
.prescription-count {
|
.prescription-count {
|
||||||
font-size: var(--tk-font-h2);
|
margin-bottom: 16px;
|
||||||
color: $tx3;
|
|
||||||
padding: 8px 0 16px;
|
text {
|
||||||
|
font-size: var(--tk-font-h2);
|
||||||
|
color: $tx3;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
.prescription-card {
|
.prescription-cards {
|
||||||
background: $card;
|
display: flex;
|
||||||
border-radius: $r;
|
flex-direction: column;
|
||||||
padding: 24px;
|
gap: var(--tk-gap-md);
|
||||||
margin-bottom: 16px;
|
|
||||||
box-shadow: $shadow-sm;
|
|
||||||
|
|
||||||
&:active {
|
|
||||||
box-shadow: $shadow-md;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
.prescription-card__header {
|
.prescription-card__header {
|
||||||
@@ -55,20 +34,6 @@
|
|||||||
color: $tx;
|
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 {
|
.prescription-card__body {
|
||||||
display: flex;
|
display: flex;
|
||||||
gap: 16px;
|
gap: 16px;
|
||||||
@@ -88,31 +53,6 @@
|
|||||||
font-variant-numeric: tabular-nums;
|
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 {
|
.fab {
|
||||||
position: fixed;
|
position: fixed;
|
||||||
right: 32px;
|
right: 32px;
|
||||||
|
|||||||
@@ -1,15 +1,20 @@
|
|||||||
import { useState, useEffect, useCallback, useRef } from 'react';
|
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 Taro, { useRouter } from '@tarojs/taro';
|
||||||
import { usePageData } from '@/hooks/usePageData';
|
import { usePageData } from '@/hooks/usePageData';
|
||||||
import { listDialysisPrescriptions, type DialysisPrescription } from '@/services/doctor/dialysis';
|
import { listDialysisPrescriptions, type DialysisPrescription } from '@/services/doctor/dialysis';
|
||||||
import { listPatients } from '@/services/doctor/patient';
|
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 ErrorState from '@/components/ErrorState';
|
||||||
import EmptyState from '@/components/EmptyState';
|
import EmptyState from '@/components/EmptyState';
|
||||||
import { useElderClass } from '../../../hooks/useElderClass';
|
import { useElderClass } from '../../../hooks/useElderClass';
|
||||||
import { safeNavigateTo } from '@/utils/navigate';
|
import { safeNavigateTo } from '@/utils/navigate';
|
||||||
import SegmentTabs from '@/components/SegmentTabs';
|
|
||||||
import './index.scss';
|
import './index.scss';
|
||||||
|
|
||||||
const TABS = [
|
const TABS = [
|
||||||
@@ -18,6 +23,11 @@ const TABS = [
|
|||||||
{ key: 'inactive', label: '已停用' },
|
{ key: 'inactive', label: '已停用' },
|
||||||
];
|
];
|
||||||
|
|
||||||
|
const STATUS_LABEL: Record<string, string> = {
|
||||||
|
active: '生效中',
|
||||||
|
inactive: '已停用',
|
||||||
|
};
|
||||||
|
|
||||||
export default function PrescriptionList() {
|
export default function PrescriptionList() {
|
||||||
const router = useRouter();
|
const router = useRouter();
|
||||||
const patientId = router.params.patientId || '';
|
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)} />;
|
if (error) return <ErrorState onRetry={() => loadData(1)} />;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<ScrollView scrollY className={`prescription-page ${modeClass}`}>
|
<PageShell safeBottom className={modeClass}>
|
||||||
{!patientId && (
|
{!patientId ? (
|
||||||
<View className='search-bar'>
|
<SearchSection
|
||||||
<Input
|
value={searchPatient}
|
||||||
className='search-input'
|
onChange={setSearchPatient}
|
||||||
placeholder='搜索患者姓名'
|
onSearch={handleSearch}
|
||||||
value={searchPatient}
|
placeholder="搜索患者姓名"
|
||||||
onInput={(e) => setSearchPatient(e.detail.value)}
|
filters={TABS}
|
||||||
confirmType='search'
|
activeFilter={activeTab}
|
||||||
onConfirm={handleSearch}
|
onFilterChange={handleTab}
|
||||||
/>
|
/>
|
||||||
</View>
|
) : (
|
||||||
|
<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 ? (
|
{prescriptions.length === 0 ? (
|
||||||
<EmptyState text='暂无透析处方' />
|
<EmptyState text="暂无透析处方" />
|
||||||
) : (
|
) : (
|
||||||
<View className='prescription-list'>
|
<>
|
||||||
<View className='prescription-count'><Text>共 {total} 条处方</Text></View>
|
<View className="prescription-count">
|
||||||
{prescriptions.map((p) => (
|
<Text>共 {total} 条处方</Text>
|
||||||
<View
|
</View>
|
||||||
key={p.id}
|
<View className="prescription-cards">
|
||||||
className='prescription-card'
|
{prescriptions.map((p) => (
|
||||||
onClick={() => safeNavigateTo(`/pages/pkg-doctor-clinical/prescription/detail/index?id=${p.id}`)}
|
<ContentCard
|
||||||
>
|
key={p.id}
|
||||||
<View className='prescription-card__header'>
|
onPress={() => safeNavigateTo(`/pages/pkg-doctor-clinical/prescription/detail/index?id=${p.id}`)}
|
||||||
<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)}
|
|
||||||
>
|
>
|
||||||
<Text>上一页</Text>
|
<View className="prescription-card__header">
|
||||||
</View>
|
<Text className="prescription-card__model">{p.dialyzer_model || '透析处方'}</Text>
|
||||||
<Text className='page-info'>{page} / {Math.ceil(total / 20)}</Text>
|
<StatusTag status={p.status} size="sm">{STATUS_LABEL[p.status] || p.status}</StatusTag>
|
||||||
<View
|
</View>
|
||||||
className={`page-btn ${page * 20 >= total ? 'page-btn--disabled' : ''}`}
|
<View className="prescription-card__body">
|
||||||
onClick={() => page * 20 < total && loadData(page + 1)}
|
{p.frequency_per_week != null && (
|
||||||
>
|
<Text className="prescription-card__meta">{p.frequency_per_week}次/周</Text>
|
||||||
<Text>下一页</Text>
|
)}
|
||||||
</View>
|
{p.duration_minutes != null && (
|
||||||
</View>
|
<Text className="prescription-card__meta">每次{p.duration_minutes}分钟</Text>
|
||||||
)}
|
)}
|
||||||
</View>
|
</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
|
<View
|
||||||
className='fab'
|
className="fab"
|
||||||
onClick={() => {
|
onClick={() => {
|
||||||
if (!currentPatientId) {
|
if (!currentPatientId) {
|
||||||
Taro.showToast({ title: '请先选择患者', icon: 'none' });
|
Taro.showToast({ title: '请先选择患者', icon: 'none' });
|
||||||
@@ -164,8 +168,8 @@ export default function PrescriptionList() {
|
|||||||
safeNavigateTo(`/pages/pkg-doctor-clinical/prescription/create/index?patientId=${currentPatientId}`);
|
safeNavigateTo(`/pages/pkg-doctor-clinical/prescription/create/index?patientId=${currentPatientId}`);
|
||||||
}}
|
}}
|
||||||
>
|
>
|
||||||
<Text className='fab-text'>+</Text>
|
<Text className="fab-text">+</Text>
|
||||||
</View>
|
</View>
|
||||||
</ScrollView>
|
</PageShell>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user