feat: 病历模块优化
This commit is contained in:
@@ -521,7 +521,7 @@ export function clearMedicalRecordApi(data) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
/** 引用最近病历 */
|
/** 引用最近病历(兼容旧入口,优先用列表抽屉) */
|
||||||
export function latestMedicalRecordApi(data) {
|
export function latestMedicalRecordApi(data) {
|
||||||
return req.request({
|
return req.request({
|
||||||
url: '/newApi/doctor-reception-wx/latest-medical-record',
|
url: '/newApi/doctor-reception-wx/latest-medical-record',
|
||||||
@@ -530,6 +530,15 @@ export function latestMedicalRecordApi(data) {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 按就诊人拉取病历列表(引用历史抽屉) */
|
||||||
|
export function listMedicalRecordByPatientApi(data) {
|
||||||
|
return req.request({
|
||||||
|
url: '/newApi/doctor-reception-wx/list-medical-record-by-patient',
|
||||||
|
method: 'GET',
|
||||||
|
data
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
/** 病历词条检索 */
|
/** 病历词条检索 */
|
||||||
export function searchMedicalRecordEntryApi(data) {
|
export function searchMedicalRecordEntryApi(data) {
|
||||||
return req.request({
|
return req.request({
|
||||||
|
|||||||
@@ -6,12 +6,6 @@
|
|||||||
模板禁止 ?? ?. 与 :class 方法调用
|
模板禁止 ?? ?. 与 :class 方法调用
|
||||||
-->
|
-->
|
||||||
<view class="mr-panel">
|
<view class="mr-panel">
|
||||||
<!-- 三个操作按钮吸顶,滚动字段时始终可见 -->
|
|
||||||
<view class="mr-actions flex-row flex-wrap">
|
|
||||||
<view class="mr-btn danger" @click="onClear">清空病历</view>
|
|
||||||
<view class="mr-btn" @click="onCite">引用历史</view>
|
|
||||||
<view class="mr-btn primary" @click="onSave">保存病历</view>
|
|
||||||
</view>
|
|
||||||
<scroll-view scroll-y class="mr-scroll" :enable-back-to-top="true">
|
<scroll-view scroll-y class="mr-scroll" :enable-back-to-top="true">
|
||||||
<view class="mr-scroll-inner">
|
<view class="mr-scroll-inner">
|
||||||
<view class="mr-field">
|
<view class="mr-field">
|
||||||
@@ -114,28 +108,68 @@
|
|||||||
<view class="mr-entry-row" @click="openEntry(item.code)">
|
<view class="mr-entry-row" @click="openEntry(item.code)">
|
||||||
<text class="color-sub fs-24">选择{{ item.label }}词条</text>
|
<text class="color-sub fs-24">选择{{ item.label }}词条</text>
|
||||||
</view>
|
</view>
|
||||||
<view v-if="item.code === 'physical_exam'" class="vitals m-b-8">
|
<!-- 体征:一段话内联(标签含单位 + 下划线输入,C 端紧凑) -->
|
||||||
<view class="vital-item">
|
<view v-if="item.code === 'physical_exam'" class="vitals-para m-b-8">
|
||||||
<text>体温</text>
|
<view class="vital-chunk">
|
||||||
<u-input v-model="form.temperature" type="digit" placeholder="℃" />
|
<text class="vital-label">体温(°C)</text>
|
||||||
|
<input
|
||||||
|
class="vital-uline"
|
||||||
|
type="digit"
|
||||||
|
:value="form.temperature"
|
||||||
|
placeholder=" "
|
||||||
|
@input="onVitalInput('temperature', $event)"
|
||||||
|
/>
|
||||||
</view>
|
</view>
|
||||||
<view class="vital-item">
|
<view class="vital-chunk">
|
||||||
<text>身高</text>
|
<text class="vital-label">身高(cm)</text>
|
||||||
<u-input v-model="form.height" type="digit" placeholder="cm" />
|
<input
|
||||||
|
class="vital-uline"
|
||||||
|
type="digit"
|
||||||
|
:value="form.height"
|
||||||
|
placeholder=" "
|
||||||
|
@input="onVitalInput('height', $event)"
|
||||||
|
/>
|
||||||
</view>
|
</view>
|
||||||
<view class="vital-item">
|
<view class="vital-chunk">
|
||||||
<text>体重</text>
|
<text class="vital-label">体重(KG)</text>
|
||||||
<u-input v-model="form.weight" type="digit" placeholder="KG" />
|
<input
|
||||||
|
class="vital-uline"
|
||||||
|
type="digit"
|
||||||
|
:value="form.weight"
|
||||||
|
placeholder=" "
|
||||||
|
@input="onVitalInput('weight', $event)"
|
||||||
|
/>
|
||||||
</view>
|
</view>
|
||||||
<view class="vital-item">
|
<view class="vital-chunk">
|
||||||
<text>呼吸</text>
|
<text class="vital-label">呼吸(次/分)</text>
|
||||||
<u-input v-model="form.respiratory_rate" type="number" placeholder="次/分" />
|
<input
|
||||||
|
class="vital-uline vital-uline--sm"
|
||||||
|
type="number"
|
||||||
|
:value="form.respiratory_rate"
|
||||||
|
placeholder=" "
|
||||||
|
@input="onVitalInput('respiratory_rate', $event)"
|
||||||
|
/>
|
||||||
</view>
|
</view>
|
||||||
<view class="vital-item">
|
<view class="vital-chunk">
|
||||||
<text>血压</text>
|
<text class="vital-label">高压 mmHg</text>
|
||||||
<u-input v-model="form.bp_systolic" type="number" placeholder="高" />
|
<input
|
||||||
<text>/</text>
|
class="vital-uline vital-uline--sm"
|
||||||
<u-input v-model="form.bp_diastolic" type="number" placeholder="低" />
|
type="number"
|
||||||
|
:value="form.bp_systolic"
|
||||||
|
placeholder=" "
|
||||||
|
@input="onVitalInput('bp_systolic', $event)"
|
||||||
|
/>
|
||||||
|
</view>
|
||||||
|
<text class="vital-sep">/</text>
|
||||||
|
<view class="vital-chunk">
|
||||||
|
<text class="vital-label">低压 mmHg</text>
|
||||||
|
<input
|
||||||
|
class="vital-uline vital-uline--sm"
|
||||||
|
type="number"
|
||||||
|
:value="form.bp_diastolic"
|
||||||
|
placeholder=" "
|
||||||
|
@input="onVitalInput('bp_diastolic', $event)"
|
||||||
|
/>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
<u-input
|
<u-input
|
||||||
@@ -148,14 +182,95 @@
|
|||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
</scroll-view>
|
</scroll-view>
|
||||||
<!-- 词条选择抽屉:page-container + v-if -->
|
<!-- 底部操作栏:与处方发送栏一致;仅病历 Tab 可见(避免 probe 时 fixed 穿透) -->
|
||||||
<page-container
|
<view v-if="active" class="mr-bottom safe-area-inset-bottom">
|
||||||
v-if="entryShow"
|
<view class="mr-bottom-left flex-row flex-ali-center">
|
||||||
:show="entryShow"
|
<view class="btnText btnText--danger" @click="onClear">清空</view>
|
||||||
position="bottom"
|
<view class="btnText" @click="onOpenCiteDrawer">引用历史</view>
|
||||||
round
|
</view>
|
||||||
@clickoverlay="entryShow = false"
|
<u-button
|
||||||
@afterleave="entryShow = false"
|
:throttle-time="0"
|
||||||
|
shape="circle"
|
||||||
|
:custom-style="saveBtnStyle"
|
||||||
|
@click="onSave"
|
||||||
|
>
|
||||||
|
保存病历
|
||||||
|
</u-button>
|
||||||
|
</view>
|
||||||
|
<!--
|
||||||
|
引用/词条抽屉用 u-popup:本页顶部已有返回拦截 page-container,
|
||||||
|
微信限制一页只能有一个 page-container,不能再套第二个。
|
||||||
|
-->
|
||||||
|
<u-popup
|
||||||
|
v-model="citeShow"
|
||||||
|
mode="bottom"
|
||||||
|
border-radius="24"
|
||||||
|
:safe-area-inset-bottom="true"
|
||||||
|
:mask-close-able="true"
|
||||||
|
height="78%"
|
||||||
|
z-index="10090"
|
||||||
|
@close="closeCiteDrawer"
|
||||||
|
>
|
||||||
|
<view class="cite-drawer">
|
||||||
|
<view class="entry-title-row">
|
||||||
|
<text class="entry-title">引用历史病历</text>
|
||||||
|
<text class="entry-count">共 {{ citeList.length }} 条</text>
|
||||||
|
</view>
|
||||||
|
<scroll-view scroll-y class="cite-scroll">
|
||||||
|
<view v-if="citeLoading" class="p-32 color-sub">加载中…</view>
|
||||||
|
<block v-else>
|
||||||
|
<view v-if="!citeList.length" class="p-32 color-sub">暂无历史病历</view>
|
||||||
|
<block v-else>
|
||||||
|
<view
|
||||||
|
v-for="row in citeList"
|
||||||
|
:key="row.id"
|
||||||
|
class="cite-item"
|
||||||
|
:class="{ 'cite-item--active': citeActiveId == row.id }"
|
||||||
|
@click="onSelectCite(row)"
|
||||||
|
>
|
||||||
|
<view class="cite-item__title">{{ row._title }}</view>
|
||||||
|
<view class="cite-item__meta flex-row flex-wrap">
|
||||||
|
<text class="cite-tag">挂号 #{{ row.register_id || '-' }}</text>
|
||||||
|
<text v-if="row._isCurrent" class="cite-tag cite-tag--warn">本次挂号</text>
|
||||||
|
<text class="cite-time">{{ row.created_at || '—' }}</text>
|
||||||
|
</view>
|
||||||
|
<view v-if="row.chief_complaint" class="cite-item__sub">主诉:{{ row.chief_complaint }}</view>
|
||||||
|
</view>
|
||||||
|
</block>
|
||||||
|
</block>
|
||||||
|
</scroll-view>
|
||||||
|
<view v-if="citeActiveRecord" class="cite-preview">
|
||||||
|
<text class="cite-preview__label">诊断</text>
|
||||||
|
<text class="cite-preview__val">{{ citeActiveRecord.diagnosis || '—' }}</text>
|
||||||
|
<text class="cite-preview__label">医嘱</text>
|
||||||
|
<text class="cite-preview__val">{{ citeActiveRecord.doctor_order || '—' }}</text>
|
||||||
|
<text
|
||||||
|
v-if="citeActiveRecord._isCurrent"
|
||||||
|
class="cite-preview__tip"
|
||||||
|
>当前选中的是本次挂号病历,引用后会覆盖编辑区内容</text>
|
||||||
|
</view>
|
||||||
|
<view class="cite-footer safe-area-inset-bottom">
|
||||||
|
<u-button
|
||||||
|
:throttle-time="0"
|
||||||
|
shape="circle"
|
||||||
|
:disabled="!citeActiveId"
|
||||||
|
:custom-style="citeConfirmStyle"
|
||||||
|
@click="onConfirmCite"
|
||||||
|
>
|
||||||
|
引用此病历
|
||||||
|
</u-button>
|
||||||
|
</view>
|
||||||
|
</view>
|
||||||
|
</u-popup>
|
||||||
|
<u-popup
|
||||||
|
v-model="entryShow"
|
||||||
|
mode="bottom"
|
||||||
|
border-radius="24"
|
||||||
|
:safe-area-inset-bottom="true"
|
||||||
|
:mask-close-able="true"
|
||||||
|
height="70%"
|
||||||
|
z-index="10090"
|
||||||
|
@close="closeEntryDrawer"
|
||||||
>
|
>
|
||||||
<view class="entry-drawer">
|
<view class="entry-drawer">
|
||||||
<view class="entry-title-row">
|
<view class="entry-title-row">
|
||||||
@@ -168,6 +283,7 @@
|
|||||||
:custom-style="entrySearchStyle"
|
:custom-style="entrySearchStyle"
|
||||||
@input="onEntryKeywordInput"
|
@input="onEntryKeywordInput"
|
||||||
/>
|
/>
|
||||||
|
<scroll-view scroll-y class="entry-scroll">
|
||||||
<view
|
<view
|
||||||
v-for="row in entryList"
|
v-for="row in entryList"
|
||||||
:key="row.id"
|
:key="row.id"
|
||||||
@@ -176,7 +292,7 @@
|
|||||||
>
|
>
|
||||||
<view class="entry-row-top">
|
<view class="entry-row-top">
|
||||||
<view class="fs-28 font-bold entry-row-name">
|
<view class="fs-28 font-bold entry-row-name">
|
||||||
<block v-for="(p, pi) in row._titleParts" :key="pi">
|
<block v-for="(p, pi) in (row._titleParts || [])" :key="pi">
|
||||||
<text v-if="p.hit" class="hit-text">{{ p.text }}</text>
|
<text v-if="p.hit" class="hit-text">{{ p.text }}</text>
|
||||||
<text v-else>{{ p.text }}</text>
|
<text v-else>{{ p.text }}</text>
|
||||||
</block>
|
</block>
|
||||||
@@ -185,13 +301,13 @@
|
|||||||
</view>
|
</view>
|
||||||
<view v-if="row.pinyin_initials" class="fs-22 color-sub m-t-4">
|
<view v-if="row.pinyin_initials" class="fs-22 color-sub m-t-4">
|
||||||
<text>首拼(全拼):</text>
|
<text>首拼(全拼):</text>
|
||||||
<block v-for="(p, pi) in row._pyParts" :key="pi">
|
<block v-for="(p, pi) in (row._pyParts || [])" :key="pi">
|
||||||
<text v-if="p.hit" class="hit-text">{{ p.text }}</text>
|
<text v-if="p.hit" class="hit-text">{{ p.text }}</text>
|
||||||
<text v-else>{{ p.text }}</text>
|
<text v-else>{{ p.text }}</text>
|
||||||
</block>
|
</block>
|
||||||
</view>
|
</view>
|
||||||
<view class="fs-24 color-sub m-t-8">
|
<view class="fs-24 color-sub m-t-8">
|
||||||
<block v-for="(p, pi) in row._contentParts" :key="pi">
|
<block v-for="(p, pi) in (row._contentParts || [])" :key="pi">
|
||||||
<text v-if="p.hit" class="hit-text">{{ p.text }}</text>
|
<text v-if="p.hit" class="hit-text">{{ p.text }}</text>
|
||||||
<text v-else>{{ p.text }}</text>
|
<text v-else>{{ p.text }}</text>
|
||||||
</block>
|
</block>
|
||||||
@@ -199,8 +315,9 @@
|
|||||||
<text v-if="row.remark" class="fs-22 color-sub">{{ row.remark }}</text>
|
<text v-if="row.remark" class="fs-22 color-sub">{{ row.remark }}</text>
|
||||||
</view>
|
</view>
|
||||||
<view v-if="!entryList.length" class="p-32 color-sub">暂无词条</view>
|
<view v-if="!entryList.length" class="p-32 color-sub">暂无词条</view>
|
||||||
|
</scroll-view>
|
||||||
</view>
|
</view>
|
||||||
</page-container>
|
</u-popup>
|
||||||
</view>
|
</view>
|
||||||
</template>
|
</template>
|
||||||
<script>
|
<script>
|
||||||
@@ -212,7 +329,7 @@ import {
|
|||||||
clearMedicalRecordApi,
|
clearMedicalRecordApi,
|
||||||
getMedicalRecordApi,
|
getMedicalRecordApi,
|
||||||
getTraditionalChineseMedicineJson,
|
getTraditionalChineseMedicineJson,
|
||||||
latestMedicalRecordApi,
|
listMedicalRecordByPatientApi,
|
||||||
saveMedicalRecordApi,
|
saveMedicalRecordApi,
|
||||||
searchMedicalRecordEntryApi,
|
searchMedicalRecordEntryApi,
|
||||||
} from '@/api/reception.js'
|
} from '@/api/reception.js'
|
||||||
@@ -278,6 +395,8 @@ export default {
|
|||||||
medicalAdvice: { type: String, default: '' },
|
medicalAdvice: { type: String, default: '' },
|
||||||
/** 在线复诊:诊断只读,必须点选 */
|
/** 在线复诊:诊断只读,必须点选 */
|
||||||
diagnosisReadonly: { type: Boolean, default: false },
|
diagnosisReadonly: { type: Boolean, default: false },
|
||||||
|
/** 是否处于病历 Tab(控制底部栏;probe 时必须为 false,否则 fixed 会穿透到处方) */
|
||||||
|
active: { type: Boolean, default: false },
|
||||||
},
|
},
|
||||||
data() {
|
data() {
|
||||||
const tcmCaseSubFields = [
|
const tcmCaseSubFields = [
|
||||||
@@ -295,6 +414,23 @@ export default {
|
|||||||
entryKeyword: '',
|
entryKeyword: '',
|
||||||
entrySearchTimer: null,
|
entrySearchTimer: null,
|
||||||
entrySearchStyle: { background: '#f7f8fa', padding: '12rpx 16rpx', marginBottom: '16rpx' },
|
entrySearchStyle: { background: '#f7f8fa', padding: '12rpx 16rpx', marginBottom: '16rpx' },
|
||||||
|
/** 引用历史抽屉 */
|
||||||
|
citeShow: false,
|
||||||
|
citeLoading: false,
|
||||||
|
citeList: [],
|
||||||
|
citeActiveId: 0,
|
||||||
|
saveBtnStyle: {
|
||||||
|
backgroundColor: '#6ACDBB',
|
||||||
|
color: '#fff',
|
||||||
|
height: '86rpx',
|
||||||
|
width: '280rpx',
|
||||||
|
},
|
||||||
|
citeConfirmStyle: {
|
||||||
|
backgroundColor: '#6ACDBB',
|
||||||
|
color: '#fff',
|
||||||
|
height: '86rpx',
|
||||||
|
width: '100%',
|
||||||
|
},
|
||||||
hydrating: false,
|
hydrating: false,
|
||||||
_draftTimer: null,
|
_draftTimer: null,
|
||||||
areaStyle: { background: '#f7f8fa', padding: '16rpx', minHeight: '120rpx' },
|
areaStyle: { background: '#f7f8fa', padding: '16rpx', minHeight: '120rpx' },
|
||||||
@@ -334,6 +470,16 @@ export default {
|
|||||||
effectivePatientId() {
|
effectivePatientId() {
|
||||||
return Number(this.userPatientId || this.form.user_patient_id || 0)
|
return Number(this.userPatientId || this.form.user_patient_id || 0)
|
||||||
},
|
},
|
||||||
|
/** 引用抽屉当前选中记录 */
|
||||||
|
citeActiveRecord() {
|
||||||
|
const id = Number(this.citeActiveId || 0)
|
||||||
|
if (!id) return null
|
||||||
|
const list = this.citeList || []
|
||||||
|
for (let i = 0; i < list.length; i++) {
|
||||||
|
if (Number(list[i].id) === id) return list[i]
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
},
|
||||||
},
|
},
|
||||||
watch: {
|
watch: {
|
||||||
registerId: {
|
registerId: {
|
||||||
@@ -356,8 +502,36 @@ export default {
|
|||||||
this.schedulePersistDraft()
|
this.schedulePersistDraft()
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
|
/** 抽屉开合同步给父页,用于 page-container 返回拦截 */
|
||||||
|
citeShow() {
|
||||||
|
this.emitOverlayChange()
|
||||||
|
},
|
||||||
|
entryShow() {
|
||||||
|
this.emitOverlayChange()
|
||||||
|
},
|
||||||
},
|
},
|
||||||
methods: {
|
methods: {
|
||||||
|
/**
|
||||||
|
* 通知父页:病历侧是否有弹层打开(与处方弹层共用返回拦截)
|
||||||
|
*/
|
||||||
|
emitOverlayChange() {
|
||||||
|
this.$emit('overlay-change', !!(this.citeShow || this.entryShow))
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* 供父页返回键先关抽屉(不退开方页)
|
||||||
|
*/
|
||||||
|
closeOverlays() {
|
||||||
|
this.citeShow = false
|
||||||
|
this.entryShow = false
|
||||||
|
this.entryTcmType = ''
|
||||||
|
},
|
||||||
|
closeCiteDrawer() {
|
||||||
|
this.citeShow = false
|
||||||
|
},
|
||||||
|
closeEntryDrawer() {
|
||||||
|
this.entryShow = false
|
||||||
|
this.entryTcmType = ''
|
||||||
|
},
|
||||||
emitDiagnosis() {
|
emitDiagnosis() {
|
||||||
if (this.diagnosisReadonly) return
|
if (this.diagnosisReadonly) return
|
||||||
this.$emit('update:diagnosis', this.localDiagnosis || '')
|
this.$emit('update:diagnosis', this.localDiagnosis || '')
|
||||||
@@ -373,6 +547,14 @@ export default {
|
|||||||
onOpenDoctorOrder() {
|
onOpenDoctorOrder() {
|
||||||
this.$emit('open-doctor-order')
|
this.$emit('open-doctor-order')
|
||||||
},
|
},
|
||||||
|
/**
|
||||||
|
* 体征原生 input 回填(不用 u-input,避免撑满成表单行)
|
||||||
|
*/
|
||||||
|
onVitalInput(field, e) {
|
||||||
|
const val = e && e.detail ? e.detail.value : ''
|
||||||
|
this.form[field] = val
|
||||||
|
this.schedulePersistDraft()
|
||||||
|
},
|
||||||
schedulePersistDraft() {
|
schedulePersistDraft() {
|
||||||
if (this.hydrating || !this.registerId) return
|
if (this.hydrating || !this.registerId) return
|
||||||
if (this._draftTimer) clearTimeout(this._draftTimer)
|
if (this._draftTimer) clearTimeout(this._draftTimer)
|
||||||
@@ -468,22 +650,87 @@ export default {
|
|||||||
PrescriptionStorage.clearMedicalRecordDraft(this.registerId)
|
PrescriptionStorage.clearMedicalRecordDraft(this.registerId)
|
||||||
uni.showToast({ title: '已清空', icon: 'none' })
|
uni.showToast({ title: '已清空', icon: 'none' })
|
||||||
},
|
},
|
||||||
async onCite() {
|
/**
|
||||||
|
* 打开引用历史抽屉:拉列表,默认选中非本次挂号的最近一条
|
||||||
|
*/
|
||||||
|
async onOpenCiteDrawer() {
|
||||||
const patientId = this.effectivePatientId
|
const patientId = this.effectivePatientId
|
||||||
if (!patientId) {
|
if (!patientId) {
|
||||||
uni.showToast({ title: '无就诊人', icon: 'none' })
|
uni.showToast({ title: '无就诊人', icon: 'none' })
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
const res = await latestMedicalRecordApi({
|
this.citeShow = true
|
||||||
|
this.citeLoading = true
|
||||||
|
this.citeList = []
|
||||||
|
this.citeActiveId = 0
|
||||||
|
try {
|
||||||
|
const res = await listMedicalRecordByPatientApi({
|
||||||
user_patient_id: patientId,
|
user_patient_id: patientId,
|
||||||
register_id: this.registerId,
|
|
||||||
store_id: this.storeId || undefined,
|
store_id: this.storeId || undefined,
|
||||||
|
limit: 50,
|
||||||
})
|
})
|
||||||
const data = (res && res.data) || res
|
const list = this.unwrapCiteList(res)
|
||||||
|
const exclude = Number(this.registerId || 0)
|
||||||
|
this.citeList = list.map((row) => {
|
||||||
|
const dx = String((row && row.diagnosis) || '').trim()
|
||||||
|
let title = '挂号 #' + ((row && row.register_id) || '-')
|
||||||
|
if (dx) {
|
||||||
|
title = dx.length > 28 ? dx.slice(0, 28) + '…' : dx
|
||||||
|
}
|
||||||
|
return Object.assign({}, row, {
|
||||||
|
_title: title,
|
||||||
|
_isCurrent: exclude > 0 && Number(row.register_id) === exclude,
|
||||||
|
})
|
||||||
|
})
|
||||||
|
this.citeActiveId = this.pickDefaultCiteId(this.citeList)
|
||||||
|
} catch (e) {
|
||||||
|
this.citeList = []
|
||||||
|
this.citeActiveId = 0
|
||||||
|
const msg = (e && e.message) || (e && e.msg) || '加载失败'
|
||||||
|
uni.showToast({ title: String(msg), icon: 'none' })
|
||||||
|
} finally {
|
||||||
|
this.citeLoading = false
|
||||||
|
}
|
||||||
|
},
|
||||||
|
/** Laravel jok 在 result;兼容 data / 直接数组 */
|
||||||
|
unwrapCiteList(res) {
|
||||||
|
if (Array.isArray(res)) return res
|
||||||
|
if (!res || typeof res !== 'object') return []
|
||||||
|
if (Array.isArray(res.result)) return res.result
|
||||||
|
if (Array.isArray(res.data)) return res.data
|
||||||
|
if (Array.isArray(res.list)) return res.list
|
||||||
|
if (res.result && Array.isArray(res.result.list)) return res.result.list
|
||||||
|
return []
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* 默认选中:优先「非当前挂号」最近一条,否则第一条
|
||||||
|
*/
|
||||||
|
pickDefaultCiteId(rows) {
|
||||||
|
if (!rows || !rows.length) return 0
|
||||||
|
const exclude = Number(this.registerId || 0)
|
||||||
|
if (exclude > 0) {
|
||||||
|
for (let i = 0; i < rows.length; i++) {
|
||||||
|
if (Number(rows[i].register_id) !== exclude) {
|
||||||
|
return Number(rows[i].id)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return Number(rows[0].id)
|
||||||
|
},
|
||||||
|
onSelectCite(row) {
|
||||||
|
if (!row) return
|
||||||
|
this.citeActiveId = Number(row.id)
|
||||||
|
},
|
||||||
|
/**
|
||||||
|
* 确认引用:把选中病历回填到表单(不直接调 latest)
|
||||||
|
*/
|
||||||
|
onConfirmCite() {
|
||||||
|
const data = this.citeActiveRecord
|
||||||
if (!data) {
|
if (!data) {
|
||||||
uni.showToast({ title: '暂无历史病历', icon: 'none' })
|
uni.showToast({ title: '请选择病历', icon: 'none' })
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
const patientId = this.effectivePatientId
|
||||||
this.form = Object.assign(emptyForm(), data, {
|
this.form = Object.assign(emptyForm(), data, {
|
||||||
user_patient_id: patientId,
|
user_patient_id: patientId,
|
||||||
})
|
})
|
||||||
@@ -496,6 +743,7 @@ export default {
|
|||||||
this.$emit('update:medicalAdvice', data.doctor_order)
|
this.$emit('update:medicalAdvice', data.doctor_order)
|
||||||
}
|
}
|
||||||
this.persistDraft()
|
this.persistDraft()
|
||||||
|
this.citeShow = false
|
||||||
uni.showToast({ title: '已引用', icon: 'success' })
|
uni.showToast({ title: '已引用', icon: 'success' })
|
||||||
},
|
},
|
||||||
async openEntry(fieldCode) {
|
async openEntry(fieldCode) {
|
||||||
@@ -576,9 +824,9 @@ export default {
|
|||||||
)
|
)
|
||||||
this.entryList = ranked.map((row) =>
|
this.entryList = ranked.map((row) =>
|
||||||
Object.assign({}, row, {
|
Object.assign({}, row, {
|
||||||
_titleParts: splitHighlight(row.title || '', kw),
|
_titleParts: splitHighlight(row.title || '', kw) || [],
|
||||||
_contentParts: splitHighlight(row.content || '', kw),
|
_contentParts: splitHighlight(row.content || '', kw) || [],
|
||||||
_pyParts: splitHighlight(row.pinyin_initials || '', kw),
|
_pyParts: splitHighlight(row.pinyin_initials || '', kw) || [],
|
||||||
}),
|
}),
|
||||||
)
|
)
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
@@ -673,35 +921,40 @@ export default {
|
|||||||
padding: 0;
|
padding: 0;
|
||||||
background: #fff;
|
background: #fff;
|
||||||
}
|
}
|
||||||
.mr-actions {
|
|
||||||
flex-shrink: 0;
|
|
||||||
gap: 16rpx;
|
|
||||||
padding: 20rpx 32rpx;
|
|
||||||
background: #fff;
|
|
||||||
border-bottom: 1px solid #eee;
|
|
||||||
z-index: 2;
|
|
||||||
}
|
|
||||||
.mr-scroll {
|
.mr-scroll {
|
||||||
flex: 1;
|
flex: 1;
|
||||||
height: 0;
|
height: 0;
|
||||||
min-height: 0;
|
min-height: 0;
|
||||||
}
|
}
|
||||||
.mr-scroll-inner {
|
.mr-scroll-inner {
|
||||||
padding: 24rpx 32rpx 48rpx;
|
padding: 24rpx 32rpx 200rpx;
|
||||||
}
|
}
|
||||||
.mr-btn {
|
.mr-bottom {
|
||||||
padding: 12rpx 24rpx;
|
position: fixed;
|
||||||
background: #f0f0f0;
|
bottom: 0;
|
||||||
border-radius: 8rpx;
|
left: 0;
|
||||||
font-size: 24rpx;
|
right: 0;
|
||||||
|
padding: 24rpx 32rpx;
|
||||||
|
background: #fff;
|
||||||
|
border-top: 1px solid #f0f0f0;
|
||||||
|
z-index: 99;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
|
align-items: center;
|
||||||
|
justify-content: space-between;
|
||||||
}
|
}
|
||||||
.mr-btn.primary {
|
.mr-bottom-left {
|
||||||
background: #6acdbb;
|
flex: 1;
|
||||||
color: #fff;
|
min-width: 0;
|
||||||
}
|
}
|
||||||
.mr-btn.danger {
|
.btnText {
|
||||||
background: #ffecec;
|
color: #6ACDBB;
|
||||||
color: #e54d42;
|
font-size: 28rpx;
|
||||||
|
padding: 0 24rpx;
|
||||||
|
flex-shrink: 0;
|
||||||
|
}
|
||||||
|
.btnText--danger {
|
||||||
|
color: #FC3636;
|
||||||
}
|
}
|
||||||
.mr-field {
|
.mr-field {
|
||||||
margin-bottom: 24rpx;
|
margin-bottom: 24rpx;
|
||||||
@@ -712,22 +965,153 @@ export default {
|
|||||||
background: #f7f8fa;
|
background: #f7f8fa;
|
||||||
border-radius: 8rpx;
|
border-radius: 8rpx;
|
||||||
}
|
}
|
||||||
.vitals {
|
.vitals-para {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
flex-direction: row;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
gap: 12rpx;
|
align-items: baseline;
|
||||||
|
padding: 20rpx 24rpx;
|
||||||
|
background: #f7faf9;
|
||||||
|
border-radius: 16rpx;
|
||||||
|
border: 1rpx solid #e6f2ef;
|
||||||
|
column-gap: 20rpx;
|
||||||
|
row-gap: 16rpx;
|
||||||
|
line-height: 1.8;
|
||||||
}
|
}
|
||||||
.vital-item {
|
.vital-chunk {
|
||||||
display: flex;
|
display: flex;
|
||||||
align-items: center;
|
flex-direction: row;
|
||||||
|
align-items: baseline;
|
||||||
|
flex-shrink: 0;
|
||||||
gap: 8rpx;
|
gap: 8rpx;
|
||||||
|
}
|
||||||
|
.vital-label {
|
||||||
|
flex-shrink: 0;
|
||||||
font-size: 24rpx;
|
font-size: 24rpx;
|
||||||
|
color: #8a9399;
|
||||||
|
}
|
||||||
|
.vital-sep {
|
||||||
|
flex-shrink: 0;
|
||||||
|
font-size: 26rpx;
|
||||||
|
color: #b0b6c2;
|
||||||
|
padding: 0 2rpx;
|
||||||
|
}
|
||||||
|
.vital-uline {
|
||||||
|
width: 88rpx;
|
||||||
|
height: 44rpx;
|
||||||
|
padding: 0 4rpx;
|
||||||
|
font-size: 28rpx;
|
||||||
|
color: #2a2e35;
|
||||||
|
text-align: center;
|
||||||
|
background: transparent;
|
||||||
|
border: none;
|
||||||
|
border-bottom: 2rpx solid #d5dde3;
|
||||||
|
border-radius: 0;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
.vital-uline--sm {
|
||||||
|
width: 72rpx;
|
||||||
|
}
|
||||||
|
.cite-drawer {
|
||||||
|
height: 100%;
|
||||||
|
background: #fff;
|
||||||
|
padding: 32rpx 32rpx 0;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
.cite-scroll {
|
||||||
|
flex: 1;
|
||||||
|
height: 0;
|
||||||
|
min-height: 200rpx;
|
||||||
|
}
|
||||||
|
.cite-item {
|
||||||
|
padding: 20rpx 24rpx;
|
||||||
|
margin-bottom: 16rpx;
|
||||||
|
border-radius: 12rpx;
|
||||||
|
background: #f7f8fa;
|
||||||
|
border: 2rpx solid transparent;
|
||||||
|
}
|
||||||
|
.cite-item--active {
|
||||||
|
border-color: #6ACDBB;
|
||||||
|
background: rgba(106, 205, 187, 0.12);
|
||||||
|
}
|
||||||
|
.cite-item__title {
|
||||||
|
font-size: 28rpx;
|
||||||
|
font-weight: bold;
|
||||||
|
color: #333;
|
||||||
|
word-break: break-all;
|
||||||
|
}
|
||||||
|
.cite-item__meta {
|
||||||
|
margin-top: 10rpx;
|
||||||
|
gap: 12rpx;
|
||||||
|
align-items: center;
|
||||||
|
}
|
||||||
|
.cite-tag {
|
||||||
|
font-size: 22rpx;
|
||||||
|
color: #666;
|
||||||
|
background: #eee;
|
||||||
|
padding: 4rpx 12rpx;
|
||||||
|
border-radius: 8rpx;
|
||||||
|
}
|
||||||
|
.cite-tag--warn {
|
||||||
|
color: #d46b08;
|
||||||
|
background: #fff7e6;
|
||||||
|
}
|
||||||
|
.cite-time {
|
||||||
|
font-size: 22rpx;
|
||||||
|
color: #999;
|
||||||
|
}
|
||||||
|
.cite-item__sub {
|
||||||
|
margin-top: 8rpx;
|
||||||
|
font-size: 24rpx;
|
||||||
|
color: #666;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
.cite-preview {
|
||||||
|
margin-top: 8rpx;
|
||||||
|
padding: 16rpx 0;
|
||||||
|
border-top: 1px solid #eee;
|
||||||
|
max-height: 160rpx;
|
||||||
|
overflow: hidden;
|
||||||
|
}
|
||||||
|
.cite-preview__label {
|
||||||
|
display: block;
|
||||||
|
font-size: 22rpx;
|
||||||
|
color: #999;
|
||||||
|
margin-top: 8rpx;
|
||||||
|
}
|
||||||
|
.cite-preview__val {
|
||||||
|
display: block;
|
||||||
|
font-size: 26rpx;
|
||||||
|
color: #333;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
}
|
||||||
|
.cite-preview__tip {
|
||||||
|
display: block;
|
||||||
|
margin-top: 12rpx;
|
||||||
|
font-size: 22rpx;
|
||||||
|
color: #d46b08;
|
||||||
|
}
|
||||||
|
.cite-footer {
|
||||||
|
padding: 24rpx 0 32rpx;
|
||||||
}
|
}
|
||||||
.entry-drawer {
|
.entry-drawer {
|
||||||
|
height: 100%;
|
||||||
background: #fff;
|
background: #fff;
|
||||||
max-height: 70vh;
|
|
||||||
padding: 32rpx;
|
padding: 32rpx;
|
||||||
overflow-y: auto;
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
box-sizing: border-box;
|
||||||
|
}
|
||||||
|
.entry-scroll {
|
||||||
|
flex: 1;
|
||||||
|
height: 0;
|
||||||
|
min-height: 200rpx;
|
||||||
}
|
}
|
||||||
.entry-title-row {
|
.entry-title-row {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
|||||||
@@ -1,13 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<view>
|
<view>
|
||||||
<!-- #ifdef MP-WEIXIN -->
|
<!-- 返回拦截由开方页唯一 page-container 统一处理,此处不再挂实例 -->
|
||||||
<page-container
|
|
||||||
v-if="show"
|
|
||||||
:show="show"
|
|
||||||
:overlay="false"
|
|
||||||
@leave="handleClose"
|
|
||||||
/>
|
|
||||||
<!-- #endif -->
|
|
||||||
<u-popup
|
<u-popup
|
||||||
v-model="show"
|
v-model="show"
|
||||||
mode="bottom"
|
mode="bottom"
|
||||||
|
|||||||
@@ -1,14 +1,6 @@
|
|||||||
<template>
|
<template>
|
||||||
<view>
|
<view>
|
||||||
<!-- #ifdef MP-WEIXIN -->
|
<!-- 返回拦截由开方页唯一 page-container 统一处理,此处不再挂实例 -->
|
||||||
<!-- 用 page-container 防止右滑误退出开方页 -->
|
|
||||||
<page-container
|
|
||||||
v-if="show"
|
|
||||||
:show="show"
|
|
||||||
:overlay="false"
|
|
||||||
@leave="handleClose"
|
|
||||||
/>
|
|
||||||
<!-- #endif -->
|
|
||||||
<u-popup
|
<u-popup
|
||||||
v-model="show"
|
v-model="show"
|
||||||
mode="bottom"
|
mode="bottom"
|
||||||
|
|||||||
@@ -3,15 +3,37 @@
|
|||||||
<view class="safe-area-inset-bottom page-bg page-flex">
|
<view class="safe-area-inset-bottom page-bg page-flex">
|
||||||
<!-- #ifdef MP-WEIXIN -->
|
<!-- #ifdef MP-WEIXIN -->
|
||||||
<page-container
|
<page-container
|
||||||
:if="pageBackGuardShow"
|
:show="pageBackGuardShow"
|
||||||
:overlay="false"
|
:overlay="false"
|
||||||
@leave="onPageBackGuardLeave"
|
@leave="onPageBackGuardLeave"
|
||||||
/>
|
/>
|
||||||
<!-- #endif -->
|
<!-- #endif -->
|
||||||
<!-- 导航栏 -->
|
<!-- 导航栏:background 用 data 对象,避免模板字面量在小程序渲染层报 not iterable -->
|
||||||
<u-navbar class="navbar rx-flex-fixed" :is-back="true" :title="navbarTitle" :custom-back="boundCustomBack" title-color="#000" background="{ background: '#fff' }">
|
<u-navbar
|
||||||
|
class="navbar rx-flex-fixed"
|
||||||
|
:is-back="true"
|
||||||
|
:title="navbarTitle || '开方'"
|
||||||
|
:custom-back="boundCustomBack"
|
||||||
|
title-color="#000"
|
||||||
|
:background="navbarBackground"
|
||||||
|
>
|
||||||
</u-navbar>
|
</u-navbar>
|
||||||
|
|
||||||
|
<!-- 进入开方页先校验门店开方种类权限 -->
|
||||||
|
<view v-if="pageLoading" class="rx-permission-loading flex-col flex-ali-center flex-jus-center">
|
||||||
|
<u-loading mode="circle" size="48" color="#6ACDBB"></u-loading>
|
||||||
|
<text class="rx-permission-loading__text m-t-24">正在校验开方权限...</text>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<view
|
||||||
|
v-else-if="showRxDenied"
|
||||||
|
class="rx-permission-loading flex-col flex-ali-center flex-jus-center"
|
||||||
|
>
|
||||||
|
<text class="rx-permission-loading__text">{{ prescriptionTypeDenyMessage || '当前门店未开通开方权限' }}</text>
|
||||||
|
</view>
|
||||||
|
|
||||||
|
<!-- 用 block 替代 template:微信小程序对 template+v-else 大块切换易触发渲染层 not iterable -->
|
||||||
|
<block v-else>
|
||||||
<!-- 外层:处方 | 病历(VIP) -->
|
<!-- 外层:处方 | 病历(VIP) -->
|
||||||
<view v-if="canUseMedicalRecord" class="outer-tabs rx-flex-fixed flex-row">
|
<view v-if="canUseMedicalRecord" class="outer-tabs rx-flex-fixed flex-row">
|
||||||
<view
|
<view
|
||||||
@@ -32,7 +54,7 @@
|
|||||||
|
|
||||||
<!-- 以下处方区与 HEAD 一致:直接挂在 page-flex 下,禁止再包一层 flex 列(会压矮 swiper) -->
|
<!-- 以下处方区与 HEAD 一致:直接挂在 page-flex 下,禁止再包一层 flex 列(会压矮 swiper) -->
|
||||||
<!-- 处方类型 Tab:与用户端订单列表一致,u-tabs + 下方 swiper 双向同步 -->
|
<!-- 处方类型 Tab:与用户端订单列表一致,u-tabs + 下方 swiper 双向同步 -->
|
||||||
<view v-show="isRxMainVisible" v-if="!isSalespersonTransferMode" class="tabs-wrap shadow-sm rx-flex-fixed">
|
<view v-if="isRxMainVisible && !isSalespersonTransferMode && prescriptionTabsList.length" class="tabs-wrap shadow-sm rx-flex-fixed">
|
||||||
<u-tabs
|
<u-tabs
|
||||||
:list="prescriptionTabsList"
|
:list="prescriptionTabsList"
|
||||||
:is-scroll="true"
|
:is-scroll="true"
|
||||||
@@ -44,13 +66,12 @@
|
|||||||
></u-tabs>
|
></u-tabs>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<view v-show="isRxMainVisible" v-if="!isSalespersonTransferMode && registerModeHint" class="register-mode-hint mx-32 m-t-12 rx-flex-fixed">
|
<view v-if="isRxMainVisible && !isSalespersonTransferMode && registerModeHint" class="register-mode-hint mx-32 m-t-12 rx-flex-fixed">
|
||||||
<text class="register-mode-hint__text">{{ registerModeHint }}</text>
|
<text class="register-mode-hint__text">{{ registerModeHint }}</text>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<view
|
<view
|
||||||
v-show="isRxMainVisible"
|
v-if="isRxMainVisible && !isSalespersonTransferMode && hasSpecialPrescriptionRecord"
|
||||||
v-if="!isSalespersonTransferMode && hasSpecialPrescriptionRecord"
|
|
||||||
class="special-rx-card mx-32 m-t-12 rx-flex-fixed"
|
class="special-rx-card mx-32 m-t-12 rx-flex-fixed"
|
||||||
>
|
>
|
||||||
<view class="special-rx-card__inner flex-row flex-ali-center">
|
<view class="special-rx-card__inner flex-row flex-ali-center">
|
||||||
@@ -101,17 +122,17 @@
|
|||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<view v-show="isRxMainVisible" v-if="!isSalespersonTransferMode && allowInsuranceCategory" class="mx-32 m-t-12 flex flex-ali-center rx-flex-fixed">
|
<view v-if="isRxMainVisible && !isSalespersonTransferMode && allowInsuranceCategory" class="mx-32 m-t-12 flex flex-ali-center rx-flex-fixed">
|
||||||
<u-button size="mini" :type="feeCategory === 1 ? 'success' : 'default'" @click="feeCategory = 1">自费</u-button>
|
<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>
|
<u-button size="mini" class="m-l-16" :type="feeCategory === 2 ? 'success' : 'default'" @click="feeCategory = 2">医保</u-button>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<view v-show="isRxMainVisible" v-if="!isSalespersonTransferMode && activeCategory === 1 && hasSalespersonTransfer" class="mx-32 m-t-12 rx-flex-fixed">
|
<view v-if="isRxMainVisible && !isSalespersonTransferMode && activeCategory === 1 && hasSalespersonTransfer" class="mx-32 m-t-12 rx-flex-fixed">
|
||||||
<u-button size="mini" type="primary" plain @click="openSalespersonTransfer">查看传方</u-button>
|
<u-button size="mini" type="primary" plain @click="openSalespersonTransfer">查看传方</u-button>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!-- 诊断:swiper 外固定(与改 VIP 前一致),线下可手输也可点选;在线复诊必须点选 -->
|
<!-- 诊断:病历 Tab 必须卸载(微信 v-show 藏不住 fixed/flex 固定块) -->
|
||||||
<view v-show="isRxMainVisible" v-if="!isSalespersonTransferMode" class="top white p-32 flex-col m-t-16 radius-12 mx-32 rx-flex-fixed">
|
<view v-if="isRxMainVisible && !isSalespersonTransferMode" class="top white p-32 flex-col m-t-16 radius-12 mx-32 rx-flex-fixed">
|
||||||
<view class="top_title flex-row flex-ali-center">
|
<view class="top_title flex-row flex-ali-center">
|
||||||
<image :src="require('@/static/image/js.png')" class="size-32"></image>
|
<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>
|
<d-text text="临床诊断" className="fs-32 m-l-16 font-bold" color="#333"></d-text>
|
||||||
@@ -144,9 +165,9 @@
|
|||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!-- 滚动内容:swiper 必须是 page-flex 的直接子项,flex:1 + height:0 才能吃满剩余高度 -->
|
<!-- 滚动内容:病历 Tab 用 v-if 卸载,避免微信 v-show 残留 -->
|
||||||
<swiper
|
<swiper
|
||||||
v-show="isRxMainVisible"
|
v-if="isRxMainVisible"
|
||||||
class="rx-order-swiper"
|
class="rx-order-swiper"
|
||||||
:current="swiperCurrent"
|
:current="swiperCurrent"
|
||||||
:disable-touch="isSalespersonTransferMode || swiperCategories.length < 2"
|
:disable-touch="isSalespersonTransferMode || swiperCategories.length < 2"
|
||||||
@@ -319,10 +340,10 @@
|
|||||||
>先煎/后下请在「选择中药」中设置</text>
|
>先煎/后下请在「选择中药」中设置</text>
|
||||||
</view>
|
</view>
|
||||||
<view class="flex-row">
|
<view class="flex-row">
|
||||||
<template v-if="!isSpecialPrescriptionCartLocked">
|
<block v-if="!isSpecialPrescriptionCartLocked">
|
||||||
<text class="fs-26 color-primary m-r-32" @click="handleOpenChineseMedicineModal">修改剂量</text>
|
<text class="fs-26 color-primary m-r-32" @click="handleOpenChineseMedicineModal">修改剂量</text>
|
||||||
<text class="fs-26 color-danger" @click="handleRemoveAllDrugs">清空</text>
|
<text class="fs-26 color-danger" @click="handleRemoveAllDrugs">清空</text>
|
||||||
</template>
|
</block>
|
||||||
<text
|
<text
|
||||||
v-else
|
v-else
|
||||||
class="fs-26 color-danger"
|
class="fs-26 color-danger"
|
||||||
@@ -453,7 +474,7 @@
|
|||||||
<text class="fs-28 color-title font-bold">本店已关闭推广员查看费用</text>
|
<text class="fs-28 color-title font-bold">本店已关闭推广员查看费用</text>
|
||||||
<text class="fs-24 color-sub m-t-8">提交后由接诊医生确认价格</text>
|
<text class="fs-24 color-sub m-t-8">提交后由接诊医生确认价格</text>
|
||||||
</view>
|
</view>
|
||||||
<template v-else>
|
<block v-else>
|
||||||
<view v-if="!isSalespersonTransferMode" class="flex-row flex-jus-sp flex-ali-center m-b-24">
|
<view v-if="!isSalespersonTransferMode" class="flex-row flex-jus-sp flex-ali-center m-b-24">
|
||||||
<text class="fs-28 color-sub">诊疗费</text>
|
<text class="fs-28 color-sub">诊疗费</text>
|
||||||
<view class="flex-row flex-ali-center">
|
<view class="flex-row flex-ali-center">
|
||||||
@@ -507,7 +528,7 @@
|
|||||||
<text class="fs-30 font-bold color-title">合计</text>
|
<text class="fs-30 font-bold color-title">合计</text>
|
||||||
<text class="fs-48 font-bold color-price">¥{{totalPrice}}</text>
|
<text class="fs-48 font-bold color-price">¥{{totalPrice}}</text>
|
||||||
</view>
|
</view>
|
||||||
</template>
|
</block>
|
||||||
</view>
|
</view>
|
||||||
|
|
||||||
<!-- 提示信息(传方模式不展示) -->
|
<!-- 提示信息(传方模式不展示) -->
|
||||||
@@ -521,8 +542,8 @@
|
|||||||
</swiper-item>
|
</swiper-item>
|
||||||
</swiper>
|
</swiper>
|
||||||
|
|
||||||
<!-- 底部操作栏(fixed,不占 flex;须与 swiper 同为 page-flex 直接子项) -->
|
<!-- 底部操作栏:fixed;病历 Tab 必须 v-if 卸载,微信 v-show 对 fixed 无效会串到病历 -->
|
||||||
<view v-show="isRxMainVisible" class="bottom flex-jus-sp flex-ali-center safe-area-inset-bottom">
|
<view v-if="isRxMainVisible" class="bottom flex-jus-sp flex-ali-center safe-area-inset-bottom">
|
||||||
<view
|
<view
|
||||||
v-if="!isSalespersonTransferMode && !isCommonPrescription && supportsCommonPrescriptionTemplate"
|
v-if="!isSalespersonTransferMode && !isCommonPrescription && supportsCommonPrescriptionTemplate"
|
||||||
class="flex-row flex-ali-center"
|
class="flex-row flex-ali-center"
|
||||||
@@ -562,12 +583,14 @@
|
|||||||
:diagnosis="diagnosisText"
|
:diagnosis="diagnosisText"
|
||||||
:medical-advice="medicalAdvice"
|
:medical-advice="medicalAdvice"
|
||||||
:diagnosis-readonly="isOnlineRevisit"
|
:diagnosis-readonly="isOnlineRevisit"
|
||||||
|
:active="isMrMainVisible"
|
||||||
@update:diagnosis="onMrDiagnosisUpdate"
|
@update:diagnosis="onMrDiagnosisUpdate"
|
||||||
@update:medicalAdvice="onMrAdviceUpdate"
|
@update:medicalAdvice="onMrAdviceUpdate"
|
||||||
@open-diagnosis="handleOpenDiagnosisModal"
|
@open-diagnosis="handleOpenDiagnosisModal"
|
||||||
@open-doctor-order="handleOpenDoctorOrderModal"
|
@open-doctor-order="handleOpenDoctorOrderModal"
|
||||||
@vip-ok="onMedicalRecordVipOk"
|
@vip-ok="onMedicalRecordVipOk"
|
||||||
@vip-denied="onMedicalRecordVipDenied"
|
@vip-denied="onMedicalRecordVipDenied"
|
||||||
|
@overlay-change="onMedicalRecordOverlayChange"
|
||||||
/>
|
/>
|
||||||
</view>
|
</view>
|
||||||
</view>
|
</view>
|
||||||
@@ -677,6 +700,7 @@
|
|||||||
@close="closePreSendPriceAdjust"
|
@close="closePreSendPriceAdjust"
|
||||||
@confirm="onPreSendPriceAdjustConfirm"
|
@confirm="onPreSendPriceAdjustConfirm"
|
||||||
/>
|
/>
|
||||||
|
</block>
|
||||||
</view>
|
</view>
|
||||||
</template>
|
</template>
|
||||||
|
|
||||||
@@ -754,6 +778,7 @@ const PRESCRIPTION_OVERLAY_KEYS = [
|
|||||||
'showSaveCommonPrescriptionModal',
|
'showSaveCommonPrescriptionModal',
|
||||||
'showTransferPatientDrawer',
|
'showTransferPatientDrawer',
|
||||||
'showWarehouseSelectDrawer',
|
'showWarehouseSelectDrawer',
|
||||||
|
'showMedicalRecordOverlay',
|
||||||
];
|
];
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
@@ -793,6 +818,13 @@ export default {
|
|||||||
],
|
],
|
||||||
/** 后端下发的默认处方类型;无本地草稿/锁定时使用 */
|
/** 后端下发的默认处方类型;无本地草稿/锁定时使用 */
|
||||||
prescriptionTypeDefault: 2,
|
prescriptionTypeDefault: 2,
|
||||||
|
/** 进入开方页校验种类权限中 */
|
||||||
|
pageLoading: true,
|
||||||
|
/** 门店是否有可开方种类(传方模式跳过) */
|
||||||
|
prescriptionTypeAllowed: true,
|
||||||
|
prescriptionTypeDenyMessage: '',
|
||||||
|
/** u-navbar background,勿在模板写对象字面量 */
|
||||||
|
navbarBackground: { background: '#fff' },
|
||||||
/** swiper / u-tabs 当前下标;默认与 activeCategory=2(西药,列表第 2 项)对齐,避免首屏 Tab/内容错位 */
|
/** swiper / u-tabs 当前下标;默认与 activeCategory=2(西药,列表第 2 项)对齐,避免首屏 Tab/内容错位 */
|
||||||
swiperCurrent: 1,
|
swiperCurrent: 1,
|
||||||
/** 防止 tabs 与 swiper 互相触发形成回路(同订单列表) */
|
/** 防止 tabs 与 swiper 互相触发形成回路(同订单列表) */
|
||||||
@@ -921,6 +953,8 @@ export default {
|
|||||||
transferDrawerSubmitting: false,
|
transferDrawerSubmitting: false,
|
||||||
/** 在线复诊选仓抽屉 */
|
/** 在线复诊选仓抽屉 */
|
||||||
showWarehouseSelectDrawer: false,
|
showWarehouseSelectDrawer: false,
|
||||||
|
/** 病历面板引用/词条抽屉(子组件同步,供返回拦截) */
|
||||||
|
showMedicalRecordOverlay: false,
|
||||||
warehouseSelectRows: [],
|
warehouseSelectRows: [],
|
||||||
/** resolvePharmacyWarehouseMap 的 Promise resolve;false=取消,null=无需选,map=已选 */
|
/** resolvePharmacyWarehouseMap 的 Promise resolve;false=取消,null=无需选,map=已选 */
|
||||||
_warehouseSelectResolve: null,
|
_warehouseSelectResolve: null,
|
||||||
@@ -950,14 +984,20 @@ export default {
|
|||||||
isMrMainVisible() {
|
isMrMainVisible() {
|
||||||
return this.canUseMedicalRecord && this.mainTab === 'mr';
|
return this.canUseMedicalRecord && this.mainTab === 'mr';
|
||||||
},
|
},
|
||||||
|
/** 无开方权限且非传方:展示拒绝态 */
|
||||||
|
showRxDenied() {
|
||||||
|
return !this.prescriptionTypeAllowed && !this.isSalespersonTransferMode;
|
||||||
|
},
|
||||||
/** u-tabs 需要 { name };有 icon_text 时拼到 name 后便于看见角标;始终返回数组避免 list 非数组警告 */
|
/** u-tabs 需要 { name };有 icon_text 时拼到 name 后便于看见角标;始终返回数组避免 list 非数组警告 */
|
||||||
prescriptionTabsList() {
|
prescriptionTabsList() {
|
||||||
const list = this.visiblePrescriptionCategories || [];
|
const list = Array.isArray(this.visiblePrescriptionCategories)
|
||||||
|
? this.visiblePrescriptionCategories
|
||||||
|
: [];
|
||||||
return list.map((c) => {
|
return list.map((c) => {
|
||||||
let name = c.label || '';
|
let name = (c && c.label) || '';
|
||||||
if (c.icon_text) name = `${name} ${c.icon_text}`;
|
if (c && c.icon_text) name = `${name} ${c.icon_text}`;
|
||||||
return { name, value: c.value, icon: c.icon || '' };
|
return { name, value: c ? c.value : 0, icon: (c && c.icon) || '' };
|
||||||
}) || [];
|
});
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* 当前处方类型显示名:简单产品列表等共用区块按 Tab label 展示,
|
* 当前处方类型显示名:简单产品列表等共用区块按 Tab label 展示,
|
||||||
@@ -1222,19 +1262,41 @@ export default {
|
|||||||
},
|
},
|
||||||
// #endif
|
// #endif
|
||||||
async initializePageData() {
|
async initializePageData() {
|
||||||
|
this.pageLoading = true;
|
||||||
try {
|
try {
|
||||||
this.restoreFromLocalStorage();
|
this.restoreFromLocalStorage();
|
||||||
if (this.isSalespersonTransferMode) {
|
if (this.isSalespersonTransferMode) {
|
||||||
await this.loadSalespersonTransferConfig();
|
await this.loadSalespersonTransferConfig();
|
||||||
await this.loadBasicConfigDataForTransfer();
|
await this.loadBasicConfigDataForTransfer();
|
||||||
|
this.prescriptionTypeAllowed = true;
|
||||||
} else {
|
} else {
|
||||||
|
// 先校验开方种类权限,再加载其余数据
|
||||||
|
const allowed = await this.loadPrescriptionTypeOptions();
|
||||||
|
if (!allowed) {
|
||||||
|
this.$toast(this.prescriptionTypeDenyMessage || '当前门店未开通开方权限');
|
||||||
|
setTimeout(() => {
|
||||||
|
uni.navigateBack({ fail: () => {} });
|
||||||
|
}, 500);
|
||||||
|
return;
|
||||||
|
}
|
||||||
await this.loadPatientInfo();
|
await this.loadPatientInfo();
|
||||||
await this.loadRegisterOrderType();
|
await this.loadRegisterOrderType();
|
||||||
await this.loadBasicConfigData();
|
await this.loadBasicConfigData();
|
||||||
await this.loadPrescriptionTypeOptions();
|
|
||||||
await this.loadRegisterStoreInfo();
|
await this.loadRegisterStoreInfo();
|
||||||
// 无 URL/上次切换/草稿时,用后端 default 兜底
|
// 指令 default 压过本地缓存(URL 锁定除外)
|
||||||
this.applyDefaultCategoryFromOptions();
|
this.applyDefaultCategoryFromOptions();
|
||||||
|
// 当前 Tab 不在权限内时强制切到 default
|
||||||
|
if (
|
||||||
|
this.prescriptionCategories.length &&
|
||||||
|
!this.prescriptionCategories.some((c) => Number(c.value) === Number(this.activeCategory))
|
||||||
|
) {
|
||||||
|
const next = Number(this.prescriptionTypeDefault) || Number(this.prescriptionCategories[0].value);
|
||||||
|
this.activeCategory = next;
|
||||||
|
if (this.registerId) {
|
||||||
|
PrescriptionStorage.saveActiveCategory(next, this.registerId);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
this.focusActiveTab();
|
||||||
}
|
}
|
||||||
this.loadCurrentCategoryDrugs();
|
this.loadCurrentCategoryDrugs();
|
||||||
this.syncSwiperCurrentFromCategory();
|
this.syncSwiperCurrentFromCategory();
|
||||||
@@ -1262,6 +1324,11 @@ export default {
|
|||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('初始化页面数据失败:', error);
|
console.error('初始化页面数据失败:', error);
|
||||||
this.$toast('初始化失败,请重试');
|
this.$toast('初始化失败,请重试');
|
||||||
|
} finally {
|
||||||
|
// 延后关掉 loading,避免小程序在同帧大块挂载时触发 clickCheckTask / not iterable
|
||||||
|
this.$nextTick(() => {
|
||||||
|
this.pageLoading = false;
|
||||||
|
});
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
async loadSalespersonTransferConfig() {
|
async loadSalespersonTransferConfig() {
|
||||||
@@ -1297,48 +1364,63 @@ export default {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* 拉取后端处方类型列表与默认选中;失败时保留本地兜底列表
|
* 拉取后端处方类型列表与默认选中;无绑定时返回 false 供页面阻断
|
||||||
|
* 注意:Laravel jok 业务体在 result,需与页面其它接口一样先解包
|
||||||
*/
|
*/
|
||||||
async loadPrescriptionTypeOptions() {
|
async loadPrescriptionTypeOptions() {
|
||||||
try {
|
try {
|
||||||
const params = {};
|
const params = {};
|
||||||
if (this.registerId) params.register_id = this.registerId;
|
if (this.registerId) params.register_id = this.registerId;
|
||||||
|
const storeId = uni.getStorageSync('store_id');
|
||||||
|
if (storeId) params.store_id = storeId;
|
||||||
const res = await getPrescriptionTypeOptionsApi(params);
|
const res = await getPrescriptionTypeOptionsApi(params);
|
||||||
// req 已解包为业务 data:{ default, list }
|
// 与同页其它 newApi 一致:result(Laravel)/ data(Yii)兜底
|
||||||
const data = res || {};
|
const data = (res && (res.result || res.data)) || res || {};
|
||||||
const list = Array.isArray(data.list) ? data.list : [];
|
const list = Array.isArray(data.list) ? data.list : [];
|
||||||
if (list.length) {
|
|
||||||
this.prescriptionCategories = list.map((item) => ({
|
this.prescriptionCategories = list.map((item) => ({
|
||||||
value: Number(item.value),
|
value: Number(item.value),
|
||||||
label: item.label || '',
|
label: item.label || '',
|
||||||
icon: item.icon || '',
|
icon: item.icon || '',
|
||||||
icon_text: item.icon_text || '',
|
icon_text: item.icon_text || '',
|
||||||
}));
|
}));
|
||||||
}
|
const def = Number(data.default) || 0;
|
||||||
const def = Number(data.default);
|
this.prescriptionTypeDefault = def;
|
||||||
if (def) this.prescriptionTypeDefault = def;
|
const allowed = data.allowed !== false && list.length > 0;
|
||||||
|
this.prescriptionTypeAllowed = allowed;
|
||||||
|
this.prescriptionTypeDenyMessage = allowed
|
||||||
|
? ''
|
||||||
|
: (data.message || '当前门店未开通开方权限');
|
||||||
|
return allowed;
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
console.error('加载处方类型失败:', e);
|
console.error('加载处方类型失败:', e);
|
||||||
|
this.prescriptionCategories = [];
|
||||||
|
this.prescriptionTypeAllowed = false;
|
||||||
|
this.prescriptionTypeDenyMessage = '处方类型加载失败,请稍后重试';
|
||||||
|
return false;
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* 开方默认 Tab 兜底:仅当无 URL 锁定、无上次切换分类、无任何药品草稿时,
|
* 开方默认 Tab:后端「指令」default 暂高于本地缓存。
|
||||||
* 使用后端下发的 default。优先级:URL > 上次切换 > 草稿 > 接口 default
|
* 优先级:URL 锁定 > 接口 default(指令)> 本地上次 Tab / 草稿分类
|
||||||
|
* 为什么暂时压过缓存:门店绑定的默认种类要以运营配置为准,避免旧缓存挡住新指令
|
||||||
*/
|
*/
|
||||||
applyDefaultCategoryFromOptions() {
|
applyDefaultCategoryFromOptions() {
|
||||||
if (this.isSalespersonTransferMode || this._categoryLockedByQuery) return;
|
if (this.isSalespersonTransferMode || this._categoryLockedByQuery) return;
|
||||||
if (!this.registerId) return;
|
if (!this.registerId) return;
|
||||||
if (PrescriptionStorage.getActiveCategory(this.registerId)) return;
|
|
||||||
if (PrescriptionStorage.hasDraftWithDrugs(this.registerId)) return;
|
|
||||||
const next = Number(this.prescriptionTypeDefault) || 0;
|
const next = Number(this.prescriptionTypeDefault) || 0;
|
||||||
if (!next || next === this.activeCategory) return;
|
if (!next) return;
|
||||||
|
// 指令种类必须在当前可开列表内
|
||||||
|
const allowed = Array.isArray(this.prescriptionCategories) ? this.prescriptionCategories : [];
|
||||||
|
if (allowed.length && !allowed.some((c) => Number(c.value) === next)) return;
|
||||||
|
if (next === this.activeCategory) {
|
||||||
|
this.focusActiveTab();
|
||||||
|
return;
|
||||||
|
}
|
||||||
this.activeCategory = next;
|
this.activeCategory = next;
|
||||||
PrescriptionStorage.saveActiveCategory(next, this.registerId);
|
PrescriptionStorage.saveActiveCategory(next, this.registerId);
|
||||||
this.focusActiveTab();
|
this.focusActiveTab();
|
||||||
},
|
},
|
||||||
/**
|
/**
|
||||||
* 开方默认 Tab 兜底:仅当无 URL 锁定、无上次切换分类、无任何药品草稿时,
|
|
||||||
* 按诊所类型设中药(1)/西药(2)。优先级:URL > 上次切换 > 草稿 > clinic_type
|
|
||||||
* @deprecated 已由 applyDefaultCategoryFromOptions 替代,保留兼容
|
* @deprecated 已由 applyDefaultCategoryFromOptions 替代,保留兼容
|
||||||
*/
|
*/
|
||||||
applyDefaultCategoryByClinicType() {
|
applyDefaultCategoryByClinicType() {
|
||||||
@@ -1744,6 +1826,12 @@ export default {
|
|||||||
if (this.registerId) {
|
if (this.registerId) {
|
||||||
PrescriptionStorage.saveRxMrTab(tab, this.registerId);
|
PrescriptionStorage.saveRxMrTab(tab, this.registerId);
|
||||||
}
|
}
|
||||||
|
// 切回处方时 swiper 会因 v-if 重新挂载,对齐下标避免错位
|
||||||
|
if (tab === 'rx') {
|
||||||
|
this.$nextTick(() => {
|
||||||
|
this.syncSwiperCurrentFromCategory();
|
||||||
|
});
|
||||||
|
}
|
||||||
},
|
},
|
||||||
/** 从本地恢复外层 Tab */
|
/** 从本地恢复外层 Tab */
|
||||||
restoreMainTab() {
|
restoreMainTab() {
|
||||||
@@ -2983,12 +3071,23 @@ export default {
|
|||||||
for (let i = 0; i < PRESCRIPTION_OVERLAY_KEYS.length; i++) {
|
for (let i = 0; i < PRESCRIPTION_OVERLAY_KEYS.length; i++) {
|
||||||
const k = PRESCRIPTION_OVERLAY_KEYS[i];
|
const k = PRESCRIPTION_OVERLAY_KEYS[i];
|
||||||
if (this[k]) {
|
if (this[k]) {
|
||||||
|
// 病历抽屉状态在子组件内,需先关子组件再清标志
|
||||||
|
if (k === 'showMedicalRecordOverlay') {
|
||||||
|
const panel = this.$refs.medicalRecordPanel;
|
||||||
|
if (panel && typeof panel.closeOverlays === 'function') {
|
||||||
|
panel.closeOverlays();
|
||||||
|
}
|
||||||
|
}
|
||||||
this[k] = false;
|
this[k] = false;
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
},
|
},
|
||||||
|
/** 病历面板抽屉开合 → 同步返回拦截标志 */
|
||||||
|
onMedicalRecordOverlayChange(open) {
|
||||||
|
this.showMedicalRecordOverlay = !!open;
|
||||||
|
},
|
||||||
/** 微信小程序:先关闭 page-container 拦截再退栈,避免 navigateBack 被吞 */
|
/** 微信小程序:先关闭 page-container 拦截再退栈,避免 navigateBack 被吞 */
|
||||||
safeNavigateBack(delayMs = 0) {
|
safeNavigateBack(delayMs = 0) {
|
||||||
const runNav = () => {
|
const runNav = () => {
|
||||||
@@ -3042,6 +3141,17 @@ export default {
|
|||||||
</script>
|
</script>
|
||||||
|
|
||||||
<style lang="scss" scoped>
|
<style lang="scss" scoped>
|
||||||
|
.rx-permission-loading {
|
||||||
|
flex: 1;
|
||||||
|
width: 100%;
|
||||||
|
min-height: 60vh;
|
||||||
|
padding: 48rpx;
|
||||||
|
}
|
||||||
|
.rx-permission-loading__text {
|
||||||
|
font-size: 28rpx;
|
||||||
|
color: #909399;
|
||||||
|
text-align: center;
|
||||||
|
}
|
||||||
/* 基础与通用样式 */
|
/* 基础与通用样式 */
|
||||||
.page-bg {
|
.page-bg {
|
||||||
background-color: #F4F6F8;
|
background-color: #F4F6F8;
|
||||||
@@ -3196,7 +3306,7 @@ export default {
|
|||||||
}
|
}
|
||||||
/* 内部滚动区铺满 swiper;底部留白避开 fixed 操作栏 */
|
/* 内部滚动区铺满 swiper;底部留白避开 fixed 操作栏 */
|
||||||
.rx-order-scroll {
|
.rx-order-scroll {
|
||||||
height: 100%;
|
height: 66vh;
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
padding-bottom: calc(200rpx + env(safe-area-inset-bottom));
|
padding-bottom: calc(200rpx + env(safe-area-inset-bottom));
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user