feat(clinic): 添加诊所相关API接口和数据模型
- 添加挂号管理接口和数据模型- 添加医生入驻申请接口和数据模型- 添加医疗记录接口和数据模型- 添加分销商用户记录接口和数据模型- 添加病例管理接口和数据模型 - 添加药品库接口和数据模型 - 添加药品出入库接口和数据模型 - 添加药品库存接口和数据模型- 添加处方订单接口和数据模型 - 修改开发环境API基础URL为本地地址
This commit is contained in:
@@ -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
|
||||
|
||||
4
src/doctor/apply/index.config.ts
Normal file
4
src/doctor/apply/index.config.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
export default definePageConfig({
|
||||
navigationBarTitleText: '会员注册',
|
||||
navigationBarTextStyle: 'black'
|
||||
})
|
||||
56
src/doctor/apply/index.tsx
Normal file
56
src/doctor/apply/index.tsx
Normal 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;
|
||||
@@ -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()
|
||||
|
||||
// 使用主题样式
|
||||
|
||||
4
src/doctor/prescription/add.config.ts
Normal file
4
src/doctor/prescription/add.config.ts
Normal file
@@ -0,0 +1,4 @@
|
||||
export default definePageConfig({
|
||||
navigationBarTitleText: '在线开方',
|
||||
navigationBarTextStyle: 'black'
|
||||
})
|
||||
135
src/doctor/prescription/add.tsx
Normal file
135
src/doctor/prescription/add.tsx
Normal 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;
|
||||
3
src/doctor/prescription/index.config.ts
Normal file
3
src/doctor/prescription/index.config.ts
Normal file
@@ -0,0 +1,3 @@
|
||||
export default definePageConfig({
|
||||
navigationBarTitleText: '处方管理'
|
||||
})
|
||||
185
src/doctor/prescription/index.tsx
Normal file
185
src/doctor/prescription/index.tsx
Normal 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
|
||||
Reference in New Issue
Block a user