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