1543 lines
34 KiB
Vue
1543 lines
34 KiB
Vue
<template>
|
||
<view class="content">
|
||
<z-nav-bar title="智能问答" bgColor="#fff">
|
||
|
||
<template slot="right">
|
||
|
||
<view class="home_top_icon">
|
||
|
||
<image src='/static/icon/icon_dialog.png' class="home_top_right" @click="showMode"></image>
|
||
<image src='/static/icon/icon_bars.png' class="home_top_left" @click="openDrawer"></image>
|
||
</view>
|
||
</template>
|
||
|
||
</z-nav-bar>
|
||
|
||
<view class="home_wrap" v-if="!showMessages">
|
||
<view class="home_logo">
|
||
<view style="display: flex;align-items: center;">
|
||
<image src='/static/logo.png'></image>
|
||
<text class="logo_main logo_con">我会结合吴门知识数据库为您分析解答</text>
|
||
</view>
|
||
|
||
|
||
</view>
|
||
<view class="home_form" style="box-shadow: 0 4px 12px rgba(0, 0, 0, .02), 0 2px 2px rgba(72, 104, 178, .01), 0 30px 60px rgba(72, 104, 178, .03);">
|
||
<template>
|
||
|
||
<view class="form_item" >
|
||
|
||
<textarea auto-height v-model="formData.symptoms" maxlength="-1"
|
||
|
||
placeholder="请输入问题" placeholder-class="custom-placeholder" />
|
||
</view>
|
||
|
||
<view class="submit_form submit_form1" v-if="!showMessages">
|
||
<view class="assistants_list">
|
||
<view v-for="(item, index) in chatAssistants" :key="index" @click="clickAssistants(item, index)" :style="chatAssistants.length>2?'margin-bottom: 20rpx;':''"
|
||
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>
|
||
</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="message_wrap" :style="{ top: 0 + '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="aiFlag">本回答由AI生成,内容仅供参考。</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/getChatAgents',
|
||
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_4.png';
|
||
item.icon_2 = '/static/icon/iocn_zs_4_a.png';
|
||
}
|
||
// 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 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 = '确定'
|
||
console.log(this.chatId,'chatIdchatIdchatIdchatIdchatId')
|
||
// //没有次数的时候要求购买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
|
||
}
|
||
|
||
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 + ',检测为阳性。';
|
||
// }
|
||
|
||
|
||
if (!this.chatId) {
|
||
this.$commonJS.showToast("请选择知识库分类");
|
||
return
|
||
}
|
||
this.question = question;
|
||
//创建对话 获取sessionId
|
||
this.createChat();
|
||
},
|
||
//创建新对话
|
||
createChat() {
|
||
let data = {
|
||
agentId: this.chatId,
|
||
|
||
}
|
||
this.$http.request({
|
||
url: 'common/ragFlowApi/createAgentChat',
|
||
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 = {
|
||
agentId: this.chatId,
|
||
|
||
question: this.question,
|
||
sessionId: this.sessionId,
|
||
|
||
};
|
||
//调用后端 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/chatToAgentStream?${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);
|
||
console.log(message,'接口ai返回')
|
||
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_icon {
|
||
// position: absolute;
|
||
left: 40rpx;
|
||
top: 85rpx;
|
||
display: flex;
|
||
align-items: center;
|
||
// margin-right: 40rpx;
|
||
|
||
.home_top_left,
|
||
.home_top_right {
|
||
width: 60rpx;
|
||
height: 60rpx;
|
||
padding: 10rpx;
|
||
box-sizing: border-box;
|
||
margin-right: 30rpx;
|
||
}
|
||
|
||
.home_top_right {
|
||
margin-left: 30rpx;
|
||
}
|
||
}
|
||
|
||
.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;
|
||
|
||
|
||
|
||
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: 140rpx 40rpx 0;
|
||
padding-bottom: 300rpx;
|
||
overflow: hidden;
|
||
|
||
.home_logo {
|
||
padding-top: 40rpx;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
|
||
image {
|
||
display: block;
|
||
width: 54rpx;
|
||
height: 54rpx;
|
||
margin-right: 10rpx;
|
||
// margin: 0 auto;
|
||
}
|
||
|
||
.logo_main {
|
||
text-align: left;
|
||
display: block;
|
||
color: #0f1115;
|
||
// padding-top: 30rpx;
|
||
font-size: 35rpx;
|
||
// color: $themeColor;
|
||
line-height: 46rpx;
|
||
}
|
||
|
||
.logo_con {
|
||
display: block;
|
||
width: auto;
|
||
// margin: 30rpx auto;
|
||
font-size: 36rpx;
|
||
color: #303030;
|
||
line-height: 40rpx;
|
||
// text-indent: 2em;
|
||
}
|
||
}
|
||
}
|
||
|
||
.home_form {
|
||
position: relative;
|
||
margin-top: 60rpx;
|
||
padding-top: 10rpx;
|
||
position: relative;
|
||
border-radius: 48rpx;
|
||
border: 1rpx solid #0000001a;
|
||
|
||
.form_item {
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
margin-bottom: 60rpx;
|
||
|
||
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%);
|
||
min-height: 240rpx;
|
||
line-height: 35rpx;
|
||
padding: 17rpx 30rpx;
|
||
font-size: 30rpx;
|
||
color: #303030;
|
||
|
||
|
||
box-sizing: border-box;
|
||
resize: none;
|
||
max-height: auto;
|
||
overflow-y: scroll;
|
||
}
|
||
}
|
||
}
|
||
|
||
.custom-placeholder {
|
||
font-size: 30rpx;
|
||
color: #8b8c90 !important;
|
||
}
|
||
|
||
.submit_form {
|
||
width: 100%;
|
||
height: 90px;
|
||
// background: #fff;
|
||
position: absolute;
|
||
left: 0;
|
||
bottom: 0rpx;
|
||
padding: 0 50rpx;
|
||
z-index: 999;
|
||
}
|
||
|
||
.submit_form_2 {
|
||
height: 135rpx;
|
||
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: auto;
|
||
overflow: hidden;
|
||
}
|
||
}
|
||
|
||
.submit_btn {
|
||
top: 42rpx;
|
||
}
|
||
}
|
||
.submit_form1{
|
||
display: flex;
|
||
align-items: center;
|
||
height: auto;
|
||
bottom: 20rpx;
|
||
padding:0 20rpx;
|
||
justify-content: space-between;
|
||
// position: relative;
|
||
.assistants_list{
|
||
width: calc(100% - 80rpx);
|
||
}
|
||
.submit_btn{
|
||
position: relative;
|
||
right: 0;
|
||
top: 0;
|
||
}
|
||
}
|
||
.assistants_list {
|
||
display: flex;
|
||
align-items: center;
|
||
flex-wrap: wrap;
|
||
;
|
||
|
||
.assistants_item {
|
||
display: flex;
|
||
align-items: center;
|
||
min-width: 200rpx;
|
||
border-radius: 18px;
|
||
padding:6rpx 24rpx;
|
||
border: 1px solid #0000001a;
|
||
margin-right: 20rpx;
|
||
|
||
|
||
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 {
|
||
border-color: #b7c8fe !important;
|
||
background-color: #edf3fe !important;
|
||
text {
|
||
color: #719feb !important;
|
||
font-weight: 700 !important;
|
||
}
|
||
|
||
.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: 92vh;
|
||
}
|
||
|
||
.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;
|
||
}
|
||
|
||
.aiFlag {
|
||
position: absolute;
|
||
bottom: 55px;
|
||
margin-bottom: 10rpx;
|
||
left: 20px;
|
||
width: calc(100% - 40px);
|
||
font-size: 11px;
|
||
line-height: 16px;
|
||
color: #f69e12;
|
||
z-index: 999;
|
||
background: rgba(254, 243, 225, 0.8);
|
||
border: 1px solid #f2d7aa;
|
||
padding: 5px;
|
||
border-radius: 6px;
|
||
}</style>
|