feat: 病历模块优化

This commit is contained in:
李琦
2026-08-05 16:22:34 +08:00
parent d1b7e75a43
commit f9e871000a
5 changed files with 650 additions and 162 deletions

View File

@@ -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({

View File

@@ -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,39 +283,41 @@
:custom-style="entrySearchStyle" :custom-style="entrySearchStyle"
@input="onEntryKeywordInput" @input="onEntryKeywordInput"
/> />
<view <scroll-view scroll-y class="entry-scroll">
v-for="row in entryList" <view
:key="row.id" v-for="row in entryList"
class="entry-row" :key="row.id"
@click="pickEntry(row)" class="entry-row"
> @click="pickEntry(row)"
<view class="entry-row-top"> >
<view class="fs-28 font-bold entry-row-name"> <view class="entry-row-top">
<block v-for="(p, pi) in row._titleParts" :key="pi"> <view class="fs-28 font-bold entry-row-name">
<block v-for="(p, pi) in (row._titleParts || [])" :key="pi">
<text v-if="p.hit" class="hit-text">{{ p.text }}</text>
<text v-else>{{ p.text }}</text>
</block>
</view>
<text v-if="entryKeyword" class="entry-score">{{ row.match_score || 0 }}%</text>
</view>
<view v-if="row.pinyin_initials" class="fs-22 color-sub m-t-4">
<text>首拼(全拼)</text>
<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>
<text v-if="entryKeyword" class="entry-score">{{ row.match_score || 0 }}%</text> <view class="fs-24 color-sub m-t-8">
<block v-for="(p, pi) in (row._contentParts || [])" :key="pi">
<text v-if="p.hit" class="hit-text">{{ p.text }}</text>
<text v-else>{{ p.text }}</text>
</block>
</view>
<text v-if="row.remark" class="fs-22 color-sub">{{ row.remark }}</text>
</view> </view>
<view v-if="row.pinyin_initials" class="fs-22 color-sub m-t-4"> <view v-if="!entryList.length" class="p-32 color-sub">暂无词条</view>
<text>首拼(全拼)</text> </scroll-view>
<block v-for="(p, pi) in row._pyParts" :key="pi">
<text v-if="p.hit" class="hit-text">{{ p.text }}</text>
<text v-else>{{ p.text }}</text>
</block>
</view>
<view class="fs-24 color-sub m-t-8">
<block v-for="(p, pi) in row._contentParts" :key="pi">
<text v-if="p.hit" class="hit-text">{{ p.text }}</text>
<text v-else>{{ p.text }}</text>
</block>
</view>
<text v-if="row.remark" class="fs-22 color-sub">{{ row.remark }}</text>
</view>
<view v-if="!entryList.length" class="p-32 color-sub">暂无词条</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
user_patient_id: patientId, this.citeLoading = true
register_id: this.registerId, this.citeList = []
store_id: this.storeId || undefined, this.citeActiveId = 0
}) try {
const data = (res && res.data) || res const res = await listMedicalRecordByPatientApi({
user_patient_id: patientId,
store_id: this.storeId || undefined,
limit: 50,
})
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;

View File

@@ -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"

View File

@@ -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"

View File

@@ -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 resolvefalse=取消null=无需选map=已选 */ /** resolvePharmacyWarehouseMap 的 Promise resolvefalse=取消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 一致resultLaravel/ dataYii兜底
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;
} this.prescriptionTypeDefault = def;
const def = Number(data.default); const allowed = data.allowed !== false && list.length > 0;
if (def) this.prescriptionTypeDefault = def; 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));
} }