失眠网,内容丰富有趣,生活中的好帮手!
失眠网 > 9.vue3医疗问诊项目 - _问诊室模块-聊天问诊功能实现 ==> 默认消息渲染 文

9.vue3医疗问诊项目 - _问诊室模块-聊天问诊功能实现 ==> 默认消息渲染 文

时间:2021-10-29 18:37:56

相关推荐

9.vue3医疗问诊项目 - _问诊室模块-聊天问诊功能实现 ==> 默认消息渲染 文

9.vue3医疗在线问诊项目 - _问诊室模块-聊天问诊功能实现 ==> 默认消息渲染、文字聊天功能、图片聊天功能、医生接单、查看处方、购买药品、评价医生

问诊室-默认消息

实现:获取接诊前后默认聊天消息并渲染

需求❓:ws连接建立后,通过监听chatMsgList事件,服务器会返回患者和提示消息

步骤:

监听默认聊天消息,并且处理成消息列表

提取常量数据进行渲染服务器返回的患者信息和提示消息预览病情图片

代码:

监听默认聊天记录,并且处理成消息列表

Room/index.vue

import {MsgType } from '@/enums'import type {Message, TimeMessages } from '@/types/room'const list = ref<Message[]>([])

// 聊天记录({ data }: { data: TimeMessages[] }) 给解构的数据指定类型socket.on('chatMsgList', ({data }: {data: TimeMessages[] }) => {// 准备转换常规消息列表const arr: Message[] = []data.forEach((item, i) => {// 1. 处理消息时间arr.push({msgType: MsgType.Notify,msg: {content: item.createTime },createTime: item.createTime,id: item.createTime})// 2. 其它消息arr.push(...item.items)})// 追加到聊天消息列表list.value.unshift(...arr)})

进行渲染

Room/index.vue

<room-message :list="list" />

Room/components/RoomMessage.vue

import {ConsultTime } from '@/enums'import type {Message } from '@/types/room'defineProps<{list: Message[] }>()

<template v-for="{ msgType, msg, id } in list" :key="id"><!-- 1. 病情描述 --><div class="msg msg-illness" v-if="msgType === MsgType.CardPat"><div class="patient van-hairline--bottom" v-if="msg.consultRecord"><p>{{ msg.consultRecord.patientInfo.name }}{{ msg.consultRecord.patientInfo.genderValue }}{{ msg.consultRecord.patientInfo.age }}岁</p><p>{{ msg.consultRecord.illnessTime }} |{{ msg.consultRecord.consultFlag }}</p></div><van-row><van-col span="6">病情描述</van-col><van-col span="18">{{ msg.consultRecord?.illnessDesc }}</van-col><van-col span="6">图片</van-col><van-col span="18"> 点击查看 </van-col></van-row></div><!--2. 温馨提示 --><div class="msg msg-tip" v-if="msgType === MsgType.NotifyTip"><div class="content"><span class="green">温馨提示:</span><span>{{ msg.content }}</span></div></div><!-- 3. 通用通知 --><div class="msg msg-tip" v-if="msgType === MsgType.Notify"><div class="content"><span>{{ msg.content }}</span></div></div></template>

格式化病情描述数据

抽取常量数据api/constants.ts

import {ConsultTime } from '@/enums'// 患病时间export const timeOptions = [{label: '一周内', value: ConsultTime.Week },{label: '一月内', value: ConsultTime.Month },{label: '半年内', value: ConsultTime.HalfYear },{label: '大于半年', value: ConsultTime.More }]// 是否就诊过export const flagOptions = [{label: '就诊过', value: 0 },{label: '没就诊过', value: 1 }]

病情描述消息卡片使用

import {flagOptions, timeOptions } from '@/api/constants'// 获取患病时间label信息const getIllnessTimeText = (time: ConsultTime) =>timeOptions.find((item) => item.value === time)?.label// 获取是否就诊label信息const getConsultFlagText = (flag: 0 | 1) =>flagOptions.find((item) => item.value === flag)?.label

<!-- 1. 病情描述 --><div class="msg msg-illness" v-if="msgType === MsgType.CardPat"><div class="patient van-hairline--bottom" v-if="msg.consultRecord"><p>{{ msg.consultRecord.patientInfo.name }}{{ msg.consultRecord.patientInfo.genderValue }}{{ msg.consultRecord.patientInfo.age }}岁</p><p>+{{ getIllnessTimeText(msg.consultRecord.illnessTime) }} |+{{ getConsultFlagText(msg.consultRecord.consultFlag) }}</p></div><van-row><van-col span="6">病情描述</van-col><van-col span="18">{{ msg.consultRecord?.illnessDesc }}</van-col><van-col span="6">图片</van-col><van-col span="18"> 点击查看 </van-col></van-row></div>

