feat(doctor): 新增医生聊天与订单确认功能
- 在医生聊天页面增加患者与医师列表渲染逻辑 - 新增订单确认页面,支持处方信息预览与发送 - 更新开方页面跳转逻辑,将数据通过本地存储传递至确认页-优化聊天页面初始化逻辑,区分医生与普通用户角色-修复 CMS 文章详情内容判断逻辑,避免空内容解析异常 - 新增关于我们页面,支持富文本内容展示- 调整医生相关模型字段,如添加头像、替换手机号字段为 phone - 补充页面配置文件,完善导航栏标题与样式设置 - 新增医生订单确认页样式文件及详细交互布局 - 提供订单确认页使用说明文档,涵盖功能概述与技术实现细节
This commit is contained in:
322
src/doctor/orders/README.md
Normal file
322
src/doctor/orders/README.md
Normal file
@@ -0,0 +1,322 @@
|
||||
# 医生开方订单确认页面使用说明
|
||||
|
||||
## 📋 功能概述
|
||||
|
||||
医生开方订单确认页面是一个独立的业务页面,用于医生在填写完处方信息后,预览并确认订单详情,然后发送给患者。
|
||||
|
||||
## 🎯 页面路径
|
||||
|
||||
```
|
||||
/doctor/orders/confirm
|
||||
```
|
||||
|
||||
## 📊 页面结构
|
||||
|
||||
### 1. 页面信息展示
|
||||
|
||||
#### 患者信息区
|
||||
- ✅ 患者头像
|
||||
- ✅ 患者姓名
|
||||
- ✅ 性别标签
|
||||
- ✅ 联系电话
|
||||
- ✅ 年龄信息
|
||||
|
||||
#### 诊断信息区
|
||||
- ✅ 诊断结果
|
||||
- ✅ 治疗方案
|
||||
- ✅ 修改按钮(返回编辑)
|
||||
|
||||
#### 处方信息区
|
||||
- ✅ 处方类型(中药/西药)
|
||||
- ✅ 药品数量统计
|
||||
- ✅ 药品明细列表
|
||||
- 药品名称
|
||||
- 单价
|
||||
- 规格
|
||||
- 数量
|
||||
- 小计
|
||||
- ✅ 煎药说明(可选)
|
||||
|
||||
#### 病例图片区(可选)
|
||||
- ✅ 图片网格展示
|
||||
- ✅ 点击预览大图
|
||||
|
||||
#### 费用明细区
|
||||
- ✅ 药品费用
|
||||
- ✅ 服务费
|
||||
- ✅ 订单总计
|
||||
|
||||
#### 温馨提示区
|
||||
- ✅ 处方发送后的流程说明
|
||||
- ✅ 患者支付提醒
|
||||
- ✅ 修改提示
|
||||
|
||||
### 2. 底部操作栏
|
||||
- ✅ 订单总价显示
|
||||
- ✅ 返回修改按钮
|
||||
- ✅ 确认并发送按钮(带loading状态)
|
||||
|
||||
## 🔄 业务流程
|
||||
|
||||
### 完整流程
|
||||
|
||||
```
|
||||
开方页面 (/doctor/orders/add.tsx)
|
||||
↓
|
||||
[填写诊断信息]
|
||||
↓
|
||||
[选择患者]
|
||||
↓
|
||||
[选择处方]
|
||||
↓
|
||||
[上传病例图片]
|
||||
↓
|
||||
[填写煎药说明]
|
||||
↓
|
||||
[点击"下一步:确认订单信息"]
|
||||
↓
|
||||
订单确认页 (/doctor/orders/confirm.tsx)
|
||||
↓
|
||||
[预览所有信息]
|
||||
↓
|
||||
[检查费用明细]
|
||||
↓
|
||||
[点击"确认并发送给患者"]
|
||||
↓
|
||||
调用API创建订单
|
||||
↓
|
||||
发送成功提示
|
||||
↓
|
||||
跳转订单列表 (/doctor/orders/index)
|
||||
```
|
||||
|
||||
### 数据传递方式
|
||||
|
||||
使用 Taro 本地存储传递数据:
|
||||
|
||||
```typescript
|
||||
// add.tsx 传递数据
|
||||
const orderData = {
|
||||
patient: toUser || selectedPatient,
|
||||
prescription: selectedPrescription,
|
||||
diagnosis: values.diagnosis,
|
||||
treatmentPlan: values.treatmentPlan,
|
||||
decoctionInstructions: values.decoctionInstructions,
|
||||
images: fileList,
|
||||
orderPrice: selectedPrescription?.orderPrice || '0.00'
|
||||
}
|
||||
Taro.setStorageSync('tempOrderData', JSON.stringify(orderData))
|
||||
|
||||
// confirm.tsx 接收数据
|
||||
const tempData = Taro.getStorageSync('tempOrderData')
|
||||
const parsedData = JSON.parse(tempData)
|
||||
```
|
||||
|
||||
## 💻 技术实现
|
||||
|
||||
### 主要组件
|
||||
|
||||
```typescript
|
||||
import {
|
||||
Button,
|
||||
Cell,
|
||||
CellGroup,
|
||||
Avatar,
|
||||
Tag,
|
||||
Divider,
|
||||
Image
|
||||
} from '@nutui/nutui-react-taro'
|
||||
```
|
||||
|
||||
### 状态管理
|
||||
|
||||
```typescript
|
||||
const [orderData, setOrderData] = useState<OrderData | null>(null)
|
||||
const [loading, setLoading] = useState<boolean>(false)
|
||||
const [submitLoading, setSubmitLoading] = useState<boolean>(false)
|
||||
```
|
||||
|
||||
### 关键方法
|
||||
|
||||
#### 1. 计算药品总价
|
||||
```typescript
|
||||
const getMedicinePrice = () => {
|
||||
if (!orderData?.prescription?.items) return '0.00'
|
||||
const total = orderData.prescription.items.reduce((sum, item) => {
|
||||
const price = parseFloat(item.unitPrice || '0')
|
||||
const quantity = item.quantity || 1
|
||||
return sum + (price * quantity)
|
||||
}, 0)
|
||||
return total.toFixed(2)
|
||||
}
|
||||
```
|
||||
|
||||
#### 2. 计算订单总价
|
||||
```typescript
|
||||
const getTotalPrice = () => {
|
||||
const medicinePrice = parseFloat(getMedicinePrice())
|
||||
const serviceFee = parseFloat(getServiceFee())
|
||||
return (medicinePrice + serviceFee).toFixed(2)
|
||||
}
|
||||
```
|
||||
|
||||
#### 3. 确认订单
|
||||
```typescript
|
||||
const handleConfirmOrder = async () => {
|
||||
const clinicOrderData = {
|
||||
userId: orderData.patient.userId,
|
||||
doctorId: Taro.getStorageSync('UserId'),
|
||||
type: 0,
|
||||
title: `${orderData.patient.realName}的处方订单`,
|
||||
totalPrice: getTotalPrice(),
|
||||
payPrice: getTotalPrice(),
|
||||
// ... 其他字段
|
||||
}
|
||||
|
||||
await addClinicOrder(clinicOrderData)
|
||||
Taro.removeStorageSync('tempOrderData')
|
||||
|
||||
Taro.redirectTo({
|
||||
url: '/doctor/orders/index'
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
## 🎨 样式特点
|
||||
|
||||
### 配色方案
|
||||
- 主色调: 绿色系 (#059669)
|
||||
- 价格强调: 红色 (#dc2626)
|
||||
- 提示信息: 黄色系 (#d97706)
|
||||
- 背景色: 浅灰 (#f5f5f5)
|
||||
|
||||
### 响应式设计
|
||||
- 采用 Flexbox 布局
|
||||
- 图片采用 Grid 布局(3列)
|
||||
- 底部操作栏固定定位
|
||||
|
||||
### 交互细节
|
||||
- 图片点击预览大图
|
||||
- 修改按钮返回编辑
|
||||
- 提交按钮带 loading 状态
|
||||
- 成功提示后自动跳转
|
||||
|
||||
## 📱 页面配置
|
||||
|
||||
```typescript
|
||||
// confirm.config.ts
|
||||
export default {
|
||||
navigationBarTitleText: '确认处方订单',
|
||||
navigationBarBackgroundColor: '#fff',
|
||||
navigationBarTextStyle: 'black',
|
||||
backgroundColor: '#f5f5f5'
|
||||
}
|
||||
```
|
||||
|
||||
## 🔐 数据类型定义
|
||||
|
||||
```typescript
|
||||
interface OrderData {
|
||||
patient: ClinicPatientUser;
|
||||
prescription?: ClinicPrescription;
|
||||
diagnosis: string;
|
||||
treatmentPlan: string;
|
||||
decoctionInstructions?: string;
|
||||
images?: Array<{
|
||||
url: string;
|
||||
name?: string;
|
||||
uid?: string;
|
||||
}>;
|
||||
orderPrice?: string;
|
||||
}
|
||||
```
|
||||
|
||||
## ✅ 与商城订单确认页的区别
|
||||
|
||||
| 维度 | 商城订单 | 医生开方订单 |
|
||||
|------|---------|------------|
|
||||
| **目标用户** | 普通消费者 | 医生专业人员 |
|
||||
| **业务场景** | 购买商品 | 诊疗开方 |
|
||||
| **关键信息** | 商品、价格、地址、优惠券 | 患者、诊断、处方、药品 |
|
||||
| **支付流程** | 立即支付 | 患者后续支付 |
|
||||
| **地址信息** | 收货地址 | 患者信息 |
|
||||
| **优惠系统** | 支持优惠券 | 无优惠券 |
|
||||
| **配送方式** | 快递/自提 | 无配送选择 |
|
||||
| **特殊功能** | 购物车合并 | 图片上传、煎药说明 |
|
||||
|
||||
## 🚀 使用示例
|
||||
|
||||
### 从开方页面跳转
|
||||
|
||||
```typescript
|
||||
// add.tsx
|
||||
const submitSucceed = async (values: any) => {
|
||||
const orderData = {
|
||||
patient: toUser,
|
||||
prescription: selectedPrescription,
|
||||
diagnosis: values.diagnosis,
|
||||
treatmentPlan: values.treatmentPlan,
|
||||
decoctionInstructions: values.decoctionInstructions,
|
||||
images: fileList,
|
||||
orderPrice: selectedPrescription?.orderPrice
|
||||
}
|
||||
|
||||
Taro.setStorageSync('tempOrderData', JSON.stringify(orderData))
|
||||
|
||||
Taro.navigateTo({
|
||||
url: '/doctor/orders/confirm'
|
||||
})
|
||||
}
|
||||
```
|
||||
|
||||
## 🐛 常见问题
|
||||
|
||||
### 1. 数据加载失败
|
||||
**原因**: 本地存储数据缺失或格式错误
|
||||
**解决**: 检查 `tempOrderData` 是否正确存储,确保 JSON 格式正确
|
||||
|
||||
### 2. 图片无法预览
|
||||
**原因**: 图片 URL 格式不正确
|
||||
**解决**: 确保图片 URL 是完整的网络地址
|
||||
|
||||
### 3. 价格计算错误
|
||||
**原因**: 药品单价或数量字段缺失
|
||||
**解决**: 确保处方明细中的 `unitPrice` 和 `quantity` 字段存在
|
||||
|
||||
### 4. 提交后跳转失败
|
||||
**原因**: 路由路径配置错误
|
||||
**解决**: 检查 `app.config.ts` 中的路由配置是否正确
|
||||
|
||||
## 📝 注意事项
|
||||
|
||||
1. ⚠️ 确保患者信息完整(特别是 userId)
|
||||
2. ⚠️ 处方信息必须包含药品明细
|
||||
3. ⚠️ 服务费金额可根据业务需求调整
|
||||
4. ⚠️ 提交成功后会清除临时数据,无法返回
|
||||
5. ⚠️ 图片上传数量限制为5张
|
||||
|
||||
## 🔧 扩展建议
|
||||
|
||||
### 可优化点
|
||||
1. 添加价格优惠功能
|
||||
2. 支持处方模板
|
||||
3. 添加患者病历查看
|
||||
4. 支持打印处方
|
||||
5. 增加订单草稿保存
|
||||
|
||||
### 待完善功能
|
||||
1. 处方审核流程
|
||||
2. 电子签名
|
||||
3. 处方分享
|
||||
4. 统计报表
|
||||
5. 患者评价
|
||||
|
||||
## 📞 技术支持
|
||||
|
||||
如有问题,请联系开发团队或查阅项目文档。
|
||||
|
||||
---
|
||||
|
||||
**版本**: v1.0.0
|
||||
**创建日期**: 2025-01-28
|
||||
**最后更新**: 2025-01-28
|
||||
@@ -214,7 +214,7 @@ const AddClinicOrder = () => {
|
||||
})
|
||||
}
|
||||
|
||||
// 提交表单
|
||||
// 提交表单 - 修改为跳转到确认页
|
||||
const submitSucceed = async (values: any) => {
|
||||
try {
|
||||
console.log('提交数据:', values)
|
||||
@@ -243,46 +243,31 @@ const AddClinicOrder = () => {
|
||||
return false;
|
||||
}
|
||||
|
||||
// 如果选择了患者,在消息内容中添加患者信息
|
||||
let content = values.content;
|
||||
if (selectedPatient) {
|
||||
content = `[患者: ${selectedPatient.realName || '未知'}] ${content}`;
|
||||
// 构建订单数据
|
||||
const orderData = {
|
||||
patient: toUser || selectedPatient,
|
||||
prescription: selectedPrescription,
|
||||
diagnosis: values.diagnosis,
|
||||
treatmentPlan: values.treatmentPlan,
|
||||
decoctionInstructions: values.decoctionInstructions || formData.decoctionInstructions,
|
||||
images: fileList,
|
||||
orderPrice: selectedPrescription?.orderPrice || '0.00'
|
||||
}
|
||||
|
||||
// 如果选择了处方,在消息内容中添加处方信息
|
||||
if (selectedPrescription) {
|
||||
content = `[处方: ${selectedPrescription.orderNo || '未知'}] ${content}`;
|
||||
}
|
||||
// 保存到本地存储
|
||||
Taro.setStorageSync('tempOrderData', JSON.stringify(orderData))
|
||||
|
||||
// 添加诊断结果和治疗方案到消息内容
|
||||
if (values.diagnosis) {
|
||||
content = `诊断结果: ${values.diagnosis}\n` + content;
|
||||
}
|
||||
console.log('跳转到订单确认页,订单数据:', orderData)
|
||||
|
||||
if (values.treatmentPlan) {
|
||||
content = `治疗方案: ${values.treatmentPlan}\n` + content;
|
||||
}
|
||||
|
||||
if (values.decoctionInstructions) {
|
||||
content = `煎药说明: ${values.decoctionInstructions}\n` + content;
|
||||
}
|
||||
|
||||
// 执行新增或更新操作
|
||||
await addClinicOrder({});
|
||||
|
||||
Taro.showToast({
|
||||
title: `发送成功`,
|
||||
icon: 'success'
|
||||
});
|
||||
|
||||
setTimeout(() => {
|
||||
Taro.navigateBack();
|
||||
}, 1000);
|
||||
// 跳转到确认页
|
||||
Taro.navigateTo({
|
||||
url: '/doctor/orders/confirm'
|
||||
})
|
||||
|
||||
} catch (error: any) {
|
||||
console.error('发送失败:', error);
|
||||
console.error('数据处理失败:', error);
|
||||
Taro.showToast({
|
||||
title: `发送失败: ${error.message || error || '未知错误'}`,
|
||||
title: `数据处理失败: ${error.message || error || '未知错误'}`,
|
||||
icon: 'error'
|
||||
});
|
||||
}
|
||||
@@ -520,7 +505,7 @@ const AddClinicOrder = () => {
|
||||
)}
|
||||
|
||||
{/* 底部浮动按钮 */}
|
||||
<FixedButton text={'生成处方并发送给患者'} onClick={() => formRef.current?.submit()}/>
|
||||
<FixedButton text={'下一步:确认订单信息'} onClick={() => formRef.current?.submit()}/>
|
||||
|
||||
</>
|
||||
);
|
||||
|
||||
6
src/doctor/orders/confirm.config.ts
Normal file
6
src/doctor/orders/confirm.config.ts
Normal file
@@ -0,0 +1,6 @@
|
||||
export default {
|
||||
navigationBarTitleText: '确认处方订单',
|
||||
navigationBarBackgroundColor: '#fff',
|
||||
navigationBarTextStyle: 'black',
|
||||
backgroundColor: '#f5f5f5'
|
||||
}
|
||||
286
src/doctor/orders/confirm.scss
Normal file
286
src/doctor/orders/confirm.scss
Normal file
@@ -0,0 +1,286 @@
|
||||
.doctor-order-confirm {
|
||||
min-height: 100vh;
|
||||
background-color: #f5f5f5;
|
||||
padding-bottom: 80px;
|
||||
|
||||
// 页面提示
|
||||
.confirm-tip {
|
||||
background: linear-gradient(135deg, #f0f9ff 0%, #e0f2fe 100%);
|
||||
padding: 12px 16px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
border-bottom: 1px solid #e5e7eb;
|
||||
|
||||
.tip-text {
|
||||
font-size: 14px;
|
||||
color: #059669;
|
||||
font-weight: 500;
|
||||
}
|
||||
}
|
||||
|
||||
// 加载状态
|
||||
.order-confirm-loading {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
height: 100vh;
|
||||
font-size: 14px;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
// 分组样式
|
||||
.section-group {
|
||||
margin-bottom: 10px;
|
||||
background: #fff;
|
||||
|
||||
.nut-cell-group__title {
|
||||
padding: 12px 16px;
|
||||
font-size: 15px;
|
||||
font-weight: 600;
|
||||
color: #1f2937;
|
||||
}
|
||||
}
|
||||
|
||||
// 患者信息
|
||||
.patient-info {
|
||||
display: flex;
|
||||
gap: 16px;
|
||||
padding: 4px 0;
|
||||
|
||||
.patient-detail {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
gap: 6px;
|
||||
|
||||
.patient-name {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
|
||||
.name {
|
||||
font-size: 16px;
|
||||
font-weight: 600;
|
||||
color: #111827;
|
||||
}
|
||||
}
|
||||
|
||||
.patient-phone {
|
||||
font-size: 14px;
|
||||
color: #6b7280;
|
||||
}
|
||||
|
||||
.patient-extra {
|
||||
display: flex;
|
||||
gap: 16px;
|
||||
font-size: 13px;
|
||||
color: #9ca3af;
|
||||
|
||||
.age, .idcard {
|
||||
font-size: 13px;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 诊断信息
|
||||
.diagnosis-content,
|
||||
.treatment-content,
|
||||
.decoction-content {
|
||||
padding: 8px 0;
|
||||
|
||||
.content-text {
|
||||
font-size: 14px;
|
||||
color: #374151;
|
||||
line-height: 1.6;
|
||||
white-space: pre-wrap;
|
||||
word-break: break-all;
|
||||
}
|
||||
}
|
||||
|
||||
// 处方信息
|
||||
.prescription-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 4px 0;
|
||||
|
||||
.prescription-type {
|
||||
font-size: 15px;
|
||||
font-weight: 600;
|
||||
color: #059669;
|
||||
}
|
||||
}
|
||||
|
||||
// 药品列表
|
||||
.medicine-list {
|
||||
padding: 8px 0;
|
||||
|
||||
.medicine-item {
|
||||
padding: 12px 16px;
|
||||
border-bottom: 1px solid #f3f4f6;
|
||||
|
||||
&:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.medicine-main {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 8px;
|
||||
|
||||
.medicine-name {
|
||||
font-size: 15px;
|
||||
font-weight: 500;
|
||||
color: #111827;
|
||||
}
|
||||
|
||||
.medicine-price {
|
||||
font-size: 15px;
|
||||
font-weight: 600;
|
||||
color: #dc2626;
|
||||
}
|
||||
}
|
||||
|
||||
.medicine-sub {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
|
||||
.medicine-spec {
|
||||
font-size: 13px;
|
||||
color: #6b7280;
|
||||
}
|
||||
|
||||
.medicine-subtotal {
|
||||
font-size: 13px;
|
||||
color: #9ca3af;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 图片画廊
|
||||
.image-gallery {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
gap: 8px;
|
||||
padding: 8px 0;
|
||||
|
||||
.image-item {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
padding-bottom: 100%; // 1:1 aspect ratio
|
||||
border-radius: 8px;
|
||||
overflow: hidden;
|
||||
border: 1px solid #e5e7eb;
|
||||
|
||||
img, image {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
object-fit: cover;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 费用明细
|
||||
.price-text {
|
||||
font-size: 15px;
|
||||
font-weight: 500;
|
||||
color: #111827;
|
||||
}
|
||||
|
||||
.total-price-row {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
padding: 8px 0;
|
||||
|
||||
.total-label {
|
||||
font-size: 15px;
|
||||
font-weight: 600;
|
||||
color: #111827;
|
||||
}
|
||||
|
||||
.total-amount {
|
||||
font-size: 20px;
|
||||
font-weight: 700;
|
||||
color: #dc2626;
|
||||
}
|
||||
}
|
||||
|
||||
// 温馨提示
|
||||
.warm-tips {
|
||||
margin: 12px 16px;
|
||||
padding: 16px;
|
||||
background: #fffbeb;
|
||||
border-radius: 8px;
|
||||
border: 1px solid #fef3c7;
|
||||
|
||||
.tips-title {
|
||||
display: block;
|
||||
font-size: 14px;
|
||||
font-weight: 600;
|
||||
color: #d97706;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.tips-item {
|
||||
display: block;
|
||||
font-size: 13px;
|
||||
color: #92400e;
|
||||
line-height: 1.8;
|
||||
margin-bottom: 4px;
|
||||
|
||||
&:last-child {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 底部操作栏
|
||||
.fixed-bottom-bar {
|
||||
position: fixed;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
right: 0;
|
||||
background: #fff;
|
||||
border-top: 1px solid #e5e7eb;
|
||||
padding: 12px 16px 24px;
|
||||
z-index: 999;
|
||||
box-shadow: 0 -2px 8px rgba(0, 0, 0, 0.05);
|
||||
|
||||
.bottom-price {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: flex-end;
|
||||
margin-bottom: 12px;
|
||||
|
||||
.price-label {
|
||||
font-size: 14px;
|
||||
color: #6b7280;
|
||||
}
|
||||
|
||||
.price-value {
|
||||
font-size: 22px;
|
||||
font-weight: 700;
|
||||
color: #dc2626;
|
||||
margin-left: 4px;
|
||||
}
|
||||
}
|
||||
|
||||
.bottom-actions {
|
||||
display: flex;
|
||||
gap: 12px;
|
||||
|
||||
.nut-button {
|
||||
flex: 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
378
src/doctor/orders/confirm.tsx
Normal file
378
src/doctor/orders/confirm.tsx
Normal file
@@ -0,0 +1,378 @@
|
||||
import {useEffect, useState} from "react";
|
||||
import {
|
||||
Button,
|
||||
Cell,
|
||||
CellGroup,
|
||||
Avatar,
|
||||
Tag,
|
||||
Image,
|
||||
Space
|
||||
} from '@nutui/nutui-react-taro'
|
||||
import {Edit} from '@nutui/icons-react-taro'
|
||||
import {View, Text} from '@tarojs/components'
|
||||
import Taro from '@tarojs/taro'
|
||||
import FixedButton from "@/components/FixedButton";
|
||||
import {ClinicPatientUser} from "@/api/clinic/clinicPatientUser/model";
|
||||
import {ClinicPrescription} from "@/api/clinic/clinicPrescription/model";
|
||||
import {addClinicOrder} from "@/api/clinic/clinicOrder";
|
||||
import './confirm.scss'
|
||||
|
||||
// 订单数据接口
|
||||
interface OrderData {
|
||||
patient: ClinicPatientUser;
|
||||
prescription?: ClinicPrescription;
|
||||
diagnosis: string;
|
||||
treatmentPlan: string;
|
||||
decoctionInstructions?: string;
|
||||
images?: Array<{
|
||||
url: string;
|
||||
name?: string;
|
||||
uid?: string;
|
||||
}>;
|
||||
orderPrice?: string;
|
||||
}
|
||||
|
||||
const DoctorOrderConfirm = () => {
|
||||
const [orderData, setOrderData] = useState<OrderData | null>(null)
|
||||
const [loading, setLoading] = useState<boolean>(false)
|
||||
const [submitLoading, setSubmitLoading] = useState<boolean>(false)
|
||||
|
||||
// 计算药品总价
|
||||
const getMedicinePrice = () => {
|
||||
if (!orderData?.prescription?.items) return '0.00'
|
||||
const total = orderData.prescription.items.reduce((sum, item) => {
|
||||
const price = parseFloat(item.unitPrice || '0')
|
||||
const quantity = item.quantity || 1
|
||||
return sum + (price * quantity)
|
||||
}, 0)
|
||||
return total.toFixed(2)
|
||||
}
|
||||
|
||||
// 计算服务费(可根据实际业务调整)
|
||||
const getServiceFee = () => {
|
||||
return '10.00' // 固定服务费10元
|
||||
}
|
||||
|
||||
// 计算订单总价
|
||||
const getTotalPrice = () => {
|
||||
const medicinePrice = parseFloat(getMedicinePrice())
|
||||
const serviceFee = parseFloat(getServiceFee())
|
||||
return (medicinePrice + serviceFee).toFixed(2)
|
||||
}
|
||||
|
||||
// 获取处方类型文本
|
||||
const getPrescriptionType = () => {
|
||||
if (!orderData?.prescription) return ''
|
||||
return orderData.prescription.prescriptionType === 0 ? '中药' : '西药'
|
||||
}
|
||||
|
||||
// 返回编辑
|
||||
const handleBack = () => {
|
||||
Taro.navigateBack()
|
||||
}
|
||||
|
||||
// 确认并发送订单
|
||||
const handleConfirmOrder = async () => {
|
||||
if (!orderData) {
|
||||
Taro.showToast({
|
||||
title: '订单数据缺失',
|
||||
icon: 'error'
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
try {
|
||||
setSubmitLoading(true)
|
||||
|
||||
// 构建诊所订单数据
|
||||
const clinicOrderData = {
|
||||
userId: orderData.patient.userId,
|
||||
doctorId: Taro.getStorageSync('UserId'), // 当前医生ID
|
||||
type: 0, // 订单类型:诊所订单
|
||||
title: `${orderData.patient.realName}的处方订单`,
|
||||
totalPrice: getTotalPrice(),
|
||||
payPrice: getTotalPrice(),
|
||||
buyerRemarks: orderData.diagnosis,
|
||||
merchantRemarks: orderData.treatmentPlan,
|
||||
comments: JSON.stringify({
|
||||
diagnosis: orderData.diagnosis,
|
||||
treatmentPlan: orderData.treatmentPlan,
|
||||
decoctionInstructions: orderData.decoctionInstructions,
|
||||
prescriptionId: orderData.prescription?.id,
|
||||
images: orderData.images
|
||||
}),
|
||||
payStatus: '0', // 未付款
|
||||
orderStatus: 0, // 待支付
|
||||
deliveryStatus: 10, // 未发货
|
||||
}
|
||||
|
||||
console.log('提交订单数据:', clinicOrderData)
|
||||
|
||||
// 调用API创建订单
|
||||
await addClinicOrder(clinicOrderData)
|
||||
|
||||
// 清除临时数据
|
||||
Taro.removeStorageSync('tempOrderData')
|
||||
|
||||
Taro.showToast({
|
||||
title: '处方已发送给患者',
|
||||
icon: 'success',
|
||||
duration: 2000
|
||||
})
|
||||
|
||||
setTimeout(() => {
|
||||
// 跳转到订单列表
|
||||
Taro.redirectTo({
|
||||
url: '/doctor/orders/index'
|
||||
})
|
||||
}, 2000)
|
||||
|
||||
} catch (error: any) {
|
||||
console.error('创建订单失败:', error)
|
||||
Taro.showToast({
|
||||
title: error.message || '发送失败,请重试',
|
||||
icon: 'error'
|
||||
})
|
||||
} finally {
|
||||
setSubmitLoading(false)
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
try {
|
||||
setLoading(true)
|
||||
|
||||
// 从本地存储获取订单数据
|
||||
const tempData = Taro.getStorageSync('tempOrderData')
|
||||
|
||||
if (!tempData) {
|
||||
Taro.showToast({
|
||||
title: '订单数据缺失,请重新填写',
|
||||
icon: 'error'
|
||||
})
|
||||
setTimeout(() => {
|
||||
Taro.navigateBack()
|
||||
}, 1500)
|
||||
return
|
||||
}
|
||||
|
||||
const parsedData = JSON.parse(tempData)
|
||||
console.log('订单确认页获取数据:', parsedData)
|
||||
|
||||
setOrderData(parsedData)
|
||||
} catch (error) {
|
||||
console.error('解析订单数据失败:', error)
|
||||
Taro.showToast({
|
||||
title: '数据解析失败',
|
||||
icon: 'error'
|
||||
})
|
||||
} finally {
|
||||
setLoading(false)
|
||||
}
|
||||
}, [])
|
||||
|
||||
if (loading || !orderData) {
|
||||
return (
|
||||
<View className="order-confirm-loading">
|
||||
<Text>加载中...</Text>
|
||||
</View>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
{/* 页面标题提示 */}
|
||||
|
||||
{/* 患者信息 */}
|
||||
<CellGroup>
|
||||
<View className={'p-3'}>患者信息</View>
|
||||
<Cell>
|
||||
<Space>
|
||||
<Avatar
|
||||
src={orderData.patient.avatar}
|
||||
size="large"
|
||||
/>
|
||||
<View className="flex flex-col gap-1">
|
||||
<Text className="font-medium">{orderData.patient.realName}</Text>
|
||||
<Text className="text-gray-500">{orderData.patient.phone}</Text>
|
||||
<Space className="patient-extra">
|
||||
<Text className="text-gray-500">{orderData.patient.age}岁</Text>
|
||||
</Space>
|
||||
</View>
|
||||
</Space>
|
||||
</Cell>
|
||||
</CellGroup>
|
||||
|
||||
{/* 诊断信息 */}
|
||||
<CellGroup>
|
||||
<View className={'p-3'}>诊断信息</View>
|
||||
<Cell
|
||||
extra={
|
||||
<Button
|
||||
size="small"
|
||||
icon={<Edit size="12"/>}
|
||||
onClick={handleBack}
|
||||
>
|
||||
修改
|
||||
</Button>
|
||||
}
|
||||
>
|
||||
<View className="text-gray-500">
|
||||
<Text>{orderData.diagnosis}</Text>
|
||||
</View>
|
||||
</Cell>
|
||||
<View className={'p-3'}>治疗方案</View>
|
||||
<Cell>
|
||||
<View className={'text-gray-500'}>
|
||||
<Text>{orderData.treatmentPlan}</Text>
|
||||
</View>
|
||||
</Cell>
|
||||
</CellGroup>
|
||||
|
||||
{/* 处方信息 */}
|
||||
{orderData.prescription && (
|
||||
<CellGroup>
|
||||
<View className={'p-3'}>处方信息</View>
|
||||
<Cell>
|
||||
<Space>
|
||||
<Text className="text-gray-500">
|
||||
RP: {getPrescriptionType()}
|
||||
</Text>
|
||||
<Tag type="success">
|
||||
共 {orderData.prescription.items?.length || 0} 味药
|
||||
</Tag>
|
||||
</Space>
|
||||
</Cell>
|
||||
|
||||
{/* 药品列表 */}
|
||||
{orderData.prescription.items?.map((item, index) => (
|
||||
<Cell key={index}>
|
||||
<View className={'flex justify-between w-full text-gray-500'}>
|
||||
<Space>
|
||||
<Text className="medicine-name">{item.medicineName}</Text>
|
||||
<Text className="medicine-price">¥{item.unitPrice}</Text>
|
||||
<Text className="medicine-spec">
|
||||
{item.specification || '规格未知'} × {item.quantity || 1}
|
||||
</Text>
|
||||
</Space>
|
||||
<View className="medicine-sub">
|
||||
<Text className="medicine-subtotal">
|
||||
小计:¥{(parseFloat(item.unitPrice || '0') * (item.quantity || 1)).toFixed(2)}
|
||||
</Text>
|
||||
</View>
|
||||
</View>
|
||||
</Cell>
|
||||
))}
|
||||
|
||||
{/* 煎药说明 */}
|
||||
{orderData.decoctionInstructions && (
|
||||
<>
|
||||
<View className={'p-3'}>煎药说明</View>
|
||||
<Cell>
|
||||
<Text className={'text-gray-500'}>{orderData.decoctionInstructions}</Text>
|
||||
</Cell>
|
||||
</>
|
||||
)}
|
||||
</CellGroup>
|
||||
)}
|
||||
|
||||
{/* 上传的图片 */}
|
||||
{orderData.images && orderData.images.length > 0 && (
|
||||
<CellGroup title="病例图片" className="section-group">
|
||||
<Cell>
|
||||
<View className="image-gallery">
|
||||
{orderData.images.map((image, index) => (
|
||||
<View
|
||||
key={image.uid || index}
|
||||
className="image-item"
|
||||
onClick={() => {
|
||||
// 预览图片
|
||||
Taro.previewImage({
|
||||
urls: orderData.images!.map(img => img.url),
|
||||
current: image.url
|
||||
})
|
||||
}}
|
||||
>
|
||||
<Image
|
||||
src={image.url}
|
||||
mode="aspectFill"
|
||||
width="100%"
|
||||
height="100%"
|
||||
/>
|
||||
</View>
|
||||
))}
|
||||
</View>
|
||||
</Cell>
|
||||
</CellGroup>
|
||||
)}
|
||||
|
||||
{/* 费用明细 */}
|
||||
<CellGroup>
|
||||
<View className={'p-3'}>费用明细</View>
|
||||
<Cell
|
||||
title={<Text className="text-gray-500">药品费用</Text>}
|
||||
extra={<Text className="price-text">¥{getMedicinePrice()}</Text>}
|
||||
/>
|
||||
<Cell
|
||||
title={<Text className="text-gray-500">服务费</Text>}
|
||||
extra={<Text className="price-text">¥{getServiceFee()}</Text>}
|
||||
/>
|
||||
<Cell extra={
|
||||
(
|
||||
<Space className="total-price-row">
|
||||
<Text className="total-label">订单总计</Text>
|
||||
<Text className="total-amount">¥{getTotalPrice()}</Text>
|
||||
</Space>
|
||||
)
|
||||
}>
|
||||
</Cell>
|
||||
</CellGroup>
|
||||
|
||||
{/* 温馨提示 */}
|
||||
<CellGroup>
|
||||
<View className={'p-3'}>📌 温馨提示</View>
|
||||
<View className={'flex flex-col px-3 pb-5 text-gray-500 text-sm'}>
|
||||
<Text>• 请仔细核对订单信息</Text>
|
||||
<Text>• 处方发送后,患者将收到支付通知</Text>
|
||||
<Text>• 患者支付成功后,订单将自动流转至配药环节</Text>
|
||||
<Text>• 如需修改处方信息,请点击对应模块的"修改"按钮</Text>
|
||||
</View>
|
||||
</CellGroup>
|
||||
|
||||
{/* 底部操作按钮 */}
|
||||
<FixedButton
|
||||
text={submitLoading ? '发送中...' : '确认并发送给患者'}
|
||||
icon={<Edit />}
|
||||
onClick={handleConfirmOrder}
|
||||
/>
|
||||
<View className="fixed-bottom-bar">
|
||||
<View className="bottom-price">
|
||||
<Text className="price-label">订单总计:</Text>
|
||||
<Text className="price-value">¥{getTotalPrice()}</Text>
|
||||
</View>
|
||||
<View className="bottom-actions">
|
||||
<Button
|
||||
size="large"
|
||||
fill="outline"
|
||||
onClick={handleBack}
|
||||
disabled={submitLoading}
|
||||
>
|
||||
返回修改
|
||||
</Button>
|
||||
<Button
|
||||
type="primary"
|
||||
size="large"
|
||||
onClick={handleConfirmOrder}
|
||||
loading={submitLoading}
|
||||
disabled={submitLoading}
|
||||
style={{flex: 1}}
|
||||
>
|
||||
{submitLoading ? '发送中...' : '确认并发送给患者'}
|
||||
</Button>
|
||||
</View>
|
||||
</View>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export default DoctorOrderConfirm
|
||||
Reference in New Issue
Block a user