Files
hospital-rescue-app-v2/components/hr-bodymap/hr-bodymap.uvue
T
2026-09-24 16:25:22 +08:00

188 lines
5.2 KiB
Plaintext
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>
<!--
2D 人体图鉴(单面)
- 人体轮廓沿用 hr-design.css 里的 .bm-* 几何(192rpx × 368rpx),不要再改用 SVG
- 热区:9 个不重叠的可点部位,点一下就是「在该部位新增标记」
- 标记:编号圆点(不旋转,保证数字可读)+ 伤口长轴条(长度=尺寸、角度=走向、颜色=出血)
-->
<view class="bm-figure">
<!-- ===== 人体轮廓(装饰,不响应点击) ===== -->
<view class="bm-head"></view>
<view class="bm-torso"></view>
<view class="bm-arm-l"></view>
<view class="bm-arm-r"></view>
<view class="bm-leg-l"></view>
<view class="bm-leg-r"></view>
<!-- 背面加一条脊柱线,让正/背面一眼能分辨 -->
<view v-if="side == 'back'" class="bmp-spine"></view>
<!-- ===== 可点部位热区 ===== -->
<view v-for="(p, i) in parts" :key="p.id" :class="hitClass(p)" :style="hitStyle(p)"
@click="onHit(p.id)">
</view>
<!-- ===== 伤情标记 ===== -->
<view v-for="(m, i) in marks" :key="m.id" class="bmp-mark" :style="markStyle(m)" @click="onMark(m.id)">
<!-- 伤口长轴:先画在下方,编号圆点盖在上面 -->
<view class="bmp-bar" :style="barStyle(m)"></view>
<view class="bmp-dot" :style="dotStyle(m)">
<text class="bmp-dot-t">{{ m.no }}</text>
</view>
</view>
</view>
</template>
<script lang="uts" setup>
import { computed } from 'vue'
import { HrInjury, HrBodyPart, sizeBarWidth, bleedingColor, bleedingBarColor } from '@/utils/injury.uts'
/**
* 2D 人体图鉴
* 只负责「画」和「命中」,业务(弹窗、表单、JSON)全部交给页面,
* 这样同一套图能复用到批量检伤、伤情详情等页面。
*/
type HrBodyMapProps = {
/** 当前视野:front 正面 / back 背面 */
side : string
/** 该视野的部位热区表(来自 utils/injury.uts 的 partsOf */
parts : HrBodyPart[]
/** 该视野要显示的标记(页面先按 side 过滤好再传进来) */
marks : HrInjury[]
}
const props = withDefaults(defineProps<HrBodyMapProps>(), {
side : 'front',
parts : () : HrBodyPart[] => [] as HrBodyPart[],
marks : () : HrInjury[] => [] as HrInjury[]
})
const emits = defineEmits<{
/** 点击部位热区 */
(e : 'part-tap', partId : string) : void
/** 点击已有标记 */
(e : 'mark-tap', id : string) : void
}>()
/* ---------- 热区 ---------- */
function hitStyle(p : HrBodyPart) : string {
return 'left:' + p.x + '%;top:' + p.y + '%;width:' + p.w + '%;height:' + p.h + '%;'
}
/** 该部位是否已有标记(用于把热区点亮,一眼看出伤在哪) */
function hasMark(partId : string) : boolean {
for (let i = 0; i < props.marks.length; i++) {
if (props.marks[i].partId == partId) { return true }
}
return false
}
function hitClass(p : HrBodyPart) : string {
return hasMark(p.id) ? 'bmp-hit bmp-hit-on' : 'bmp-hit'
}
function onHit(partId : string) : void {
emits('part-tap', partId)
}
/* ---------- 标记 ---------- */
/** 标记整体位置:靠负 margin 居中(不用 transform,把 transform 留给旋转) */
function markStyle(m : HrInjury) : string {
return 'left:' + m.x + '%;top:' + m.y + '%;'
}
function dotStyle(m : HrInjury) : string {
return 'background-color:' + bleedingColor(m.bleeding) + ';'
}
/**
* 伤口长轴条:长度按尺寸、角度按 rot。
* left:50% + 负 margin 做水平居中,避免和 rotate 抢 transform。
*/
function barStyle(m : HrInjury) : string {
const w = sizeBarWidth(m.size)
const half = Math.round(w / 2)
return 'width:' + w + 'rpx;margin-left:-' + half + 'rpx;left:50%;' +
'background-color:' + bleedingBarColor(m.bleeding) + ';' +
'transform:rotate(' + m.rot + 'deg);'
}
function onMark(id : string) : void {
emits('mark-tap', id)
}
/* ---------- 导出给外部复用的换算(页面里的旋转预览用同一套) ---------- */
const sideLabel = computed(() : string => {
return props.side == 'back' ? '背面' : '正面'
})
</script>
<style>
/* 背面识别线:躯干中轴 */
.bmp-spine {
position: absolute;
left: 94rpx;
top: 78rpx;
width: 4rpx;
height: 108rpx;
border-radius: 2rpx;
background-color: #E2E8F0;
}
/* 部位热区:平时只用极浅虚线提示「这里可点」,避免糊住人体图 */
.bmp-hit {
position: absolute;
border-radius: 10rpx;
border: 2rpx dashed #EDF0F4;
}
/* 已有标记的部位点亮,形成「伤情分布」 */
.bmp-hit-on {
border: 2rpx solid #BFDBFE;
background-color: #EFF6FF;
}
/* 标记容器:绝对定位 + 负 margin 居中 */
.bmp-mark {
position: absolute;
width: 76rpx;
height: 76rpx;
margin-left: -38rpx;
margin-top: -38rpx;
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
}
/* 伤口长轴条:竖直居中于容器((76-24)/2 = 26rpx */
.bmp-bar {
position: absolute;
top: 26rpx;
height: 24rpx;
border-radius: 12rpx;
opacity: 0.8;
}
/* 编号圆点:不参与旋转 */
.bmp-dot {
width: 44rpx;
height: 44rpx;
border-radius: 22rpx;
border: 3rpx solid #FFFFFF;
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
}
.bmp-dot-t {
font-size: 23rpx;
font-weight: bold;
color: #FFFFFF;
}
</style>