预览病情图片Room/components/RoomMessage.vue

<van-row><van-col span="6">病情描述</van-col><van-col span="18">{{ msg.consultRecord?.illnessDesc }}</van-col><van-col span="6">图片</van-col>+ <van-col span="18" @click="previewImg(msg.consultRecord?.pictures)"> 点击查看 </van-col></van-row>

import {ImagePreview } from 'vant'const previewImg = (pictures?: Image[]) => {if (pictures && pictures.length) ImagePreview(pictures.map((item) => item.url))}

注意❓:ImagePreview函数形式,需要在入口手动引入 ImagePreview 组件的样式

main.ts

import 'vant/es/image-preview/style';

问诊室-文字聊天

实现:可以发送文字消息,可以接收文字消息

需求分析❓:

底部RoomAction.vue操作组件,可以输入文字,触发send-text事件传出文字问诊室组件,监听send-text事件接收文字定义订单详情api接口和数据类型,需要使用医生ID,作为收信息人的标识获取订单详情,然后通过socket.emitsendChatMsg事件发送文字给服务器,发送消息需要字段:from发送人(患者)、to收消息人(医生)、msgType消息类型、msg消息内容通过socket.onreceiveChatMsg事件接收发送成功或者医生发来的消息,追加到聊天列表list中,并处理滚动在RoomMessage.vue组件展示渲染聊天消息在RoomAction.vue中处理医生未接诊,禁用消息输入框

代码:

1)底部操作组件,可以输入文字,触发send-text事件传出文字

Room/components/RoomAction.vue

import {ref } from 'vue'const emit = defineEmits<{(e: 'send-text', text: string): void}>()const text = ref('')const sendText = () => {emit('send-text', text.value)text.value = ''}

<van-field+v-model="text"type="text"class="input":border="false"placeholder="问医生"autocomplete="off"+@keyup.enter="sendText"></van-field>

2)问诊室组件,监听send-text事件接收文字

Room/index.vue

<room-action @send-text="sendText" />

const sendText = (text: string) => {// 发送消息}

3)获取订单详情1息人的标识

types/consult.d.ts

// 问诊订单详情类型export type ConsultOrderItem = Consult & {createTime: stringdocInfo?: DoctorpatientInfo: PatientorderNo: stringstatusValue: stringtypeValue: stringstatus: OrderTypecountdown: numberprescriptionId?: stringevaluateId: numberpayment: numbercouponDeduction: numberpointDeduction: numberactualPayment: number}

api/consult.ts

export const getConsultOrderDetail = (orderId: string) =>request.get<ConsultOrderItem>('/patient/consult/order/detail', {params:{orderId} })

4)通过socket.emitsendChatMsg发送文字给服务器

Room/index.vue

import type {ConsultOrderItem } from '@/types/consult'import {getConsultOrderDetail } from '@/services/consult'// 1. 获取订单详情const consult = ref<ConsultOrderItem>()onMounted(async () => {const res = await getConsultOrderDetail(route.query.orderId as string)consult.value = res.data}) // 2. 发送消息const sendText = (text: string) => {// 发送信息需要数据:发送人、收消息人、消息类型、消息内容socket.emit('sendChatMsg', {from: store.user?.id, // 发送人to: consult.value?.docInfo?.id, // 收消息人msgType: MsgType.MsgText, // 消息类型msg: {content: text } // 消息内容})}

5)通过socket.onreceiveChatMsg接收发送成功或者医生发来的消息

// 接收消息socket.on('receiveChatMsg', async (msg) => {list.value.push(msg)await nextTick()window.scrollTo(0, document.body.scrollHeight) // 收到新消息后滚动到底部})

6)展示消息Room/components/RoomMessage.vue

pnpm i dayjs

import dayjs from 'dayjs'const formatTime = (time: string) => dayjs(time).format('HH:mm')const store = useUserStore()

<!-- 发送文字:患者发的消息 --><div class="msg msg-to" v-if="msgType === MsgType.MsgText && store.user?.id === from"><div class="content"><div class="time">{{ formatTime(createTime) }}</div><div class="pao">{{ msg.content }}</div></div><van-image :src="store.user?.avatar" /></div><!-- 接收文字:医生发的消息 --><div class="msg msg-from" v-if="msgType === MsgType.MsgText && store.user?.id !== from"><van-image :src="fromAvatar" /><div class="content"><div class="time">{{ formatTime(createTime) }}</div><div class="pao">{{ msg.content }}</div></div></div>

说明❓:

