feat(clinic): 添加诊所相关API接口和数据模型

- 添加挂号管理接口和数据模型- 添加医生入驻申请接口和数据模型- 添加医疗记录接口和数据模型- 添加分销商用户记录接口和数据模型- 添加病例管理接口和数据模型
- 添加药品库接口和数据模型
- 添加药品出入库接口和数据模型
- 添加药品库存接口和数据模型- 添加处方订单接口和数据模型
- 修改开发环境API基础URL为本地地址
This commit is contained in:
2025-10-22 16:20:34 +08:00
parent a760e46e8b
commit 1873299eb8
149 changed files with 13008 additions and 509 deletions

View File

@@ -1,433 +1,325 @@
import {useEffect, useState, useRef} from "react";
import {Loading, CellGroup, Input, Form, Avatar, Button, Space} from '@nutui/nutui-react-taro'
import {Edit} from '@nutui/icons-react-taro'
import {useEffect, useState} from "react";
import {Image} from '@nutui/nutui-react-taro'
import {ConfigProvider} from '@nutui/nutui-react-taro'
import Taro from '@tarojs/taro'
import {View} from '@tarojs/components'
import FixedButton from "@/components/FixedButton";
import {useUser} from "@/hooks/useUser";
import {TenantId} from "@/config/app";
import {updateUser} from "@/api/system/user";
import {User} from "@/api/system/user/model";
import {getStoredInviteParams, handleInviteRelation} from "@/utils/invite";
import {addShopDealerUser} from "@/api/shop/shopDealerUser";
import {listUserRole, updateUserRole} from "@/api/system/userRole";
// 类型定义
interface ChooseAvatarEvent {
detail: {
avatarUrl: string;
};
}
import {
Form,
Button,
Input,
Radio,
} from '@nutui/nutui-react-taro'
import {UserVerify} from "@/api/system/userVerify/model";
import {addUserVerify, myUserVerify, updateUserVerify} from "@/api/system/userVerify";
import {uploadFile} from "@/api/system/file";
interface InputEvent {
detail: {
value: string;
};
}
function Index() {
const [isUpdate, setIsUpdate] = useState<boolean>(false)
const [submitText, setSubmitText] = useState<string>('提交')
const AddDoctor = () => {
const {user, loginUser} = useUser()
const [loading, setLoading] = useState<boolean>(true)
const [FormData, setFormData] = useState<User>()
const formRef = useRef<any>(null)
const reload = async () => {
const inviteParams = getStoredInviteParams()
if (inviteParams?.inviter) {
setFormData({
...user,
refereeId: Number(inviteParams.inviter),
// 清空昵称,强制用户手动输入
nickname: '',
})
} else {
// 如果没有邀请参数,也要确保昵称为空
setFormData({
...user,
nickname: '',
})
}
}
const uploadAvatar = ({detail}: ChooseAvatarEvent) => {
// 先更新本地显示的头像(临时显示)
const tempFormData = {
...FormData,
avatar: `${detail.avatarUrl}`,
}
setFormData(tempFormData)
Taro.uploadFile({
url: 'https://server.websoft.top/api/oss/upload',
filePath: detail.avatarUrl,
name: 'file',
header: {
'content-type': 'application/json',
TenantId
},
success: async (res) => {
const data = JSON.parse(res.data);
if (data.code === 0) {
const finalAvatarUrl = `${data.data.thumbnail}`
try {
// 使用 useUser hook 的 updateUser 方法更新头像
await updateUser({
avatar: finalAvatarUrl
})
Taro.showToast({
title: '头像上传成功',
icon: 'success',
duration: 1500
})
} catch (error) {
console.error('更新用户头像失败:', error)
}
// 无论用户信息更新是否成功都要更新本地FormData
const finalFormData = {
...tempFormData,
avatar: finalAvatarUrl
}
setFormData(finalFormData)
// 同步更新表单字段
if (formRef.current) {
formRef.current.setFieldsValue({
avatar: finalAvatarUrl
})
}
} else {
// 上传失败,恢复原来的头像
setFormData({
...FormData,
avatar: user?.avatar || ''
})
Taro.showToast({
title: '上传失败',
icon: 'error'
})
const [FormData, setFormData] = useState<UserVerify>({
userId: undefined,
type: undefined,
phone: undefined,
avatar: undefined,
realName: undefined,
idCard: undefined,
birthday: undefined,
sfz1: undefined,
sfz2: undefined,
zzCode: undefined,
zzImg: undefined,
status: undefined,
statusText: undefined,
comments: undefined
})
const reload = () => {
myUserVerify({}).then(data => {
if (data) {
setIsUpdate(true);
setFormData(data)
if(data.status == 2){
setSubmitText('重新提交')
}
},
fail: (error) => {
console.error('上传头像失败:', error)
Taro.showToast({
title: '上传失败',
icon: 'error'
})
// 恢复原来的头像
} else {
setFormData({
...FormData,
avatar: user?.avatar || ''
type: 0
})
}
})
}
// 提交表单
const submitSucceed = async (values: any) => {
try {
// 验证必填字段
if (!values.phone && !FormData?.phone) {
const submitSucceed = (values: any) => {
console.log('提交表单', values);
if (FormData.status != 2 && FormData.status != undefined) return false;
if (FormData.type == 0) {
if (!FormData.sfz1 || !FormData.sfz2) {
Taro.showToast({
title: '请先获取手机号',
icon: 'error'
title: '请上传身份证正反面',
icon: 'none'
});
return;
return false;
}
// 验证昵称:必须填写且不能是默认的微信昵称
const nickname = values.realName || FormData?.nickname || '';
if (!nickname || nickname.trim() === '') {
if (!FormData.realName || !FormData.idCard) {
Taro.showToast({
title: '请填写昵称',
icon: 'error'
title: '请填写真实姓名和身份证号码',
icon: 'none'
});
return;
return false;
}
// 检查是否为默认的微信昵称(常见的默认昵称)
const defaultNicknames = ['微信用户', 'WeChat User', '微信昵称'];
if (defaultNicknames.includes(nickname.trim())) {
}
if (FormData.type == 1) {
if (!FormData.zzImg) {
Taro.showToast({
title: '请填写真实昵称,不能使用默认昵称',
icon: 'error'
title: '请上传营业执照',
icon: 'none'
});
return;
return false;
}
// 验证昵称长度
if (nickname.trim().length < 2) {
if (!FormData.name || !FormData.zzCode) {
Taro.showToast({
title: '昵称至少需要2个字符',
icon: 'error'
title: '请填写主体名称和营业执照号码',
icon: 'none'
});
return;
return false;
}
if (!values.avatar && !FormData?.avatar) {
Taro.showToast({
title: '请上传头像',
icon: 'error'
});
return;
}
console.log(values,FormData)
const roles = await listUserRole({userId: user?.userId})
console.log(roles, 'roles...')
// 准备提交的数据
await updateUser({
userId: user?.userId,
nickname: values.realName || FormData?.nickname,
phone: values.phone || FormData?.phone,
avatar: values.avatar || FormData?.avatar,
refereeId: values.refereeId || FormData?.refereeId
});
await addShopDealerUser({
userId: user?.userId,
realName: values.realName || FormData?.nickname,
mobile: values.phone || FormData?.phone,
refereeId: values.refereeId || FormData?.refereeId
})
if (roles.length > 0) {
await updateUserRole({
...roles[0],
roleId: 1848
})
}
}
if(!FormData.realName){
Taro.showToast({
title: `注册成功`,
icon: 'success'
title: '请填写真实姓名',
icon: 'none'
});
return false;
}
const saveOrUpdate = isUpdate ? updateUserVerify : addUserVerify;
saveOrUpdate({...FormData, status: 0}).then(() => {
Taro.showToast({title: `提交成功`, icon: 'success'})
setTimeout(() => {
Taro.navigateBack();
}, 1000);
} catch (error) {
console.error('验证邀请人失败:', error);
}
}
// 获取微信昵称
const getWxNickname = (nickname: string) => {
// 更新表单数据
const updatedFormData = {
...FormData,
nickname: nickname
}
setFormData(updatedFormData);
// 同步更新表单字段
if (formRef.current) {
formRef.current.setFieldsValue({
realName: nickname
})
}
}
/* 获取用户手机号 */
const handleGetPhoneNumber = ({detail}: { detail: { code?: string, encryptedData?: string, iv?: string } }) => {
const {code, encryptedData, iv} = detail
Taro.login({
success: (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: 0,
sceneType: 'save_referee',
tenantId: TenantId
},
header: {
'content-type': 'application/json',
TenantId
},
success: async 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;
console.log(userData, 'userData...')
// 使用useUser Hook的loginUser方法更新状态
loginUser(token, userData);
if (userData.phone) {
console.log('手机号已获取', userData.phone)
const updatedFormData = {
...FormData,
phone: userData.phone,
// 不自动填充微信昵称,保持用户已输入的昵称
nickname: FormData?.nickname || '',
// 只在没有头像时才使用微信头像
avatar: FormData?.avatar || userData.avatar
}
setFormData(updatedFormData)
// 更新表单字段值
if (formRef.current) {
formRef.current.setFieldsValue({
phone: userData.phone,
// 不覆盖用户已输入的昵称
realName: FormData?.nickname || '',
avatar: FormData?.avatar || userData.avatar
})
}
Taro.showToast({
title: '手机号获取成功',
icon: 'success',
duration: 1500
})
}
// 处理邀请关系
if (userData?.userId) {
try {
const inviteSuccess = await handleInviteRelation(userData.userId)
if (inviteSuccess) {
Taro.showToast({
title: '邀请关系建立成功',
icon: 'success',
duration: 2000
})
}
} catch (error) {
console.error('处理邀请关系失败:', error)
}
}
// 显示登录成功提示
// Taro.showToast({
// title: '注册成功',
// icon: 'success',
// duration: 1500
// })
// 不需要重新启动小程序状态已经通过useUser更新
// 可以选择性地刷新当前页面数据
// await reload();
}
})
} else {
console.log('登录失败!')
}
}
return Taro.navigateBack()
}, 1000)
}).catch(() => {
Taro.showToast({
title: '提交失败',
icon: 'error'
});
}).finally(() => {
reload();
})
}
// 处理固定按钮点击事件
const handleFixedButtonClick = () => {
// 触发表单提交
formRef.current?.submit();
};
const submitFailed = (error: any) => {
console.log(error, 'err...')
}
useEffect(() => {
reload().then(() => {
setLoading(false)
})
}, [user?.userId]); // 依赖用户ID当用户变化时重新加载
// 当FormData变化时同步更新表单字段值
useEffect(() => {
if (formRef.current && FormData) {
formRef.current.setFieldsValue({
refereeId: FormData.refereeId,
phone: FormData.phone,
avatar: FormData.avatar,
realName: FormData.nickname
});
}
}, [FormData]);
if (loading) {
return <Loading className={'px-2'}></Loading>
const uploadSfz1 = () => {
if (FormData.status != 2 && FormData.status != undefined) return false;
uploadFile().then(data => {
setFormData({
...FormData,
sfz1: data.url
})
});
}
const uploadSfz2 = () => {
if (FormData.status != 2 && FormData.status != undefined) return false;
uploadFile().then(data => {
setFormData({
...FormData,
sfz2: data.url
})
});
}
const uploadZzImg = () => {
if (FormData.status != 2 && FormData.status != undefined) return false;
uploadFile().then(data => {
setFormData({
...FormData,
zzImg: data.url
})
});
}
useEffect(() => {
reload()
}, []);
return (
<>
<Form
ref={formRef}
divider
initialValues={FormData}
labelPosition="left"
onFinish={(values) => submitSucceed(values)}
onFinishFailed={(errors) => submitFailed(errors)}
>
<View className={'bg-gray-100 h-3'}></View>
<CellGroup style={{padding: '4px 0'}}>
<Form.Item name="refereeId" label="邀请人ID" initialValue={FormData?.refereeId} required>
<Input placeholder="邀请人ID" disabled={true}/>
</Form.Item>
<Form.Item name="phone" label="手机号" initialValue={FormData?.phone} required>
<View className="flex items-center justify-between">
<Input
placeholder="请填写手机号"
disabled={true}
maxLength={11}
value={FormData?.phone || ''}
/>
<Button style={{color: '#ffffff'}} open-type="getPhoneNumber" onGetPhoneNumber={handleGetPhoneNumber}>
<Space>
<Button size="small"></Button>
</Space>
</Button>
</View>
</Form.Item>
{
FormData?.phone && <Form.Item name="avatar" label="头像" initialValue={FormData?.avatar} required>
<Button open-type="chooseAvatar" style={{height: '58px'}} onChooseAvatar={uploadAvatar}>
<Avatar src={FormData?.avatar || user?.avatar} size="54"/>
</Button>
<div className={'p-4'}>
<ConfigProvider>
<Form
divider
initialValues={FormData}
labelPosition="left"
onFinish={(values) => submitSucceed(values)}
onFinishFailed={(errors) => submitFailed(errors)}
footer={
FormData.status != 1 && FormData.status != 0 && (
<div
style={{
display: 'flex',
justifyContent: 'center',
width: '100%'
}}
>
<Button nativeType="submit" block type={'info'}
disabled={FormData.status != 2 && FormData.status != undefined}>
{submitText}
</Button>
</div>
)
}
>
<Form.Item
label="类型"
name="type"
initialValue={FormData.type}
required
>
<Radio.Group value={FormData?.type} direction="horizontal"
disabled={FormData.status != 2 && FormData.status != undefined}
onChange={(value) => setFormData({...FormData, type: value})}>
<Radio key={0} value={0}>
</Radio>
<Radio key={1} value={1}>
</Radio>
</Radio.Group>
</Form.Item>
}
<Form.Item name="realName" label="昵称" initialValue="" required>
<Input
type="nickname"
className="info-content__input"
placeholder="请获取微信昵称"
value={FormData?.nickname || ''}
onInput={(e: InputEvent) => getWxNickname(e.detail.value)}
/>
</Form.Item>
</CellGroup>
</Form>
{
// 个人类型
FormData.type == 0 && (
<>
<Form.Item
label={'真实姓名'}
name="realName"
required
initialValue={FormData.realName}
rules={[{message: '请输入真实姓名'}]}
>
<Input
placeholder={'请输入真实姓名'}
type="text"
disabled={FormData.status != 2 && FormData.status != undefined}
value={FormData?.realName}
onChange={(value) => setFormData({...FormData, realName: value})}
/>
</Form.Item>
<Form.Item
label={'身份证号码'}
name="idCard"
required
initialValue={FormData.idCard}
rules={[{message: '请输入身份证号码'}]}
>
<Input
placeholder="请输入身份证号码"
type="text"
value={FormData?.idCard}
disabled={FormData.status != 2 && FormData.status != undefined}
maxLength={18}
onChange={(value) => setFormData({...FormData, idCard: value})}
/>
</Form.Item>
<Form.Item
label={'上传证件'}
name="image"
required
rules={[{message: '请上传身份证正反面'}]}
>
<div className={'flex gap-2'}>
<div onClick={uploadSfz1}>
<Image src={FormData.sfz1} lazyLoad={false}
radius="10%" width="80" height="80"/>
</div>
<div onClick={uploadSfz2}>
<Image src={FormData.sfz2} mode={'scaleToFill'} lazyLoad={false}
radius="10%" width="80" height="80"/>
</div>
</div>
</Form.Item>
</>
)
}
{
// 企业类型
FormData.type == 1 && (
<>
<Form.Item
label={'主体名称'}
name="name"
required
initialValue={FormData.name}
rules={[{message: '请输入公司名称或单位名称'}]}
>
<Input
placeholder={'请输入主体名称'}
type="text"
value={FormData?.name}
onChange={(value) => setFormData({...FormData, name: value})}
/>
</Form.Item>
<Form.Item
label={'营业执照号码'}
name="zzCode"
required
initialValue={FormData.zzCode}
rules={[{message: '请输入营业执照号码'}]}
>
<Input
placeholder="请输入营业执照号码"
type="text"
value={FormData?.zzCode}
maxLength={18}
onChange={(value) => setFormData({...FormData, zzCode: value})}
/>
</Form.Item>
<Form.Item
label={'上传营业执照'}
name="zzImg"
required
rules={[{message: '请上传营业执照'}
]}
>
<div onClick={uploadZzImg}>
<Image src={FormData.zzImg} mode={'scaleToFill'} lazyLoad={false}
radius="10%" width="80" height="80"/>
</div>
</Form.Item>
</>
)
}
{
FormData.status != undefined && (
<Form.Item
label={'审核状态'}
name="status"
>
<span className={'text-gray-500'}>{FormData.statusText}</span>
</Form.Item>
)
}
{/* 底部浮动按钮 */}
<FixedButton
icon={<Edit/>}
text={'立即注册'}
onClick={handleFixedButtonClick}
/>
{FormData.status == 2 && (
<Form.Item
label={'驳回原因'}
name="comments"
>
<div className={'flex text-orange-500'}>
{FormData.comments}
</div>
</Form.Item>
)}
</Form>
</ConfigProvider>
</div>
</>
);
};
)
}
export default AddDoctor;
export default Index

View File

@@ -0,0 +1,4 @@
export default definePageConfig({
navigationBarTitleText: '会员注册',
navigationBarTextStyle: 'black'
})

View File

@@ -0,0 +1,56 @@
import React, {useEffect} from 'react'
import {View, Text} from '@tarojs/components'
import {Space} from '@nutui/nutui-react-taro'
import navTo from "@/utils/common";
const ApplyIndex: React.FC = () => {
// 初始化当前用户名称
useEffect(() => {
}, [])
return (
<View className="p-4">
<View
className={`bg-white rounded-lg p-4 mb-3 shadow-sm`}
onClick={() => navTo(`/clinic/clinicPatientUser/add`)}
>
<View className="flex items-center mb-3">
<View className="flex-1">
<View className="flex items-center justify-between mb-1">
<View className="flex items-center">
<Space>
<Text className="font-semibold text-gray-800 mr-2"></Text>
</Space>
</View>
</View>
<Text className="text-xs text-gray-500">
</Text>
</View>
</View>
</View>
<View
className={`bg-white rounded-lg p-4 mb-3 shadow-sm`}
onClick={() => navTo(`/clinic/clinicDoctorUser/add`)}
>
<View className="flex items-center mb-3">
<View className="flex-1">
<View className="flex items-center justify-between mb-1">
<View className="flex items-center">
<Space>
<Text className="font-semibold text-gray-800 mr-2"></Text>
</Space>
</View>
</View>
<Text className="text-xs text-gray-500">
</Text>
</View>
</View>
</View>
</View>
)
}
export default ApplyIndex;

View File

@@ -1,6 +1,6 @@
import React from 'react'
import {View, Text} from '@tarojs/components'
import {ConfigProvider, Button, Grid, Avatar} from '@nutui/nutui-react-taro'
import {ConfigProvider, Grid, Avatar} from '@nutui/nutui-react-taro'
import {
User,
UserAdd,
@@ -19,9 +19,7 @@ import Taro from '@tarojs/taro'
const DealerIndex: React.FC = () => {
const {
dealerUser,
error,
refresh,
dealerUser
} = useDealerUser()
// 使用主题样式

View File

@@ -0,0 +1,4 @@
export default definePageConfig({
navigationBarTitleText: '在线开方',
navigationBarTextStyle: 'black'
})

View File

@@ -0,0 +1,135 @@
import {useEffect, useState, useRef} from "react";
import {useRouter} from '@tarojs/taro'
import {Loading, CellGroup, Input, Form, Cell, Avatar} from '@nutui/nutui-react-taro'
import {ArrowRight} from '@nutui/icons-react-taro'
import {View, Text} from '@tarojs/components'
import Taro from '@tarojs/taro'
import FixedButton from "@/components/FixedButton";
import {addShopChatMessage} from "@/api/shop/shopChatMessage";
import {ShopChatMessage} from "@/api/shop/shopChatMessage/model";
import navTo from "@/utils/common";
import {getUser} from "@/api/system/user";
import {User} from "@/api/system/user/model";
const AddMessage = () => {
const {params} = useRouter();
const [toUser, setToUser] = useState<User>()
const [loading, setLoading] = useState<boolean>(true)
const [FormData, _] = useState<ShopChatMessage>()
const formRef = useRef<any>(null)
// 判断是编辑还是新增模式
const isEditMode = !!params.id
const toUserId = params.id ? Number(params.id) : undefined
const reload = async () => {
if(toUserId){
getUser(Number(toUserId)).then(data => {
setToUser(data)
})
}
}
// 提交表单
const submitSucceed = async (values: any) => {
try {
// 准备提交的数据
const submitData = {
...values
};
console.log('提交数据:', submitData)
// 参数校验
if(!toUser){
Taro.showToast({
title: `请选择发送对象`,
icon: 'error'
});
return false;
}
// 判断内容是否为空
if (!values.content) {
Taro.showToast({
title: `请输入内容`,
icon: 'error'
});
return false;
}
// 执行新增或更新操作
await addShopChatMessage({
toUserId: toUserId,
formUserId: Taro.getStorageSync('UserId'),
type: 'text',
content: values.content
});
Taro.showToast({
title: `发送成功`,
icon: 'success'
});
setTimeout(() => {
Taro.navigateBack();
}, 1000);
} catch (error) {
console.error('发送失败:', error);
Taro.showToast({
title: `发送失败`,
icon: 'error'
});
}
}
const submitFailed = (error: any) => {
console.log(error, 'err...')
}
useEffect(() => {
reload().then(() => {
setLoading(false)
})
}, [isEditMode]);
if (loading) {
return <Loading className={'px-2'}></Loading>
}
return (
<>
<Cell title={toUser ? (
<View className={'flex items-center'}>
<Avatar src={toUser.avatar}/>
<View className={'ml-2 flex flex-col'}>
<Text>{toUser.alias || toUser.nickname}</Text>
<Text className={'text-gray-300'}>{toUser.mobile}</Text>
</View>
</View>
) : '选择患者'} extra={(
<ArrowRight color="#cccccc" className={toUser ? 'mt-2' : ''} size={toUser ? 20 : 18}/>
)}
onClick={() => navTo(`/doctor/customer/index`, true)}/>
<Form
ref={formRef}
divider
initialValues={FormData}
labelPosition="left"
onFinish={(values) => submitSucceed(values)}
onFinishFailed={(errors) => submitFailed(errors)}
>
<CellGroup style={{padding: '4px 0'}}>
<Form.Item name="content" initialValue={FormData?.content} required>
<Input placeholder="填写消息内容" maxLength={300}/>
</Form.Item>
</CellGroup>
</Form>
{/* 底部浮动按钮 */}
<FixedButton text={isEditMode ? '立即发送' : '立即发送'} onClick={() => formRef.current?.submit()}/>
</>
);
};
export default AddMessage;

View File

@@ -0,0 +1,3 @@
export default definePageConfig({
navigationBarTitleText: '处方管理'
})

View File

@@ -0,0 +1,185 @@
import React, {useState, useEffect, useCallback} from 'react'
import {View, Text, ScrollView} from '@tarojs/components'
import {Empty, Tag, PullToRefresh, Loading} from '@nutui/nutui-react-taro'
import Taro from '@tarojs/taro'
import {pageShopDealerOrder} from '@/api/shop/shopDealerOrder'
import {useDealerUser} from '@/hooks/useDealerUser'
import type {ShopDealerOrder} from '@/api/shop/shopDealerOrder/model'
interface OrderWithDetails extends ShopDealerOrder {
orderNo?: string
customerName?: string
userCommission?: string
}
const DealerOrders: React.FC = () => {
const [loading, setLoading] = useState<boolean>(false)
const [refreshing, setRefreshing] = useState<boolean>(false)
const [loadingMore, setLoadingMore] = useState<boolean>(false)
const [orders, setOrders] = useState<OrderWithDetails[]>([])
const [currentPage, setCurrentPage] = useState<number>(1)
const [hasMore, setHasMore] = useState<boolean>(true)
const {dealerUser} = useDealerUser()
// 获取订单数据
const fetchOrders = useCallback(async (page: number = 1, isRefresh: boolean = false) => {
if (!dealerUser?.userId) return
try {
if (isRefresh) {
setRefreshing(true)
} else if (page === 1) {
setLoading(true)
} else {
setLoadingMore(true)
}
const result = await pageShopDealerOrder({
page,
limit: 10
})
if (result?.list) {
const newOrders = result.list.map(order => ({
...order,
orderNo: `${order.orderId}`,
customerName: `用户${order.userId}`,
userCommission: order.firstMoney || '0.00'
}))
if (page === 1) {
setOrders(newOrders)
} else {
setOrders(prev => [...prev, ...newOrders])
}
setHasMore(newOrders.length === 10)
setCurrentPage(page)
}
} catch (error) {
console.error('获取分销订单失败:', error)
Taro.showToast({
title: '获取订单失败',
icon: 'error'
})
} finally {
setLoading(false)
setRefreshing(false)
setLoadingMore(false)
}
}, [dealerUser?.userId])
// 下拉刷新
const handleRefresh = async () => {
await fetchOrders(1, true)
}
// 加载更多
const handleLoadMore = async () => {
if (!loadingMore && hasMore) {
await fetchOrders(currentPage + 1)
}
}
// 初始化加载数据
useEffect(() => {
if (dealerUser?.userId) {
fetchOrders(1)
}
}, [fetchOrders])
const getStatusText = (isSettled?: number, isInvalid?: number) => {
if (isInvalid === 1) return '已失效'
if (isSettled === 1) return '已结算'
return '待结算'
}
const getStatusColor = (isSettled?: number, isInvalid?: number) => {
if (isInvalid === 1) return 'danger'
if (isSettled === 1) return 'success'
return 'warning'
}
const renderOrderItem = (order: OrderWithDetails) => (
<View key={order.id} className="bg-white rounded-lg p-4 mb-3 shadow-sm">
<View className="flex justify-between items-start mb-1">
<Text className="font-semibold text-gray-800">
{order.orderNo}
</Text>
<Tag type={getStatusColor(order.isSettled, order.isInvalid)}>
{getStatusText(order.isSettled, order.isInvalid)}
</Tag>
</View>
<View className="flex justify-between items-center mb-1">
<Text className="text-sm text-gray-400">
¥{order.orderPrice || '0.00'}
</Text>
<Text className="text-sm text-orange-500 font-semibold">
¥{order.userCommission}
</Text>
</View>
<View className="flex justify-between items-center">
<Text className="text-sm text-gray-400">
{order.customerName}
</Text>
<Text className="text-sm text-gray-400">
{order.createTime}
</Text>
</View>
</View>
)
return (
<View className="min-h-screen bg-gray-50">
<PullToRefresh
onRefresh={handleRefresh}
disabled={refreshing}
pullingText="下拉刷新"
canReleaseText="释放刷新"
refreshingText="刷新中..."
completeText="刷新完成"
>
<ScrollView
scrollY
className="h-screen"
onScrollToLower={handleLoadMore}
lowerThreshold={50}
>
<View className="p-4">
{loading && orders.length === 0 ? (
<View className="text-center py-8">
<Loading/>
<Text className="text-gray-500 mt-2">...</Text>
</View>
) : orders.length > 0 ? (
<>
{orders.map(renderOrderItem)}
{loadingMore && (
<View className="text-center py-4">
<Loading/>
<Text className="text-gray-500 mt-1 text-sm">...</Text>
</View>
)}
{!hasMore && orders.length > 0 && (
<View className="text-center py-4">
<Text className="text-gray-400 text-sm"></Text>
</View>
)}
</>
) : (
<Empty description="暂无处方" style={{
backgroundColor: 'transparent'
}}/>
)}
</View>
</ScrollView>
</PullToRefresh>
</View>
)
}
export default DealerOrders