feat(doctor): 新增医生聊天与订单确认功能
- 在医生聊天页面增加患者与医师列表渲染逻辑 - 新增订单确认页面,支持处方信息预览与发送 - 更新开方页面跳转逻辑,将数据通过本地存储传递至确认页-优化聊天页面初始化逻辑,区分医生与普通用户角色-修复 CMS 文章详情内容判断逻辑,避免空内容解析异常 - 新增关于我们页面,支持富文本内容展示- 调整医生相关模型字段,如添加头像、替换手机号字段为 phone - 补充页面配置文件,完善导航栏标题与样式设置 - 新增医生订单确认页样式文件及详细交互布局 - 提供订单确认页使用说明文档,涵盖功能概述与技术实现细节
This commit is contained in:
@@ -12,8 +12,10 @@ export interface ClinicDoctorUser {
|
||||
userId?: number;
|
||||
// 姓名
|
||||
realName?: string;
|
||||
// 头像
|
||||
avatar?: string;
|
||||
// 手机号
|
||||
mobile?: string;
|
||||
phone?: string;
|
||||
// 支付密码
|
||||
payPassword?: string;
|
||||
// 当前可提现佣金
|
||||
|
||||
@@ -25,7 +25,14 @@ export default {
|
||||
"root": "cms",
|
||||
"pages": [
|
||||
'category/index',
|
||||
"detail/index"
|
||||
"detail/index",
|
||||
"about/index"
|
||||
]
|
||||
},
|
||||
{
|
||||
"root": "chat",
|
||||
"pages": [
|
||||
"doctor/index"
|
||||
]
|
||||
},
|
||||
{
|
||||
@@ -81,6 +88,7 @@ export default {
|
||||
"withdraw/index",
|
||||
"orders/index",
|
||||
"orders/add",
|
||||
"orders/confirm",
|
||||
"orders/selectPatient",
|
||||
"orders/selectPrescription",
|
||||
"team/index",
|
||||
|
||||
21
src/app.ts
21
src/app.ts
@@ -8,12 +8,14 @@ import {TenantId} from "@/config/app";
|
||||
import {saveStorageByLoginUser} from "@/utils/server";
|
||||
import {parseInviteParams, saveInviteParams, trackInviteSource, handleInviteRelation} from "@/utils/invite";
|
||||
import {useConfig} from "@/hooks/useConfig";
|
||||
import {addShopUser, getShopUser} from "@/api/shop/shopUser"; // 引入新的自定义Hook
|
||||
import { User } from '@/api/system/user/model';
|
||||
import {addShopUser, getShopUser} from "@/api/shop/shopUser";
|
||||
|
||||
function App(props: { children: any; }) {
|
||||
const {refetch: handleTheme} = useConfig(); // 使用新的Hook
|
||||
|
||||
const reload = () => {
|
||||
|
||||
Taro.login({
|
||||
success: (res) => {
|
||||
// 无感登录
|
||||
@@ -24,6 +26,14 @@ function App(props: { children: any; }) {
|
||||
if (data) {
|
||||
saveStorageByLoginUser(data.access_token, data.user)
|
||||
|
||||
// 判断是否是医生
|
||||
if (hasRole('doctor', data.user)) {
|
||||
console.log('是否医生?', true)
|
||||
Taro.setStorageSync('Doctor',true)
|
||||
}else {
|
||||
Taro.setStorageSync('Doctor',false)
|
||||
}
|
||||
|
||||
// 处理邀请关系
|
||||
if (data.user?.userId) {
|
||||
try {
|
||||
@@ -56,6 +66,15 @@ function App(props: { children: any; }) {
|
||||
}
|
||||
})
|
||||
};
|
||||
|
||||
// 检查是否有特定角色
|
||||
const hasRole = (roleCode: string, item: User) => {
|
||||
if (!item || !item.roles) {
|
||||
return false;
|
||||
}
|
||||
return item.roles.some(role => role.roleCode === roleCode);
|
||||
}
|
||||
|
||||
// 可以使用所有的 React Hooks
|
||||
useEffect(() => {
|
||||
// 设置主题 (现在由useConfig Hook处理)
|
||||
|
||||
3
src/chat/doctor/index.config.ts
Normal file
3
src/chat/doctor/index.config.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
export default definePageConfig({
|
||||
navigationBarTitleText: '聊天'
|
||||
})
|
||||
158
src/chat/doctor/index.tsx
Normal file
158
src/chat/doctor/index.tsx
Normal file
@@ -0,0 +1,158 @@
|
||||
import {useState, useEffect} from 'react'
|
||||
import {View, Text} from '@tarojs/components'
|
||||
import Taro, {useDidShow} from '@tarojs/taro'
|
||||
import {useRouter} from '@tarojs/taro'
|
||||
import {
|
||||
Loading,
|
||||
InfiniteLoading,
|
||||
Empty,
|
||||
Space,
|
||||
Input,
|
||||
Avatar,
|
||||
Tag,
|
||||
Divider,
|
||||
Button
|
||||
} from '@nutui/nutui-react-taro'
|
||||
import { Voice, FaceMild, AddCircle } from '@nutui/icons-react-taro'
|
||||
import {getClinicDoctorUser} from "@/api/clinic/clinicDoctorUser";
|
||||
import {ClinicDoctorUser} from "@/api/clinic/clinicDoctorUser/model";
|
||||
import {ClinicPatientUser} from "@/api/clinic/clinicPatientUser/model";
|
||||
import navTo from "@/utils/common";
|
||||
import {pageShopChatMessage} from "@/api/shop/shopChatMessage";
|
||||
import {ShopChatMessage} from "@/api/shop/shopChatMessage/model";
|
||||
import Line from "@/components/Gap";
|
||||
|
||||
const CustomerIndex = () => {
|
||||
const {params} = useRouter();
|
||||
const [doctor, setDoctor] = useState<ClinicDoctorUser>()
|
||||
const [list, setList] = useState<ShopChatMessage[]>([])
|
||||
|
||||
const [isDoctor, setIsDoctor] = useState<boolean>(false)
|
||||
const [doctors, setDoctors] = useState<ClinicDoctorUser[]>([])
|
||||
const [patientUsers, setPatientUsers] = useState<ClinicPatientUser[]>([])
|
||||
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 fetchData = async () => {
|
||||
setLoading(true);
|
||||
if (Taro.getStorageSync('Doctor')) {
|
||||
setIsDoctor(true)
|
||||
}
|
||||
const doctorUser = await getClinicDoctorUser(Number(params.id))
|
||||
if (doctorUser) {
|
||||
setDoctor(doctorUser)
|
||||
Taro.setNavigationBarTitle({title: `${doctorUser.realName}`});
|
||||
}
|
||||
const messages = await pageShopChatMessage({})
|
||||
|
||||
}
|
||||
|
||||
// 初始化数据
|
||||
useEffect(() => {
|
||||
fetchData().then()
|
||||
}, []);
|
||||
|
||||
// 监听页面显示,当从其他页面返回时刷新数据
|
||||
useDidShow(() => {
|
||||
// 刷新当前tab的数据和统计信息
|
||||
fetchData().then();
|
||||
});
|
||||
|
||||
// 渲染医师项
|
||||
const renderDoctorItem = (item: ClinicDoctorUser) => (
|
||||
<View key={item.userId} className="bg-white rounded-lg p-4 mb-3 shadow-sm">
|
||||
<View className="flex items-center">
|
||||
<View className="flex-1 flex justify-between items-center">
|
||||
<View className="flex justify-between">
|
||||
<Avatar src={item.avatar} size={'large'}/>
|
||||
<View className={'flex flex-col mx-3'}>
|
||||
<Text className="font-semibold text-gray-800 mr-2">
|
||||
{item.realName} 医师
|
||||
</Text>
|
||||
<View>
|
||||
<Tag background="#f3f3f3" color="#999999">中医内科</Tag>
|
||||
</View>
|
||||
<View className={'my-1'}>
|
||||
<Text className={'text-gray-400 text-xs'}>问诊 1 次</Text>
|
||||
<Divider direction="vertical"/>
|
||||
<Text className={'text-gray-400 text-xs'}>开方 3 次</Text>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
<Button type="warning">咨询医生</Button>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
|
||||
// 渲染患者项
|
||||
const renderPatientUserItem = (item: ClinicPatientUser) => (
|
||||
<View key={item.userId} className="bg-white rounded-lg p-4 mb-3 shadow-sm">
|
||||
<View className="flex items-center">
|
||||
<View className="flex-1 flex justify-between items-center">
|
||||
<View className="flex justify-between">
|
||||
<Avatar src={item.avatar} size={'large'}/>
|
||||
<View className={'flex flex-col mx-3'}>
|
||||
<Text className="font-semibold text-gray-800 mr-2">
|
||||
{item.realName}
|
||||
</Text>
|
||||
<View>
|
||||
{
|
||||
<Text
|
||||
className={'text-gray-400 text-xs'}>{item.sex}</Text>
|
||||
}
|
||||
{
|
||||
item.age && (
|
||||
<>
|
||||
<Divider direction="vertical"/>
|
||||
<Text className={'text-gray-400 text-xs'}>{item.age}岁</Text>
|
||||
</>
|
||||
)
|
||||
}
|
||||
{
|
||||
item.weight && (
|
||||
<>
|
||||
<Divider direction="vertical"/>
|
||||
<Text className={'text-gray-400 text-xs'}>{item.weight}</Text>
|
||||
</>
|
||||
)
|
||||
}
|
||||
</View>
|
||||
<View>
|
||||
<Text className={'text-gray-400 text-xs'}>{item.allergyHistory}</Text>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
<Button type="warning" onClick={() => navTo(`/doctor/orders/add?id=${item.userId}`)}>开方</Button>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
|
||||
|
||||
return (
|
||||
<View className="min-h-screen bg-gray-50 w-full">
|
||||
<View className={'p-4'}>
|
||||
{list?.map(renderPatientUserItem)}
|
||||
</View>
|
||||
<View className={'fixed bottom-0 w-full bg-orange-50 pt-2 pb-8'}>
|
||||
<View className={'flex flex-1 items-center justify-between'}>
|
||||
<Voice className={'mx-2'} />
|
||||
<Input className={'w-full'} style={{
|
||||
borderRadius: '6px',
|
||||
paddingLeft: '12px',
|
||||
paddingRight: '12px'
|
||||
}} />
|
||||
<FaceMild size={26} className={'ml-2'} />
|
||||
<AddCircle size={26} className={'mx-2'} />
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
export default CustomerIndex;
|
||||
4
src/cms/about/index.config.ts
Normal file
4
src/cms/about/index.config.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
export default definePageConfig({
|
||||
navigationBarTitleText: '关于我们',
|
||||
navigationBarTextStyle: 'black'
|
||||
})
|
||||
48
src/cms/about/index.tsx
Normal file
48
src/cms/about/index.tsx
Normal file
@@ -0,0 +1,48 @@
|
||||
import Taro from '@tarojs/taro'
|
||||
import {useEffect, useState} from 'react'
|
||||
import {Loading} from '@nutui/nutui-react-taro'
|
||||
import {View, RichText} from '@tarojs/components'
|
||||
import {wxParse} from "@/utils/common";
|
||||
import {getCmsArticleByCode} from "@/api/cms/cmsArticle";
|
||||
import {CmsArticle} from "@/api/cms/cmsArticle/model"
|
||||
import Line from "@/components/Gap";
|
||||
|
||||
function Index() {
|
||||
const [loading, setLoading] = useState<boolean>(true)
|
||||
// 文章详情
|
||||
const [item, setItem] = useState<CmsArticle>()
|
||||
const reload = async () => {
|
||||
const item = await getCmsArticleByCode('xieyi')
|
||||
|
||||
if (item && item.content) {
|
||||
item.content = wxParse(item.content)
|
||||
setItem(item)
|
||||
Taro.setNavigationBarTitle({
|
||||
title: `${item?.categoryName}`
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
reload().then(() => {
|
||||
setLoading(false)
|
||||
});
|
||||
}, []);
|
||||
|
||||
if (loading) {
|
||||
return (
|
||||
<Loading className={'px-2'}>加载中</Loading>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={'bg-white'}>
|
||||
<View className={'content p-4'}>
|
||||
<RichText nodes={item?.content}/>
|
||||
</View>
|
||||
<Line height={44}/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default Index
|
||||
@@ -17,7 +17,7 @@ function Detail() {
|
||||
const reload = async () => {
|
||||
const item = await getCmsArticle(Number(params.id))
|
||||
|
||||
if (item) {
|
||||
if (item && item.content) {
|
||||
item.content = wxParse(item.content)
|
||||
setItem(item)
|
||||
Taro.setNavigationBarTitle({
|
||||
|
||||
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
|
||||
@@ -11,28 +11,28 @@ import {
|
||||
Button,
|
||||
SearchBar
|
||||
} from '@nutui/nutui-react-taro'
|
||||
import {ShopUser} from "@/api/shop/shopUser/model";
|
||||
import {useUser} from "@/hooks/useUser";
|
||||
import {useDoctorUser} from "@/hooks/useDoctorUser";
|
||||
import {pageClinicDoctorUser} from "@/api/clinic/clinicDoctorUser";
|
||||
import {ClinicDoctorUser} from "@/api/clinic/clinicDoctorUser/model";
|
||||
import {ClinicPatientUser} from "@/api/clinic/clinicPatientUser/model";
|
||||
import {pageClinicDoctorUser} from "@/api/clinic/clinicDoctorUser";
|
||||
import {pageClinicPatientUser} from "@/api/clinic/clinicPatientUser";
|
||||
import navTo from "@/utils/common";
|
||||
|
||||
const CustomerIndex = () => {
|
||||
const [list, setList] = useState<any[]>([])
|
||||
const [isDoctor, setIsDoctor] = useState<boolean>(false)
|
||||
const [doctors, setDoctors] = useState<ClinicDoctorUser[]>([])
|
||||
const [patientUsers, setPatientUsers] = useState<ClinicPatientUser[]>([])
|
||||
const [loading, setLoading] = useState<boolean>(false)
|
||||
const [activeTab, setActiveTab] = useState<any>('all')
|
||||
const [searchValue, setSearchValue] = useState<string>('')
|
||||
const [displaySearchValue, setDisplaySearchValue] = useState<string>('')
|
||||
const [page, setPage] = useState(1)
|
||||
const [hasMore, setHasMore] = useState(true)
|
||||
|
||||
|
||||
const {user} = useUser();
|
||||
|
||||
// 获取客户数据
|
||||
const fetchDoctorUsers = useCallback(async (resetPage = false, targetPage?: number) => {
|
||||
// 获取列表数据
|
||||
const fetchData = useCallback(async (resetPage = false, targetPage?: number) => {
|
||||
setLoading(true);
|
||||
if (Taro.getStorageSync('Doctor')) {
|
||||
setIsDoctor(true)
|
||||
}
|
||||
try {
|
||||
const currentPage = resetPage ? 1 : (targetPage || page);
|
||||
|
||||
@@ -41,8 +41,11 @@ const CustomerIndex = () => {
|
||||
page: currentPage
|
||||
};
|
||||
|
||||
const res = await pageClinicDoctorUser(params);
|
||||
|
||||
console.log(isDoctor, 'isDoctor>>>>')
|
||||
// 获取患者数据
|
||||
if (isDoctor || Taro.getStorageSync('Doctor')) {
|
||||
console.log('获取患者数据')
|
||||
const res = await pageClinicPatientUser(params);
|
||||
if (res?.list && res.list.length > 0) {
|
||||
const mappedList = res.list.map(item => ({
|
||||
...item
|
||||
@@ -50,24 +53,48 @@ const CustomerIndex = () => {
|
||||
|
||||
// 如果是重置页面或第一页,直接设置新数据;否则追加数据
|
||||
if (resetPage || currentPage === 1) {
|
||||
setList(mappedList);
|
||||
setPatientUsers(mappedList);
|
||||
} else {
|
||||
setList(prevList => prevList.concat(mappedList));
|
||||
setPatientUsers(prevList => prevList.concat(mappedList));
|
||||
}
|
||||
|
||||
// 正确判断是否还有更多数据
|
||||
}
|
||||
} else {
|
||||
// 获取医师数据
|
||||
console.log('获取医师数据')
|
||||
const res = await pageClinicDoctorUser(params);
|
||||
if (res?.list && res.list.length > 0) {
|
||||
const mappedList = res.list.map(item => ({
|
||||
...item
|
||||
}));
|
||||
|
||||
console.log(mappedList, 'mappedList')
|
||||
// 如果是重置页面或第一页,直接设置新数据;否则追加数据
|
||||
if (resetPage || currentPage === 1) {
|
||||
console.log('设置新数据')
|
||||
setDoctors(mappedList);
|
||||
} else {
|
||||
console.log('追加数据')
|
||||
setDoctors(prevList => prevList.concat(mappedList));
|
||||
}
|
||||
console.log(doctors, 'doctors1')
|
||||
// 正确判断是否还有更多数据
|
||||
const hasMoreData = res.list.length >= 10; // 假设每页10条数据
|
||||
setHasMore(hasMoreData);
|
||||
console.log(doctors, 'doctors2')
|
||||
} else {
|
||||
if (resetPage || currentPage === 1) {
|
||||
setList([]);
|
||||
console.log('设置新数据222')
|
||||
setDoctors([]);
|
||||
}
|
||||
setHasMore(false);
|
||||
}
|
||||
}
|
||||
|
||||
setPage(currentPage);
|
||||
} catch (error) {
|
||||
console.error('获取客户数据失败:', error);
|
||||
console.error('获取患者数据失败:', error);
|
||||
Taro.showToast({
|
||||
title: '加载失败,请重试',
|
||||
icon: 'none'
|
||||
@@ -75,12 +102,22 @@ const CustomerIndex = () => {
|
||||
} finally {
|
||||
setLoading(false);
|
||||
}
|
||||
}, [activeTab, page]);
|
||||
}, [page]);
|
||||
|
||||
const reloadMore = async () => {
|
||||
if (loading || !hasMore) return; // 防止重复加载
|
||||
const nextPage = page + 1;
|
||||
await fetchDoctorUsers( false, nextPage);
|
||||
await fetchData(false, nextPage);
|
||||
}
|
||||
|
||||
const getSexName = (sex: any) => {
|
||||
if (sex === '1') {
|
||||
return '男'
|
||||
}
|
||||
if (sex === '2') {
|
||||
return '女'
|
||||
}
|
||||
return '未知'
|
||||
}
|
||||
|
||||
|
||||
@@ -93,50 +130,29 @@ const CustomerIndex = () => {
|
||||
return () => clearTimeout(timer);
|
||||
}, [searchValue]);
|
||||
|
||||
// 根据搜索条件筛选数据(状态筛选已在API层面处理)
|
||||
const getFilteredList = () => {
|
||||
let filteredList = list;
|
||||
|
||||
// 按搜索关键词筛选
|
||||
if (displaySearchValue.trim()) {
|
||||
const keyword = displaySearchValue.trim().toLowerCase();
|
||||
filteredList = filteredList.filter(item =>
|
||||
(item.realName && item.realName.toLowerCase().includes(keyword)) ||
|
||||
(item.dealerName && item.dealerName.toLowerCase().includes(keyword)) ||
|
||||
(item.dealerCode && item.dealerCode.toLowerCase().includes(keyword)) ||
|
||||
(item.mobile && item.mobile.includes(keyword)) ||
|
||||
(item.userId && item.userId.toString().includes(keyword))
|
||||
);
|
||||
}
|
||||
|
||||
return filteredList;
|
||||
};
|
||||
|
||||
// 初始化数据
|
||||
useEffect(() => {
|
||||
fetchDoctorUsers( true).then();
|
||||
|
||||
fetchData(true).then();
|
||||
}, []);
|
||||
|
||||
// 当activeTab变化时重新获取数据
|
||||
useEffect(() => {
|
||||
setList([]); // 清空列表
|
||||
setPage(1); // 重置页码
|
||||
setHasMore(true); // 重置加载状态
|
||||
fetchDoctorUsers(true);
|
||||
}, [activeTab]);
|
||||
// useEffect(() => {
|
||||
// setPage(1); // 重置页码
|
||||
// setHasMore(true); // 重置加载状态
|
||||
// fetchData(true);
|
||||
// }, []);
|
||||
|
||||
// 监听页面显示,当从其他页面返回时刷新数据
|
||||
useDidShow(() => {
|
||||
// 刷新当前tab的数据和统计信息
|
||||
setList([]);
|
||||
setPatientUsers([]);
|
||||
setDoctors([])
|
||||
setPage(1);
|
||||
setHasMore(true);
|
||||
fetchDoctorUsers(true);
|
||||
fetchData(true).then();
|
||||
});
|
||||
|
||||
// 渲染医师项
|
||||
const renderDoctorItem = (item: ShopUser) => (
|
||||
const renderDoctorItem = (item: ClinicDoctorUser) => (
|
||||
<View key={item.userId} className="bg-white rounded-lg p-4 mb-3 shadow-sm">
|
||||
<View className="flex items-center">
|
||||
<View className="flex-1 flex justify-between items-center">
|
||||
@@ -156,14 +172,14 @@ const CustomerIndex = () => {
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
<Button type="warning">咨询医生</Button>
|
||||
<Button type="warning" onClick={() => navTo(`/chat/doctor/index?id=${item.userId}`)}>咨询医生</Button>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
);
|
||||
|
||||
// 渲染患者项
|
||||
const renderPatientUserItem = (item: ShopUser) => (
|
||||
const renderPatientUserItem = (item: ClinicPatientUser) => (
|
||||
<View key={item.userId} className="bg-white rounded-lg p-4 mb-3 shadow-sm">
|
||||
<View className="flex items-center">
|
||||
<View className="flex-1 flex justify-between items-center">
|
||||
@@ -171,16 +187,36 @@ const CustomerIndex = () => {
|
||||
<Avatar src={item.avatar} size={'large'}/>
|
||||
<View className={'flex flex-col mx-3'}>
|
||||
<Text className="font-semibold text-gray-800 mr-2">
|
||||
{item.realName} {item.sex} {item.age}岁
|
||||
{item.realName}
|
||||
</Text>
|
||||
<View className={'my-1'}>
|
||||
<Text className={'text-gray-400 text-xs'}>头晕</Text>
|
||||
<View>
|
||||
{
|
||||
<Text
|
||||
className={'text-gray-400 text-xs'}>{getSexName(item.sex)}</Text>
|
||||
}
|
||||
{
|
||||
item.age && (
|
||||
<>
|
||||
<Divider direction="vertical"/>
|
||||
<Text className={'text-gray-400 text-xs'}>呕吐</Text>
|
||||
<Text className={'text-gray-400 text-xs'}>{item.age}岁</Text>
|
||||
</>
|
||||
)
|
||||
}
|
||||
{
|
||||
item.weight && (
|
||||
<>
|
||||
<Divider direction="vertical"/>
|
||||
<Text className={'text-gray-400 text-xs'}>{item.weight}</Text>
|
||||
</>
|
||||
)
|
||||
}
|
||||
</View>
|
||||
<View>
|
||||
<Text className={'text-gray-400 text-xs'}>{item.allergyHistory}</Text>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
<Button type="warning">开方</Button>
|
||||
<Button type="warning" onClick={() => navTo(`/doctor/orders/add?id=${item.userId}`)}>开方</Button>
|
||||
</View>
|
||||
</View>
|
||||
</View>
|
||||
@@ -188,16 +224,14 @@ const CustomerIndex = () => {
|
||||
|
||||
// 渲染患者列表
|
||||
const renderPatientUserList = () => {
|
||||
const filteredList = getFilteredList();
|
||||
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} 条记录
|
||||
搜索 "{displaySearchValue}" 的结果,共找到 {patientUsers.length} 条记录
|
||||
</Text>
|
||||
</View>
|
||||
)}
|
||||
@@ -223,10 +257,10 @@ const CustomerIndex = () => {
|
||||
</>
|
||||
}
|
||||
loadMoreText={
|
||||
filteredList.length === 0 ? (
|
||||
patientUsers.length === 0 ? (
|
||||
<Empty
|
||||
style={{backgroundColor: 'transparent'}}
|
||||
description={loading ? "加载中..." : "暂无客户数据"}
|
||||
description={loading ? "加载中..." : "暂无患者数据"}
|
||||
/>
|
||||
) : (
|
||||
<View className={'h-3 flex items-center justify-center'}>
|
||||
@@ -235,13 +269,13 @@ const CustomerIndex = () => {
|
||||
)
|
||||
}
|
||||
>
|
||||
{loading && filteredList.length === 0 ? (
|
||||
{loading && patientUsers.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(renderPatientUserItem)
|
||||
patientUsers.map(renderPatientUserItem)
|
||||
)}
|
||||
</InfiniteLoading>
|
||||
</View>
|
||||
@@ -251,16 +285,14 @@ const CustomerIndex = () => {
|
||||
|
||||
// 渲染医生列表
|
||||
const renderDoctorList = () => {
|
||||
const filteredList = getFilteredList();
|
||||
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}" 的结果,共找到 {filteredList.length} 条记录
|
||||
搜索 "{displaySearchValue}" 的结果,共找到 {doctors.length} 条记录
|
||||
</Text>
|
||||
</View>
|
||||
)}
|
||||
@@ -286,7 +318,7 @@ const CustomerIndex = () => {
|
||||
</>
|
||||
}
|
||||
loadMoreText={
|
||||
filteredList.length === 0 ? (
|
||||
doctors.length === 0 ? (
|
||||
<Empty
|
||||
style={{backgroundColor: 'transparent'}}
|
||||
description={loading ? "加载中..." : "暂无医生数据"}
|
||||
@@ -298,13 +330,13 @@ const CustomerIndex = () => {
|
||||
)
|
||||
}
|
||||
>
|
||||
{loading && filteredList.length === 0 ? (
|
||||
{loading && doctors.length === 0 ? (
|
||||
<View className="flex items-center justify-center py-8">
|
||||
<Loading/>
|
||||
<Text className="text-gray-500 mt-2 ml-2">加载中...</Text>
|
||||
</View>
|
||||
) : (
|
||||
filteredList.map(renderDoctorItem)
|
||||
doctors.map(renderDoctorItem)
|
||||
)}
|
||||
</InfiniteLoading>
|
||||
</View>
|
||||
@@ -318,7 +350,7 @@ const CustomerIndex = () => {
|
||||
<View className="bg-white pt-2 border-b border-gray-100">
|
||||
<SearchBar
|
||||
value={searchValue}
|
||||
placeholder="搜索客户名称、手机号"
|
||||
placeholder="搜索患者名称、手机号"
|
||||
onChange={(value) => setSearchValue(value)}
|
||||
onClear={() => {
|
||||
setSearchValue('');
|
||||
@@ -329,10 +361,9 @@ const CustomerIndex = () => {
|
||||
</View>
|
||||
|
||||
{/* 患者列表 */}
|
||||
{!user?.certification && (renderPatientUserList())}
|
||||
{isDoctor && (renderPatientUserList())}
|
||||
{/* 医生列表 */}
|
||||
{user?.certification && (renderDoctorList())}
|
||||
|
||||
{!isDoctor && (renderDoctorList())}
|
||||
|
||||
</View>
|
||||
);
|
||||
|
||||
@@ -1,30 +1,48 @@
|
||||
import {useEffect, useState} from "react";
|
||||
import Taro from '@tarojs/taro'
|
||||
import {useEffect, useState} from 'react'
|
||||
import {Loading} from '@nutui/nutui-react-taro'
|
||||
import {View, RichText} from '@tarojs/components'
|
||||
import {wxParse} from "@/utils/common";
|
||||
import {getCmsArticleByCode} from "@/api/cms/cmsArticle";
|
||||
import {CmsArticle} from "@/api/cms/cmsArticle/model"
|
||||
import Line from "@/components/Gap";
|
||||
|
||||
const Agreement = () => {
|
||||
function Agreement() {
|
||||
const [loading, setLoading] = useState<boolean>(true)
|
||||
// 文章详情
|
||||
const [item, setItem] = useState<CmsArticle>()
|
||||
const reload = async () => {
|
||||
const item = await getCmsArticleByCode('xieyi')
|
||||
|
||||
const [content, setContent] = useState<any>('')
|
||||
const reload = () => {
|
||||
Taro.hideTabBar()
|
||||
setContent('<p>' +
|
||||
'<span style="font-size: 14px;">欢迎使用</span>' +
|
||||
'<span style="font-size: 14px;"> </span>' +
|
||||
'<span style="font-size: 14px;"><strong><span style="color: rgb(255, 0, 0);">【WebSoft】</span></strong></span>' +
|
||||
'<span style="font-size: 14px;">服务协议 </span>' +
|
||||
'</p>')
|
||||
if (item && item.content) {
|
||||
item.content = wxParse(item.content)
|
||||
setItem(item)
|
||||
Taro.setNavigationBarTitle({
|
||||
title: `${item?.categoryName}`
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
reload()
|
||||
}, [])
|
||||
reload().then(() => {
|
||||
setLoading(false)
|
||||
});
|
||||
}, []);
|
||||
|
||||
if (loading) {
|
||||
return (
|
||||
<Loading className={'px-2'}>加载中</Loading>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<View className={'content text-gray-700 text-sm p-4'}>
|
||||
<RichText nodes={content}/>
|
||||
<div className={'bg-white'}>
|
||||
<View className={'content p-4'}>
|
||||
<RichText nodes={item?.content}/>
|
||||
</View>
|
||||
</>
|
||||
<Line height={44}/>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
export default Agreement
|
||||
|
||||
@@ -1,55 +1,118 @@
|
||||
import {useEffect, useState} from "react";
|
||||
import Taro from '@tarojs/taro'
|
||||
import {Input, Radio, Button} from '@nutui/nutui-react-taro'
|
||||
import {View} from '@tarojs/components'
|
||||
import {Radio, Button} from '@nutui/nutui-react-taro'
|
||||
import {getStoredInviteParams} from "@/utils/invite";
|
||||
import {TenantId} from "@/config/app";
|
||||
import {useUser} from "@/hooks/useUser";
|
||||
|
||||
const Login = () => {
|
||||
const [isAgree, setIsAgree] = useState(false)
|
||||
|
||||
const {loginUser} = useUser();
|
||||
|
||||
const reload = () => {
|
||||
Taro.hideTabBar()
|
||||
}
|
||||
|
||||
/* 获取用户手机号 */
|
||||
const handleGetPhoneNumber = ({detail}: { detail: { code?: string, encryptedData?: string, iv?: string } }) => {
|
||||
const {code, encryptedData, iv} = detail
|
||||
|
||||
// 获取存储的邀请参数
|
||||
const inviteParams = getStoredInviteParams()
|
||||
const refereeId = inviteParams?.inviter ? parseInt(inviteParams.inviter) : 0
|
||||
|
||||
Taro.login({
|
||||
success: function (loginRes) {
|
||||
if (code) {
|
||||
Taro.request({
|
||||
url: 'https://server.websoft.top/api/wx-login/loginByMpWxPhone',
|
||||
method: 'POST',
|
||||
data: {
|
||||
authCode: loginRes.code,
|
||||
code,
|
||||
encryptedData,
|
||||
iv,
|
||||
notVerifyPhone: true,
|
||||
refereeId: refereeId, // 使用解析出的推荐人ID
|
||||
sceneType: 'save_referee',
|
||||
tenantId: TenantId
|
||||
},
|
||||
header: {
|
||||
'content-type': 'application/json',
|
||||
TenantId
|
||||
},
|
||||
success: function (res) {
|
||||
if (res.data.code == 1) {
|
||||
Taro.showToast({
|
||||
title: res.data.message,
|
||||
icon: 'error',
|
||||
duration: 2000
|
||||
})
|
||||
return false;
|
||||
}
|
||||
// 登录成功
|
||||
const token = res.data.data.access_token;
|
||||
const userData = res.data.data.user;
|
||||
|
||||
// 使用useUser Hook的loginUser方法更新状态
|
||||
loginUser(token, userData);
|
||||
|
||||
// 显示登录成功提示
|
||||
Taro.showToast({
|
||||
title: '登录成功',
|
||||
icon: 'success',
|
||||
duration: 1500
|
||||
})
|
||||
|
||||
// 返回上一页
|
||||
Taro.navigateBack();
|
||||
}
|
||||
})
|
||||
} else {
|
||||
console.log('登录失败!')
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
reload()
|
||||
}, [])
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className={'flex flex-col justify-center px-5'}>
|
||||
<div className={'text-3xl text-center py-5 font-normal my-10'}>账号登录</div>
|
||||
<View className={'flex flex-col justify-center px-5'}>
|
||||
<View className={'text-3xl text-center py-5 font-normal my-10'}>授权登录</View>
|
||||
|
||||
<>
|
||||
<div className={'flex flex-col justify-between items-center my-2'}>
|
||||
<Input type="text" placeholder="手机号" maxLength={11}
|
||||
style={{backgroundColor: '#ffffff', borderRadius: '8px'}}/>
|
||||
</div>
|
||||
<div className={'flex flex-col justify-between items-center my-2'}>
|
||||
<Input type="password" placeholder="密码" style={{backgroundColor: '#ffffff', borderRadius: '8px'}}/>
|
||||
</div>
|
||||
<div className={'flex justify-between my-2 text-left px-1'}>
|
||||
<a href={'#'} className={'text-blue-600 text-sm'}
|
||||
onClick={() => Taro.navigateTo({url: '/passport/forget'})}>忘记密码</a>
|
||||
<a href={'#'} className={'text-blue-600 text-sm'}
|
||||
onClick={() => Taro.navigateTo({url: '/passport/register'})}>立即注册</a>
|
||||
</div>
|
||||
<div className={'flex justify-center my-5'}>
|
||||
<Button type="info" size={'large'} className={'w-full rounded-lg p-2'} disabled={!isAgree}>登录</Button>
|
||||
</div>
|
||||
<div className={'my-2 flex fixed justify-center bottom-20 left-0 text-sm items-center text-center w-full'}>
|
||||
<Button onClick={() => Taro.navigateTo({url: '/passport/setting'})}>服务配置</Button>
|
||||
</div>
|
||||
{/*<div className={'w-full fixed bottom-20 my-2 flex justify-center text-sm items-center text-center'}>*/}
|
||||
{/* 没有账号?<a href={''} onClick={() => Taro.navigateTo({url: '/passport/register'})}*/}
|
||||
{/* className={'text-blue-600'}>立即注册</a>*/}
|
||||
{/*</div>*/}
|
||||
{isAgree && (
|
||||
<View className={'flex justify-center my-5 py-3 rounded-lg '} style={{
|
||||
backgroundColor: '#22c55e',
|
||||
}}>
|
||||
<Button open-type="getPhoneNumber"
|
||||
style={{color: '#ffffff', height: 'auto'}}
|
||||
onGetPhoneNumber={handleGetPhoneNumber} size={'large'} className={'w-full rounded-lg p-2'}
|
||||
disabled={!isAgree}>授权手机号并登录</Button>
|
||||
</View>
|
||||
)}
|
||||
{!isAgree && (
|
||||
<View className={'flex justify-center my-5 py-1 rounded-lg '} style={{
|
||||
backgroundColor: '#93a3af',
|
||||
}}>
|
||||
<View style={{color: '#ffffff', height: 'auto'}} className={'w-full rounded-lg text-sm text-center p-2'}>授权手机号并登录</View>
|
||||
</View>
|
||||
)}
|
||||
</>
|
||||
|
||||
<div className={'my-2 flex text-sm items-center px-1'}>
|
||||
<View className={'my-2 flex text-sm items-center px-1'}>
|
||||
<Radio style={{color: '#333333'}} checked={isAgree} onClick={() => setIsAgree(!isAgree)}></Radio>
|
||||
<span className={'text-gray-400'} onClick={() => setIsAgree(!isAgree)}>勾选表示您已阅读并同意</span><a
|
||||
onClick={() => Taro.navigateTo({url: '/passport/agreement'})}
|
||||
className={'text-blue-600'}>《服务协议及隐私政策》</a>
|
||||
</div>
|
||||
</div>
|
||||
</View>
|
||||
</View>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1,42 +1,22 @@
|
||||
import {useEffect, useState} from "react";
|
||||
import Taro from '@tarojs/taro';
|
||||
import {listCmsArticle} from "@/api/cms/cmsArticle";
|
||||
import {getCmsArticleByCode} from "@/api/cms/cmsArticle";
|
||||
import {Avatar, Cell, Divider} from '@nutui/nutui-react-taro'
|
||||
import {ArrowRight} from '@nutui/icons-react-taro'
|
||||
import {CmsNavigation} from "@/api/cms/cmsNavigation/model";
|
||||
import {listCmsNavigation} from "@/api/cms/cmsNavigation";
|
||||
// 显示html富文本
|
||||
import {View, RichText} from '@tarojs/components'
|
||||
import {listCmsDesign} from "@/api/cms/cmsDesign";
|
||||
import {CmsDesign} from "@/api/cms/cmsDesign/model";
|
||||
import {type Config} from "@/api/cms/cmsWebsiteField/model";
|
||||
import {configWebsiteField} from "@/api/cms/cmsWebsiteField";
|
||||
import {useConfig} from "@/hooks/useConfig";
|
||||
import {useShopInfo} from "@/hooks/useShopInfo";
|
||||
import Line from "@/components/Gap";
|
||||
import {CmsArticle} from "@/api/cms/cmsArticle/model";
|
||||
|
||||
|
||||
const Helper = () => {
|
||||
const [nav, setNav] = useState<CmsNavigation>()
|
||||
const [design, setDesign] = useState<CmsDesign>()
|
||||
const [category, setCategory] = useState<CmsNavigation[]>([])
|
||||
const [config, setConfig] = useState<Config>()
|
||||
const [about, setAbout] = useState<CmsArticle>()
|
||||
const {config} = useConfig()
|
||||
const {shopInfo} = useShopInfo()
|
||||
|
||||
const reload = async () => {
|
||||
const navs = await listCmsNavigation({model: 'page', parentId: 0});
|
||||
if (navs.length > 0) {
|
||||
const nav = navs[0];
|
||||
setNav(nav);
|
||||
// 查询页面信息
|
||||
const design = await listCmsDesign({categoryId: nav.navigationId})
|
||||
setDesign(design[0])
|
||||
// 查询子栏目
|
||||
const category = await listCmsNavigation({parentId: nav.navigationId})
|
||||
category.map(async (item, index) => {
|
||||
category[index].articles = await listCmsArticle({categoryId: item.navigationId});
|
||||
})
|
||||
setCategory(category)
|
||||
// 查询字段
|
||||
const configInfo = await configWebsiteField({})
|
||||
setConfig(configInfo)
|
||||
}
|
||||
const aboutInfo = await getCmsArticleByCode('about')
|
||||
setAbout(aboutInfo)
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
@@ -44,51 +24,38 @@ const Helper = () => {
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<div className={'px-3'}>
|
||||
<View className={'px-3'}>
|
||||
<Cell>
|
||||
{nav && (
|
||||
<View className={'flex flex-col justify-center items-center w-full'}>
|
||||
<Avatar
|
||||
src={design?.photo}
|
||||
src={shopInfo?.logo}
|
||||
size={'100'}
|
||||
/>
|
||||
<View className={'font-bold text-sm'}>
|
||||
{design?.comments}
|
||||
{shopInfo?.appName}
|
||||
</View>
|
||||
<View className={'text-left py-3 text-gray-600'}>
|
||||
<RichText
|
||||
nodes={design?.content || '关于我们的简单描述'}/>
|
||||
nodes={shopInfo?.description || '关于我们的简单描述'}/>
|
||||
</View>
|
||||
</View>
|
||||
)}
|
||||
</Cell>
|
||||
{category.map((item, index) => (
|
||||
<Cell
|
||||
title={(
|
||||
<div className={'font-bold'} id={`${index}`}>
|
||||
{item.categoryName}
|
||||
</div>
|
||||
)}
|
||||
description={(
|
||||
<Cell title={about?.title} description={(
|
||||
<>
|
||||
<Divider/>
|
||||
{item.articles?.map((child, _) => (
|
||||
<View className={'item flex justify-between items-center my-2'}>
|
||||
<View
|
||||
onClick={() => Taro.navigateTo({url: `/cms/detail/index?id=${child.articleId}`})}>{child.title}</View>
|
||||
<ArrowRight size={16} className={'text-gray-400'}/>
|
||||
<View className={'text-left text-gray-500'}>
|
||||
<RichText
|
||||
nodes={about?.content}/>
|
||||
</View>
|
||||
))}
|
||||
</>
|
||||
)}
|
||||
>
|
||||
)}>
|
||||
</Cell>
|
||||
))}
|
||||
<Cell className={'flex flex-col'}>
|
||||
<span>服务热线:{config?.tel}</span>
|
||||
<span>工作日:{config?.workDay}</span>
|
||||
<span>工作日:{config?.deliveryText}</span>
|
||||
</Cell>
|
||||
</div>
|
||||
<Line height={44}/>
|
||||
</View>
|
||||
);
|
||||
};
|
||||
|
||||
|
||||
Reference in New Issue
Block a user