完善功能
This commit is contained in:
@@ -2,19 +2,23 @@
|
|||||||
export const ENV_CONFIG = {
|
export const ENV_CONFIG = {
|
||||||
// 开发环境
|
// 开发环境
|
||||||
development: {
|
development: {
|
||||||
API_BASE_URL: 'http://127.0.0.1:9200/api',
|
API_BASE_URL: 'https://clinic-api.websoft.top/api',
|
||||||
|
// API_BASE_URL: 'http://127.0.0.1:9200/api',
|
||||||
|
WS_URL: 'ws://127.0.0.1:9200/api/chat',
|
||||||
APP_NAME: '开发环境',
|
APP_NAME: '开发环境',
|
||||||
DEBUG: 'true',
|
DEBUG: 'true',
|
||||||
},
|
},
|
||||||
// 生产环境
|
// 生产环境
|
||||||
production: {
|
production: {
|
||||||
API_BASE_URL: 'https://clinic-api.websoft.top/api',
|
API_BASE_URL: 'https://clinic-api.websoft.top/api',
|
||||||
|
WS_URL: 'wss://clinic-api.websoft.top/api/chat',
|
||||||
APP_NAME: '通源堂健康生态平台',
|
APP_NAME: '通源堂健康生态平台',
|
||||||
DEBUG: 'false',
|
DEBUG: 'false',
|
||||||
},
|
},
|
||||||
// 测试环境
|
// 测试环境
|
||||||
test: {
|
test: {
|
||||||
API_BASE_URL: 'https://clinic-api.websoft.top/api',
|
API_BASE_URL: 'https://clinic-api.websoft.top/api',
|
||||||
|
WS_URL: 'wss://clinic-api.websoft.top/api/chat',
|
||||||
APP_NAME: '测试环境',
|
APP_NAME: '测试环境',
|
||||||
DEBUG: 'true',
|
DEBUG: 'true',
|
||||||
}
|
}
|
||||||
@@ -37,6 +41,7 @@ export function getEnvConfig() {
|
|||||||
// 导出环境变量
|
// 导出环境变量
|
||||||
export const {
|
export const {
|
||||||
API_BASE_URL,
|
API_BASE_URL,
|
||||||
|
WS_URL,
|
||||||
APP_NAME,
|
APP_NAME,
|
||||||
DEBUG
|
DEBUG
|
||||||
} = getEnvConfig()
|
} = getEnvConfig()
|
||||||
|
|||||||
@@ -99,3 +99,13 @@ export async function getClinicDoctorUser(id: number) {
|
|||||||
}
|
}
|
||||||
return Promise.reject(new Error(res.message));
|
return Promise.reject(new Error(res.message));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function getClinicDoctorUserByUserId(id: number) {
|
||||||
|
const res = await request.get<ApiResult<ClinicDoctorUser>>(
|
||||||
|
'/clinic/clinic-doctor-user/getByUserId/' + id
|
||||||
|
);
|
||||||
|
if (res.code === 0 && res.data) {
|
||||||
|
return res.data;
|
||||||
|
}
|
||||||
|
return Promise.reject(new Error(res.message));
|
||||||
|
}
|
||||||
|
|||||||
@@ -16,6 +16,20 @@ export async function pageClinicPatientUser(params: ClinicPatientUserParam) {
|
|||||||
return Promise.reject(new Error(res.message));
|
return Promise.reject(new Error(res.message));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 分页查询患者
|
||||||
|
*/
|
||||||
|
export async function userPageClinicPatientUser(params: ClinicPatientUserParam) {
|
||||||
|
const res = await request.get<ApiResult<PageResult<ClinicPatientUser>>>(
|
||||||
|
'/clinic/clinic-patient-user/userPage',
|
||||||
|
params
|
||||||
|
);
|
||||||
|
if (res.code === 0) {
|
||||||
|
return res.data;
|
||||||
|
}
|
||||||
|
return Promise.reject(new Error(res.message));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 查询患者列表
|
* 查询患者列表
|
||||||
*/
|
*/
|
||||||
@@ -99,3 +113,13 @@ export async function getClinicPatientUser(id: number) {
|
|||||||
}
|
}
|
||||||
return Promise.reject(new Error(res.message));
|
return Promise.reject(new Error(res.message));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function clinicPatientUserByPatientUserId(id: number) {
|
||||||
|
const res = await request.get<ApiResult<ClinicPatientUser>>(
|
||||||
|
'/clinic/clinic-patient-user/getByPatientUserId/' + id
|
||||||
|
);
|
||||||
|
if (res.code === 0 && res.data) {
|
||||||
|
return res.data;
|
||||||
|
}
|
||||||
|
return Promise.reject(new Error(res.message));
|
||||||
|
}
|
||||||
|
|||||||
@@ -10,6 +10,7 @@ export interface ClinicPatientUser {
|
|||||||
type?: number;
|
type?: number;
|
||||||
// 自增ID
|
// 自增ID
|
||||||
userId?: number;
|
userId?: number;
|
||||||
|
patientUserId?: number;
|
||||||
// 姓名
|
// 姓名
|
||||||
realName?: string;
|
realName?: string;
|
||||||
// 头像
|
// 头像
|
||||||
|
|||||||
@@ -1,5 +1,6 @@
|
|||||||
import type { PageParam } from '@/api/index';
|
import type { PageParam } from '@/api/index';
|
||||||
import {ClinicPrescriptionItem} from "@/api/clinic/clinicPrescriptionItem/model";
|
import {ClinicPrescriptionItem} from "@/api/clinic/clinicPrescriptionItem/model";
|
||||||
|
import {ShopOrder} from "@/api/shop/shopOrder/model";
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 处方主表
|
* 处方主表
|
||||||
@@ -66,6 +67,7 @@ export interface ClinicPrescription {
|
|||||||
payStatus?: boolean;
|
payStatus?: boolean;
|
||||||
// 订单状态
|
// 订单状态
|
||||||
orderStatus?: number;
|
orderStatus?: number;
|
||||||
|
shopOrder?: ShopOrder
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -77,4 +79,5 @@ export interface ClinicPrescriptionParam extends PageParam {
|
|||||||
doctorId?: number;
|
doctorId?: number;
|
||||||
userId?: number;
|
userId?: number;
|
||||||
keywords?: string;
|
keywords?: string;
|
||||||
|
withDoctor?: boolean;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -46,6 +46,16 @@ export async function addClinicPrescriptionItem(data: ClinicPrescriptionItem) {
|
|||||||
}
|
}
|
||||||
return Promise.reject(new Error(res.message));
|
return Promise.reject(new Error(res.message));
|
||||||
}
|
}
|
||||||
|
export async function batchAddClinicPrescriptionItem(data: ClinicPrescriptionItem[]) {
|
||||||
|
const res = await request.post<ApiResult<unknown>>(
|
||||||
|
'/clinic/clinic-prescription-item/batch',
|
||||||
|
data
|
||||||
|
);
|
||||||
|
if (res.code === 0) {
|
||||||
|
return res.message;
|
||||||
|
}
|
||||||
|
return Promise.reject(new Error(res.message));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 修改处方明细表
|
* 修改处方明细表
|
||||||
|
|||||||
@@ -34,12 +34,12 @@ export async function listShopChatConversation(params?: ShopChatConversationPara
|
|||||||
* 添加聊天会话表
|
* 添加聊天会话表
|
||||||
*/
|
*/
|
||||||
export async function addShopChatConversation(data: ShopChatConversation) {
|
export async function addShopChatConversation(data: ShopChatConversation) {
|
||||||
const res = await request.post<ApiResult<unknown>>(
|
const res = await request.post<ApiResult<ShopChatConversation>>(
|
||||||
'/shop/shop-chat-conversation',
|
'/shop/shop-chat-conversation',
|
||||||
data
|
data
|
||||||
);
|
);
|
||||||
if (res.code === 0) {
|
if (res.code === 0) {
|
||||||
return res.message;
|
return res.data;
|
||||||
}
|
}
|
||||||
return Promise.reject(new Error(res.message));
|
return Promise.reject(new Error(res.message));
|
||||||
}
|
}
|
||||||
@@ -99,3 +99,13 @@ export async function getShopChatConversation(id: number) {
|
|||||||
}
|
}
|
||||||
return Promise.reject(new Error(res.message));
|
return Promise.reject(new Error(res.message));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function chatConversationByBothUserId(userId: string) {
|
||||||
|
const res = await request.get<ApiResult<ShopChatConversation>>(
|
||||||
|
'/shop/shop-chat-conversation/getByBothUserId/' + userId
|
||||||
|
);
|
||||||
|
if (res.code === 0) {
|
||||||
|
return res.data;
|
||||||
|
}
|
||||||
|
return Promise.reject(new Error(res.message));
|
||||||
|
}
|
||||||
|
|||||||
@@ -30,6 +30,8 @@ export interface ShopChatMessage {
|
|||||||
type?: string;
|
type?: string;
|
||||||
// 消息内容
|
// 消息内容
|
||||||
content?: string;
|
content?: string;
|
||||||
|
// 会话ID
|
||||||
|
conversationId?: number;
|
||||||
// 屏蔽接收方
|
// 屏蔽接收方
|
||||||
sideTo?: number;
|
sideTo?: number;
|
||||||
// 屏蔽发送方
|
// 屏蔽发送方
|
||||||
@@ -52,6 +54,8 @@ export interface ShopChatMessage {
|
|||||||
createTime?: string;
|
createTime?: string;
|
||||||
// 修改时间
|
// 修改时间
|
||||||
updateTime?: string;
|
updateTime?: string;
|
||||||
|
// 是否本人消息
|
||||||
|
isMine?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -59,5 +63,6 @@ export interface ShopChatMessage {
|
|||||||
*/
|
*/
|
||||||
export interface ShopChatMessageParam extends PageParam {
|
export interface ShopChatMessageParam extends PageParam {
|
||||||
id?: number;
|
id?: number;
|
||||||
|
conversationId?: number;
|
||||||
keywords?: string;
|
keywords?: string;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -53,7 +53,7 @@ export async function updateShopOrder(data: ShopOrder) {
|
|||||||
data
|
data
|
||||||
);
|
);
|
||||||
if (res.code === 0) {
|
if (res.code === 0) {
|
||||||
return res.message;
|
return res;
|
||||||
}
|
}
|
||||||
return Promise.reject(new Error(res.message));
|
return Promise.reject(new Error(res.message));
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -147,6 +147,7 @@ export interface ShopOrder {
|
|||||||
hasTakeGift?: string;
|
hasTakeGift?: string;
|
||||||
// 订单商品项
|
// 订单商品项
|
||||||
orderGoods?: OrderGoods[];
|
orderGoods?: OrderGoods[];
|
||||||
|
makePay?: boolean
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -44,6 +44,17 @@ export async function addShopOrderGoods(data: ShopOrderGoods) {
|
|||||||
return Promise.reject(new Error(res.message));
|
return Promise.reject(new Error(res.message));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export async function batchAddShopOrderGoods(data: ShopOrderGoods[]) {
|
||||||
|
const res = await request.post<ApiResult<unknown>>(
|
||||||
|
'/shop/shop-order-goods/batch',
|
||||||
|
data
|
||||||
|
);
|
||||||
|
if (res.code === 0) {
|
||||||
|
return res.message;
|
||||||
|
}
|
||||||
|
return Promise.reject(new Error(res.message));
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 修改商品信息
|
* 修改商品信息
|
||||||
*/
|
*/
|
||||||
|
|||||||
@@ -82,6 +82,7 @@ export interface Order {
|
|||||||
updateTime?: string;
|
updateTime?: string;
|
||||||
// 创建时间
|
// 创建时间
|
||||||
createTime?: string;
|
createTime?: string;
|
||||||
|
makePay?: boolean;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -108,6 +108,7 @@ export default {
|
|||||||
"clinicPatientUser/add",
|
"clinicPatientUser/add",
|
||||||
"clinicPatientUser/selectPatient",
|
"clinicPatientUser/selectPatient",
|
||||||
"clinicPatientUser/prescription",
|
"clinicPatientUser/prescription",
|
||||||
|
"clinicPatientUser/detail",
|
||||||
"clinicDoctorUser/index",
|
"clinicDoctorUser/index",
|
||||||
"clinicDoctorUser/add",
|
"clinicDoctorUser/add",
|
||||||
"clinicPrescription/index",
|
"clinicPrescription/index",
|
||||||
|
|||||||
@@ -1,158 +1,284 @@
|
|||||||
import {useState, useEffect} from 'react'
|
import {useState, useEffect, useRef} from 'react'
|
||||||
import {View, Text} from '@tarojs/components'
|
import {View, Text} from '@tarojs/components'
|
||||||
import Taro, {useDidShow} from '@tarojs/taro'
|
import Taro, {useDidShow, useRouter, useLoad} from '@tarojs/taro'
|
||||||
import {useRouter} from '@tarojs/taro'
|
import {Input, Button} from '@nutui/nutui-react-taro'
|
||||||
import {
|
import {Voice, FaceMild} from '@nutui/icons-react-taro'
|
||||||
Loading,
|
import {getClinicDoctorUserByUserId} from "@/api/clinic/clinicDoctorUser";
|
||||||
InfiniteLoading,
|
import {addShopChatMessage, listShopChatMessage} from "@/api/shop/shopChatMessage";
|
||||||
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 {ShopChatMessage} from "@/api/shop/shopChatMessage/model";
|
||||||
import Line from "@/components/Gap";
|
import {addShopChatConversation, chatConversationByBothUserId} from "@/api/shop/shopChatConversation";
|
||||||
|
// @ts-ignore
|
||||||
|
import {WS_URL} from "@/config/env";
|
||||||
|
import {clinicPatientUserByPatientUserId} from "@/api/clinic/clinicPatientUser";
|
||||||
|
|
||||||
const CustomerIndex = () => {
|
const CustomerIndex = () => {
|
||||||
const {params} = useRouter();
|
const {params} = useRouter()
|
||||||
const [doctor, setDoctor] = useState<ClinicDoctorUser>()
|
const [messages, setMessages] = useState<ShopChatMessage[]>([])
|
||||||
const [list, setList] = useState<ShopChatMessage[]>([])
|
const [conversationId, setConversationId] = useState<number | null>(null)
|
||||||
|
const [friendUserId, setFriendUserId] = useState<string>('')
|
||||||
|
const [messageInput, setMessageInput] = useState<string>('')
|
||||||
|
const [sending, setSending] = useState<boolean>(false)
|
||||||
const [isDoctor, setIsDoctor] = useState<boolean>(false)
|
const [isDoctor, setIsDoctor] = useState<boolean>(false)
|
||||||
const [doctors, setDoctors] = useState<ClinicDoctorUser[]>([])
|
const socketRef = useRef<Taro.SocketTask | null>(null)
|
||||||
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 quickActions = [
|
||||||
const fetchData = async () => {
|
{label: '开方', type: 'prescription'},
|
||||||
setLoading(true);
|
{label: '快捷回复', type: 'quickReply'},
|
||||||
|
{label: '发问诊单', type: 'sendInquiry'}
|
||||||
|
]
|
||||||
|
|
||||||
|
const handleQuickAction = (actionType: string) => {
|
||||||
|
switch (actionType) {
|
||||||
|
case 'prescription':
|
||||||
|
if (friendUserId) {
|
||||||
|
Taro.navigateTo({url: `/doctor/orders/add?id=${friendUserId}`})
|
||||||
|
} else {
|
||||||
|
Taro.showToast({title: '请选择患者', icon: 'none'})
|
||||||
|
}
|
||||||
|
break
|
||||||
|
case 'quickReply':
|
||||||
|
Taro.showToast({title: '快捷回复敬请期待', icon: 'none'})
|
||||||
|
break
|
||||||
|
case 'sendInquiry':
|
||||||
|
Taro.showToast({title: '问诊单功能敬请期待', icon: 'none'})
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const fetchData = async (userId?: string) => {
|
||||||
|
if (!userId) return
|
||||||
|
try {
|
||||||
|
console.log("Taro.getStorageSync('Doctor')", Taro.getStorageSync('Doctor'))
|
||||||
if (Taro.getStorageSync('Doctor')) {
|
if (Taro.getStorageSync('Doctor')) {
|
||||||
setIsDoctor(true)
|
setIsDoctor(true)
|
||||||
}
|
}
|
||||||
const doctorUser = await getClinicDoctorUser(Number(params.id))
|
const isDoctorData = Taro.getStorageSync('Doctor')
|
||||||
if (doctorUser) {
|
if (!isDoctorData) {
|
||||||
setDoctor(doctorUser)
|
const doctorUser = await getClinicDoctorUserByUserId(Number(params.userId))
|
||||||
Taro.setNavigationBarTitle({title: `${doctorUser.realName}`});
|
if (doctorUser?.realName) {
|
||||||
|
await Taro.setNavigationBarTitle({title: `${doctorUser.realName}`})
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
const patient = await clinicPatientUserByPatientUserId(Number(params.userId))
|
||||||
|
if (patient?.realName) {
|
||||||
|
await Taro.setNavigationBarTitle({title: `${patient.realName}`})
|
||||||
}
|
}
|
||||||
const messages = await pageShopChatMessage({})
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// 初始化数据
|
let conversation = await chatConversationByBothUserId(userId)
|
||||||
|
if (!conversation) {
|
||||||
|
conversation = await addShopChatConversation({
|
||||||
|
friendId: parseInt(userId, 10),
|
||||||
|
content: ''
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
if (conversation?.id) {
|
||||||
|
setConversationId(conversation.id)
|
||||||
|
const messageList = await listShopChatMessage({conversationId: conversation.id})
|
||||||
|
setMessages(messageList || [])
|
||||||
|
} else {
|
||||||
|
setMessages([])
|
||||||
|
}
|
||||||
|
} catch (error) {
|
||||||
|
console.error('加载聊天数据失败:', error)
|
||||||
|
Taro.showToast({title: '聊天加载失败', icon: 'none'})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const connectWebSocket = async (id: number) => {
|
||||||
|
const base = (WS_URL || '').replace(/\/$/, '')
|
||||||
|
if (!base) {
|
||||||
|
console.warn('WS_URL 未配置')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if (socketRef.current) {
|
||||||
|
socketRef.current.close({})
|
||||||
|
}
|
||||||
|
|
||||||
|
const userId = Taro.getStorageSync('UserId')
|
||||||
|
const result = Taro.connectSocket({
|
||||||
|
url: `${base}/${id}_${userId}`
|
||||||
|
})
|
||||||
|
|
||||||
|
const socketTask = typeof (result as Promise<any>)?.then === 'function'
|
||||||
|
? await (result as Promise<Taro.SocketTask>)
|
||||||
|
: (result as Taro.SocketTask)
|
||||||
|
|
||||||
|
if (!socketTask) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
socketRef.current = socketTask
|
||||||
|
|
||||||
|
socketTask.onOpen(() => {
|
||||||
|
console.log('WebSocket连接成功')
|
||||||
|
})
|
||||||
|
|
||||||
|
socketTask.onMessage((event) => {
|
||||||
|
console.log('收到消息:', event,)
|
||||||
|
try {
|
||||||
|
if (event.data === '连接成功' || event.data === 'pong') return
|
||||||
|
const payload = typeof event.data === 'string' ? JSON.parse(event.data) : event.data
|
||||||
|
if (!payload) return
|
||||||
|
if (Array.isArray(payload)) {
|
||||||
|
setMessages(prev => [...prev, ...payload])
|
||||||
|
} else {
|
||||||
|
payload.isMine = parseInt(payload.fromUserId) !== parseInt(params?.userId)
|
||||||
|
setMessages(prev => [...prev, payload])
|
||||||
|
}
|
||||||
|
} catch (err) {
|
||||||
|
console.error('解析消息失败:', err)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
socketTask.onError((err) => {
|
||||||
|
console.error('WebSocket异常:', err)
|
||||||
|
})
|
||||||
|
|
||||||
|
socketTask.onClose(() => {
|
||||||
|
socketRef.current = null
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleSendMessage = async () => {
|
||||||
|
const content = messageInput.trim()
|
||||||
|
if (!content) {
|
||||||
|
Taro.showToast({title: '请输入内容', icon: 'none'})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (!conversationId || !friendUserId) {
|
||||||
|
Taro.showToast({title: '会话未初始化', icon: 'none'})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (sending) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
setSending(true)
|
||||||
|
await addShopChatMessage({
|
||||||
|
content,
|
||||||
|
conversationId,
|
||||||
|
toUserId: parseInt(friendUserId, 10),
|
||||||
|
type: 'text'
|
||||||
|
})
|
||||||
|
|
||||||
|
// const localMessage: ShopChatMessage = {
|
||||||
|
// id: Date.now(),
|
||||||
|
// content,
|
||||||
|
// conversationId,
|
||||||
|
// toUserId: parseInt(friendUserId, 10),
|
||||||
|
// type: 'text',
|
||||||
|
// isMine: true
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// setMessages(prev => [...prev, localMessage])
|
||||||
|
setMessageInput('')
|
||||||
|
} catch (error) {
|
||||||
|
console.error('发送消息失败:', error)
|
||||||
|
Taro.showToast({title: '发送失败,请重试', icon: 'none'})
|
||||||
|
} finally {
|
||||||
|
setSending(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
useLoad((options) => {
|
||||||
|
if (options?.userId) {
|
||||||
|
const userId = String(options.userId)
|
||||||
|
setFriendUserId(userId)
|
||||||
|
fetchData(userId)
|
||||||
|
}
|
||||||
|
console.log('Taro.getStorageSync(\'UserId\')', Taro.getStorageSync('UserId'))
|
||||||
|
})
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
fetchData().then()
|
if (conversationId) {
|
||||||
}, []);
|
connectWebSocket(conversationId).catch(err => {
|
||||||
|
console.error('WebSocket连接失败:', err)
|
||||||
// 监听页面显示,当从其他页面返回时刷新数据
|
})
|
||||||
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>
|
|
||||||
}
|
}
|
||||||
{
|
return () => {
|
||||||
item.age && (
|
socketRef.current?.close()
|
||||||
<>
|
socketRef.current = null
|
||||||
<Divider direction="vertical"/>
|
|
||||||
<Text className={'text-gray-400 text-xs'}>{item.age}岁</Text>
|
|
||||||
</>
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
{
|
}, [conversationId])
|
||||||
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 (
|
return (
|
||||||
<View className="min-h-screen bg-gray-50 w-full">
|
<View className="min-h-screen bg-gray-50 w-full pb-24">
|
||||||
<View className={'p-4'}>
|
<View className="px-4 pt-4 pb-24">
|
||||||
{list?.map(renderPatientUserItem)}
|
{messages.length === 0 ? (
|
||||||
|
<View className="mt-10 text-center text-gray-400 text-sm">
|
||||||
|
<Text>暂时还没有聊天记录</Text>
|
||||||
</View>
|
</View>
|
||||||
<View className={'fixed bottom-0 w-full bg-orange-50 pt-2 pb-8'}>
|
) : (
|
||||||
<View className={'flex flex-1 items-center justify-between'}>
|
messages.map((item, index) => (
|
||||||
<Voice className={'mx-2'} />
|
<View
|
||||||
<Input className={'w-full'} style={{
|
key={item.id || `msg-${index}`}
|
||||||
|
className={`flex mb-4 ${item.isMine ? 'justify-end' : 'justify-start'}`}
|
||||||
|
>
|
||||||
|
<View
|
||||||
|
className={`px-3 py-2 rounded-2xl text-sm leading-relaxed break-words ${item.isMine ? 'bg-amber-400 text-white' : 'bg-white text-gray-800'}`}
|
||||||
|
style={{maxWidth: '75%'}}
|
||||||
|
>
|
||||||
|
<Text>{item.content}</Text>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
))
|
||||||
|
)}
|
||||||
|
</View>
|
||||||
|
|
||||||
|
<View className="fixed bottom-0 left-0 right-0 bg-orange-50 pt-2 pb-8 px-3 border-t border-orange-100">
|
||||||
|
<View className="flex gap-3 mb-2 overflow-x-auto">
|
||||||
|
{quickActions.map(action => (
|
||||||
|
<View
|
||||||
|
key={action.type}
|
||||||
|
onClick={() => handleQuickAction(action.type)}
|
||||||
|
style={{
|
||||||
|
padding: '6px 12px',
|
||||||
|
borderRadius: '20px',
|
||||||
|
backgroundColor: '#fff',
|
||||||
|
fontSize: '14px',
|
||||||
|
color: '#8b5a2b',
|
||||||
|
boxShadow: '0 4px 10px rgba(0,0,0,0.05)',
|
||||||
|
display: 'flex',
|
||||||
|
alignItems: 'center',
|
||||||
|
flexShrink: 0
|
||||||
|
}}
|
||||||
|
>
|
||||||
|
<Text>{action.label}</Text>
|
||||||
|
</View>
|
||||||
|
))}
|
||||||
|
</View>
|
||||||
|
<View className="flex flex-1 items-center justify-between">
|
||||||
|
<Voice className="mx-2"/>
|
||||||
|
<Input
|
||||||
|
className="w-full"
|
||||||
|
style={{
|
||||||
borderRadius: '6px',
|
borderRadius: '6px',
|
||||||
paddingLeft: '12px',
|
paddingLeft: '12px',
|
||||||
paddingRight: '12px'
|
paddingRight: '12px'
|
||||||
}} />
|
}}
|
||||||
<FaceMild size={26} className={'ml-2'} />
|
value={messageInput}
|
||||||
<AddCircle size={26} className={'mx-2'} />
|
onChange={(value) => setMessageInput(value)}
|
||||||
|
confirmType="send"
|
||||||
|
onConfirm={handleSendMessage}
|
||||||
|
/>
|
||||||
|
<FaceMild size={26} className="ml-2"/>
|
||||||
|
<Button
|
||||||
|
size="small"
|
||||||
|
type="primary"
|
||||||
|
className="ml-2"
|
||||||
|
loading={sending}
|
||||||
|
onClick={handleSendMessage}
|
||||||
|
>
|
||||||
|
发送
|
||||||
|
</Button>
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
);
|
)
|
||||||
};
|
}
|
||||||
|
|
||||||
export default CustomerIndex;
|
export default CustomerIndex;
|
||||||
|
|||||||
4
src/clinic/clinicPatientUser/detail.config.ts
Normal file
4
src/clinic/clinicPatientUser/detail.config.ts
Normal file
@@ -0,0 +1,4 @@
|
|||||||
|
export default definePageConfig({
|
||||||
|
navigationBarTitleText: '开方详情',
|
||||||
|
navigationBarTextStyle: 'black'
|
||||||
|
})
|
||||||
72
src/clinic/clinicPatientUser/detail.scss
Normal file
72
src/clinic/clinicPatientUser/detail.scss
Normal file
@@ -0,0 +1,72 @@
|
|||||||
|
.prescription-detail-page {
|
||||||
|
min-height: 100vh;
|
||||||
|
background: #f7f8fa;
|
||||||
|
padding: 12px;
|
||||||
|
box-sizing: border-box;
|
||||||
|
font-size: 25rpx;
|
||||||
|
line-height: 1.6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.detail-header-card {
|
||||||
|
background: #ffffff;
|
||||||
|
border-radius: 16px;
|
||||||
|
padding: 16px;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.04);
|
||||||
|
}
|
||||||
|
|
||||||
|
.detail-header-card__time {
|
||||||
|
font-weight: 600;
|
||||||
|
color: #1f2c3d;
|
||||||
|
font-size: 25rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.detail-card {
|
||||||
|
background: #ffffff;
|
||||||
|
border-radius: 16px;
|
||||||
|
padding: 16px;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
font-size: 25rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.detail-action-row {
|
||||||
|
margin-top: 12px;
|
||||||
|
display: flex;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.detail-row {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
margin-bottom: 6px;
|
||||||
|
color: #4a5568;
|
||||||
|
font-size: 25rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.detail-row strong {
|
||||||
|
color: #1f2c3d;
|
||||||
|
}
|
||||||
|
|
||||||
|
.detail-medicine-list {
|
||||||
|
display: flex;
|
||||||
|
flex-wrap: wrap;
|
||||||
|
gap: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.detail-medicine-chip {
|
||||||
|
padding: 6px 10px;
|
||||||
|
background: #f5f7fa;
|
||||||
|
border-radius: 12px;
|
||||||
|
color: #1f2c3d;
|
||||||
|
font-size: 25rpx;
|
||||||
|
}
|
||||||
|
|
||||||
|
.detail-section-title {
|
||||||
|
font-weight: 600;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
color: #1f2c3d;
|
||||||
|
font-size: 25rpx;
|
||||||
|
}
|
||||||
159
src/clinic/clinicPatientUser/detail.tsx
Normal file
159
src/clinic/clinicPatientUser/detail.tsx
Normal file
@@ -0,0 +1,159 @@
|
|||||||
|
import {useEffect, useState} from "react";
|
||||||
|
import {View, Text} from '@tarojs/components'
|
||||||
|
import {Button, Cell, CellGroup, Loading, Space, Tag} from '@nutui/nutui-react-taro'
|
||||||
|
import Taro, {useRouter} from '@tarojs/taro'
|
||||||
|
import {ClinicPrescription} from "@/api/clinic/clinicPrescription/model";
|
||||||
|
import {getClinicPrescription} from "@/api/clinic/clinicPrescription";
|
||||||
|
import {copyText} from "@/utils/common";
|
||||||
|
import './detail.scss'
|
||||||
|
|
||||||
|
const statusMap: Record<number, string> = {
|
||||||
|
0: '待开方',
|
||||||
|
1: '已完成',
|
||||||
|
2: '已支付',
|
||||||
|
3: '已取消'
|
||||||
|
}
|
||||||
|
|
||||||
|
const PrescriptionDetail = () => {
|
||||||
|
const {params} = useRouter()
|
||||||
|
const [detail, setDetail] = useState<ClinicPrescription>()
|
||||||
|
const [loading, setLoading] = useState<boolean>(false)
|
||||||
|
|
||||||
|
const loadDetail = async () => {
|
||||||
|
if (!params.id) return
|
||||||
|
try {
|
||||||
|
setLoading(true)
|
||||||
|
const data = await getClinicPrescription(Number(params.id))
|
||||||
|
setDetail(data)
|
||||||
|
} catch (error) {
|
||||||
|
Taro.showToast({
|
||||||
|
title: '加载详情失败',
|
||||||
|
icon: 'error'
|
||||||
|
})
|
||||||
|
} finally {
|
||||||
|
setLoading(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
loadDetail()
|
||||||
|
}, [params.id])
|
||||||
|
|
||||||
|
const getPatientDesc = () => {
|
||||||
|
if (!detail) return ''
|
||||||
|
const sexText = detail.sex === 0 ? '男' : detail.sex === 1 ? '女' : ''
|
||||||
|
const ageText = detail.age ? `${detail.age}岁` : ''
|
||||||
|
return [detail.realName, sexText, ageText].filter(Boolean).join(' ')
|
||||||
|
}
|
||||||
|
|
||||||
|
if (loading || !detail) {
|
||||||
|
return (
|
||||||
|
<View className="flex items-center justify-center h-full py-10">
|
||||||
|
<Loading>加载中...</Loading>
|
||||||
|
</View>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const medicines = detail.items || []
|
||||||
|
// const shopOrder: any = (detail as any).shopOrder
|
||||||
|
|
||||||
|
return (
|
||||||
|
<View className="prescription-detail-page">
|
||||||
|
<View className="detail-header-card">
|
||||||
|
<View>
|
||||||
|
<Text className="detail-header-card__time">{detail.createTime || ''}</Text>
|
||||||
|
<View className="text-gray-500 mt-1" style={{fontSize: '25rpx'}}>开方</View>
|
||||||
|
</View>
|
||||||
|
<Text className="text-gray-700" style={{fontSize: '25rpx'}}>{detail.doctorName || ''}</Text>
|
||||||
|
</View>
|
||||||
|
|
||||||
|
{/*<View className="detail-card">*/}
|
||||||
|
{/* <Text className="detail-card__title">处方状态</Text>*/}
|
||||||
|
{/* <Text className="detail-status">{statusMap[detail.status || 0] || '待处理'}</Text>*/}
|
||||||
|
{/* {shopOrder?.address && (*/}
|
||||||
|
{/* <Text className="detail-address">*/}
|
||||||
|
{/* {shopOrder.address}*/}
|
||||||
|
{/* </Text>*/}
|
||||||
|
{/* )}*/}
|
||||||
|
{/* <View className="detail-action-row">*/}
|
||||||
|
{/* <Button size="small" plain type="primary" onClick={() => Taro.showToast({title: '敬请期待', icon: 'none'})}>*/}
|
||||||
|
{/* 查看物流*/}
|
||||||
|
{/* </Button>*/}
|
||||||
|
{/* <Button size="small" plain type="success" onClick={() => Taro.showToast({title: '已复制开方信息', icon: 'none'})}>*/}
|
||||||
|
{/* 复诊开方*/}
|
||||||
|
{/* </Button>*/}
|
||||||
|
{/* </View>*/}
|
||||||
|
{/*</View>*/}
|
||||||
|
|
||||||
|
<View className="detail-card">
|
||||||
|
<View className="detail-row" style={{fontSize: '25rpx'}}>
|
||||||
|
<Text>药方编号</Text>
|
||||||
|
<Space>
|
||||||
|
<Text className="text-gray-700" style={{fontSize: '25rpx'}}>{detail.orderNo || '-'}</Text>
|
||||||
|
{detail.orderNo && (
|
||||||
|
<Text className="text-green-600" style={{fontSize: '25rpx'}} onClick={() => copyText(detail.orderNo || '')}>复制</Text>
|
||||||
|
)}
|
||||||
|
</Space>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
|
||||||
|
<View className="detail-card">
|
||||||
|
<Text className="detail-section-title">患者信息</Text>
|
||||||
|
<View className="detail-row">
|
||||||
|
<Text className="detail-cell-text" style={{fontSize: '25rpx'}}>{getPatientDesc()}</Text>
|
||||||
|
<Text className="text-green-600" style={{fontSize: '25rpx'}} onClick={() => Taro.showToast({title: '患者档案敬请期待', icon: 'none'})}>
|
||||||
|
患者档案
|
||||||
|
</Text>
|
||||||
|
</View>
|
||||||
|
{detail.diagnosis && (
|
||||||
|
<View className="detail-row">
|
||||||
|
<Text className="detail-cell-text" style={{fontSize: '25rpx'}}>诊断:{detail.diagnosis}</Text>
|
||||||
|
<Text className="text-green-600" style={{fontSize: '25rpx'}} onClick={() => Taro.showToast({title: '诊断详情敬请期待', icon: 'none'})}>
|
||||||
|
查看详情
|
||||||
|
</Text>
|
||||||
|
</View>
|
||||||
|
)}
|
||||||
|
{detail.treatmentPlan && (
|
||||||
|
<Text className="detail-cell-text mt-2" style={{fontSize: '25rpx'}}>治疗方案:{detail.treatmentPlan}</Text>
|
||||||
|
)}
|
||||||
|
{detail.decoctionInstructions && (
|
||||||
|
<Text className="detail-cell-text mt-2" style={{fontSize: '25rpx'}}>用药说明:{detail.decoctionInstructions}</Text>
|
||||||
|
)}
|
||||||
|
</View>
|
||||||
|
|
||||||
|
<View className="detail-card">
|
||||||
|
<View className="flex justify-between mb-2">
|
||||||
|
<Text className="detail-section-title">药方信息</Text>
|
||||||
|
<Text className="text-green-600" style={{fontSize: '25rpx'}} onClick={() => Taro.showToast({title: '已设为常用', icon: 'success'})}>存为常用方</Text>
|
||||||
|
</View>
|
||||||
|
<View className="detail-medicine-list">
|
||||||
|
{medicines.length === 0 && (
|
||||||
|
<Text className="text-gray-400" style={{fontSize: '25rpx'}}>暂无药材信息</Text>
|
||||||
|
)}
|
||||||
|
{medicines.map((item, index) => (
|
||||||
|
<View key={index} className="detail-medicine-chip">
|
||||||
|
{item.medicineName} {item.quantity || item.dosage || ''}
|
||||||
|
</View>
|
||||||
|
))}
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
|
||||||
|
<CellGroup>
|
||||||
|
<Cell title="剂数" extra={`${medicines.length} 味药`} titleStyle={{fontSize: '25rpx'}} descriptionStyle={{fontSize: '25rpx'}}/>
|
||||||
|
<Cell title="服用方式" extra={detail.decoctionInstructions || '遵医嘱'} titleStyle={{fontSize: '25rpx'}} descriptionStyle={{fontSize: '25rpx'}}/>
|
||||||
|
<Cell title="订单备注" extra={detail.comments || '无'} titleStyle={{fontSize: '25rpx'}} descriptionStyle={{fontSize: '25rpx'}}/>
|
||||||
|
<Cell
|
||||||
|
title="合计"
|
||||||
|
extra={(
|
||||||
|
<Space>
|
||||||
|
<Text className="text-red-500 font-semibold">¥{detail.orderPrice || '0.00'}</Text>
|
||||||
|
<Text className="text-green-600" style={{fontSize: '25rpx'}} onClick={() => Taro.showToast({title: '暂无明细', icon: 'none'})}>价格明细</Text>
|
||||||
|
</Space>
|
||||||
|
)}
|
||||||
|
/>
|
||||||
|
</CellGroup>
|
||||||
|
</View>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
export default PrescriptionDetail;
|
||||||
@@ -9,12 +9,12 @@ import {
|
|||||||
getStatusText,
|
getStatusText,
|
||||||
getStatusTagType,
|
getStatusTagType,
|
||||||
getStatusOptions,
|
getStatusOptions,
|
||||||
mapApplyStatusToCustomerStatus,
|
|
||||||
mapCustomerStatusToApplyStatus
|
mapCustomerStatusToApplyStatus
|
||||||
} from '@/utils/customerStatus';
|
} from '@/utils/customerStatus';
|
||||||
import FixedButton from "@/components/FixedButton";
|
import FixedButton from "@/components/FixedButton";
|
||||||
import navTo from "@/utils/common";
|
import navTo from "@/utils/common";
|
||||||
import {pageShopDealerApply, removeShopDealerApply, updateShopDealerApply} from "@/api/shop/shopDealerApply";
|
import {pageShopDealerApply, removeShopDealerApply, updateShopDealerApply} from "@/api/shop/shopDealerApply";
|
||||||
|
import {userPageClinicPatientUser} from "@/api/clinic/clinicPatientUser";
|
||||||
|
|
||||||
// 扩展User类型,添加客户状态和保护天数
|
// 扩展User类型,添加客户状态和保护天数
|
||||||
interface CustomerUser extends UserType {
|
interface CustomerUser extends UserType {
|
||||||
@@ -102,69 +102,6 @@ const CustomerIndex = () => {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
// 计算剩余保护天数(基于过期时间)
|
|
||||||
const calculateProtectDays = (expirationTime?: string, applyTime?: string): number => {
|
|
||||||
try {
|
|
||||||
// 优先使用过期时间字段
|
|
||||||
if (expirationTime) {
|
|
||||||
const expDate = new Date(expirationTime.replace(' ', 'T'));
|
|
||||||
const now = new Date();
|
|
||||||
|
|
||||||
// 计算剩余毫秒数
|
|
||||||
const remainingMs = expDate.getTime() - now.getTime();
|
|
||||||
|
|
||||||
// 转换为天数,向上取整
|
|
||||||
const remainingDays = Math.ceil(remainingMs / (1000 * 60 * 60 * 24));
|
|
||||||
|
|
||||||
console.log('=== 基于过期时间计算 ===');
|
|
||||||
console.log('过期时间:', expirationTime);
|
|
||||||
console.log('当前时间:', now.toLocaleString());
|
|
||||||
console.log('剩余天数:', remainingDays);
|
|
||||||
console.log('======================');
|
|
||||||
|
|
||||||
return Math.max(0, remainingDays);
|
|
||||||
}
|
|
||||||
|
|
||||||
// 如果没有过期时间,回退到基于申请时间计算
|
|
||||||
if (!applyTime) return 0;
|
|
||||||
|
|
||||||
const protectionPeriod = 7; // 保护期7天
|
|
||||||
|
|
||||||
// 解析申请时间
|
|
||||||
let applyDate: Date;
|
|
||||||
if (applyTime.includes('T')) {
|
|
||||||
applyDate = new Date(applyTime);
|
|
||||||
} else {
|
|
||||||
applyDate = new Date(applyTime.replace(' ', 'T'));
|
|
||||||
}
|
|
||||||
|
|
||||||
// 获取当前时间
|
|
||||||
const now = new Date();
|
|
||||||
|
|
||||||
// 只比较日期部分,忽略时间
|
|
||||||
const applyDateOnly = new Date(applyDate.getFullYear(), applyDate.getMonth(), applyDate.getDate());
|
|
||||||
const currentDateOnly = new Date(now.getFullYear(), now.getMonth(), now.getDate());
|
|
||||||
|
|
||||||
// 计算已经过去的天数
|
|
||||||
const timeDiff = currentDateOnly.getTime() - applyDateOnly.getTime();
|
|
||||||
const daysPassed = Math.floor(timeDiff / (1000 * 60 * 60 * 24));
|
|
||||||
|
|
||||||
// 计算剩余保护天数
|
|
||||||
const remainingDays = protectionPeriod - daysPassed;
|
|
||||||
|
|
||||||
console.log('=== 基于申请时间计算 ===');
|
|
||||||
console.log('申请时间:', applyTime);
|
|
||||||
console.log('已过去天数:', daysPassed);
|
|
||||||
console.log('剩余保护天数:', remainingDays);
|
|
||||||
console.log('======================');
|
|
||||||
|
|
||||||
return Math.max(0, remainingDays);
|
|
||||||
} catch (error) {
|
|
||||||
console.error('日期计算错误:', error);
|
|
||||||
return 0;
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
// 获取客户数据
|
// 获取客户数据
|
||||||
const fetchCustomerData = useCallback(async (statusFilter?: CustomerStatus, resetPage = false, targetPage?: number) => {
|
const fetchCustomerData = useCallback(async (statusFilter?: CustomerStatus, resetPage = false, targetPage?: number) => {
|
||||||
@@ -174,22 +111,22 @@ const CustomerIndex = () => {
|
|||||||
|
|
||||||
// 构建API参数,根据状态筛选
|
// 构建API参数,根据状态筛选
|
||||||
const params: any = {
|
const params: any = {
|
||||||
type: 4,
|
// type: 4,
|
||||||
page: currentPage
|
page: currentPage,
|
||||||
};
|
};
|
||||||
const applyStatus = mapCustomerStatusToApplyStatus(statusFilter || activeTab);
|
const applyStatus = mapCustomerStatusToApplyStatus(statusFilter || activeTab);
|
||||||
if (applyStatus !== undefined) {
|
if (applyStatus !== undefined) {
|
||||||
params.applyStatus = applyStatus;
|
params.applyStatus = applyStatus;
|
||||||
}
|
}
|
||||||
|
|
||||||
const res = await pageShopDealerApply(params);
|
const res = await userPageClinicPatientUser(params);
|
||||||
|
|
||||||
if (res?.list && res.list.length > 0) {
|
if (res?.list && res.list.length > 0) {
|
||||||
// 正确映射状态并计算保护天数
|
// 正确映射状态并计算保护天数
|
||||||
const mappedList = res.list.map(customer => ({
|
const mappedList = res.list.map(customer => ({
|
||||||
...customer,
|
...customer,
|
||||||
customerStatus: mapApplyStatusToCustomerStatus(customer.applyStatus || 10),
|
// customerStatus: mapApplyStatusToCustomerStatus(customer.applyStatus || 10),
|
||||||
protectDays: calculateProtectDays(customer.expirationTime, customer.applyTime || customer.createTime || '')
|
// protectDays: calculateProtectDays(customer.expirationTime, customer.applyTime || customer.createTime || '')
|
||||||
}));
|
}));
|
||||||
|
|
||||||
// 如果是重置页面或第一页,直接设置新数据;否则追加数据
|
// 如果是重置页面或第一页,直接设置新数据;否则追加数据
|
||||||
|
|||||||
@@ -1,24 +1,32 @@
|
|||||||
import {useState} from "react";
|
import {useState} from "react";
|
||||||
import Taro, {useDidShow} from '@tarojs/taro'
|
import Taro, {useDidShow} from '@tarojs/taro'
|
||||||
import {Button, Cell, CellGroup, Space, Empty, ConfigProvider, Tag} from '@nutui/nutui-react-taro'
|
import {Button, Cell, CellGroup, Space, Empty, ConfigProvider, Tag, Popup} from '@nutui/nutui-react-taro'
|
||||||
import {View, Text} from '@tarojs/components'
|
import {View, Text} from '@tarojs/components'
|
||||||
import {ClinicPrescription} from "@/api/clinic/clinicPrescription/model";
|
import {ClinicPrescription} from "@/api/clinic/clinicPrescription/model";
|
||||||
import {
|
import {
|
||||||
pageClinicPrescription, updateClinicPrescription,
|
pageClinicPrescription,
|
||||||
WxPayResult
|
WxPayResult
|
||||||
} from "@/api/clinic/clinicPrescription";
|
} from "@/api/clinic/clinicPrescription";
|
||||||
import {copyText} from "@/utils/common";
|
import {copyText} from "@/utils/common";
|
||||||
import {createOrder} from "@/api/shop/shopOrder";
|
import {updateShopOrder} from "@/api/shop/shopOrder";
|
||||||
|
import {listShopUserAddress} from "@/api/shop/shopUserAddress";
|
||||||
|
import {ShopUserAddress} from "@/api/shop/shopUserAddress/model";
|
||||||
|
|
||||||
const ClinicPrescriptionList = () => {
|
const ClinicPrescriptionList = () => {
|
||||||
const [list, setList] = useState<ClinicPrescription[]>([])
|
const [list, setList] = useState<ClinicPrescription[]>([])
|
||||||
const [loading, setLoading] = useState<boolean>(false)
|
const [loading, setLoading] = useState<boolean>(false)
|
||||||
|
const [addressList, setAddressList] = useState<ShopUserAddress[]>([])
|
||||||
|
const [addressPopupVisible, setAddressPopupVisible] = useState<boolean>(false)
|
||||||
|
const [addressLoading, setAddressLoading] = useState<boolean>(false)
|
||||||
|
const [addressSaving, setAddressSaving] = useState<boolean>(false)
|
||||||
|
const [selectedAddressId, setSelectedAddressId] = useState<number | null>(null)
|
||||||
|
const [pendingOrder, setPendingOrder] = useState<ClinicPrescription | null>(null)
|
||||||
|
|
||||||
const reload = () => {
|
const reload = () => {
|
||||||
setLoading(true)
|
setLoading(true)
|
||||||
pageClinicPrescription({
|
pageClinicPrescription({
|
||||||
// 添加查询条件
|
|
||||||
userId: Taro.getStorageSync('UserId'),
|
userId: Taro.getStorageSync('UserId'),
|
||||||
|
withDoctor: true,
|
||||||
})
|
})
|
||||||
.then(data => {
|
.then(data => {
|
||||||
setList(data?.list || [])
|
setList(data?.list || [])
|
||||||
@@ -35,6 +43,112 @@ const ClinicPrescriptionList = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
const fetchAddressList = async () => {
|
||||||
|
try {
|
||||||
|
setAddressLoading(true)
|
||||||
|
const data = await listShopUserAddress({
|
||||||
|
userId: Taro.getStorageSync('UserId')
|
||||||
|
})
|
||||||
|
const addressData = data || []
|
||||||
|
setAddressList(addressData)
|
||||||
|
if (addressData.length > 0) {
|
||||||
|
setSelectedAddressId(addressData[0].id || null)
|
||||||
|
} else {
|
||||||
|
setSelectedAddressId(null)
|
||||||
|
}
|
||||||
|
return addressData
|
||||||
|
} catch (error) {
|
||||||
|
console.error('加载收货地址失败:', error)
|
||||||
|
Taro.showToast({
|
||||||
|
title: '加载收货地址失败',
|
||||||
|
icon: 'error'
|
||||||
|
})
|
||||||
|
setAddressList([])
|
||||||
|
setSelectedAddressId(null)
|
||||||
|
return []
|
||||||
|
} finally {
|
||||||
|
setAddressLoading(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const closeAddressPopup = () => {
|
||||||
|
setAddressPopupVisible(false)
|
||||||
|
setPendingOrder(null)
|
||||||
|
}
|
||||||
|
|
||||||
|
const formatFullAddress = (address?: ShopUserAddress) => {
|
||||||
|
if (!address) return ''
|
||||||
|
return address.fullAddress || `${address.province || ''}${address.city || ''}${address.region || ''}${address.address || ''}`
|
||||||
|
}
|
||||||
|
|
||||||
|
const ensureAddressBeforePay = async (item: ClinicPrescription) => {
|
||||||
|
setPendingOrder(item)
|
||||||
|
const addresses = await fetchAddressList()
|
||||||
|
if (addresses.length === 0) {
|
||||||
|
Taro.showModal({
|
||||||
|
title: '暂无收货地址',
|
||||||
|
content: '请先添加收货地址后再支付',
|
||||||
|
confirmText: '去添加',
|
||||||
|
success: (res) => {
|
||||||
|
if (res.confirm) {
|
||||||
|
Taro.navigateTo({url: '/user/address/index'})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
setAddressPopupVisible(true)
|
||||||
|
}
|
||||||
|
|
||||||
|
const handleAddressConfirm = async () => {
|
||||||
|
if (!selectedAddressId) {
|
||||||
|
Taro.showToast({
|
||||||
|
title: '请选择收货地址',
|
||||||
|
icon: 'error'
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if (!pendingOrder || !(pendingOrder as any).shopOrder || !(pendingOrder as any).shopOrder.orderId) {
|
||||||
|
Taro.showToast({
|
||||||
|
title: '订单信息缺失',
|
||||||
|
icon: 'error'
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
const targetAddress = addressList.find(addr => addr.id === selectedAddressId)
|
||||||
|
if (!targetAddress) {
|
||||||
|
Taro.showToast({
|
||||||
|
title: '地址不存在',
|
||||||
|
icon: 'error'
|
||||||
|
})
|
||||||
|
return
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
setAddressSaving(true)
|
||||||
|
await updateShopOrder({
|
||||||
|
orderId: (pendingOrder as any).shopOrder.orderId,
|
||||||
|
addressId: targetAddress.id,
|
||||||
|
realName: targetAddress.name,
|
||||||
|
address: formatFullAddress(targetAddress)
|
||||||
|
} as any)
|
||||||
|
Taro.showToast({
|
||||||
|
title: '地址已更新',
|
||||||
|
icon: 'success'
|
||||||
|
})
|
||||||
|
const orderToPay = pendingOrder
|
||||||
|
closeAddressPopup()
|
||||||
|
await onPay(orderToPay, true)
|
||||||
|
} catch (error: any) {
|
||||||
|
console.error('更新收货地址失败:', error)
|
||||||
|
Taro.showToast({
|
||||||
|
title: error?.message || '更新地址失败',
|
||||||
|
icon: 'error'
|
||||||
|
})
|
||||||
|
} finally {
|
||||||
|
setAddressSaving(false)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 处理微信支付
|
* 处理微信支付
|
||||||
*/
|
*/
|
||||||
@@ -45,11 +159,6 @@ const ClinicPrescriptionList = () => {
|
|||||||
throw new Error('微信支付参数错误');
|
throw new Error('微信支付参数错误');
|
||||||
}
|
}
|
||||||
|
|
||||||
// 验证微信支付必要参数
|
|
||||||
if (!result.timeStamp || !result.nonceStr || !result.package || !result.paySign) {
|
|
||||||
throw new Error('微信支付参数不完整');
|
|
||||||
}
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await Taro.requestPayment({
|
await Taro.requestPayment({
|
||||||
timeStamp: result.timeStamp,
|
timeStamp: result.timeStamp,
|
||||||
@@ -79,7 +188,14 @@ const ClinicPrescriptionList = () => {
|
|||||||
/**
|
/**
|
||||||
* 统一支付入口
|
* 统一支付入口
|
||||||
*/
|
*/
|
||||||
const onPay = async (item: ClinicPrescription) => {
|
const onPay = async (item: ClinicPrescription | null, skipAddressCheck: boolean = false) => {
|
||||||
|
if (!item) {
|
||||||
|
Taro.showToast({
|
||||||
|
title: '处方信息缺失',
|
||||||
|
icon: 'error'
|
||||||
|
});
|
||||||
|
return;
|
||||||
|
}
|
||||||
if (!item.id) {
|
if (!item.id) {
|
||||||
Taro.showToast({
|
Taro.showToast({
|
||||||
title: '处方信息缺失',
|
title: '处方信息缺失',
|
||||||
@@ -88,48 +204,39 @@ const ClinicPrescriptionList = () => {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
Taro.showLoading({title: '支付中...'});
|
const shopOrder = (item as any).shopOrder
|
||||||
|
if (!skipAddressCheck) {
|
||||||
|
if (shopOrder && (!shopOrder.addressId || shopOrder.addressId === 0)) {
|
||||||
|
await ensureAddressBeforePay(item)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
await Taro.showLoading({title: '支付中...'});
|
||||||
|
|
||||||
try {
|
try {
|
||||||
// 调用统一支付接口
|
// 调用统一支付接口
|
||||||
const result = await createOrder(
|
// @ts-ignore
|
||||||
|
const {data} = await updateShopOrder(
|
||||||
{
|
{
|
||||||
type: 1,
|
orderId: shopOrder.orderId,
|
||||||
addressId: 10951,
|
makePay: true
|
||||||
comments: '开方',
|
|
||||||
deliveryType: 0,
|
|
||||||
payType: 1,
|
|
||||||
goodsItems: [
|
|
||||||
{
|
|
||||||
goodsId: 10056,
|
|
||||||
quantity: 1
|
|
||||||
}
|
|
||||||
]
|
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
console.log(result, 'resultresultresultresultresultresult')
|
const result = data as WxPayResult;
|
||||||
|
|
||||||
console.log('订单创建结果:', result);
|
console.log('订单创建结果:', result);
|
||||||
|
|
||||||
if (!result) {
|
|
||||||
throw new Error('创建订单失败');
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!result.orderNo) {
|
|
||||||
throw new Error('订单号获取失败');
|
|
||||||
}
|
|
||||||
|
|
||||||
// 调用微信支付
|
// 调用微信支付
|
||||||
await handleWechatPay(result);
|
await handleWechatPay(result);
|
||||||
|
|
||||||
// 支付成功
|
// 支付成功
|
||||||
console.log('支付成功,订单号:', result.orderNo);
|
// console.log('支付成功,订单号:', result.orderNo);
|
||||||
|
|
||||||
await updateClinicPrescription({
|
// await updateClinicPrescription({
|
||||||
id: item.id,
|
// id: item.id,
|
||||||
orderNo: result.orderNo,
|
// orderNo: result.orderNo,
|
||||||
status: 2
|
// status: 2
|
||||||
})
|
// })
|
||||||
|
|
||||||
Taro.showToast({
|
Taro.showToast({
|
||||||
title: '支付成功',
|
title: '支付成功',
|
||||||
@@ -185,7 +292,7 @@ const ClinicPrescriptionList = () => {
|
|||||||
<Cell
|
<Cell
|
||||||
title={`${item.id}`}
|
title={`${item.id}`}
|
||||||
extra={
|
extra={
|
||||||
<Tag type={'warning'} className="font-medium">{item.status == 2 ? '已支付' : '待支付'}</Tag>
|
<Tag type={'warning'} className="font-medium">{item?.shopOrder.payStatus == 1 ? '已支付' : '待支付'}</Tag>
|
||||||
}
|
}
|
||||||
onClick={() => copyText(`${item.orderNo}`)}
|
onClick={() => copyText(`${item.orderNo}`)}
|
||||||
/>
|
/>
|
||||||
@@ -226,21 +333,81 @@ const ClinicPrescriptionList = () => {
|
|||||||
</Text>
|
</Text>
|
||||||
}
|
}
|
||||||
/>
|
/>
|
||||||
{item.status == 0 && (
|
|
||||||
<Cell>
|
<Cell>
|
||||||
<Space className="w-full justify-end">
|
<Space className="w-full justify-end">
|
||||||
|
<Button
|
||||||
|
size="small"
|
||||||
|
type="warning"
|
||||||
|
onClick={() => Taro.navigateTo({url: `/clinic/clinicPatientUser/detail?id=${item.id}`})}
|
||||||
|
>
|
||||||
|
查看详情
|
||||||
|
</Button>
|
||||||
|
{item?.shopOrder?.payStatus == 0 && (
|
||||||
<Button
|
<Button
|
||||||
type={'danger'}
|
type={'danger'}
|
||||||
|
size="small"
|
||||||
onClick={() => onPay(item)}
|
onClick={() => onPay(item)}
|
||||||
>
|
>
|
||||||
去支付
|
去支付
|
||||||
</Button>
|
</Button>
|
||||||
|
)}
|
||||||
</Space>
|
</Space>
|
||||||
</Cell>
|
</Cell>
|
||||||
)}
|
|
||||||
</CellGroup>
|
</CellGroup>
|
||||||
))}
|
))}
|
||||||
</View>
|
</View>
|
||||||
|
|
||||||
|
<Popup
|
||||||
|
visible={addressPopupVisible}
|
||||||
|
position="bottom"
|
||||||
|
round
|
||||||
|
onClose={closeAddressPopup}
|
||||||
|
>
|
||||||
|
<View className="p-4 bg-white">
|
||||||
|
<View className="text-lg font-medium mb-3">请选择收货地址</View>
|
||||||
|
{addressLoading ? (
|
||||||
|
<View className="text-center text-gray-500 py-6">加载地址中...</View>
|
||||||
|
) : addressList.length === 0 ? (
|
||||||
|
<View className="text-center text-gray-500 py-6">暂无收货地址,请先添加</View>
|
||||||
|
) : (
|
||||||
|
<View style={{maxHeight: '300px', overflow: 'auto'}}>
|
||||||
|
{addressList.map(address => (
|
||||||
|
<View
|
||||||
|
key={address.id}
|
||||||
|
className={`border rounded-lg p-3 mb-2 ${selectedAddressId === address.id ? 'border-orange-400 bg-orange-50' : 'border-gray-200'}`}
|
||||||
|
onClick={() => setSelectedAddressId(address.id || null)}
|
||||||
|
>
|
||||||
|
<View className="flex justify-between mb-1">
|
||||||
|
<Text className="font-medium">{address.name}</Text>
|
||||||
|
<Text className="text-gray-600">{address.phone}</Text>
|
||||||
|
</View>
|
||||||
|
<Text className="text-gray-500 text-sm">{formatFullAddress(address)}</Text>
|
||||||
|
{address.isDefault && (
|
||||||
|
<Text className="text-orange-500 text-xs mt-1">默认地址</Text>
|
||||||
|
)}
|
||||||
|
</View>
|
||||||
|
))}
|
||||||
|
</View>
|
||||||
|
)}
|
||||||
|
|
||||||
|
<View className="flex gap-3 mt-4">
|
||||||
|
<Button
|
||||||
|
className="flex-1"
|
||||||
|
onClick={() => Taro.navigateTo({url: '/user/address/index'})}
|
||||||
|
>
|
||||||
|
管理地址
|
||||||
|
</Button>
|
||||||
|
<Button
|
||||||
|
type="primary"
|
||||||
|
className="flex-1"
|
||||||
|
loading={addressSaving}
|
||||||
|
onClick={handleAddressConfirm}
|
||||||
|
>
|
||||||
|
确定
|
||||||
|
</Button>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
</Popup>
|
||||||
</>
|
</>
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -5,7 +5,7 @@ import {Loading, InfiniteLoading, Empty, Space, SearchBar, Button} from '@nutui/
|
|||||||
import {Phone} from '@nutui/icons-react-taro'
|
import {Phone} from '@nutui/icons-react-taro'
|
||||||
import type {ClinicPatientUser as PatientUserType} from "@/api/clinic/clinicPatientUser/model";
|
import type {ClinicPatientUser as PatientUserType} from "@/api/clinic/clinicPatientUser/model";
|
||||||
import {
|
import {
|
||||||
pageClinicPatientUser
|
userPageClinicPatientUser
|
||||||
} from "@/api/clinic/clinicPatientUser";
|
} from "@/api/clinic/clinicPatientUser";
|
||||||
|
|
||||||
// 患者类型
|
// 患者类型
|
||||||
@@ -63,7 +63,7 @@ const SelectPatient = () => {
|
|||||||
params.keywords = displaySearchValue.trim();
|
params.keywords = displaySearchValue.trim();
|
||||||
}
|
}
|
||||||
|
|
||||||
const res = await pageClinicPatientUser(params);
|
const res = await userPageClinicPatientUser(params);
|
||||||
|
|
||||||
if (res?.list && res.list.length > 0) {
|
if (res?.list && res.list.length > 0) {
|
||||||
// 如果是重置页面或第一页,直接设置新数据;否则追加数据
|
// 如果是重置页面或第一页,直接设置新数据;否则追加数据
|
||||||
|
|||||||
@@ -76,6 +76,7 @@ const AddClinicOrder = () => {
|
|||||||
// 设置选中的处方(供其他页面调用)
|
// 设置选中的处方(供其他页面调用)
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
const setSelectedPrescriptionFunc = (prescription: ClinicPrescription) => {
|
const setSelectedPrescriptionFunc = (prescription: ClinicPrescription) => {
|
||||||
|
console.log('设置选中的处方:', prescription)
|
||||||
setSelectedPrescription(prescription)
|
setSelectedPrescription(prescription)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import PaymentCountdown from "@/components/PaymentCountdown";
|
|||||||
import {PaymentType} from "@/utils/payment";
|
import {PaymentType} from "@/utils/payment";
|
||||||
import {goTo} from "@/utils/navigation";
|
import {goTo} from "@/utils/navigation";
|
||||||
import {pageClinicPrescription} from "@/api/clinic/clinicPrescription";
|
import {pageClinicPrescription} from "@/api/clinic/clinicPrescription";
|
||||||
|
import Prescription from "@/clinic/clinicPatientUser/prescription";
|
||||||
|
|
||||||
// 判断订单是否支付已过期
|
// 判断订单是否支付已过期
|
||||||
const isPaymentExpired = (createTime: string, timeoutHours: number = 24): boolean => {
|
const isPaymentExpired = (createTime: string, timeoutHours: number = 24): boolean => {
|
||||||
@@ -389,9 +390,10 @@ function OrderList(props: OrderListProps) {
|
|||||||
};
|
};
|
||||||
|
|
||||||
// 立即支付
|
// 立即支付
|
||||||
const payOrder = async (order: ShopOrder) => {
|
// @ts-ignore
|
||||||
|
const payOrder = async (order: Prescription) => {
|
||||||
try {
|
try {
|
||||||
if (!order.orderId || !order.orderNo) {
|
if (!order.id || !order.orderNo) {
|
||||||
Taro.showToast({
|
Taro.showToast({
|
||||||
title: '订单信息错误',
|
title: '订单信息错误',
|
||||||
icon: 'error'
|
icon: 'error'
|
||||||
@@ -428,19 +430,20 @@ function OrderList(props: OrderListProps) {
|
|||||||
Taro.showLoading({title: '发起支付...'});
|
Taro.showLoading({title: '发起支付...'});
|
||||||
|
|
||||||
// 构建商品数据
|
// 构建商品数据
|
||||||
const goodsItems = order.orderGoods?.map(goods => ({
|
const goodsItems = order.items?.map(goods => ({
|
||||||
goodsId: goods.goodsId,
|
goodsId: goods.medicineId,
|
||||||
quantity: goods.totalNum || 1
|
quantity: goods.totalNum || 1
|
||||||
})) || [];
|
})) || [];
|
||||||
|
|
||||||
// 对于已存在的订单,我们需要重新发起支付
|
// 对于已存在的订单,我们需要重新发起支付
|
||||||
// 构建支付请求数据,包含完整的商品信息
|
// 构建支付请求数据,包含完整的商品信息
|
||||||
const paymentData = {
|
const paymentData = {
|
||||||
orderId: order.orderId,
|
orderId: order.id,
|
||||||
orderNo: order.orderNo,
|
orderNo: order.orderNo,
|
||||||
goodsItems: goodsItems,
|
goodsItems: goodsItems,
|
||||||
addressId: order.addressId,
|
// addressId: order.addressId,
|
||||||
payType: PaymentType.WECHAT
|
payType: PaymentType.WECHAT,
|
||||||
|
type: 3
|
||||||
};
|
};
|
||||||
|
|
||||||
console.log('重新支付数据:', paymentData);
|
console.log('重新支付数据:', paymentData);
|
||||||
@@ -701,10 +704,12 @@ function OrderList(props: OrderListProps) {
|
|||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
void cancelOrder(item);
|
void cancelOrder(item);
|
||||||
}}>取消订单</Button>
|
}}>取消订单</Button>
|
||||||
|
{item.showPayButton && (
|
||||||
<Button size={'small'} type="primary" onClick={(e) => {
|
<Button size={'small'} type="primary" onClick={(e) => {
|
||||||
e.stopPropagation();
|
e.stopPropagation();
|
||||||
void payOrder(item);
|
void payOrder(item);
|
||||||
}}>立即支付</Button>
|
}}>立即支付</Button>
|
||||||
|
)}
|
||||||
</Space>
|
</Space>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
|
|||||||
@@ -281,9 +281,11 @@ const DoctorOrderConfirm = () => {
|
|||||||
<Space>
|
<Space>
|
||||||
<Text className="medicine-name">{item.medicineName}</Text>
|
<Text className="medicine-name">{item.medicineName}</Text>
|
||||||
<Text className="medicine-price">¥{item.unitPrice}</Text>
|
<Text className="medicine-price">¥{item.unitPrice}</Text>
|
||||||
|
{!!item.specification && (
|
||||||
<Text className="medicine-spec">
|
<Text className="medicine-spec">
|
||||||
{item.specification || '规格未知'} × {item.quantity || 1}
|
{item.specification || '规格未知'} × {item.quantity || 1}
|
||||||
</Text>
|
</Text>
|
||||||
|
)}
|
||||||
</Space>
|
</Space>
|
||||||
<View className="medicine-sub">
|
<View className="medicine-sub">
|
||||||
<Text className="medicine-subtotal">
|
<Text className="medicine-subtotal">
|
||||||
|
|||||||
@@ -11,7 +11,7 @@ function ClinicPrescriptionList() {
|
|||||||
const [searchParams, setSearchParams] = useState<ShopOrderParam>({
|
const [searchParams, setSearchParams] = useState<ShopOrderParam>({
|
||||||
statusFilter: params.statusFilter != undefined && params.statusFilter != '' ? parseInt(params.statusFilter) : -1
|
statusFilter: params.statusFilter != undefined && params.statusFilter != '' ? parseInt(params.statusFilter) : -1
|
||||||
})
|
})
|
||||||
const [showSearch, setShowSearch] = useState(false)
|
const [showSearch] = useState(false)
|
||||||
const [searchKeyword, setSearchKeyword] = useState('')
|
const [searchKeyword, setSearchKeyword] = useState('')
|
||||||
const searchTimeoutRef = useRef<NodeJS.Timeout>()
|
const searchTimeoutRef = useRef<NodeJS.Timeout>()
|
||||||
|
|
||||||
|
|||||||
@@ -128,11 +128,14 @@ const SelectPrescription = () => {
|
|||||||
<View className="flex items-center mb-1">
|
<View className="flex items-center mb-1">
|
||||||
<Space direction="vertical">
|
<Space direction="vertical">
|
||||||
<Text className="text-xs text-gray-500">
|
<Text className="text-xs text-gray-500">
|
||||||
处方类型: {prescription.prescriptionType === 0 ? '中药' : prescription.prescriptionType === 1 ? '西药' : '未知'}
|
处方名称: {prescription.treatmentPlan}
|
||||||
</Text>
|
</Text>
|
||||||
<Text className="text-xs text-gray-500">
|
<Text className="text-xs text-gray-500">
|
||||||
诊断结果: {prescription.diagnosis || '无'}
|
处方类型: {prescription.prescriptionType === 0 ? '中药' : prescription.prescriptionType === 1 ? '西药' : '未知'}
|
||||||
</Text>
|
</Text>
|
||||||
|
{/*<Text className="text-xs text-gray-500">*/}
|
||||||
|
{/* 诊断结果: {prescription.diagnosis || '无'}*/}
|
||||||
|
{/*</Text>*/}
|
||||||
<Text className="text-xs text-gray-500">
|
<Text className="text-xs text-gray-500">
|
||||||
创建时间: {prescription.createTime || '未知'}
|
创建时间: {prescription.createTime || '未知'}
|
||||||
</Text>
|
</Text>
|
||||||
|
|||||||
@@ -56,7 +56,7 @@ const AddPatient = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 验证手机号格式
|
// 验证手机号格式
|
||||||
const phoneRegex = /^1[3-9]\d{9}$/;
|
const phoneRegex = /^1[2-9]\d{9}$/;
|
||||||
if (!phoneRegex.test(values.phone)) {
|
if (!phoneRegex.test(values.phone)) {
|
||||||
Taro.showToast({
|
Taro.showToast({
|
||||||
title: '请填写正确的手机号',
|
title: '请填写正确的手机号',
|
||||||
|
|||||||
134
src/doctor/orders/add.scss
Normal file
134
src/doctor/orders/add.scss
Normal file
@@ -0,0 +1,134 @@
|
|||||||
|
.usage-card {
|
||||||
|
margin: 12px 16px;
|
||||||
|
margin-top: 4px;
|
||||||
|
padding: 16px;
|
||||||
|
border-radius: 16px;
|
||||||
|
background: #ffffff;
|
||||||
|
box-shadow: 0 12px 32px rgba(54, 87, 142, 0.08);
|
||||||
|
}
|
||||||
|
|
||||||
|
.usage-card__header {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
gap: 8px;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.usage-card__icon {
|
||||||
|
width: 32px;
|
||||||
|
height: 32px;
|
||||||
|
border-radius: 10px;
|
||||||
|
background: linear-gradient(135deg, #fceecd, #ffd886);
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.usage-card__icon-text {
|
||||||
|
font-size: 16px;
|
||||||
|
color: #8c5a00;
|
||||||
|
font-weight: 600;
|
||||||
|
}
|
||||||
|
|
||||||
|
.usage-card__title {
|
||||||
|
font-size: 16px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #1c1c1e;
|
||||||
|
}
|
||||||
|
|
||||||
|
.usage-card__dose-row {
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
padding-bottom: 12px;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
border-bottom: 1px solid #f0f0f0;
|
||||||
|
}
|
||||||
|
|
||||||
|
.usage-card__label {
|
||||||
|
font-size: 14px;
|
||||||
|
color: #7c7c7c;
|
||||||
|
}
|
||||||
|
|
||||||
|
.usage-card__dose-value {
|
||||||
|
display: flex;
|
||||||
|
align-items: baseline;
|
||||||
|
gap: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.usage-card__dose-value .nut-input {
|
||||||
|
width: 72px;
|
||||||
|
background: transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.usage-card__dose-value .nut-input input {
|
||||||
|
text-align: center;
|
||||||
|
font-size: 18px;
|
||||||
|
color: #1c1c1e;
|
||||||
|
}
|
||||||
|
|
||||||
|
.usage-card__grid-item--picker {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.usage-card__dose-number {
|
||||||
|
font-size: 20px;
|
||||||
|
font-weight: 600;
|
||||||
|
color: #1c1c1e;
|
||||||
|
}
|
||||||
|
|
||||||
|
.usage-card__dose-unit {
|
||||||
|
font-size: 12px;
|
||||||
|
color: #a1a1a1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.usage-card__grid {
|
||||||
|
display: flex;
|
||||||
|
gap: 10px;
|
||||||
|
margin-bottom: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.usage-card__grid-item {
|
||||||
|
flex: 1;
|
||||||
|
background: #f8f9fb;
|
||||||
|
border-radius: 12px;
|
||||||
|
padding: 10px 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.usage-card__grid-label {
|
||||||
|
font-size: 12px;
|
||||||
|
color: #9aa1b2;
|
||||||
|
}
|
||||||
|
|
||||||
|
.usage-card__grid-value {
|
||||||
|
margin-top: 6px;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
|
}
|
||||||
|
|
||||||
|
.usage-card__grid-text {
|
||||||
|
font-size: 14px;
|
||||||
|
color: #1d1d1f;
|
||||||
|
font-weight: 500;
|
||||||
|
}
|
||||||
|
|
||||||
|
.usage-card__desc {
|
||||||
|
font-size: 12px;
|
||||||
|
color: #8c8c8c;
|
||||||
|
margin-bottom: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.usage-card__hint {
|
||||||
|
font-size: 12px;
|
||||||
|
color: #4b4b4d;
|
||||||
|
background: #f5f5f5;
|
||||||
|
border-radius: 12px;
|
||||||
|
padding: 12px;
|
||||||
|
margin-bottom: 8px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.usage-card__hint--muted {
|
||||||
|
color: #9ba0ab;
|
||||||
|
background: #f0f2f5;
|
||||||
|
}
|
||||||
@@ -8,9 +8,10 @@ import {
|
|||||||
Avatar,
|
Avatar,
|
||||||
Input,
|
Input,
|
||||||
Space,
|
Space,
|
||||||
TextArea
|
TextArea,
|
||||||
|
Picker as NutPicker
|
||||||
} from '@nutui/nutui-react-taro'
|
} from '@nutui/nutui-react-taro'
|
||||||
import {ArrowRight} from '@nutui/icons-react-taro'
|
import {ArrowRight, ArrowDown} from '@nutui/icons-react-taro'
|
||||||
import {View, Text} from '@tarojs/components'
|
import {View, Text} from '@tarojs/components'
|
||||||
import Taro from '@tarojs/taro'
|
import Taro from '@tarojs/taro'
|
||||||
import FixedButton from "@/components/FixedButton";
|
import FixedButton from "@/components/FixedButton";
|
||||||
@@ -18,7 +19,8 @@ import navTo from "@/utils/common";
|
|||||||
import {ClinicPatientUser} from "@/api/clinic/clinicPatientUser/model";
|
import {ClinicPatientUser} from "@/api/clinic/clinicPatientUser/model";
|
||||||
import {ClinicPrescription} from "@/api/clinic/clinicPrescription/model";
|
import {ClinicPrescription} from "@/api/clinic/clinicPrescription/model";
|
||||||
import {TenantId} from "@/config/app";
|
import {TenantId} from "@/config/app";
|
||||||
import {getClinicPatientUser} from "@/api/clinic/clinicPatientUser";
|
import {clinicPatientUserByPatientUserId} from "@/api/clinic/clinicPatientUser";
|
||||||
|
import './add.scss'
|
||||||
|
|
||||||
// 图片数据接口
|
// 图片数据接口
|
||||||
interface UploadedImageData {
|
interface UploadedImageData {
|
||||||
@@ -30,6 +32,215 @@ interface UploadedImageData {
|
|||||||
type?: string;
|
type?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const frequencyOptions = ['一次', '两次', '三次', '四次', '五次']
|
||||||
|
const perDoseOptions = Array.from({length: 20}, (_, index) => `${(index + 1) * 5}g`)
|
||||||
|
|
||||||
|
const getFrequencyIndexFromText = (text?: string) => {
|
||||||
|
if (!text) {
|
||||||
|
return 2
|
||||||
|
}
|
||||||
|
const numberMatch = text.match(/\d+/)
|
||||||
|
if (numberMatch) {
|
||||||
|
const num = Math.min(Math.max(parseInt(numberMatch[0], 10), 1), 5)
|
||||||
|
return num - 1
|
||||||
|
}
|
||||||
|
const chineseDigits = ['一', '二', '三', '四', '五']
|
||||||
|
const foundIndex = chineseDigits.findIndex(char => text.includes(char))
|
||||||
|
if (foundIndex !== -1) {
|
||||||
|
return foundIndex
|
||||||
|
}
|
||||||
|
return 2
|
||||||
|
}
|
||||||
|
|
||||||
|
const getPerDoseIndexFromText = (text?: string) => {
|
||||||
|
if (!text) {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
const numberMatch = text.match(/\d+/)
|
||||||
|
if (!numberMatch) {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
const num = Math.min(Math.max(parseInt(numberMatch[0], 10), 5), 100)
|
||||||
|
const normalized = Math.round(num / 5)
|
||||||
|
const index = Math.max(0, Math.min(perDoseOptions.length - 1, normalized - 1))
|
||||||
|
return index
|
||||||
|
}
|
||||||
|
|
||||||
|
interface UsageSummaryCardProps {
|
||||||
|
prescription: ClinicPrescription;
|
||||||
|
doseCount: string;
|
||||||
|
onDoseChange?: (value: string) => void;
|
||||||
|
frequencyIndex: number;
|
||||||
|
onFrequencyChange?: (value: number) => void;
|
||||||
|
perDoseIndex: number;
|
||||||
|
onPerDoseChange?: (value: number) => void;
|
||||||
|
}
|
||||||
|
|
||||||
|
const UsageSummaryCard = ({
|
||||||
|
prescription,
|
||||||
|
doseCount,
|
||||||
|
onDoseChange,
|
||||||
|
frequencyIndex,
|
||||||
|
onFrequencyChange,
|
||||||
|
perDoseIndex,
|
||||||
|
onPerDoseChange
|
||||||
|
}: UsageSummaryCardProps) => {
|
||||||
|
const firstItem = prescription.items?.[0]
|
||||||
|
const [showFrequencyPicker, setShowFrequencyPicker] = useState(false)
|
||||||
|
const [showPerDosePicker, setShowPerDosePicker] = useState(false)
|
||||||
|
|
||||||
|
const frequencyPickerOptions = frequencyOptions.map((label, index) => ({
|
||||||
|
text: label,
|
||||||
|
value: String(index)
|
||||||
|
}))
|
||||||
|
|
||||||
|
const perDosePickerOptions = perDoseOptions.map((label, index) => ({
|
||||||
|
text: label,
|
||||||
|
value: String(index)
|
||||||
|
}))
|
||||||
|
|
||||||
|
// const formatFrequency = (frequency?: string) => {
|
||||||
|
// if (!frequency) {
|
||||||
|
// return {label: '每日', value: '3次'}
|
||||||
|
// }
|
||||||
|
// if (frequency.includes('每日')) {
|
||||||
|
// return {label: '每日', value: frequency.replace('每日', '') || '1次'}
|
||||||
|
// }
|
||||||
|
// if (frequency.includes('每天')) {
|
||||||
|
// return {label: '每天', value: frequency.replace('每天', '') || '1次'}
|
||||||
|
// }
|
||||||
|
// return {label: '频率', value: frequency}
|
||||||
|
// }
|
||||||
|
|
||||||
|
const formatDosage = () => {
|
||||||
|
const dosageText = firstItem?.dosage || firstItem?.specification
|
||||||
|
if (!dosageText) {
|
||||||
|
return '5g'
|
||||||
|
}
|
||||||
|
return dosageText
|
||||||
|
}
|
||||||
|
|
||||||
|
const formatDuration = () => {
|
||||||
|
const totalDoses = Number(doseCount) || 0
|
||||||
|
const perDay = frequencyIndex + 1
|
||||||
|
if (totalDoses <= 0) {
|
||||||
|
return '0天'
|
||||||
|
}
|
||||||
|
const days = Math.max(1, Math.ceil(totalDoses / perDay))
|
||||||
|
return `${days}天`
|
||||||
|
}
|
||||||
|
|
||||||
|
// const summaryDesc = firstItem?.comments || prescription.comments || '请根据患者情况调整剂量,严格按照医嘱服用。'
|
||||||
|
// const decoctionHint = prescription.decoctionInstructions || '设置用药时间、用药禁忌、医嘱等'
|
||||||
|
|
||||||
|
const handleDoseInputChange = (value: string) => {
|
||||||
|
const sanitized = value.replace(/[^0-9]/g, '')
|
||||||
|
onDoseChange?.(sanitized)
|
||||||
|
}
|
||||||
|
|
||||||
|
return (
|
||||||
|
<View className="usage-card">
|
||||||
|
<View className="usage-card__header">
|
||||||
|
<View className="usage-card__icon">
|
||||||
|
<Text className="usage-card__icon-text">用</Text>
|
||||||
|
</View>
|
||||||
|
<Text className="usage-card__title">用法</Text>
|
||||||
|
</View>
|
||||||
|
|
||||||
|
<View className="usage-card__dose-row">
|
||||||
|
<Text className="usage-card__label">剂数</Text>
|
||||||
|
<View className="usage-card__dose-value">
|
||||||
|
<Input
|
||||||
|
className="usage-card__dose-input"
|
||||||
|
value={doseCount}
|
||||||
|
type="number"
|
||||||
|
placeholder="填写"
|
||||||
|
style={{
|
||||||
|
minWidth: '60px',
|
||||||
|
backgroundColor: '#f8f9fb',
|
||||||
|
borderRadius: '8px',
|
||||||
|
padding: '2px 6px'
|
||||||
|
}}
|
||||||
|
onChange={(value) => handleDoseInputChange(value)}
|
||||||
|
/>
|
||||||
|
<Text className="usage-card__dose-unit">剂</Text>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
|
||||||
|
<View className="usage-card__grid">
|
||||||
|
<View
|
||||||
|
className="usage-card__grid-item usage-card__grid-item--picker"
|
||||||
|
onClick={() => setShowFrequencyPicker(true)}
|
||||||
|
>
|
||||||
|
<Text className="usage-card__grid-label">每日</Text>
|
||||||
|
<View className="usage-card__grid-value">
|
||||||
|
<Text className="usage-card__grid-text">{frequencyOptions[frequencyIndex] || '一次'}</Text>
|
||||||
|
<ArrowDown size={10} color="#C1C1C1"/>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
<View
|
||||||
|
className="usage-card__grid-item usage-card__grid-item--picker"
|
||||||
|
onClick={() => setShowPerDosePicker(true)}
|
||||||
|
>
|
||||||
|
<Text className="usage-card__grid-label">每次</Text>
|
||||||
|
<View className="usage-card__grid-value">
|
||||||
|
<Text className="usage-card__grid-text">{perDoseOptions[perDoseIndex] || formatDosage()}</Text>
|
||||||
|
<ArrowDown size={10} color="#C1C1C1"/>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
<View className="usage-card__grid-item">
|
||||||
|
<Text className="usage-card__grid-label">可服</Text>
|
||||||
|
<View className="usage-card__grid-value">
|
||||||
|
<Text className="usage-card__grid-text">{formatDuration()}</Text>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
</View>
|
||||||
|
|
||||||
|
{/*<View className="usage-card__desc">*/}
|
||||||
|
{/* <Text>{summaryDesc}</Text>*/}
|
||||||
|
{/*</View>*/}
|
||||||
|
|
||||||
|
{/*<View className="usage-card__hint">*/}
|
||||||
|
{/* <Text>{decoctionHint}</Text>*/}
|
||||||
|
{/*</View>*/}
|
||||||
|
|
||||||
|
{/*<View className="usage-card__hint usage-card__hint--muted">*/}
|
||||||
|
{/* <Text>请输入制作要求,该内容患者不可见</Text>*/}
|
||||||
|
{/*</View>*/}
|
||||||
|
|
||||||
|
<NutPicker
|
||||||
|
visible={showFrequencyPicker}
|
||||||
|
title="请选择每日次数"
|
||||||
|
options={frequencyPickerOptions}
|
||||||
|
value={[String(frequencyIndex)]}
|
||||||
|
defaultValue={[String(frequencyIndex)]}
|
||||||
|
onConfirm={(_, value) => {
|
||||||
|
const selected = Array.isArray(value) && value.length > 0 ? Number(value[0]) : 0
|
||||||
|
onFrequencyChange?.(selected)
|
||||||
|
setShowFrequencyPicker(false)
|
||||||
|
}}
|
||||||
|
onClose={() => setShowFrequencyPicker(false)}
|
||||||
|
onCancel={() => setShowFrequencyPicker(false)}
|
||||||
|
/>
|
||||||
|
|
||||||
|
<NutPicker
|
||||||
|
visible={showPerDosePicker}
|
||||||
|
title="请选择每次用量"
|
||||||
|
options={perDosePickerOptions}
|
||||||
|
value={[String(perDoseIndex)]}
|
||||||
|
defaultValue={[String(perDoseIndex)]}
|
||||||
|
onConfirm={(_, value) => {
|
||||||
|
const selected = Array.isArray(value) && value.length > 0 ? Number(value[0]) : 0
|
||||||
|
onPerDoseChange?.(selected)
|
||||||
|
setShowPerDosePicker(false)
|
||||||
|
}}
|
||||||
|
onClose={() => setShowPerDosePicker(false)}
|
||||||
|
onCancel={() => setShowPerDosePicker(false)}
|
||||||
|
/>
|
||||||
|
</View>
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
const AddClinicOrder = () => {
|
const AddClinicOrder = () => {
|
||||||
const {params} = useRouter();
|
const {params} = useRouter();
|
||||||
const [toUser, setToUser] = useState<ClinicPatientUser>()
|
const [toUser, setToUser] = useState<ClinicPatientUser>()
|
||||||
@@ -53,13 +264,17 @@ const AddClinicOrder = () => {
|
|||||||
image: '' // 添加image字段
|
image: '' // 添加image字段
|
||||||
})
|
})
|
||||||
|
|
||||||
|
const [doseCount, setDoseCount] = useState<string>('1')
|
||||||
|
const [frequencyIndex, setFrequencyIndex] = useState<number>(2)
|
||||||
|
const [perDoseIndex, setPerDoseIndex] = useState<number>(0)
|
||||||
|
|
||||||
// 判断是编辑还是新增模式
|
// 判断是编辑还是新增模式
|
||||||
const isEditMode = !!params.id
|
const isEditMode = !!params.id
|
||||||
const toUserId = params.id ? Number(params.id) : undefined
|
const toUserId = params.id ? Number(params.id) : undefined
|
||||||
|
|
||||||
const reload = async () => {
|
const reload = async () => {
|
||||||
if (toUserId) {
|
if (toUserId) {
|
||||||
getClinicPatientUser(Number(toUserId)).then(data => {
|
clinicPatientUserByPatientUserId(Number(toUserId)).then(data => {
|
||||||
setToUser(data)
|
setToUser(data)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@@ -320,6 +535,24 @@ const AddClinicOrder = () => {
|
|||||||
}
|
}
|
||||||
}, [isEditMode]);
|
}, [isEditMode]);
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (selectedPrescription) {
|
||||||
|
const firstItem = selectedPrescription.items?.[0]
|
||||||
|
const inferredDose = firstItem?.quantity || firstItem?.amount || firstItem?.days || selectedPrescription.items?.length
|
||||||
|
if (inferredDose) {
|
||||||
|
setDoseCount(String(inferredDose))
|
||||||
|
} else {
|
||||||
|
setDoseCount('1')
|
||||||
|
}
|
||||||
|
setFrequencyIndex(getFrequencyIndexFromText(firstItem?.usageFrequency))
|
||||||
|
setPerDoseIndex(getPerDoseIndexFromText(firstItem?.dosage || firstItem?.specification))
|
||||||
|
} else {
|
||||||
|
setDoseCount('1')
|
||||||
|
setFrequencyIndex(2)
|
||||||
|
setPerDoseIndex(0)
|
||||||
|
}
|
||||||
|
}, [selectedPrescription])
|
||||||
|
|
||||||
if (loading) {
|
if (loading) {
|
||||||
return <Loading className={'px-2'}>加载中</Loading>
|
return <Loading className={'px-2'}>加载中</Loading>
|
||||||
}
|
}
|
||||||
@@ -479,7 +712,7 @@ const AddClinicOrder = () => {
|
|||||||
onClick={() => navTo(`/doctor/orders/selectPrescription`, true)}
|
onClick={() => navTo(`/doctor/orders/selectPrescription`, true)}
|
||||||
/>
|
/>
|
||||||
{/* 药方信息 */}
|
{/* 药方信息 */}
|
||||||
{selectedPrescription && (
|
{selectedPrescription ? (
|
||||||
<>
|
<>
|
||||||
<Cell extra={'药方信息'}>
|
<Cell extra={'药方信息'}>
|
||||||
<View className={'flex flex-col'}>
|
<View className={'flex flex-col'}>
|
||||||
@@ -492,6 +725,16 @@ const AddClinicOrder = () => {
|
|||||||
</View>
|
</View>
|
||||||
</Cell>
|
</Cell>
|
||||||
|
|
||||||
|
<UsageSummaryCard
|
||||||
|
prescription={selectedPrescription}
|
||||||
|
doseCount={doseCount}
|
||||||
|
onDoseChange={setDoseCount}
|
||||||
|
frequencyIndex={frequencyIndex}
|
||||||
|
onFrequencyChange={setFrequencyIndex}
|
||||||
|
perDoseIndex={perDoseIndex}
|
||||||
|
onPerDoseChange={setPerDoseIndex}
|
||||||
|
/>
|
||||||
|
|
||||||
{/* 煎药说明 */}
|
{/* 煎药说明 */}
|
||||||
<TextArea
|
<TextArea
|
||||||
value={formData.decoctionInstructions}
|
value={formData.decoctionInstructions}
|
||||||
@@ -501,7 +744,7 @@ const AddClinicOrder = () => {
|
|||||||
maxLength={200}
|
maxLength={200}
|
||||||
/>
|
/>
|
||||||
</>
|
</>
|
||||||
)}
|
) : null}
|
||||||
|
|
||||||
{/* 底部浮动按钮 */}
|
{/* 底部浮动按钮 */}
|
||||||
<FixedButton text={'下一步:确认订单信息'} onClick={() => formRef.current?.submit()}/>
|
<FixedButton text={'下一步:确认订单信息'} onClick={() => formRef.current?.submit()}/>
|
||||||
|
|||||||
@@ -15,8 +15,10 @@ import FixedButton from "@/components/FixedButton";
|
|||||||
import {ClinicPatientUser} from "@/api/clinic/clinicPatientUser/model";
|
import {ClinicPatientUser} from "@/api/clinic/clinicPatientUser/model";
|
||||||
import {ClinicPrescription} from "@/api/clinic/clinicPrescription/model";
|
import {ClinicPrescription} from "@/api/clinic/clinicPrescription/model";
|
||||||
import {addClinicPrescription} from "@/api/clinic/clinicPrescription";
|
import {addClinicPrescription} from "@/api/clinic/clinicPrescription";
|
||||||
import {addClinicPrescriptionItem} from "@/api/clinic/clinicPrescriptionItem";
|
import {batchAddClinicPrescriptionItem} from "@/api/clinic/clinicPrescriptionItem";
|
||||||
import './confirm.scss'
|
import './confirm.scss'
|
||||||
|
import request from "@/utils/request";
|
||||||
|
import {ApiResult} from "@/api";
|
||||||
|
|
||||||
// 订单数据接口
|
// 订单数据接口
|
||||||
interface OrderData {
|
interface OrderData {
|
||||||
@@ -90,7 +92,7 @@ const DoctorOrderConfirm = () => {
|
|||||||
// 第一步:创建处方主表记录
|
// 第一步:创建处方主表记录
|
||||||
console.log('开始创建处方记录...')
|
console.log('开始创建处方记录...')
|
||||||
const prescriptionData: ClinicPrescription = {
|
const prescriptionData: ClinicPrescription = {
|
||||||
userId: orderData.patient.userId,
|
userId: orderData.patient.patientUserId,
|
||||||
doctorId: doctorId,
|
doctorId: doctorId,
|
||||||
prescriptionType: orderData.prescription?.prescriptionType || 0, // 处方类型
|
prescriptionType: orderData.prescription?.prescriptionType || 0, // 处方类型
|
||||||
diagnosis: orderData.diagnosis, // 诊断结果
|
diagnosis: orderData.diagnosis, // 诊断结果
|
||||||
@@ -118,6 +120,7 @@ const DoctorOrderConfirm = () => {
|
|||||||
// 第二步:创建处方明细记录(药品列表)
|
// 第二步:创建处方明细记录(药品列表)
|
||||||
if (orderData.prescription?.items && orderData.prescription.items.length > 0) {
|
if (orderData.prescription?.items && orderData.prescription.items.length > 0) {
|
||||||
console.log('开始创建处方明细...')
|
console.log('开始创建处方明细...')
|
||||||
|
const list = []
|
||||||
for (const item of orderData.prescription.items) {
|
for (const item of orderData.prescription.items) {
|
||||||
const prescriptionItemData = {
|
const prescriptionItemData = {
|
||||||
prescriptionId: prescriptionId, // 关联处方ID
|
prescriptionId: prescriptionId, // 关联处方ID
|
||||||
@@ -134,11 +137,35 @@ const DoctorOrderConfirm = () => {
|
|||||||
userId: orderData.patient.userId,
|
userId: orderData.patient.userId,
|
||||||
comments: item.comments
|
comments: item.comments
|
||||||
}
|
}
|
||||||
await addClinicPrescriptionItem(prescriptionItemData)
|
list.push(prescriptionItemData)
|
||||||
}
|
}
|
||||||
|
await batchAddClinicPrescriptionItem(list)
|
||||||
console.log('处方明细创建成功')
|
console.log('处方明细创建成功')
|
||||||
}
|
|
||||||
|
|
||||||
|
const order: any = {
|
||||||
|
userId: orderData.patient.userId,
|
||||||
|
orderNo: createdPrescription.orderNo,
|
||||||
|
type: 3,
|
||||||
|
title: "药方",
|
||||||
|
totalPrice: getTotalPrice(),
|
||||||
|
goodsItems: []
|
||||||
|
}
|
||||||
|
// @ts-ignore
|
||||||
|
const orderGoodsList = []
|
||||||
|
for (const item of orderData.prescription.items) {
|
||||||
|
const orderGoods = {
|
||||||
|
goodsId: item.medicineId,
|
||||||
|
quantity: item.quantity,
|
||||||
|
}
|
||||||
|
orderGoodsList.push(orderGoods)
|
||||||
|
}
|
||||||
|
order.goodsItems = orderGoodsList
|
||||||
|
|
||||||
|
const res = await request.post<ApiResult<unknown>>('/shop/shop-order', order)
|
||||||
|
if (res.code !== 0) {
|
||||||
|
throw new Error(res.message || '创建商城订单失败')
|
||||||
|
}
|
||||||
|
}
|
||||||
console.log('处方创建完成,处方ID:', prescriptionId)
|
console.log('处方创建完成,处方ID:', prescriptionId)
|
||||||
|
|
||||||
// 清除临时数据
|
// 清除临时数据
|
||||||
@@ -146,14 +173,14 @@ const DoctorOrderConfirm = () => {
|
|||||||
|
|
||||||
Taro.showToast({
|
Taro.showToast({
|
||||||
title: '处方已发送给患者',
|
title: '处方已发送给患者',
|
||||||
icon: 'success',
|
icon: 'none',
|
||||||
duration: 2000
|
duration: 2000
|
||||||
})
|
})
|
||||||
|
|
||||||
setTimeout(() => {
|
setTimeout(() => {
|
||||||
// 跳转到订单列表
|
// 跳转到订单列表
|
||||||
Taro.redirectTo({
|
Taro.redirectTo({
|
||||||
url: '/doctor/orders/index'
|
url: '/clinic/clinicPatientUser/prescription'
|
||||||
})
|
})
|
||||||
}, 2000)
|
}, 2000)
|
||||||
|
|
||||||
|
|||||||
@@ -172,7 +172,7 @@ const CustomerIndex = () => {
|
|||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
<Button type="warning" onClick={() => navTo(`/chat/doctor/index?id=${item.userId}`)}>咨询医生</Button>
|
<Button type="warning" onClick={() => navTo(`/chat/doctor/index?userId=${item.userId}`)}>咨询医生</Button>
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
@@ -216,7 +216,8 @@ const CustomerIndex = () => {
|
|||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
<Button type="warning" onClick={() => navTo(`/doctor/orders/add?id=${item.userId}`)}>开方</Button>
|
<Button type="warning" onClick={() => navTo(`/doctor/orders/add?id=${item.patientUserId}`)}>开方</Button>
|
||||||
|
<Button type="primary" onClick={() => navTo(`/chat/doctor/index?userId=${item.patientUserId}`)}>聊天</Button>
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
</View>
|
</View>
|
||||||
@@ -350,7 +351,7 @@ const CustomerIndex = () => {
|
|||||||
<View className="bg-white pt-2 border-b border-gray-100">
|
<View className="bg-white pt-2 border-b border-gray-100">
|
||||||
<SearchBar
|
<SearchBar
|
||||||
value={searchValue}
|
value={searchValue}
|
||||||
placeholder="搜索患者名称、手机号"
|
placeholder="搜索医师名称、手机号"
|
||||||
onChange={(value) => setSearchValue(value)}
|
onChange={(value) => setSearchValue(value)}
|
||||||
onClear={() => {
|
onClear={() => {
|
||||||
setSearchValue('');
|
setSearchValue('');
|
||||||
|
|||||||
@@ -53,6 +53,7 @@ function Index() {
|
|||||||
const submitSucceed = (values: any) => {
|
const submitSucceed = (values: any) => {
|
||||||
console.log('提交表单', values);
|
console.log('提交表单', values);
|
||||||
if (FormData.status != 2 && FormData.status != undefined) return false;
|
if (FormData.status != 2 && FormData.status != undefined) return false;
|
||||||
|
console.log(FormData.type)
|
||||||
if (FormData.type == 0) {
|
if (FormData.type == 0) {
|
||||||
if (!FormData.sfz1 || !FormData.sfz2) {
|
if (!FormData.sfz1 || !FormData.sfz2) {
|
||||||
Taro.showToast({
|
Taro.showToast({
|
||||||
@@ -249,6 +250,21 @@ function Index() {
|
|||||||
// 企业类型
|
// 企业类型
|
||||||
FormData.type == 1 && (
|
FormData.type == 1 && (
|
||||||
<>
|
<>
|
||||||
|
<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
|
<Form.Item
|
||||||
label={'主体名称'}
|
label={'主体名称'}
|
||||||
name="name"
|
name="name"
|
||||||
|
|||||||
Reference in New Issue
Block a user