转诊方功能优化
This commit is contained in:
@@ -466,6 +466,7 @@
|
||||
"path": "chat/chat",
|
||||
"style": {
|
||||
"navigationBarTitleText": "在线问诊",
|
||||
"navigationStyle": "custom",
|
||||
"enablePullDownRefresh": false
|
||||
}
|
||||
},
|
||||
|
||||
@@ -58,4 +58,21 @@ export async function submitTransferQuestionsApi(params) {
|
||||
*/
|
||||
export async function getTransferAssistantConfigApi() {
|
||||
return await get('/transfer-assistant-config/get-config', {}, 3)
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取转诊咨询协议信息(固定文案和协议列表)
|
||||
* @returns {Promise<*>}
|
||||
*/
|
||||
export async function getTransferConsultationAgreementInfoApi() {
|
||||
return await get('/transfer-consultation/get-agreement-info', {}, 3)
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取协议详情(根据协议ID)
|
||||
* @param {number} agreementId 协议ID
|
||||
* @returns {Promise<*>}
|
||||
*/
|
||||
export async function getAgreementDetailApi(agreementId) {
|
||||
return await get(`/agreement/get-detail?id=${agreementId}`, {}, 3)
|
||||
}
|
||||
@@ -2,6 +2,16 @@
|
||||
<!-- 根容器 -->
|
||||
<view class="chat-container">
|
||||
|
||||
<!-- ================= 自定义导航栏 ================= -->
|
||||
<u-navbar
|
||||
class="chat-navbar"
|
||||
:is-back="true"
|
||||
:title="doctorUserInfo.nick_name || '在线问诊'"
|
||||
:custom-back="handleBack"
|
||||
title-color="#333"
|
||||
:border-bottom="false"
|
||||
></u-navbar>
|
||||
|
||||
<!-- ================= 顶部导航栏 (保持原样) ================= -->
|
||||
<view class="chat-header">
|
||||
<view class="header-main" @click="toggleDoctorDetail">
|
||||
@@ -49,6 +59,18 @@
|
||||
>
|
||||
<view class="message-list-container" id="msglist" :style="{ paddingBottom: (keyboardHeight > 0 ? keyboardHeight + 10 : 0) + 'px' }">
|
||||
|
||||
<!-- 协议提示信息 -->
|
||||
<view class="notice-box" v-if="showNotice">
|
||||
<view class="notice-text">
|
||||
<text v-for="(part, index) in parsedNoticeParts" :key="index">
|
||||
<text v-if="part.type === 'text'">{{ part.text }}</text>
|
||||
<text v-else-if="part.type === 'agreement'" class="notice-link" @click="viewAgreement(part.agreementId)">
|
||||
《{{ part.text }}》
|
||||
</text>
|
||||
</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 加载指示器 -->
|
||||
<view v-if="loading" class="loading-spinner">
|
||||
<view class="spinner"></view>
|
||||
@@ -92,6 +114,19 @@
|
||||
<view style="height: 180rpx;"></view>
|
||||
</scroll-view>
|
||||
|
||||
<!-- 协议抽屉 -->
|
||||
<u-popup v-model="showAgreement" :safe-area-inset-bottom="true" mode="bottom" height="88%" border-radius="20"
|
||||
:closeable="true" z-index="20000">
|
||||
<view class="agreement-popup">
|
||||
<view class="agreement-header">
|
||||
<text class="agreement-title">{{ currentAgreementName || '协议详情' }}</text>
|
||||
</view>
|
||||
<view class="agreement-content">
|
||||
<u-parse :html="agreementContent"></u-parse>
|
||||
</view>
|
||||
</view>
|
||||
</u-popup>
|
||||
|
||||
<!-- ================= 底部输入与工具栏 (重构优化版) ================= -->
|
||||
<view class="chat-footer safe-area-inset-bottom" :style="{ bottom: keyboardHeight + 'px' }">
|
||||
<view v-if="isConsultationEnded" class="consultation-ended-notice">
|
||||
@@ -161,7 +196,7 @@ import MessageBubble from './components/MessageBubble.vue'; // 引入组件
|
||||
import { webSocketManager } from '@/utils/ws/websocket';
|
||||
import { ChatManager } from '@/store/chat/chat.js';
|
||||
import {getChatRegisterInfoApi, sendToUserApi, upLoadChatFileApi, getDoctorInfoApi, getRoomStatusApi} from "@/request/api/im";
|
||||
import {getTransferAssistantConfigApi} from "@/request/api/transferPrescription";
|
||||
import {getTransferAssistantConfigApi, getTransferConsultationAgreementInfoApi, getAgreementDetailApi} from "@/request/api/transferPrescription";
|
||||
import { checkDev } from '@/utils/utils';
|
||||
|
||||
// 根据环境获取上传URL
|
||||
@@ -218,6 +253,14 @@ export default {
|
||||
isLoadHistory: false,
|
||||
isConsultationEnded: false,
|
||||
keyboardHeight: 0,
|
||||
showNotice: true, // 显示协议提示
|
||||
noticeText: '', // 提示文案
|
||||
agreements: [], // 协议列表
|
||||
parsedNoticeParts: [], // 解析后的文案部分(文本和协议链接)
|
||||
showAgreement: false, // 控制协议抽屉显示
|
||||
agreementContent: '', // 协议内容
|
||||
currentAgreementId: 0, // 当前查看的协议ID
|
||||
currentAgreementName: '', // 当前查看的协议名称
|
||||
};
|
||||
},
|
||||
|
||||
@@ -230,7 +273,9 @@ export default {
|
||||
icon: 'none'
|
||||
});
|
||||
setTimeout(() => {
|
||||
uni.navigateBack();
|
||||
uni.reLaunch({
|
||||
url: '/pages/index/index'
|
||||
});
|
||||
}, 1500);
|
||||
return;
|
||||
}
|
||||
@@ -258,8 +303,9 @@ export default {
|
||||
// 无论是否有nickName,都需要获取医生详情信息(职称、医院、擅长)
|
||||
this.fetchDoctorInfo(doctorId);
|
||||
|
||||
// 加载助理配置信息
|
||||
// 加载助理配置信息和协议信息
|
||||
this.loadAssistantConfig();
|
||||
this.loadAgreementInfo();
|
||||
|
||||
const nickName = this.doctorUserInfo.nick_name;
|
||||
if (nickName && nickName !== 'undefined' && nickName !== 'null') {
|
||||
@@ -278,6 +324,12 @@ export default {
|
||||
});
|
||||
},
|
||||
|
||||
onBackPress() {
|
||||
// 拦截物理返回键,跳转到首页
|
||||
this.handleBack();
|
||||
return true; // 阻止默认返回行为
|
||||
},
|
||||
|
||||
onUnload() {
|
||||
if (this.innerAudioContext) {
|
||||
this.innerAudioContext.destroy();
|
||||
@@ -369,6 +421,117 @@ export default {
|
||||
}
|
||||
},
|
||||
|
||||
// 加载协议信息
|
||||
async loadAgreementInfo() {
|
||||
try {
|
||||
const res = await getTransferConsultationAgreementInfoApi();
|
||||
if (res.data && res.data.code === 0) {
|
||||
const result = res.data.result || {};
|
||||
this.noticeText = result.notice_text || '';
|
||||
this.agreements = result.agreements || [];
|
||||
|
||||
// 解析文案,提取协议链接
|
||||
this.parseNoticeText();
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('加载协议信息失败:', error);
|
||||
// 失败时使用默认文案
|
||||
this.noticeText = '根据国家互联网医院管理办法要求,平台仅为复诊患者提供服务。为了保障您的用药安全,购买该药物需要经过医生问诊,请根据真实情况回答,并请仔细阅读《互联网医疗风险告知及知情同意书》,继续咨询即表示您已知悉相关规则与风险并同意相关条款。';
|
||||
this.parseNoticeText();
|
||||
}
|
||||
},
|
||||
// 解析提示文案,将《协议名称》替换为可点击的链接
|
||||
parseNoticeText() {
|
||||
if (!this.noticeText) {
|
||||
this.parsedNoticeParts = [];
|
||||
return;
|
||||
}
|
||||
|
||||
const parts = [];
|
||||
// 使用正则表达式匹配《》中的协议名称
|
||||
const regex = /《([^》]+)》/g;
|
||||
let lastIndex = 0;
|
||||
let match;
|
||||
|
||||
while ((match = regex.exec(this.noticeText)) !== null) {
|
||||
// 添加匹配前的文本
|
||||
if (match.index > lastIndex) {
|
||||
parts.push({
|
||||
type: 'text',
|
||||
text: this.noticeText.substring(lastIndex, match.index)
|
||||
});
|
||||
}
|
||||
|
||||
// 查找匹配的协议
|
||||
const agreementName = match[1];
|
||||
const agreement = this.agreements.find(ag => ag.name === agreementName || ag.name.includes(agreementName));
|
||||
|
||||
if (agreement) {
|
||||
// 找到匹配的协议,添加为可点击的链接
|
||||
parts.push({
|
||||
type: 'agreement',
|
||||
text: agreementName,
|
||||
agreementId: agreement.id
|
||||
});
|
||||
} else {
|
||||
// 没找到匹配的协议,作为普通文本显示
|
||||
parts.push({
|
||||
type: 'text',
|
||||
text: match[0] // 《协议名称》
|
||||
});
|
||||
}
|
||||
|
||||
lastIndex = regex.lastIndex;
|
||||
}
|
||||
|
||||
// 添加剩余的文本
|
||||
if (lastIndex < this.noticeText.length) {
|
||||
parts.push({
|
||||
type: 'text',
|
||||
text: this.noticeText.substring(lastIndex)
|
||||
});
|
||||
}
|
||||
|
||||
this.parsedNoticeParts = parts;
|
||||
},
|
||||
// 查看协议
|
||||
async viewAgreement(agreementId) {
|
||||
if (!agreementId) {
|
||||
uni.showToast({
|
||||
title: '协议ID不能为空',
|
||||
icon: 'none'
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
// 查找协议名称
|
||||
const agreement = this.agreements.find(ag => ag.id === agreementId);
|
||||
this.currentAgreementId = agreementId;
|
||||
this.currentAgreementName = agreement ? agreement.name : '协议详情';
|
||||
this.showAgreement = true;
|
||||
|
||||
try {
|
||||
const res = await getAgreementDetailApi(agreementId);
|
||||
if (res.data && res.data.code === 0) {
|
||||
const result = res.data.result || {};
|
||||
this.agreementContent = result.content || '';
|
||||
// 如果API返回了协议名称,使用API返回的名称
|
||||
if (result.name) {
|
||||
this.currentAgreementName = result.name;
|
||||
}
|
||||
} else {
|
||||
throw new Error(res.data.message || '获取协议详情失败');
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取协议详情失败:', error);
|
||||
uni.showToast({
|
||||
title: error.message || '获取协议详情失败',
|
||||
icon: 'none'
|
||||
});
|
||||
this.showAgreement = false;
|
||||
}
|
||||
},
|
||||
|
||||
// === 音频播放逻辑 ===
|
||||
playAudio(msg) {
|
||||
const audioUrl = getParsedContent(msg.message_type, msg.message_content);
|
||||
@@ -475,13 +638,22 @@ export default {
|
||||
const systemInfo = uni.getSystemInfoSync();
|
||||
const windowHeight = systemInfo.windowHeight;
|
||||
const query = uni.createSelectorQuery().in(this);
|
||||
query.select('.chat-header').boundingClientRect((headerRect) => {
|
||||
const headerHeight = headerRect ? headerRect.height : 0;
|
||||
const query2 = uni.createSelectorQuery().in(this);
|
||||
query2.select('.chat-footer').boundingClientRect((footerRect) => {
|
||||
const footerHeight = footerRect ? footerRect.height : 0;
|
||||
const scrollHeight = windowHeight - headerHeight - footerHeight;
|
||||
this.scrollViewStyle = `height: ${scrollHeight}px;`;
|
||||
|
||||
// 获取u-navbar导航栏高度(uView会自动处理状态栏高度)
|
||||
query.select('.chat-navbar').boundingClientRect((navbarRect) => {
|
||||
const navbarHeight = navbarRect ? navbarRect.height : 0;
|
||||
|
||||
// 获取聊天头部高度
|
||||
query.select('.chat-header').boundingClientRect((headerRect) => {
|
||||
const headerHeight = headerRect ? headerRect.height : 0;
|
||||
|
||||
// 获取底部输入栏高度
|
||||
const query2 = uni.createSelectorQuery().in(this);
|
||||
query2.select('.chat-footer').boundingClientRect((footerRect) => {
|
||||
const footerHeight = footerRect ? footerRect.height : 0;
|
||||
const scrollHeight = windowHeight - navbarHeight - headerHeight - footerHeight;
|
||||
this.scrollViewStyle = `height: ${scrollHeight}px;`;
|
||||
}).exec();
|
||||
}).exec();
|
||||
}).exec();
|
||||
});
|
||||
@@ -840,7 +1012,27 @@ export default {
|
||||
viewPrescription(id, orderNo) { uni.navigateTo({ url: "/subPackages/my/myrecord-detail?id=" + id + '&no=' + orderNo }); },
|
||||
goToOrderMedicine(id, orderNo) { uni.navigateTo({ url: "/subPackages/my/my-drug/drug-info?id=" + id + '&order_type=prescription' }); },
|
||||
viewRegister(id, orderNo) { uni.navigateTo({ url: "/subPackages/register/register-detail?id=" + id + '&no=' + orderNo }); },
|
||||
viewOrder(orderId, orderNo) { uni.navigateTo({ url: "/subPackages/my/my-drug/drug-info?id=" + orderId }); }
|
||||
viewOrder(orderId, orderNo) { uni.navigateTo({ url: "/subPackages/my/my-drug/drug-info?id=" + orderId }); },
|
||||
|
||||
// 处理返回操作(自定义返回逻辑)
|
||||
handleBack() {
|
||||
// 跳转到首页(tabBar页面需要使用switchTab)
|
||||
uni.switchTab({
|
||||
url: '/pages/index/index',
|
||||
success: () => {
|
||||
console.log('跳转到首页成功');
|
||||
},
|
||||
fail: (err) => {
|
||||
console.error('跳转到首页失败:', err);
|
||||
// 如果switchTab失败,尝试使用reLaunch
|
||||
uni.reLaunch({
|
||||
url: '/pages/index/index'
|
||||
});
|
||||
}
|
||||
});
|
||||
// 返回 false 阻止 u-navbar 的默认返回行为(重要!)
|
||||
return false;
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
@@ -866,6 +1058,7 @@ $bg-white: #ffffff;
|
||||
z-index: 100;
|
||||
position: relative;
|
||||
box-shadow: 0 1rpx 2rpx rgba(0,0,0,0.05);
|
||||
// u-navbar会自动处理状态栏和导航栏高度,这里不需要额外设置margin-top
|
||||
|
||||
.header-main {
|
||||
display: flex;
|
||||
@@ -934,6 +1127,26 @@ $bg-white: #ffffff;
|
||||
padding: 30rpx 24rpx;
|
||||
}
|
||||
|
||||
.notice-box {
|
||||
margin-bottom: 20rpx;
|
||||
padding: 24rpx;
|
||||
background: #F5F5F5;
|
||||
border-radius: 12rpx;
|
||||
font-size: 24rpx;
|
||||
line-height: 1.6;
|
||||
color: #666;
|
||||
|
||||
.notice-text {
|
||||
display: block;
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
.notice-link {
|
||||
color: #0A84FF;
|
||||
text-decoration: underline;
|
||||
}
|
||||
}
|
||||
|
||||
.empty-state { text-align: center; padding: 40rpx 0; .empty-text { font-size: 24rpx; color: #ccc; } }
|
||||
.loading-spinner { display: flex; justify-content: center; align-items: center; padding: 20rpx; .spinner { width: 30rpx; height: 30rpx; border: 2rpx solid #ddd; border-top-color: #999; border-radius: 50%; animation: spin 0.8s linear infinite; } .spinner-text { font-size: 24rpx; color: #999; margin-left: 10rpx;} }
|
||||
@keyframes spin { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } }
|
||||
@@ -1007,4 +1220,34 @@ $bg-white: #ffffff;
|
||||
.tip { font-size: 24rpx; color: #ccc; }
|
||||
}
|
||||
}
|
||||
|
||||
.agreement-popup {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.agreement-header {
|
||||
padding: 30rpx 50rpx 20rpx;
|
||||
border-bottom: 1rpx solid #e5e7eb;
|
||||
background: #fff;
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
.agreement-title {
|
||||
font-size: 32rpx;
|
||||
font-weight: 600;
|
||||
color: #1e293b;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.agreement-content {
|
||||
flex: 1;
|
||||
padding: 30rpx 50rpx 40rpx;
|
||||
overflow-y: auto;
|
||||
min-height: 0;
|
||||
}
|
||||
</style>
|
||||
@@ -39,8 +39,8 @@
|
||||
<text class="question-number">{{ index + 1 }}.</text>
|
||||
<text class="question-content">{{ qa.question }}</text>
|
||||
</view>
|
||||
<view class="answer-text" :class="qa.answer === '是' ? 'answer-yes' : 'answer-no'">
|
||||
<text class="answer-icon">{{ qa.answer === '是' ? '✓' : '✗' }}</text>
|
||||
<view class="answer-text" :class="qa.answer === '是' ? 'answer-yes' : (qa.answer === '否' ? 'answer-no' : 'answer-other')">
|
||||
<text class="answer-icon">{{ qa.answer === '是' ? '✓' : (qa.answer === '否' ? '✗' : '?') }}</text>
|
||||
<text>{{ qa.answer }}</text>
|
||||
</view>
|
||||
</view>
|
||||
@@ -78,6 +78,26 @@ export default {
|
||||
},
|
||||
handleReject() {
|
||||
this.$emit('reject', this.messageData);
|
||||
},
|
||||
// 获取答案的样式类:是=绿色,否=红色,其他=黄色
|
||||
getAnswerClass(answer) {
|
||||
if (answer === '是') {
|
||||
return 'answer-yes';
|
||||
} else if (answer === '否') {
|
||||
return 'answer-no';
|
||||
} else {
|
||||
return 'answer-other';
|
||||
}
|
||||
},
|
||||
// 获取答案的图标
|
||||
getAnswerIcon(answer) {
|
||||
if (answer === '是') {
|
||||
return '✓';
|
||||
} else if (answer === '否') {
|
||||
return '✗';
|
||||
} else {
|
||||
return '?';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -273,6 +293,15 @@ export default {
|
||||
color: #dc2626;
|
||||
}
|
||||
|
||||
.answer-other {
|
||||
background: #fef3c7;
|
||||
color: #d97706;
|
||||
}
|
||||
|
||||
.answer-other .answer-icon {
|
||||
color: #d97706;
|
||||
}
|
||||
|
||||
.answer-icon {
|
||||
font-size: 24rpx;
|
||||
font-weight: bold;
|
||||
|
||||
@@ -11,17 +11,34 @@
|
||||
|
||||
<!-- 提示信息(类似图片中的灰色提示框) -->
|
||||
<view class="notice-box" v-if="showNotice">
|
||||
<text class="notice-text">
|
||||
根据国家互联网医院管理办法要求,平台仅为复诊患者提供服务。为了保障您的用药安全,购买该药物需要经过医生问诊,请根据真实情况回答,并请仔细阅读《互联网医疗风险告知及知情同意书》,继续咨询即表示您已知悉相关规则与风险并同意相关条款。
|
||||
</text>
|
||||
<text class="notice-link" @click="viewAgreement">《互联网医疗风险告知及知情同意书》</text>
|
||||
<view class="notice-text">
|
||||
<text v-for="(part, index) in parsedNoticeParts" :key="index">
|
||||
<text v-if="part.type === 'text'">{{ part.text }}</text>
|
||||
<text v-else-if="part.type === 'agreement'" class="notice-link" @click="viewAgreement(part.agreementId)">
|
||||
《{{ part.text }}》
|
||||
</text>
|
||||
</text>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 协议抽屉 -->
|
||||
<u-popup v-model="showAgreement" :safe-area-inset-bottom="true" mode="bottom" height="88%" border-radius="20"
|
||||
:closeable="true" z-index="20000">
|
||||
<view class="agreement-popup">
|
||||
<view class="agreement-header">
|
||||
<text class="agreement-title">{{ currentAgreementName || '协议详情' }}</text>
|
||||
</view>
|
||||
<view class="agreement-content">
|
||||
<u-parse :html="agreementContent"></u-parse>
|
||||
</view>
|
||||
</view>
|
||||
</u-popup>
|
||||
|
||||
<!-- 聊天消息区域 -->
|
||||
<scroll-view class="messages-scroll" scroll-y="true" :scroll-into-view="scrollIntoView" scroll-with-animation>
|
||||
<view class="messages-container">
|
||||
<!-- 按时间顺序渲染消息 -->
|
||||
<view v-for="(message, index) in displayedMessages" :key="message.id || index">
|
||||
<view v-for="(message, index) in displayedMessages" :key="message.id">
|
||||
<!-- 欢迎消息 -->
|
||||
<view v-if="message.type === 'welcome'" class="message-item other">
|
||||
<image class="avatar" :src="assistantAvatar" mode="aspectFill"></image>
|
||||
@@ -86,10 +103,16 @@
|
||||
</view>
|
||||
</scroll-view>
|
||||
|
||||
<!-- 底部操作按钮(只有"是"和"否") -->
|
||||
<!-- 底部操作按钮(根据问题的answers动态生成) -->
|
||||
<view class="action-footer safe-area-inset-bottom" v-if="currentQuestion && !isSubmitting && !showWaiting">
|
||||
<button class="action-btn no-btn" @click="handleAnswer('否')">否</button>
|
||||
<button class="action-btn yes-btn" @click="handleAnswer('是')">是</button>
|
||||
<button
|
||||
v-for="(answer, index) in currentQuestionAnswers"
|
||||
:key="index"
|
||||
:class="['action-btn', getAnswerButtonClass(answer, index)]"
|
||||
@click="handleAnswer(answer)"
|
||||
>
|
||||
{{ answer }}
|
||||
</button>
|
||||
</view>
|
||||
|
||||
<!-- 提交中提示 -->
|
||||
@@ -107,7 +130,9 @@ import {
|
||||
getTransferPrescriptionDetailApi,
|
||||
getTransferQuestionsApi,
|
||||
submitTransferQuestionsApi,
|
||||
getTransferAssistantConfigApi
|
||||
getTransferAssistantConfigApi,
|
||||
getTransferConsultationAgreementInfoApi,
|
||||
getAgreementDetailApi
|
||||
} from '@/request/api/transferPrescription.js'
|
||||
|
||||
export default {
|
||||
@@ -128,7 +153,14 @@ export default {
|
||||
userAvatar: '/static/images/user-avatar.png', // 用户头像(需要替换为实际路径)
|
||||
prescriptionInfo: '炒黄芪等共2味', // 处方信息(可以从转诊详情中获取)
|
||||
replyDelay: 1.00, // 回复延迟时间(秒),从配置中获取
|
||||
isTyping: false // 是否显示"正在回复"提示
|
||||
isTyping: false, // 是否显示"正在回复"提示
|
||||
noticeText: '', // 提示文案
|
||||
agreements: [], // 协议列表
|
||||
parsedNoticeParts: [], // 解析后的文案部分(文本和协议链接)
|
||||
showAgreement: false, // 控制协议抽屉显示
|
||||
agreementContent: '', // 协议内容
|
||||
currentAgreementId: 0, // 当前查看的协议ID
|
||||
currentAgreementName: '' // 当前查看的协议名称
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
@@ -139,6 +171,33 @@ export default {
|
||||
}
|
||||
return null
|
||||
},
|
||||
// 当前问题的答案选项
|
||||
currentQuestionAnswers() {
|
||||
if (!this.currentQuestion) {
|
||||
return ['是', '否'] // 默认答案
|
||||
}
|
||||
|
||||
// 获取问题的answers字段
|
||||
let answers = this.currentQuestion.answers
|
||||
|
||||
// 如果answers是字符串(JSON格式),解析为数组
|
||||
if (typeof answers === 'string') {
|
||||
try {
|
||||
answers = JSON.parse(answers)
|
||||
} catch (e) {
|
||||
console.error('解析答案选项失败:', e)
|
||||
answers = ['是', '否']
|
||||
}
|
||||
}
|
||||
|
||||
// 如果answers不是数组或为空,使用默认值
|
||||
if (!Array.isArray(answers) || answers.length === 0) {
|
||||
answers = ['是', '否']
|
||||
}
|
||||
|
||||
// 过滤空值
|
||||
return answers.filter(answer => answer && answer.trim().length > 0)
|
||||
},
|
||||
// 按时间顺序显示的消息列表
|
||||
displayedMessages() {
|
||||
const messages = []
|
||||
@@ -187,6 +246,35 @@ export default {
|
||||
return messages
|
||||
}
|
||||
},
|
||||
watch: {
|
||||
// 监听当前问题索引变化,自动滚动到底部
|
||||
currentQuestionIndex(newVal, oldVal) {
|
||||
if (newVal !== oldVal && newVal >= 0) {
|
||||
this.$nextTick(() => {
|
||||
this.scrollToBottom()
|
||||
})
|
||||
}
|
||||
},
|
||||
// 监听用户答案变化,自动滚动到底部
|
||||
userAnswers: {
|
||||
handler(newVal, oldVal) {
|
||||
if (newVal.length > (oldVal?.length || 0)) {
|
||||
this.$nextTick(() => {
|
||||
this.scrollToBottom()
|
||||
})
|
||||
}
|
||||
},
|
||||
deep: true
|
||||
},
|
||||
// 监听显示等待消息,自动滚动到底部
|
||||
showWaiting(newVal) {
|
||||
if (newVal) {
|
||||
this.$nextTick(() => {
|
||||
this.scrollToBottom()
|
||||
})
|
||||
}
|
||||
}
|
||||
},
|
||||
onLoad(options) {
|
||||
// 获取转诊ID
|
||||
this.transferId = parseInt(options.transfer_id || 0)
|
||||
@@ -201,10 +289,11 @@ export default {
|
||||
return
|
||||
}
|
||||
|
||||
// 加载转诊详情、问题列表和助理配置
|
||||
// 加载转诊详情、问题列表、助理配置和协议信息
|
||||
this.loadTransferDetail()
|
||||
this.loadQuestions()
|
||||
this.loadAssistantConfig()
|
||||
this.loadAgreementInfo()
|
||||
},
|
||||
methods: {
|
||||
// 加载转诊详情(获取药品信息)
|
||||
@@ -330,7 +419,7 @@ export default {
|
||||
// 显示第一个问题
|
||||
this.currentQuestionIndex = 0
|
||||
this.$nextTick(() => {
|
||||
this.scrollIntoView = 'bottom-anchor'
|
||||
this.scrollToBottom()
|
||||
})
|
||||
}, this.replyDelay * 1000) // 延迟时间(毫秒)
|
||||
}
|
||||
@@ -357,30 +446,24 @@ export default {
|
||||
// 保存答案
|
||||
this.userAnswers.push(answer)
|
||||
|
||||
// 立即滚动到底部,显示答案
|
||||
this.$nextTick(() => {
|
||||
this.scrollToBottom()
|
||||
})
|
||||
|
||||
// 如果还有下一个问题,延迟显示下一个问题(模拟真人回复)
|
||||
if (this.currentQuestionIndex < this.questions.length - 1) {
|
||||
// 显示"正在回复"提示
|
||||
this.isTyping = true
|
||||
// 滚动到底部
|
||||
this.$nextTick(() => {
|
||||
this.scrollIntoView = 'bottom-anchor'
|
||||
})
|
||||
|
||||
// 延迟后显示下一个问题
|
||||
setTimeout(() => {
|
||||
this.isTyping = false
|
||||
this.currentQuestionIndex++
|
||||
// 滚动到底部,显示新问题
|
||||
this.$nextTick(() => {
|
||||
this.scrollIntoView = 'bottom-anchor'
|
||||
})
|
||||
// watch会自动触发滚动
|
||||
}, this.replyDelay * 1000) // 延迟时间(毫秒)
|
||||
} else {
|
||||
// 所有问题已回答,提交答案
|
||||
// 先滚动到底部
|
||||
this.$nextTick(() => {
|
||||
this.scrollIntoView = 'bottom-anchor'
|
||||
})
|
||||
// 延迟提交,让用户看到最后的问题和答案
|
||||
setTimeout(() => {
|
||||
this.submitAnswers()
|
||||
@@ -396,10 +479,7 @@ export default {
|
||||
this.isSubmitting = true
|
||||
this.showWaiting = true
|
||||
|
||||
// 滚动到底部
|
||||
this.$nextTick(() => {
|
||||
this.scrollIntoView = 'bottom-anchor'
|
||||
})
|
||||
// 滚动到底部(watch会自动触发)
|
||||
|
||||
try {
|
||||
// 构建答案数组(按照问题的顺序,确保与后端保存顺序一致)
|
||||
@@ -465,6 +545,27 @@ export default {
|
||||
})
|
||||
}
|
||||
},
|
||||
// 获取答案按钮的样式类
|
||||
getAnswerButtonClass(answer, index) {
|
||||
const answers = this.currentQuestionAnswers
|
||||
const totalAnswers = answers.length
|
||||
|
||||
// 如果只有两个答案,使用原来的样式(是/否)
|
||||
if (totalAnswers === 2) {
|
||||
if (answer === '是') {
|
||||
return 'yes-btn'
|
||||
} else if (answer === '否') {
|
||||
return 'no-btn'
|
||||
}
|
||||
}
|
||||
|
||||
// 多个答案时,第一个按钮使用主色调,其他使用次要样式
|
||||
if (index === 0) {
|
||||
return 'yes-btn' // 第一个按钮使用主色调
|
||||
} else {
|
||||
return 'no-btn' // 其他按钮使用次要样式
|
||||
}
|
||||
},
|
||||
// 返回
|
||||
handleBack() {
|
||||
uni.showModal({
|
||||
@@ -477,13 +578,123 @@ export default {
|
||||
}
|
||||
})
|
||||
},
|
||||
// 滚动到底部
|
||||
scrollToBottom() {
|
||||
this.scrollIntoView = 'bottom-anchor'
|
||||
// 延迟一下确保滚动生效
|
||||
setTimeout(() => {
|
||||
this.scrollIntoView = ''
|
||||
}, 100)
|
||||
},
|
||||
// 加载协议信息
|
||||
async loadAgreementInfo() {
|
||||
try {
|
||||
const res = await getTransferConsultationAgreementInfoApi()
|
||||
if (res.data && res.data.code === 0) {
|
||||
const result = res.data.result || {}
|
||||
this.noticeText = result.notice_text || ''
|
||||
this.agreements = result.agreements || []
|
||||
|
||||
// 解析文案,提取协议链接
|
||||
this.parseNoticeText()
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('加载协议信息失败:', error)
|
||||
// 失败时使用默认文案
|
||||
this.noticeText = '根据国家互联网医院管理办法要求,平台仅为复诊患者提供服务。为了保障您的用药安全,购买该药物需要经过医生问诊,请根据真实情况回答,并请仔细阅读《互联网医疗风险告知及知情同意书》,继续咨询即表示您已知悉相关规则与风险并同意相关条款。'
|
||||
this.parseNoticeText()
|
||||
}
|
||||
},
|
||||
// 解析提示文案,将《协议名称》替换为可点击的链接
|
||||
parseNoticeText() {
|
||||
if (!this.noticeText) {
|
||||
this.parsedNoticeParts = []
|
||||
return
|
||||
}
|
||||
|
||||
const parts = []
|
||||
// 使用正则表达式匹配《》中的协议名称
|
||||
const regex = /《([^》]+)》/g
|
||||
let lastIndex = 0
|
||||
let match
|
||||
|
||||
while ((match = regex.exec(this.noticeText)) !== null) {
|
||||
// 添加匹配前的文本
|
||||
if (match.index > lastIndex) {
|
||||
parts.push({
|
||||
type: 'text',
|
||||
text: this.noticeText.substring(lastIndex, match.index)
|
||||
})
|
||||
}
|
||||
|
||||
// 查找匹配的协议
|
||||
const agreementName = match[1]
|
||||
const agreement = this.agreements.find(ag => ag.name === agreementName || ag.name.includes(agreementName))
|
||||
|
||||
if (agreement) {
|
||||
// 找到匹配的协议,添加为可点击的链接
|
||||
parts.push({
|
||||
type: 'agreement',
|
||||
text: agreementName,
|
||||
agreementId: agreement.id
|
||||
})
|
||||
} else {
|
||||
// 没找到匹配的协议,作为普通文本显示
|
||||
parts.push({
|
||||
type: 'text',
|
||||
text: match[0] // 《协议名称》
|
||||
})
|
||||
}
|
||||
|
||||
lastIndex = regex.lastIndex
|
||||
}
|
||||
|
||||
// 添加剩余的文本
|
||||
if (lastIndex < this.noticeText.length) {
|
||||
parts.push({
|
||||
type: 'text',
|
||||
text: this.noticeText.substring(lastIndex)
|
||||
})
|
||||
}
|
||||
|
||||
this.parsedNoticeParts = parts
|
||||
},
|
||||
// 查看协议
|
||||
viewAgreement() {
|
||||
// TODO: 跳转到协议页面
|
||||
uni.showToast({
|
||||
title: '协议页面开发中',
|
||||
icon: 'none'
|
||||
})
|
||||
async viewAgreement(agreementId) {
|
||||
if (!agreementId) {
|
||||
uni.showToast({
|
||||
title: '协议ID不能为空',
|
||||
icon: 'none'
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// 查找协议名称
|
||||
const agreement = this.agreements.find(ag => ag.id === agreementId)
|
||||
this.currentAgreementId = agreementId
|
||||
this.currentAgreementName = agreement ? agreement.name : '协议详情'
|
||||
this.showAgreement = true
|
||||
|
||||
try {
|
||||
const res = await getAgreementDetailApi(agreementId)
|
||||
if (res.data && res.data.code === 0) {
|
||||
const result = res.data.result || {}
|
||||
this.agreementContent = result.content || ''
|
||||
// 如果API返回了协议名称,使用API返回的名称
|
||||
if (result.name) {
|
||||
this.currentAgreementName = result.name
|
||||
}
|
||||
} else {
|
||||
throw new Error(res.data.message || '获取协议详情失败')
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取协议详情失败:', error)
|
||||
uni.showToast({
|
||||
title: error.message || '获取协议详情失败',
|
||||
icon: 'none'
|
||||
})
|
||||
this.showAgreement = false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -545,7 +756,7 @@ export default {
|
||||
|
||||
.notice-text {
|
||||
display: block;
|
||||
margin-bottom: 12rpx;
|
||||
word-break: break-all;
|
||||
}
|
||||
|
||||
.notice-link {
|
||||
@@ -737,4 +948,34 @@ export default {
|
||||
0% { transform: rotate(0deg); }
|
||||
100% { transform: rotate(360deg); }
|
||||
}
|
||||
|
||||
.agreement-popup {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
height: 100%;
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.agreement-header {
|
||||
padding: 30rpx 50rpx 20rpx;
|
||||
border-bottom: 1rpx solid #e5e7eb;
|
||||
background: #fff;
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
.agreement-title {
|
||||
font-size: 32rpx;
|
||||
font-weight: 600;
|
||||
color: #1e293b;
|
||||
line-height: 1.5;
|
||||
}
|
||||
|
||||
.agreement-content {
|
||||
flex: 1;
|
||||
padding: 30rpx 50rpx 40rpx;
|
||||
overflow-y: auto;
|
||||
min-height: 0;
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user