This commit is contained in:
Your Name
2026-04-07 18:13:03 +08:00
parent a780356908
commit fdf714f833
397 changed files with 15086 additions and 1043 deletions
+76 -60
View File
@@ -234,15 +234,10 @@
<canvas
ref="signatureCanvasRef"
class="signature-canvas"
width="280"
height="120"
@mousedown="onSignatureStart"
@mousemove="onSignatureMove"
@mouseup="onSignatureEnd"
@mouseleave="onSignatureEnd"
@touchstart.prevent="onSignatureStart"
@touchmove.prevent="onSignatureMove"
@touchend.prevent="onSignatureEnd"
@pointerdown.prevent="onSignaturePointerDown"
@pointermove.prevent="onSignaturePointerMove"
@pointerup.prevent="onSignaturePointerUp"
@pointercancel.prevent="onSignaturePointerUp"
/>
<div class="signature-actions">
<el-button size="small" @click="clearSignature">清空签名</el-button>
@@ -399,7 +394,7 @@
<div class="cr-item"><span class="cr-label">诊断类型</span>{{ getDictLabel(diagnosisTypeOptions, savedPrescription.case_record.diagnosis_type) || '—' }}</div>
<div class="cr-item"><span class="cr-label">证型</span>{{ getDictLabel(syndromeTypeOptions, savedPrescription.case_record.syndrome_type) || '—' }}</div>
<div class="cr-item"><span class="cr-label">糖尿病期数</span>{{ getDictLabel(diabetesTypeOptions, savedPrescription.case_record.diabetes_type) || '—' }}</div>
<div class="cr-item"><span class="cr-label">发现糖尿病患病史</span>{{ savedPrescription.case_record.diabetes_discovery_year != null ? savedPrescription.case_record.diabetes_discovery_year + ' 年' : '—' }}</div>
<div class="cr-item"><span class="cr-label">发现糖尿病患病史</span>{{ formatDiabetesDiscoveryDisplay(savedPrescription.case_record?.diabetes_discovery_year) }}</div>
<div class="cr-item cr-full"><span class="cr-label">当地医院诊断</span>{{ formatLocalDiagnosis(savedPrescription.case_record.local_hospital_diagnosis) }}</div>
<div class="cr-item cr-full"><span class="cr-label">当地就诊医院</span>{{ savedPrescription.case_record.local_hospital_name || '—' }}</div>
</div>
@@ -562,6 +557,7 @@
<script setup lang="ts">
import MedicineNameSelect from '@/components/medicine-name-select/index.vue'
import feedback from '@/utils/feedback'
import { formatDiabetesDiscoveryDisplay } from '@/utils/diabetes-discovery-display'
import { prescriptionAdd, prescriptionDetail, prescriptionGetByAppointment, prescriptionVoid, tcmDiagnosisDetail, prescriptionLibraryLists } from '@/api/tcm'
import { getDictData } from '@/api/app'
import html2canvas from 'html2canvas'
@@ -618,7 +614,11 @@ const voiding = ref(false)
const prescriptionPrintRef = ref<HTMLElement>()
const caseRecordPrintRef = ref<HTMLElement>()
const signatureCanvasRef = ref<HTMLCanvasElement>()
const isDrawing = ref(false)
/** 当前一笔的 pointerId(配合 setPointerCapture,移出画布仍跟手) */
const signatureActivePointerId = ref<number | null>(null)
/** 签名板 CSS 像素尺寸(内部分辨率按 DPR 放大,触摸更跟手) */
const SIGNATURE_CSS_W = 560
const SIGNATURE_CSS_H = 168
const savedPrescription = ref<any>(null)
/** 已通过且未作废:与消费者处方「查看」一致,仅预览,不可作废/再开新方 */
@@ -874,76 +874,90 @@ const handleClose = () => {
savedPrescription.value = null
}
const getSignatureCoords = (e: MouseEvent | TouchEvent) => {
/**
* 签名坐标:与 initSignatureCanvas 里 ctx.scale(dpr) 后的「逻辑像素」一致(0…SIGNATURE_CSS_W/H),
* 不能用 canvas.width/rect.width(会把 dpr 乘两次,笔画和鼠标严重错位)。
*/
const getSignatureLocalPoint = (e: PointerEvent) => {
const canvas = signatureCanvasRef.value
if (!canvas) return { x: 0, y: 0 }
const rect = canvas.getBoundingClientRect()
const scaleX = canvas.width / rect.width
const scaleY = canvas.height / rect.height
if (e instanceof TouchEvent) {
const touch = e.touches[0] || e.changedTouches[0]
return {
x: (touch.clientX - rect.left) * scaleX,
y: (touch.clientY - rect.top) * scaleY
}
}
if (rect.width <= 0 || rect.height <= 0) return { x: 0, y: 0 }
const x = ((e.clientX - rect.left) / rect.width) * SIGNATURE_CSS_W
const y = ((e.clientY - rect.top) / rect.height) * SIGNATURE_CSS_H
return {
x: (e.clientX - rect.left) * scaleX,
y: (e.clientY - rect.top) * scaleY
x: Math.max(0, Math.min(SIGNATURE_CSS_W, x)),
y: Math.max(0, Math.min(SIGNATURE_CSS_H, y))
}
}
const onSignatureStart = (e: MouseEvent | TouchEvent) => {
isDrawing.value = true
const ctx = signatureCanvasRef.value?.getContext('2d')
if (!ctx) return
const { x, y } = getSignatureCoords(e)
ctx.beginPath()
ctx.moveTo(x, y)
}
const onSignatureMove = (e: MouseEvent | TouchEvent) => {
if (!isDrawing.value) return
const ctx = signatureCanvasRef.value?.getContext('2d')
if (!ctx) return
const { x, y } = getSignatureCoords(e)
ctx.lineTo(x, y)
ctx.stroke()
}
const onSignatureEnd = () => {
isDrawing.value = false
const commitSignatureImage = () => {
const canvas = signatureCanvasRef.value
if (canvas) {
formData.doctor_signature = canvas.toDataURL('image/png')
}
}
const clearSignature = () => {
const onSignaturePointerDown = (e: PointerEvent) => {
if (e.pointerType === 'mouse' && e.button !== 0) return
const canvas = signatureCanvasRef.value
const ctx = canvas?.getContext('2d')
if (!canvas || !ctx) return
canvas.setPointerCapture(e.pointerId)
signatureActivePointerId.value = e.pointerId
const { x, y } = getSignatureLocalPoint(e)
ctx.beginPath()
ctx.moveTo(x, y)
}
const onSignaturePointerMove = (e: PointerEvent) => {
if (signatureActivePointerId.value !== e.pointerId) return
const ctx = signatureCanvasRef.value?.getContext('2d')
if (!ctx) return
const { x, y } = getSignatureLocalPoint(e)
ctx.lineTo(x, y)
ctx.stroke()
ctx.beginPath()
ctx.moveTo(x, y)
}
const onSignaturePointerUp = (e: PointerEvent) => {
if (signatureActivePointerId.value !== e.pointerId) return
signatureActivePointerId.value = null
const canvas = signatureCanvasRef.value
if (canvas) {
const ctx = canvas.getContext('2d')
if (ctx) {
ctx.fillStyle = '#fff'
ctx.fillRect(0, 0, canvas.width, canvas.height)
try {
canvas.releasePointerCapture(e.pointerId)
} catch {
/* 已释放或非当前捕获 */
}
formData.doctor_signature = ''
}
commitSignatureImage()
}
const clearSignature = () => {
initSignatureCanvas()
formData.doctor_signature = ''
}
const initSignatureCanvas = () => {
const canvas = signatureCanvasRef.value
if (canvas) {
const ctx = canvas.getContext('2d')
if (ctx) {
ctx.fillStyle = '#fff'
ctx.fillRect(0, 0, canvas.width, canvas.height)
ctx.strokeStyle = '#333'
ctx.lineWidth = 2
ctx.lineCap = 'round'
ctx.lineJoin = 'round'
}
}
if (!canvas) return
const dpr = Math.min(typeof window !== 'undefined' ? window.devicePixelRatio || 1 : 1, 2.5)
canvas.style.width = `${SIGNATURE_CSS_W}px`
canvas.style.height = `${SIGNATURE_CSS_H}px`
canvas.width = Math.round(SIGNATURE_CSS_W * dpr)
canvas.height = Math.round(SIGNATURE_CSS_H * dpr)
const ctx = canvas.getContext('2d')
if (!ctx) return
ctx.setTransform(1, 0, 0, 1, 0, 0)
ctx.scale(dpr, dpr)
ctx.fillStyle = '#fff'
ctx.fillRect(0, 0, SIGNATURE_CSS_W, SIGNATURE_CSS_H)
ctx.strokeStyle = '#333'
ctx.lineWidth = 2.5
ctx.lineCap = 'round'
ctx.lineJoin = 'round'
}
const handleAddHerb = () => {
@@ -1316,6 +1330,8 @@ defineExpose({
}
.signature-pad-wrap {
width: 100%;
max-width: 600px;
border: 1px solid #dcdfe6;
border-radius: 4px;
overflow: hidden;