患者:store.user?.id === from医生:store.user?.id !== from医生未接单,发送消息无响应

7)医生未接诊,不是咨询中状态禁用消息输入框

Room/index.vue

<room-action@send-text="sendText"+:disabled="consult?.status !== OrderType.ConsultChat"></room-action>

Room/components/RoomAction.vue

// 发消息输入框是否可用defineProps<{disabled: boolean}>()

<div class="room-action"><van-field+:disabled="disabled"v-model="text"type="text"class="input":border="false"placeholder="问医生"autocomplete="off"@keyup.enter="sendText"></van-field><van-uploader+ :disabled="disabled":preview-image="false"><cp-icon name="consult-img" /></van-uploader></div>

问诊室-医生接单

使用医生端app接单

步骤:

抢单前需要,监听超级医生订单状态变更

1)医生接单后,会触发ws的statusChange事件,更新订单状态

Room/index.vue

onMounted(async () => {// ...// 3. 监听超级医生订单状态变更,更新订单状态(必须)socket.on('statusChange', async () => {const res = await getConsultOrderDetail(route.query.orderId as string)consult.value = res.dataconsole.log('订单状态更新了:', consult.value)})})

2)处理问诊订单状态

Room/index.vue

<!-- 1. 问诊状态--><room-status+ :status="consult?.status"+ :countdown="consult?.countdown"></room-status>

Room/components/RoomStatus.vue

<script setup lang="ts">import { OrderType } from '@/enums'const { status, countdown = 0 } = defineProps<{status?: OrderTypecountdown?: number}>()</script><template><div class="room-status"><!-- 1. 未接诊 --><div class="wait" v-if="status === OrderType.ConsultWait">已通知医生尽快接诊,24小时内医生未回复将自动退款</div><!-- 2. 接诊:咨询中 --><div class="chat" v-if="status === OrderType.ConsultChat"><span>咨询中</span><span>剩余时间:<van-count-down :time="countdown * 1000"></van-count-down></span></div><!-- 3. 问诊结束 --><divclass="end"v-if="status === OrderType.ConsultComplete || status === OrderType.ConsultCancel"><van-icon name="passed" /> 已结束</div></div></template>

在抢单中心,输入订单号抢单

抢单成功后,进入医生问诊室,可以和患者端沟通

问诊室-图片聊天

实现:点击图片icon上传图片,成功后发送图片消息

需求分析❓:

底部操作组件,可以调用后台api函数上传图片,触发send-image事件传出图片对象

问诊室组件,监听send-image事件接收图片对象

通过socket.emitsendChatMsg发送图片给服务器

展示图片消息

代码:

1)底部操作组件,可以上传图片,触发send-image事件传出图片对象

Room/components/RoomAction.vue

import {uploadImage } from '@/api/consult'import type {Image } from '@/types/consult'import {Toast } from 'vant'import type {UploaderAfterRead } from 'vant/lib/uploader/types'

const emit = defineEmits<{(e: 'send-text', text: string): void+ (e: 'send-image', img: Image): void}>()

const sendImage: UploaderAfterRead = async (data) => {// 排除多图上传数组情况if (Array.isArray(data)) return// 排除不存在情况if (!data.file) returnconst t = Toast.loading('正在上传')const res = await uploadImage(data.file)// 关闭提示t.clear()emit('send-image', res.data)}

<van-uploader :preview-image="false" :disabled="disabled"+ :after-read="sendImage"><cp-icon name="consult-img" /></van-uploader>

2)问诊室组件,监听send-image事件接收图片对象,通过socket.emitsendChatMsg发送图片给服务器

Room/index.vue

<room-action @send-text="sendText"+ @send-image="sendImage" />

import type {Image } from '@/types/consult'const sendImage = (img: Image) => {socket.emit('sendChatMsg', {from: store.user?.id,to: consult.value?.docInfo?.id,msgType: MsgType.MsgImage,msg: {picture: img }})}

3)展示消息

Room/components/RoomMessage.vue

<!-- 6. 发送图片 --><div class="msg msg-to" v-if="msgType === MsgType.MsgImage && store.user?.id === from"><div class="content"><div class="time">{{ formatTime(createTime) }}</div><van-image @load="loadSuccess()" fit="contain" :src="msg.picture?.url" /></div><van-image :src="store.user?.avatar" /></div><!-- 7. 接收图片 --><div class="msg msg-from" v-if="msgType === MsgType.MsgImage && store.user?.id !== from"><van-image :src="fromAvatar" /><div class="content"><div class="time">{{ formatTime(createTime) }}</div><van-image @load="loadSuccess()" fit="contain" :src="msg.picture?.url" /></div></div>

