feat(clinic): 添加患者头像和处方图片字段

- 在患者用户模型中添加 avatar 字段
- 在处方模型中添加 image 字段
- 更新新增诊所订单页面的表单结构
- 调整图片上传组件的样式和逻辑
- 优化表单验证规则和提示信息- 修改药方信息展示方式
- 更新页面跳转和数据获取逻辑
This commit is contained in:
2025-10-23 21:16:14 +08:00
parent 1d1693cfc1
commit 18940c8fd6
3 changed files with 124 additions and 155 deletions

View File

@@ -12,6 +12,8 @@ export interface ClinicPatientUser {
userId?: number; userId?: number;
// 姓名 // 姓名
realName?: string; realName?: string;
// 头像
avatar?: string;
// 年龄 // 年龄
age?: string; age?: string;
// 性别 // 性别

View File

@@ -24,6 +24,8 @@ export interface ClinicPrescription {
treatmentPlan?: string; treatmentPlan?: string;
// 煎药说明 // 煎药说明
decoctionInstructions?: string; decoctionInstructions?: string;
// 图片
image?: string;
// 订单总金额 // 订单总金额
orderPrice?: string; orderPrice?: string;
// 单价 // 单价

View File

@@ -1,16 +1,25 @@
import {useEffect, useState, useRef} from "react"; import {useEffect, useState, useRef} from "react";
import {useRouter} from '@tarojs/taro' import {useRouter} from '@tarojs/taro'
import {Loading, CellGroup, Input, Form, Cell,Button, Avatar, Tag, TextArea, ImagePreview} from '@nutui/nutui-react-taro' import {
Loading,
Button,
Form,
Cell,
Avatar,
Input,
Space,
TextArea
} from '@nutui/nutui-react-taro'
import {ArrowRight} from '@nutui/icons-react-taro' import {ArrowRight} from '@nutui/icons-react-taro'
import {View, Text} from '@tarojs/components' import {View, Text} from '@tarojs/components'
import Taro from '@tarojs/taro' import Taro from '@tarojs/taro'
import FixedButton from "@/components/FixedButton"; import FixedButton from "@/components/FixedButton";
import navTo from "@/utils/common"; import navTo from "@/utils/common";
import {getUser} from "@/api/system/user";
import {User} from "@/api/system/user/model";
import {ClinicPatientUser} from "@/api/clinic/clinicPatientUser/model"; import {ClinicPatientUser} from "@/api/clinic/clinicPatientUser/model";
import {ClinicPrescription} from "@/api/clinic/clinicPrescription/model"; import {ClinicPrescription} from "@/api/clinic/clinicPrescription/model";
import {TenantId} from "@/config/app"; import {TenantId} from "@/config/app";
import {getClinicPatientUser} from "@/api/clinic/clinicPatientUser";
import {addClinicOrder} from "@/api/clinic/clinicOrder";
// 图片数据接口 // 图片数据接口
interface UploadedImageData { interface UploadedImageData {
@@ -24,24 +33,24 @@ interface UploadedImageData {
const AddClinicOrder = () => { const AddClinicOrder = () => {
const {params} = useRouter(); const {params} = useRouter();
const [toUser, setToUser] = useState<User>() const [toUser, setToUser] = useState<ClinicPatientUser>()
const [loading, setLoading] = useState<boolean>(false) const [loading, setLoading] = useState<boolean>(false)
const formRef = useRef<any>(null) const formRef = useRef<any>()
const [disabled, setDisabled] = useState<boolean>(false)
const [fileList, setFileList] = useState<UploadedImageData[]>([]) // 图片文件列表 const [fileList, setFileList] = useState<UploadedImageData[]>([]) // 图片文件列表
const [showBdImgPreview, setShowBdImgPreview] = useState(false)
const [fileList2, setFileList2] = useState<UploadedImageData[]>([]) // 图片文件列表
// 患者和处方状态 // 患者和处方状态
const [selectedPatient, setSelectedPatient] = useState<ClinicPatientUser | null>(null) const [selectedPatient, setSelectedPatient] = useState<ClinicPatientUser>()
const [selectedPrescription, setSelectedPrescription] = useState<ClinicPrescription | null>(null) const [selectedPrescription, setSelectedPrescription] = useState<ClinicPrescription>()
// 表单数据 // 表单数据
const [formData, setFormData] = useState({ const [formData, setFormData] = useState<ClinicPrescription>({
userId: undefined,
doctorId: undefined,
diagnosis: '', diagnosis: '',
treatmentPlan: '', treatmentPlan: '',
orderPrice: '',
decoctionInstructions: '', decoctionInstructions: '',
content: '', items: [],
image: '' // 添加image字段 image: '' // 添加image字段
}) })
@@ -51,7 +60,7 @@ const AddClinicOrder = () => {
const reload = async () => { const reload = async () => {
if (toUserId) { if (toUserId) {
getUser(Number(toUserId)).then(data => { getClinicPatientUser(Number(toUserId)).then(data => {
setToUser(data) setToUser(data)
}) })
} }
@@ -60,6 +69,8 @@ const AddClinicOrder = () => {
// 设置选中的患者(供其他页面调用) // 设置选中的患者(供其他页面调用)
// @ts-ignore // @ts-ignore
const setSelectedPatientFunc = (patient: ClinicPatientUser) => { const setSelectedPatientFunc = (patient: ClinicPatientUser) => {
console.log('设置选中的患者:', patient)
setToUser(patient)
setSelectedPatient(patient) setSelectedPatient(patient)
} }
@@ -77,7 +88,6 @@ const AddClinicOrder = () => {
})) }))
} }
// 选择并上传图片 // 选择并上传图片
const handleChooseImage = () => { const handleChooseImage = () => {
if (fileList.length >= 5) { // 修正最大图片数量为5 if (fileList.length >= 5) { // 修正最大图片数量为5
@@ -218,10 +228,16 @@ const AddClinicOrder = () => {
return false; return false;
} }
// 判断内容是否为空 if (!values.diagnosis) {
if (!values.content) {
Taro.showToast({ Taro.showToast({
title: `请输入消息内容`, title: `请输入诊断结果`,
icon: 'error'
});
return false;
}
if (!values.treatmentPlan) {
Taro.showToast({
title: `请输入治疗方案`,
icon: 'error' icon: 'error'
}); });
return false; return false;
@@ -252,7 +268,7 @@ const AddClinicOrder = () => {
} }
// 执行新增或更新操作 // 执行新增或更新操作
// await addClinicOrder({}); await addClinicOrder({});
Taro.showToast({ Taro.showToast({
title: `发送成功`, title: `发送成功`,
@@ -327,70 +343,73 @@ const AddClinicOrder = () => {
return ( return (
<> <>
{/* 显示已选择的用户(如果有的话) */} {/* 显示已选择的用户(如果有的话) */}
{toUser && ( <Cell title={(
<Cell title={( <View className={'flex items-center'}>
<View className={'flex items-center'}> <Avatar src={toUser?.avatar}/>
<Avatar src={toUser.avatar}/> <View className={'ml-2 flex flex-col'}>
<View className={'ml-2 flex flex-col'}> <Text>{toUser?.realName || '请选择'}</Text>
<Text>{toUser.alias || toUser.nickname}</Text> <Text className={'text-gray-300'}>{toUser?.phone}</Text>
<Text className={'text-gray-300'}>{toUser.mobile}</Text>
</View>
</View> </View>
)} extra={( </View>
<ArrowRight color="#cccccc" className={'mt-2'} size={20}/> )} extra={(
)}/> <ArrowRight color="#cccccc" className={'mt-2'} size={20}/>
)} )} onClick={() => navTo(`/doctor/orders/selectPatient`, true)}
{/* 选择患者 */}
<Cell
title="选择患者"
extra={selectedPatient ? (
<View className={'flex items-center'}>
<Text className={'mr-2'}>{selectedPatient.realName || '未知患者'}</Text>
<ArrowRight color="#cccccc" size={18}/>
</View>
) : (
<Text className={'text-gray-400'}></Text>
)}
onClick={() => navTo(`/doctor/orders/selectPatient`, true)}
/> />
{/* 诊断结果 */} {toUser && (
<CellGroup> <Form
<Cell title="诊断结果"> ref={formRef}
<TextArea divider
value={formData.diagnosis} initialValues={formData}
onChange={(value) => handleFormChange('diagnosis', value)} labelPosition={'top'}
placeholder="股骨头坏死" onFinish={(values) => submitSucceed(values)}
rows={2} onFinishFailed={(errors) => submitFailed(errors)}
maxLength={100} >
/> {/* 基本信息 */}
</Cell> <Form.Item
<Cell title="治疗方案"> name="diagnosis"
<TextArea label={'诊断结果'}
value={formData.treatmentPlan} required
onChange={(value) => handleFormChange('treatmentPlan', value)} rules={[{required: true, message: '请填写诊断结果'}]}
placeholder="请填写治疗方案" initialValue={formData.diagnosis}
rows={2} >
maxLength={100} <Input placeholder="请输入诊断结果" style={{
/> backgroundColor: '#f9f9f9',
</Cell> padding: '4px',
<Cell title="添加图片" extra={'可上传病例/舌苔/面相等'}> }}/>
</Form.Item>
<Form.Item
name="treatmentPlan"
label={'治疗方案'}
required
rules={[{required: true, message: '请填写治疗方案'}]}
initialValue={formData.treatmentPlan}
>
<TextArea
value={formData.treatmentPlan}
onChange={(value) => handleFormChange('treatmentPlan', value)}
placeholder="请填写治疗方案"
style={{
backgroundColor: '#f9f9f9',
padding: '4px',
}}
/>
</Form.Item>
<Form.Item <Form.Item
label={'拍照上传'} label={'拍照上传'}
name="image" name="image"
required
rules={[{message: '请上传照片'}]} rules={[{message: '请上传照片'}]}
> >
<div style={{ <View style={{
display: 'flex', display: 'flex',
flexDirection: 'row',
flexWrap: 'wrap', flexWrap: 'wrap',
gap: '12px', gap: '8px',
padding: '8px 0' padding: '0'
}}> }}>
{/* 显示已上传的图片 */} {/* 显示已上传的图片 */}
{fileList.map((file) => ( {fileList.map((file) => (
<div key={file.uid} style={{ <View key={file.uid} style={{
position: 'relative', position: 'relative',
width: '80px', width: '80px',
height: '80px', height: '80px',
@@ -426,12 +445,12 @@ const AddClinicOrder = () => {
> >
× ×
</Button> </Button>
</div> </View>
))} ))}
{/* 添加图片按钮 */} {/* 添加图片按钮 */}
{fileList.length < 5 && ( {fileList.length < 5 && (
<div <View
onClick={handleChooseImage} onClick={handleChooseImage}
style={{ style={{
width: '80px', width: '80px',
@@ -448,27 +467,19 @@ const AddClinicOrder = () => {
> >
<span style={{fontSize: '20px', color: '#d9d9d9'}}>+</span> <span style={{fontSize: '20px', color: '#d9d9d9'}}>+</span>
<span style={{fontSize: '10px', marginTop: '2px', color: '#666'}}> <span style={{fontSize: '10px', marginTop: '2px', color: '#666'}}>
</span> </span>
</div> </View>
)} )}
</View>
{/* 显示上传数量提示 */} <View className="text-xs text-gray-500">
{fileList.length > 0 && ( //{fileList.length}
<div style={{ </View>
width: '100%',
fontSize: '12px',
color: '#52c41a',
textAlign: 'center',
marginTop: '4px'
}}>
{fileList.length}5
</div>
)}
</div>
</Form.Item> </Form.Item>
</Cell> </Form>
</CellGroup> )}
{/* 选择处方 */} {/* 选择处方 */}
<Cell <Cell
@@ -483,82 +494,36 @@ const AddClinicOrder = () => {
)} )}
onClick={() => navTo(`/doctor/orders/selectPrescription`, true)} onClick={() => navTo(`/doctor/orders/selectPrescription`, true)}
/> />
{/* 药方信息 */} {/* 药方信息 */}
{selectedPrescription && ( {selectedPrescription && (
<CellGroup> <>
<Cell title="药方信息"> <Cell extra={'药方信息'}>
<View className={'flex flex-wrap'}> <View className={'flex flex-col'}>
{selectedPrescription.items?.map(item => ( <View className={'py-3'}>RP: {selectedPrescription.prescriptionType === 0 ? '中药' : '西药'} {selectedPrescription.items?.length}{selectedPrescription.orderPrice}</View>
<Tag key={item.id} className={'mr-2 mb-2'}>{item.medicineName}</Tag> <Space className={'flex flex-wrap'}>
))} {selectedPrescription.items?.map(item => (
<Button>{item.medicineName} 105</Button>
))}
</Space>
</View> </View>
</Cell> </Cell>
{/* 煎药说明 */} {/* 煎药说明 */}
<Cell title="煎药说明"> <TextArea
<TextArea value={formData.decoctionInstructions}
value={formData.decoctionInstructions} onChange={(value) => handleFormChange('decoctionInstructions', value)}
onChange={(value) => handleFormChange('decoctionInstructions', value)} placeholder="请填写用药说明"
placeholder="请填写煎药说明" rows={2}
rows={2} maxLength={200}
maxLength={200} />
/> </>
</Cell>
</CellGroup>
)} )}
{/* 消息内容 */}
<CellGroup>
<Cell title="消息内容">
<TextArea
value={formData.content}
onChange={(value) => handleFormChange('content', value)}
placeholder="请填写要发送的消息内容"
rows={3}
maxLength={300}
/>
</Cell>
</CellGroup>
<Form
ref={formRef}
divider
initialValues={formData}
labelPosition="left"
onFinish={(values) => submitSucceed(values)}
onFinishFailed={(errors) => submitFailed(errors)}
>
<CellGroup style={{padding: '4px 0'}}>
<Form.Item name="diagnosis" initialValue={formData.diagnosis}>
<Input type="hidden"/>
</Form.Item>
<Form.Item name="treatmentPlan" initialValue={formData.treatmentPlan}>
<Input type="hidden"/>
</Form.Item>
<Form.Item name="decoctionInstructions" initialValue={formData.decoctionInstructions}>
<Input type="hidden"/>
</Form.Item>
<Form.Item name="content" initialValue={formData.content} required>
<Input type="hidden"/>
</Form.Item>
<Form.Item name="image" initialValue={formData.image}>
<Input type="hidden"/>
</Form.Item>
</CellGroup>
</Form>
{/* 底部浮动按钮 */} {/* 底部浮动按钮 */}
<FixedButton text={'立即发送'} onClick={() => formRef.current?.submit()}/> <FixedButton text={'生成处方并发送给患者'} onClick={() => formRef.current?.submit()}/>
<ImagePreview
autoPlay
images={fileList2.filter(file => file.src).map(file => ({ src: file.src! }))}
visible={showBdImgPreview}
onClose={() => setShowBdImgPreview(false)}
/>
</> </>
); );
}; };
export default AddClinicOrder; export default AddClinicOrder;