1494 lines
35 KiB
Vue
1494 lines
35 KiB
Vue
<template>
|
||
<view class="content">
|
||
<view class="home_top">
|
||
<view class="home_top_icon">
|
||
<image src='../../static/icon/icon_bars.png' class="home_top_left" @click="openDrawer"></image>
|
||
<image src='../../static/icon/icon_dialog.png' class="home_top_right" @click="showMode"></image>
|
||
</view>
|
||
<text>智慧医疗</text>
|
||
<view class="home_top_folder" v-if="showMessages" @click="clickFolder">
|
||
<image src='../../static/icon/icon_folder.png'></image>
|
||
</view>
|
||
</view>
|
||
<view class="home_wrap" v-if="!showMessages">
|
||
<view class="home_logo">
|
||
<image src='../../static/logo.png'></image>
|
||
<text class="logo_main">我是太湖云医的智慧医疗<br/>很高兴见到您!</text>
|
||
<view class="logo_con">我是您的诊断小助手,您可以把情况发给我,我会结合太湖学堂知识数据库为您进行分析解答。
|
||
<view class="vip_count" :class="vipStatus&&freeCount==0?'vip_count_block':''">
|
||
<text v-if="vipStatus">{{vipText}}{{vipCount}}次</text>
|
||
<view v-if="freeStatus">
|
||
<text v-if="freeCount>0">{{freeText}}{{freeCount}}次</text>
|
||
<text v-if="freeCount==0&&!vipStatus">{{freeText}}{{freeCount}}次</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
<view class="home_form">
|
||
<template>
|
||
<view class="form_item">
|
||
<text>患者姓名</text>
|
||
<input type="text" v-model="formData.name" placeholder="如果您是医生,建议填写" placeholder-class="custom-placeholder" />
|
||
</view>
|
||
<view class="form_item">
|
||
<text>西医诊断</text>
|
||
<input type="text" v-model="formData.diagnosis" placeholder="请输入西医诊断" placeholder-class="custom-placeholder" />
|
||
</view>
|
||
<view class="form_item">
|
||
<text>主要病史</text>
|
||
<textarea v-model="formData.illness" maxlength="-1"
|
||
auto-height
|
||
placeholder="请输入病情历史记录"
|
||
placeholder-class="custom-placeholder" />
|
||
</view>
|
||
<view class="form_item">
|
||
<text>主要症状</text>
|
||
<textarea auto-height v-model="formData.symptoms" maxlength="-1"
|
||
placeholder="请输入目前的主要症状" placeholder-class="custom-placeholder" />
|
||
</view>
|
||
<view class="form_item">
|
||
<text>检查结果</text>
|
||
<textarea auto-height v-model="formData.result" maxlength="-1"
|
||
placeholder="请输入检查结果" placeholder-class="custom-placeholder" />
|
||
</view>
|
||
<view class="form_item">
|
||
<text>基因检测阳性</text>
|
||
<textarea auto-height v-model="formData.genetic" maxlength="-1"
|
||
v-if="selectedItems.length==0"
|
||
@input="handleInput"
|
||
:placeholder="selectedItems.length==0?'选填 可多填':''"
|
||
placeholder-class="custom-placeholder" />
|
||
|
||
<!-- 已选标签展示区 -->
|
||
<view class="tags-block" v-if="selectedItems.length > 0">
|
||
<view class="tags-container">
|
||
<view
|
||
v-for="(item, index) in selectedItems"
|
||
:key="item.id"
|
||
class="tag">
|
||
{{ item }}
|
||
<span @click="removeTag(index)" class="tag-close">×</span>
|
||
</view>
|
||
</view>
|
||
<input @input="handleInput" v-model="formData.genetic" />
|
||
</view>
|
||
</view>
|
||
</template>
|
||
</view>
|
||
<!-- 搜索结果列表 -->
|
||
<scroll-view scroll-y class="result-list" v-if="searchResultStatus&&searchResults.length > 0">
|
||
<view
|
||
v-for="item in searchResults"
|
||
:key="item.id"
|
||
@click="selectItem(item)"
|
||
class="result-item">
|
||
{{ item.name }}
|
||
</view>
|
||
</scroll-view>
|
||
</view>
|
||
|
||
<view class="submit_form" v-if="!showMessages">
|
||
<view class="assistants_list">
|
||
<view v-for="(item,index) in chatAssistants" :key="index"
|
||
@click="clickAssistants(item,index)"
|
||
class="assistants_item"
|
||
:class="activeIndex==index?'active':''"
|
||
>
|
||
<image class="assistants_img_1" :src="item.icon_1" mode="widthFix" ></image>
|
||
<image class="assistants_img_2" :src="item.icon_2" mode="widthFix" ></image>
|
||
<text>{{item.name}}</text>
|
||
</view>
|
||
</view>
|
||
<button class="submit_btn" @click="submit">
|
||
<image src='../../static/icon/icon_submit.png'></image>
|
||
</button>
|
||
</view>
|
||
|
||
<view class="message_wrap" :style="{ top: 80 + 'px' }" v-if="showMessages">
|
||
<text class="message_title" v-if="tishi">好的,结合您的情况,下面是分析结果。</text>
|
||
<!-- 显示聊天记录 -->
|
||
<scroll-view
|
||
scroll-y
|
||
:style="{ height: '100%' }"
|
||
:scroll-into-view="scrollIntoView"
|
||
scroll-with-animation
|
||
>
|
||
<view class="message-container-block">
|
||
<view class="message-container" v-for="(item, index) in messages" :key="index"
|
||
:class="{'message-left': item.type === 'answer', 'message-right': item.type === 'question'}">
|
||
<text v-html="item.content"></text>
|
||
</view>
|
||
<view class="loading-spinner" v-if="loading"></view>
|
||
</view>
|
||
<view id="bottom-anchor"></view>
|
||
</scroll-view>
|
||
|
||
<view class="submit_form submit_form_2">
|
||
<view class="assistants_list">
|
||
<textarea auto-height v-model="question_send" placeholder="给智慧医疗发送消息" placeholder-class="custom-placeholder" />
|
||
</view>
|
||
<button class="submit_btn" @click="sendAgain">
|
||
<image src='../../static/icon/icon_submit.png' v-if="!pauseStatus"></image>
|
||
<image src='../../static/icon/icon_submit_pause.png' v-if="pauseStatus"></image>
|
||
</button>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- 左侧弹窗 -->
|
||
<uni-drawer ref="drawer" mode="left" :width="230">
|
||
<view class="drawer-content">
|
||
<scroll-view scroll-y="true" class="list_content" v-if="record_list.length>0">
|
||
<view v-for="(item, index) in record_list" :key="index" class="list_item"
|
||
:class="activeRecord == index?'active_item':''"
|
||
@click="clickRecord(item, index)">
|
||
<text class="text_item">{{ item.chatName }}</text>
|
||
</view>
|
||
</scroll-view>
|
||
<view class="null_text" v-else>{{ null_text }}</view>
|
||
</view>
|
||
</uni-drawer>
|
||
|
||
<uni-popup ref="popup" class="folder_popup">
|
||
<view class="popup-content">
|
||
<view class="popup-top" @click="createFolder"><uni-icons type="folder-add" size="18" color="#fff"></uni-icons>创建病历夹</view>
|
||
<view class="popup-list">
|
||
<scroll-view scroll-y class="popup-scroll">
|
||
<view class="item-list" v-for="item in folderList" @click="askJoin(item)">
|
||
{{ item.folderName }}
|
||
<text v-if="item.here==1">所在位置</text>
|
||
</view>
|
||
</scroll-view>
|
||
</view>
|
||
</view>
|
||
</uni-popup>
|
||
|
||
<uni-popup ref="add_folder" class="folder_popup">
|
||
<view class="popup-content">
|
||
<text class="add_folder_name">创建病历夹</text>
|
||
<view class="add_folder_input">
|
||
<input type="text" v-model="folderName" placeholder="请输入病历夹名称" placeholder-class="custom-placeholder" />
|
||
</view>
|
||
<label class="checkbox-item">
|
||
<checkbox
|
||
:checked="isChecked"
|
||
@click="toggleCheck"
|
||
color="#007AFF"
|
||
/>
|
||
<text>把当前会话放入病历夹</text>
|
||
</label>
|
||
<button class="add_folder_btn" @click="addRecordFolder">保存</button>
|
||
</view>
|
||
</uni-popup>
|
||
<z-navigation></z-navigation>
|
||
</view>
|
||
</template>
|
||
|
||
<script>
|
||
import $http from "@/config/requestConfig.js";
|
||
import { mapState, mapMutations } from "vuex";
|
||
import qs from 'qs'
|
||
export default {
|
||
data() {
|
||
return {
|
||
containerHeight: null,
|
||
formData: {
|
||
diagnosis: '',
|
||
illness: '',
|
||
symptoms: '',
|
||
result: '',
|
||
genetic: '',
|
||
name: '',
|
||
},
|
||
chatId: null, //选择助手的id
|
||
chatName: '',
|
||
chatAssistants: [],
|
||
activeIndex: null,
|
||
sessionId: null, //对话id
|
||
|
||
eventSource: null,
|
||
showMessages: false,
|
||
loading: false,
|
||
question: '', //传递的问题
|
||
messages: [],
|
||
previousAnswer: null, //存储上一条回答内容
|
||
question_send: '',
|
||
pauseStatus: false, //暂停操作
|
||
record_list: [], //对话列表
|
||
null_text: '',
|
||
activeRecord: null,
|
||
tishi: false, //提示语
|
||
folderList: [], //病历夹列表
|
||
folderName: '', //病历夹名称
|
||
chatAssistantName: '',
|
||
patientName: '',
|
||
pageData: {},
|
||
pageType: '', //从患者列表过来的type
|
||
isChecked: true,
|
||
|
||
selectedItems: [], //模糊已选项
|
||
selectedId: [],
|
||
searchResults: [], //模糊搜索结果
|
||
searchResultStatus: false,
|
||
vipCount: null,
|
||
vipText: '',
|
||
vipStatus: null,
|
||
freeCount: null,
|
||
freeText: '',
|
||
freeStatus: null,
|
||
flag: null,
|
||
scrollIntoView: ''
|
||
}
|
||
},
|
||
computed: {
|
||
...mapState(["userInfo"]),
|
||
},
|
||
onLoad() {
|
||
uni.hideTabBar();
|
||
uni.removeStorageSync('homeParams');
|
||
//获取设备信息
|
||
const systemInfo = uni.getSystemInfoSync();
|
||
this.containerHeight = systemInfo.windowHeight; //获取设备的窗口高度
|
||
this.getChatAssistants();
|
||
//重置
|
||
this.$nextTick(() => {
|
||
this.showMode(); //包含-剩余次数
|
||
});
|
||
},
|
||
onShow(){
|
||
console.log('进入到onShow方法')
|
||
this.activeRecord = null;
|
||
this.getRecordsData();
|
||
this.getUserAiVipCount(); //更新次数
|
||
|
||
//我的-会话记录跳转来的
|
||
this.pageData = uni.getStorageSync('homeParams').data;
|
||
this.pageType = uni.getStorageSync('homeParams').type;
|
||
let index = uni.getStorageSync('homeParams').index;
|
||
if(this.pageData&&!this.pageType){
|
||
this.clickRecord(this.pageData, index);
|
||
}
|
||
},
|
||
methods: {
|
||
//设置滚动到最底部
|
||
scrollToBottom() {
|
||
this.scrollIntoView = '';
|
||
setTimeout(() => {
|
||
this.scrollIntoView = 'bottom-anchor';
|
||
}, 50);
|
||
},
|
||
//基因
|
||
handleInput(val){
|
||
let name = val.detail.value;
|
||
this.getGenes(name);
|
||
},
|
||
//模糊搜索选中
|
||
selectItem(item){
|
||
if (!this.selectedItems.some(i => i.id === item.id)) {
|
||
this.selectedItems.push(item.name);
|
||
this.selectedId.push(item.id)
|
||
}
|
||
this.formData.genetic = '';
|
||
this.searchResultStatus = false;
|
||
console.log(this.selectedItems)
|
||
console.log(this.selectedId)
|
||
},
|
||
//删除标签
|
||
removeTag(index) {
|
||
this.selectedItems.splice(index, 1);
|
||
this.selectedId.splice(index, 1);
|
||
console.log(this.selectedItems)
|
||
console.log(this.selectedId)
|
||
},
|
||
//基因模糊查询列表
|
||
getGenes(name){
|
||
this.$http.request({
|
||
url: 'taihumed/precisionMedicine/getPrecisionMedicineGenes',
|
||
method: "POST",
|
||
data: {name: name},
|
||
header: {
|
||
"Content-Type": "application/json",
|
||
},
|
||
})
|
||
.then(res=> {
|
||
if(res.code==0){
|
||
if(res.genes&&res.genes.length>0){
|
||
this.searchResults = res.genes;
|
||
this.searchResultStatus = true;
|
||
}else{
|
||
this.searchResults = '';
|
||
this.searchResultStatus = false;
|
||
}
|
||
}
|
||
})
|
||
},
|
||
//勾选
|
||
toggleCheck() {
|
||
this.isChecked = !this.isChecked;
|
||
},
|
||
//获取病症种类数据
|
||
getChatAssistants() {
|
||
uni.showLoading({
|
||
title: '加载中'
|
||
})
|
||
this.$http.request({
|
||
url: 'common/ragFlowApi/getChatAssistants',
|
||
method: "POST",
|
||
data: {},
|
||
header: {
|
||
"Content-Type": "application/json",
|
||
},
|
||
})
|
||
.then(res=> {
|
||
uni.hideLoading();
|
||
if (res.list&&res.list.length>0) {
|
||
res.list.forEach(item => {
|
||
if (item.name === "呼吸专科") {
|
||
item.icon_1 = '../../static/icon/iocn_zs_2.png';
|
||
item.icon_2 = '../../static/icon/iocn_zs_2_a.png';
|
||
} else if (item.name === "妇科专科") {
|
||
item.icon_1 = '../../static/icon/iocn_zs_5.png';
|
||
item.icon_2 = '../../static/icon/iocn_zs_5_a.png';
|
||
} else if (item.name === "风湿免疫专科") {
|
||
item.icon_1 = '../../static/icon/iocn_zs_3.png';
|
||
item.icon_2 = '../../static/icon/iocn_zs_3_a.png';
|
||
} else if (item.name === "消化专科") {
|
||
item.icon_1 = '../../static/icon/iocn_zs_1.png';
|
||
item.icon_2 = '../../static/icon/iocn_zs_1_a.png';
|
||
} else if (item.name === "肿瘤专科") {
|
||
item.icon_1 = '../../static/icon/iocn_zs_4.png';
|
||
item.icon_2 = '../../static/icon/iocn_zs_4_a.png';
|
||
} else if (item.name === "全科医生") {
|
||
item.icon_1 = '../../static/icon/iocn_zs_6.png';
|
||
item.icon_2 = '../../static/icon/iocn_zs_6_a.png';
|
||
}
|
||
});
|
||
this.chatAssistants = res.list;
|
||
}
|
||
})
|
||
.catch(e=>{
|
||
uni.hideLoading();
|
||
console.log(e)
|
||
});
|
||
},
|
||
//获取对话记录数据
|
||
getRecordsData() {
|
||
this.record_list = [];
|
||
this.$http.request({
|
||
url: 'common/ragFlowApi/getChats',
|
||
method: "POST",
|
||
data: {
|
||
chatId: '',
|
||
sessionId: '',
|
||
page: 1,
|
||
pageSize: 300
|
||
},
|
||
header: {
|
||
"Content-Type": "application/json",
|
||
},
|
||
})
|
||
.then(res=> {
|
||
if(res.code==0){
|
||
console.log('请求最新列表')
|
||
if(res.list&&res.list.length>0){
|
||
this.record_list = res.list;
|
||
|
||
//患者列表跳转来的
|
||
const index = res.list.findIndex(item =>
|
||
item.chatAssistantId === this.pageData.chatAssistantId &&
|
||
item.chatId === this.pageData.chatId
|
||
);
|
||
if(this.pageType&&this.pageType=='patient'){
|
||
this.clickRecord(this.pageData, index);
|
||
}
|
||
}else{
|
||
this.record_list = [];
|
||
this.null_text = '暂无数据';
|
||
}
|
||
}
|
||
})
|
||
.catch(e=>{
|
||
console.log(e)
|
||
});
|
||
},
|
||
//请求问答剩余次数
|
||
getUserAiVipCount() {
|
||
this.$http.request({
|
||
url: 'taihumed/aiVip/getUserAiVip',
|
||
method: "POST",
|
||
data: {},
|
||
header: {
|
||
"Content-Type": "application/json",
|
||
},
|
||
})
|
||
.then(res=> {
|
||
if(res.code==0){
|
||
this.flag = res.flag; //0-免费次数,1-可升级
|
||
if(res.aiVipLog&&res.aiVipLog.surplusCount>=0){
|
||
this.vipCount = res.aiVipLog.surplusCount;
|
||
this.vipText = '问答剩余:';
|
||
this.vipStatus = true;
|
||
}
|
||
|
||
if(res.freeCount>=0){
|
||
this.freeStatus = true;
|
||
this.freeCount = res.freeCount;
|
||
this.freeText = '赠送[全科医生]免费问答:';
|
||
}else{
|
||
this.freeStatus = false;
|
||
this.freeCount = null;
|
||
}
|
||
}
|
||
})
|
||
.catch(e=>{
|
||
uni.setStorageSync("guidePages", 2);
|
||
});
|
||
},
|
||
//点击每个记录
|
||
clickRecord(item,index){
|
||
//重新定义id
|
||
this.chatId = item.chatAssistantId;
|
||
this.sessionId = item.chatId;
|
||
//助手类型
|
||
this.chatName = item.chatAssistantName;
|
||
this.patientName = item.patientName;
|
||
console.log('我是:'+ this.patientName)
|
||
uni.setStorageSync('homeParams', { data: item, index: index });
|
||
this.activeRecord = index;
|
||
this.messages = [];
|
||
this.showMessages = true;
|
||
//请求病历夹接口
|
||
this.getRecordFolderList();
|
||
//如果正在回答的时候切换需要中断回答
|
||
this.closeWebSocket();
|
||
|
||
uni.showLoading({
|
||
title: '加载中'
|
||
})
|
||
this.$http.request({
|
||
url: 'common/ragFlowApi/getChats',
|
||
method: "POST",
|
||
data: {
|
||
chatId: item.chatAssistantId,
|
||
sessionId: item.chatId,
|
||
page: 1,
|
||
pageSize: 300
|
||
},
|
||
header: {
|
||
"Content-Type": "application/json",
|
||
},
|
||
})
|
||
.then(res=> {
|
||
if(res.code==0){
|
||
uni.hideLoading();
|
||
if(res.list&&res.list.length>0){
|
||
this.messages = res.list.map(item => {
|
||
let content = item.content.replace(/##\d+$$/g, '');
|
||
content = item.content.replace(/<\/?think>/g, '');
|
||
content = content.replace(/\*\*(.*?)\*\*/g, '<b class="bold-text">$1</b>');
|
||
content = content.replace(/\*(.*?)\*/g, '<span class="red-text">$1</span>');
|
||
content = content.replace(/\n\n/g, '\n');
|
||
content = content.replace(/\n/g, '<br>');
|
||
//content = content.replace(/^#{3,4}.*(\r?\n)?/gm, '');
|
||
|
||
return {
|
||
...item,
|
||
type: item.type === 1 ? 'answer' : 'question',
|
||
content: content
|
||
};
|
||
});
|
||
//滚动到最底部锚点
|
||
this.$nextTick(() => {
|
||
this.scrollToBottom();
|
||
});
|
||
}
|
||
|
||
this.tishi = false; //提示语不用展示
|
||
this.$refs.drawer.close();
|
||
}
|
||
});
|
||
},
|
||
|
||
//选中科目类别
|
||
clickAssistants(item, index){
|
||
this.activeIndex = index;
|
||
this.chatId = item.id;
|
||
this.chatName = item.name;
|
||
},
|
||
//提交
|
||
submit(){
|
||
let content = '';
|
||
let confirmText = '确定'
|
||
//没有次数的时候要求购买vip
|
||
if(this.flag==0){
|
||
content = '您没有问答次数了,马上去购买吧~'
|
||
}else if(this.flag==1){
|
||
content = '您的次数已经用完了,马上去升级吧~';
|
||
}else if(this.flag==2){
|
||
content = '您购买的次数已经用完了,请到期后再购买';
|
||
confirmText = '知道了'
|
||
}
|
||
if(this.freeCount==0&&this.vipStatus==false || this.vipCount==0&&this.freeCount==0){
|
||
uni.showModal({
|
||
title: '提示',
|
||
content: content,
|
||
confirmText: confirmText,
|
||
cancelText: "取消",
|
||
success: (res) => {
|
||
if (res.confirm&&this.flag!=2) {
|
||
uni.navigateTo({
|
||
url: '/pages/vip/index?flag='+this.flag
|
||
})
|
||
}
|
||
}
|
||
})
|
||
|
||
return
|
||
}
|
||
if(!this.formData.diagnosis){
|
||
this.$commonJS.showToast("请输入西医诊断");
|
||
return
|
||
}
|
||
if(!this.formData.illness){
|
||
this.$commonJS.showToast("请输入主要病史");
|
||
return
|
||
}
|
||
if(!this.formData.symptoms){
|
||
this.$commonJS.showToast("请输入主要症状");
|
||
return
|
||
}
|
||
if(!this.chatId){
|
||
this.$commonJS.showToast("请选择助手分类");
|
||
return
|
||
}
|
||
let question = '';
|
||
if(this.formData.name){
|
||
question += '患者'+this.formData.name+',';
|
||
}
|
||
if(this.formData.diagnosis){
|
||
question += '诊断:'+this.formData.diagnosis+',';
|
||
}
|
||
if(this.formData.illness){
|
||
question += '主要病史:'+this.formData.illness+',';
|
||
}
|
||
|
||
if(this.formData.symptoms){
|
||
question += '主要症状:'+this.formData.symptoms;
|
||
}
|
||
if(this.formData.result){
|
||
question += ',检查结果:'+this.formData.result+'。';
|
||
}
|
||
//基因检测
|
||
if(this.selectedItems.length>0){
|
||
const names = this.selectedItems.map(item => `'${item}'`).join('、');
|
||
question += ',基因检测:'+names+',检测为阳性。';
|
||
}
|
||
this.question = question;
|
||
//创建对话 获取sessionId
|
||
this.createChat();
|
||
},
|
||
//创建新对话
|
||
createChat(){
|
||
let data = {
|
||
chatId: this.chatId,
|
||
assistantName: this.chatName,
|
||
name: this.question.slice(0, 30)
|
||
}
|
||
this.$http.request({
|
||
url: 'common/ragFlowApi/createChat',
|
||
method: "POST",
|
||
data: data,
|
||
header: {
|
||
"Content-Type": "application/json",
|
||
},
|
||
})
|
||
.then(res=> {
|
||
if (res.code==0) {
|
||
this.sessionId = res.id;
|
||
//获取回答
|
||
console.log('sessionId', this.sessionId)
|
||
this.sendQuestion();
|
||
}
|
||
});
|
||
},
|
||
//交谈请求,获取回答
|
||
sendQuestion(){
|
||
//清空消息记录
|
||
this.messages = [];
|
||
this.showMessages = true;
|
||
this.pauseStatus = true;
|
||
this.loading = true;
|
||
//请求病历夹接口
|
||
this.getRecordFolderList();
|
||
//展示提示语
|
||
this.tishi = true;
|
||
|
||
const params = {
|
||
chatId: this.chatId,
|
||
chatName: this.chatName,
|
||
question: this.question,
|
||
sessionId: this.sessionId,
|
||
sessionName: this.question.slice(0, 30),
|
||
patientName: this.formData.name,
|
||
geneIds: this.selectedId.join(',')||''
|
||
};
|
||
//调用后端 SSE 接口,发送问题并接收实时回答
|
||
this.startSSE(params);
|
||
},
|
||
//开始监听 SSE 数据
|
||
startSSE(params){
|
||
const queryString = qs.stringify(params);
|
||
var data = {};
|
||
this.eventSource = uni.connectSocket({
|
||
url: this.$baseUrl + `websocket`,
|
||
success: () => {
|
||
console.log('WebSocket连接中...');
|
||
$http.request({
|
||
url: `common/ragFlowApi/chatToAssistantStream?${queryString}`,
|
||
method: "GET",
|
||
data,
|
||
header: {
|
||
"Content-Type": "application/json",
|
||
},
|
||
})
|
||
.then(res=> {
|
||
console.log('请求成功')
|
||
})
|
||
.catch(e=>{
|
||
console.log('失败')
|
||
});
|
||
},
|
||
fail: (err) => {
|
||
console.error('连接失败', err);
|
||
uni.showToast({ title: '连接失败', icon: 'error' });
|
||
}
|
||
});
|
||
|
||
// 监听服务器发送的消息
|
||
uni.onSocketMessage((event) => {
|
||
try {
|
||
const message = JSON.parse(event.data);
|
||
if (message.data === true) {
|
||
console.log("回答已结束");
|
||
this.pauseStatus = false;
|
||
this.loading = false;
|
||
//获取最近的会话记录数据
|
||
this.getRecordsData();
|
||
setTimeout(() => {
|
||
this.closeWebSocket();
|
||
}, 200);
|
||
return;
|
||
}
|
||
|
||
const answer = message.data.answer.replace(/##\d+$$/g, '');
|
||
console.log(message.data.answer)
|
||
let newAnswer = ''; //初始化 newAnswer 变量
|
||
if(this.previousAnswer === null){
|
||
console.log("第一次进来")
|
||
//如果没有包含上一条的内容,则直接显示当前 answer
|
||
newAnswer = answer;
|
||
this.messages.push({ content: newAnswer, type: 'answer' });
|
||
this.previousAnswer = newAnswer;
|
||
}else{
|
||
//只显示新增的部分
|
||
newAnswer = answer.replace(this.previousAnswer, '');
|
||
newAnswer = newAnswer.replace(/\*\*(.*?)\*\*/g, '<b class="bold-text">$1</b>');
|
||
newAnswer = newAnswer.replace(/\*(.*?)\*/g, '<span class="red-text">$1</span>');
|
||
newAnswer = newAnswer.replace(/\n\n/g, '\n');
|
||
newAnswer = newAnswer.replace(/\n/g, '<br>');
|
||
|
||
newAnswer = newAnswer.replace(/####([\s\S]*?)$/gm, '<h4>$1</h4>')
|
||
.replace(/###([\s\S]*?)$/gm, '<h3>$1</h3>');
|
||
|
||
this.messages.push({ content: newAnswer, type: 'answer' });
|
||
this.previousAnswer = answer;
|
||
}
|
||
//滚动到最底部锚点
|
||
this.$nextTick(() => {
|
||
this.scrollToBottom();
|
||
});
|
||
} catch (error) {
|
||
this.loading = false;
|
||
this.closeWebSocket();
|
||
}
|
||
});
|
||
//监听WebSocket连接打开
|
||
uni.onSocketOpen(() => {
|
||
console.log('WebSocket已连接');
|
||
});
|
||
//监听WebSocket错误
|
||
uni.onSocketError((err) => {
|
||
console.error('WebSocket连接错误', err);
|
||
});
|
||
//监听WebSocket关闭
|
||
uni.onSocketClose((res) => {
|
||
console.log('WebSocket 已关闭', res);
|
||
});
|
||
},
|
||
|
||
//回答界面的提交
|
||
sendAgain(){
|
||
console.log('这是再一次提问')
|
||
if(!this.pauseStatus){
|
||
if(!this.question_send){
|
||
this.$commonJS.showToast("请输入发送内容");
|
||
return
|
||
}
|
||
const params = {
|
||
chatId: this.chatId,
|
||
chatName: this.chatName,
|
||
question: this.question_send,
|
||
sessionId: this.sessionId,
|
||
sessionName: this.question_send.slice(0, 15),
|
||
patientName: this.formData.name
|
||
};
|
||
this.messages.push({
|
||
content: `${this.question_send}`,
|
||
type: 'question',
|
||
});
|
||
//滚动到最底部锚点
|
||
this.$nextTick(() => {
|
||
this.scrollToBottom();
|
||
});
|
||
this.loading = true;
|
||
this.question_send = '';
|
||
this.pauseStatus = true;
|
||
this.previousAnswer = null;
|
||
//调用后端 SSE 接口,发送问题并接收实时回答
|
||
this.startSSE(params);
|
||
}else{
|
||
console.log('不能点击了')
|
||
}
|
||
},
|
||
//点击左侧弹窗
|
||
openDrawer() {
|
||
this.$refs.drawer.open();
|
||
},
|
||
//点击新会话
|
||
showMode(){
|
||
this.showMessages = false;
|
||
this.messages = [];
|
||
this.formData = {
|
||
diagnosis: '',
|
||
illness: '',
|
||
symptoms: '',
|
||
genetic: '',
|
||
name: ''
|
||
}
|
||
this.chatId = null;
|
||
this.activeIndex = null;
|
||
this.activeRecord = null;
|
||
this.patientName = '';
|
||
this.selectedItems = [];
|
||
this.searchResultStatus = false;
|
||
//中断
|
||
this.closeWebSocket();
|
||
this.previousAnswer = null;
|
||
this.pauseStatus = false;
|
||
//把缓存清除
|
||
uni.removeStorageSync('homeParams');
|
||
//剩余次数
|
||
this.getUserAiVipCount();
|
||
},
|
||
//问答界面点击创建病历夹
|
||
clickFolder(){
|
||
if(this.pauseStatus){
|
||
this.$commonJS.showToast("正在回答中,请稍候");
|
||
return
|
||
}
|
||
if(this.folderList.length==0){
|
||
uni.showModal({
|
||
title: "提示",
|
||
content: "您还没有病历夹,确定创建新的病历夹吗?",
|
||
confirmText: "确定",
|
||
cancelText: "取消",
|
||
success: (res) => {
|
||
if (res.confirm) {
|
||
this.$refs.add_folder.open('center');
|
||
} else if (res.cancel) {
|
||
console.log('取消创建病历夹');
|
||
}
|
||
}
|
||
})
|
||
}else{
|
||
this.$refs.popup.open('center');
|
||
}
|
||
},
|
||
|
||
//获取病历夹列表
|
||
getRecordFolderList() {
|
||
this.$http.request({
|
||
url: 'taihumed/aiRecordFolder/getRecordFolders',
|
||
method: "POST",
|
||
data: {
|
||
assistantId: this.chatId,
|
||
chatId: this.sessionId,
|
||
folderName: '',
|
||
patientName: ''
|
||
},
|
||
header: {
|
||
"Content-Type": "application/json",
|
||
},
|
||
})
|
||
.then(res=> {
|
||
if(res.code==0){
|
||
if (res.list&&res.list.length>0) {
|
||
this.folderList = res.list;
|
||
}
|
||
}
|
||
})
|
||
.catch(e=>{
|
||
console.log(e)
|
||
});
|
||
},
|
||
//点击创建病历夹
|
||
createFolder(){
|
||
this.$refs.popup.close();
|
||
this.$refs.add_folder.open();
|
||
this.folderName = '';
|
||
this.isChecked = true;
|
||
},
|
||
//创建病历夹
|
||
addRecordFolder(){
|
||
if(!this.folderName){
|
||
this.$commonJS.showToast("请输入病历夹名称");
|
||
return
|
||
}
|
||
if(!this.isChecked){
|
||
uni.showLoading({
|
||
title: '正在创建中'
|
||
})
|
||
}
|
||
this.$http.request({
|
||
url: 'taihumed/aiRecordFolder/addRecordFolder',
|
||
method: "POST",
|
||
data: {
|
||
folderName: this.folderName,
|
||
sort: 0
|
||
},
|
||
header: {
|
||
"Content-Type": "application/json",
|
||
},
|
||
})
|
||
.then(res=> {
|
||
if(res.code==0){
|
||
uni.hideLoading();
|
||
//如果把当前对话放入病历夹
|
||
if(this.isChecked){
|
||
let id = res.aiRecordFolder.id;
|
||
this.addRecordFolderChat(id);
|
||
}else{
|
||
uni.showToast({
|
||
title: '创建成功',
|
||
icon: 'success'
|
||
})
|
||
this.$refs.add_folder.close();
|
||
this.getRecordFolderList(); //刷新列表数据
|
||
}
|
||
}
|
||
})
|
||
},
|
||
//询问是否加入
|
||
askJoin(data){
|
||
if(data.here==1){
|
||
this.$commonJS.showToast("不可重复加入病历夹");
|
||
return
|
||
}
|
||
this.$refs.popup.close();
|
||
uni.showModal({
|
||
title: "提示",
|
||
content: '确定加入['+data.folderName+']病历夹吗?',
|
||
confirmText: "确定",
|
||
cancelText: "取消",
|
||
success: (res) => {
|
||
if (res.confirm) {
|
||
this.addRecordFolderChat(data.id, '0');
|
||
} else if (res.cancel) {
|
||
console.log('取消加入病历夹');
|
||
}
|
||
}
|
||
})
|
||
},
|
||
//对话记录加入病历夹
|
||
addRecordFolderChat(id, type){
|
||
let text1 = '';
|
||
let text2 = '';
|
||
let patientName = '';
|
||
if(type=='0'){
|
||
text1 = '正在加入该病历夹';
|
||
text2 = '加入成功';
|
||
}else{
|
||
text1 = '正在创建中';
|
||
text2 = '创建成功';
|
||
}
|
||
uni.showLoading({
|
||
title: text1
|
||
})
|
||
//患者名字
|
||
if(this.formData.name){
|
||
patientName = this.formData.name;
|
||
}else{
|
||
patientName = this.patientName;
|
||
}
|
||
this.$http.request({
|
||
url: 'taihumed/aiRecordFolder/addRecordFolderChat',
|
||
method: "POST",
|
||
data: {
|
||
folderId: id,
|
||
patientName: patientName,
|
||
chatAssistantId: this.chatId,
|
||
chatId: this.sessionId
|
||
},
|
||
header: {
|
||
"Content-Type": "application/json",
|
||
},
|
||
})
|
||
.then(res=> {
|
||
uni.hideLoading();
|
||
uni.showToast({
|
||
title: text2,
|
||
icon: 'success'
|
||
})
|
||
this.$refs.add_folder.close();
|
||
this.$refs.popup.close();
|
||
this.getRecordFolderList(); //刷新列表数据
|
||
})
|
||
},
|
||
|
||
|
||
//关闭进程和监听
|
||
closeWebSocket() {
|
||
if (this.eventSource) {
|
||
// 关闭连接并移除监听
|
||
this.eventSource.close({
|
||
success: () => {
|
||
console.log('WebSocket 已关闭-closeWebSocket');
|
||
uni.offSocketMessage(); //移除消息监听
|
||
}
|
||
});
|
||
this.eventSource = null;
|
||
this.loading = false;
|
||
this.pauseStatus = false;
|
||
}
|
||
}
|
||
},
|
||
onHide() {
|
||
this.closeWebSocket();
|
||
},
|
||
onUnload() {
|
||
this.closeWebSocket();
|
||
}
|
||
}
|
||
</script>
|
||
|
||
<style lang="scss" scoped>
|
||
@import '@/static/mixin.scss';
|
||
.content{
|
||
background: linear-gradient(to bottom, #d8e6ff 0%, #ffffff 90%);
|
||
}
|
||
.home_top{
|
||
width: 100%;
|
||
background: #fff;
|
||
position: fixed;
|
||
top: 0;
|
||
left: 0;
|
||
padding: 90rpx 50rpx 30rpx;
|
||
text-align: center;
|
||
box-sizing: border-box;
|
||
z-index: 999;
|
||
|
||
.home_top_icon{
|
||
position: absolute;
|
||
left: 40rpx;
|
||
top: 85rpx;
|
||
display: flex;
|
||
align-items: center;
|
||
|
||
.home_top_left,.home_top_right{
|
||
width: 60rpx;
|
||
height: 60rpx;
|
||
padding: 10rpx;
|
||
box-sizing: border-box;
|
||
}
|
||
.home_top_right{
|
||
margin-left: 30rpx;
|
||
}
|
||
}
|
||
|
||
text{
|
||
font-size: 43rpx;
|
||
line-height: 44rpx;
|
||
font-weight: bold;
|
||
color: $themeColor;
|
||
}
|
||
.home_top_folder{
|
||
padding: 12rpx;
|
||
position: absolute;
|
||
right: 40rpx;
|
||
top: 75rpx;
|
||
|
||
image{
|
||
width: 50rpx;
|
||
height: 50rpx;
|
||
}
|
||
}
|
||
}
|
||
.home_wrap{
|
||
margin: 170rpx 50rpx 0;
|
||
padding-bottom: 300rpx;
|
||
overflow: hidden;
|
||
|
||
.home_logo{
|
||
padding-top: 40rpx;
|
||
|
||
image{
|
||
display: block;
|
||
width: 130rpx;
|
||
height: 130rpx;
|
||
margin: 0 auto;
|
||
}
|
||
|
||
.logo_main{
|
||
text-align: center;
|
||
display: block;
|
||
padding-top: 30rpx;
|
||
font-size: 35rpx;
|
||
color: $themeColor;
|
||
line-height: 46rpx;
|
||
}
|
||
|
||
.logo_con{
|
||
display: block;
|
||
width: 510rpx;
|
||
margin: 30rpx auto;
|
||
font-size: 28rpx;
|
||
color: #303030;
|
||
line-height: 40rpx;
|
||
text-indent: 2em;
|
||
}
|
||
}
|
||
}
|
||
|
||
.home_form{
|
||
margin-top: 60rpx;
|
||
position: relative;
|
||
|
||
.form_item{
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
margin-bottom: 30rpx;
|
||
|
||
text{
|
||
line-height: 68rpx;
|
||
font-size: 30rpx;
|
||
color: #fff;
|
||
background: $themeBgColor;
|
||
text-align: center;
|
||
border-radius: 20rpx;
|
||
padding: 0 25rpx;
|
||
border: 1rpx solid $themeBgColor;
|
||
box-sizing: border-box;
|
||
}
|
||
textarea,input{
|
||
display: inline-block;
|
||
width:calc(100% - 255rpx);
|
||
height: 70rpx;
|
||
line-height: 35rpx;
|
||
padding: 17rpx 30rpx;
|
||
font-size: 28rpx;
|
||
color: #303030;
|
||
border-radius: 50rpx;
|
||
border: 1rpx solid #777778;
|
||
|
||
box-sizing: border-box;
|
||
resize: none;
|
||
max-height: 200rpx;
|
||
overflow-y: scroll;
|
||
}
|
||
}
|
||
}
|
||
.custom-placeholder{
|
||
font-size: 25rpx;
|
||
color: #8b8c90 !important;
|
||
}
|
||
.submit_form{
|
||
width: 100%;
|
||
height: 90px;
|
||
background: #fff;
|
||
position: fixed;
|
||
left: 0;
|
||
bottom: 120rpx;
|
||
padding: 0 50rpx;
|
||
z-index: 999;
|
||
}
|
||
.submit_form_2{
|
||
height: 145rpx;
|
||
padding: 30rpx;
|
||
|
||
.assistants_list{
|
||
|
||
textarea,input{
|
||
width: calc(100% - 65px);
|
||
font-size: 26rpx;
|
||
line-height: 34rpx;
|
||
border-radius: 50rpx;
|
||
padding: 22rpx 30rpx;
|
||
color: #303030;
|
||
background: #f3f4f6;
|
||
max-height: 72rpx;
|
||
overflow: hidden;
|
||
}
|
||
}
|
||
.submit_btn{
|
||
top: 42rpx;
|
||
}
|
||
}
|
||
.assistants_list{
|
||
display: flex;
|
||
align-items: center;
|
||
flex-wrap: wrap;;
|
||
|
||
.assistants_item{
|
||
display: flex;
|
||
align-items: center;
|
||
width: 217rpx;
|
||
margin-top: 30rpx;
|
||
|
||
image{
|
||
width: 38rpx;
|
||
height: 38rpx;
|
||
}
|
||
text{
|
||
padding-left: 10rpx;
|
||
display: inline-block;
|
||
font-size: 28rpx;
|
||
color: #606061;
|
||
}
|
||
.assistants_img_1{
|
||
display: block;
|
||
}
|
||
.assistants_img_2{
|
||
display: none;
|
||
}
|
||
}
|
||
}
|
||
.submit_btn{
|
||
position: absolute;
|
||
right: 50rpx;
|
||
top: 90rpx;
|
||
|
||
image{
|
||
width: 55rpx;
|
||
height: 55rpx;
|
||
}
|
||
}
|
||
.active{
|
||
|
||
text{
|
||
color: $themeBgColor;
|
||
}
|
||
.assistants_img_1{
|
||
display: none !important;
|
||
}
|
||
.assistants_img_2{
|
||
display: block !important;
|
||
}
|
||
}
|
||
|
||
.message_wrap{
|
||
position: relative;
|
||
width: 100%;
|
||
background: #fff;
|
||
z-index: 99;
|
||
padding: 0 50rpx;
|
||
height: 80vh;
|
||
}
|
||
.message_title{
|
||
text-align: center;
|
||
font-size: 34rpx;
|
||
color: #333;
|
||
line-height: 50rpx;
|
||
display: block;
|
||
font-weight: bold;
|
||
padding: 10rpx 0;
|
||
}
|
||
.message-container-block{
|
||
padding-top: 10rpx;
|
||
padding-bottom: 80px;
|
||
font-size: 30rpx;
|
||
}
|
||
.message-container {
|
||
display: inline;
|
||
line-height: 48rpx;
|
||
}
|
||
|
||
/* 自定义的loading效果 */
|
||
.loading-spinner {
|
||
margin-top: 10rpx;
|
||
border: 2px solid #f3f3f3; /* 灰色背景 */
|
||
border-top: 2px solid #3498db; /* 蓝色顶部 */
|
||
border-radius: 50%;
|
||
width: 16px;
|
||
height: 16px;
|
||
animation: spin 1s linear infinite; /* 旋转动画 */
|
||
}
|
||
/* 旋转动画 */
|
||
@keyframes spin {
|
||
0% { transform: rotate(0deg); }
|
||
100% { transform: rotate(360deg); }
|
||
}
|
||
|
||
.bold-text {
|
||
display: block;
|
||
font-weight: bold;
|
||
font-size: 28rpx; /* 增大字号 */
|
||
}
|
||
h3{
|
||
font-size: 36rpx;
|
||
font-weight: bold;
|
||
}
|
||
h3{
|
||
font-size: 32rpx;
|
||
}
|
||
.red-text {
|
||
color: red;
|
||
font-size: 28rpx; /* 增大字号 */
|
||
}
|
||
.message-right{
|
||
display: block;
|
||
text-align: right;
|
||
margin: 30rpx 0;
|
||
|
||
text{
|
||
display: inline-block;
|
||
padding: 20rpx;
|
||
line-height: 38rpx;
|
||
background-color: rgba(81, 136, 229, 0.2);
|
||
border-radius: 15rpx;
|
||
color: #333;
|
||
font-size: 28rpx;
|
||
text-align: left;
|
||
}
|
||
}
|
||
|
||
.drawer-content {
|
||
height: 100vh;
|
||
display: flex;
|
||
flex-direction: column;
|
||
}
|
||
.list_content{
|
||
padding: 70rpx 20rpx 30rpx;
|
||
overflow-y: auto;
|
||
}
|
||
.list_item{
|
||
padding: 18rpx 10rpx;
|
||
}
|
||
.text_item{
|
||
display: block;
|
||
width: 100%;
|
||
font-size: 30rpx;
|
||
line-height: 40rpx;
|
||
padding: 0 10rpx;
|
||
white-space: nowrap;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
}
|
||
.null_text{
|
||
display: block;
|
||
text-align: center;
|
||
font-size: 30rpx;
|
||
color: #999;
|
||
padding-top: 150rpx;
|
||
}
|
||
.active_item{
|
||
background: #d8e6ff;
|
||
border-radius: 15rpx;
|
||
|
||
text{
|
||
color: #5188e5;
|
||
}
|
||
}
|
||
|
||
.folder_popup{
|
||
z-index: 9999;
|
||
|
||
.popup-content{
|
||
padding: 30rpx;
|
||
width: 550rpx;
|
||
background: #fff;
|
||
border-radius: 10rpx;
|
||
|
||
.popup-top{
|
||
padding: 15rpx;
|
||
display: flex;
|
||
align-items: center;
|
||
background: #5188e5;
|
||
border-radius: 10rpx;
|
||
color: #fff;
|
||
font-size: 28rpx;
|
||
line-height: 45rpx;
|
||
|
||
.uni-icons{
|
||
margin-right: 2rpx;
|
||
}
|
||
}
|
||
|
||
.popup-list{
|
||
|
||
.item-list{
|
||
padding: 15rpx;
|
||
background: #d8e6ff;
|
||
border-radius: 10rpx;
|
||
font-size: 28rpx;
|
||
line-height: 45rpx;
|
||
color: #5188e5;
|
||
margin-top: 15rpx;
|
||
white-space: nowrap;
|
||
overflow: hidden;
|
||
text-overflow: ellipsis;
|
||
position: relative;
|
||
|
||
text{
|
||
position: absolute;
|
||
right: 20rpx;
|
||
top: 12rpx;
|
||
font-size: 22rpx;
|
||
line-height: 45rpx;
|
||
color: #ff7800;
|
||
}
|
||
}
|
||
|
||
.popup-scroll{
|
||
max-height: 330rpx;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
.add_folder_name{
|
||
font-size: 30rpx;
|
||
line-height: 45rpx;
|
||
color: #5188e5;
|
||
}
|
||
.add_folder_input{
|
||
margin-top: 20rpx;
|
||
|
||
input{
|
||
height: 70rpx;
|
||
line-height: 70rpx;
|
||
padding: 0 20rpx;
|
||
font-size: 26rpx;
|
||
color: #303030;
|
||
border-radius: 10rpx;
|
||
border: 1rpx solid #ddd;
|
||
box-sizing: border-box;
|
||
}
|
||
|
||
.custom-placeholder{
|
||
font-size: 26rpx;
|
||
}
|
||
}
|
||
.add_folder_btn{
|
||
width: 50%;
|
||
margin: 25rpx auto 0;
|
||
background: #5188e5;
|
||
border-radius: 50rpx;
|
||
font-size: 26rpx;
|
||
color: #fff;
|
||
line-height: 70rpx;
|
||
}
|
||
.checkbox-item {
|
||
display: flex;
|
||
align-items: center;
|
||
margin-top: 20rpx;
|
||
|
||
checkbox {
|
||
transform: scale(0.55);
|
||
|
||
/deep/.uni-checkbox-input{
|
||
border: 1rpx solid $themeColor;
|
||
margin-right: 0;
|
||
}
|
||
}
|
||
|
||
text{
|
||
color: #666;
|
||
font-size: 26rpx;
|
||
margin-left: -5rpx;
|
||
}
|
||
}
|
||
|
||
//基因模糊
|
||
.tags-block{
|
||
width: calc(100% - 255rpx);
|
||
line-height: 46rpx;
|
||
padding: 12rpx 20rpx;
|
||
border-radius: 50rpx;
|
||
border: 1rpx solid #777778;
|
||
box-sizing: border-box;
|
||
|
||
input{
|
||
border: none !important;
|
||
padding: 5rpx 10rpx !important;
|
||
height: 50rpx !important;
|
||
}
|
||
}
|
||
.tags-container {
|
||
width: 100%;
|
||
display: flex;
|
||
flex-wrap: wrap;
|
||
}
|
||
|
||
.tag {
|
||
margin: 5rpx;
|
||
display: flex;
|
||
align-items: center;
|
||
background-color: #d8e6ff;
|
||
border-radius: 20rpx;
|
||
color: $themeColor;
|
||
padding: 0 15rpx;
|
||
font-size: 24rpx;
|
||
line-height: 38rpx;
|
||
}
|
||
|
||
.tag-close {
|
||
font-size: 24rpx;
|
||
font-weight: bold;
|
||
padding-left: 10rpx;
|
||
}
|
||
|
||
.result-list {
|
||
background: #fff;
|
||
width: calc(100% - 255rpx);
|
||
max-height: 325rpx;
|
||
border: 1px solid #eee;
|
||
border-radius: 10rpx;
|
||
margin-top: -30rpx;
|
||
float: right;
|
||
}
|
||
|
||
.result-item {
|
||
padding: 18rpx 20rpx;
|
||
border-bottom: 1px solid #f5f5f5;
|
||
font-size: 26rpx;
|
||
line-height: 30rpx;
|
||
}
|
||
.result-item:last-child{
|
||
border-bottom: 0;
|
||
}
|
||
.vip_count{
|
||
font-size: 26rpx;
|
||
color: red;
|
||
text-indent: 0;
|
||
|
||
text{
|
||
display: block;
|
||
}
|
||
}
|
||
.vip_count_block{
|
||
display: inline-block;
|
||
text-indent: 0;
|
||
padding-top: 10rpx;
|
||
}
|
||
</style>
|