说明❓:解决图片加载滚动没有到最底部问题

图片加载完成后,执行一次滚动

// 图片加载成功=> 执行滚动const loadSuccess = () => {window.scrollTo(0, document.body.scrollHeight)}

问诊室-查看处方

实现:在医生端app,点击开处方,患者端app展示处方消息并支持查看

医生端app: 患者端app:

步骤:

展示处方消息 定义查看处方API函数点击查看处方预览处方图片

代码:

1)展示处方消息

Room/components/RoomMessage.vue

<!-- 8. 处方消息 --><div class="msg msg-recipe" v-if="msgType === MsgType.CardPre"><div class="content"><div class="head van-hairline--bottom"><div class="head-tit"><h3>电子处方</h3><p>原始处方 <van-icon name="arrow"></van-icon></p></div><p>{{ msg.prescription?.name }}{{ msg.prescription?.genderValue }}{{ msg.prescription?.age }}岁{{ msg.prescription?.diagnosis }}</p><p>开方时间:{{ msg.prescription?.createTime }}</p></div><div class="body"><div class="body-item" v-for="med in msg.prescription?.medicines" :key="med.id"><div class="durg"><p>{{ med.name }} {{ med.specs }}</p><p>{{ med.usageDosag }}</p></div><div class="num">x{{ med.quantity }}</div></div></div><div class="foot"><span>购买药品</span></div></div></div>

2)定义查看处方API

api/consult.ts

// 查看处方export const getPrescriptionPic = (id: string) =>request.get<{url: string }>(`/patient/consult/prescription/${id}`)

3)点击查看处方预览处方图片

Room/components/RoomMessage.vue

<div class="head van-hairline--bottom"><div class="head-tit"><h3>电子处方</h3><p+ @click="showPrescription(msg.prescription?.id)">原始处方 <van-icon name="arrow"></van-icon></p></div><p>{{ msg.prescription?.name }}{{ msg.prescription?.genderValue }}{{ msg.prescription?.age }}岁{{ msg.prescription?.diagnosis }}</p><p>开方时间:{{ msg.prescription?.createTime }}</p></div>

import {getPrescriptionPic } from '@/api/consult'const showPrescription = async (id?: string) => {if (id) {const res = await getPrescriptionPic(id)ImagePreview([res.data.url])}}

说明❓:后台处方图片没实现

问诊室-购买药品

实现:点击处方的购买药品,进行处方订单的支付跳转

步骤:

处方状态不同此按钮操作不同: 如果处方失效:提示即可如果没付款:去药品预支付页面

代码:

Room/components/RoomMessage.vue

<div class="body"><div class="body-item" v-for="med in msg.prescription?.medicines" :key="med.id"><div class="durg"><p>{{ med.name }} {{ med.specs }}</p><p>{{ med.usageDosag }}</p></div><div class="num">x{{ med.quantity }}</div></div></div>+ <div class="foot"><span @click="buy(msg.prescription)">购买药品</span></div></div>

import {useRouter } from 'vue-router'import {PrescriptionStatus } from '@/enums'import {Toast } from 'vant'// 点击处方的跳转const router = useRouter()const buy = (pre?: Prescription) => {if (pre) {// 1. 如果处方失效:提示即可if (pre.status === PrescriptionStatus.Invalid) return Toast('处方已失效')// 2. 如果没付款且没订单ID:去药品预支付页面if (pre.status === PrescriptionStatus.NotPayment)return router.push(`/medicine/pay?id=${pre.id}`)}}

问诊室-评价医生

实现:医生端app,点击问诊结束,给医生做评价

步骤:

展示问诊室状态和评价组件评价表单数据绑定和校验提交评价修改消息

代码:

1)展示问诊室状态和评价组件

Room/components/RoomMessage.vue

<!-- 9. 订单取消/关闭诊室 --><div class="msg msg-tip msg-tip-cancel" v-if="msgType === MsgType.NotifyCancel"><div class="content"><span>{{ msg.content }}</span></div></div><!-- 10. 医生评价 --><div class="msg" v-if="msgType === MsgType.CardEva || msgType === MsgType.CardEvaForm"><evaluate-card :evaluateDoc="msg.evaluateDoc"></evaluate-card></div>

Room/components/evaluateCard.vue

