feat(clinic): 新增患者管理功能模块

- 新增患者管理页面,支持查看和管理患者信息
- 添加患者报备功能,支持新增和编辑患者资料- 实现患者列表展示,包含搜索、筛选和分页功能
- 支持患者状态跟踪和保护期计算- 集成电话拨打和号码复制功能
- 添加处方管理相关数据模型和接口定义
- 更新环境配置中的API地址指向新的生产环境- 优化医生端主页UI布局和功能导航
- 调整部分字段命名以提高代码一致性
-修复医生认证状态下的文案提示问题
This commit is contained in:
2025-10-23 14:20:08 +08:00
parent 1873299eb8
commit 77c2df8d1f
24 changed files with 1890 additions and 675 deletions

View File

@@ -0,0 +1,236 @@
import {useState, useEffect, useCallback} from 'react'
import {View, Text} from '@tarojs/components'
import Taro, {useDidShow} from '@tarojs/taro'
import {Loading, InfiniteLoading, Empty, Space, SearchBar, Button} from '@nutui/nutui-react-taro'
import {
pageClinicPrescription
} from "@/api/clinic/clinicPrescription";
import {ClinicPrescription} from "@/api/clinic/clinicPrescription/model";
const SelectPrescription = () => {
const [list, setList] = useState<ClinicPrescription[]>([])
const [loading, setLoading] = useState<boolean>(false)
const [searchValue, setSearchValue] = useState<string>('')
const [displaySearchValue, setDisplaySearchValue] = useState<string>('')
const [page, setPage] = useState(1)
const [hasMore, setHasMore] = useState(true)
// 获取处方数据
const fetchPrescriptionData = useCallback(async (resetPage = false, targetPage?: number) => {
setLoading(true);
try {
const currentPage = resetPage ? 1 : (targetPage || page);
// 构建API参数
const params: any = {
page: currentPage
};
// 添加搜索关键词
if (displaySearchValue.trim()) {
params.keywords = displaySearchValue.trim();
}
const res = await pageClinicPrescription(params);
if (res?.list && res.list.length > 0) {
// 如果是重置页面或第一页,直接设置新数据;否则追加数据
if (resetPage || currentPage === 1) {
setList(res.list);
} else {
setList(prevList => prevList.concat(res.list));
}
// 正确判断是否还有更多数据
const hasMoreData = res.list.length >= 10; // 假设每页10条数据
setHasMore(hasMoreData);
} else {
if (resetPage || currentPage === 1) {
setList([]);
}
setHasMore(false);
}
setPage(currentPage);
} catch (error) {
console.error('获取处方数据失败:', error);
Taro.showToast({
title: '加载失败,请重试',
icon: 'none'
});
} finally {
setLoading(false);
}
}, [page, displaySearchValue]);
const reloadMore = async () => {
if (loading || !hasMore) return; // 防止重复加载
const nextPage = page + 1;
await fetchPrescriptionData(false, nextPage);
}
// 防抖搜索功能
useEffect(() => {
const timer = setTimeout(() => {
setDisplaySearchValue(searchValue);
}, 300); // 300ms 防抖
return () => clearTimeout(timer);
}, [searchValue]);
// 初始化数据
useEffect(() => {
fetchPrescriptionData(true).then();
}, [displaySearchValue]);
// 监听页面显示,当从其他页面返回时刷新数据
useDidShow(() => {
// 刷新数据
setList([]);
setPage(1);
setHasMore(true);
fetchPrescriptionData(true);
});
// 选择处方
const selectPrescription = (prescription: ClinicPrescription) => {
// 将选中的处方信息传递回上一个页面
const pages = Taro.getCurrentPages();
if (pages.length > 1) {
const prevPage = pages[pages.length - 2];
// @ts-ignore
if (prevPage && typeof prevPage.setSelectedPrescription === 'function') {
// @ts-ignore
prevPage.setSelectedPrescription(prescription);
}
}
Taro.navigateBack();
};
// 渲染处方项
const renderPrescriptionItem = (prescription: ClinicPrescription) => (
<View key={prescription.id} className="bg-white rounded-lg p-4 mb-3 shadow-sm">
<View className="flex items-center mb-3">
<View className="flex-1">
<View className="flex items-center justify-between mb-1">
<Text className="font-semibold text-gray-800 mr-2">
: {prescription.orderNo || '无编号'}
</Text>
</View>
<View className="flex items-center mb-1">
<Space direction="vertical">
<Text className="text-xs text-gray-500">
: {prescription.prescriptionType === 0 ? '中药' : prescription.prescriptionType === 1 ? '西药' : '未知'}
</Text>
<Text className="text-xs text-gray-500">
: {prescription.diagnosis || '无'}
</Text>
<Text className="text-xs text-gray-500">
: {prescription.createTime || '未知'}
</Text>
</Space>
</View>
{/* 显示备注字段 */}
<View className="flex items-center">
<Text className="text-xs text-gray-500">: {prescription.comments || '暂无'}</Text>
</View>
</View>
</View>
{/* 选择按钮 */}
<Button
size="small"
onClick={() => selectPrescription(prescription)}
style={{backgroundColor: '#1890ff', color: 'white'}}
>
</Button>
</View>
);
// 渲染处方列表
const renderPrescriptionList = () => {
const isSearching = displaySearchValue.trim().length > 0;
return (
<View className="flex-1">
{/* 搜索结果统计 */}
{isSearching && (
<View className="bg-white px-4 py-2 border-b border-gray-100">
<Text className="text-sm text-gray-600">
"{displaySearchValue}" {list.length}
</Text>
</View>
)}
<View className="p-4" style={{
height: isSearching ? 'calc(90vh - 40px)' : '90vh',
overflowY: 'auto',
overflowX: 'hidden'
}}>
<InfiniteLoading
target="scroll"
hasMore={hasMore}
onLoadMore={reloadMore}
onScroll={() => {
// 滚动事件处理
}}
onScrollToUpper={() => {
// 滚动到顶部事件处理
}}
loadingText={
<>
...
</>
}
loadMoreText={
list.length === 0 ? (
<Empty
style={{backgroundColor: 'transparent'}}
description={loading ? "加载中..." : "暂无处方数据"}
/>
) : (
<View className={'h-3 flex items-center justify-center'}>
<Text className="text-gray-500 text-sm"></Text>
</View>
)
}
>
{loading && list.length === 0 ? (
<View className="flex items-center justify-center py-8">
<Loading/>
<Text className="text-gray-500 mt-2 ml-2">...</Text>
</View>
) : (
list.map(renderPrescriptionItem)
)}
</InfiniteLoading>
</View>
</View>
);
};
return (
<View className="min-h-screen bg-gray-50">
{/* 搜索栏 */}
<View className="bg-white py-2 border-b border-gray-100">
<SearchBar
value={searchValue}
placeholder="搜索处方编号、诊断结果"
onChange={(value) => setSearchValue(value)}
onClear={() => {
setSearchValue('');
setDisplaySearchValue('');
}}
clearable
/>
</View>
{/* 处方列表 */}
{renderPrescriptionList()}
</View>
);
};
export default SelectPrescription;