55 个文件中 82 处空 catch 块添加模块前缀日志输出: - stores: auth/health/points (7 处) - services: request/ai-chat/health/ble/* (10 处) - hooks: useLongPolling/usePagination (3 处) - pages: 核心+子包共 35 个页面 (62 处) 保留静默的 catch: secure-storage fallback、Storage 恢复、 analytics 防洪、BLE 断连清理、用户拒绝订阅等合理忽略场景
178 lines
6.1 KiB
TypeScript
178 lines
6.1 KiB
TypeScript
import { useState, useEffect, useCallback, useRef } from 'react';
|
||
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 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 { useDoctorClass } from '@/hooks/useDoctorClass';
|
||
import { safeNavigateTo } from '@/utils/navigate';
|
||
import './index.scss';
|
||
|
||
const TABS = [
|
||
{ key: '', label: '全部' },
|
||
{ key: 'active', label: '生效中' },
|
||
{ key: 'inactive', label: '已停用' },
|
||
];
|
||
|
||
const STATUS_LABEL: Record<string, string> = {
|
||
active: '生效中',
|
||
inactive: '已停用',
|
||
};
|
||
|
||
export default function PrescriptionList() {
|
||
const router = useRouter();
|
||
const patientId = router.params.patientId || '';
|
||
const modeClass = useDoctorClass();
|
||
const [searchPatient, setSearchPatient] = useState('');
|
||
const [currentPatientId, setCurrentPatientId] = useState(patientId);
|
||
const [activeTab, setActiveTab] = useState('');
|
||
const [prescriptions, setPrescriptions] = useState<DialysisPrescription[]>([]);
|
||
const [loading, setLoading] = useState(false);
|
||
const [error, setError] = useState(false);
|
||
const [total, setTotal] = useState(0);
|
||
const [page, setPage] = useState(1);
|
||
const mountedRef = useRef(false);
|
||
|
||
const loadData = useCallback(async (p: number) => {
|
||
setLoading(true);
|
||
setError(false);
|
||
try {
|
||
const res = await listDialysisPrescriptions({
|
||
patient_id: currentPatientId || undefined,
|
||
status: activeTab || undefined,
|
||
page: p,
|
||
page_size: 20,
|
||
});
|
||
setPrescriptions(res.data || []);
|
||
setTotal(res.total || 0);
|
||
setPage(p);
|
||
} catch (err) {
|
||
console.warn('[doctor-prescription] 加载数据失败:', err);
|
||
setError(true);
|
||
Taro.showToast({ title: '加载失败', icon: 'none' });
|
||
} finally {
|
||
setLoading(false);
|
||
}
|
||
}, [currentPatientId, activeTab]);
|
||
|
||
usePageData(
|
||
useCallback(() => loadData(1), [loadData]),
|
||
);
|
||
|
||
// tab/patientId 变化时重新加载(跳过首次 mount,由 usePageData 的 useDidShow 处理)
|
||
useEffect(() => {
|
||
if (mountedRef.current) {
|
||
loadData(1);
|
||
}
|
||
mountedRef.current = true;
|
||
}, [currentPatientId, activeTab, loadData]);
|
||
|
||
const handleSearch = async () => {
|
||
if (!searchPatient.trim()) return;
|
||
setLoading(true);
|
||
try {
|
||
const res = await listPatients({ search: searchPatient.trim(), page: 1, page_size: 1 });
|
||
if (res.data && res.data.length > 0) {
|
||
setCurrentPatientId(res.data[0].id);
|
||
} else {
|
||
Taro.showToast({ title: '未找到患者', icon: 'none' });
|
||
}
|
||
} catch (err) {
|
||
console.warn('[doctor-prescription] 搜索失败:', err);
|
||
Taro.showToast({ title: '搜索失败', icon: 'none' });
|
||
} finally {
|
||
setLoading(false);
|
||
}
|
||
};
|
||
|
||
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 (
|
||
<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" />
|
||
)}
|
||
|
||
{prescriptions.length === 0 ? (
|
||
<EmptyState text="暂无透析处方" />
|
||
) : (
|
||
<>
|
||
<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}`)}
|
||
>
|
||
<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"
|
||
onClick={() => {
|
||
if (!currentPatientId) {
|
||
Taro.showToast({ title: '请先选择患者', icon: 'none' });
|
||
return;
|
||
}
|
||
safeNavigateTo(`/pages/pkg-doctor-clinical/prescription/create/index?patientId=${currentPatientId}`);
|
||
}}
|
||
>
|
||
<Text className="fab-text">+</Text>
|
||
</View>
|
||
</PageShell>
|
||
);
|
||
}
|