feat: 诊所问题优化

This commit is contained in:
李琦
2026-07-30 07:37:29 +08:00
parent 5629955dd3
commit f9dd9ad3a2
6 changed files with 400 additions and 254 deletions

View File

@@ -115,21 +115,30 @@
v-else-if="showQuestionButtons && currentQuestion && !isSubmitting && !showWaiting && answerMode === 'multi'"
class="action-footer multi-footer safe-area-inset-bottom"
>
<!-- 多选适用症点选 toggling确认后再提交 -->
<!-- 多选适用症点选 toggling确认后再提交其他可填自定义症状 -->
<view class="multi-options">
<button
v-for="(answer, index) in currentQuestionAnswers"
:key="index"
:class="['multi-option-btn', { selected: !!multiSelectedMap[index] }]"
hover-class="none"
:class="['multi-option-btn', { selected: !!multiSelectedFlag[index] }]"
@click="toggleMultiAnswer(index)"
>
{{ answer }}
</button>
</view>
<input
v-if="multiHasOtherSelected"
class="multi-other-input"
v-model="multiOtherText"
placeholder="请填写具体症状"
:maxlength="100"
/>
<!-- 不用原生 disabled避免微信小程序卡死无法点击未达标时 toast -->
<button
class="action-btn yes-btn confirm-btn"
:disabled="multiSelectedCount === 0"
:class="{ disabled: multiSelectedCount === 0 }"
hover-class="none"
:class="{ disabled: !multiConfirmEnabled }"
@click="confirmMultiAnswer"
>
确认
@@ -142,8 +151,9 @@
<button
v-for="(answer, index) in currentQuestionAnswers"
:key="index"
hover-class="none"
:class="['action-btn', answerButtonClass(answer, index)]"
@click="$emit('answer', answer)"
@click="emitSingleAnswer(answer)"
>
{{ answer }}
</button>
@@ -259,17 +269,25 @@ export default {
},
data() {
return {
/** 多选模式下已选中的选项下标映射,避免模板里写 indexOf */
multiSelectedMap: {},
/** 多选已选下标列表(整表替换,微信小程序下比 object+$set 更稳) */
multiSelectedIndexes: [],
/** 选「其他」时的自定义症状文案 */
multiOtherText: '',
/** 防双击emit 后立刻锁,题目切换解锁 */
answeringLock: false,
};
},
watch: {
// 题目切换时清空多选,避免上一题选中态残留
currentQuestion() {
this.multiSelectedMap = {};
this.multiSelectedIndexes = [];
this.multiOtherText = '';
this.answeringLock = false;
},
answerMode() {
this.multiSelectedMap = {};
this.multiSelectedIndexes = [];
this.multiOtherText = '';
this.answeringLock = false;
},
},
computed: {
@@ -281,8 +299,37 @@ export default {
this.$emit('update:showAgreement', val)
},
},
/** 供模板判断选中,避免 :class 里写 indexOf */
multiSelectedFlag() {
const map = {};
const list = this.multiSelectedIndexes || [];
for (let i = 0; i < list.length; i++) {
map[list[i]] = true;
}
return map;
},
multiSelectedCount() {
return Object.keys(this.multiSelectedMap).filter((k) => this.multiSelectedMap[k]).length;
return (this.multiSelectedIndexes || []).length;
},
/** 是否选中了「其他」选项 */
multiHasOtherSelected() {
const answers = this.currentQuestionAnswers || [];
const list = this.multiSelectedIndexes || [];
for (let i = 0; i < list.length; i++) {
const idx = list[i];
if (answers[idx] === '其他') {
return true;
}
}
return false;
},
/** 多选确认是否可提交:至少选一项;选了其他则自定义文案非空 */
multiConfirmEnabled() {
if (this.multiSelectedCount === 0) return false;
if (this.multiHasOtherSelected) {
return !!(this.multiOtherText && String(this.multiOtherText).trim());
}
return true;
},
},
methods: {
@@ -306,27 +353,54 @@ export default {
if (index === 0) return 'yes-btn'
return 'no-btn'
},
/** 单选:点一下即提交,防双击重复 emit */
emitSingleAnswer(answer) {
if (this.answeringLock || this.isSubmitting) return;
this.answeringLock = true;
this.$emit('answer', answer);
},
/** 多选:切换某个适用症的选中状态 */
toggleMultiAnswer(index) {
if (this.multiSelectedMap[index]) {
this.$delete(this.multiSelectedMap, index);
if (this.answeringLock || this.isSubmitting) return;
const list = this.multiSelectedIndexes || [];
const pos = list.indexOf(index);
if (pos === -1) {
this.multiSelectedIndexes = list.concat([index]);
} else {
this.$set(this.multiSelectedMap, index, true);
this.multiSelectedIndexes = list.filter((i) => i !== index);
if (this.currentQuestionAnswers[index] === '其他') {
this.multiOtherText = '';
}
}
},
/** 多选确认:把选中标签用顿号拼接后交给父组件 */
/**
* 多选确认:选中标签用顿号拼接;「其他」替换为自定义填写内容
*/
confirmMultiAnswer() {
const indexes = Object.keys(this.multiSelectedMap)
.filter((k) => this.multiSelectedMap[k])
.map((k) => Number(k))
.sort((a, b) => a - b);
if (this.answeringLock || this.isSubmitting) return;
if (!this.multiConfirmEnabled) {
if (this.multiSelectedCount === 0) {
uni.showToast({ title: '请至少选择一项', icon: 'none' });
} else if (this.multiHasOtherSelected) {
uni.showToast({ title: '请填写具体症状', icon: 'none' });
}
return;
}
const indexes = (this.multiSelectedIndexes || []).slice().sort((a, b) => a - b);
if (!indexes.length) return;
const otherText = (this.multiOtherText || '').trim();
const labels = indexes
.map((i) => this.currentQuestionAnswers[i])
.map((i) => {
const label = this.currentQuestionAnswers[i];
if (label === '其他') return otherText;
return label;
})
.filter(Boolean);
if (!labels.length) return;
this.answeringLock = true;
this.$emit('answer', labels.join('、'));
this.multiSelectedMap = {};
this.multiSelectedIndexes = [];
this.multiOtherText = '';
},
},
}
@@ -531,19 +605,22 @@ export default {
font-weight: 500;
border: none;
white-space: normal;
/* 覆盖微信 button 默认白底/按下灰底,避免按钮组背景错乱 */
background-color: #0a84ff !important;
color: #fff !important;
&::after {
border: none;
}
&.yes-btn {
background: #0a84ff;
color: #fff;
background-color: #0a84ff !important;
color: #fff !important;
}
&.no-btn {
background: #fff;
color: #333;
background-color: #fff !important;
color: #333 !important;
border: 2rpx solid #ddd;
}
@@ -563,6 +640,18 @@ export default {
gap: 16rpx;
}
.multi-other-input {
width: 100%;
box-sizing: border-box;
padding: 16rpx 24rpx;
min-height: 72rpx;
border-radius: 12rpx;
background: #f8fafc;
font-size: 28rpx;
color: #333;
border: 1rpx solid #e2e8f0;
}
.multi-option-btn {
min-width: 140rpx;
padding: 0 28rpx;
@@ -570,8 +659,8 @@ export default {
line-height: 64rpx;
border-radius: 32rpx;
font-size: 26rpx;
background: rgba(10, 132, 255, 0.08);
color: #475569;
background-color: rgba(10, 132, 255, 0.08) !important;
color: #475569 !important;
border: 2rpx solid transparent;
&::after {
@@ -579,14 +668,16 @@ export default {
}
&.selected {
background: #0a84ff;
color: #fff;
background-color: #0a84ff !important;
color: #fff !important;
}
}
.confirm-btn {
width: 100%;
flex: none;
background-color: #0a84ff !important;
color: #fff !important;
}
}
}

View File

@@ -146,6 +146,18 @@
<text class="block-label">主诉</text>
<text class="block-content">{{ parsedContent.chief_complaint }}</text>
</view>
<view class="text-block" v-if="parsedContent.illnessInfo">
<text class="block-label">适用症/症状</text>
<text class="block-content">{{ parsedContent.illnessInfo }}</text>
</view>
<view class="info-row" v-if="parsedContent.illnessInfo">
<text class="label">是否就诊过</text>
<text class="value">{{ parsedContent.has_visited === 1 || parsedContent.has_visited === '1' ? '是' : '否' }}</text>
</view>
<view class="info-row" v-if="parsedContent.illnessInfo">
<text class="label">是否使用过药品</text>
<text class="value">{{ parsedContent.has_used_drug === 1 || parsedContent.has_used_drug === '1' ? '是' : '否' }}</text>
</view>
<view v-if="registerCardDrugRows.length" class="drug-list-block">
<text class="block-label">所选药品</text>
<view v-for="row in registerCardDrugRows" :key="row._wxKey" class="info-row drug-spec-row">
@@ -184,6 +196,13 @@
<text class="label">您的选择</text>
<text class="value">{{ followUpAnsweredLabel }}</text>
</view>
<view
v-if="parsedContent.has_visited === 1 || parsedContent.has_visited === '1' || parsedContent.has_visited === 0 || parsedContent.has_visited === '0'"
class="info-row"
>
<text class="label">是否就诊过</text>
<text class="value">{{ parsedContent.has_visited === 1 || parsedContent.has_visited === '1' ? '是' : '否' }}</text>
</view>
<view class="text-block" v-if="parsedContent.illness_info">
<text class="block-label">适用症/症状</text>
<text class="block-content">{{ parsedContent.illness_info }}</text>
@@ -250,7 +269,7 @@
<view class="title-group"><u-icon name="checkmark-circle-fill" size="16" color="#059669"></u-icon><text class="card-title">问诊结束</text></view>
</view>
<view class="card-body">
<text class="desc-text">{{ parsedContent.reason || '本次服务已完成' }}</text>
<text class="desc-text">{{ parsedContent.end_reason || parsedContent.reason || parsedContent.message || '本次服务已完成' }}</text>
</view>
</block>

View File

@@ -1,7 +1,8 @@
<template>
<view v-if="mode === 'clinic_post_register'">
<!-- 诊所挂号后确认 / 药店购药全屏助手壳与诊所同 UI数量在入口页已选 -->
<view v-if="mode === 'clinic_post_register' || mode === 'pharmacy'">
<AssistantConsultationShell
header-title="复诊用药确认"
:header-title="assistantHeaderTitle"
:show-notice="showPostRegisterNotice"
:parsed-notice-parts="parsedPostRegisterNoticeParts"
:show-agreement.sync="showPostRegisterAgreement"
@@ -25,7 +26,6 @@
@answer="onPostRegisterShellAnswer"
@view-agreement="viewPostRegisterAgreement"
>
<!-- 有补充底部输入框填完再转接医生 -->
<view slot="footer" class="supplement-footer safe-area-inset-bottom">
<textarea
class="supplement-input"
@@ -36,7 +36,8 @@
/>
<button
class="supplement-submit"
:disabled="postRegisterSubmitting || !postRegisterSupplementCanSubmit"
hover-class="none"
:class="{ disabled: postRegisterSubmitting || !postRegisterSupplementCanSubmit }"
@click="onPostRegisterSupplementSubmit"
>
提交补充
@@ -46,65 +47,11 @@
<u-toast ref="uToast" />
</view>
<!-- 诊所复诊聊天内用药确认表单保留 -->
<view v-else class="page">
<view class="page-inner safe-area-bottom">
<view class="page-title">{{ pageTitle }}</view>
<!-- 药店商品摘要 -->
<block v-if="mode === 'pharmacy'">
<view class="product-info-row">
<image class="p-img" :src="productDetail.image" mode="aspectFill"></image>
<view class="p-info">
<view class="p-name">{{ productDetail.drug_name }}</view>
<view class="p-spec">{{ productDetail.specification || '暂无规格' }}</view>
<view class="p-price">¥{{ productPrice }}</view>
</view>
</view>
<view class="quantity-row">
<text class="label">购买数量</text>
<u-number-box v-model="prescriptionQuantity" :min="1" :max="999" integer :step="1"></u-number-box>
</view>
<u-form :model="prescriptionFormData" ref="prescriptionForm">
<u-form-item label="是否就诊过" prop="hasVisited" label-width="200">
<u-radio-group v-model="prescriptionFormData.hasVisited">
<u-radio name="1" active-color="#2B85E4" style="margin-right: 40rpx;"></u-radio>
<u-radio name="0" active-color="#2B85E4"></u-radio>
</u-radio-group>
</u-form-item>
<u-form-item label="是否用过此药" prop="hasUsedDrug" label-width="200">
<u-radio-group v-model="prescriptionFormData.hasUsedDrug">
<u-radio name="1" active-color="#2B85E4" style="margin-right: 40rpx;"></u-radio>
<u-radio name="0" active-color="#2B85E4"></u-radio>
</u-radio-group>
</u-form-item>
<view class="illness-section">
<view class="illness-label">症状</view>
<view class="illness-tags">
<u-tag
v-for="(item, index) in illnessOptions"
:key="index"
:text="item.label"
mode="dark"
:bg-color="selectedIllnessIndexes.includes(index) ? '#2B85E4' : 'rgba(43,133,228,0.08)'"
:color="selectedIllnessIndexes.includes(index) ? '#FFFFFF' : '#475569'"
@tap="toggleIllnessTag(index)"
/>
</view>
<u-input
v-if="showIllnessOtherInput"
v-model="illnessOtherText"
placeholder="请填写具体症状"
:border="true"
@input="syncIllnessInfo"
/>
</view>
</u-form>
</block>
<!-- 诊所复诊用药确认仅聊天内入口 -->
<block v-else-if="mode === 'clinic_followup'">
<block v-if="mode === 'clinic_followup'">
<view v-if="clinicQuestion" class="clinic-question">{{ clinicQuestion }}</view>
<view v-if="clinicDrugs.length" class="clinic-drugs">
<view class="sub-title">涉及药品</view>
@@ -122,13 +69,11 @@
</u-form-item>
</u-form>
</block>
<view class="popup-actions">
<button class="btn-cancel" @click="goBack">取消</button>
<button class="btn-confirm" @click="onSubmit">{{ submitButtonText }}</button>
</view>
</view>
<u-toast ref="uToast" />
</view>
</template>
@@ -174,13 +119,10 @@ export default {
},
prescriptionQuantity: 1,
prescriptionFormData: {
hasVisited: '1',
hasUsedDrug: '1',
hasVisited: '',
hasUsedDrug: '',
illnessInfo: '',
},
illnessOptions: [],
selectedIllnessIndexes: [],
illnessOtherText: '',
delegateStoreId: '',
doctorIdFromQuery: '',
submitted: false,
@@ -232,7 +174,15 @@ export default {
submitButtonText() {
return '提交';
},
assistantHeaderTitle() {
return this.mode === 'pharmacy' ? '购药用药确认' : '复诊用药确认';
},
postRegisterPrescriptionInfo() {
if (this.mode === 'pharmacy') {
const name = this.productDetail.drug_name || '药品';
const qty = this.prescriptionQuantity != null ? this.prescriptionQuantity : 1;
return `${name}×${qty}`;
}
if (!this.clinicDrugs || !this.clinicDrugs.length) return '相关药品';
return this.clinicDrugs
.map((row) => `${row.name || '药品'}×${row.quantity != null ? row.quantity : 1}`)
@@ -263,7 +213,6 @@ export default {
});
}
}
// 有补充输入提交后追加一条补充内容气泡
if (this.postRegisterFinalSupplement) {
messages.push({
type: 'answer',
@@ -293,12 +242,10 @@ export default {
if (!q || !Array.isArray(q.answers)) return ['是', '否'];
return q.answers;
},
/** 适用症题用 multi其余单选 */
postRegisterAnswerMode() {
const q = this.postRegisterCurrentQuestion;
return q && q.type === 'symptom' ? 'multi' : 'single';
},
/** 补充文案是否可提交(避免模板里调用 trim */
postRegisterSupplementCanSubmit() {
return !!(this.postRegisterSupplementText && String(this.postRegisterSupplementText).trim());
},
@@ -312,11 +259,6 @@ export default {
}
return parseFloat(this.productDetail.price || 0);
},
showIllnessOtherInput() {
return this.selectedIllnessIndexes.some(
(idx) => this.illnessOptions[idx] && this.illnessOptions[idx].value === '__other__',
);
},
},
onLoad(options) {
if (options.mode === 'clinic_post_register') {
@@ -395,14 +337,29 @@ export default {
}
if (options.price) this.productDetail.price = parseFloat(options.price);
if (options.type) this.productDetail.type = parseInt(options.type, 10) || 2;
// 预定数量在商品页已选,经 query 传入
if (options.quantity) {
const q = parseInt(options.quantity, 10);
if (!Number.isNaN(q) && q >= 1) {
this.prescriptionQuantity = Math.min(999, q);
}
}
if (this.productDetail.id) {
this.getDetail();
this.loadPostRegisterAssistantConfig();
this.loadPostRegisterAgreementInfo();
this.getDetail().then(() => {
this.buildPharmacyQuestionList();
this.$nextTick(() => this.beginPostRegisterAssistantFlow());
});
} else {
uni.showToast({ title: '缺少商品信息', icon: 'none' });
}
},
onBackPress() {
if (this.mode === 'clinic_post_register' && this.showPostRegisterAgreement) {
if (
(this.mode === 'clinic_post_register' || this.mode === 'pharmacy') &&
this.showPostRegisterAgreement
) {
this.showPostRegisterAgreement = false;
return true;
}
@@ -417,10 +374,16 @@ export default {
},
/**
* 构建诊所复诊助手题目:
* 1) 是否用过药 2) 每个有适用症的药一题 3) 是否还需补充
* 就诊过 → 用过药 → 适用症 → 是否还需补充(与药店对齐)
*/
buildPostRegisterQuestionList() {
const list = [
{
id: 'clinic-post-reg-visited',
type: 'visited',
question: '您是否曾在线下就诊过?',
answers: ['是', '否'],
},
{
id: 'clinic-post-reg-used',
type: 'used_drug',
@@ -430,14 +393,19 @@ export default {
];
(this.clinicDrugs || []).forEach((drug, idx) => {
const answers = this.normalizeDrugIndicationLabels(drug);
if (!answers.length) return;
if (!answers.length) {
// 无库内适用症时仍给「其他」,避免无法描述症状
answers.push('其他');
} else if (answers.indexOf('其他') === -1) {
answers.push('其他');
}
const name = drug.name || drug.drug_name || '药品';
list.push({
id: `clinic-post-reg-symptom-${drug.drug_id || idx}`,
type: 'symptom',
drug_id: Number(drug.drug_id || drug.id) || 0,
drug_name: name,
question: `请选择「${name}」本次复诊的适用症`,
question: '请问您是因为什么疾病使用该药品呢?',
answers,
});
});
@@ -449,6 +417,49 @@ export default {
});
this.postRegisterQuestionList = list;
},
/**
* 构建药店购药助手题目:
* 就诊过 → 用过药 → 适用症多选 → 是否还需补充
*/
buildPharmacyQuestionList() {
const name = this.productDetail.drug_name || '药品';
const list = [
{
id: 'pharmacy-visited',
type: 'visited',
question: '您是否曾在线下就诊过?',
answers: ['是', '否'],
},
{
id: 'pharmacy-used',
type: 'used_drug',
question: '您是否曾使用过此类药品?',
answers: ['是', '否'],
},
];
const answers = this.normalizeDrugIndicationLabels({
indications: this.productDetail.indications,
indications_array: this.productDetail.indications_array,
});
if (answers.indexOf('其他') === -1) {
answers.push('其他');
}
list.push({
id: 'pharmacy-symptom',
type: 'symptom',
drug_id: Number(this.productDetail.id) || 0,
drug_name: name,
question: '请问您是因为什么疾病使用该药品呢?',
answers,
});
list.push({
id: 'pharmacy-confirm',
type: 'confirm',
question: POST_REGISTER_CONFIRM_QUESTION,
answers: [POST_REGISTER_CONFIRM_NO_MORE, POST_REGISTER_CONFIRM_HAS_MORE],
});
this.postRegisterQuestionList = list;
},
/**
* 若选药 payload 未带适用症,用门店西药列表接口按 id 回填
* 原因:直接读 yii_drug.indications避开商品详情对处方药清空适用症
@@ -514,6 +525,13 @@ export default {
this.postRegisterFinalSupplement = '';
this.postRegisterWaiting = false;
this.postRegisterQuestionIndex = -1;
this.submitted = false;
// 诊所/药店开场都清空就诊与用药答案,避免上一单残留
this.prescriptionFormData.hasVisited = '';
this.prescriptionFormData.hasUsedDrug = '';
if (this.mode === 'pharmacy') {
this.prescriptionFormData.illnessInfo = '';
}
this.postRegisterIsTyping = true;
const delayMs = Math.max(200, (this.postRegisterReplyDelay || 0.65) * 1000);
setTimeout(() => {
@@ -540,20 +558,59 @@ export default {
this.$nextTick(() => this.postRegisterScrollToBottom());
return;
}
// 全部答完 → 提交转接
this.$nextTick(() => this.postRegisterScrollToBottom());
setTimeout(() => {
this.submitClinicPostRegister();
this.finishAssistantQuestionFlow();
}, 500);
}, typingMs);
},
/** 助手题全部答完:诊所写回挂号信息,药店创建就诊信息并去挂号 */
finishAssistantQuestionFlow() {
// 防双击:已提交或提交中不再进提交流程
if (this.submitted || this.postRegisterStep === 'submitting') return;
if (this.mode === 'pharmacy') {
this.applyPharmacyAnswersToForm();
this.submitPharmacy();
return;
}
this.submitClinicPostRegister();
},
/** 把助手答案落到 pharmacy 提交用的 form 字段 */
applyPharmacyAnswersToForm() {
const illness = this.buildPostRegisterIllnessInfo();
const extra = (this.postRegisterFinalSupplement || '').trim();
let illnessInfo = illness;
if (extra) {
illnessInfo = illnessInfo ? `${illnessInfo};补充:${extra}` : `补充:${extra}`;
}
this.prescriptionFormData.illnessInfo = illnessInfo;
if (!this.prescriptionFormData.hasVisited) {
this.prescriptionFormData.hasVisited = '0';
}
if (!this.prescriptionFormData.hasUsedDrug) {
this.prescriptionFormData.hasUsedDrug =
this.clinicForm.hasUsedDrug === '1' || this.clinicForm.hasUsedDrug === '0'
? this.clinicForm.hasUsedDrug
: '0';
}
},
onPostRegisterShellAnswer(answer) {
// 仅允许在等待答题态处理,避免双击推进两题
if (this.postRegisterStep !== 'await_answer' || this.submitted) return;
const q = this.postRegisterCurrentQuestion;
if (!q) return;
// 立刻离开 await_answer阻断同题二次点击
this.postRegisterStep = 'typing_after';
this.postRegisterUserAnswers = this.postRegisterUserAnswers.concat([answer]);
if (q.type === 'visited') {
this.prescriptionFormData.hasVisited = answer === '是' ? '1' : '0';
this.advancePostRegisterAfterAnswer();
return;
}
if (q.type === 'used_drug') {
this.clinicForm.hasUsedDrug = answer === '是' ? '1' : '0';
const val = answer === '是' ? '1' : '0';
this.clinicForm.hasUsedDrug = val;
this.prescriptionFormData.hasUsedDrug = val;
this.advancePostRegisterAfterAnswer();
return;
}
@@ -570,7 +627,6 @@ export default {
}
if (q.type === 'confirm') {
if (answer === POST_REGISTER_CONFIRM_HAS_MORE) {
// 进入补充输入,不立刻提交
this.postRegisterStep = 'await_supplement';
this.postRegisterSupplementText = '';
this.$nextTick(() => this.postRegisterScrollToBottom());
@@ -597,7 +653,7 @@ export default {
this.postRegisterIsTyping = false;
this.$nextTick(() => this.postRegisterScrollToBottom());
setTimeout(() => {
this.submitClinicPostRegister();
this.finishAssistantQuestionFlow();
}, 500);
}, typingMs);
},
@@ -720,6 +776,18 @@ export default {
this.showPostRegisterAgreement = false;
return;
}
if (this.mode === 'pharmacy') {
uni.showModal({
title: '提示',
content: '确定返回吗?未完成确认将无法继续购药。',
success: (res) => {
if (res.confirm) {
uni.navigateBack({ delta: 1 });
}
},
});
return;
}
uni.showModal({
title: '提示',
content: '确定返回首页吗?未完成确认将无法同步给医生。',
@@ -737,44 +805,18 @@ export default {
}
uni.navigateBack({ delta: 1 });
},
/** 拉取商品详情并回填适用症,供药店助手出题 */
getDetail() {
getStoreDrugDetailApi({ id: this.productDetail.id }).then((res) => {
return getStoreDrugDetailApi({ id: this.productDetail.id })
.then((res) => {
if (res.data.code === 0) {
const data = res.data.result;
Object.assign(this.productDetail, data);
const options = (data.indications_array || []).map((item) => ({
value: item.label || item.value,
label: item.label || item.value,
}));
options.push({ value: '__other__', label: '其他' });
this.illnessOptions = options;
}
});
},
toggleIllnessTag(index) {
const pos = this.selectedIllnessIndexes.indexOf(index);
if (pos === -1) {
this.selectedIllnessIndexes.push(index);
} else {
this.selectedIllnessIndexes.splice(pos, 1);
if (this.illnessOptions[index] && this.illnessOptions[index].value === '__other__') {
this.illnessOtherText = '';
}
}
this.syncIllnessInfo();
},
syncIllnessInfo() {
const parts = this.selectedIllnessIndexes
.map((idx) => {
const opt = this.illnessOptions[idx];
if (!opt) return '';
if (opt.value === '__other__') {
return (this.illnessOtherText || '').trim();
}
return opt.label || opt.value;
})
.filter(Boolean);
this.prescriptionFormData.illnessInfo = parts.join('、');
.catch((e) => {
console.warn('getDetail', e);
});
},
async getDefaultDoctorId() {
try {
@@ -795,8 +837,6 @@ export default {
onSubmit() {
if (this.mode === 'clinic_followup') {
this.submitClinic();
} else {
this.submitPharmacy();
}
},
submitClinicPostRegister() {
@@ -804,6 +844,11 @@ export default {
uni.showToast({ title: '参数缺失', icon: 'none' });
return;
}
const hasVisited = this.prescriptionFormData.hasVisited;
if (hasVisited !== '0' && hasVisited !== '1') {
uni.showToast({ title: '请完成全部问题', icon: 'none' });
return;
}
if (this.clinicForm.hasUsedDrug !== '0' && this.clinicForm.hasUsedDrug !== '1') {
uni.showToast({ title: '请先选择是否用过药', icon: 'none' });
return;
@@ -821,6 +866,7 @@ export default {
confirmFollowUpDrugUseApi({
register_info_id: this.registerInfoId,
has_used_drug: val,
has_visited: hasVisited,
illnessInfo,
supplement,
})
@@ -844,6 +890,7 @@ export default {
answerStatus,
illnessInfo,
supplement,
hasVisited,
});
markFollowUpAssistSent(this.postRegisterRegisterId);
}
@@ -915,26 +962,25 @@ export default {
},
async submitPharmacy() {
if (!this.prescriptionFormData.hasVisited || !this.prescriptionFormData.hasUsedDrug) {
uni.showToast({ title: '请填写完整信息', icon: 'none' });
uni.showToast({ title: '请完成全部问题', icon: 'none' });
this.resetPostRegisterAfterSubmitFailure();
return;
}
if (!this.selectedIllnessIndexes.length) {
uni.showToast({ title: '请选择症状', icon: 'none' });
return;
}
if (this.showIllnessOtherInput && !(this.illnessOtherText || '').trim()) {
uni.showToast({ title: '请填写具体症状', icon: 'none' });
return;
}
this.syncIllnessInfo();
if (!this.prescriptionFormData.illnessInfo) {
uni.showToast({ title: '请选择症状', icon: 'none' });
// 有适用症题却未选时拦截(跳过无适用症药品)
const hasSymptomQ = (this.postRegisterQuestionList || []).some((q) => q.type === 'symptom');
if (hasSymptomQ && !this.buildPostRegisterIllnessInfo()) {
uni.showToast({ title: '请选择适用症', icon: 'none' });
this.resetPostRegisterAfterSubmitFailure();
return;
}
if (this.submitted) return;
const formData = { ...this.prescriptionFormData };
const quantity = this.prescriptionQuantity;
this.submitted = true;
this.postRegisterSubmitting = true;
this.postRegisterWaiting = true;
this.postRegisterStep = 'submitting';
this.$nextTick(() => this.postRegisterScrollToBottom());
try {
const storeId = uni.getStorageSync('store_id');
let delegateStoreId = this.delegateStoreId;
@@ -947,7 +993,7 @@ export default {
icon: 'none',
duration: 2000,
});
this.submitted = false;
this.resetPostRegisterAfterSubmitFailure();
return;
}
delegateStoreId = configRes.data.result.delegate_store_id || storeId;
@@ -958,7 +1004,7 @@ export default {
}
if (!doctorId) {
uni.showToast({ title: '该门店暂不支持在线复诊', icon: 'none', duration: 2000 });
this.submitted = false;
this.resetPostRegisterAfterSubmitFailure();
return;
}
const createRes = await request('/xkApi/order/create-register-info', {
@@ -995,7 +1041,10 @@ export default {
} catch (error) {
console.error('提交处方药信息失败:', error);
uni.showToast({ title: '提交失败,请重试', icon: 'none' });
this.submitted = false;
this.resetPostRegisterAfterSubmitFailure();
} finally {
this.postRegisterSubmitting = false;
this.postRegisterWaiting = false;
}
},
},
@@ -1018,72 +1067,10 @@ export default {
font-weight: bold;
margin-bottom: 30rpx;
}
.product-info-row {
display: flex;
margin-bottom: 30rpx;
.p-img {
width: 160rpx;
height: 160rpx;
border-radius: 12rpx;
background: #f8f8f8;
margin-right: 20rpx;
}
.p-info {
flex: 1;
display: flex;
flex-direction: column;
justify-content: space-between;
.p-name {
font-size: 30rpx;
font-weight: bold;
color: #333;
}
.p-spec {
font-size: 24rpx;
color: #999;
}
.p-price {
font-size: 36rpx;
color: #ff4d4f;
font-weight: bold;
}
}
}
.quantity-row {
display: flex;
justify-content: space-between;
align-items: center;
padding: 20rpx 0;
border-top: 1rpx solid #f5f5f5;
border-bottom: 1rpx solid #f5f5f5;
margin-bottom: 40rpx;
.label {
font-size: 28rpx;
color: #333;
}
}
.illness-section {
padding: 20rpx 0;
.illness-label {
font-size: 28rpx;
color: #333;
margin-bottom: 16rpx;
}
.illness-tags {
display: flex;
flex-wrap: wrap;
::v-deep .u-tag {
margin: 0 20rpx 16rpx 0;
}
}
}
.popup-actions {
display: flex;
gap: 30rpx;
margin-top: 40rpx;
&.post-register-actions {
flex-direction: column;
}
button {
flex: 1;
height: 80rpx;
@@ -1098,19 +1085,8 @@ export default {
background: #2b85e4;
color: #fff;
}
&.btn-confirm-block {
flex: none;
width: 100%;
}
}
}
.post-register-exit {
text-align: center;
margin-top: 28rpx;
font-size: 26rpx;
color: #909399;
padding: 16rpx 0;
}
.clinic-question {
font-size: 28rpx;
color: #333;
@@ -1171,10 +1147,11 @@ export default {
border-radius: 44rpx;
font-size: 32rpx;
font-weight: 500;
background: #0a84ff;
color: #fff;
background-color: #0a84ff !important;
color: #fff !important;
border: none;
}
.supplement-submit.disabled,
.supplement-submit[disabled] {
opacity: 0.45;
}

View File

@@ -240,6 +240,35 @@
</view>
</u-popup>
<!-- 处方药提交预定先选数量再进助手提问 -->
<u-popup
v-model="showRxReservePopup"
mode="bottom"
border-radius="24"
:closeable="true"
@close="closeRxReservePopup"
>
<view class="popup-wrapper safe-area-bottom">
<view class="popup-header-title">选择预定数量</view>
<view class="product-info-row">
<image class="p-img" :src="productDetail.image" mode="aspectFill"></image>
<view class="p-info">
<view class="p-name">{{ productDetail.drug_name }}</view>
<view class="p-spec">{{ productDetail.specification || '暂无规格' }}</view>
<view class="p-price">¥{{ productPrice }}</view>
</view>
</view>
<view class="quantity-row">
<text class="label">预定数量</text>
<u-number-box v-model="rxReserveQuantity" :min="1" :max="999" integer :step="1"></u-number-box>
</view>
<view class="popup-actions">
<button class="btn-cancel" @click="closeRxReservePopup">取消</button>
<button class="btn-confirm" @click="confirmRxReserve">去填写用药信息</button>
</view>
</view>
</u-popup>
<u-toast ref="uToast" />
</view>
</template>
@@ -297,6 +326,9 @@ export default {
cartQuantity: 1,
showBuyPopup: false,
buyQuantity: 1,
/** 处方药提交预定:数量弹窗 */
showRxReservePopup: false,
rxReserveQuantity: 1,
orderText: '本次商品物流配送,由萧康医药提供服务。',
// 物流仓提示单独挂 data保证详情接口返回后一定能驱动 computed 重算
deliveryWarehouseFlag: 0,
@@ -555,6 +587,20 @@ export default {
},
handleBuyNow() {
if (this.productDetail.is_otc === 0 && this.productDetail.type == 2) {
// 处方药:先选数量,再进助手提问页
this.rxReserveQuantity = 1;
this.showRxReservePopup = true;
} else {
this.showBuyPopup = true;
}
},
closeRxReservePopup() {
this.showRxReservePopup = false;
this.rxReserveQuantity = 1;
},
/** 确认预定数量后跳转药店助手用药确认页 */
confirmRxReserve() {
const qty = Math.min(999, Math.max(1, Number(this.rxReserveQuantity) || 1));
const q = [
`mode=pharmacy`,
`id=${encodeURIComponent(this.productDetail.id)}`,
@@ -564,13 +610,12 @@ export default {
`image=${encodeURIComponent(this.productDetail.image || '')}`,
`price=${encodeURIComponent(String(this.productPrice))}`,
`drug_name=${encodeURIComponent(this.productDetail.drug_name || '')}`,
`quantity=${encodeURIComponent(String(qty))}`,
].join('&');
this.showRxReservePopup = false;
uni.navigateTo({
url: `/subPackages/follow-up/medication-info?${q}`,
});
} else {
this.showBuyPopup = true;
}
},
buyNowConfirm() {
genOrderApi({

View File

@@ -517,6 +517,7 @@
const storeType = uni.getStorageSync('store_type');
if (storeType === '1' || storeType === 1) {
// 药店:患者就诊经历 + 挂号卡(卡内展示适用症等选择)
const patientExperienceData = {
symptom_description: that.rInfo.illness_info || '',
has_visited: that.rInfo.has_visited || '0',
@@ -536,6 +537,16 @@
message_content: JSON.stringify(patientExperienceData),
duration: 0
});
await sendToUserApi({
room_id: roomId,
sender_user_id: `user-${patientId}`,
receiver_user_id: `doctor-${doctorId}`,
message_type: 10,
message_content: JSON.stringify({
id: that.register_id,
}),
duration: 0
});
} else {
await sendToUserApi({
room_id: roomId,

View File

@@ -63,6 +63,7 @@ export async function sendFollowUpDrugAssistantMessage({
answerStatus,
illnessInfo,
supplement,
hasVisited,
}) {
const payload = {
flow: 'follow_up_drug',
@@ -74,6 +75,8 @@ export async function sendFollowUpDrugAssistantMessage({
// 症状摘要给医生聊天侧展示
illness_info: illnessInfo || '',
supplement: supplement || '',
// 是否曾线下就诊(与药店就诊经历字段对齐)
has_visited: hasVisited != null && hasVisited !== '' ? String(hasVisited) : '',
};
await sendToUserApi({
room_id: roomId,