更新
This commit is contained in:
@@ -0,0 +1,538 @@
|
||||
<template>
|
||||
<div ref="paperRef" class="rx-paper">
|
||||
<div class="rx-notice">
|
||||
<span class="rx-notice-text">
|
||||
服药前请核对姓名、电话、医生等信息以及服法、医嘱等要点
|
||||
</span>
|
||||
<span class="rx-notice-meta">
|
||||
<span>日期:{{ dateText }}</span>
|
||||
<span>编号:{{ serialText }}</span>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="rx-info">
|
||||
<div class="rx-info-row">
|
||||
<div class="rx-info-cell">
|
||||
<span class="rx-key">姓名</span>
|
||||
<span class="rx-val">{{ data?.patient_name || '—' }}</span>
|
||||
</div>
|
||||
<div class="rx-info-cell">
|
||||
<span class="rx-key">性别</span>
|
||||
<span class="rx-val">{{ genderLabel }}</span>
|
||||
</div>
|
||||
<div class="rx-info-cell">
|
||||
<span class="rx-key">年龄</span>
|
||||
<span class="rx-val">{{ ageText }}</span>
|
||||
</div>
|
||||
<div class="rx-info-cell">
|
||||
<span class="rx-key">电话</span>
|
||||
<span class="rx-val">{{ data?.phone || data?.recipient_phone || '—' }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="rx-info-row rx-info-row-full">
|
||||
<div class="rx-info-cell">
|
||||
<span class="rx-key">收件信息</span>
|
||||
<span class="rx-val">{{ recipientText }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="rx-info-row rx-info-row-full">
|
||||
<div class="rx-info-cell">
|
||||
<span class="rx-key">临床诊断</span>
|
||||
<span class="rx-val">{{ data?.clinical_diagnosis || '—' }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="rx-rp">
|
||||
<div class="rx-watermark">药房联</div>
|
||||
<div class="rx-rp-head">
|
||||
<div class="rx-rp-label">Rp.</div>
|
||||
<div class="rx-rp-cols-head">
|
||||
<div class="rx-rp-col-head">
|
||||
<span>用药 (单剂)</span>
|
||||
<span>总量</span>
|
||||
</div>
|
||||
<div class="rx-rp-col-head">
|
||||
<span>用药 (单剂)</span>
|
||||
<span>总量</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="rx-herbs">
|
||||
<template v-if="mainHerbs.length">
|
||||
<div class="rx-herb-section-label">主方</div>
|
||||
<div
|
||||
v-for="(h, i) in mainHerbs"
|
||||
:key="'main-' + i"
|
||||
class="rx-herb-cell"
|
||||
>
|
||||
<span class="rx-herb-name">{{ h.name }} ({{ h.dosage }}克)</span>
|
||||
<span class="rx-herb-total">{{ herbTotal(h.dosage) }}克</span>
|
||||
</div>
|
||||
</template>
|
||||
<template v-if="auxHerbs.length">
|
||||
<div class="rx-herb-section-label rx-herb-section-label--aux">辅方</div>
|
||||
<div
|
||||
v-for="(h, i) in auxHerbs"
|
||||
:key="'aux-' + i"
|
||||
class="rx-herb-cell"
|
||||
>
|
||||
<span class="rx-herb-name">{{ h.name }} ({{ h.dosage }}克)</span>
|
||||
<span class="rx-herb-total">{{ herbTotal(h.dosage) }}克</span>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="rx-text">
|
||||
<p>主方服法:{{ usageText }}</p>
|
||||
<p v-if="auxUsageText">辅方服法:{{ auxUsageText }}</p>
|
||||
<p v-if="adviceText">医嘱:{{ adviceText }}</p>
|
||||
<p v-if="dietaryText">忌口:{{ dietaryText }}</p>
|
||||
<p v-if="remarkText">备注:{{ remarkText }}</p>
|
||||
<p v-if="pharmacyRemarkText" class="rx-text-warn">
|
||||
药房备注:{{ pharmacyRemarkText }}
|
||||
</p>
|
||||
<p v-if="outPelletText" class="rx-text-warn">
|
||||
出丸:{{ outPelletText }}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="rx-bottom">
|
||||
<div class="rx-bot-row rx-bot-row-1">
|
||||
<div class="rx-bot-cell rx-bot-stack rx-bot-doctor">
|
||||
<div class="rx-bot-label">医师</div>
|
||||
<div class="rx-bot-doctor-body">
|
||||
<img
|
||||
v-if="data?.doctor_signature"
|
||||
:src="data.doctor_signature"
|
||||
alt="医师签名"
|
||||
class="rx-bot-sign-img"
|
||||
/>
|
||||
<div v-if="!data?.doctor_signature" class="rx-bot-doctor-name">
|
||||
{{ data?.doctor_name || '—' }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="rx-bot-cell rx-bot-meta">
|
||||
<span class="rx-bot-meta-key">类型:</span>
|
||||
<span class="rx-bot-meta-val">{{ typeText }}</span>
|
||||
</div>
|
||||
<div class="rx-bot-cell rx-bot-meta">
|
||||
<span class="rx-bot-meta-key">天数:</span>
|
||||
<span class="rx-bot-meta-val">{{ data?.dose_count || '—' }}剂</span>
|
||||
</div>
|
||||
<div class="rx-bot-cell rx-bot-meta">
|
||||
<span class="rx-bot-meta-key">单剂量:</span>
|
||||
<span class="rx-bot-meta-val">{{ perDoseAmount }}克</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script lang="ts" setup>
|
||||
import { computed, ref } from 'vue'
|
||||
import {
|
||||
getAuxHerbs,
|
||||
getMainHerbs,
|
||||
rxAdviceText,
|
||||
rxAuxUsageText,
|
||||
rxDateText,
|
||||
rxHerbTotal,
|
||||
rxOutPelletText,
|
||||
rxPerDoseAmount,
|
||||
rxPharmacyRemarkText,
|
||||
rxRecipientText,
|
||||
rxRemarkText,
|
||||
rxSerialText,
|
||||
rxTypeText,
|
||||
rxUsageText,
|
||||
slipAgeText,
|
||||
slipDietaryText,
|
||||
slipGenderLabel
|
||||
} from './utils'
|
||||
|
||||
defineOptions({ name: 'PrescriptionSlip' })
|
||||
|
||||
const props = defineProps<{
|
||||
/** 处方详情 / 列表行(字段与 tcm.prescription/detail 对齐) */
|
||||
data: Record<string, any> | null | undefined
|
||||
}>()
|
||||
|
||||
const paperRef = ref<HTMLElement | null>(null)
|
||||
|
||||
const dateText = computed(() => rxDateText(props.data))
|
||||
const serialText = computed(() => rxSerialText(props.data))
|
||||
const recipientText = computed(() => rxRecipientText(props.data))
|
||||
const genderLabel = computed(() => slipGenderLabel(props.data?.gender))
|
||||
const ageText = computed(() => slipAgeText(props.data?.age))
|
||||
const mainHerbs = computed(() => getMainHerbs(props.data))
|
||||
const auxHerbs = computed(() => getAuxHerbs(props.data))
|
||||
const usageText = computed(() => rxUsageText(props.data))
|
||||
const auxUsageText = computed(() => rxAuxUsageText(props.data))
|
||||
const adviceText = computed(() => rxAdviceText(props.data))
|
||||
const dietaryText = computed(() => slipDietaryText(props.data))
|
||||
const remarkText = computed(() => rxRemarkText(props.data))
|
||||
const pharmacyRemarkText = computed(() => rxPharmacyRemarkText(props.data))
|
||||
const outPelletText = computed(() => rxOutPelletText(props.data))
|
||||
const typeText = computed(() => rxTypeText(props.data))
|
||||
const perDoseAmount = computed(() => rxPerDoseAmount(props.data))
|
||||
|
||||
function herbTotal(dosage: number | string) {
|
||||
return rxHerbTotal(props.data, dosage)
|
||||
}
|
||||
|
||||
defineExpose({
|
||||
/** 供打印 / html2canvas 使用的纸张根节点 */
|
||||
paperEl: paperRef,
|
||||
/** 兼容旧用法:直接拿到 DOM */
|
||||
$el: paperRef
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
/* ============================================================
|
||||
* 处方笺(A4 药房联)样式
|
||||
* 严格按 A4:210mm × 297mm,padding 8mm 10mm
|
||||
* 唯一视觉基准:consumer/prescription/index.vue
|
||||
* ============================================================ */
|
||||
.rx-paper {
|
||||
width: 210mm;
|
||||
min-height: 297mm;
|
||||
margin: 0 auto;
|
||||
background: #fff;
|
||||
box-sizing: border-box;
|
||||
padding: 8mm 10mm;
|
||||
color: #1f1f1f;
|
||||
font-size: 13px;
|
||||
line-height: 1.5;
|
||||
font-family: 'Microsoft YaHei', 'PingFang SC', -apple-system, BlinkMacSystemFont, 'Segoe UI',
|
||||
sans-serif;
|
||||
box-shadow: 0 0 0 1px #d6d6d6, 0 6px 24px rgba(0, 0, 0, 0.06);
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
.rx-notice {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
background: #f3f4f6;
|
||||
border: 1px solid #e5e7eb;
|
||||
padding: 6px 10px;
|
||||
border-radius: 2px;
|
||||
font-size: 12px;
|
||||
color: #1f1f1f;
|
||||
margin-bottom: 6px;
|
||||
|
||||
.rx-notice-text {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.rx-notice-meta {
|
||||
display: inline-flex;
|
||||
gap: 24px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
|
||||
.rx-info {
|
||||
border: 1px solid #c8c8c8;
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.rx-info-row {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(4, 1fr);
|
||||
border-bottom: 1px solid #c8c8c8;
|
||||
}
|
||||
|
||||
.rx-info-row-full {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.rx-info-cell {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
padding: 6px 10px;
|
||||
font-size: 13px;
|
||||
border-right: 1px solid #c8c8c8;
|
||||
min-height: 32px;
|
||||
|
||||
&:last-child {
|
||||
border-right: none;
|
||||
}
|
||||
|
||||
.rx-key {
|
||||
color: #1f1f1f;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.rx-val {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
word-break: break-all;
|
||||
}
|
||||
}
|
||||
|
||||
.rx-rp {
|
||||
position: relative;
|
||||
border-left: 1px solid #c8c8c8;
|
||||
border-right: 1px solid #c8c8c8;
|
||||
padding: 8px 10px 16px;
|
||||
overflow: hidden;
|
||||
box-sizing: border-box;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.rx-watermark {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 88px;
|
||||
font-weight: 700;
|
||||
color: rgba(0, 0, 0, 0.06);
|
||||
transform: rotate(-22deg);
|
||||
letter-spacing: 0.18em;
|
||||
pointer-events: none;
|
||||
user-select: none;
|
||||
z-index: 0;
|
||||
}
|
||||
|
||||
.rx-rp-head {
|
||||
display: grid;
|
||||
grid-template-columns: 36px 1fr;
|
||||
align-items: end;
|
||||
column-gap: 8px;
|
||||
padding-bottom: 4px;
|
||||
border-bottom: 1px solid #d4d4d4;
|
||||
margin-bottom: 6px;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.rx-rp-label {
|
||||
font-size: 16px;
|
||||
font-weight: 700;
|
||||
color: #1f1f1f;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.rx-rp-cols-head {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
column-gap: 24px;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.rx-rp-col-head {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 64px;
|
||||
column-gap: 8px;
|
||||
font-size: 13px;
|
||||
color: #1f1f1f;
|
||||
min-width: 0;
|
||||
|
||||
& > span:last-child {
|
||||
text-align: right;
|
||||
}
|
||||
}
|
||||
|
||||
.rx-herbs {
|
||||
padding-left: 44px;
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
column-gap: 24px;
|
||||
row-gap: 6px;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.rx-herb-section-label {
|
||||
grid-column: 1 / -1;
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
color: #409eff;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.rx-herb-section-label--aux {
|
||||
color: #e6a23c;
|
||||
margin-top: 4px;
|
||||
}
|
||||
|
||||
.rx-herb-cell {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 64px;
|
||||
column-gap: 8px;
|
||||
font-size: 13px;
|
||||
line-height: 1.85;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.rx-herb-name {
|
||||
color: #1f1f1f;
|
||||
min-width: 0;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.rx-herb-total {
|
||||
color: #1f1f1f;
|
||||
text-align: right;
|
||||
font-variant-numeric: tabular-nums;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.rx-text {
|
||||
border: 1px solid #c8c8c8;
|
||||
border-top: none;
|
||||
padding: 10px 12px;
|
||||
font-size: 13px;
|
||||
line-height: 1.85;
|
||||
|
||||
p {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.rx-text-warn {
|
||||
color: #d72424;
|
||||
font-weight: 600;
|
||||
}
|
||||
}
|
||||
|
||||
.rx-bottom {
|
||||
border: 1px solid #c8c8c8;
|
||||
border-top: none;
|
||||
}
|
||||
|
||||
.rx-bot-row {
|
||||
display: grid;
|
||||
border-bottom: 1px solid #c8c8c8;
|
||||
|
||||
&:last-child {
|
||||
border-bottom: none;
|
||||
}
|
||||
}
|
||||
|
||||
.rx-bot-row-1 {
|
||||
grid-template-columns: 1.4fr 0.6fr 0.6fr 0.6fr 0.6fr 1.7fr 0.95fr 1.25fr;
|
||||
min-height: 70px;
|
||||
}
|
||||
|
||||
.rx-bot-cell {
|
||||
border-right: 1px solid #c8c8c8;
|
||||
padding: 6px 10px;
|
||||
font-size: 12px;
|
||||
color: #1f1f1f;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
min-height: 32px;
|
||||
box-sizing: border-box;
|
||||
|
||||
&:last-child {
|
||||
border-right: none;
|
||||
}
|
||||
}
|
||||
|
||||
.rx-bot-stack {
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
justify-content: flex-start;
|
||||
}
|
||||
|
||||
.rx-bot-label {
|
||||
font-size: 13px;
|
||||
color: #1f1f1f;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.rx-bot-doctor {
|
||||
.rx-bot-doctor-body {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
min-height: 36px;
|
||||
}
|
||||
|
||||
.rx-bot-doctor-name {
|
||||
font-size: 13px;
|
||||
color: #1f1f1f;
|
||||
}
|
||||
|
||||
.rx-bot-sign-img {
|
||||
max-height: 40px;
|
||||
max-width: 110px;
|
||||
object-fit: contain;
|
||||
}
|
||||
}
|
||||
|
||||
.rx-bot-meta {
|
||||
flex-wrap: nowrap;
|
||||
|
||||
.rx-bot-meta-key {
|
||||
color: #1f1f1f;
|
||||
margin-right: 6px;
|
||||
white-space: nowrap;
|
||||
flex-shrink: 0;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.rx-bot-meta-val {
|
||||
font-weight: 500;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
min-width: 0;
|
||||
line-height: 1.4;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
<!-- 全局打印样式:body * 选择器不能放进 scoped -->
|
||||
<style>
|
||||
@media print {
|
||||
body * {
|
||||
visibility: hidden !important;
|
||||
}
|
||||
|
||||
.rx-paper,
|
||||
.rx-paper * {
|
||||
visibility: visible !important;
|
||||
}
|
||||
|
||||
.rx-paper {
|
||||
position: fixed !important;
|
||||
left: 0 !important;
|
||||
top: 0 !important;
|
||||
width: 210mm !important;
|
||||
height: 297mm !important;
|
||||
margin: 0 !important;
|
||||
padding: 8mm 10mm !important;
|
||||
box-shadow: none !important;
|
||||
page-break-after: always;
|
||||
}
|
||||
|
||||
@page {
|
||||
size: A4;
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,291 @@
|
||||
/**
|
||||
* 处方笺(A4 药房联)字段计算
|
||||
* 唯一视觉基准:consumer/prescription/index.vue 查看态
|
||||
*/
|
||||
|
||||
export type FormulaType = '主方' | '辅方'
|
||||
|
||||
export type SlipHerb = {
|
||||
medicine_id?: number
|
||||
name: string
|
||||
dosage: number
|
||||
formula_type?: FormulaType | string
|
||||
}
|
||||
|
||||
export type AuxUsageForm = {
|
||||
dosage_amount: number
|
||||
dosage_bag_count: number
|
||||
need_decoction: boolean
|
||||
bags_per_dose: number
|
||||
times_per_day: number
|
||||
usage_days: number
|
||||
prescription_name?: string
|
||||
}
|
||||
|
||||
export function formatNum(n: number): string {
|
||||
if (!isFinite(n)) return '0'
|
||||
return Number.isInteger(n) ? String(n) : n.toFixed(2).replace(/\.?0+$/, '')
|
||||
}
|
||||
|
||||
export function slipGenderLabel(g: unknown) {
|
||||
if (g === 1 || g === '1') return '男'
|
||||
if (g === 0 || g === '0') return '女'
|
||||
return '—'
|
||||
}
|
||||
|
||||
export function slipAgeText(age: unknown) {
|
||||
if (age === null || age === undefined || age === '') return '—'
|
||||
return `${age}岁`
|
||||
}
|
||||
|
||||
export function normalizeFormulaType(v: unknown): FormulaType {
|
||||
return v === '辅方' ? '辅方' : '主方'
|
||||
}
|
||||
|
||||
function padN(n: number) {
|
||||
return String(n).padStart(2, '0')
|
||||
}
|
||||
|
||||
export function defaultAuxUsage(prescriptionType = '浓缩水丸'): AuxUsageForm {
|
||||
if (prescriptionType === '饮片') {
|
||||
return {
|
||||
dosage_amount: 50,
|
||||
dosage_bag_count: 1,
|
||||
need_decoction: false,
|
||||
bags_per_dose: 1,
|
||||
times_per_day: 3,
|
||||
usage_days: 7
|
||||
}
|
||||
}
|
||||
if (prescriptionType === '浓缩水丸') {
|
||||
return {
|
||||
dosage_amount: 5,
|
||||
dosage_bag_count: 1,
|
||||
need_decoction: false,
|
||||
bags_per_dose: 1,
|
||||
times_per_day: 3,
|
||||
usage_days: 7
|
||||
}
|
||||
}
|
||||
return {
|
||||
dosage_amount: 1,
|
||||
dosage_bag_count: 1,
|
||||
need_decoction: false,
|
||||
bags_per_dose: 1,
|
||||
times_per_day: 3,
|
||||
usage_days: 7
|
||||
}
|
||||
}
|
||||
|
||||
export function normalizeAuxUsageForm(raw: unknown, prescriptionType: string): AuxUsageForm {
|
||||
const base = defaultAuxUsage(prescriptionType)
|
||||
if (!raw || typeof raw !== 'object') return { ...base }
|
||||
const o = raw as Record<string, unknown>
|
||||
return {
|
||||
dosage_amount:
|
||||
o.dosage_amount !== null && o.dosage_amount !== undefined && o.dosage_amount !== ''
|
||||
? Number(o.dosage_amount)
|
||||
: base.dosage_amount,
|
||||
dosage_bag_count: o.dosage_bag_count != null ? Number(o.dosage_bag_count) || 1 : base.dosage_bag_count,
|
||||
need_decoction: o.need_decoction === 1 || o.need_decoction === true,
|
||||
bags_per_dose: o.bags_per_dose != null ? Number(o.bags_per_dose) || 1 : base.bags_per_dose,
|
||||
times_per_day: o.times_per_day != null ? Number(o.times_per_day) || 3 : base.times_per_day,
|
||||
usage_days: o.usage_days != null ? Number(o.usage_days) || 7 : base.usage_days,
|
||||
prescription_name: String(o.prescription_name ?? '').trim()
|
||||
}
|
||||
}
|
||||
|
||||
export function buildUsageSegmentText(
|
||||
usage: {
|
||||
prescription_type?: string
|
||||
dosage_amount?: number | null
|
||||
dosage_unit?: string
|
||||
dosage_bag_count?: number
|
||||
times_per_day?: number
|
||||
usage_way?: string
|
||||
usage_time?: string
|
||||
},
|
||||
fallbackWay?: string,
|
||||
fallbackTime?: string
|
||||
): string {
|
||||
const pt = usage.prescription_type || '浓缩水丸'
|
||||
const times = Number(usage.times_per_day) > 0 ? Number(usage.times_per_day) : 3
|
||||
const amount = usage.dosage_amount != null ? Number(usage.dosage_amount) : 10
|
||||
const unit = usage.dosage_unit || (pt === '饮片' ? 'ml' : 'g')
|
||||
const usageWay = usage.usage_way || fallbackWay || '温水送服'
|
||||
const usageTime = usage.usage_time || fallbackTime || ''
|
||||
const seg: string[] = []
|
||||
seg.push(`每天${times}次`)
|
||||
if (pt === '浓缩水丸') {
|
||||
const bags = Number(usage.dosage_bag_count) > 0 ? Number(usage.dosage_bag_count) : 1
|
||||
seg.push(`一次${bags}袋`)
|
||||
seg.push(`每袋${amount}${unit}`)
|
||||
} else {
|
||||
seg.push(`一次${amount}${unit}`)
|
||||
}
|
||||
seg.push(usageWay)
|
||||
if (usageTime) seg.push(usageTime)
|
||||
return seg.join(', ')
|
||||
}
|
||||
|
||||
export function getSlipHerbs(data: Record<string, any> | null | undefined): SlipHerb[] {
|
||||
const h = data?.herbs
|
||||
return Array.isArray(h) ? (h as SlipHerb[]) : []
|
||||
}
|
||||
|
||||
export function getMainHerbs(data: Record<string, any> | null | undefined): SlipHerb[] {
|
||||
return getSlipHerbs(data).filter((h) => normalizeFormulaType(h.formula_type) === '主方')
|
||||
}
|
||||
|
||||
export function getAuxHerbs(data: Record<string, any> | null | undefined): SlipHerb[] {
|
||||
return getSlipHerbs(data).filter((h) => normalizeFormulaType(h.formula_type) === '辅方')
|
||||
}
|
||||
|
||||
export function getDoseCount(data: Record<string, any> | null | undefined): number {
|
||||
const n = Number(data?.dose_count)
|
||||
return Number.isFinite(n) && n > 0 ? n : 1
|
||||
}
|
||||
|
||||
export function rxDateText(data: Record<string, any> | null | undefined): string {
|
||||
if (!data) return '—'
|
||||
const ts = data.create_time ?? data.update_time ?? null
|
||||
if (ts) {
|
||||
const t = String(ts).length === 10 ? Number(ts) * 1000 : Number(ts)
|
||||
const d = new Date(t)
|
||||
if (!isNaN(d.getTime())) {
|
||||
return `${d.getFullYear()}-${padN(d.getMonth() + 1)}-${padN(d.getDate())} ${padN(d.getHours())}:${padN(d.getMinutes())}`
|
||||
}
|
||||
}
|
||||
return data.prescription_date || '—'
|
||||
}
|
||||
|
||||
export function rxSerialText(data: Record<string, any> | null | undefined): string {
|
||||
if (!data) return '—'
|
||||
return (
|
||||
data.order_no ||
|
||||
data.serial_no ||
|
||||
data.serial_number ||
|
||||
data.no ||
|
||||
data.prescription_no ||
|
||||
data.sn ||
|
||||
data.visit_no ||
|
||||
(data.id ? `G${data.id}` : '—')
|
||||
)
|
||||
}
|
||||
|
||||
export function rxRecipientText(data: Record<string, any> | null | undefined): string {
|
||||
if (!data) return '—'
|
||||
if (data.recipient_text) return data.recipient_text
|
||||
const parts: string[] = []
|
||||
if (data.recipient_name || data.patient_name) parts.push(data.recipient_name || data.patient_name)
|
||||
if (data.recipient_phone || data.phone) parts.push(data.recipient_phone || data.phone)
|
||||
let region = data.region
|
||||
if (Array.isArray(region)) region = region.filter(Boolean).join('')
|
||||
if (!region) {
|
||||
region = [data.shipping_province, data.shipping_city, data.shipping_district]
|
||||
.filter(Boolean)
|
||||
.join('')
|
||||
}
|
||||
if (region) parts.push(String(region))
|
||||
if (data.shipping_address) parts.push(String(data.shipping_address))
|
||||
else if (data.address) parts.push(String(data.address))
|
||||
return parts.length ? parts.join(',') : '—'
|
||||
}
|
||||
|
||||
export function rxPerDoseAmount(data: Record<string, any> | null | undefined): string {
|
||||
const list = getSlipHerbs(data)
|
||||
const sum = list.reduce((acc, h) => acc + (Number(h?.dosage) || 0), 0)
|
||||
return formatNum(sum)
|
||||
}
|
||||
|
||||
export function rxHerbTotal(data: Record<string, any> | null | undefined, dosage: number | string): string {
|
||||
const d = Number(dosage) || 0
|
||||
return formatNum(d * getDoseCount(data))
|
||||
}
|
||||
|
||||
export function rxTypeText(data: Record<string, any> | null | undefined): string {
|
||||
if (!data) return '—'
|
||||
const t = data.prescription_type || '浓缩水丸'
|
||||
return /丸|散|膏|片/.test(t) ? `浓缩丸-${t}` : t
|
||||
}
|
||||
|
||||
export function rxUsageText(data: Record<string, any> | null | undefined): string {
|
||||
if (!data) return '—'
|
||||
if (data.usage_text) return data.usage_text
|
||||
return buildUsageSegmentText(data)
|
||||
}
|
||||
|
||||
export function rxAuxUsageText(data: Record<string, any> | null | undefined): string {
|
||||
if (!data || !getAuxHerbs(data).length) return ''
|
||||
const aux = normalizeAuxUsageForm(data.aux_usage, data.prescription_type || '浓缩水丸')
|
||||
return buildUsageSegmentText(
|
||||
{
|
||||
prescription_type: data.prescription_type,
|
||||
dosage_amount: aux.dosage_amount,
|
||||
dosage_unit: data.dosage_unit,
|
||||
dosage_bag_count: aux.dosage_bag_count,
|
||||
times_per_day: aux.times_per_day,
|
||||
usage_way: data.usage_way,
|
||||
usage_time: data.usage_time
|
||||
},
|
||||
data.usage_way,
|
||||
data.usage_time
|
||||
)
|
||||
}
|
||||
|
||||
export function rxAdviceText(data: Record<string, any> | null | undefined): string {
|
||||
if (!data) return ''
|
||||
return data.medical_advice || data.doctor_advice || ''
|
||||
}
|
||||
|
||||
export function slipDietaryText(data: Record<string, any> | null | undefined): string {
|
||||
const d = data?.dietary_taboo
|
||||
if (Array.isArray(d)) return d.filter(Boolean).join('、')
|
||||
if (typeof d === 'string' && d.trim()) return d.trim()
|
||||
return ''
|
||||
}
|
||||
|
||||
export function rxRemarkText(data: Record<string, any> | null | undefined): string {
|
||||
if (!data) return ''
|
||||
const parts: string[] = []
|
||||
parts.push(`共${getSlipHerbs(data).length}味药`)
|
||||
if (data.usage_notes) parts.push(String(data.usage_notes))
|
||||
if (data.remark) parts.push(String(data.remark))
|
||||
return parts.join(' ')
|
||||
}
|
||||
|
||||
export function rxPharmacyRemarkText(data: Record<string, any> | null | undefined): string {
|
||||
if (!data) return ''
|
||||
return data.pharmacy_remark || data.pharmacy_note || ''
|
||||
}
|
||||
|
||||
function computeOutPellet(data: Record<string, any>): string {
|
||||
if ((data.prescription_type || '浓缩水丸') === '浓缩水丸') {
|
||||
const days = Number(data.medication_days ?? data.usage_days ?? data.dose_count)
|
||||
const times = Number(data.times_per_day)
|
||||
const doseG = Number(data.dosage_amount)
|
||||
const bags = Number(data.dosage_bag_count) > 0 ? Number(data.dosage_bag_count) : 1
|
||||
if (
|
||||
Number.isFinite(days) &&
|
||||
Number.isFinite(times) &&
|
||||
Number.isFinite(doseG) &&
|
||||
days > 0 &&
|
||||
times > 0 &&
|
||||
doseG > 0
|
||||
) {
|
||||
return formatNum(days * times * doseG * bags)
|
||||
}
|
||||
}
|
||||
const explicit = data.out_pellet || data.out_pellet_text || data.total_weight
|
||||
if (explicit && Number(explicit) > 0) return formatNum(Number(explicit))
|
||||
const list = getSlipHerbs(data)
|
||||
if (!list.length) return ''
|
||||
const perDose = list.reduce((acc, h) => acc + (Number(h?.dosage) || 0), 0)
|
||||
return formatNum(perDose * getDoseCount(data))
|
||||
}
|
||||
|
||||
export function rxOutPelletText(data: Record<string, any> | null | undefined): string {
|
||||
if (!data) return ''
|
||||
const out = computeOutPellet(data)
|
||||
return out ? `${out}克` : ''
|
||||
}
|
||||
@@ -534,13 +534,6 @@
|
||||
</div>
|
||||
|
||||
<div v-else class="rx-wrap">
|
||||
<el-tabs
|
||||
v-model="prescriptionTabType"
|
||||
class="rx-slip-tabs"
|
||||
>
|
||||
<el-tab-pane label="药房联" name="internal" />
|
||||
<el-tab-pane label="处方联" name="user" />
|
||||
</el-tabs>
|
||||
<div class="rx-toolbar">
|
||||
<el-tag v-if="savedPrescription.void_status === 1" type="danger" size="small">已作废</el-tag>
|
||||
<el-tag v-else-if="approvedPreviewOnly" type="success" size="small">已通过</el-tag>
|
||||
@@ -565,190 +558,11 @@
|
||||
</div>
|
||||
|
||||
<div class="prescription-preview-content">
|
||||
<div v-if="savedPrescription" ref="prescriptionPrintRef" class="rx-paper">
|
||||
<div class="rx-title">{{ prescriptionTabType === 'internal' ? '药房联' : SLIP_TITLE }}</div>
|
||||
<div class="rx-notice">
|
||||
<span class="rx-notice-text">
|
||||
{{
|
||||
prescriptionTabType === 'user'
|
||||
? '服药前请核对姓名、电话、医生等信息以及医嘱等要点'
|
||||
: '服药前请核对姓名、电话、医生等信息以及服法、医嘱等要点'
|
||||
}}
|
||||
</span>
|
||||
<span class="rx-notice-meta">
|
||||
<span>日期:{{ rxDateText }}</span>
|
||||
<span>编号:{{ rxSerialText }}</span>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<div class="rx-info">
|
||||
<div class="rx-info-row">
|
||||
<div class="rx-info-cell">
|
||||
<span class="rx-key">姓名</span>
|
||||
<span class="rx-val">{{ savedPrescription.patient_name || '—' }}</span>
|
||||
</div>
|
||||
<div class="rx-info-cell">
|
||||
<span class="rx-key">性别</span>
|
||||
<span class="rx-val">{{ slipGenderLabel(savedPrescription.gender) }}</span>
|
||||
</div>
|
||||
<div class="rx-info-cell">
|
||||
<span class="rx-key">年龄</span>
|
||||
<span class="rx-val">{{ slipAgeText(savedPrescription.age) }}</span>
|
||||
</div>
|
||||
<div class="rx-info-cell">
|
||||
<span class="rx-key">电话</span>
|
||||
<span class="rx-val">{{ savedPrescription.phone || '—' }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="rx-info-row rx-info-row-full">
|
||||
<div class="rx-info-cell">
|
||||
<span class="rx-key">门诊号</span>
|
||||
<span class="rx-val">{{ savedPrescription.visit_no || '—' }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="savedPrescription.pulse || savedPrescription.tongue" class="rx-info-row rx-info-row-full">
|
||||
<div v-if="savedPrescription.pulse" class="rx-info-cell">
|
||||
<span class="rx-key">面象</span>
|
||||
<span class="rx-val">{{ savedPrescription.pulse }}</span>
|
||||
</div>
|
||||
<div v-if="savedPrescription.tongue" class="rx-info-cell">
|
||||
<span class="rx-key">舌象</span>
|
||||
<span class="rx-val">{{ savedPrescription.tongue }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="rx-info-row rx-info-row-full">
|
||||
<div class="rx-info-cell">
|
||||
<span class="rx-key">临床诊断</span>
|
||||
<span class="rx-val">{{ savedPrescription.clinical_diagnosis || '—' }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="rx-rp">
|
||||
<div class="rx-watermark">{{ prescriptionTabType === 'internal' ? '药房联' : '处方联' }}</div>
|
||||
<div class="rx-rp-head">
|
||||
<div class="rx-rp-label">Rp.</div>
|
||||
<div class="rx-rp-cols-head">
|
||||
<div class="rx-rp-col-head">
|
||||
<span>用药 (单剂)</span>
|
||||
<span>{{ prescriptionTabType === 'user' ? '用量' : '总量' }}</span>
|
||||
</div>
|
||||
<div class="rx-rp-col-head">
|
||||
<span>用药 (单剂)</span>
|
||||
<span>{{ prescriptionTabType === 'user' ? '用量' : '总量' }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="rx-herbs">
|
||||
<template v-if="prescriptionTabType === 'internal'">
|
||||
<template v-if="savedMainHerbs.length">
|
||||
<div class="rx-herb-section-label">主方</div>
|
||||
<div
|
||||
v-for="(h, i) in savedMainHerbs"
|
||||
:key="'main-' + i"
|
||||
class="rx-herb-cell"
|
||||
>
|
||||
<span class="rx-herb-name">{{ h.name }} ({{ h.dosage }}克)</span>
|
||||
<span class="rx-herb-total">{{ rxHerbTotal(h.dosage) }}克</span>
|
||||
</div>
|
||||
</template>
|
||||
<template v-if="savedAuxHerbs.length">
|
||||
<div class="rx-herb-section-label rx-herb-section-label--aux">辅方</div>
|
||||
<div
|
||||
v-for="(h, i) in savedAuxHerbs"
|
||||
:key="'aux-' + i"
|
||||
class="rx-herb-cell"
|
||||
>
|
||||
<span class="rx-herb-name">{{ h.name }} ({{ h.dosage }}克)</span>
|
||||
<span class="rx-herb-total">{{ rxHerbTotal(h.dosage) }}克</span>
|
||||
</div>
|
||||
</template>
|
||||
</template>
|
||||
<template v-else>
|
||||
<template v-if="savedMainHerbs.length">
|
||||
<div class="rx-herb-section-label">主方</div>
|
||||
<div
|
||||
v-for="(h, i) in savedMainHerbs"
|
||||
:key="'user-main-' + i"
|
||||
class="rx-herb-cell"
|
||||
>
|
||||
<span class="rx-herb-name">{{ h.name }}</span>
|
||||
<span class="rx-herb-total">{{ h.dosage }}克</span>
|
||||
</div>
|
||||
</template>
|
||||
<template v-if="savedAuxHerbs.length">
|
||||
<div class="rx-herb-section-label rx-herb-section-label--aux">辅方</div>
|
||||
<div
|
||||
v-for="(h, i) in savedAuxHerbs"
|
||||
:key="'user-aux-' + i"
|
||||
class="rx-herb-cell"
|
||||
>
|
||||
<span class="rx-herb-name">{{ h.name }}</span>
|
||||
<span class="rx-herb-total">{{ h.dosage }}克</span>
|
||||
</div>
|
||||
</template>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="rx-text">
|
||||
<template v-if="prescriptionTabType === 'internal'">
|
||||
<p v-if="savedAuxUsageText">主方服法:{{ rxUsageText }}</p>
|
||||
<p v-else>服法:{{ rxUsageText }}</p>
|
||||
<p v-if="savedAuxUsageText">辅方服法:{{ savedAuxUsageText }}</p>
|
||||
</template>
|
||||
<p v-if="rxAdviceText">医嘱:{{ rxAdviceText }}</p>
|
||||
<p v-if="prescriptionTabType === 'internal' && rxRemarkText">备注:{{ rxRemarkText }}</p>
|
||||
<p v-if="savedPrescription.dietary_taboo && savedPrescription.dietary_taboo.length">
|
||||
忌口:{{ Array.isArray(savedPrescription.dietary_taboo) ? savedPrescription.dietary_taboo.join('、') : savedPrescription.dietary_taboo }}
|
||||
</p>
|
||||
<p v-if="rxPharmacyRemarkText" class="rx-text-warn">药房备注:{{ rxPharmacyRemarkText }}</p>
|
||||
<p
|
||||
v-if="prescriptionTabType === 'internal' && rxOutPelletText && savedPrescription.prescription_type !== '饮片'"
|
||||
class="rx-text-warn"
|
||||
>
|
||||
出丸:{{ rxOutPelletText }}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="rx-bottom">
|
||||
<div
|
||||
class="rx-bot-row rx-bot-row-1"
|
||||
:class="{ 'rx-bot-row-1--user': prescriptionTabType === 'user' }"
|
||||
>
|
||||
<div class="rx-bot-cell rx-bot-stack rx-bot-doctor">
|
||||
<div class="rx-bot-label">医师</div>
|
||||
<div class="rx-bot-doctor-body">
|
||||
<img
|
||||
v-if="savedPrescription.doctor_signature"
|
||||
:src="savedPrescription.doctor_signature"
|
||||
alt="医师签名"
|
||||
class="rx-bot-sign-img"
|
||||
/>
|
||||
<div v-if="!savedPrescription.doctor_signature" class="rx-bot-doctor-name">
|
||||
{{ savedPrescription.doctor_name || '—' }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="prescriptionTabType === 'internal'" class="rx-bot-cell rx-bot-meta">
|
||||
<span class="rx-bot-meta-key">类型:</span>
|
||||
<span class="rx-bot-meta-val">{{ rxTypeText }}</span>
|
||||
</div>
|
||||
<div class="rx-bot-cell rx-bot-meta">
|
||||
<span class="rx-bot-meta-key">天数:</span>
|
||||
<span class="rx-bot-meta-val">{{ rxSlipMedicationDaysText }}</span>
|
||||
</div>
|
||||
<div v-if="prescriptionTabType === 'internal'" class="rx-bot-cell rx-bot-meta">
|
||||
<span class="rx-bot-meta-key">剂量:</span>
|
||||
<span class="rx-bot-meta-val">{{ rxPerDoseAmount }}克</span>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="prescriptionTabType === 'user'" class="rx-bot-row rx-hospital-row">
|
||||
<div class="rx-hospital-line">成都双流甄养堂互联网医院有限公司 联系方式:4001667339</div>
|
||||
<div class="rx-hospital-line">地址:四川省成都市双流区黄甲街道黄龙大道二段280号</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<PrescriptionSlip
|
||||
v-if="savedPrescription"
|
||||
ref="rxSlipRef"
|
||||
:data="savedPrescription"
|
||||
/>
|
||||
|
||||
<!-- 详细病历 - 单独A3纸张 -->
|
||||
<div v-if="savedPrescription.case_record && !hideCaseRecordPreview" ref="caseRecordPrintRef" class="case-record-a3">
|
||||
@@ -1045,6 +859,7 @@ import { formatDiabetesDiscoveryDisplay } from '@/utils/diabetes-discovery-displ
|
||||
import { prescriptionAdd, prescriptionDetail, prescriptionGetByAppointment, prescriptionVoid, tcmDiagnosisDetail, prescriptionLibraryLists } from '@/api/tcm'
|
||||
import { medicineLists } from '@/api/medicine'
|
||||
import { getDictData } from '@/api/app'
|
||||
import PrescriptionSlip from '@/components/prescription-slip/index.vue'
|
||||
import html2canvas from 'html2canvas'
|
||||
import { jsPDF } from 'jspdf'
|
||||
import useUserStore from '@/stores/modules/user'
|
||||
@@ -1219,7 +1034,7 @@ const visible = ref(false)
|
||||
const formRef = ref()
|
||||
const saving = ref(false)
|
||||
const voiding = ref(false)
|
||||
const prescriptionPrintRef = ref<HTMLElement>()
|
||||
const rxSlipRef = ref<InstanceType<typeof PrescriptionSlip> | null>(null)
|
||||
const caseRecordPrintRef = ref<HTMLElement>()
|
||||
const signatureCanvasRef = ref<HTMLCanvasElement>()
|
||||
/** 当前一笔的 pointerId(配合 setPointerCapture,移出画布仍跟手) */
|
||||
@@ -1229,7 +1044,7 @@ const SIGNATURE_CSS_W = 560
|
||||
const SIGNATURE_CSS_H = 168
|
||||
const savedPrescription = ref<any>(null)
|
||||
/** 预览联次:药房联 / 处方联(与消费者处方查看一致) */
|
||||
const prescriptionTabType = ref<'internal' | 'user'>('internal')
|
||||
const prescriptionTabType = ref<'internal' | 'user'>('internal') // 保留兼容 openPreview options.slipType
|
||||
/** 诊单列表「查看」仅展示处方联,不展示详细病历 A3 */
|
||||
const hideCaseRecordPreview = ref(false)
|
||||
|
||||
@@ -2397,7 +2212,7 @@ const handleNewPrescription = async () => {
|
||||
}
|
||||
|
||||
const captureElement = async (): Promise<HTMLCanvasElement> => {
|
||||
const el = prescriptionPrintRef.value
|
||||
const el = (rxSlipRef.value as { paperEl?: HTMLElement | null } | null)?.paperEl ?? null
|
||||
if (!el) throw new Error('未找到处方内容')
|
||||
return html2canvas(el, {
|
||||
scale: 2,
|
||||
|
||||
@@ -342,144 +342,8 @@
|
||||
<div v-if="slipView.audit_remark" class="mt-1">驳回意见:{{ slipView.audit_remark }}</div>
|
||||
</div>
|
||||
|
||||
<!-- A4 处方纸 -->
|
||||
<div v-if="slipView" ref="rxPaperRef" class="rx-paper">
|
||||
<!-- 顶部告知栏 -->
|
||||
<div class="rx-notice">
|
||||
<span class="rx-notice-text">
|
||||
服药前请核对姓名、电话、医生等信息以及服法、医嘱等要点
|
||||
</span>
|
||||
<span class="rx-notice-meta">
|
||||
<span>日期:{{ rxDateText }}</span>
|
||||
<span>编号:{{ rxSerialText }}</span>
|
||||
</span>
|
||||
</div>
|
||||
|
||||
<!-- 患者信息表 -->
|
||||
<div class="rx-info">
|
||||
<div class="rx-info-row">
|
||||
<div class="rx-info-cell">
|
||||
<span class="rx-key">姓名</span>
|
||||
<span class="rx-val">{{ slipView.patient_name || '—' }}</span>
|
||||
</div>
|
||||
<div class="rx-info-cell">
|
||||
<span class="rx-key">性别</span>
|
||||
<span class="rx-val">{{ slipGenderLabel(slipView.gender) }}</span>
|
||||
</div>
|
||||
<div class="rx-info-cell">
|
||||
<span class="rx-key">年龄</span>
|
||||
<span class="rx-val">{{ slipAgeText(slipView.age) }}</span>
|
||||
</div>
|
||||
<div class="rx-info-cell">
|
||||
<span class="rx-key">电话</span>
|
||||
<span class="rx-val">{{ slipView.phone || '—' }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="rx-info-row rx-info-row-full">
|
||||
<div class="rx-info-cell">
|
||||
<span class="rx-key">收件信息</span>
|
||||
<span class="rx-val">{{ rxRecipientText }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="rx-info-row rx-info-row-full">
|
||||
<div class="rx-info-cell">
|
||||
<span class="rx-key">临床诊断</span>
|
||||
<span class="rx-val">{{ slipView.clinical_diagnosis || '—' }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Rp 区域(带水印) -->
|
||||
<div class="rx-rp">
|
||||
<div class="rx-watermark">药房联</div>
|
||||
<div class="rx-rp-head">
|
||||
<div class="rx-rp-label">Rp.</div>
|
||||
<div class="rx-rp-cols-head">
|
||||
<div class="rx-rp-col-head">
|
||||
<span>用药 (单剂)</span>
|
||||
<span>总量</span>
|
||||
</div>
|
||||
<div class="rx-rp-col-head">
|
||||
<span>用药 (单剂)</span>
|
||||
<span>总量</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="rx-herbs">
|
||||
<template v-if="slipMainHerbs.length">
|
||||
<div class="rx-herb-section-label">主方</div>
|
||||
<div
|
||||
v-for="(h, i) in slipMainHerbs"
|
||||
:key="'main-' + i"
|
||||
class="rx-herb-cell"
|
||||
>
|
||||
<span class="rx-herb-name">{{ h.name }} ({{ h.dosage }}克)</span>
|
||||
<span class="rx-herb-total">{{ rxHerbTotal(h.dosage) }}克</span>
|
||||
</div>
|
||||
</template>
|
||||
<template v-if="slipAuxHerbs.length">
|
||||
<div class="rx-herb-section-label rx-herb-section-label--aux">辅方</div>
|
||||
<div
|
||||
v-for="(h, i) in slipAuxHerbs"
|
||||
:key="'aux-' + i"
|
||||
class="rx-herb-cell"
|
||||
>
|
||||
<span class="rx-herb-name">{{ h.name }} ({{ h.dosage }}克)</span>
|
||||
<span class="rx-herb-total">{{ rxHerbTotal(h.dosage) }}克</span>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 服法 / 医嘱 / 备注 / 药房备注 / 出丸 -->
|
||||
<div class="rx-text">
|
||||
<p>主方服法:{{ rxUsageText }}</p>
|
||||
<p v-if="rxAuxUsageText">辅方服法:{{ rxAuxUsageText }}</p>
|
||||
<p v-if="rxAdviceText">医嘱:{{ rxAdviceText }}</p>
|
||||
<p v-if="slipDietaryText">忌口:{{ slipDietaryText }}</p>
|
||||
<p v-if="rxRemarkText">备注:{{ rxRemarkText }}</p>
|
||||
<p v-if="rxPharmacyRemarkText" class="rx-text-warn">
|
||||
药房备注:{{ rxPharmacyRemarkText }}
|
||||
</p>
|
||||
<p v-if="rxOutPelletText" class="rx-text-warn">
|
||||
出丸:{{ rxOutPelletText }}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<!-- 底部表:医师/审方/配药/复核/发药 + 类型/剂数/单剂量 -->
|
||||
<div class="rx-bottom">
|
||||
<div class="rx-bot-row rx-bot-row-1">
|
||||
<div class="rx-bot-cell rx-bot-stack rx-bot-doctor">
|
||||
<div class="rx-bot-label">医师</div>
|
||||
<div class="rx-bot-doctor-body">
|
||||
<img
|
||||
v-if="slipView.doctor_signature"
|
||||
:src="slipView.doctor_signature"
|
||||
alt="医师签名"
|
||||
class="rx-bot-sign-img"
|
||||
/>
|
||||
<div v-if="!slipView.doctor_signature" class="rx-bot-doctor-name">
|
||||
{{ slipView.doctor_name || '—' }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="rx-bot-cell rx-bot-meta">
|
||||
<span class="rx-bot-meta-key">类型:</span>
|
||||
<span class="rx-bot-meta-val">{{ rxTypeText }}</span>
|
||||
</div>
|
||||
<div class="rx-bot-cell rx-bot-meta">
|
||||
<span class="rx-bot-meta-key">天数:</span>
|
||||
<span class="rx-bot-meta-val">{{ slipView.dose_count || '—' }}剂</span>
|
||||
</div>
|
||||
<div class="rx-bot-cell rx-bot-meta">
|
||||
<span class="rx-bot-meta-key">单剂量:</span>
|
||||
<span class="rx-bot-meta-val">{{ rxPerDoseAmount }}克</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
<!-- A4 处方纸:统一组件,与全端药房联一比一 -->
|
||||
<PrescriptionSlip v-if="slipView" ref="rxSlipRef" :data="slipView" />
|
||||
|
||||
<!-- 审核痕迹(不进入打印区) -->
|
||||
<div
|
||||
@@ -1719,6 +1583,7 @@ import { hasPermission } from '@/utils/perm'
|
||||
import type { FormInstance, FormRules } from 'element-plus'
|
||||
import DaterangePicker from '@/components/daterange-picker/index.vue'
|
||||
import MedicineNameSelect from '@/components/medicine-name-select/index.vue'
|
||||
import PrescriptionSlip from '@/components/prescription-slip/index.vue'
|
||||
import { Search, Download, Printer, EditPen } from '@element-plus/icons-vue'
|
||||
import { computed, onMounted, reactive, ref, watch, nextTick, defineAsyncComponent } from 'vue'
|
||||
import { useRouter } from 'vue-router'
|
||||
@@ -2542,9 +2407,14 @@ const slipDietaryText = computed(() => {
|
||||
/* ============================================================
|
||||
* A4 处方笺(药房联)相关计算
|
||||
* ============================================================ */
|
||||
const rxPaperRef = ref<HTMLElement | null>(null)
|
||||
const rxSlipRef = ref<InstanceType<typeof PrescriptionSlip> | null>(null)
|
||||
const downloadingSlip = ref(false)
|
||||
|
||||
function getRxPaperEl(): HTMLElement | null {
|
||||
const exposed = rxSlipRef.value as { paperEl?: HTMLElement | null } | null
|
||||
return exposed?.paperEl ?? null
|
||||
}
|
||||
|
||||
const padN = (n: number) => String(n).padStart(2, '0')
|
||||
|
||||
const rxDateText = computed(() => {
|
||||
@@ -2752,7 +2622,7 @@ function handlePrintSlip() {
|
||||
|
||||
/* === 下载 PDF(A4) === */
|
||||
async function handleDownloadSlip() {
|
||||
const node = rxPaperRef.value
|
||||
const node = getRxPaperEl()
|
||||
if (!node) {
|
||||
feedback.msgWarning('处方笺尚未渲染完成')
|
||||
return
|
||||
@@ -4155,10 +4025,7 @@ onMounted(async () => {
|
||||
overflow-y: auto;
|
||||
}
|
||||
|
||||
/* ============================================================
|
||||
* 处方笺(A4 药房联)样式
|
||||
* 严格按 A4:210mm × 297mm,padding 12mm
|
||||
* ============================================================ */
|
||||
/* 查看区外壳;A4 纸张样式见 @/components/prescription-slip */
|
||||
.rx-wrap {
|
||||
min-height: 160px;
|
||||
background: #f5f6f8;
|
||||
@@ -4193,370 +4060,6 @@ onMounted(async () => {
|
||||
border: 1px solid #fecaca;
|
||||
}
|
||||
|
||||
/* === A4 纸 === */
|
||||
.rx-paper {
|
||||
width: 210mm;
|
||||
min-height: 297mm;
|
||||
margin: 0 auto;
|
||||
background: #fff;
|
||||
box-sizing: border-box;
|
||||
padding: 8mm 10mm;
|
||||
color: #1f1f1f;
|
||||
font-size: 13px;
|
||||
line-height: 1.5;
|
||||
font-family: "Microsoft YaHei", "PingFang SC", -apple-system, BlinkMacSystemFont, "Segoe UI", sans-serif;
|
||||
box-shadow: 0 0 0 1px #d6d6d6, 0 6px 24px rgba(0, 0, 0, 0.06);
|
||||
/* 防止 html2canvas 把内部表格裁切 */
|
||||
overflow: visible;
|
||||
}
|
||||
|
||||
/* === 顶部告知 === */
|
||||
.rx-notice {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
background: #f3f4f6;
|
||||
border: 1px solid #e5e7eb;
|
||||
padding: 6px 10px;
|
||||
border-radius: 2px;
|
||||
font-size: 12px;
|
||||
color: #1f1f1f;
|
||||
margin-bottom: 6px;
|
||||
|
||||
.rx-notice-text {
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.rx-notice-meta {
|
||||
display: inline-flex;
|
||||
gap: 24px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
|
||||
/* === 患者信息表 === */
|
||||
.rx-info {
|
||||
border: 1px solid #c8c8c8;
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.rx-info-row {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(4, 1fr);
|
||||
border-bottom: 1px solid #c8c8c8;
|
||||
}
|
||||
|
||||
.rx-info-row-full {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
|
||||
.rx-info-cell {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
padding: 6px 10px;
|
||||
font-size: 13px;
|
||||
border-right: 1px solid #c8c8c8;
|
||||
min-height: 32px;
|
||||
|
||||
&:last-child { border-right: none; }
|
||||
|
||||
.rx-key {
|
||||
color: #1f1f1f;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
.rx-val {
|
||||
flex: 1;
|
||||
min-width: 0;
|
||||
word-break: break-all;
|
||||
}
|
||||
}
|
||||
|
||||
/* === Rp 区域 + 水印 === */
|
||||
.rx-rp {
|
||||
position: relative;
|
||||
border-left: 1px solid #c8c8c8;
|
||||
border-right: 1px solid #c8c8c8;
|
||||
padding: 8px 10px 16px;
|
||||
overflow: hidden;
|
||||
box-sizing: border-box;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.rx-watermark {
|
||||
position: absolute;
|
||||
/* 不使用 inset 简写:html2canvas 对 inset 解析不稳定,
|
||||
* 会导致水印容器尺寸异常,进而影响内层网格对齐 */
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
left: 0;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
font-size: 88px;
|
||||
font-weight: 700;
|
||||
color: rgba(0, 0, 0, 0.06);
|
||||
transform: rotate(-22deg);
|
||||
letter-spacing: 0.18em;
|
||||
pointer-events: none;
|
||||
user-select: none;
|
||||
z-index: 0;
|
||||
}
|
||||
|
||||
.rx-rp-head {
|
||||
display: grid;
|
||||
grid-template-columns: 36px 1fr;
|
||||
align-items: end;
|
||||
column-gap: 8px;
|
||||
padding-bottom: 4px;
|
||||
border-bottom: 1px solid #d4d4d4;
|
||||
margin-bottom: 6px;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.rx-rp-label {
|
||||
font-size: 16px;
|
||||
font-weight: 700;
|
||||
color: #1f1f1f;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.rx-rp-cols-head {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
column-gap: 24px;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
/* 注意:列内 1fr 64px 与 .rx-herb-cell 完全一致,
|
||||
* 配合 .rx-herbs 与 .rx-rp-cols-head 同样的左侧 44px 缩进,
|
||||
* 即可保证「用药 (单剂)」与药材名、「总量」与总量数字精确对齐。 */
|
||||
.rx-rp-col-head {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 64px;
|
||||
column-gap: 8px;
|
||||
font-size: 13px;
|
||||
color: #1f1f1f;
|
||||
min-width: 0;
|
||||
|
||||
& > span:last-child {
|
||||
text-align: right;
|
||||
}
|
||||
}
|
||||
|
||||
.rx-herbs {
|
||||
/* 与 .rx-rp-head 中 Rp. 列(36px) + column-gap(8px) 等宽缩进,
|
||||
* 这样下方两列的左右边界与表头两列完全对齐。 */
|
||||
padding-left: 44px;
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 1fr;
|
||||
column-gap: 24px;
|
||||
row-gap: 6px;
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
width: 100%;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.rx-herb-section-label {
|
||||
grid-column: 1 / -1;
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
color: #409eff;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.rx-herb-section-label--aux {
|
||||
color: #e6a23c;
|
||||
margin-top: 4px;
|
||||
}
|
||||
|
||||
.herb-formula-block__head {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.usage-formula-title {
|
||||
font-size: 13px;
|
||||
font-weight: 600;
|
||||
margin: 8px 0 4px;
|
||||
color: #409eff;
|
||||
}
|
||||
|
||||
.usage-formula-title--aux {
|
||||
color: #e6a23c;
|
||||
margin-top: 12px;
|
||||
}
|
||||
|
||||
.rx-herb-cell {
|
||||
display: grid;
|
||||
grid-template-columns: 1fr 64px;
|
||||
column-gap: 8px;
|
||||
font-size: 13px;
|
||||
line-height: 1.85;
|
||||
min-width: 0;
|
||||
}
|
||||
|
||||
.rx-herb-name {
|
||||
color: #1f1f1f;
|
||||
min-width: 0;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.rx-herb-total {
|
||||
color: #1f1f1f;
|
||||
text-align: right;
|
||||
font-variant-numeric: tabular-nums;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
/* === 服法 / 备注 文本块 === */
|
||||
.rx-text {
|
||||
border: 1px solid #c8c8c8;
|
||||
border-top: none;
|
||||
padding: 10px 12px;
|
||||
font-size: 13px;
|
||||
line-height: 1.85;
|
||||
|
||||
p { margin: 0; }
|
||||
|
||||
.rx-text-warn {
|
||||
color: #d72424;
|
||||
font-weight: 600;
|
||||
}
|
||||
}
|
||||
|
||||
/* === 底部表格 === */
|
||||
.rx-bottom {
|
||||
border: 1px solid #c8c8c8;
|
||||
border-top: none;
|
||||
}
|
||||
|
||||
.rx-bot-row {
|
||||
display: grid;
|
||||
border-bottom: 1px solid #c8c8c8;
|
||||
|
||||
&:last-child { border-bottom: none; }
|
||||
}
|
||||
|
||||
.rx-bot-row-1 {
|
||||
/* 类型/剂数/单剂量 三个 meta 列适当加宽,
|
||||
* 避免"浓缩丸-浓缩水丸"被强制折行后把整行挤错位 */
|
||||
grid-template-columns: 1.4fr 0.6fr 0.6fr 0.6fr 0.6fr 1.7fr 0.95fr 1.25fr;
|
||||
min-height: 70px;
|
||||
}
|
||||
|
||||
.rx-bot-row-2 {
|
||||
grid-template-columns: repeat(6, 1fr);
|
||||
}
|
||||
|
||||
/* 默认:单行内容左对齐、上下居中(适配 类型/剂数/单剂量/费用 等格子)。
|
||||
* 需要"标题置顶 + 签名留白"的格子(医师/审方/配药/复核/发药)请加 .rx-bot-stack。
|
||||
* 这样无需任何 !important 反向覆盖,html2canvas/原生渲染表现一致。 */
|
||||
.rx-bot-cell {
|
||||
border-right: 1px solid #c8c8c8;
|
||||
padding: 6px 10px;
|
||||
font-size: 12px;
|
||||
color: #1f1f1f;
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
justify-content: flex-start;
|
||||
min-height: 32px;
|
||||
box-sizing: border-box;
|
||||
|
||||
&:last-child { border-right: none; }
|
||||
}
|
||||
|
||||
.rx-bot-stack {
|
||||
flex-direction: column;
|
||||
align-items: flex-start;
|
||||
justify-content: flex-start;
|
||||
}
|
||||
|
||||
.rx-bot-label {
|
||||
font-size: 13px;
|
||||
color: #1f1f1f;
|
||||
margin-bottom: 4px;
|
||||
}
|
||||
|
||||
.rx-bot-doctor {
|
||||
.rx-bot-doctor-body {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
min-height: 36px;
|
||||
}
|
||||
|
||||
.rx-bot-doctor-name {
|
||||
font-size: 13px;
|
||||
color: #1f1f1f;
|
||||
}
|
||||
|
||||
.rx-bot-sign-img {
|
||||
max-height: 40px;
|
||||
max-width: 110px;
|
||||
object-fit: contain;
|
||||
}
|
||||
}
|
||||
|
||||
.rx-bot-meta {
|
||||
flex-wrap: nowrap;
|
||||
|
||||
.rx-bot-meta-key {
|
||||
color: #1f1f1f;
|
||||
margin-right: 6px;
|
||||
white-space: nowrap;
|
||||
flex-shrink: 0;
|
||||
line-height: 1.4;
|
||||
}
|
||||
|
||||
.rx-bot-meta-val {
|
||||
font-weight: 500;
|
||||
white-space: nowrap;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
min-width: 0;
|
||||
line-height: 1.4;
|
||||
}
|
||||
}
|
||||
|
||||
.rx-fee {
|
||||
font-size: 13px;
|
||||
line-height: 1.4;
|
||||
padding: 8px 10px;
|
||||
|
||||
.rx-fee-key {
|
||||
color: #1f1f1f;
|
||||
margin-right: 6px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.rx-fee-val {
|
||||
font-variant-numeric: tabular-nums;
|
||||
white-space: nowrap;
|
||||
}
|
||||
}
|
||||
|
||||
.rx-fee-total {
|
||||
font-weight: 700;
|
||||
background: #fafafa;
|
||||
|
||||
.rx-fee-val { font-weight: 700; }
|
||||
}
|
||||
|
||||
/* === 审核痕迹(不打印) === */
|
||||
.rx-audit {
|
||||
margin-top: 12px;
|
||||
@@ -4569,8 +4072,6 @@ onMounted(async () => {
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
/* 打印控制规则放到非 scoped 的全局 style 中,参见底部 <style> */
|
||||
|
||||
/* 创建业务订单弹窗:宽松留白、默认字号、卡片化信息区 */
|
||||
.create-order-dialog {
|
||||
:deep(.el-dialog__header) {
|
||||
@@ -4865,28 +4366,4 @@ onMounted(async () => {
|
||||
}
|
||||
</style>
|
||||
|
||||
<!-- 全局打印样式(不能 scoped,否则 body * 不生效) -->
|
||||
<style>
|
||||
@media print {
|
||||
body * { visibility: hidden !important; }
|
||||
|
||||
.rx-paper, .rx-paper * { visibility: visible !important; }
|
||||
|
||||
.rx-paper {
|
||||
position: fixed !important;
|
||||
left: 0 !important;
|
||||
top: 0 !important;
|
||||
width: 210mm !important;
|
||||
height: 297mm !important;
|
||||
margin: 0 !important;
|
||||
padding: 8mm 10mm !important;
|
||||
box-shadow: none !important;
|
||||
page-break-after: always;
|
||||
}
|
||||
|
||||
@page {
|
||||
size: A4;
|
||||
margin: 0;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
@@ -1770,12 +1770,6 @@
|
||||
</div>
|
||||
</template>
|
||||
<div v-loading="prescriptionViewLoading" class="rx-wrap">
|
||||
<!-- 处方签签 -->
|
||||
<el-tabs v-model="prescriptionTabType" class="mx-4 mt-2" style="margin-bottom: 0px; z-index: 1; position: relative;" v-if="prescriptionViewData">
|
||||
<el-tab-pane label="药房联" name="internal" />
|
||||
<el-tab-pane label="处方联" name="user" />
|
||||
</el-tabs>
|
||||
|
||||
<!-- 状态条(不进入打印/导出范围) -->
|
||||
<div class="rx-statusbar" v-if="prescriptionViewData">
|
||||
<el-tag
|
||||
@@ -1824,205 +1818,11 @@
|
||||
<div v-if="prescriptionViewData.audit_remark" class="mt-1">驳回意见:{{ prescriptionViewData.audit_remark }}</div>
|
||||
</div>
|
||||
|
||||
<!-- A4 处方纸(药房联) -->
|
||||
<div v-if="prescriptionViewData" ref="prescriptionSlipPrintRef" class="rx-paper">
|
||||
<div class="rx-title">{{ prescriptionTabType === 'internal' ? '药房联' : SLIP_TITLE }}</div>
|
||||
<div class="rx-notice">
|
||||
<span class="rx-notice-text">
|
||||
{{
|
||||
prescriptionTabType === 'user'
|
||||
? '服药前请核对姓名、电话、医生等信息以及医嘱等要点'
|
||||
: '服药前请核对姓名、电话、医生等信息以及服法、医嘱等要点'
|
||||
}}
|
||||
</span>
|
||||
<div class="rx-notice-meta">
|
||||
<div class="rx-meta-item">
|
||||
<span class="rx-meta-label">日期</span>
|
||||
<span class="rx-meta-value">{{ rxDateText }}</span>
|
||||
</div>
|
||||
<div class="rx-meta-item">
|
||||
<span class="rx-meta-label">处方编号</span>
|
||||
<span class="rx-meta-value">{{ rxPrescriptionSnText }}</span>
|
||||
</div>
|
||||
<div class="rx-meta-item">
|
||||
<span class="rx-meta-label">流转编号(挂号)</span>
|
||||
<span class="rx-meta-value">{{ rxAppointmentFlowText }}</span>
|
||||
</div>
|
||||
<div v-if="rxBusinessOrderNoText" class="rx-meta-item">
|
||||
<span class="rx-meta-label">业务单号</span>
|
||||
<span class="rx-meta-value">{{ rxBusinessOrderNoText }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="rx-info">
|
||||
<div class="rx-info-row">
|
||||
<div class="rx-info-cell">
|
||||
<span class="rx-key">姓名</span>
|
||||
<span class="rx-val">{{ prescriptionViewData.patient_name || '—' }}</span>
|
||||
</div>
|
||||
<div class="rx-info-cell">
|
||||
<span class="rx-key">性别</span>
|
||||
<span class="rx-val">{{ slipGenderLabel(prescriptionViewData.gender) }}</span>
|
||||
</div>
|
||||
<div class="rx-info-cell">
|
||||
<span class="rx-key">年龄</span>
|
||||
<span class="rx-val">{{ slipAgeText(prescriptionViewData.age) }}</span>
|
||||
</div>
|
||||
<div class="rx-info-cell">
|
||||
<span class="rx-key">电话</span>
|
||||
<span class="rx-val">{{ prescriptionViewData.phone || prescriptionViewData.recipient_phone || '—' }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="rx-info-row rx-info-row-full">
|
||||
<div class="rx-info-cell">
|
||||
<span class="rx-key">收件信息</span>
|
||||
<span class="rx-val">{{ rxRecipientText }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="rx-info-row rx-info-row-full">
|
||||
<div class="rx-info-cell">
|
||||
<span class="rx-key">临床诊断</span>
|
||||
<span class="rx-val">{{ prescriptionViewData.clinical_diagnosis || '—' }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="rx-rp">
|
||||
<div class="rx-watermark">{{ prescriptionTabType === 'internal' ? '药房联' : '处方联' }}</div>
|
||||
<div class="rx-rp-head">
|
||||
<div class="rx-rp-label">Rp.</div>
|
||||
<div class="rx-rp-cols-head">
|
||||
<div class="rx-rp-col-head">
|
||||
<span>用药 (单剂)</span>
|
||||
<span>{{ prescriptionTabType === 'user' ? '用量' : '总量' }}</span>
|
||||
</div>
|
||||
<div class="rx-rp-col-head">
|
||||
<span>用药 (单剂)</span>
|
||||
<span>{{ prescriptionTabType === 'user' ? '用量' : '总量' }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="rx-herbs">
|
||||
<template v-if="prescriptionTabType === 'internal'">
|
||||
<template v-if="slipMainHerbs.length">
|
||||
<div class="rx-herb-section-label">主方</div>
|
||||
<div
|
||||
v-for="(h, i) in slipMainHerbs"
|
||||
:key="'main-' + i"
|
||||
class="rx-herb-cell"
|
||||
>
|
||||
<span class="rx-herb-name">{{ h.name }} ({{ h.dosage }}克)</span>
|
||||
<span class="rx-herb-total">{{ rxHerbTotal(h.dosage) }}克</span>
|
||||
</div>
|
||||
</template>
|
||||
<template v-if="slipAuxHerbs.length">
|
||||
<div class="rx-herb-section-label rx-herb-section-label--aux">
|
||||
辅方<span v-if="slipAuxLibraryName" class="rx-herb-library-name">({{ slipAuxLibraryName }})</span>
|
||||
</div>
|
||||
<div
|
||||
v-for="(h, i) in slipAuxHerbs"
|
||||
:key="'aux-' + i"
|
||||
class="rx-herb-cell"
|
||||
>
|
||||
<span class="rx-herb-name">{{ h.name }} ({{ h.dosage }}克)</span>
|
||||
<span class="rx-herb-total">{{ rxHerbTotal(h.dosage) }}克</span>
|
||||
</div>
|
||||
</template>
|
||||
</template>
|
||||
<template v-else>
|
||||
<template v-if="slipMainHerbs.length">
|
||||
<div class="rx-herb-section-label">主方</div>
|
||||
<div
|
||||
v-for="(h, i) in slipMainHerbs"
|
||||
:key="'user-main-' + i"
|
||||
class="rx-herb-cell"
|
||||
>
|
||||
<span class="rx-herb-name">{{ h.name }}</span>
|
||||
<span class="rx-herb-total">{{ h.dosage }}克</span>
|
||||
</div>
|
||||
</template>
|
||||
<template v-if="slipAuxHerbs.length">
|
||||
<div class="rx-herb-section-label rx-herb-section-label--aux">辅方</div>
|
||||
<div
|
||||
v-for="(h, i) in slipAuxHerbs"
|
||||
:key="'user-aux-' + i"
|
||||
class="rx-herb-cell"
|
||||
>
|
||||
<span class="rx-herb-name">{{ h.name }}</span>
|
||||
<span class="rx-herb-total">{{ h.dosage }}克</span>
|
||||
</div>
|
||||
</template>
|
||||
</template>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="rx-text">
|
||||
<template v-if="prescriptionTabType === 'internal'">
|
||||
<p v-if="rxAuxUsageText">主服法:{{ rxUsageText }}</p>
|
||||
<p v-else>服法:{{ rxUsageText }}</p>
|
||||
<p v-if="rxAuxUsageText">辅服法:{{ rxAuxUsageText }}</p>
|
||||
</template>
|
||||
<p v-if="rxAdviceText">医嘱:{{ rxAdviceText }}</p>
|
||||
<p v-if="slipDietaryText">忌口:{{ slipDietaryText }}</p>
|
||||
<p v-if="prescriptionTabType === 'internal' && rxRemarkText">备注:{{ rxRemarkText }}</p>
|
||||
<p v-if="rxPharmacyRemarkText" class="rx-text-warn">
|
||||
药房备注:{{ rxPharmacyRemarkText }}
|
||||
</p>
|
||||
<p
|
||||
v-if="prescriptionTabType === 'internal' && rxOutPelletText && prescriptionViewData?.prescription_type !== '饮片'"
|
||||
class="rx-text-warn"
|
||||
>
|
||||
出丸:{{ rxOutPelletText }}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="rx-bottom">
|
||||
<div
|
||||
class="rx-bot-row rx-bot-row-1"
|
||||
:class="{ 'rx-bot-row-1--user': prescriptionTabType === 'user' }"
|
||||
>
|
||||
<div class="rx-bot-cell rx-bot-doctor">
|
||||
<div class="rx-bot-label rx-bot-label--doctor">医师</div>
|
||||
<div class="rx-bot-doctor-body">
|
||||
<img
|
||||
v-if="prescriptionViewData.doctor_signature"
|
||||
:src="prescriptionViewData.doctor_signature"
|
||||
alt="医师签名"
|
||||
class="rx-bot-sign-img"
|
||||
/>
|
||||
<span
|
||||
v-if="!prescriptionViewData.doctor_signature"
|
||||
class="rx-bot-doctor-name"
|
||||
>{{ prescriptionViewData.doctor_name || '—' }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div v-if="prescriptionTabType === 'internal'" class="rx-bot-cell rx-bot-meta">
|
||||
<span class="rx-bot-meta-key">类型:</span>
|
||||
<span class="rx-bot-meta-val">{{ rxTypeText }}</span>
|
||||
</div>
|
||||
<div class="rx-bot-cell rx-bot-meta">
|
||||
<span class="rx-bot-meta-key">天数:</span>
|
||||
<span class="rx-bot-meta-val">
|
||||
{{
|
||||
detailData?.medication_days != null &&
|
||||
String(detailData.medication_days).trim() !== ''
|
||||
? detailData.medication_days + ' 天'
|
||||
: '—'
|
||||
}} </span>
|
||||
</div>
|
||||
<div v-if="prescriptionTabType === 'internal'" class="rx-bot-cell rx-bot-meta">
|
||||
<span class="rx-bot-meta-key">剂量:</span>
|
||||
<span class="rx-bot-meta-val">{{ rxPerDoseAmount }}克</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="rx-bot-row rx-hospital-row">
|
||||
<div class="rx-hospital-line">成都双流甄养堂互联网医院有限公司 联系方式:4001667339</div>
|
||||
<div class="rx-hospital-line">地址:四川省成都市双流区黄甲街道黄龙大道二段280号</div>
|
||||
</div>
|
||||
</div>
|
||||
<PrescriptionSlip
|
||||
v-if="prescriptionViewData"
|
||||
ref="rxSlipRef"
|
||||
:data="prescriptionViewData"
|
||||
/>
|
||||
|
||||
<!-- 审核痕迹(不进入导出) -->
|
||||
<div
|
||||
@@ -2281,6 +2081,7 @@ import {
|
||||
getDoctors,
|
||||
getAssistants
|
||||
} from '@/api/tcm'
|
||||
import PrescriptionSlip from '@/components/prescription-slip/index.vue'
|
||||
import html2canvas from 'html2canvas'
|
||||
import { jsPDF } from 'jspdf'
|
||||
import { getDictData } from '@/api/app'
|
||||
@@ -4665,8 +4466,7 @@ const prescriptionViewLoading = ref(false)
|
||||
const prescriptionViewData = ref<any>(null)
|
||||
/** 药房联辅方标题:处方库中该辅方模板的 prescription_name(导入或药材匹配解析) */
|
||||
const slipAuxLibraryName = ref('')
|
||||
const prescriptionTabType = ref('internal')
|
||||
const prescriptionSlipPrintRef = ref<HTMLElement | null>(null)
|
||||
const rxSlipRef = ref<InstanceType<typeof PrescriptionSlip> | null>(null)
|
||||
const prescriptionSlipExporting = ref(false)
|
||||
|
||||
function buildSlipUsageSegmentText(
|
||||
@@ -5064,8 +4864,7 @@ async function openPrescriptionView(row: any) {
|
||||
prescriptionViewLoading.value = true
|
||||
prescriptionViewData.value = null
|
||||
slipAuxLibraryName.value = ''
|
||||
prescriptionTabType.value = 'internal'
|
||||
|
||||
|
||||
try {
|
||||
const res: any = await prescriptionDetail({ id: row.prescription_id })
|
||||
const base = res?.data ?? res ?? null
|
||||
@@ -5135,7 +4934,7 @@ async function preloadSlipImagesToDataUrl(el: HTMLElement): Promise<void> {
|
||||
|
||||
async function capturePrescriptionSlipCanvas(): Promise<HTMLCanvasElement> {
|
||||
await nextTick()
|
||||
const el = prescriptionSlipPrintRef.value
|
||||
const el = (rxSlipRef.value as { paperEl?: HTMLElement | null } | null)?.paperEl ?? null
|
||||
if (!el) throw new Error('未找到处方笺内容')
|
||||
el.scrollIntoView({ behavior: 'instant', block: 'nearest' })
|
||||
await preloadSlipImagesToDataUrl(el)
|
||||
|
||||
@@ -2390,145 +2390,11 @@
|
||||
<div v-if="prescriptionViewData.audit_remark" class="mt-1">驳回意见:{{ prescriptionViewData.audit_remark }}</div>
|
||||
</div>
|
||||
|
||||
<!-- A4 处方纸(药房联) -->
|
||||
<div v-if="prescriptionViewData" ref="prescriptionSlipPrintRef" class="rx-paper">
|
||||
<div class="rx-title">{{ SLIP_TITLE }}</div>
|
||||
<div class="rx-notice">
|
||||
<span class="rx-notice-text">
|
||||
服药前请核对姓名、电话、医生等信息以及服法、医嘱等要点
|
||||
</span>
|
||||
<div class="rx-notice-meta">
|
||||
<div class="rx-meta-item">
|
||||
<span class="rx-meta-label">日期</span>
|
||||
<span class="rx-meta-value">{{ rxDateText }}</span>
|
||||
</div>
|
||||
<div class="rx-meta-item">
|
||||
<span class="rx-meta-label">处方编号</span>
|
||||
<span class="rx-meta-value">{{ rxPrescriptionSnText }}</span>
|
||||
</div>
|
||||
<div class="rx-meta-item">
|
||||
<span class="rx-meta-label">流转编号(挂号)</span>
|
||||
<span class="rx-meta-value">{{ rxAppointmentFlowText }}</span>
|
||||
</div>
|
||||
<div v-if="rxBusinessOrderNoText" class="rx-meta-item">
|
||||
<span class="rx-meta-label">业务单号</span>
|
||||
<span class="rx-meta-value">{{ rxBusinessOrderNoText }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="rx-info">
|
||||
<div class="rx-info-row">
|
||||
<div class="rx-info-cell">
|
||||
<span class="rx-key">姓名</span>
|
||||
<span class="rx-val">{{ prescriptionViewData.patient_name || '—' }}</span>
|
||||
</div>
|
||||
<div class="rx-info-cell">
|
||||
<span class="rx-key">性别</span>
|
||||
<span class="rx-val">{{ slipGenderLabel(prescriptionViewData.gender) }}</span>
|
||||
</div>
|
||||
<div class="rx-info-cell">
|
||||
<span class="rx-key">年龄</span>
|
||||
<span class="rx-val">{{ slipAgeText(prescriptionViewData.age) }}</span>
|
||||
</div>
|
||||
<div class="rx-info-cell">
|
||||
<span class="rx-key">电话</span>
|
||||
<span class="rx-val">{{ prescriptionViewData.phone || prescriptionViewData.recipient_phone || '—' }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="rx-info-row rx-info-row-full">
|
||||
<div class="rx-info-cell">
|
||||
<span class="rx-key">收件信息</span>
|
||||
<span class="rx-val">{{ rxRecipientText }}</span>
|
||||
</div>
|
||||
</div>
|
||||
<div class="rx-info-row rx-info-row-full">
|
||||
<div class="rx-info-cell">
|
||||
<span class="rx-key">临床诊断</span>
|
||||
<span class="rx-val">{{ prescriptionViewData.clinical_diagnosis || '—' }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="rx-rp">
|
||||
<div class="rx-watermark">药房联</div>
|
||||
<div class="rx-rp-head">
|
||||
<div class="rx-rp-label">Rp.</div>
|
||||
<div class="rx-rp-cols-head">
|
||||
<div class="rx-rp-col-head">
|
||||
<span>用药 (单剂)</span>
|
||||
<span>总量</span>
|
||||
</div>
|
||||
<div class="rx-rp-col-head">
|
||||
<span>用药 (单剂)</span>
|
||||
<span>总量</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="rx-herbs">
|
||||
<div v-for="(h, i) in slipHerbsList" :key="i" class="rx-herb-cell">
|
||||
<span class="rx-herb-name">{{ h.name }} ({{ h.dosage }}克)</span>
|
||||
<span class="rx-herb-total">{{ rxHerbTotal(h.dosage) }}克</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="rx-text">
|
||||
<p>服法:{{ rxUsageText }}</p>
|
||||
<p v-if="rxAdviceText">医嘱:{{ rxAdviceText }}</p>
|
||||
<p v-if="slipDietaryText">忌口:{{ slipDietaryText }}</p>
|
||||
<p v-if="rxRemarkText">备注:{{ rxRemarkText }}</p>
|
||||
<p v-if="rxPharmacyRemarkText" class="rx-text-warn">
|
||||
药房备注:{{ rxPharmacyRemarkText }}
|
||||
</p>
|
||||
<p v-if="rxOutPelletText" class="rx-text-warn">
|
||||
出丸:{{ rxOutPelletText }}
|
||||
</p>
|
||||
</div>
|
||||
|
||||
<div class="rx-bottom">
|
||||
<div class="rx-bot-row rx-bot-row-1">
|
||||
<div class="rx-bot-cell rx-bot-doctor">
|
||||
<div class="rx-bot-label rx-bot-label--doctor">医师</div>
|
||||
<div class="rx-bot-doctor-body">
|
||||
<img
|
||||
v-if="prescriptionViewData.doctor_signature"
|
||||
:src="prescriptionViewData.doctor_signature"
|
||||
alt="医师签名"
|
||||
class="rx-bot-sign-img"
|
||||
/>
|
||||
<span
|
||||
v-if="!prescriptionViewData.doctor_signature"
|
||||
class="rx-bot-doctor-name"
|
||||
>{{ prescriptionViewData.doctor_name || '—' }}</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="rx-bot-cell rx-bot-meta">
|
||||
<span class="rx-bot-meta-key">类型:</span>
|
||||
<span class="rx-bot-meta-val">{{ rxTypeText }}</span>
|
||||
</div>
|
||||
<div class="rx-bot-cell rx-bot-meta">
|
||||
<span class="rx-bot-meta-key">天数:</span>
|
||||
<span class="rx-bot-meta-val">
|
||||
{{
|
||||
detailData.medication_days != null &&
|
||||
String(detailData.medication_days).trim() !== ''
|
||||
? detailData.medication_days + ' 天'
|
||||
: '—'
|
||||
}} </span>
|
||||
</div>
|
||||
<div class="rx-bot-cell rx-bot-meta">
|
||||
<span class="rx-bot-meta-key">剂量:</span>
|
||||
<span class="rx-bot-meta-val">{{ rxPerDoseAmount }}克</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
</div>
|
||||
<div class="rx-bot-row rx-hospital-row">
|
||||
<div class="rx-hospital-line">成都双流甄养堂互联网医院有限公司 联系方式:4001667339</div>
|
||||
<div class="rx-hospital-line">地址:四川省成都市双流区黄甲街道黄龙大道二段280号</div>
|
||||
</div>
|
||||
</div>
|
||||
<PrescriptionSlip
|
||||
v-if="prescriptionViewData"
|
||||
ref="rxSlipRef"
|
||||
:data="prescriptionViewData"
|
||||
/>
|
||||
|
||||
<!-- 审核痕迹(不进入导出) -->
|
||||
<div
|
||||
@@ -2750,6 +2616,7 @@ import {
|
||||
supplyModeLabel,
|
||||
supplyModeTagType
|
||||
} from './components/prescription-order-utils'
|
||||
import PrescriptionSlip from '@/components/prescription-slip/index.vue'
|
||||
import html2canvas from 'html2canvas'
|
||||
import { jsPDF } from 'jspdf'
|
||||
import { getDictData } from '@/api/app'
|
||||
@@ -5429,7 +5296,7 @@ const SLIP_ADDRESS_LINE = '地址:四川省成都市双流区黄甲街道黄
|
||||
const prescriptionViewVisible = ref(false)
|
||||
const prescriptionViewLoading = ref(false)
|
||||
const prescriptionViewData = ref<any>(null)
|
||||
const prescriptionSlipPrintRef = ref<HTMLElement | null>(null)
|
||||
const rxSlipRef = ref<InstanceType<typeof PrescriptionSlip> | null>(null)
|
||||
const prescriptionSlipExporting = ref(false)
|
||||
|
||||
// 处方单辅助函数
|
||||
@@ -5763,7 +5630,7 @@ async function preloadSlipImagesToDataUrl(el: HTMLElement): Promise<void> {
|
||||
|
||||
async function capturePrescriptionSlipCanvas(): Promise<HTMLCanvasElement> {
|
||||
await nextTick()
|
||||
const el = prescriptionSlipPrintRef.value
|
||||
const el = (rxSlipRef.value as { paperEl?: HTMLElement | null } | null)?.paperEl ?? null
|
||||
if (!el) throw new Error('未找到处方笺内容')
|
||||
el.scrollIntoView({ behavior: 'instant', block: 'nearest' })
|
||||
await preloadSlipImagesToDataUrl(el)
|
||||
|
||||
Reference in New Issue
Block a user