Files
xk-doctor-wx/subPackages/sub_clinic_admin/order/components/PrescriptionDetailPopup.vue
李琦 cb290f3e95 fix(login): 密码登录补充密码非空前端校验
- doctor-login submitLogin 增加「请输入密码」前置校验,
  与 getCode 一致,避免空密码误触发登录请求
- 配合后端 service_user 空密码回退 PC 医生/药师密码校验
2026-07-19 16:08:23 +08:00

618 lines
18 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<template>
<!-- 小程序抽屉须用 page-container + v-if避免 u-popup 在订单详情页底内联 -->
<page-container
v-if="show"
:show="show"
:overlay="true"
position="bottom"
:round="true"
@beforeleave="onClose"
@clickoverlay="onClose"
>
<view class="rx-drawer">
<view class="rx-drawer-head">
<text class="rx-drawer-title">处方详情</text>
<text class="rx-drawer-close" @click="onClose">关闭</text>
</view>
<view class="popup-wrap">
<scroll-view v-if="view" scroll-y class="popup-scroll">
<view class="container safe-area-inset-bottom">
<view class="head">
<view class="head_record_top">
<view class="record">
处方编号{{ view.prescriptionNo }}
</view>
<view class="record_state">
{{ view.typeText }}
</view>
</view>
<view class="head_record">
<image :src="view.sealImage || '/static/group/xkyard.png'" mode="aspectFit"></image>
<view class="record_yard">
<view class="yard">
{{ view.storeName }}
</view>
<view class="state">
处方笺
</view>
</view>
</view>
<view class="head_record_time">
<text style="text-align: right;">开具日期{{ view.createdAt }}</text>
</view>
</view>
<view class="my_info">
<view class="info">
<view class="info_item">
<view class="name">姓名<text>{{ view.patientName }}</text></view>
</view>
<view class="info_item">
<view class="name">性别<text>{{ view.patientSex }}</text></view>
</view>
<view class="info_item">
<view class="name">年龄<text>{{ view.patientAge }}</text></view>
</view>
<view class="info_item">
<view class="name">类别<text>{{ view.category }}</text></view>
</view>
</view>
<view class="info">
<view class="info_item">
<view class="name">科室<text>{{ view.departName }}</text></view>
</view>
<view class="info_item" v-if="view.patientMobile">
<view class="name">电话<text>{{ view.patientMobile }}</text></view>
</view>
</view>
<view class="info">
<view class="info_item">
<view class="names">诊断<text>{{ view.diagnose }}</text></view>
</view>
</view>
<view v-if="view.tcmSyndrome || view.tcmMethod || view.tcmDisease" class="info">
<view v-if="view.tcmSyndrome" class="info_item">
<view class="names">中医证候<text>{{ view.tcmSyndrome }}</text></view>
</view>
<view v-if="view.tcmMethod" class="info_item">
<view class="names">中医治法<text>{{ view.tcmMethod }}</text></view>
</view>
<view v-if="view.tcmDisease" class="info_item">
<view class="names">中医疾病<text>{{ view.tcmDisease }}</text></view>
</view>
</view>
</view>
<view class="bg">
<view class="my_medical">
<view class="title">Rp</view>
<view v-for="(recipe, rIdx) in view.recipes" :key="rIdx" class="item">
<view class="name">
<view class="_name" v-for="(drug, dIdx) in recipe.drugs" :key="dIdx">
<view class="text">
<text>{{ drug.name }}</text>
<text v-if="drug.specification" class="drug-spec">{{ drug.specification }}</text>
<text class="_abbr" v-if="drug.usage">[{{ drug.usage }}]</text>
</view>
<text class="name_num">{{ drug.qty }}</text>
</view>
</view>
<view class="details" v-if="recipe.usage || recipe.process">
<view class="text">
<text v-if="recipe.usage">用法{{ recipe.usage }}</text>
<text v-if="recipe.process" style="margin-left: 10rpx;">包装方式{{ recipe.process }}</text>
</view>
</view>
</view>
</view>
<view class="doctor_order">
<view class="yz">
<text class="order_info_left">医嘱:</text>
<view class="order_info">
<view class="info">{{ view.doctorOrder !== '-' ? view.doctorOrder : '无' }}</view>
</view>
</view>
<view class="titles">处方开具已完毕</view>
</view>
</view>
<view class="my_doctor">
<view class="doctor_info">
<view class="info">
<text>医师</text>
<image v-if="view.doctorSignImage" :src="checkImageUrl(view.doctorSignImage)" mode="aspectFit"></image>
<text v-else class="name">{{ view.doctorName }}</text>
</view>
<view class="info">
<text>审核药师</text>
<image v-if="view.pharmacistSignImage" :src="checkImageUrl(view.pharmacistSignImage)" mode="aspectFit"></image>
<text v-else class="name"></text>
</view>
<view class="info">
<text>发药人</text>
<image v-if="view.pharmacistSignImage" :src="checkImageUrl(view.pharmacistSignImage)" mode="aspectFit"></image>
<text v-else class="name"></text>
</view>
<view class="info">
<text>核对人</text>
<image v-if="view.pharmacistSignImage" :src="checkImageUrl(view.pharmacistSignImage)" mode="aspectFit"></image>
<text v-else class="name"></text>
</view>
<view class="info">
<text>调配人</text>
<image v-if="view.pharmacistSignImage" :src="checkImageUrl(view.pharmacistSignImage)" mode="aspectFit"></image>
<text v-else class="name"></text>
</view>
</view>
<view class="price" v-if="view.totalPayPrice">
价格 {{ view.totalPayPrice }}
</view>
</view>
<view class="my_prompt">
<view class="title">
温馨提示请遵医嘱服药处方{{ view.validHours }}小时有效
</view>
<image src="/static/group/img-yzf.png" mode="aspectFit" v-if="view.status == 2"></image>
</view>
</view>
</scroll-view>
<view v-else-if="loading" class="popup-loading">加载中...</view>
<view v-else class="popup-loading">暂无数据</view>
</view>
</view>
</page-container>
</template>
<script>
/**
* 处方详情抽屉page-container 底部浮层(诊所管理端)
*/
import { getPrescriptionDetail } from '@/api/clinicAdmin.js'
import { mapPrescriptionDetailView } from '@/subPackages/sub_clinic_admin/common/display.js'
import { formatStoreNameWithHu } from '@/utils/formatStoreNameWithHu.js'
export default {
name: 'PrescriptionDetailPopup',
data() {
return {
show: false,
loading: false,
view: null,
}
},
methods: {
/** 解析签名图片,自动兼容 base64 和 http 链接 */
checkImageUrl(url) {
if (!url) return ''
return url.includes('http') ? url : 'data:image/jpeg;base64,' + url
},
open(prescriptionId) {
if (!prescriptionId) return
this.show = true
this.view = null
this.loading = true
getPrescriptionDetail(prescriptionId).then(w => {
if (w.ok && w.data) {
const mapped = mapPrescriptionDetailView(w.data)
mapped.storeName = formatStoreNameWithHu(
w.data.store ? w.data.store.name : '未知诊所',
w.data.is_online,
)
mapped.sealImage = w.data.store ? w.data.store.offical_seal : ''
mapped.typeText = w.data.type == 1 ? '普通' : '常用'
mapped.totalPayPrice = w.data.total_pay_price || '0.00'
mapped.doctorSignImage =
w.data.doctor_sign_image
|| (w.data.doctor_info && w.data.doctor_info.identity_info && w.data.doctor_info.identity_info.sign_image)
|| (w.data.doctorInfo && w.data.doctorInfo.identityInfo && w.data.doctorInfo.identityInfo.sign_image)
|| ''
mapped.pharmacistSignImage =
w.data.Pharmacist_sign_image
|| (w.data.pharmacist_info && w.data.pharmacist_info.identity && w.data.pharmacist_info.identity.sign_image)
|| (w.data.pharmacistInfo && w.data.pharmacistInfo.identity && w.data.pharmacistInfo.identity.sign_image)
|| ''
mapped.validHours = w.data.valid_hours || 72
mapped.status = w.data.status
mapped.patientMobile = w.data.patient ? w.data.patient.mobile : ''
this.view = mapped
}
}).finally(() => {
this.loading = false
})
},
onClose() {
this.view = null
this.show = false
},
},
}
</script>
<style lang="scss" scoped>
.rx-drawer {
height: 85vh;
background: #fff;
display: flex;
flex-direction: column;
padding-bottom: env(safe-area-inset-bottom);
border-radius: 24rpx 24rpx 0 0;
overflow: hidden;
}
.rx-drawer-head {
display: flex;
justify-content: space-between;
align-items: center;
padding: 28rpx 32rpx;
border-bottom: 1rpx solid #f0f0f0;
flex-shrink: 0;
}
.rx-drawer-title { font-size: 32rpx; font-weight: 600; color: #1d2129; }
.rx-drawer-close { font-size: 28rpx; color: #6acdbb; }
.popup-wrap {
flex: 1;
min-height: 0;
display: flex;
flex-direction: column;
background-color: #fff;
position: relative;
}
.popup-scroll {
flex: 1;
height: 0;
}
.popup-loading {
text-align: center;
color: #999;
padding: 80rpx;
}
/* --- 原汁原味的 prescriptionDetail.vue 核心样式 (自适应修复版) --- */
.container {
width: 100%;
padding: 0 20rpx 40rpx;
box-sizing: border-box;
.bg {
min-height: 400rpx;
}
.head {
.head_record_top {
width: 100%;
margin: 14rpx auto 0;
display: flex;
align-items: center;
justify-content: space-between;
.record {
font-size: 28rpx;
font-family: PingFang SC-Regular, PingFang SC;
font-weight: 400;
color: #6C7380;
}
.record_state {
width: 80rpx;
height: 42rpx;
line-height: 42rpx;
text-align: center;
font-size: 24rpx;
font-family: PingFang SC-Regular, PingFang SC;
font-weight: 400;
color: #A7ABB0;
border: 1rpx solid #C4C7CC;
}
}
.head_record {
display: flex;
align-items: center;
justify-content: center;
position: relative;
image {
width: 160rpx;
height: 150rpx;
position: absolute;
left: 40%; /* 也可以根据需要调整居中印章 */
top: -8rpx;
opacity: 0.2; /* 添加微透明度防止完全遮盖文字 */
z-index: 0;
}
.record_yard {
text-align: center;
z-index: 1;
.yard {
font-size: 46rpx;
font-family: PingFang SC-Regular, PingFang SC;
font-weight: 400;
color: #31353D;
}
.state {
height: 44rpx;
font-size: 32rpx;
font-family: PingFang SC-Regular, PingFang SC;
font-weight: 400;
color: #31353D;
line-height: 38rpx;
}
}
}
.head_record_time {
text-align: right;
margin-top: 20rpx;
width: 100%;
height: 34rpx;
font-size: 24rpx;
font-family: PingFang SC-Regular, PingFang SC;
font-weight: 400;
color: #6C7380;
line-height: 28rpx;
}
}
// 经典信息区
.my_info {
width: 100%;
margin: 4rpx auto;
border-top: 1rpx solid #31353D;
border-bottom: 1rpx solid #31353D;
display: flex;
flex-direction: column;
justify-content: space-around;
padding: 4rpx 4rpx;
.info {
width: 100%;
display: flex;
align-items: center;
flex-wrap: wrap;
.info_item {
.names {
display: flex;
align-items: center;
justify-content: space-around;
font-size: 28rpx;
font-family: PingFang SC-Regular, PingFang SC;
font-weight: 400;
color: #6C7380;
text {
flex: 1;
display: block;
height: 40rpx;
font-size: 28rpx;
font-family: PingFang SC-Regular, PingFang SC;
font-weight: 400;
color: #31353D;
line-height: 40rpx;
margin: 0 0rpx 0 8rpx;
}
}
.name {
display: flex;
align-items: center;
justify-content: space-around;
font-size: 28rpx;
font-family: PingFang SC-Regular, PingFang SC;
font-weight: 400;
color: #6C7380;
text {
flex: 1;
display: block;
height: 40rpx;
font-size: 28rpx;
font-family: PingFang SC-Regular, PingFang SC;
font-weight: 400;
color: #31353D;
line-height: 40rpx;
margin: 0 26rpx 0 6rpx;
}
}
}
}
}
// 药品列表
.my_medical {
width: 100%;
margin: 4rpx auto;
padding: 1rpx 10rpx;
box-sizing: border-box;
.title {
font-size: 32rpx;
font-family: PingFang SC-Semibold, PingFang SC;
font-weight: 600;
color: #31353D;
margin-bottom: 2rpx;
}
.item {
width: 100%;
padding: 1rpx 0;
.name {
font-size: 28rpx;
font-family: PingFang SC-Regular, PingFang SC;
font-weight: 400;
color: #31353D;
display: flex;
flex-wrap: wrap;
._name {
display: flex;
justify-content: space-between;
margin: 2rpx 80rpx 2rpx 0;
.text {
margin-right: 16rpx;
display: flex;
flex-direction: column;
.drug-spec {
color: #8A92A3;
font-size: 22rpx;
margin-left: 8rpx;
}
._abbr {
color: #A7ABB0;
font-size: 20rpx;
margin-top: 2rpx;
}
}
}
}
.details {
font-size: 28rpx;
font-family: PingFang SC-Regular, PingFang SC;
font-weight: 400;
color: #6C7380;
display: flex;
flex-direction: column;
justify-content: space-around;
.text {
text {
word-spacing: 1rpx;
}
}
}
}
}
// 医嘱
.doctor_order {
width: 100%;
margin: 2rpx auto;
padding: 0 10rpx;
word-spacing: 2rpx;
box-sizing: border-box;
.yz {
display: flex;
width: 100%;
color: #6C7380;
.order_info_left {
margin-right: 3rpx;
}
.order_info {
font-size: 28rpx;
font-family: PingFang SC-Regular, PingFang SC;
font-weight: 400;
line-height: 40rpx;
margin-bottom: 2rpx;
.info {
flex: 1;
}
}
}
.titles {
width: 100%;
font-size: 24rpx;
font-family: PingFang SC-Regular, PingFang SC;
font-weight: 400;
color: #A7ABB0;
margin: 8rpx 0 20rpx;
text-align: center;
}
}
// 电子签名及价格
.my_doctor {
width: 100%;
margin: 2rpx auto;
border-top: 1rpx solid #31353D;
border-bottom: 1rpx solid #31353D;
padding: 2rpx 4rpx;
box-sizing: border-box;
.doctor_info {
width: 100%;
display: flex;
align-items: center;
margin-bottom: 4rpx;
flex-wrap: wrap;
.info {
min-width: 222rpx;
max-width: 400rpx;
margin: 5rpx 0 8rpx;
text {
font-size: 24rpx;
font-family: PingFang SC-Regular, PingFang SC;
font-weight: 400;
color: #31353D;
}
image {
width: 82rpx;
height: 62rpx;
vertical-align: middle;
margin-left: 10rpx;
}
.name {
display: inline-block;
width: 106rpx;
font-size: 32rpx;
font-family: PingFang SC-Regular, PingFang SC;
font-weight: 400;
color: #31353D;
line-height: 38rpx;
border-bottom: 2rpx solid #000000;
margin-left: 16rpx;
text-align: center;
min-height: 38rpx;
}
}
}
.price {
width: 100%;
font-size: 24rpx;
font-family: PingFang SC-Regular, PingFang SC;
font-weight: 400;
color: #31353D;
margin: 8rpx 0;
}
}
// 温馨提示
.my_prompt {
width: 100%;
margin: 0 auto;
padding: 0 4rpx;
position: relative;
box-sizing: border-box;
.title {
width: 100%;
font-size: 28rpx;
font-family: PingFang SC-Regular, PingFang SC;
font-weight: 400;
color: #A7ABB0;
line-height: 40rpx;
margin: 32rpx 0;
}
image {
width: 159rpx;
height: 100rpx;
position: absolute;
left: 60%;
top: 0%;
}
}
}
</style>