import type {EvaluateDoc } from '@/types/room'defineProps<{// 接收评价数据:存在说明评价过,相反没有evaluateDoc?: EvaluateDoc}>()

<template>- <!-- 1. 已经评价过 -->+ <div class="evalutate-card" v-if="evaluateDoc"><p class="title">医生服务评价</p><p class="desc">我们会更加努力提升服务质量</p><van-rate+:modelValue="evaluateDoc.score"size="7vw"gutter="3vw"color="#FADB14"void-icon="star"void-color="rgba(0,0,0,0.04)"/></div>- <!-- 2. 还没有评价 --><div class="evalutate-card" v-else><p class="title">感谢您的评价</p><p class="desc">本次在线问诊服务您还满意吗?</p><van-ratesize="7vw"gutter="3vw"color="#FADB14"void-icon="star"void-color="rgba(0,0,0,0.04)"/><van-fieldtype="textarea"maxlength="150"show-word-limitrows="3"placeholder="请描述您对医生的评价或是在医生看诊过程中遇到的问题"></van-field><div class="footer"><van-checkbox>匿名评价</van-checkbox><van-button type="primary" size="small" round> 提交评价 </van-button></div></div></template>

2)评价表单数据绑定和校验

import {Toast } from 'vant'import {computed, inject, ref } from 'vue'const score = ref(0)const anonymousFlag = ref(false)const content = ref('')const disabled = computed(() => !score.value || !content.value)const onSubmit = async () => {}

- <!-- 2. 还没有评价 --><div class="evalutate-card" v-else><p class="title">感谢您的评价</p><p class="desc">本次在线问诊服务您还满意吗?</p><van-rate+ v-model="score"size="7vw"gutter="3vw"color="#FADB14"void-icon="star"void-color="rgba(0,0,0,0.04)"/><van-field+v-model="content"type="textarea"maxlength="150"show-word-limitrows="3"placeholder="请描述您对医生的评价或是在医生看诊过程中遇到的问题"></van-field><div class="footer">+<van-checkbox v-model="anonymousFlag">匿名评价</van-checkbox>+<van-button :disabled="disabled" @click="onSubmit" type="primary" size="small" round>提交评价</van-button></div></div>

4)提交评价

定义评价api函数

api/consult.ts

// 评价问诊export const evaluateConsultOrder = (data: {docId: string // 医生IDorderId: string // 订单IDscore: numbercontent: stringanonymousFlag: 0 | 1}) => request.post<{id: string }>('/patient/order/evaluate', data)

注入订单信息:提供医生ID和订单ID

Room/index.vue

import {provide } from 'vue'// ...provide('consult', consult)

评价组件接收注入的数据

Room/components/EvaluateCard.vue

import {inject, type Ref } from 'vue'import type {ConsultOrderItem } from '@/types/consult'// 说明❓:Ref<type> 指定类型为Ref,提交时才可以使用.value访问变量const consult = inject<Ref<ConsultOrderItem>>('consult')

提交评价

+ import { evaluateConsultOrder } from '@/api/consult'const onSubmit = async () => {if (!score.value) return Toast('请选择评分')if (!content.value) return Toast('请输入评价')+ if (!consult?.value) return Toast('未找到订单')+ if (consult.value.docInfo?.id) {+ await evaluateConsultOrder({+docId: consult.value?.docInfo?.id,+orderId: consult.value?.id,+score: score.value,+content: content.value,+anonymousFlag: anonymousFlag.value ? 1 : 0+ })+ }// 修改消息:评价请求成功,改成已评价}

5)成功后修改消息,切换显示已评价

Room/index.vue

// 评价成功,修改评价消息状态和数据,切换卡片展示const completeEva = (score: number) => {// 获取评价信息数据const item = list.value.find((item) => item.msgType === MsgType.CardEvaForm)if (item) {item.msg.evaluateDoc = {score }item.msgType = MsgType.CardEva}}provide('completeEva', completeEva)

Room/components/EvaluateCard.vue

+ const completeEva = inject<(score: number) => void>('completeEva')const onSubmit = async () => {if (!consult?.value) return Toast('未找到订单')if (consult.value.docInfo?.id) {await evaluateConsultOrder({docId: consult.value.docInfo?.id,orderId: consult.value?.id,score: score.value,content: content.value,anonymousFlag: anonymousFlag.value ? 1 : 0})}- // 修改消息:评价请求成功,改成已评价+ completeEva && completeEva(score.value)}

9.vue3医疗在线问诊项目 - _问诊室模块-聊天问诊功能实现 ==> 默认消息渲染 文字聊天功能 图片聊天功能 医生接单 查看处方 购买药品 评价医生

如果觉得《9.vue3医疗问诊项目 - _问诊室模块-聊天问诊功能实现 ==> 默认消息渲染 文》对你有帮助,请点赞、收藏,并留下你的观点哦!

本内容不代表本网观点和政治立场,如有侵犯你的权益请联系我们处理。
网友评论
网友评论仅供其表达个人看法,并不表明网站立场。