Files
xk-doctor-wx/subPackages/sub_workbench/prescription_v2/index.vue
2026-07-03 12:35:47 +08:00

2660 lines
106 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<view class="safe-area-inset-bottom page-bg">
<!-- #ifdef MP-WEIXIN -->
<page-container
:show="pageBackGuardShow"
:overlay="false"
@leave="onPageBackGuardLeave"
/>
<!-- #endif -->
<!-- 导航栏 -->
<u-navbar class="navbar" :is-back="true" :title="navbarTitle" :custom-back="boundCustomBack" title-color="#000" background="{ background: '#fff' }">
</u-navbar>
<!-- 处方类型Tab切换 -->
<view v-if="!isSalespersonTransferMode" class="tabs flex p-32 flex-ali-center flex-jus-sp shadow-sm" style="overflow-x: auto; position: relative; z-index: 10;">
<u-button
:throttle-time="0"
:plain="true"
:type="activeCategory === category.value ? 'success' : 'default'"
v-for="(category, index) in prescriptionCategories"
:key="category.value"
:custom-style="tabsBtnStyle"
@click="handleSwitchCategory(category.value)"
:hairline="true"
:hoverStayTime="0"
:style="{
display: (index === 0 && identity === 2) || identity === 1 ? 'block' : 'none',
marginLeft: '10rpx',
}">
{{ category.label }}
</u-button>
</view>
<view v-if="!isSalespersonTransferMode && registerModeHint" class="register-mode-hint mx-32 m-t-12">
<text class="register-mode-hint__text">{{ registerModeHint }}</text>
</view>
<view
v-if="!isSalespersonTransferMode && hasSpecialPrescriptionRecord"
class="special-rx-card mx-32 m-t-12"
>
<view class="special-rx-card__inner flex-row flex-ali-center">
<u-image
v-if="specialPrescriptionRecord.special_prescription && specialPrescriptionRecord.special_prescription.cover_image"
width="96rpx"
height="96rpx"
border-radius="12"
:src="specialPrescriptionRecord.special_prescription.cover_image"
/>
<view class="flex-1 m-l-16">
<view class="fs-28 font-bold text-gray">
{{ (specialPrescriptionRecord.special_prescription && specialPrescriptionRecord.special_prescription.name) || '特色方' }}
</view>
<view class="fs-24 color-sub">
剂量{{ specialPrescriptionRecord.dose_count }}
</view>
<view class="">
<u-tag
v-if="Number(specialPrescriptionRecord.status) === 0"
text="待导入"
type="warning"
size="mini"
/>
<u-tag
v-else-if="Number(specialPrescriptionRecord.status) === 1"
text="已应用"
type="success"
size="mini"
/>
<u-tag v-else text="已完成" type="info" size="mini" />
</view>
</view>
</view>
<view class="special-rx-card__action m-t-16">
<u-button
size="default"
type="primary"
:loading="applyingSpecialPrescription"
:custom-style="{ backgroundColor: '#6ACDBB', color: '#fff', border: 'none', width: '100%' }"
@click="handleApplySpecialPrescription"
>
导入
</u-button>
</view>
</view>
<view v-if="!isSalespersonTransferMode && allowInsuranceCategory" class="mx-32 m-t-12 flex flex-ali-center">
<u-button size="mini" :type="feeCategory === 1 ? 'success' : 'default'" @click="feeCategory = 1">自费</u-button>
<u-button size="mini" class="m-l-16" :type="feeCategory === 2 ? 'success' : 'default'" @click="feeCategory = 2">医保</u-button>
</view>
<view v-if="!isSalespersonTransferMode && activeCategory === 1 && hasSalespersonTransfer" class="mx-32 m-t-12">
<u-button size="mini" type="primary" plain @click="openSalespersonTransfer">查看传方</u-button>
</view>
<!-- 诊断输入区域 -->
<view class="top white p-32 flex-col m-t-16 radius-12 mx-32">
<view class="top_title flex-row flex-ali-center">
<image :src="require('@/static/image/js.png')" class="size-32"></image>
<d-text text="临床诊断" className="fs-32 m-l-16 font-bold" color="#333"></d-text>
</view>
<view class="m-t-16 flex flex-wrap">
<view class="m-r-16 m-t-16" v-for="(item, i) in diagnoses" :key="item.id">
<u-tag
shape="circle"
:text="item.name"
bg-color="#E8F6F4"
color="#6ACDBB"
border-color="#E8F6F4"
close-color="#6ACDBB"
:closeable="true"
@close="handleRemoveDiagnosis(i)" />
</view>
</view>
<view class="m-t-24">
<u-input
disabled
placeholder="请选择或输入疾病诊断"
:custom-style="illnessStyle"
@click="handleOpenDiagnosisModal" />
</view>
</view>
<!-- 滚动内容区域 -->
<scroll-view :style="scrollStyle" scroll-y="true">
<!-- 在线复诊 + 中药证候/治法/中医疾病接口 diseases_idmethod_idsyndrome_id PC 在线问诊处方一致 -->
<view v-if="activeCategory === 1 && isOnlineRevisit" class="white p-32 radius-12 mx-32 m-t-24 flex-col">
<view class="top_title flex-row flex-ali-center m-b-16">
<image :src="require('@/static/image/js.png')" class="size-32"></image>
<d-text text="中医辨证" className="fs-32 m-l-16 font-bold" color="#333"></d-text>
</view>
<view class="m-t-8">
<d-text text="中医证候" className="fs-26 color-sub"></d-text>
<u-input
disabled
:value="onlineTcmDiseasesLabel"
placeholder="请点击选择中医证候"
:custom-style="illnessStyle"
class="m-t-8"
@click="handleOpenTcmPicker('diseases')" />
</view>
<view class="m-t-24">
<d-text text="中医治法" className="fs-26 color-sub"></d-text>
<u-input
disabled
:value="onlineTcmMethodLabel"
placeholder="请点击选择中医治法"
:custom-style="illnessStyle"
class="m-t-8"
@click="handleOpenTcmPicker('method')" />
</view>
<view class="m-t-24">
<d-text text="中医疾病" className="fs-26 color-sub"></d-text>
<u-input
disabled
:value="onlineTcmSyndromeLabel"
placeholder="请点击选择中医疾病"
:custom-style="illnessStyle"
class="m-t-8"
@click="handleOpenTcmPicker('syndrome')" />
</view>
</view>
<!-- 西药列表 -->
<view class="mx-32 m-t-24" v-if="activeCategory === 2">
<view class="flex-row flex-jus-sp flex-ali-center m-b-24">
<view class="flex-row flex-ali-center">
<view class="rp-icon">Rp</view>
<text class="fs-28 text-gray m-l-8">西药处方</text>
</view>
<view class="flex-row" style="height: 50rpx;">
<u-button
v-if="!isCommonPrescription && supportsCommonPrescriptionTemplate"
:throttle-time="0"
shape="circle"
size="mini"
:custom-style="{backgroundColor: 'transparent !important',border:'1px solid #6ACDBB',color:'#6ACDBB'}"
plain
@click="handleOpenCommonPrescriptionModal"
:ripple="true">
常用方
</u-button>
<view class="m-l-16">
<u-button
:throttle-time="0"
shape="circle"
size="mini"
:custom-style="{ backgroundColor:'#6ACDBB',color:'#fff', border: 'none' }"
@click="handleOpenWesternMedicineModal">
<u-icon name="plus" size="22" class="m-r-4"></u-icon> 添加药品
</u-button>
</view>
</view>
</view>
<!-- 西药卡片设计 -->
<view class="medicine-card" v-for="(item, index) in currentDrugs" :key="item.index_id">
<view class="flex-row">
<image
v-if="item.image"
:src="item.image"
class="drug-image-modern"
mode="aspectFill"
></image>
<view class="flex-1 m-l-24 flex-col">
<view class="flex-row flex-jus-sp flex-ali-start">
<text class="fs-30 font-bold color-title line-clamp-2 w-70">{{index+1}}. {{item.drug_name}}</text>
<text class="fs-32 font-bold color-price">{{parseFloat(item.price || 0).toFixed(2)}}</text>
</view>
<view class="flex-row flex-jus-sp flex-ali-center m-t-12">
<text class="fs-24 color-sub">规格{{item.specification || '--'}}</text>
<text class="fs-26 color-title font-bold">×{{item.select_number || item.number}}</text>
</view>
<view class="m-t-16 usage-capsule">
<u-icon name="info-circle" size="24" color="#6ACDBB" class="m-r-8"></u-icon>
<text class="fs-24" style="color:#008771">{{formatDrugUsage(item) || '请设置用法用量'}}</text>
</view>
</view>
</view>
<!-- 内敛的卡片操作区 -->
<view class="card-actions flex-row flex-jus-sp flex-ali-center">
<view class="action-btn-text danger" @click="handleRemoveDrug(index)">
<u-icon name="trash" size="28"></u-icon> 删除
</view>
<view class="action-btn-ghost primary" @click="handleEditWesternDrug(item, index)">
调整用量
</view>
</view>
</view>
<view class="white p-32 radius-12 m-t-24 flex-col" v-if="currentDrugs.length > 0">
<view class="flex-jus-sp flex-row flex-ali-center">
<d-text text="医嘱补充" color="#333" className="font-bold"></d-text>
<d-text @click="handleOpenDoctorOrderModal" text="选择常用医嘱" color="#6ACDBB"></d-text>
</view>
<u-input v-model="medicalAdvice" placeholder="请输入医嘱注意事项..." :custom-style="entrustStyle" />
</view>
<view class="flex-row flex-jus-center m-t-64" v-if="currentDrugs.length === 0">
<d-empty @click="handleOpenWesternMedicineModal"></d-empty>
</view>
</view>
<!-- 中药列表 -->
<view class="mx-32 m-t-24" v-if="activeCategory === 1">
<view class="flex-row flex-jus-sp flex-ali-center m-b-24">
<view class="flex-row flex-ali-center">
<view class="rp-icon">Rp</view>
<text class="fs-28 text-gray m-l-8">中药处方</text>
</view>
<view class="flex-row" style="height: 50rpx;">
<u-button
v-if="!isSalespersonTransferMode && !isCommonPrescription && supportsCommonPrescriptionTemplate"
:throttle-time="0"
shape="circle"
size="mini"
:custom-style="{backgroundColor: 'transparent !important',border:'1px solid #6ACDBB',color:'#6ACDBB'}"
plain
@click="handleOpenCommonPrescriptionModal"
:ripple="true">
常用方
</u-button>
<view class="m-l-16">
<u-button
:throttle-time="0"
:custom-style="{ backgroundColor:'#6ACDBB',color:'#fff', border: 'none' }"
shape="circle"
size="mini"
@click="handleOpenChineseMedicineModal">
<u-icon name="plus" size="22" class="m-r-4"></u-icon> 添加药材
</u-button>
</view>
</view>
</view>
<!-- 中药极简高密度标签云设计 -->
<view class="white radius-12 p-32" v-if="currentDrugs.length > 0">
<view class="flex-row flex-jus-sp flex-ali-center m-b-24">
<text class="fs-28 font-bold color-title">药材清单 ({{currentDrugs.length}})</text>
<view class="flex-row">
<text class="fs-26 color-primary m-r-32" @click="handleOpenChineseMedicineModal">修改剂量</text>
<text class="fs-26 color-danger" @click="handleRemoveAllDrugs">清空</text>
</view>
</view>
<view class="tcm-tag-cloud">
<view
class="tcm-tag"
v-for="(it, tcmIndex) in currentDrugs"
:key="tcmTagKey(it, tcmIndex)"
:data-idx="tcmIndex"
@longpress.stop="onTcmTagLongPress">
<text class="tcm-name">{{it.drug_name || it.name}}</text>
<text class="tcm-weight">{{String(it.number)}}{{it.unit ? it.unit.name : 'g'}}</text>
<text
v-if="seeRate == 1 && getChineseItemMargin(it) !== '--'"
class="tcm-margin"
>毛利率{{ getChineseItemMargin(it) }}%</text>
</view>
</view>
</view>
<view class="flex-row flex-jus-center m-t-64" v-else>
<d-empty @click="handleOpenChineseMedicineModal"></d-empty>
</view>
</view>
<!-- 简单产品列表保健食品产品服务包非药品医疗器械 -->
<view class="mx-32 m-t-24" v-if="[3, 5, 6, 7].includes(activeCategory)">
<view class="flex-row flex-jus-sp flex-ali-center m-b-24">
<view class="flex-row flex-ali-center">
<view class="rp-icon">Rp</view>
<text class="fs-28 text-gray m-l-8">产品/器械</text>
</view>
<view class="flex-row m-l-163" style="height: 50rpx;">
<view class="m-l-16">
<u-button
:throttle-time="0"
:custom-style="{ backgroundColor:'#6ACDBB',color:'#fff', border: 'none' }"
shape="circle"
size="mini"
@click="handleOpenSimpleProductModal">
<u-icon name="plus" size="22" class="m-r-4"></u-icon> 添加商品
</u-button>
</view>
</view>
</view>
<!-- 简单产品卡片设计 -->
<view class="medicine-card" v-for="(item, index) in currentDrugs" :key="item.index_id">
<view class="flex-row">
<image
v-if="item.image"
:src="item.image"
class="drug-image-modern"
mode="aspectFill"
></image>
<view class="flex-1 m-l-24 flex-col">
<view class="flex-row flex-jus-sp flex-ali-start">
<text class="fs-30 font-bold color-title line-clamp-2 w-70">{{index+1}}. {{item.drug_name}}</text>
<text class="fs-32 font-bold color-price">{{parseFloat(item.price || 0).toFixed(2)}}</text>
</view>
<view class="flex-row flex-jus-sp flex-ali-center m-t-12">
<text class="fs-24 color-sub">规格{{item.specification || '--'}}</text>
<!-- 精致步进器 -->
<view class="modern-stepper flex-row flex-ali-center">
<view class="stepper-btn" @click.stop="handleDecrementSimpleProduct(index)">-</view>
<text class="stepper-val fs-26">{{item.select_number || item.number}}{{getProductUnit(activeCategory)}}</text>
<view class="stepper-btn" @click.stop="handleIncrementSimpleProduct(index)">+</view>
</view>
</view>
</view>
</view>
<view class="card-actions flex-row flex-jus-end">
<view class="action-btn-text danger" @click="handleRemoveDrug(index)">
<u-icon name="trash" size="28"></u-icon> 删除
</view>
</view>
</view>
<view class="white p-32 radius-12 m-t-24 flex-col" v-if="currentDrugs.length > 0">
<view class="flex-jus-sp flex-row flex-ali-center">
<d-text text="医嘱补充" color="#333" className="font-bold"></d-text>
<d-text @click="handleOpenDoctorOrderModal" text="选择常用医嘱" color="#6ACDBB"></d-text>
</view>
<u-input v-model="medicalAdvice" placeholder="请输入注意事项..." :custom-style="entrustStyle" />
</view>
<view class="flex-row flex-jus-center m-t-64" v-if="currentDrugs.length === 0">
<d-empty @click="handleOpenSimpleProductModal"></d-empty>
</view>
</view>
<!-- 医嘱输入区域中药和简单产品共用 -->
<view class="flex-col white p-32 radius-12 mx-32 m-t-24" v-if="activeCategory === 1 && currentDrugs.length > 0">
<view class="flex-jus-sp flex-row flex-ali-center">
<d-text text="医嘱补充" color="#333" className="font-bold"></d-text>
<d-text @click="handleOpenDoctorOrderModal" text="选择常用医嘱" color="#6ACDBB"></d-text>
</view>
<u-input v-model="medicalAdvice" placeholder="请输入服药注意事项..." :custom-style="entrustStyle" />
</view>
<!-- 中药配置区域 -->
<view class="white radius-12 p-32 mx-32 m-t-24" v-if="activeCategory === 1">
<ChineseMedicineConfig
:config="chineseConfig"
:processRuleList="processRuleList"
:childProcessRuleList="childProcessRuleList"
:processRuleNoteList="processRuleNoteList"
@update:config="handleUpdateChineseConfig"
/>
</view>
<!-- 费用计算区域 -->
<view class="fee-summary white radius-12 p-32 mx-32 m-t-24 m-b-40">
<view v-if="isTransferFeeHidden" class="transfer-fee-hidden-tip flex-col">
<text class="fs-28 color-title font-bold">本店已关闭推广员查看费用</text>
<text class="fs-24 color-sub m-t-8">提交后由接诊医生确认价格</text>
</view>
<template v-else>
<view v-if="!isSalespersonTransferMode" class="flex-row flex-jus-sp flex-ali-center m-b-24">
<text class="fs-28 color-sub">诊疗费</text>
<view class="flex-row flex-ali-center">
<text class="fs-28 m-r-8"></text>
<u-input
v-model="treatmentPrice"
placeholder="0.00"
input-align="right"
:custom-style="priceInputStyle" />
</view>
</view>
<view
v-if="!isSalespersonTransferMode && processingFee > 0"
class="flex-row flex-jus-sp flex-ali-center m-b-24">
<text class="fs-28 color-sub">加工费</text>
<text class="fs-30 color-title font-bold">{{processingFee.toFixed(2)}}</text>
</view>
<view class="flex-row flex-jus-sp flex-ali-center m-b-32">
<view class="flex-col">
<text class="fs-28 color-sub">商品计费</text>
<text
v-if="grossMarginText"
class="fs-24 color-primary m-t-8"
>毛利率 {{ grossMarginText }}%</text>
</view>
<text
v-if="canPreSendPriceAdjust"
class="fs-30 color-title font-bold price-link"
@click="openPreSendOrderPriceAdjust"
>{{totalProductCost.toFixed(2)}}</text>
<text v-else class="fs-30 color-title font-bold">{{totalProductCost.toFixed(2)}}</text>
</view>
<view
v-if="canPreSendPriceAdjust && currentDrugs.length > 0"
class="flex-row flex-jus-sp flex-ali-center m-b-24"
>
<text class="fs-26 color-sub">当前浮动{{ preSendDiscountLabel || '无' }}</text>
<text
v-if="priceDiscount !== 100"
class="fs-26 color-primary"
@click="clearPreSendPriceDiscount"
>清除浮动</text>
</view>
<view
class="total-price-bar flex-row flex-jus-sp flex-ali-center pt-32 border-t-dashed"
v-if="currentDrugs.length > 0">
<text class="fs-30 font-bold color-title">合计</text>
<text class="fs-48 font-bold color-price">{{totalPrice}}</text>
</view>
</template>
</view>
<!-- 提示信息 -->
<view class="ac p-b-40 p-x-32 flex-row flex-jus-center">
<u-icon name="shield-check" color="#B0B6C2" size="28" class="m-r-8"></u-icon>
<text class="fs-24 color-sub">请确认患者已在实体医院就诊并有明确诊断</text>
</view>
</scroll-view>
<!-- 底部操作栏 -->
<view class="bottom flex-jus-sp flex-ali-center safe-area-inset-bottom">
<view v-if="!isSalespersonTransferMode && !isCommonPrescription && supportsCommonPrescriptionTemplate" @click="handleSaveAsCommonPrescription" class="btnText">另存常用方</view>
<u-button
:throttle-time="0"
@click="handleSendPrescription"
shape="circle"
:custom-style="{
backgroundColor: isSubmitting ? '#A5DCD2' : '#6ACDBB',
color: '#fff',
height: '86rpx',
width: (isSalespersonTransferMode || isCommonPrescription || !supportsCommonPrescriptionTemplate) ? '686rpx' : '448rpx'
}"
:disabled="isSubmitting">
{{ isCommonPrescription ? '保存常用方' : (isSalespersonTransferMode ? '提交传方' : '发送处方') }}
</u-button>
</view>
<TraditionalTermPickerModal
v-model="showTcmPicker"
:title="tcmPickerTitle"
:options="tcmPickerList"
:selected-id="tcmPickerCurrentSelectedId"
@confirm="handleTcmPickerConfirm"
/>
<!-- 诊断选择弹窗 -->
<DiagnosisModal
v-model="showDiagnosisModal"
:selectedDiagnosis="diagnosisText"
:salesperson-transfer-mode="isSalespersonTransferMode"
@confirm="handleDiagnosisConfirm"
/>
<!-- 医嘱选择弹窗 -->
<DoctorOrderModal
v-model="showDoctorOrderModal"
:selectedDoctorOrder="medicalAdvice"
@confirm="handleDoctorOrderConfirm"
/>
<!-- 常用方选择弹窗 -->
<CommonPrescriptionModal
v-model="showCommonPrescriptionModal"
:prescriptionType="activeCategory"
@select="handleSelectCommonPrescription"
/>
<!-- 西药选择弹窗 -->
<WesternMedicineModal
v-model="showWesternMedicineModal"
:currentDrugs="currentDrugs"
:initial-search-key="westernModalInitialKeyword"
@select="handleSelectWesternDrug"
/>
<!-- 中药选择弹窗 -->
<ChineseMedicineModal
v-model="showChineseMedicineModal"
:currentDrugs="currentDrugs"
:initial-search-key="chineseModalInitialKeyword"
:register-id="registerId"
:see-rate="seeRate"
:salesperson-transfer-mode="isSalespersonTransferMode"
:show-drug-price="showSalespersonTransferPrice"
@select="handleSelectChineseDrug"
/>
<TransferPatientDrawer
v-model="showTransferPatientDrawer"
:submitting="transferDrawerSubmitting"
ref="transferPatientDrawer"
@confirm="handleTransferPatientConfirm"
/>
<!-- 简单产品选择弹窗 -->
<SimpleProductModal
v-model="showSimpleProductModal"
:productType="activeCategory"
:currentProducts="currentDrugs"
@select="handleSelectSimpleProduct"
/>
<!-- 西药用法用量设置弹窗 -->
<WesternMedicineUsageModal
v-model="showWesternUsageModal"
:drug="editingDrug"
@confirm="handleConfirmWesternUsage"
/>
<!-- 保存常用方弹窗 -->
<u-modal
v-model="showSaveCommonPrescriptionModal"
:show-cancel-button="true"
:mask-close-able="true"
title="保存常用方"
@confirm="handleConfirmSaveCommonPrescription"
@cancel="showSaveCommonPrescriptionModal = false">
<view class="slot-content" style="padding: 20rpx;">
<u-input
v-model="commonPrescriptionName"
placeholder="请输入处方名称"
type="text"
:border="true" />
</view>
</u-modal>
<order-price-percent-adjust-popup
:visible="priceAdjustVisible"
:current-discount="priceDiscount"
:quick-options="priceAdjustQuickOptions"
@close="closePreSendPriceAdjust"
@confirm="onPreSendPriceAdjustConfirm"
/>
</view>
</template>
<script>
// 注意:这部分保持你原有的逻辑代码,不进行任何修改,确保业务完全正常
import {
addWestPrescription,
checkChineseMedicineConflictApi,
getDiseaseList,
getDrugUseList,
getProductListDoctorReception,
getPatientInfoByPatientId,
getPatientItem,
applySpecialPrescriptionApi,
getProcessRuleList,
getCommonPrescriptionListApi,
saveWestCommonPrescriptionApi,
saveChineseCommonPrescriptionApi,
getCurrentStoreTypeApi,
getPriceAdjustConfigApi,
getSalespersonTransferByRegisterApi,
getPrescriptionInfoApi,
getTraditionalChineseMedicineJson
} from '@/api/reception.js';
import { parseTransferPrescriptionContent } from '@/subPackages/sub_online_reception/utils/transferPrescriptionImport.js';
import { getChatRoomByRegisterApi, sendToUserHttpApi } from '@/api/chat.js';
import { PrescriptionStorage } from './utils/prescriptionStorage.js';
import { PrescriptionCalculator } from './utils/prescriptionCalculator.js';
import { PrescriptionValidator } from './utils/prescriptionValidator.js';
import DiagnosisModal from './components/modals/DiagnosisModal.vue';
import DoctorOrderModal from './components/modals/DoctorOrderModal.vue';
import CommonPrescriptionModal from './components/modals/CommonPrescriptionModal.vue';
import WesternMedicineModal from './components/modals/WesternMedicineModal.vue';
import ChineseMedicineModal from './components/modals/ChineseMedicineModal.vue';
import SimpleProductModal from './components/modals/SimpleProductModal.vue';
import WesternMedicineUsageModal from './components/modals/WesternMedicineUsageModal.vue';
import ChineseMedicineConfig from './components/ChineseMedicineConfig.vue';
import TraditionalTermPickerModal from './components/modals/TraditionalTermPickerModal.vue';
import TransferPatientDrawer from './components/modals/TransferPatientDrawer.vue';
import OrderPricePercentAdjustPopup from '@/subPackages/sub_business_shared/components/OrderPricePercentAdjustPopup.vue';
import { applyRatioToDrugs, formatPriceDiscountLabel, normalizeQuickOptions } from '@/subPackages/sub_business_shared/common/pricePercentAdjust.js';
import {
createClinicSalespersonTransferPrescription,
getClinicSalespersonSeePriceConfig,
getClinicSalespersonProcessRuleList,
} from '@/api/clinicSalesperson.js';
import {
pickSpecialPrescriptionRecord,
applySpecialPrescriptionPayload,
} from './utils/specialPrescription.js';
const PENDING_RX_FROM_CHAT = 'xk_pending_rx_from_chat';
const SALESPERSON_TRANSFER_STORAGE_KEY = 'salesperson_transfer';
const SALESPERSON_HOME_URL = '/subPackages/sub_clinic_salesperson/home/index';
/** 与 closeTopOverlayIfAny 一致:任意为 true 时启用微信小程序 page-container 返回拦截 */
const PRESCRIPTION_OVERLAY_KEYS = [
'showWesternUsageModal',
'showTcmPicker',
'showWesternMedicineModal',
'showChineseMedicineModal',
'showSimpleProductModal',
'showCommonPrescriptionModal',
'showDiagnosisModal',
'showDoctorOrderModal',
'showSaveCommonPrescriptionModal',
'showTransferPatientDrawer'
];
export default {
name: 'PrescriptionV2',
components: {
DiagnosisModal,
DoctorOrderModal,
CommonPrescriptionModal,
WesternMedicineModal,
ChineseMedicineModal,
SimpleProductModal,
WesternMedicineUsageModal,
ChineseMedicineConfig,
TraditionalTermPickerModal,
TransferPatientDrawer,
OrderPricePercentAdjustPopup,
},
data() {
return {
registerId: '',
patientId: '',
pendingReusePrescriptionId: '',
patientInfo: null,
prescriptionCategories: [
{ label: '中药', value: 1 },
{ label: '西(中成)药', value: 2 },
{ label: '保健食品', value: 3 },
{ label: '产品服务包', value: 5 },
{ label: '非药品', value: 6 },
{ label: '医疗器械', value: 7 }
],
activeCategory: 2,
currentDrugs: [],
diagnoses: [],
diagnosisText: '',
medicalAdvice: '',
treatmentPrice: '',
chineseConfig: {
ruleType: 1,
packageMethodId: null,
processRuleId: null,
childProcessRuleId: null,
processRuleNoteId: null,
dosage: 7,
dayDosage: 2
},
processRuleList: [],
childProcessRuleList: [],
processRuleNoteList: [],
drugUseList: {
drug_use_num: [],
drug_use_frequency: [],
drug_use_type: [],
drug_unit: [],
drug_time: [],
drug_use_way: []
},
showDiagnosisModal: false,
showDoctorOrderModal: false,
showCommonPrescriptionModal: false,
showWesternMedicineModal: false,
westernModalInitialKeyword: '',
showChineseMedicineModal: false,
chineseModalInitialKeyword: '',
showSimpleProductModal: false,
showWesternUsageModal: false,
showSaveCommonPrescriptionModal: false,
editingDrug: null,
editingDrugIndex: -1,
commonPrescriptionName: '',
isCommonPrescription: false,
_presetActiveCategory: null,
isSubmitting: false,
identity: uni.getStorageSync('role'),
registerStoreInfo: null,
feeCategory: 1,
salespersonTransferPrescriptionId: 0,
hasSalespersonTransfer: false,
/** 是否可查看毛利率 0/1xk-api see_rate */
seeRate: 0,
enableOrderPricePercentAdjust: 0,
priceAdjustVisible: false,
priceDiscount: 100,
priceAdjustQuickOptions: [],
priceAdjustScopeMode: 'sale_only',
sendMode: 0,
selectedStoreId: null,
registerOrderType: null,
specialPrescriptionRecord: null,
applyingSpecialPrescription: false,
pendingImportSpecial: false,
/** 在线复诊中药术语表HTTP 拉取,内存缓存) */
traditionalTcmData: null,
traditionalTcmLoadPromise: null,
showTcmPicker: false,
tcmPickerTitle: '',
tcmPickerField: '',
tcmPickerList: [],
/** 与 PC prescriptionStore 提交字段一致diseases_id←证候(diseases)method_id←治法syndrome_id←中医疾病(syndrome) */
onlineTcmDiseasesId: null,
onlineTcmMethodId: null,
onlineTcmSyndromeId: null,
// UI Styles
tabsBtnStyle: {
padding: '8rpx 32rpx',
height: '60rpx',
fontSize: '28rpx',
borderRadius: '30rpx'
},
illnessStyle: {
fontSize: '28rpx',
backgroundColor: '#F7F8FA',
borderRadius: '16rpx',
height: '76rpx',
padding: '0 32rpx'
},
entrustStyle: {
fontSize: '28rpx',
backgroundColor: '#F7F8FA',
height: '80rpx',
padding: '0 24rpx',
borderRadius: '12rpx',
marginTop: '20rpx'
},
priceInputStyle: {
fontSize: '32rpx',
fontWeight: 'bold',
color: '#333',
width: '160rpx',
height: '60rpx',
backgroundColor: '#F7F8FA',
borderRadius: '8rpx',
padding: '0 16rpx'
},
/** 微信小程序 page-container有抽屉/弹层时为 true拦截侧滑等返回 */
pageBackGuardShow: false,
/** 程序化隐藏 page-container 时抑制 @leave避免误触发页面退出确认 */
_suppressPageBackGuardLeave: false,
/** 推广员传方模式 */
isSalespersonTransferMode: false,
/** 推广员是否可查看售价 0/1门店 salesperson see_price */
salespersonSeePrice: 0,
showTransferPatientDrawer: false,
transferDrawerSubmitting: false,
};
},
computed: {
allowInsuranceCategory() {
return Number(this.registerStoreInfo?.allow_insurance_category ?? 0) === 1;
},
canPreSendPriceAdjust() {
return Number(this.enableOrderPricePercentAdjust) === 1 && this.currentDrugs.length > 0;
},
preSendDiscountLabel() {
return formatPriceDiscountLabel(this.priceDiscount);
},
effectivePriceAdjustStoreId() {
return this.selectedStoreId
|| this.registerStoreInfo?.store_id
|| uni.getStorageSync('store_id')
|| null;
},
totalProductCost() {
if (this.currentDrugs.length === 0) return 0;
if (this.activeCategory === 1) {
return PrescriptionCalculator.calculateChineseMedicineProductPrice(this.currentDrugs, this.chineseConfig.dosage);
} else if (this.activeCategory === 2) {
return PrescriptionCalculator.calculateWesternMedicineProductPrice(this.currentDrugs);
} else if ([3, 5, 6, 7].includes(this.activeCategory)) {
return PrescriptionCalculator.calculateSimpleProductPrice(this.currentDrugs);
}
return 0;
},
processingFee() {
if (this.activeCategory !== 1 || this.chineseConfig.ruleType !== 2) return 0;
const processRule = this.childProcessRuleList.find(item => item.id === this.chineseConfig.childProcessRuleId);
if (!processRule) return 0;
const totalDrugQuantity = this.currentDrugs.reduce((sum, drug) => sum + parseFloat(drug.number || 1), 0);
return PrescriptionCalculator.calculateProcessingFee(processRule, this.chineseConfig.dosage, totalDrugQuantity);
},
totalPrice() {
const total = PrescriptionCalculator.calculateTotalPrice({
category: this.activeCategory,
drugs: this.currentDrugs,
dosage: this.chineseConfig.dosage,
processingFee: this.processingFee,
treatmentPrice: parseFloat(this.treatmentPrice || 0)
});
return total.toFixed(2);
},
grossMarginText() {
if (this.activeCategory !== 1 || Number(this.seeRate) !== 1) {
return '';
}
return PrescriptionCalculator.calculateChineseTotalGrossMarginPercent(
this.currentDrugs,
this.chineseConfig.dosage
);
},
showSalespersonTransferPrice() {
return !this.isSalespersonTransferMode || Number(this.salespersonSeePrice) === 1;
},
isTransferFeeHidden() {
return this.isSalespersonTransferMode && Number(this.salespersonSeePrice) === 0;
},
supportsCommonPrescriptionTemplate() {
return [1, 2].includes(this.activeCategory);
},
isOnlineRevisit() {
const t = Number(this.registerOrderType);
return t === 2 || t === 3;
},
registerModeHint() {
if (this.registerOrderType === null || this.registerOrderType === undefined || this.registerOrderType === '') return '';
return this.isOnlineRevisit ? '当前:在线复诊开方(提交后将向患者发送处方消息)' : '当前:线下开方';
},
hasSpecialPrescriptionRecord() {
return !!this.specialPrescriptionRecord;
},
navbarTitle() {
if (this.isSalespersonTransferMode) {
return '传方';
}
if (this.registerOrderType === null || this.registerOrderType === undefined || this.registerOrderType === '') {
return '开方';
}
return this.isOnlineRevisit ? '开方 · 在线复诊' : '开方 · 线下';
},
hasTransferDraft() {
if (!this.isSalespersonTransferMode) return false;
return this.currentDrugs.length > 0
|| this.diagnoses.length > 0
|| !!(this.medicalAdvice && String(this.medicalAdvice).trim());
},
shouldEnablePageBackGuard() {
return this.hasPrescriptionOverlay;
},
scrollStyle() {
try {
const sys = uni.getSystemInfoSync();
const rpxRatio = sys.windowWidth / 750;
const topRpx = this.isSalespersonTransferMode ? 500 : 600;
const bottomRpx = 200;
const heightPx = sys.windowHeight - topRpx * rpxRatio - bottomRpx * rpxRatio;
return {
height: `${Math.max(Math.floor(heightPx), 200)}px`,
marginBottom: '200rpx',
};
} catch (e) {
const topRpx = this.isSalespersonTransferMode ? 500 : 600;
return {
height: `calc(100vh - ${topRpx}rpx)`,
marginBottom: '200rpx',
};
}
},
onlineTcmDiseasesLabel() {
const data = this.traditionalTcmData;
const id = this.onlineTcmDiseasesId;
if (!data || id == null || id === '') return '';
const row = (data.diseases || []).find((x) => Number(x.id) === Number(id));
return row ? row.name : '';
},
onlineTcmMethodLabel() {
const data = this.traditionalTcmData;
const id = this.onlineTcmMethodId;
if (!data || id == null || id === '') return '';
const row = (data.method || []).find((x) => Number(x.id) === Number(id));
return row ? row.name : '';
},
onlineTcmSyndromeLabel() {
const data = this.traditionalTcmData;
const id = this.onlineTcmSyndromeId;
if (!data || id == null || id === '') return '';
const row = (data.syndrome || []).find((x) => Number(x.id) === Number(id));
return row ? row.name : '';
},
tcmPickerCurrentSelectedId() {
if (this.tcmPickerField === 'diseases') return this.onlineTcmDiseasesId;
if (this.tcmPickerField === 'method') return this.onlineTcmMethodId;
if (this.tcmPickerField === 'syndrome') return this.onlineTcmSyndromeId;
return null;
},
hasPrescriptionOverlay() {
for (let i = 0; i < PRESCRIPTION_OVERLAY_KEYS.length; i++) {
if (this[PRESCRIPTION_OVERLAY_KEYS[i]]) return true;
}
return false;
}
},
watch: {
showWesternMedicineModal(val) {
if (!val) this.westernModalInitialKeyword = '';
},
showChineseMedicineModal(val) {
if (!val) this.chineseModalInitialKeyword = '';
},
// #ifdef MP-WEIXIN
shouldEnablePageBackGuard(val) {
this.setPageBackGuardShow(!!val, { suppressLeave: !val });
this.syncSalespersonTransferUnloadAlert();
},
hasTransferDraft() {
this.syncSalespersonTransferUnloadAlert();
},
// #endif
},
created() {
// u-navbar 在小程序内 bind($parent) 可能拿不到页面实例,用箭头函数固定 this
this.boundCustomBack = () => this.handleCustomBack();
},
onLoad(options) {
this.isSalespersonTransferMode = String(options.salesperson_transfer) === '1';
if (this.isSalespersonTransferMode) {
this.registerId = SALESPERSON_TRANSFER_STORAGE_KEY;
this._presetActiveCategory = 1;
} else {
this.registerId = options.register_id || '';
}
this.patientId = options.patient_id || '';
this.pendingReusePrescriptionId = options.reuse_prescription_id ? String(options.reuse_prescription_id) : '';
this.pendingImportSpecial = String(options.import_special) === '1';
this.isCommonPrescription = String(options.save_common) === '1';
if (!this.isCommonPrescription) {
uni.removeStorageSync('add');
}
if (options.initial_category !== undefined && options.initial_category !== '') {
const n = parseInt(options.initial_category, 10);
if (!Number.isNaN(n)) this._presetActiveCategory = n;
}
this.initializePageData();
},
async onShow() {
this.loadCurrentCategoryDrugs();
if (this.activeCategory === 1 && this.chineseConfig.ruleType === 2) {
await this.hydrateChineseProcessRuleListsFromConfig();
}
if (!this.isSalespersonTransferMode && this.activeCategory === 1) {
await this.checkAndShowTransferTip();
}
},
onUnload() {
if (this.isCommonPrescription) {
uni.removeStorageSync('add');
}
// #ifdef MP-WEIXIN
if (this.isSalespersonTransferMode) {
uni.disableAlertBeforeUnload();
}
// #endif
},
methods: {
// #ifdef MP-WEIXIN
setPageBackGuardShow(show, { suppressLeave = false } = {}) {
if (suppressLeave) this._suppressPageBackGuardLeave = true;
this.pageBackGuardShow = !!show;
},
syncSalespersonTransferUnloadAlert() {
if (!this.isSalespersonTransferMode) return;
if (this.hasTransferDraft && !this.hasPrescriptionOverlay) {
uni.enableAlertBeforeUnload({ message: '有未提交的传方内容,确定退出?' });
} else {
uni.disableAlertBeforeUnload();
}
},
// #endif
async initializePageData() {
try {
this.restoreFromLocalStorage();
if (this.isSalespersonTransferMode) {
await this.loadSalespersonTransferConfig();
await this.loadBasicConfigDataForTransfer();
} else {
await this.loadPatientInfo();
await this.loadRegisterOrderType();
await this.loadBasicConfigData();
await this.loadRegisterStoreInfo();
}
this.loadCurrentCategoryDrugs();
if (this.activeCategory === 1 && this.chineseConfig.ruleType === 2) {
await this.hydrateChineseProcessRuleListsFromConfig();
}
if (!this.isSalespersonTransferMode) {
await this.maybeApplyReusePrescription();
if (this.activeCategory === 1) await this.checkAndShowTransferTip();
this.applyPendingExperienceDrugFromChat();
if (this.pendingImportSpecial && this.hasSpecialPrescriptionRecord) {
await this.handleApplySpecialPrescription();
this.pendingImportSpecial = false;
}
}
// #ifdef MP-WEIXIN
this.$nextTick(() => {
this.setPageBackGuardShow(this.shouldEnablePageBackGuard);
this.syncSalespersonTransferUnloadAlert();
});
// #endif
} catch (error) {
console.error('初始化页面数据失败:', error);
this.$toast('初始化失败,请重试');
}
},
async loadSalespersonTransferConfig() {
try {
const wrap = await getClinicSalespersonSeePriceConfig();
if (wrap && wrap.ok) {
this.salespersonSeePrice = Number(wrap.data?.see_price ?? 0);
}
} catch (error) {
console.error('获取推广员价格配置失败:', error);
}
},
async loadBasicConfigDataForTransfer() {
try {
if (this.activeCategory === 1 && this.processRuleList.length === 0) {
await this.loadProcessRuleList(0, 0);
}
} catch (error) {
console.error('加载基础配置数据失败:', error);
}
},
restoreFromLocalStorage() {
if (this._presetActiveCategory !== null && this._presetActiveCategory !== undefined && !Number.isNaN(this._presetActiveCategory)) {
this.activeCategory = this._presetActiveCategory;
this._presetActiveCategory = null;
return;
}
const savedCategory = PrescriptionStorage.getActiveCategory(this.registerId);
if (savedCategory) {
this.activeCategory = savedCategory;
} else {
const categories = [1, 2, 3, 5, 6, 7];
for (const category of categories) {
const data = PrescriptionStorage.loadPrescriptionData(category, this.registerId);
if (data && data.drugs && data.drugs.length > 0) {
this.activeCategory = category;
break;
}
}
}
},
loadCurrentCategoryDrugs() {
const data = PrescriptionStorage.loadPrescriptionData(this.activeCategory, this.registerId);
if (data) {
this.currentDrugs = data.drugs || [];
this.diagnoses = data.diagnoses || [];
this.diagnosisText = data.diagnosisText || '';
this.medicalAdvice = data.medicalAdvice || '';
this.treatmentPrice = data.treatmentPrice || '';
if (data.chineseConfig) this.chineseConfig = { ...this.chineseConfig, ...data.chineseConfig };
if (this.activeCategory === 1) {
if (data.onlineTcmDiseasesId !== undefined && data.onlineTcmDiseasesId !== null) this.onlineTcmDiseasesId = data.onlineTcmDiseasesId;
else this.onlineTcmDiseasesId = null;
if (data.onlineTcmMethodId !== undefined && data.onlineTcmMethodId !== null) this.onlineTcmMethodId = data.onlineTcmMethodId;
else this.onlineTcmMethodId = null;
if (data.onlineTcmSyndromeId !== undefined && data.onlineTcmSyndromeId !== null) this.onlineTcmSyndromeId = data.onlineTcmSyndromeId;
else this.onlineTcmSyndromeId = null;
} else {
this.onlineTcmDiseasesId = null;
this.onlineTcmMethodId = null;
this.onlineTcmSyndromeId = null;
}
} else {
this.currentDrugs = [];
this.onlineTcmDiseasesId = null;
this.onlineTcmMethodId = null;
this.onlineTcmSyndromeId = null;
}
},
/**
* 从 chineseConfig 中的 ID 拉取委托调剂三级列表(本地缓存只存 ID不存列表
*/
async hydrateChineseProcessRuleListsFromConfig() {
if (this.activeCategory !== 1 || this.chineseConfig.ruleType !== 2) return;
if (!this.processRuleList || this.processRuleList.length === 0) {
await this.loadProcessRuleList(0, 0);
}
if (this.chineseConfig.processRuleId) {
await this.loadProcessRuleList(this.chineseConfig.processRuleId, 0);
}
if (this.chineseConfig.childProcessRuleId) {
await this.loadProcessRuleList(0, this.chineseConfig.childProcessRuleId);
}
},
saveToLocalStorage() {
const data = {
drugs: this.currentDrugs,
diagnoses: this.diagnoses,
diagnosisText: this.diagnosisText,
medicalAdvice: this.medicalAdvice,
treatmentPrice: this.treatmentPrice,
chineseConfig: this.activeCategory === 1 ? this.chineseConfig : null,
onlineTcmDiseasesId: this.activeCategory === 1 ? this.onlineTcmDiseasesId : undefined,
onlineTcmMethodId: this.activeCategory === 1 ? this.onlineTcmMethodId : undefined,
onlineTcmSyndromeId: this.activeCategory === 1 ? this.onlineTcmSyndromeId : undefined
};
PrescriptionStorage.savePrescriptionData(this.activeCategory, this.registerId, data);
PrescriptionStorage.saveActiveCategory(this.activeCategory, this.registerId);
},
async loadPatientInfo() {
if (!this.patientId) return;
try {
const res = await getPatientInfoByPatientId(this.patientId);
const data = res.result || res.data || res;
if (data && data.user_patient) {
this.patientInfo = {
id: data.user_patient.id || this.patientId,
name: data.user_patient.name,
mobile: data.user_patient.mobile,
age: data.user_patient.age,
sex: data.user_patient.sex
};
}
} catch (error) {
console.error('获取患者信息失败:', error);
}
},
async loadRegisterOrderType() {
if (!this.registerId) {
this.registerOrderType = null;
this.specialPrescriptionRecord = null;
return;
}
try {
const res = await getPatientItem(this.registerId);
const data = res.result || res.data || res;
if (data && data.type !== undefined && data.type !== null) {
this.registerOrderType = data.type;
} else {
this.registerOrderType = null;
}
this.specialPrescriptionRecord = pickSpecialPrescriptionRecord(data);
} catch (e) {
console.warn(e);
this.registerOrderType = null;
this.specialPrescriptionRecord = null;
}
},
async loadBasicConfigData() {
try {
const drugUseRes = await getDrugUseList();
if (drugUseRes) {
const inner = drugUseRes.result || drugUseRes.data;
this.drugUseList = inner && (inner.drug_time != null || inner.drug_use_type != null || inner.drug_use_way != null) ? inner : drugUseRes;
}
if (this.activeCategory === 1 && this.processRuleList.length === 0) {
await this.loadProcessRuleList(0, 0);
}
} catch (error) {
console.error('加载基础配置数据失败:', error);
}
},
getChineseItemMargin(drug) {
if (Number(this.seeRate) !== 1) {
return '';
}
return PrescriptionCalculator.calculateItemGrossMarginPercent(
drug.price,
drug.buy_price
);
},
async loadPriceAdjustConfigForStore(storeId) {
try {
const res = await getPriceAdjustConfigApi(storeId);
if (res) {
this.priceAdjustScopeMode = res.order_price_adjust_scope === 'both' ? 'both' : 'sale_only';
this.priceAdjustQuickOptions = normalizeQuickOptions(res.order_discount_quick_options);
if (res.enable_order_price_percent_adjust != null) {
this.enableOrderPricePercentAdjust = Number(res.enable_order_price_percent_adjust);
}
}
} catch (e) {
console.error('获取调价配置失败:', e);
}
},
snapshotDrugOrigins(drugs) {
return (drugs || []).map((drug) => ({
...drug,
origin_price: drug.origin_price ?? drug.price,
origin_buy_price: drug.origin_buy_price ?? drug.buy_price,
}));
},
prepareDrugForCart(drug) {
const [prepared] = applyRatioToDrugs(
this.snapshotDrugOrigins([drug]),
this.priceDiscount,
this.priceAdjustScopeMode,
);
return prepared;
},
applyPreSendPriceDiscount(discount) {
this.priceDiscount = discount;
const scope = this.priceAdjustScopeMode;
this.currentDrugs = applyRatioToDrugs(this.snapshotDrugOrigins(this.currentDrugs), discount, scope);
uni.showToast({ title: discount === 100 ? '已清除浮动' : '调价成功', icon: 'none' });
},
clearPreSendPriceDiscount() {
this.applyPreSendPriceDiscount(100);
},
openPreSendOrderPriceAdjust() {
if (!this.canPreSendPriceAdjust) return;
this.currentDrugs = this.snapshotDrugOrigins(this.currentDrugs);
this.priceAdjustVisible = true;
},
closePreSendPriceAdjust() {
this.priceAdjustVisible = false;
},
onPreSendPriceAdjustConfirm(payload) {
this.applyPreSendPriceDiscount(payload.priceDiscount);
this.closePreSendPriceAdjust();
},
async fetchStoreSeeRate() {
try {
const storeId = uni.getStorageSync('store_id');
const params = {};
if (storeId) {
params.store_id = storeId;
}
if (this.registerId) {
params.register_id = this.registerId;
}
if (!params.store_id && !params.register_id) {
return;
}
const res = await getCurrentStoreTypeApi(params);
if (res) {
this.seeRate = Number(res.see_rate ?? 0);
this.enableOrderPricePercentAdjust = Number(res.enable_order_price_percent_adjust ?? 0);
await this.loadPriceAdjustConfigForStore(params.store_id || storeId);
}
} catch (error) {
console.error('获取毛利率权限失败:', error);
}
},
async loadRegisterStoreInfo() {
if (!this.registerId) {
await this.fetchStoreSeeRate();
return;
}
try {
const storeId = uni.getStorageSync('store_id');
const res = await getCurrentStoreTypeApi({
register_id: this.registerId,
...(storeId ? { store_id: storeId } : {})
});
if (res) {
this.registerStoreInfo = res;
this.seeRate = Number(res.see_rate ?? 0);
if (Number(res.allow_insurance_category ?? 0) !== 1) {
this.feeCategory = 1;
}
this.checkSalespersonTransfer();
if (res.is_from_transfer === 1 && res.delegate_store_id) {
this.selectedStoreId = res.delegate_store_id;
this.sendMode = 1;
} else if (res.store_id) {
this.selectedStoreId = res.store_id;
this.sendMode = res.can_change ? 1 : 0;
}
const priceStoreId = this.selectedStoreId || res.store_id || storeId;
this.enableOrderPricePercentAdjust = Number(res.enable_order_price_percent_adjust ?? 0);
await this.loadPriceAdjustConfigForStore(priceStoreId);
}
} catch (error) {
console.error('获取挂号诊所信息失败:', error);
}
},
async checkSalespersonTransfer() {
if (!this.registerId) {
this.hasSalespersonTransfer = false;
return;
}
try {
const res = await getSalespersonTransferByRegisterApi(this.registerId);
const list = res?.result || res?.data || res;
this.hasSalespersonTransfer = Array.isArray(list) && list.length > 0;
} catch {
this.hasSalespersonTransfer = false;
}
},
async openSalespersonTransfer() {
try {
const res = await getSalespersonTransferByRegisterApi(this.registerId);
const list = res?.result || res?.data || res;
if (!Array.isArray(list) || !list.length) {
this.$toast('暂无待导入传方');
return;
}
const item = list[0];
const parsed = parseTransferPrescriptionContent(item);
if (!parsed || !parsed.drugList?.length) {
this.$toast('传方数据无效');
return;
}
this.salespersonTransferPrescriptionId = item.id;
this.activeCategory = 1;
parsed.drugList.forEach((drug) => {
this.currentDrugs.push(this.prepareDrugForCart({
index_id: drug.index_id ?? drug.drug_id ?? drug.id,
id: drug.drug_id || drug.id,
drug_id: drug.drug_id || drug.id,
drug_name: drug.drug_name || drug.name,
name: drug.drug_name || drug.name,
number: drug.number || 1,
way_id: drug.way_id || 0,
price: drug.price ?? 0,
buy_price: drug.buy_price,
}));
});
if (parsed.clinical_diagnose) {
this.diagnoses = [{ id: Date.now(), name: parsed.clinical_diagnose }];
}
if (parsed.doctor_order) this.medicalAdvice = parsed.doctor_order;
this.saveToLocalStorage();
this.$toast('传方已导入');
} catch (e) {
console.error(e);
this.$toast('加载传方失败');
}
},
async handleSwitchCategory(category) {
this.saveToLocalStorage();
this.activeCategory = category;
this.loadCurrentCategoryDrugs();
if (category === 1) {
await this.checkSalespersonTransfer();
await this.checkAndShowTransferTip();
if (this.processRuleList.length === 0) await this.loadProcessRuleList(0, 0);
}
},
async checkAndShowTransferTip() {
try {
if (!this.registerStoreInfo) await this.loadRegisterStoreInfo();
const storeInfo = this.registerStoreInfo;
if (!storeInfo) return;
if (storeInfo.clinic_type === 1 && storeInfo.delegate_store) {
const delegateStore = storeInfo.delegate_store;
const doctorName = delegateStore.doctor?.name || '委托医生';
const storeName = delegateStore.name || '委托诊所';
uni.showModal({
title: '转诊提示',
content: `检测到当前诊所为西医诊所,本次处方开具后自动转接到【${storeName}】的【${doctorName}】的在线复诊中`,
showCancel: false,
confirmText: '我知道了'
});
}
} catch (error) {
console.error('获取诊所信息失败:', error);
}
},
handleOpenDiagnosisModal() { this.showDiagnosisModal = true; },
async ensureTraditionalTcmData() {
if (this.traditionalTcmData) return this.traditionalTcmData;
if (this.traditionalTcmLoadPromise) return this.traditionalTcmLoadPromise;
this.traditionalTcmLoadPromise = getTraditionalChineseMedicineJson()
.then((d) => {
this.traditionalTcmData = d;
return d;
})
.finally(() => {
this.traditionalTcmLoadPromise = null;
});
return this.traditionalTcmLoadPromise;
},
async handleOpenTcmPicker(field) {
if (!this.isOnlineRevisit || this.activeCategory !== 1) return;
try {
uni.showLoading({ title: '加载中', mask: true });
await this.ensureTraditionalTcmData();
uni.hideLoading();
this.tcmPickerField = field;
this.tcmPickerTitle = field === 'diseases' ? '中医证候' : field === 'method' ? '中医治法' : '中医疾病';
const d = this.traditionalTcmData;
this.tcmPickerList = field === 'diseases' ? (d.diseases || []) : field === 'method' ? (d.method || []) : (d.syndrome || []);
this.showTcmPicker = true;
} catch (e) {
uni.hideLoading();
console.error(e);
uni.showToast({ title: '术语表加载失败', icon: 'none' });
}
},
handleTcmPickerConfirm(id) {
const nid = id != null ? Number(id) : null;
if (this.tcmPickerField === 'diseases') this.onlineTcmDiseasesId = nid;
else if (this.tcmPickerField === 'method') this.onlineTcmMethodId = nid;
else if (this.tcmPickerField === 'syndrome') this.onlineTcmSyndromeId = nid;
this.saveToLocalStorage();
this.showTcmPicker = false;
},
handleDiagnosisConfirm(diagnosisText) {
this.diagnosisText = diagnosisText;
if (diagnosisText) {
const diagnosisNames = diagnosisText.split('').filter(Boolean);
this.diagnoses = diagnosisNames.map((name, index) => ({ id: `temp_${index}`, name: name }));
} else {
this.diagnoses = [];
}
this.saveToLocalStorage();
},
handleRemoveDiagnosis(index) {
this.diagnoses.splice(index, 1);
this.diagnosisText = this.diagnoses.map(item => item.name).join('');
this.saveToLocalStorage();
},
handleOpenDoctorOrderModal() { this.showDoctorOrderModal = true; },
handleDoctorOrderConfirm(doctorOrderText) {
this.medicalAdvice = doctorOrderText;
this.saveToLocalStorage();
},
handleOpenCommonPrescriptionModal() { this.showCommonPrescriptionModal = true; },
async handleApplySpecialPrescription() {
if (!this.registerId || this.applyingSpecialPrescription) return;
this.applyingSpecialPrescription = true;
try {
const res = await applySpecialPrescriptionApi({ register_id: this.registerId });
const data = res.result || res.data || res;
applySpecialPrescriptionPayload(this, data);
await this.loadRegisterOrderType();
this.$toast(`已导入特色方:${data.special_prescription_name || ''}`);
} catch (error) {
console.error('导入特色方失败:', error);
this.$toast('导入特色方失败,请稍后重试');
} finally {
this.applyingSpecialPrescription = false;
}
},
async handleSelectCommonPrescription(item) {
try {
const payload = item && item.apply_payload;
if (!payload || !Array.isArray(payload.drugs)) {
this.$toast('常用方数据异常');
return;
}
this.currentDrugs.splice(0, this.currentDrugs.length, ...payload.drugs);
if (payload.diagnosisText) {
this.diagnosisText = payload.diagnosisText;
const names = payload.diagnosisText.split('').filter(Boolean);
this.diagnoses = names.map((name, index) => ({ id: `temp_${index}`, name }));
}
if (payload.medicalAdvice) this.medicalAdvice = payload.medicalAdvice;
if (this.activeCategory === 1 && payload.chineseConfig) {
this.chineseConfig = { ...this.chineseConfig, ...payload.chineseConfig };
if (this.chineseConfig.processRuleId) await this.loadProcessRuleList(this.chineseConfig.processRuleId, 0);
if (this.chineseConfig.childProcessRuleId) await this.loadProcessRuleList(0, this.chineseConfig.childProcessRuleId);
}
this.saveToLocalStorage();
this.$toast('常用方已填充');
} catch (error) {
console.error('选择常用方失败:', error);
this.$toast('选择常用方失败');
}
},
getCommonPrescriptionType(category) {
if (category === 1) return 'chinese';
else if (category === 2) return 'west';
else return 'granular';
},
applyPendingExperienceDrugFromChat() {
let raw;
try {
raw = uni.getStorageSync(PENDING_RX_FROM_CHAT);
} catch (e) {
return;
}
if (!raw) return;
let p;
try {
p = typeof raw === 'string' ? JSON.parse(raw) : raw;
} catch (e2) {
try {
uni.removeStorageSync(PENDING_RX_FROM_CHAT);
} catch (e3) {}
return;
}
if (!p || String(p.register_id) !== String(this.registerId)) return;
if (p.source === 'register_drugs' && Array.isArray(p.drugs) && p.drugs.length > 0) {
try {
uni.removeStorageSync(PENDING_RX_FROM_CHAT);
} catch (e4) {}
this.activeCategory = 2;
this.saveToLocalStorage();
this.loadCurrentCategoryDrugs();
this.mergeWesternRowsFromRegisterDrugs(p.drugs);
return;
}
if (p.source === 'transfer_import' && Array.isArray(p.drugs) && p.drugs.length > 0) {
try {
uni.removeStorageSync(PENDING_RX_FROM_CHAT);
} catch (e5) {}
this.activeCategory = 1;
this.saveToLocalStorage();
this.loadCurrentCategoryDrugs();
this.mergeTransferChineseRows(p.drugs);
if (p.diagnosisText) {
this.diagnosisText = String(p.diagnosisText);
const names = this.diagnosisText.split('').filter(Boolean);
this.diagnoses = names.map((name, index) => ({ id: `temp_${index}`, name }));
}
if (p.medicalAdvice != null && String(p.medicalAdvice).trim() !== '') {
this.medicalAdvice = String(p.medicalAdvice);
}
this.saveToLocalStorage();
return;
}
try {
uni.removeStorageSync(PENDING_RX_FROM_CHAT);
} catch (e4) {}
this.activeCategory = 2;
this.westernModalInitialKeyword = String(p.drug_name || '').trim();
this.saveToLocalStorage();
this.loadCurrentCategoryDrugs();
this.$nextTick(() => {
this.showWesternMedicineModal = true;
});
},
mergeWesternRowsFromRegisterDrugs(rows) {
let added = 0;
for (const row of rows) {
const d = row.drug || {};
const qtyRaw = row.purchase_quantity != null ? Number(row.purchase_quantity) : null;
const qty =
qtyRaw != null && !Number.isNaN(qtyRaw) && qtyRaw >= 1 ? Math.min(99, qtyRaw) : Number(d.number || 1) || 1;
const drugId = row.drug_id || d.id;
const synthetic = {
id: drugId,
drug_id: drugId,
drug_name: d.drug_name,
number: qty,
select_number: qty,
price: parseFloat(row.price) || 0,
specification: d.specification || '',
image: d.image || '',
instruction: d.instruction || '',
time_id: d.time_id || 0,
type_id: d.type_id || 0,
frequency_id: d.frequency_id || 0,
unit_id: d.unit_id || 0,
drug: d
};
if (PrescriptionValidator.isDrugExists(this.currentDrugs, synthetic)) {
continue;
}
const newDrug = {
index_id: row.id,
id: drugId,
drug_id: drugId,
drug_name: d.drug_name,
number: qty,
select_number: qty,
price: parseFloat(row.price) || 0,
specification: d.specification || '',
image: d.image || '',
instruction: d.instruction || '',
time_id: d.time_id || 0,
type_id: d.type_id || 0,
frequency_id: d.frequency_id || 0,
unit_id: d.unit_id || 0,
use_num: this.drugUseList.drug_time?.find((item) => item.id === (d.time_id || 0)),
use_type: this.drugUseList.drug_use_type?.find((item) => item.id === (d.type_id || 0)),
use_frequency: this.drugUseList.drug_use_frequency?.find((item) => item.id === (d.frequency_id || 0)),
unit: this.drugUseList.drug_unit?.find((item) => item.id === (d.unit_id || 0))
};
this.currentDrugs.push(this.prepareDrugForCart(newDrug));
added++;
}
this.saveToLocalStorage();
if (added > 0) {
this.$toast(`已添加 ${added} 个药品`);
} else {
this.$toast('药品已在列表中或未能匹配');
}
},
mergeTransferChineseRows(rows) {
let added = 0;
for (const product of rows) {
if (PrescriptionValidator.isDrugExists(this.currentDrugs, product)) {
continue;
}
this.currentDrugs.push(this.prepareDrugForCart({ ...product }));
added++;
}
this.saveToLocalStorage();
if (added > 0) {
this.$toast(`已添加 ${added} 味药`);
} else {
this.$toast('药品已在列表中');
}
},
handleOpenWesternMedicineModal() {
this.westernModalInitialKeyword = '';
this.showWesternMedicineModal = true;
},
handleSelectWesternDrug(drug) {
if (PrescriptionValidator.isDrugExists(this.currentDrugs, drug)) {
this.$toast('该药品已在列表中');
return;
}
const newDrug = {
index_id: drug.id,
id: drug.drug_id || drug.drug?.id || drug.id,
drug_id: drug.drug_id || drug.drug?.id || drug.id,
drug_name: drug.drug_name || drug.drug?.drug_name,
number: drug.number || 1,
select_number: drug.select_number || 1,
price: drug.price || 0,
specification: drug.specification || drug.drug?.specification || '',
image: drug.image || drug.drug?.image || '',
instruction: drug.instruction || drug.drug?.instruction || '',
time_id: drug.time_id || drug.drug?.time_id || 0,
type_id: drug.type_id || drug.drug?.type_id || 0,
frequency_id: drug.frequency_id || drug.drug?.frequency_id || 0,
unit_id: drug.unit_id || drug.drug?.unit_id || 0,
use_num: this.drugUseList.drug_time?.find(item => item.id === (drug.time_id || drug.drug?.time_id)),
use_type: this.drugUseList.drug_use_type?.find(item => item.id === (drug.type_id || drug.drug?.type_id)),
use_frequency: this.drugUseList.drug_use_frequency?.find(item => item.id === (drug.frequency_id || drug.drug?.frequency_id)),
unit: this.drugUseList.drug_unit?.find(item => item.id === (drug.unit_id || drug.drug?.unit_id))
};
this.currentDrugs.push(this.prepareDrugForCart(newDrug));
const index = this.currentDrugs.length - 1;
this.handleEditWesternDrug(this.currentDrugs[index], index);
this.saveToLocalStorage();
this.$toast(`已添加 ${newDrug.drug_name}`);
},
handleOpenChineseMedicineModal() {
this.chineseModalInitialKeyword = '';
this.showChineseMedicineModal = true;
},
/** 微信小程序 :key 中勿写 it.id || it.index_id 等表达式,会编译异常 */
tcmTagKey(it, tcmIndex) {
if (it && it.id != null && it.id !== '') return `tcm-${it.id}`;
if (it && it.index_id != null && it.index_id !== '') return `tcm-i-${it.index_id}`;
return `tcm-idx-${tcmIndex}`;
},
/**
* 中药标签长按菜单(小程序 showActionSheet 回调里勿依赖闭包 item用 dataset.idx + currentDrugs
*/
onTcmTagLongPress(e) {
const raw = e && e.currentTarget && e.currentTarget.dataset ? e.currentTarget.dataset.idx : undefined;
const idx = raw === undefined || raw === '' ? NaN : Number(raw);
if (Number.isNaN(idx) || idx < 0 || idx >= this.currentDrugs.length) return;
uni.showActionSheet({
itemList: ['编辑', '删除'],
success: (res) => {
const row = this.currentDrugs[idx];
if (!row) return;
if (res.tapIndex === 0) {
this.chineseModalInitialKeyword = String(row.drug_name || row.name || '').trim();
this.showChineseMedicineModal = true;
} else if (res.tapIndex === 1) {
this.handleRemoveDrug(idx);
}
}
});
},
handleSelectChineseDrug(drugs) {
this.currentDrugs = applyRatioToDrugs(
this.snapshotDrugOrigins(drugs || []),
this.priceDiscount,
this.priceAdjustScopeMode,
);
this.saveToLocalStorage();
},
handleOpenSimpleProductModal() { this.showSimpleProductModal = true; },
handleSelectSimpleProduct(product) {
if (PrescriptionValidator.isDrugExists(this.currentDrugs, product)) {
this.$toast('该产品已在列表中');
return;
}
const newProduct = {
index_id: product.id,
id: product.drug_id || product.drug?.id || product.id,
drug_name: product.drug_name || product.drug?.drug_name || product.name,
number: 1,
price: product.price || 0,
image: product.image || product.drug?.image || '',
instruction: product.instruction || product.drug?.instruction || '',
type: product.drug?.type || product.type || this.activeCategory,
select_number: 1
};
this.currentDrugs.push(this.prepareDrugForCart(newProduct));
this.saveToLocalStorage();
this.$toast(`已添加 ${newProduct.drug_name}`);
},
handleEditWesternDrug(drug, index) {
this.editingDrug = JSON.parse(JSON.stringify(drug));
this.editingDrugIndex = index;
this.showWesternUsageModal = true;
},
handleConfirmWesternUsage(usageData) {
if (this.editingDrugIndex >= 0) {
const drug = this.currentDrugs[this.editingDrugIndex];
drug.time_id = usageData.time_id;
drug.type_id = usageData.type_id;
drug.frequency_id = usageData.frequency_id;
drug.unit_id = usageData.unit_id;
drug.number = usageData.number;
drug.select_number = usageData.select_number || drug.select_number;
drug.use_num = usageData.use_num;
drug.use_type = usageData.use_type;
drug.use_frequency = usageData.use_frequency;
drug.unit = usageData.unit;
this.saveToLocalStorage();
}
this.editingDrug = null;
this.editingDrugIndex = -1;
},
handleRemoveDrug(index) {
uni.showModal({
title: '移除药品',
content: '确定要从处方中移除该药品吗?',
confirmColor: '#FC3636',
success: ({ confirm }) => {
if (confirm) {
this.currentDrugs.splice(index, 1);
this.saveToLocalStorage();
}
}
});
},
handleIncrementSimpleProduct(index) {
if (index >= 0 && index < this.currentDrugs.length) {
const product = this.currentDrugs[index];
const currentNumber = product.select_number || product.number || 1;
this.$set(product, 'select_number', currentNumber + 1);
this.saveToLocalStorage();
}
},
handleDecrementSimpleProduct(index) {
if (index >= 0 && index < this.currentDrugs.length) {
const product = this.currentDrugs[index];
const currentNumber = product.select_number || product.number || 1;
if (currentNumber > 1) {
this.$set(product, 'select_number', currentNumber - 1);
this.saveToLocalStorage();
} else {
this.handleRemoveDrug(index);
}
}
},
handleRemoveAllDrugs() {
uni.showModal({
title: '清空药材',
content: '确定要清空所有中药材吗?',
confirmColor: '#FC3636',
success: ({ confirm }) => {
if (confirm) {
this.currentDrugs = [];
this.saveToLocalStorage();
}
}
});
},
async handleUpdateChineseConfig(config) {
const oldRuleType = this.chineseConfig.ruleType;
const oldProcessRuleId = this.chineseConfig.processRuleId;
const oldChildProcessRuleId = this.chineseConfig.childProcessRuleId;
if (config.ruleType === 2 && config.ruleType !== oldRuleType) {
if (this.processRuleList.length === 0) await this.loadProcessRuleList(0, 0);
}
if (config.ruleType === 1 && oldRuleType === 2) {
config.processRuleId = null;
config.childProcessRuleId = null;
config.processRuleNoteId = null;
this.processRuleList = [];
this.childProcessRuleList = [];
this.processRuleNoteList = [];
}
if (config.processRuleId && config.processRuleId !== oldProcessRuleId) {
config.childProcessRuleId = null;
config.processRuleNoteId = null;
this.childProcessRuleList = [];
this.processRuleNoteList = [];
}
if (config.childProcessRuleId && config.childProcessRuleId !== oldChildProcessRuleId) {
config.processRuleNoteId = null;
this.processRuleNoteList = [];
}
this.chineseConfig = { ...this.chineseConfig, ...config };
if (config.processRuleId && config.processRuleId !== oldProcessRuleId) {
await this.loadProcessRuleList(config.processRuleId, 0);
}
if (config.childProcessRuleId && config.childProcessRuleId !== oldChildProcessRuleId) {
await this.loadProcessRuleList(0, config.childProcessRuleId);
}
this.saveToLocalStorage();
},
async loadProcessRuleList(pid = 0, ruleId = 0) {
try {
const data = ruleId === 0 ? { pid } : { rule_id: ruleId };
let list = [];
if (this.isSalespersonTransferMode) {
const wrap = await getClinicSalespersonProcessRuleList(data);
if (wrap && wrap.ok) {
list = Array.isArray(wrap.data) ? wrap.data : [];
}
} else {
const res = await getProcessRuleList(data);
list = res?.data || res?.result || res || [];
}
if (ruleId !== 0) {
this.processRuleNoteList = list;
} else if (pid === 0) {
this.processRuleList = list;
} else {
this.childProcessRuleList = list;
}
} catch (error) {
console.error('加载加工规则列表失败:', error);
this.$toast('加载加工规则列表失败');
}
},
parsePrescriptionDetailContent(detail) {
let c = detail && detail.content;
if (typeof c === 'string') {
try { c = JSON.parse(c); } catch (e) { c = {}; }
}
return c && typeof c === 'object' ? c : {};
},
mapWestRepiceToProduct(recipe) {
if (!recipe) return null;
let drugObj = {};
try {
const raw = recipe.content;
drugObj = typeof raw === 'string' ? JSON.parse(raw) : (raw || {});
} catch (e) { return null; }
const drugId = Number(drugObj.id);
if (!drugId) return null;
const selectNumber = Math.max(1, Number(recipe.number ?? drugObj.select_number ?? drugObj.number ?? 1) || 1);
const grainNumber = Math.max(1, Number(drugObj.number ?? drugObj.grain_number ?? recipe.grain_number ?? 1) || 1);
const timeId = recipe.time_id ?? drugObj.time_id;
const typeId = recipe.type_id ?? drugObj.type_id;
const freqId = recipe.f_id ?? drugObj.frequency_id;
const unitId = recipe.wu_id ?? drugObj.unit_id;
const list = this.drugUseList || {};
const wayId = drugObj.way_id;
return {
index_id: drugId, id: drugId,
drug_name: drugObj.drug_name || drugObj.name || '',
number: grainNumber,
use_num: (list.drug_time || []).find((item) => item.id === timeId),
use_type: (list.drug_use_type || []).find((item) => item.id === typeId),
use_frequency: (list.drug_use_frequency || []).find((item) => item.id === freqId),
unit: (list.drug_unit || []).find((item) => item.id === unitId),
price: Number(drugObj.sell_price ?? drugObj.price ?? 0),
way_id: wayId,
use_ways: (list.drug_use_way || []).find((item) => item.id === wayId),
time_id: timeId, type_id: typeId, frequency_id: freqId, unit_id: unitId,
image: drugObj.image || '', instruction: drugObj.instruction || '',
type: drugObj.type, select_number: selectNumber, specification: drugObj.specification || ''
};
},
async applyHistoricalPrescriptionDetail(detail) {
const content = this.parsePrescriptionDetailContent(detail);
const pt = Number(detail.prescription_type);
if (![1, 2, 3, 5, 6, 7].includes(pt)) {
this.$toast('该处方类型暂不支持复用');
return false;
}
if (!this.patientId) {
this.$toast('患者信息异常');
return false;
}
this.saveToLocalStorage();
this.activeCategory = pt;
PrescriptionStorage.saveActiveCategory(pt, this.registerId);
const repiceList = Array.isArray(content.repice) ? content.repice : [];
if (pt === 1) {
const rep0 = repiceList[0];
if (!rep0) return false;
let rows = [];
try {
const raw = rep0.content;
rows = typeof raw === 'string' ? JSON.parse(raw) : (raw || []);
} catch (e) { return false; }
if (!Array.isArray(rows) || rows.length === 0) return false;
const list = this.drugUseList || {};
this.currentDrugs = rows.map((d) => {
const drugId = Number(d.drug_id ?? d.id);
const wayId = d.way_id ?? d.use_way?.id ?? d.useWay?.id ?? d.order;
const unitId = d.unit_id;
return {
index_id: d.id ?? drugId, id: drugId,
drug_name: d.name || d.drug_name || '',
number: d.number ?? 1, price: Number(d.price ?? 0),
way_id: wayId,
use_ways: (list.drug_use_way || []).find((item) => item.id === wayId) ?? d.use_way ?? d.useWay,
use_num: (list.drug_time || []).find((item) => item.id === d.time_id),
use_type: (list.drug_use_type || []).find((item) => item.id === d.type_id),
use_frequency: (list.drug_use_frequency || []).find((item) => item.id === d.frequency_id),
unit: (list.drug_unit || []).find((item) => item.id === unitId) || d.unit,
time_id: d.time_id, type_id: d.type_id, frequency_id: d.frequency_id, unit_id: unitId,
image: d.image || '', instruction: d.instruction || '', type: d.type
};
});
const ruleType = rep0.deployment === 2 ? 1 : 2;
this.chineseConfig = {
...this.chineseConfig, ruleType,
dosage: Number(rep0.dosage ?? 7), dayDosage: Number(rep0.consumption ?? 2),
packageMethodId: rep0.package_method_id ?? 2,
processRuleId: rep0.process_rule_id && Number(rep0.process_rule_id) > 0 ? rep0.process_rule_id : null,
childProcessRuleId: rep0.child_process_rule_id && Number(rep0.child_process_rule_id) > 0 ? rep0.child_process_rule_id : null,
processRuleNoteId: rep0.process_rule_note_id && Number(rep0.process_rule_note_id) > 0 ? rep0.process_rule_note_id : null
};
if (ruleType === 2) await this.hydrateChineseProcessRuleListsFromConfig();
} else {
const mapped = [];
for (const recipe of repiceList) {
const row = this.mapWestRepiceToProduct(recipe);
if (row) mapped.push(row);
}
if (mapped.length === 0) return false;
this.currentDrugs = mapped;
}
const dx = detail.clinical_diagnose ?? content.clinical_diagnose ?? '';
this.diagnosisText = dx;
if (dx) {
const names = String(dx).split('').filter(Boolean);
this.diagnoses = names.map((name, index) => ({ id: `temp_${index}`, name }));
} else {
this.diagnoses = [];
}
this.medicalAdvice = detail.doctor_order ?? content.doctor_order ?? '';
this.saveToLocalStorage();
return true;
},
async maybeApplyReusePrescription() {
const rawId = this.pendingReusePrescriptionId;
if (!rawId) return;
this.pendingReusePrescriptionId = '';
const pid = parseInt(rawId, 10);
if (!pid || !this.registerId) return;
const hasDraft = PrescriptionStorage.hasDraftWithDrugs(this.registerId);
const run = async () => {
try {
const res = await getPrescriptionInfoApi(pid);
if (res && ((typeof res.code === 'number' && res.code !== 0) || (typeof res.errcode === 'number' && res.errcode !== 0))) {
this.$toast(res.msg || res.message || '加载处方详情失败');
return;
}
const detail = res.result || res.data || res;
const ok = await this.applyHistoricalPrescriptionDetail(detail);
if (ok) this.$toast('已复用历史处方,请核对后发送');
} catch (e) {
console.error('复用处方失败:', e);
this.$toast('加载处方详情失败');
}
};
if (hasDraft) {
uni.showModal({
title: '覆盖当前草稿?', content: '当前处方清单中有药品,复用将替换为所选历史处方内容。',
success: (r) => { if (r.confirm) run(); }
});
} else {
await run();
}
},
formatDrugUsage(drug) {
if (!drug.use_type || !drug.use_frequency || !drug.use_num || !drug.unit) return '';
return `${drug.use_type.name}${drug.use_frequency.name}${drug.use_num.name},每次${drug.number}${drug.unit.name}`;
},
getProductUnit(category) {
if (category === 5) return '周期';
return '件';
},
validatePrescriptionData() {
return PrescriptionValidator.validatePrescriptionData({
category: this.activeCategory,
drugs: this.currentDrugs,
diagnoses: this.diagnoses,
medicalAdvice: this.medicalAdvice,
chineseConfig: this.activeCategory === 1 ? this.chineseConfig : null,
requireOnlineTcm: this.activeCategory === 1 && this.isOnlineRevisit,
onlineTcmDiseasesId: this.onlineTcmDiseasesId,
onlineTcmMethodId: this.onlineTcmMethodId,
onlineTcmSyndromeId: this.onlineTcmSyndromeId
});
},
async checkChineseMedicineConflict() {
if (this.activeCategory !== 1) return { hasConflict: false };
try {
const names = this.currentDrugs.map((item) => item.drug_name || item.name);
const res = await checkChineseMedicineConflictApi({ names });
if (res && res.is_exist === true) return { hasConflict: true, message: res.message || '检测到该处方内具有药物相冲' };
return { hasConflict: false };
} catch (error) {
console.error('检查中药相冲失败:', error);
return { hasConflict: false };
}
},
async handleSendPrescription(doctorSecondSign = 0) {
if (this.isSubmitting) { this.$toast('正在处理中,请勿重复提交'); return; }
if (this.isCommonPrescription) { this.handleSaveAsCommonPrescription(); return; }
const validation = this.validatePrescriptionData();
if (!validation.valid) { this.$toast(validation.message); return; }
if (this.isSalespersonTransferMode) {
this.showTransferPatientDrawer = true;
return;
}
this.isSubmitting = true;
try {
if (!this.registerStoreInfo) await this.loadRegisterStoreInfo();
if (this.activeCategory === 1) {
const conflictCheck = await this.checkChineseMedicineConflict();
if (conflictCheck.hasConflict) {
uni.showModal({
title: '药物相冲提示',
content: conflictCheck.message || '检测到该处方内具有药物相冲,是否继续开方?',
showCancel: true,
success: ({ confirm }) => {
if (confirm) this.handleSendPrescriptionWithStoreSelection(1);
else this.isSubmitting = false;
}
});
return;
}
}
await this.handleSendPrescriptionWithStoreSelection(doctorSecondSign);
} catch (error) {
console.error('发送处方失败:', error);
this.$toast('发送处方失败,请重试');
this.isSubmitting = false;
}
},
async handleSendPrescriptionWithStoreSelection(doctorSecondSign = 0) {
const storeInfo = this.registerStoreInfo;
if (storeInfo?.is_from_transfer === 1 && storeInfo?.delegate_store_id) {
await this.executeSendPrescription(doctorSecondSign, 1, storeInfo.delegate_store_id);
} else if (storeInfo?.can_change) {
const registerStoreName = storeInfo?.store_name || '挂号诊所';
const registerStoreId = storeInfo?.store_id;
const currentStoreName = storeInfo?.current_store_name || '当前诊所';
uni.showModal({
title: '选择诊所',
content: `请选择使用哪个诊所开方:\n\n挂号诊所${registerStoreName}\n当前诊所${currentStoreName}`,
confirmText: '使用挂号诊所',
cancelText: '使用当前诊所',
success: ({ confirm, cancel }) => {
if (confirm) this.executeSendPrescription(doctorSecondSign, 1, registerStoreId);
else if (cancel) this.executeSendPrescription(doctorSecondSign, 0, null);
else this.isSubmitting = false;
},
fail: () => { this.isSubmitting = false; }
});
} else {
await this.executeSendPrescription(doctorSecondSign, 0, null);
}
},
async executeSendPrescription(doctorSecondSign = 0, sendMode = 0, customStoreId = null) {
try {
if (this.activeCategory === 2) {
if (!this.patientInfo || !this.patientId) {
if (this.patientId && !this.patientInfo) await this.loadPatientInfo();
if (!this.patientInfo || !this.patientId) {
this.$toast('患者信息缺失,无法发送处方');
this.isSubmitting = false; return;
}
}
const invalidDrug = this.currentDrugs.find((drug) => {
return !(drug.id && (drug.select_number || drug.number) && drug.time_id && drug.type_id && drug.frequency_id && drug.unit_id && drug.use_type && drug.use_type.name && drug.use_num && drug.use_num.name && drug.unit && drug.unit.name);
});
if (invalidDrug) { this.$toast('请先为西药设置完整的用法用量'); return; }
}
let finalSendMode = sendMode; let finalStoreId = customStoreId;
const storeInfo = this.registerStoreInfo;
if (storeInfo?.is_from_transfer === 1 && storeInfo?.delegate_store_id) {
finalSendMode = 1; finalStoreId = storeInfo.delegate_store_id;
}
const clinical_diagnose = this.diagnosisText || this.diagnoses.map((item) => item.name).join(',');
let doctorSecondSignValue = typeof doctorSecondSign === 'number' ? doctorSecondSign : (typeof doctorSecondSign === 'object' && doctorSecondSign !== null && typeof doctorSecondSign.value === 'number' ? doctorSecondSign.value : (typeof doctorSecondSign === 'string' ? parseInt(doctorSecondSign, 10) || 0 : (doctorSecondSign ? 1 : 0)));
const patientData = { id: this.patientInfo.id || this.patientId, user_id: this.patientInfo.user_id || null, name: this.patientInfo.name || '', age: this.patientInfo.age || 0, sex: this.patientInfo.sex || 0, mobile: this.patientInfo.mobile || '' };
let drugsData;
if (this.activeCategory === 1) {
drugsData = this.currentDrugs.map((drug) => ({ index_id: drug.index_id, id: drug.id, drug_name: drug.drug_name || drug.name, number: drug.number || drug._quantity || 0, price: drug.price || '0.0000', way_id: drug.way_id || 0, select_number: drug.select_number || 1 }));
} else if ([3, 5, 6, 7].includes(this.activeCategory)) {
drugsData = this.currentDrugs.map((drug) => {
const cleanDrug = { index_id: drug.index_id, id: drug.id, drug_name: drug.drug_name || drug.name, number: drug.number || 1, price: drug.price || '0.0000', select_number: drug.select_number || 1 };
if (drug.image) cleanDrug.image = drug.image;
if (drug.instruction) cleanDrug.instruction = drug.instruction;
if (drug.type) cleanDrug.type = drug.type;
return cleanDrug;
});
} else {
drugsData = this.currentDrugs.map(drug => {
const drugData = { ...drug };
if (!drugData.use_type || typeof drugData.use_type !== 'object' || !drugData.use_type.name) drugData.use_type = (drugData.type_id && this.drugUseList.drug_use_type) ? this.drugUseList.drug_use_type.find(item => item.id === drugData.type_id) || { name: '' } : { name: '' };
if (!drugData.use_num || typeof drugData.use_num !== 'object' || !drugData.use_num.name) drugData.use_num = (drugData.time_id && this.drugUseList.drug_time) ? this.drugUseList.drug_time.find(item => item.id === drugData.time_id) || { name: '' } : { name: '' };
if (!drugData.unit || typeof drugData.unit !== 'object' || !drugData.unit.name) drugData.unit = (drugData.unit_id && this.drugUseList.drug_unit) ? this.drugUseList.drug_unit.find(item => item.id === drugData.unit_id) || { name: '' } : { name: '' };
return drugData;
});
}
const params = {
patient: patientData, drugs: drugsData, diagnosis: clinical_diagnose, medicalAdvice: this.medicalAdvice, total: parseFloat(this.totalPrice), category: this.feeCategory, drug_type: 2, register_id: parseInt(this.registerId), treatment_price: parseFloat(this.treatmentPrice || 0), prescription_type: this.activeCategory, doctor_second_sign: doctorSecondSignValue, send_mode: finalSendMode, custom_store_id: finalSendMode === 1 ? finalStoreId : null,
salesperson_transfer_prescription_id: this.salespersonTransferPrescriptionId || undefined,
price_discount: this.priceDiscount,
};
if (this.activeCategory === 1) {
params.package_method_id = this.chineseConfig.packageMethodId || null; params.process_rule_id = this.chineseConfig.processRuleId || null; params.process_rule_note_id = this.chineseConfig.processRuleNoteId || null; params.child_process_rule_id = this.chineseConfig.childProcessRuleId || null; params.process_rule_type = this.chineseConfig.ruleType || 1; params.processing_fee = this.processingFee; params.dosage = this.chineseConfig.dosage || 7; params.day_dosage = this.chineseConfig.dayDosage || 2;
if (this.isOnlineRevisit) {
params.diseases_id = this.onlineTcmDiseasesId;
params.method_id = this.onlineTcmMethodId;
params.syndrome_id = this.onlineTcmSyndromeId;
}
}
const res = await addWestPrescription(params);
if (res && (res.code === 0 || res.errcode === 0)) {
this.$toast('发送成功');
const needTransfer = res?.result?.need_transfer || res?.data?.need_transfer || res?.need_transfer;
if (needTransfer) {
const transferId = res?.result?.transfer_prescription_id || res?.data?.transfer_prescription_id || res?.transfer_prescription_id;
if (transferId) {
uni.showModal({
title: '转诊提示', content: '该处方已触发转诊流程,转诊消息已发送给委托诊所的在线问诊医生。', showCancel: false, confirmText: '确定',
success: () => { this.clearPrescriptionData(); PrescriptionStorage.clearAllPrescriptionData(this.registerId); this.safeNavigateBack(500); }
});
return;
}
}
if (this.isOnlineRevisit) {
try {
await this.trySendPrescriptionChatMessage(res);
} catch (e) {
console.error('处方消息同步失败', e);
uni.showToast({ title: '处方消息同步失败', icon: 'none' });
}
}
this.clearPrescriptionData();
PrescriptionStorage.clearAllPrescriptionData(this.registerId);
this.safeNavigateBack(1500);
} else {
this.$toast(res.msg || res.message || '发送失败');
}
} catch (error) {
console.error('发送处方失败:', error);
this.$toast('发送处方失败,请重试');
throw error;
} finally {
this.isSubmitting = false;
}
},
async trySendPrescriptionChatMessage(res) {
const inner = res && (res.result !== undefined ? res.result : res.data !== undefined ? res.data : null);
if (!inner || typeof inner !== 'object') return;
if (!this.registerId) return;
const roomRes = await getChatRoomByRegisterApi(this.registerId);
const roomPayload = roomRes && (roomRes.result || roomRes.data || roomRes);
const roomId = roomPayload && roomPayload.room_id;
const receiverUserId = roomPayload && roomPayload.receiver_user_id;
if (!roomId || !receiverUserId) return;
const doctorId = uni.getStorageSync('doctor_id') || uni.getStorageSync('user_id');
if (!doctorId) return;
const senderUserId = String(doctorId).startsWith('doctor-') ? String(doctorId) : `doctor-${doctorId}`;
try {
await sendToUserHttpApi({
room_id: roomId,
sender_user_id: senderUserId,
receiver_user_id: receiverUserId,
message_type: 4,
message_content: JSON.stringify(inner),
duration: 0
});
} catch (e) {
console.warn('trySendPrescriptionChatMessage', e);
}
},
clearPrescriptionData() {
this.currentDrugs = []; this.diagnoses = []; this.diagnosisText = ''; this.medicalAdvice = ''; this.treatmentPrice = '';
this.chineseConfig = { ruleType: 1, packageMethodId: null, processRuleId: null, childProcessRuleId: null, processRuleNoteId: null, dosage: 7, dayDosage: 2 };
this.onlineTcmDiseasesId = null;
this.onlineTcmMethodId = null;
this.onlineTcmSyndromeId = null;
},
handleSaveAsCommonPrescription() {
if (this.currentDrugs.length === 0) { this.$toast('请先添加药品后再保存为常用方'); return; }
if (![1, 2].includes(this.activeCategory)) { this.$toast('当前分类不支持保存为常用方'); return; }
this.showSaveCommonPrescriptionModal = true;
},
async handleConfirmSaveCommonPrescription() {
if (!this.commonPrescriptionName || !this.commonPrescriptionName.trim()) { this.$toast('请输入常用方名称'); return; }
if (![1, 2].includes(this.activeCategory)) { this.$toast('当前分类不支持保存为常用方'); return; }
try {
const storeId = uni.getStorageSync('store_id') || 11001;
if (this.activeCategory === 1) {
const drugs = this.currentDrugs.map((drug) => ({ drug_id: drug.id || drug.drug_id, drug_name: drug.drug_name || drug.name, number: drug.number || 1, price: drug.price || 0, way_id: drug.way_id || 0 }));
const rt = this.chineseConfig.ruleType;
const payload = { name: this.commonPrescriptionName.trim(), drugs, store_id: storeId, clinical_diagnose: this.diagnosisText || '', doctor_order: this.medicalAdvice || '', category: '1', dosage: this.chineseConfig.dosage || 7, day_dosage: this.chineseConfig.dayDosage || 2, rule_type: rt };
if (rt === 1) payload.package_method_id = this.chineseConfig.packageMethodId;
if (rt === 2) { payload.process_rule_id = this.chineseConfig.processRuleId || undefined; payload.child_process_rule_id = this.chineseConfig.childProcessRuleId || undefined; payload.process_rule_note_id = this.chineseConfig.processRuleNoteId || undefined; }
const resCn = await saveChineseCommonPrescriptionApi(payload);
if (!resCn || resCn.code !== 0) { this.$toast(resCn.msg || resCn.message || '保存失败'); return; }
} else {
const drugs = this.currentDrugs.map((drug) => ({ drug_id: drug.id || drug.drug_id, drug_name: drug.drug_name || drug.name, number: drug.number || 1, select_number: drug.select_number || 1, price: drug.price || 0, time_id: drug.time_id || 0, type_id: drug.type_id || 0, frequency_id: drug.frequency_id || 0, unit_id: drug.unit_id || 0, image: drug.image || '', instruction: drug.instruction || '', specification: drug.specification || '' }));
const resW = await saveWestCommonPrescriptionApi({ name: this.commonPrescriptionName.trim(), drugs, store_id: storeId, clinical_diagnose: this.diagnosisText || '', doctor_order: this.medicalAdvice || '', category: '1' });
if (!resW || resW.code !== 0) { this.$toast(resW.msg || resW.message || '保存失败'); return; }
}
this.$toast('保存成功');
this.showSaveCommonPrescriptionModal = false; this.commonPrescriptionName = '';
if (this.isCommonPrescription) { uni.removeStorageSync('add'); this.safeNavigateBack(400); }
} catch (error) { console.error('保存常用方失败:', error); this.$toast('保存常用方失败,请重试'); }
},
buildSalespersonTransferPayload(patientData) {
const clinicalDiagnose = this.diagnosisText || this.diagnoses.map((item) => item.name).join(',');
const drugs = this.currentDrugs.map((drug) => ({
drug_id: drug.id || drug.drug_id || drug.index_id,
drug_name: drug.drug_name || drug.name,
number: drug.number || drug._quantity || 1,
way_id: drug.way_id || 0,
}));
return {
patient_name: patientData.patient_name,
patient_mobile: patientData.patient_mobile,
patient_address: patientData.patient_address,
prescription_images: patientData.prescription_images || [],
clinical_diagnose: clinicalDiagnose,
doctor_order: this.medicalAdvice || '',
dosage: this.chineseConfig.dosage || 7,
day_dosage: this.chineseConfig.dayDosage || 2,
drugs,
};
},
async handleTransferPatientConfirm(patientData) {
this.transferDrawerSubmitting = true;
try {
const wrap = await createClinicSalespersonTransferPrescription(
this.buildSalespersonTransferPayload(patientData)
);
if (wrap && wrap.ok) {
PrescriptionStorage.clearAllPrescriptionData(SALESPERSON_TRANSFER_STORAGE_KEY);
this.showTransferPatientDrawer = false;
if (this.$refs.transferPatientDrawer && this.$refs.transferPatientDrawer.resetForm) {
this.$refs.transferPatientDrawer.resetForm();
}
// #ifdef MP-WEIXIN
this.setPageBackGuardShow(false, { suppressLeave: true });
uni.disableAlertBeforeUnload();
// #endif
this.$toast('提交成功');
setTimeout(() => {
uni.navigateTo({ url: '/subPackages/sub_clinic_salesperson/transfer-prescription/list' });
}, 500);
} else {
this.$toast(wrap?.res?.msg || wrap?.res?.message || '提交失败');
}
} catch (error) {
console.error('提交传方失败:', error);
this.$toast('提交传方失败,请重试');
} finally {
this.transferDrawerSubmitting = false;
}
},
tryLeaveSalespersonTransferPage(onConfirm) {
if (this.hasTransferDraft) {
uni.showModal({
title: '提示',
content: '有未提交的传方内容,确定退出?',
success: ({ confirm }) => {
if (confirm) {
// #ifdef MP-WEIXIN
this.setPageBackGuardShow(false, { suppressLeave: true });
uni.disableAlertBeforeUnload();
// #endif
if (typeof onConfirm === 'function') onConfirm();
} else {
// #ifdef MP-WEIXIN
this.setPageBackGuardShow(this.shouldEnablePageBackGuard, {
suppressLeave: !this.shouldEnablePageBackGuard,
});
// #endif
}
},
});
return;
}
// #ifdef MP-WEIXIN
this.setPageBackGuardShow(false, { suppressLeave: true });
uni.disableAlertBeforeUnload();
// #endif
if (typeof onConfirm === 'function') onConfirm();
},
navigateToSalespersonWorkbench() {
const pages = getCurrentPages();
if (pages.length > 1) {
this.safeNavigateBack(0);
return;
}
// #ifdef MP-WEIXIN
this.setPageBackGuardShow(false, { suppressLeave: true });
uni.disableAlertBeforeUnload();
// #endif
uni.reLaunch({ url: SALESPERSON_HOME_URL });
},
/** 若有弹层打开则关闭第一个并返回 true用于导航返回 / 微信 page-container leave 先关抽屉) */
closeTopOverlayIfAny() {
for (let i = 0; i < PRESCRIPTION_OVERLAY_KEYS.length; i++) {
const k = PRESCRIPTION_OVERLAY_KEYS[i];
if (this[k]) {
this[k] = false;
return true;
}
}
return false;
},
/** 微信小程序:先关闭 page-container 拦截再退栈,避免 navigateBack 被吞 */
safeNavigateBack(delayMs = 0) {
const runNav = () => {
// #ifdef MP-WEIXIN
this.setPageBackGuardShow(false, { suppressLeave: true });
setTimeout(() => {
uni.navigateBack();
}, 16);
// #endif
// #ifndef MP-WEIXIN
uni.navigateBack();
// #endif
};
if (delayMs > 0) {
setTimeout(runNav, delayMs);
} else {
runNav();
}
},
// #ifdef MP-WEIXIN
onPageBackGuardLeave() {
if (this._suppressPageBackGuardLeave) {
this._suppressPageBackGuardLeave = false;
return;
}
if (this.closeTopOverlayIfAny()) {
this.$nextTick(() => {
this.setPageBackGuardShow(this.shouldEnablePageBackGuard, { suppressLeave: true });
this.syncSalespersonTransferUnloadAlert();
});
return;
}
this.setPageBackGuardShow(false);
if (this.isSalespersonTransferMode) {
this.tryLeaveSalespersonTransferPage(() => this.navigateToSalespersonWorkbench());
return;
}
this.safeNavigateBack(0);
},
// #endif
handleCustomBack() {
if (this.closeTopOverlayIfAny()) return;
if (this.isSalespersonTransferMode) {
this.tryLeaveSalespersonTransferPage(() => this.navigateToSalespersonWorkbench());
return;
}
this.safeNavigateBack(0);
}
}
};
</script>
<style lang="scss" scoped>
/* 基础与通用样式 */
.page-bg {
background-color: #F4F6F8;
min-height: 100vh;
}
.safe-area-inset-bottom { padding-bottom: env(safe-area-inset-bottom); }
.mx-32 { margin-left: 32rpx; margin-right: 32rpx; }
.m-x-32 { margin-left: 32rpx; margin-right: 32rpx; }
.m-b-24 { margin-bottom: 24rpx; }
.m-b-32 { margin-bottom: 32rpx; }
.m-b-40 { margin-bottom: 40rpx; }
.m-t-12 { margin-top: 12rpx; }
.m-t-16 { margin-top: 16rpx; }
.m-t-24 { margin-top: 24rpx; }
.m-t-64 { margin-top: 64rpx; }
.m-l-8 { margin-left: 8rpx; }
.m-l-16 { margin-left: 16rpx; }
.m-l-24 { margin-left: 24rpx; }
.m-l-32 { margin-left: 32rpx; }
.m-r-4 { margin-right: 4rpx; }
.m-r-8 { margin-right: 8rpx; }
.m-r-16 { margin-right: 16rpx; }
.m-r-32 { margin-right: 32rpx; }
.p-32 { padding: 32rpx; }
.p-b-40 { padding-bottom: 40rpx; }
.p-x-32 { padding-left: 32rpx; padding-right: 32rpx; }
.pt-20 { padding-top: 20rpx; }
.pt-32 { padding-top: 32rpx; }
/* 字体与颜色层级 */
.fs-24 { font-size: 24rpx; }
.fs-26 { font-size: 26rpx; }
.fs-28 { font-size: 28rpx; }
.fs-30 { font-size: 30rpx; }
.fs-32 { font-size: 32rpx; }
.fs-48 { font-size: 48rpx; }
.font-bold { font-weight: bold; }
.color-title { color: #2A2E35; }
.color-sub { color: #8A92A3; }
.color-primary { color: #00A88A; }
.color-danger { color: #FF4D4F; }
.color-price { color: #F53F3F; }
.price-link { color: #6ACDBB; text-decoration: underline; }
.text-gray { color: #6C7380; }
.white { background: #fff; }
.radius-12 { border-radius: 12rpx; }
.shadow-sm { box-shadow: 0 4rpx 12rpx rgba(0,0,0,0.03); }
.border-t-dashed { border-top: 1px dashed #E2E8F0; }
.line-clamp-2 {
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 2;
overflow: hidden;
}
.w-70 { width: 70%; }
/* Flex 布局辅助 */
.flex { display: flex; }
.flex-wrap { flex-wrap: wrap; }
.flex-row { display: flex; flex-direction: row; }
.flex-col { display: flex; flex-direction: column; }
.flex-jus-sp { justify-content: space-between; }
.flex-ali-center { align-items: center; }
.flex-ali-start { align-items: flex-start; }
.flex-jus-center { justify-content: center; }
.flex-1 { flex: 1; }
.ac { text-align: center; }
/* 定制化组件样式 */
.rp-icon {
background: #2A2E35;
color: #fff;
font-size: 20rpx;
font-weight: bold;
padding: 4rpx 10rpx;
border-radius: 6rpx 2rpx 6rpx 2rpx;
}
.tabs {
background: #fff;
border-bottom: 1px solid #F0F2F5;
}
/* 现代卡片设计 (西药 / 产品) */
.medicine-card {
background: #fff;
border-radius: 16rpx;
padding: 24rpx;
margin-bottom: 24rpx;
box-shadow: 0 4rpx 16rpx rgba(42, 46, 53, 0.04);
position: relative;
overflow: hidden;
}
.drug-image-modern {
width: 140rpx;
height: 140rpx;
border-radius: 12rpx;
background-color: #F7F8FA;
flex-shrink: 0;
border: 1px solid #F0F2F5;
}
.usage-capsule {
display: inline-flex;
align-items: center;
background: #E8F6F4;
padding: 8rpx 16rpx;
border-radius: 6rpx;
}
.card-actions {
margin-top: 24rpx;
padding-top: 20rpx;
border-top: 1px dashed #F0F2F5;
}
.action-btn-text {
display: flex;
align-items: center;
font-size: 26rpx;
&.danger { color: #8A92A3; } /* 弱化删除,避免误触 */
}
.action-btn-ghost {
padding: 10rpx 32rpx;
border-radius: 30rpx;
font-size: 26rpx;
font-weight: bold;
&.primary {
color: #00A88A;
background: #E8F6F4;
}
}
/* 中药极简高密度标签云 */
.tcm-tag-cloud {
display: flex;
flex-wrap: wrap;
gap: 16rpx;
}
.tcm-tag {
display: inline-flex;
align-items: center;
background: #F7F8FA;
padding: 12rpx 20rpx;
border-radius: 8rpx;
border: 1px solid #EBEFF5;
transition: all 0.2s;
.tcm-name {
font-size: 28rpx;
color: #2A2E35;
font-weight: 500;
}
.tcm-weight {
font-size: 26rpx;
color: #00A88A;
margin-left: 12rpx;
}
.tcm-margin {
font-size: 22rpx;
color: #ff9800;
margin-left: 8rpx;
font-weight: bold;
}
}
/* 简单产品精美步进器 */
.modern-stepper {
background: #F7F8FA;
border-radius: 8rpx;
overflow: hidden;
border: 1px solid #E2E8F0;
.stepper-btn {
width: 50rpx;
height: 50rpx;
display: flex;
align-items: center;
justify-content: center;
color: #6C7380;
font-size: 32rpx;
font-weight: bold;
background: #fff;
}
.stepper-val {
min-width: 60rpx;
text-align: center;
color: #2A2E35;
font-weight: bold;
}
}
/* 底部操作栏(恢复原状) */
.register-mode-hint {
padding: 16rpx 24rpx;
background: #f0faf8;
border-radius: 12rpx;
border: 1rpx solid #d4efe8;
}
.register-mode-hint__text {
font-size: 24rpx;
color: #008771;
line-height: 1.5;
}
.special-rx-card {
padding: 20rpx 24rpx;
background: #fffaf5;
border-radius: 12rpx;
border: 1rpx solid #ffe0c2;
}
.special-rx-card__inner {
width: 100%;
}
.special-rx-card__action {
width: 100%;
}
.bottom {
position: fixed;
bottom: 0;
left: 0;
right: 0;
padding: 32rpx;
background: #fff;
border-top: 1px solid #f0f0f0;
z-index: 99;
display: flex;
flex-direction: row;
align-items: center;
justify-content: space-between;
}
.btnText {
color: #6ACDBB;
font-size: 28rpx;
padding: 0 32rpx;
flex-shrink: 0;
}
</style>