1486 lines
41 KiB
Plaintext
1486 lines
41 KiB
Plaintext
<template>
|
||
<hr-page page-bg="#F3F4F6" status-bg="#FFFFFF" nav-title="检伤建档" @right-click="onSaveDraft">
|
||
<template v-slot:right>
|
||
<text class="c-nav-draft">存草稿</text>
|
||
</template>
|
||
|
||
<view class="hr-body" style="padding-bottom:22px">
|
||
|
||
<!-- ============ 基本信息 ============ -->
|
||
<hr-sec title="基本信息" more="扫描身份证 ›" :first="true" @more-click="onScanId"></hr-sec>
|
||
<hr-card>
|
||
<view class="c-row">
|
||
<view class="c-col">
|
||
<view class="hr-field">
|
||
<text class="hr-field-label">姓名</text>
|
||
<input class="hr-field-input" type="text" v-model="form.name" placeholder="必填"
|
||
placeholder-style="color:#9CA3AF" />
|
||
</view>
|
||
</view>
|
||
<view class="c-col c-col-last">
|
||
<view class="hr-field">
|
||
<text class="hr-field-label">性别</text>
|
||
<input class="hr-field-input" type="text" v-model="form.gender" placeholder="男 / 女"
|
||
placeholder-style="color:#9CA3AF" />
|
||
</view>
|
||
</view>
|
||
</view>
|
||
<view class="c-row c-row-last">
|
||
<view class="c-col">
|
||
<view class="hr-field">
|
||
<text class="hr-field-label">年龄</text>
|
||
<input class="hr-field-input" type="number" v-model="form.age" placeholder="岁"
|
||
placeholder-style="color:#9CA3AF" />
|
||
</view>
|
||
</view>
|
||
<view class="c-col c-col-last">
|
||
<view class="hr-field">
|
||
<text class="hr-field-label">身份证</text>
|
||
<input class="hr-field-input" type="text" v-model="form.idCard"
|
||
placeholder="4406••••••••1234" placeholder-style="color:#9CA3AF" />
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</hr-card>
|
||
|
||
<!-- ============ 检伤分级 ============ -->
|
||
<hr-sec title="检伤分级" more="START 标准 ›" @more-click="onStartStandard"></hr-sec>
|
||
<hr-card style="padding:11px">
|
||
<view class="c-grades">
|
||
<view v-for="(g, i) in grades" :key="i"
|
||
:class="i == grades.length - 1 ? 'c-grade c-grade-last' : 'c-grade'" :style="gradeStyle(i)"
|
||
@click="pickGrade(i)">
|
||
<view class="c-grade-dot" :style="{ backgroundColor: gradeDotColor(i) }">
|
||
<text class="c-grade-dot-t">✓</text>
|
||
</view>
|
||
<text :class="gradeVal == i ? 'c-grade-t c-grade-t-on' : 'c-grade-t'">{{ g }}</text>
|
||
</view>
|
||
</view>
|
||
</hr-card>
|
||
|
||
<!-- ============ 伤情标记(2D 人体图鉴) ============ -->
|
||
<hr-sec title="伤情标记(2D 人体图鉴)" more="+ 手动添加" @more-click="onAddByHand"></hr-sec>
|
||
<hr-card>
|
||
<view class="bm-row">
|
||
<view class="bm-col">
|
||
<hr-bodymap side="front" :parts="frontParts" :marks="frontMarks" @part-tap="onFrontPartTap"
|
||
@mark-tap="onMarkTap"></hr-bodymap>
|
||
<text class="bm-tag">正面 · 点部位即标记</text>
|
||
</view>
|
||
<view class="bm-col bm-col-last">
|
||
<hr-bodymap side="back" :parts="backParts" :marks="backMarks" @part-tap="onBackPartTap"
|
||
@mark-tap="onMarkTap"></hr-bodymap>
|
||
<text class="bm-tag">背面 · 点部位即标记</text>
|
||
</view>
|
||
</view>
|
||
|
||
<view class="c-sum">
|
||
<text class="c-sum-t">{{ markSummary }}</text>
|
||
<text class="c-sum-json" @click="onCopyJson">查看 JSON ›</text>
|
||
</view>
|
||
|
||
<view class="c-mklist">
|
||
<view v-for="(m, i) in marks" :key="m.id" class="c-mk">
|
||
<!-- 主体可点 → 进编辑态;删除按钮做成同级元素,避免点击冒泡冲突 -->
|
||
<view class="c-mk-main" @click="onMarkTap(m.id)">
|
||
<view class="c-mk-no" :style="mkNoStyle(m)">
|
||
<text class="c-mk-no-t">{{ m.no }}</text>
|
||
</view>
|
||
<view class="hr-flex1">
|
||
<text class="c-mk-t">{{ m.partName }} · {{ m.nature }}</text>
|
||
<text class="c-mk-s">{{ mkSub(m) }}</text>
|
||
</view>
|
||
<view class="c-mk-side" :style="mkSideStyle(m)">
|
||
<text class="c-mk-side-t">{{ mkSideText(m) }}</text>
|
||
</view>
|
||
</view>
|
||
<view class="c-mk-del" @click="askDelete(m.id)">
|
||
<x-icon name="close-line" color="#6B7280" font-size="13"></x-icon>
|
||
</view>
|
||
</view>
|
||
<view class="c-addmark" @click="onAddByHand">
|
||
<x-icon name="add-line" color="#2563EB" font-size="15"></x-icon>
|
||
<text class="c-addmark-t">点人体图部位快速添加,或点此手动添加</text>
|
||
</view>
|
||
</view>
|
||
</hr-card>
|
||
|
||
<!-- ============ 生命体征 ============ -->
|
||
<hr-sec title="生命体征" more="重新测量 ›" @more-click="onReMeasure"></hr-sec>
|
||
<hr-card>
|
||
<view class="c-row">
|
||
<view class="c-col c-col-3">
|
||
<view class="hr-field">
|
||
<text class="hr-field-label">T(℃)</text>
|
||
<input class="hr-field-input" type="digit" v-model="vital.t"
|
||
placeholder-style="color:#9CA3AF" />
|
||
</view>
|
||
</view>
|
||
<view class="c-col c-col-3">
|
||
<view class="hr-field">
|
||
<text class="hr-field-label">P(次/分)</text>
|
||
<input class="hr-field-input" type="number" v-model="vital.p"
|
||
placeholder-style="color:#9CA3AF" />
|
||
</view>
|
||
</view>
|
||
<view class="c-col c-col-3 c-col-last">
|
||
<view class="hr-field">
|
||
<text class="hr-field-label">R(次/分)</text>
|
||
<input class="hr-field-input" type="number" v-model="vital.r"
|
||
placeholder-style="color:#9CA3AF" />
|
||
</view>
|
||
</view>
|
||
</view>
|
||
<view class="c-row c-row-last">
|
||
<view class="c-col">
|
||
<view class="hr-field">
|
||
<text class="hr-field-label">BP(mmHg)</text>
|
||
<input class="hr-field-input" type="text" v-model="vital.bp"
|
||
placeholder-style="color:#9CA3AF" />
|
||
</view>
|
||
</view>
|
||
<view class="c-col c-col-last">
|
||
<view class="hr-field">
|
||
<text class="hr-field-label">SpO₂(%)</text>
|
||
<input class="hr-field-input" type="number" v-model="vital.spo2"
|
||
placeholder-style="color:#9CA3AF" />
|
||
</view>
|
||
</view>
|
||
</view>
|
||
<view class="c-warn">
|
||
<x-icon name="alert-line" color="#DC2626" font-size="14"></x-icon>
|
||
<text class="c-warn-t">脉搏偏快 · 血压偏低 · 血氧 88% 低于 95% 阈值,建议优先处置</text>
|
||
</view>
|
||
</hr-card>
|
||
|
||
<!-- ============ 检伤评估 ============ -->
|
||
<hr-sec title="检伤评估" more="START 标准 ›" @more-click="onStartStandard"></hr-sec>
|
||
<hr-card>
|
||
<view class="c-row">
|
||
<view class="c-col">
|
||
<view class="c-pick" @click="openPickSheet(0)">
|
||
<view class="hr-flex1">
|
||
<text class="c-pick-l">{{ pickLabels[0] }}</text>
|
||
<text :class="pickVal(0)">{{ pickValues[0] }}</text>
|
||
</view>
|
||
<x-icon name="arrow-down-s-line" color="#9CA3AF" font-size="14"></x-icon>
|
||
</view>
|
||
</view>
|
||
<view class="c-col c-col-last">
|
||
<view class="c-pick" @click="openPickSheet(1)">
|
||
<view class="hr-flex1">
|
||
<text class="c-pick-l">{{ pickLabels[1] }}</text>
|
||
<text :class="pickVal(1)">{{ pickValues[1] }}</text>
|
||
</view>
|
||
<x-icon name="arrow-down-s-line" color="#9CA3AF" font-size="14"></x-icon>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
<view class="c-row c-row-last">
|
||
<view class="c-col">
|
||
<view class="c-pick" @click="openPickSheet(2)">
|
||
<view class="hr-flex1">
|
||
<text class="c-pick-l">{{ pickLabels[2] }}</text>
|
||
<text :class="pickVal(2)">{{ pickValues[2] }}</text>
|
||
</view>
|
||
<x-icon name="arrow-down-s-line" color="#9CA3AF" font-size="14"></x-icon>
|
||
</view>
|
||
</view>
|
||
<view class="c-col c-col-last">
|
||
<view class="c-pick" @click="openPickSheet(3)">
|
||
<view class="hr-flex1">
|
||
<text class="c-pick-l">{{ pickLabels[3] }}</text>
|
||
<text :class="pickVal(3)">{{ pickValues[3] }}</text>
|
||
</view>
|
||
<x-icon name="arrow-down-s-line" color="#9CA3AF" font-size="14"></x-icon>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
<view style="margin-top:9px">
|
||
<view class="hr-field">
|
||
<text class="hr-field-label">关键体征</text>
|
||
<input class="hr-field-input" type="text" v-model="form.keySign" placeholder="如 面色苍白、四肢湿冷"
|
||
placeholder-style="color:#9CA3AF" />
|
||
</view>
|
||
</view>
|
||
</hr-card>
|
||
|
||
<!-- ============ 多模态记录 ============ -->
|
||
<hr-sec title="多模态记录" more="+ 添加 ›" @more-click="onAddRecord"></hr-sec>
|
||
<hr-card>
|
||
<textarea class="c-ta" v-model="form.record" placeholder="记录现场情况、处置措施、伤情变化…"
|
||
placeholder-style="color:#9CA3AF" />
|
||
<text class="c-count">{{ form.record.length }} / 500</text>
|
||
|
||
<!-- 图片 / 音视频 / 文档选择(组件库 xUploadFile) -->
|
||
<x-upload-file :default-list="fileList" :max-count="6" :max-file-size="maxFileSize" :multiple="true"
|
||
:auto-start="false" :disabled="false" @change="onFileChange"></x-upload-file>
|
||
<text class="c-upl-hint">支持图片 / 音频 / 视频 / 文档,单个不超过 50 MB</text>
|
||
</hr-card>
|
||
|
||
<view style="height:14px"></view>
|
||
</view>
|
||
|
||
<template v-slot:footer>
|
||
<view class="hr-fixedbar">
|
||
<view class="hr-btn hr-btn-ghost c-foot-draft" @click="onSaveDraft">
|
||
<text class="c-foot-t">保存草稿</text>
|
||
</view>
|
||
<view class="hr-btn hr-btn-primary" style="flex:1" @click="onSubmit">
|
||
<text class="c-foot-t2">提交建档</text>
|
||
</view>
|
||
</view>
|
||
</template>
|
||
|
||
<template v-slot:overlay>
|
||
<!-- ============ 评估项选择器(原底部选择器) ============ -->
|
||
<view v-if="sheetType != ''" class="hr-mask" @click="closeSheet">
|
||
<view class="hr-sheet" @click="stop">
|
||
<view class="hr-sheet-h">
|
||
<text class="hr-sheet-h-b">{{ sheetTitle }}</text>
|
||
<view class="hr-sheet-h-x" @click="closeSheet">
|
||
<x-icon name="close-line" color="#9CA3AF" font-size="18"></x-icon>
|
||
</view>
|
||
</view>
|
||
<view v-for="(o, i) in sheetOptions" :key="i" class="hr-sheet-opt"
|
||
:class="i == sheetOptions.length - 1 ? 'hr-sheet-opt-last' : ''" @click="pickSheetOption(i)">
|
||
<text class="hr-sheet-opt-t">{{ o }}</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- ============================================================
|
||
伤情录入抽屉(xDrawer)
|
||
点 2D 人体图任一部位 → 弹窗,部位自动填入,其余字段给默认值并自动
|
||
生成描述,救援人员微调后即可保存,全程不用手打字。
|
||
============================================================ -->
|
||
<x-drawer v-model:show="drawerShow" position="bottom" size="72%" round="12px" :show-footer="true"
|
||
:content-margin="'16'">
|
||
<template v-slot:title>
|
||
<view class="d-head">
|
||
<view class="d-head-dot" :style="dHeadDotStyle">
|
||
<text class="d-head-dot-t">{{ dNoLabel }}</text>
|
||
</view>
|
||
<view class="hr-flex1">
|
||
<text class="d-head-t">{{ drawerTitle }}</text>
|
||
<text class="d-head-s">部位已自动填入,其余按默认值预填,可直接保存</text>
|
||
</view>
|
||
</view>
|
||
</template>
|
||
|
||
<!-- ---------- 部位 ---------- -->
|
||
<view class="d-blk">
|
||
<view class="d-blk-h">
|
||
<text class="d-fl">部位</text>
|
||
<view class="d-side">
|
||
<view class="d-side-i" :style="segStyle(dSide == 'front')" @click="pickDside('front')">
|
||
<text :style="segTextStyle(dSide == 'front')">正面</text>
|
||
</view>
|
||
<view class="d-side-i" :style="segStyle(dSide == 'back')" @click="pickDside('back')">
|
||
<text :style="segTextStyle(dSide == 'back')">背面</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
<view class="d-chips">
|
||
<view v-for="(p, i) in dParts" :key="p.id + dSide" class="d-chip"
|
||
:style="chipStyle(dPartId == p.id)" @click="pickDpart(p.id)">
|
||
<text :style="chipTextStyle(dPartId == p.id)">{{ p.name }}</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- ---------- 性质 ---------- -->
|
||
<view class="d-blk">
|
||
<text class="d-fl">伤口性质</text>
|
||
<view class="d-chips">
|
||
<view v-for="(n, i) in natureOptions" :key="i" class="d-chip" :style="chipStyle(dNature == n)"
|
||
@click="pickDnature(n)">
|
||
<text :style="chipTextStyle(dNature == n)">{{ n }}</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- ---------- 出血情况 ---------- -->
|
||
<view class="d-blk">
|
||
<text class="d-fl">出血情况(决定标记颜色)</text>
|
||
<view class="d-chips">
|
||
<view v-for="(b, i) in bleedingOptions" :key="i" class="d-chip"
|
||
:style="chipStyle(dBleeding == b)" @click="pickDbleeding(b)">
|
||
<view class="d-chip-dot" :style="bleedDotStyle(b)"></view>
|
||
<text :style="chipTextStyle(dBleeding == b)">{{ b }}</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- ---------- 尺寸 ---------- -->
|
||
<view class="d-blk">
|
||
<text class="d-fl">尺寸 / 面积</text>
|
||
<view class="d-chips">
|
||
<view v-for="(s, i) in sizeOptions" :key="i" class="d-chip" :style="chipStyle(dSize == s)"
|
||
@click="pickDsize(s)">
|
||
<text :style="chipTextStyle(dSize == s)">{{ s }}</text>
|
||
</view>
|
||
</view>
|
||
<input class="d-input" type="text" v-model="dSize" placeholder="也可手输,如 6×3cm"
|
||
placeholder-style="color:#9CA3AF" />
|
||
</view>
|
||
|
||
<!-- ---------- 伤口走向(可旋转) ---------- -->
|
||
<view class="d-blk">
|
||
<view class="d-blk-h">
|
||
<text class="d-fl">伤口走向</text>
|
||
<view class="d-rot">
|
||
<view class="d-rot-b" @click="stepRot(-45)">
|
||
<text class="d-rot-b-t">−45°</text>
|
||
</view>
|
||
<text class="d-rot-v">{{ dRot }}°</text>
|
||
<view class="d-rot-b" @click="stepRot(45)">
|
||
<text class="d-rot-b-t">+45°</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
<!-- 走向实时预览:和人体图上的长轴条同一套换算 -->
|
||
<view class="d-prev">
|
||
<view class="d-prev-bar" :style="previewBarStyle"></view>
|
||
<view class="d-prev-dot" :style="dHeadDotStyle">
|
||
<text class="d-prev-dot-t">{{ dNoLabel }}</text>
|
||
</view>
|
||
</view>
|
||
<view class="d-chips">
|
||
<view v-for="(r, i) in rotOptions" :key="i" class="d-chip" :style="chipStyle(dRot == r)"
|
||
@click="pickDrot(r)">
|
||
<text :style="chipTextStyle(dRot == r)">{{ r }}°</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- ---------- 详情描述 ---------- -->
|
||
<view class="d-blk">
|
||
<view class="d-blk-h">
|
||
<text class="d-fl">详情描述</text>
|
||
<text class="d-regen" @click="regenDesc">↻ 按当前选项重生成</text>
|
||
</view>
|
||
<textarea class="d-ta" v-model="dDesc" placeholder="伤情描述" placeholder-style="color:#9CA3AF" />
|
||
</view>
|
||
|
||
<!-- ---------- 编辑态:删除 ---------- -->
|
||
<view v-if="drawerMode == 'edit'" class="d-del" @click="askDeleteEditing">
|
||
<text class="d-del-t">删除这处标记</text>
|
||
</view>
|
||
|
||
<view style="height:8px"></view>
|
||
|
||
<!-- 底部操作:用页面统一按钮,不用组件库默认按钮 -->
|
||
<template v-slot:footer>
|
||
<view class="d-foot">
|
||
<view class="hr-btn hr-btn-ghost d-foot-cancel" @click="closeDrawer">
|
||
<text class="d-foot-t">取消</text>
|
||
</view>
|
||
<view class="hr-btn hr-btn-primary d-foot-ok" @click="onInjuryConfirm">
|
||
<text class="d-foot-t2">保存标记</text>
|
||
</view>
|
||
</view>
|
||
</template>
|
||
</x-drawer>
|
||
</template>
|
||
</hr-page>
|
||
</template>
|
||
|
||
<script lang="uts" setup>
|
||
import { ref, computed } from 'vue'
|
||
import { gradeList, triagePickList, triagePickValues, triagePickOptions } from '@/mock/index.uts'
|
||
import { navBack, relaunchTo, navTo } from '@/utils/nav.uts'
|
||
import {
|
||
HrInjury, HrBodyPart,
|
||
HR_FRONT_PARTS, HR_BACK_PARTS,
|
||
HR_NATURE_OPTIONS, HR_BLEEDING_OPTIONS, HR_SIZE_PRESETS, HR_ROT_PRESETS,
|
||
HR_DEFAULT_BLEEDING, HR_DEFAULT_SIZE,
|
||
partsOf, findPart, findInjury, injuriesOfSide, partNameOf,
|
||
defaultNature, defaultInjuryDesc,
|
||
bleedingColor, bleedingBarColor, sizeBarWidth,
|
||
newInjuryId, copyInjury, renumberInjuries, excludeInjury, nextMarkAnchor,
|
||
injuriesToJson, injuriesFromJson, injurySummary
|
||
} from '@/utils/injury.uts'
|
||
|
||
/**
|
||
* s05 检伤建档
|
||
* 基本信息 → 一行四级检伤分级 → 2D 人体图鉴伤情标记 → 五项生命体征 → 检伤评估 → 多模态记录
|
||
*
|
||
* 伤情标记交互(本页核心):
|
||
* 点 2D 人体图任一部位 → 底部 xDrawer 弹窗(部位自动填入、其余字段默认填充、
|
||
* 描述自动生成)→ 微调后保存 → 人体图上出现「编号圆点 + 伤口长轴条」,
|
||
* 长轴条长度 = 尺寸、角度 = 走向(可旋转)、颜色 = 出血情况。
|
||
* 数据层全部在 utils/injury.uts:标记对象是纯基础类型,injuriesToJson /
|
||
* injuriesFromJson 一对函数负责存草稿与回显,接后端时替换这两个函数的实现即可。
|
||
*/
|
||
|
||
type TriageForm = {
|
||
name : string
|
||
gender : string
|
||
age : string
|
||
idCard : string
|
||
keySign : string
|
||
record : string
|
||
}
|
||
|
||
type TriageVital = { t : string, p : string, r : string, bp : string, spo2 : string }
|
||
|
||
const grades : string[] = gradeList
|
||
const pickLabels : string[] = triagePickList
|
||
|
||
const form = ref<TriageForm>({
|
||
name: '李华', gender: '男', age: '35', idCard: '4406••••••••1234',
|
||
keySign: '面色苍白、四肢湿冷、桡动脉细弱',
|
||
record: '14:23 于坍塌现场 B 区发现,俯卧位,呼之可睁眼。移除压覆物后前臂出现活动性出血,已加压包扎。'
|
||
} as TriageForm)
|
||
|
||
const vital = ref<TriageVital>({ t: '37.8', p: '102', r: '22', bp: '95/60', spo2: '88' } as TriageVital)
|
||
|
||
const gradeVal = ref(1)
|
||
const pickValues = ref<string[]>([
|
||
triagePickValues[0], triagePickValues[1], triagePickValues[2], triagePickValues[3]
|
||
])
|
||
|
||
/* ============================ 伤情标记 ============================ */
|
||
|
||
/** 草稿存储键(存的是 injuriesToJson 出来的 JSON 串) */
|
||
const HR_DRAFT_KEY : string = 'hr_triage_draft_injuries'
|
||
|
||
/** 正/背面的部位热区表与选项表,直接来自数据层,页面不重复定义 */
|
||
const frontParts : HrBodyPart[] = HR_FRONT_PARTS
|
||
const backParts : HrBodyPart[] = HR_BACK_PARTS
|
||
const natureOptions : string[] = HR_NATURE_OPTIONS
|
||
const bleedingOptions : string[] = HR_BLEEDING_OPTIONS
|
||
const sizeOptions : string[] = HR_SIZE_PRESETS
|
||
const rotOptions : number[] = HR_ROT_PRESETS
|
||
|
||
/** 造一条示例标记(打开页面即有内容;接后端后用接口数据替换) */
|
||
function demoMark(no : number, side : string, partId : string, rot : number, nature : string, size : string, bleeding : string) : HrInjury {
|
||
const p = findPart(side, partId)
|
||
const name = p == null ? partNameOf(side, partId) : p!.name
|
||
const mx = p == null ? 50 : p!.mx
|
||
const my = p == null ? 30 : p!.my
|
||
return {
|
||
id: 'inj_demo_' + no.toString(), no: no, side: side, partId: partId, partName: name,
|
||
x: mx, y: my, rot: rot, nature: nature, size: size, bleeding: bleeding,
|
||
desc: defaultInjuryDesc(name, nature, size, bleeding)
|
||
} as HrInjury
|
||
}
|
||
|
||
const marks = ref<HrInjury[]>([
|
||
demoMark(1, 'front', 'head', 0, '开放性外伤', '4×2cm', '活动性出血'),
|
||
demoMark(2, 'front', 'armVL', 90, '闭合性骨折', '5×3cm', '已止血'),
|
||
demoMark(3, 'back', 'legVL', 45, '挤压伤', '8×4cm', '渗血')
|
||
])
|
||
|
||
/**
|
||
* 附件初始列表(xUploadFile 的 defaultList,只为带出已有附件)
|
||
* ⚠ 项里没有 `request`(服务端返回的成功数据)是合法的,组件已打补丁支持缺省;
|
||
* 若哪天报 `null cannot be cast to non-null type kotlin.Any`,先看
|
||
* `uni_modules/tmx-ui/components/x-upload-file/x-upload-file.uvue` 的 covertList()
|
||
* 补丁是否被插件升级覆盖(见 MEMORY.md「uni_modules 本地补丁清单」)。
|
||
*/
|
||
const fileList = ref<UTSJSONObject[]>([
|
||
{ name: '现场照片.jpg', url: '' } as UTSJSONObject,
|
||
{ name: '现场录音.m4a', url: '' } as UTSJSONObject
|
||
])
|
||
|
||
/**
|
||
* 单个附件大小上限(字节)。
|
||
* ⚠ xUploadFile 的 maxFileSize 单位是「字节」,不是 MB。
|
||
* 之前传 50 相当于 50 字节,导致任何文件都被判为「超过大小」。
|
||
*/
|
||
const maxFileSize : number = 50 * 1024 * 1024
|
||
|
||
const frontMarks = computed(() : HrInjury[] => injuriesOfSide(marks.value, 'front'))
|
||
const backMarks = computed(() : HrInjury[] => injuriesOfSide(marks.value, 'back'))
|
||
const markSummary = computed(() : string => injurySummary(marks.value))
|
||
|
||
/* ---------- 人体图 / 列表上的样式下发 ---------- */
|
||
/* uvue 不支持复合类选择器(.a.b),选中态一律用内联 style 字符串下发 */
|
||
|
||
/** 编号圆点底色 = 出血情况色(红/橙/绿/灰),与人体图上的圆点同一套 */
|
||
function mkNoStyle(m : HrInjury) : string {
|
||
return 'background-color:' + bleedingColor(m.bleeding) + ';'
|
||
}
|
||
|
||
/** 列表副标题:尺寸 · 出血 · 走向 */
|
||
function mkSub(m : HrInjury) : string {
|
||
return m.size + ' · ' + m.bleeding + ' · 走向 ' + m.rot.toString() + '°'
|
||
}
|
||
|
||
function mkSideText(m : HrInjury) : string {
|
||
return m.side == 'back' ? '背面' : '正面'
|
||
}
|
||
|
||
function mkSideStyle(m : HrInjury) : string {
|
||
if (m.side == 'back') { return 'background-color:#F3F4F6;' }
|
||
return 'background-color:#EFF6FF;'
|
||
}
|
||
|
||
/* ============================ 伤情录入抽屉 ============================ */
|
||
|
||
const drawerShow = ref(false)
|
||
const drawerMode = ref('add')
|
||
/** 编辑态下的目标标记 id;新增态为空串 */
|
||
const editId = ref('')
|
||
|
||
/* 表单字段全部用顶层 ref:uvue 里嵌套对象/数组的原地修改不触发刷新,
|
||
顶层 ref 最稳,同时让「默认填充」逻辑一眼可读 */
|
||
const dSide = ref('front')
|
||
const dPartId = ref('chest')
|
||
const dNature = ref('开放性外伤')
|
||
const dBleeding = ref(HR_DEFAULT_BLEEDING)
|
||
const dSize = ref(HR_DEFAULT_SIZE)
|
||
const dRot = ref(0)
|
||
const dDesc = ref('')
|
||
const dNo = ref(1)
|
||
/** 上一次自动生成的描述:用来判断用户是否手改过描述 */
|
||
const lastAutoDesc = ref('')
|
||
|
||
const dPartName = computed(() : string => partNameOf(dSide.value, dPartId.value))
|
||
const dParts = computed(() : HrBodyPart[] => partsOf(dSide.value))
|
||
const drawerTitle = computed(() : string => drawerMode.value == 'edit' ? '编辑伤情' : '新增伤情')
|
||
const dNoLabel = computed(() : string => dNo.value.toString())
|
||
const dHeadDotStyle = computed(() : string => 'background-color:' + bleedingColor(dBleeding.value) + ';')
|
||
|
||
/** 走向实时预览条:长度 = 尺寸、角度 = 走向、颜色 = 出血(与人体图同一套换算) */
|
||
const previewBarStyle = computed(() : string => {
|
||
const w = sizeBarWidth(dSize.value)
|
||
const half = Math.round(w / 2)
|
||
return 'width:' + w + 'rpx;margin-left:-' + half + 'rpx;left:50%;' +
|
||
'background-color:' + bleedingBarColor(dBleeding.value) + ';' +
|
||
'transform:rotate(' + dRot.value + 'deg);'
|
||
})
|
||
|
||
function chipStyle(on : boolean) : string {
|
||
if (on) { return 'border:2rpx solid #2563EB;background-color:#2563EB;' }
|
||
return 'border:2rpx solid #E5E7EB;background-color:#FFFFFF;'
|
||
}
|
||
|
||
function chipTextStyle(on : boolean) : string {
|
||
if (on) { return 'color:#FFFFFF;font-weight:bold;' }
|
||
return 'color:#4B5563;'
|
||
}
|
||
|
||
function bleedDotStyle(b : string) : string {
|
||
return 'background-color:' + bleedingColor(b) + ';'
|
||
}
|
||
|
||
function segStyle(on : boolean) : string {
|
||
if (on) { return 'background-color:#2563EB;' }
|
||
return 'background-color:#FFFFFF;'
|
||
}
|
||
|
||
function segTextStyle(on : boolean) : string {
|
||
if (on) { return 'color:#FFFFFF;' }
|
||
return 'color:#6B7280;'
|
||
}
|
||
|
||
/* ---------- 默认填充 / 自动描述 ---------- */
|
||
|
||
/**
|
||
* 同步自动描述。
|
||
* force=true 强制覆盖(用户点「重生成」);
|
||
* 否则只在描述为空、或与上一次自动生成的一致时才覆盖 —— 用户手改过就不动。
|
||
*/
|
||
function syncAutoDesc(force : boolean) : void {
|
||
const auto = defaultInjuryDesc(dPartName.value, dNature.value, dSize.value, dBleeding.value)
|
||
if (force || dDesc.value == '' || dDesc.value == lastAutoDesc.value) {
|
||
dDesc.value = auto
|
||
}
|
||
lastAutoDesc.value = auto
|
||
}
|
||
|
||
function regenDesc() : void {
|
||
syncAutoDesc(true)
|
||
uni.showToast({ title: '描述已按当前选项重生成', icon: 'none' })
|
||
}
|
||
|
||
/* ---------- 打开:新增 / 编辑 ---------- */
|
||
|
||
/** 点人体图部位 → 新增态,部位自动填入 + 其余字段默认值 */
|
||
function openForPart(side : string, partId : string) : void {
|
||
drawerMode.value = 'add'
|
||
editId.value = ''
|
||
dSide.value = side
|
||
dPartId.value = partId
|
||
dBleeding.value = HR_DEFAULT_BLEEDING
|
||
dNature.value = defaultNature(HR_DEFAULT_BLEEDING)
|
||
dSize.value = HR_DEFAULT_SIZE
|
||
dRot.value = 0
|
||
dNo.value = marks.value.length + 1
|
||
syncAutoDesc(true)
|
||
drawerShow.value = true
|
||
}
|
||
|
||
function onFrontPartTap(partId : string) : void {
|
||
openForPart('front', partId)
|
||
}
|
||
|
||
function onBackPartTap(partId : string) : void {
|
||
openForPart('back', partId)
|
||
}
|
||
|
||
/** 不点人体图时的手动入口:默认正面胸部(弹窗内可改) */
|
||
function onAddByHand() : void {
|
||
openForPart('front', 'chest')
|
||
}
|
||
|
||
/** 点已有标记(人体图上的圆点 或 列表行)→ 编辑态 */
|
||
function onMarkTap(id : string) : void {
|
||
const found = findInjury(marks.value, id)
|
||
if (found == null) { return }
|
||
const m = found!
|
||
drawerMode.value = 'edit'
|
||
editId.value = m.id
|
||
dSide.value = m.side
|
||
dPartId.value = m.partId
|
||
dNature.value = m.nature
|
||
dBleeding.value = m.bleeding
|
||
dSize.value = m.size
|
||
dRot.value = m.rot
|
||
dNo.value = m.no
|
||
dDesc.value = m.desc
|
||
lastAutoDesc.value = defaultInjuryDesc(m.partName, m.nature, m.size, m.bleeding)
|
||
drawerShow.value = true
|
||
}
|
||
|
||
function closeDrawer() : void {
|
||
drawerShow.value = false
|
||
}
|
||
|
||
function pickDside(s : string) : void {
|
||
dSide.value = s
|
||
syncAutoDesc(false)
|
||
}
|
||
|
||
function pickDpart(id : string) : void {
|
||
dPartId.value = id
|
||
syncAutoDesc(false)
|
||
}
|
||
|
||
function pickDnature(v : string) : void {
|
||
dNature.value = v
|
||
syncAutoDesc(false)
|
||
}
|
||
|
||
function pickDbleeding(v : string) : void {
|
||
dBleeding.value = v
|
||
syncAutoDesc(false)
|
||
}
|
||
|
||
function pickDsize(v : string) : void {
|
||
dSize.value = v
|
||
syncAutoDesc(false)
|
||
}
|
||
|
||
function pickDrot(v : number) : void {
|
||
dRot.value = v
|
||
}
|
||
|
||
/** 走向 ±45°,180° 即 0°(伤口长轴无方向),所以取模到 0~179 */
|
||
function stepRot(delta : number) : void {
|
||
let r = (dRot.value + delta) % 180
|
||
if (r < 0) { r += 180 }
|
||
dRot.value = r
|
||
}
|
||
|
||
/* ---------- 保存 / 删除 ---------- */
|
||
|
||
function onInjuryConfirm() : void {
|
||
if (dPartId.value == '') {
|
||
uni.showToast({ title: '请先选择部位', icon: 'none' })
|
||
return
|
||
}
|
||
|
||
// 落点:同部位已有标记时按环序错开,避免编号圆点完全重叠
|
||
const base = excludeInjury(marks.value, editId.value)
|
||
const anchor = nextMarkAnchor(dSide.value, dPartId.value, base, '')
|
||
|
||
const m = {
|
||
id: editId.value == '' ? newInjuryId() : editId.value,
|
||
no: dNo.value,
|
||
side: dSide.value,
|
||
partId: dPartId.value,
|
||
partName: dPartName.value,
|
||
x: anchor[0],
|
||
y: anchor[1],
|
||
rot: dRot.value,
|
||
nature: dNature.value,
|
||
size: dSize.value,
|
||
bleeding: dBleeding.value,
|
||
desc: dDesc.value
|
||
} as HrInjury
|
||
|
||
const out : HrInjury[] = []
|
||
let replaced = false
|
||
for (let i = 0; i < marks.value.length; i++) {
|
||
const old = marks.value[i]
|
||
if (old.id == editId.value) {
|
||
// 部位没变 → 沿用原落点,避免「编辑一下位置就跳走」
|
||
if (old.side == dSide.value && old.partId == dPartId.value) {
|
||
m.x = old.x
|
||
m.y = old.y
|
||
}
|
||
out.push(m)
|
||
replaced = true
|
||
} else {
|
||
out.push(copyInjury(old))
|
||
}
|
||
}
|
||
if (!replaced) { out.push(m) }
|
||
|
||
// 编号重排必须整体重建数组:uvue 原地改元素不触发刷新
|
||
marks.value = renumberInjuries(out)
|
||
drawerShow.value = false
|
||
uni.showToast({ title: drawerMode.value == 'edit' ? '已更新标记' : '已添加标记', icon: 'none' })
|
||
}
|
||
|
||
function removeMark(id : string) : void {
|
||
marks.value = renumberInjuries(excludeInjury(marks.value, id))
|
||
uni.showToast({ title: '已删除标记', icon: 'none' })
|
||
}
|
||
|
||
function askDelete(id : string) : void {
|
||
uni.showModal({
|
||
title: '删除标记',
|
||
content: '确定删除这处伤情标记?',
|
||
cancelText: '取消',
|
||
confirmText: '删除',
|
||
success: (res) => {
|
||
if (res.confirm) {
|
||
removeMark(id)
|
||
drawerShow.value = false
|
||
}
|
||
}
|
||
})
|
||
}
|
||
|
||
function askDeleteEditing() : void {
|
||
if (editId.value != '') { askDelete(editId.value) }
|
||
}
|
||
|
||
/* ---------- 草稿(JSON 存取 / 回显) ---------- */
|
||
|
||
/** 查看当前标记的 JSON(接后端时直接把这个串作为 body 字段) */
|
||
function onCopyJson() : void {
|
||
const json = injuriesToJson(marks.value)
|
||
uni.setClipboardData({
|
||
data: json,
|
||
success: () => {
|
||
uni.showToast({ title: '标记 JSON 已复制', icon: 'none' })
|
||
}
|
||
})
|
||
}
|
||
|
||
/** 页面加载时回显上次存的草稿(存的就是 JSON,后端返回同结构即可) */
|
||
function loadInjuryDraft() : void {
|
||
try {
|
||
const raw = uni.getStorageSync(HR_DRAFT_KEY)
|
||
if (raw == null) { return }
|
||
const s = raw as string
|
||
if (s == '') { return }
|
||
const list = injuriesFromJson(s)
|
||
if (list.length > 0) { marks.value = renumberInjuries(list) }
|
||
} catch (e) {
|
||
// 草稿损坏时静默跳过,保持页面默认的示例标记
|
||
}
|
||
}
|
||
|
||
onLoad(() => {
|
||
loadInjuryDraft()
|
||
})
|
||
|
||
/* ---------- 检伤评估选择器 ---------- */
|
||
const sheetType = ref('')
|
||
const sheetIndex = ref(0)
|
||
|
||
const sheetTitle = computed(() : string => {
|
||
return '选择' + pickLabels[sheetIndex.value]
|
||
})
|
||
|
||
const sheetOptions = computed(() : string[] => {
|
||
return triagePickOptions[sheetIndex.value]
|
||
})
|
||
|
||
function openPickSheet(i : number) : void {
|
||
sheetType.value = 'pick'
|
||
sheetIndex.value = i
|
||
}
|
||
|
||
function closeSheet() : void {
|
||
sheetType.value = ''
|
||
}
|
||
|
||
function stop() : void {
|
||
}
|
||
|
||
function pickSheetOption(i : number) : void {
|
||
const arr : string[] = []
|
||
for (let k = 0; k < pickValues.value.length; k++) {
|
||
arr.push(k == sheetIndex.value ? sheetOptions.value[i] : pickValues.value[k])
|
||
}
|
||
pickValues.value = arr
|
||
sheetType.value = ''
|
||
}
|
||
|
||
/* ---------- 分级 / 评估 ---------- */
|
||
/**
|
||
* 分级选中态用内联样式下发。
|
||
* 原因:uvue 对复合类选择器(.a.b)支持不稳定,用内联样式最稳且跨端一致。
|
||
*/
|
||
function gradeStyle(i : number) : string {
|
||
const base = 'border-radius:12px;padding-top:11px;padding-bottom:10px;'
|
||
if (gradeVal.value != i) {
|
||
return base + 'border:1.5px solid #E5E7EB;background-color:#FFFFFF;'
|
||
}
|
||
if (i == 0) { return base + 'border:1.5px solid #10B981;background-color:#F0FDF4;' }
|
||
if (i == 1) { return base + 'border:1.5px solid #F59E0B;background-color:#FFFBEB;' }
|
||
if (i == 2) { return base + 'border:1.5px solid #DC2626;background-color:#FEF2F2;' }
|
||
return base + 'border:1.5px solid #1F2937;background-color:#F3F4F6;'
|
||
}
|
||
|
||
function gradeDotColor(i : number) : string {
|
||
if (i == 0) { return '#10B981' }
|
||
if (i == 1) { return '#F59E0B' }
|
||
if (i == 2) { return '#DC2626' }
|
||
return '#1F2937'
|
||
}
|
||
|
||
function pickGrade(i : number) : void {
|
||
gradeVal.value = i
|
||
uni.showToast({ title: '已选择「' + grades[i] + '」', icon: 'none' })
|
||
}
|
||
|
||
/** 下拉当前值的文字样式(空值走灰色提示态) */
|
||
function pickVal(i : number) : string {
|
||
return pickValues.value[i] == '' ? 'c-pick-v c-pick-v-empty' : 'c-pick-v'
|
||
}
|
||
|
||
/* ---------- 多模态上传(xUploadFile 接管选择与列表) ---------- */
|
||
/**
|
||
* 选择 / 删除附件后的回调。
|
||
* ⚠ 不要把这个列表写回 fileList:xUploadFile 内部 watch 了 defaultList,
|
||
* 一旦父级回写就会触发它的 covertList(),把每个附件重置成
|
||
* 「已上传、无大小」(realFilePath 清空),列表显示随之错乱。
|
||
* 附件状态由组件自己维护即可,这里只做提示。
|
||
*/
|
||
function onFileChange(list : UTSJSONObject[]) : void {
|
||
uni.showToast({ title: '已添加附件 ' + list.length + ' 个', icon: 'none' })
|
||
}
|
||
|
||
/* ---------- 提交 ---------- */
|
||
function onSaveDraft() : void {
|
||
uni.setStorageSync(HR_DRAFT_KEY, injuriesToJson(marks.value))
|
||
uni.showToast({ title: '草稿已保存(含 ' + marks.value.length.toString() + ' 处标记)', icon: 'success' })
|
||
}
|
||
|
||
function onSubmit() : void {
|
||
if (form.value.name == '') {
|
||
uni.showToast({ title: '请填写姓名', icon: 'none' })
|
||
return
|
||
}
|
||
uni.showLoading({ title: '提交中' })
|
||
setTimeout(() => {
|
||
uni.hideLoading()
|
||
uni.showToast({ title: '建档成功', icon: 'success' })
|
||
setTimeout(() => {
|
||
navTo('/pages/triage/detail')
|
||
}, 600)
|
||
}, 700)
|
||
}
|
||
|
||
function onScanId() : void {
|
||
uni.showToast({ title: '对准身份证自动识别', icon: 'none' })
|
||
}
|
||
|
||
function onStartStandard() : void {
|
||
uni.showModal({
|
||
title: 'START 检伤分类标准',
|
||
content: '可走 → 轻伤(绿)\n不可走 + 有呼吸 + 有脉搏 → 重伤(黄)\n呼吸 > 30 或 脉搏 > 120 或 意识障碍 → 危重(红)\n无呼吸无脉搏 → 死亡(黑)',
|
||
showCancel: false
|
||
})
|
||
}
|
||
|
||
function onReMeasure() : void {
|
||
uni.showToast({ title: '正在连接监护设备…', icon: 'none' })
|
||
}
|
||
|
||
function onAddRecord() : void {
|
||
uni.showToast({ title: '可继续追加语音 / 视频记录', icon: 'none' })
|
||
}
|
||
</script>
|
||
|
||
<style>
|
||
.c-nav-draft {
|
||
font-size: 29.2rpx;
|
||
color: #6B7280;
|
||
}
|
||
|
||
.c-row {
|
||
display: flex;
|
||
flex-direction: row;
|
||
margin-bottom: 18rpx;
|
||
}
|
||
|
||
.c-row-last {
|
||
margin-bottom: 0rpx;
|
||
}
|
||
|
||
.c-col {
|
||
flex: 1;
|
||
margin-right: 18rpx;
|
||
}
|
||
|
||
.c-col-last {
|
||
margin-right: 0rpx;
|
||
}
|
||
|
||
/* ---------- 分级 ---------- */
|
||
.c-grades {
|
||
display: flex;
|
||
flex-direction: row;
|
||
}
|
||
|
||
.c-grade {
|
||
flex: 1;
|
||
border: 3rpx solid #E5E7EB;
|
||
border-radius: 24rpx;
|
||
background-color: #FFFFFF;
|
||
padding: 22rpx 4rpx 20rpx 4rpx;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
margin-right: 16rpx;
|
||
}
|
||
|
||
.c-grade-last {
|
||
margin-right: 0rpx;
|
||
}
|
||
|
||
.c-grade-dot {
|
||
width: 44rpx;
|
||
height: 44rpx;
|
||
border-radius: 22rpx;
|
||
display: flex;
|
||
flex-direction: row;
|
||
align-items: center;
|
||
justify-content: center;
|
||
margin-bottom: 14rpx;
|
||
}
|
||
|
||
.c-grade-dot-t {
|
||
font-size: 24.6rpx;
|
||
font-weight: bold;
|
||
color: #FFFFFF;
|
||
}
|
||
|
||
.c-grade-t {
|
||
font-size: 28rpx;
|
||
font-weight: bold;
|
||
color: #1F2937;
|
||
}
|
||
|
||
.c-grade-t-on {
|
||
color: #1F2937;
|
||
}
|
||
|
||
/* 选中态的边框/底色由 gradeStyle() 内联下发,此处不再用复合选择器 */
|
||
|
||
/* ---------- 伤情标记概览 ---------- */
|
||
.c-sum {
|
||
display: flex;
|
||
flex-direction: row;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
margin-top: 22rpx;
|
||
}
|
||
|
||
.c-sum-t {
|
||
font-size: 23.6rpx;
|
||
color: #6B7280;
|
||
}
|
||
|
||
.c-sum-json {
|
||
font-size: 23.6rpx;
|
||
color: #2563EB;
|
||
}
|
||
|
||
/* ---------- 标记列表 ---------- */
|
||
.c-mklist {
|
||
flex-direction: column;
|
||
margin-top: 16rpx;
|
||
}
|
||
|
||
.c-mk {
|
||
display: flex;
|
||
flex-direction: row;
|
||
align-items: center;
|
||
background-color: #F9FAFB;
|
||
border: 2rpx solid #E5E7EB;
|
||
border-radius: 22rpx;
|
||
padding: 20rpx 24rpx 20rpx 24rpx;
|
||
margin-bottom: 16rpx;
|
||
}
|
||
|
||
/* 主体整块可点(进编辑态),删除按钮做成它的同级兄弟,避免点击冒泡打架 */
|
||
.c-mk-main {
|
||
display: flex;
|
||
flex-direction: row;
|
||
align-items: center;
|
||
flex: 1;
|
||
}
|
||
|
||
.c-mk-no {
|
||
width: 44rpx;
|
||
height: 44rpx;
|
||
border-radius: 22rpx;
|
||
display: flex;
|
||
flex-direction: row;
|
||
align-items: center;
|
||
justify-content: center;
|
||
margin-right: 20rpx;
|
||
}
|
||
|
||
.c-mk-no-t {
|
||
font-size: 24.6rpx;
|
||
font-weight: bold;
|
||
color: #FFFFFF;
|
||
}
|
||
|
||
.c-mk-t {
|
||
font-size: 28rpx;
|
||
font-weight: bold;
|
||
color: #1F2937;
|
||
}
|
||
|
||
.c-mk-s {
|
||
font-size: 23.6rpx;
|
||
color: #9CA3AF;
|
||
margin-top: 4rpx;
|
||
}
|
||
|
||
.c-mk-side {
|
||
border-radius: 10rpx;
|
||
padding: 4rpx 14rpx 4rpx 14rpx;
|
||
margin-left: 12rpx;
|
||
}
|
||
|
||
.c-mk-side-t {
|
||
font-size: 21.2rpx;
|
||
color: #6B7280;
|
||
}
|
||
|
||
.c-mk-del {
|
||
width: 40rpx;
|
||
height: 40rpx;
|
||
border-radius: 20rpx;
|
||
background-color: #F3F4F6;
|
||
display: flex;
|
||
flex-direction: row;
|
||
align-items: center;
|
||
justify-content: center;
|
||
margin-left: 16rpx;
|
||
}
|
||
|
||
.c-addmark {
|
||
display: flex;
|
||
flex-direction: row;
|
||
align-items: center;
|
||
justify-content: center;
|
||
height: 80rpx;
|
||
border: 3rpx dashed #C7CDD6;
|
||
border-radius: 22rpx;
|
||
background-color: #FFFFFF;
|
||
}
|
||
|
||
.c-addmark-t {
|
||
font-size: 25.8rpx;
|
||
color: #6B7280;
|
||
margin-left: 12rpx;
|
||
}
|
||
|
||
/* ---------- 伤情录入抽屉 ---------- */
|
||
.d-head {
|
||
display: flex;
|
||
flex-direction: row;
|
||
align-items: center;
|
||
width: 100%;
|
||
margin: 5px 16px;
|
||
padding-top: 6rpx;
|
||
}
|
||
|
||
.d-head-dot {
|
||
width: 44rpx;
|
||
height: 44rpx;
|
||
border-radius: 22rpx;
|
||
display: flex;
|
||
flex-direction: row;
|
||
align-items: center;
|
||
justify-content: center;
|
||
margin-right: 16rpx;
|
||
}
|
||
|
||
.d-head-dot-t {
|
||
font-size: 23rpx;
|
||
font-weight: bold;
|
||
color: #FFFFFF;
|
||
}
|
||
|
||
.d-head-t {
|
||
font-size: 30rpx;
|
||
font-weight: bold;
|
||
color: #1F2937;
|
||
}
|
||
|
||
.d-head-s {
|
||
font-size: 21.2rpx;
|
||
color: #9CA3AF;
|
||
margin-top: 4rpx;
|
||
}
|
||
|
||
.d-blk {
|
||
margin-bottom: 24rpx;
|
||
}
|
||
|
||
.d-blk-h {
|
||
display: flex;
|
||
flex-direction: row;
|
||
align-items: center;
|
||
justify-content: space-between;
|
||
}
|
||
|
||
.d-fl {
|
||
font-size: 25.8rpx;
|
||
font-weight: bold;
|
||
color: #1F2937;
|
||
}
|
||
|
||
.d-regen {
|
||
font-size: 22.8rpx;
|
||
color: #2563EB;
|
||
}
|
||
|
||
.d-chips {
|
||
display: flex;
|
||
flex-direction: row;
|
||
flex-wrap: wrap;
|
||
margin-top: 14rpx;
|
||
}
|
||
|
||
/* 选中态(边框/底色/文字色)一律由 chipStyle / chipTextStyle 内联下发 */
|
||
.d-chip {
|
||
display: flex;
|
||
flex-direction: row;
|
||
align-items: center;
|
||
border-radius: 14rpx;
|
||
padding: 12rpx 20rpx 12rpx 20rpx;
|
||
margin-right: 14rpx;
|
||
margin-bottom: 14rpx;
|
||
}
|
||
|
||
.d-chip-dot {
|
||
width: 16rpx;
|
||
height: 16rpx;
|
||
border-radius: 8rpx;
|
||
margin-right: 10rpx;
|
||
}
|
||
|
||
.d-side {
|
||
display: flex;
|
||
flex-direction: row;
|
||
border: 2rpx solid #E5E7EB;
|
||
border-radius: 14rpx;
|
||
overflow: hidden;
|
||
}
|
||
|
||
.d-side-i {
|
||
padding: 8rpx 22rpx 8rpx 22rpx;
|
||
}
|
||
|
||
.d-rot {
|
||
display: flex;
|
||
flex-direction: row;
|
||
align-items: center;
|
||
}
|
||
|
||
.d-rot-b {
|
||
width: 80rpx;
|
||
height: 52rpx;
|
||
border: 2rpx solid #E5E7EB;
|
||
border-radius: 14rpx;
|
||
background-color: #FFFFFF;
|
||
display: flex;
|
||
flex-direction: row;
|
||
align-items: center;
|
||
justify-content: center;
|
||
}
|
||
|
||
.d-rot-b-t {
|
||
font-size: 22.8rpx;
|
||
color: #4B5563;
|
||
}
|
||
|
||
.d-rot-v {
|
||
width: 84rpx;
|
||
font-size: 25.8rpx;
|
||
font-weight: bold;
|
||
color: #2563EB;
|
||
text-align: center;
|
||
}
|
||
|
||
/* 走向实时预览:容器高 132rpx,长轴条 24rpx → 竖直居中 top = 54rpx */
|
||
.d-prev {
|
||
width: 100%;
|
||
height: 132rpx;
|
||
background-color: #FAFBFC;
|
||
border: 2rpx dashed #E5E7EB;
|
||
border-radius: 18rpx;
|
||
position: relative;
|
||
margin-top: 16rpx;
|
||
}
|
||
|
||
.d-prev-bar {
|
||
position: absolute;
|
||
top: 54rpx;
|
||
height: 24rpx;
|
||
border-radius: 12rpx;
|
||
opacity: 0.85;
|
||
}
|
||
|
||
.d-prev-dot {
|
||
position: absolute;
|
||
left: 50%;
|
||
top: 50%;
|
||
width: 44rpx;
|
||
height: 44rpx;
|
||
border-radius: 22rpx;
|
||
margin-left: -22rpx;
|
||
margin-top: -22rpx;
|
||
border: 3rpx solid #FFFFFF;
|
||
display: flex;
|
||
flex-direction: row;
|
||
align-items: center;
|
||
justify-content: center;
|
||
}
|
||
|
||
.d-prev-dot-t {
|
||
font-size: 23rpx;
|
||
font-weight: bold;
|
||
color: #FFFFFF;
|
||
}
|
||
|
||
.d-input {
|
||
width: 100%;
|
||
height: 72rpx;
|
||
background-color: #F9FAFB;
|
||
border: 2rpx solid #E5E7EB;
|
||
border-radius: 16rpx;
|
||
padding: 0rpx 20rpx 0rpx 20rpx;
|
||
font-size: 26.4rpx;
|
||
color: #1F2937;
|
||
}
|
||
|
||
.d-ta {
|
||
width: 100%;
|
||
height: 176rpx;
|
||
background-color: #F9FAFB;
|
||
border: 2rpx solid #E5E7EB;
|
||
border-radius: 18rpx;
|
||
padding: 18rpx 20rpx 18rpx 20rpx;
|
||
font-size: 26.4rpx;
|
||
color: #1F2937;
|
||
margin-top: 14rpx;
|
||
}
|
||
|
||
.d-del {
|
||
height: 80rpx;
|
||
background-color: #FEF2F2;
|
||
border: 2rpx solid #FCA5A5;
|
||
border-radius: 18rpx;
|
||
display: flex;
|
||
flex-direction: row;
|
||
align-items: center;
|
||
justify-content: center;
|
||
}
|
||
|
||
.d-del-t {
|
||
font-size: 25.8rpx;
|
||
color: #DC2626;
|
||
}
|
||
|
||
.d-foot {
|
||
display: flex;
|
||
flex-direction: row;
|
||
align-items: center;
|
||
}
|
||
|
||
.d-foot-cancel {
|
||
flex: 0 0 220rpx;
|
||
margin-right: 18rpx;
|
||
}
|
||
|
||
.d-foot-ok {
|
||
flex: 1;
|
||
}
|
||
|
||
.d-foot-t {
|
||
font-size: 31.4rpx;
|
||
font-weight: bold;
|
||
color: #1F2937;
|
||
}
|
||
|
||
.d-foot-t2 {
|
||
font-size: 32.4rpx;
|
||
font-weight: bold;
|
||
color: #FFFFFF;
|
||
}
|
||
|
||
/* ---------- 生命体征 ---------- */
|
||
.c-warn {
|
||
display: flex;
|
||
flex-direction: row;
|
||
align-items: flex-start;
|
||
margin-top: 20rpx;
|
||
}
|
||
|
||
.c-warn-t {
|
||
flex: 1;
|
||
font-size: 24.6rpx;
|
||
color: #DC2626;
|
||
margin-left: 12rpx;
|
||
line-height: 1.5;
|
||
}
|
||
|
||
/* ---------- 评估下拉 ---------- */
|
||
/* 检伤评估四项下拉:设计稿中均为浅蓝填充的「已选」外观 */
|
||
.c-pick {
|
||
display: flex;
|
||
flex-direction: row;
|
||
align-items: center;
|
||
background-color: #EFF6FF;
|
||
border: 2rpx solid #BFDBFE;
|
||
border-radius: 22rpx;
|
||
padding: 20rpx 22rpx 20rpx 22rpx;
|
||
}
|
||
|
||
.c-pick-l {
|
||
font-size: 23.6rpx;
|
||
color: #3B6BC7;
|
||
}
|
||
|
||
.c-pick-v {
|
||
font-size: 28rpx;
|
||
font-weight: bold;
|
||
color: #1F2937;
|
||
margin-top: 4rpx;
|
||
}
|
||
|
||
.c-pick-v-empty {
|
||
color: #9CA3AF;
|
||
}
|
||
|
||
/* ---------- 多模态 ---------- */
|
||
.c-ta {
|
||
width: 100%;
|
||
background-color: #F9FAFB;
|
||
border: 2rpx solid #E5E7EB;
|
||
border-radius: 22rpx;
|
||
padding: 20rpx 20rpx 20rpx 20rpx;
|
||
font-size: 28rpx;
|
||
color: #1F2937;
|
||
height: 192rpx;
|
||
}
|
||
|
||
.c-count {
|
||
text-align: right;
|
||
font-size: 23.6rpx;
|
||
color: #9CA3AF;
|
||
margin: 8rpx 0;
|
||
}
|
||
|
||
.c-upls {
|
||
display: flex;
|
||
flex-direction: row;
|
||
flex-wrap: wrap;
|
||
margin-top: 20rpx;
|
||
}
|
||
|
||
.c-upl {
|
||
width: 31%;
|
||
height: 168rpx;
|
||
border-radius: 22rpx;
|
||
background-color: #F9FAFB;
|
||
border: 2rpx solid #E5E7EB;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
justify-content: center;
|
||
margin-right: 2.5%;
|
||
margin-bottom: 16rpx;
|
||
position: relative;
|
||
overflow: hidden;
|
||
}
|
||
|
||
.c-upl-add {
|
||
border: 3rpx dashed #C7CDD6;
|
||
background-color: #FFFFFF;
|
||
}
|
||
|
||
.c-upl-t {
|
||
font-size: 23.6rpx;
|
||
color: #9CA3AF;
|
||
margin-top: 8rpx;
|
||
}
|
||
|
||
.c-upl-tag {
|
||
position: absolute;
|
||
left: 0rpx;
|
||
right: 0rpx;
|
||
bottom: 0rpx;
|
||
padding: 4rpx 0rpx 4rpx 0rpx;
|
||
}
|
||
|
||
.c-upl-tag-t {
|
||
font-size: 21.2rpx;
|
||
color: #FFFFFF;
|
||
text-align: center;
|
||
}
|
||
|
||
.c-upl-del {
|
||
position: absolute;
|
||
right: 8rpx;
|
||
top: 8rpx;
|
||
width: 34rpx;
|
||
height: 34rpx;
|
||
border-radius: 18rpx;
|
||
background-color: rgba(17, 24, 39, 0.6);
|
||
display: flex;
|
||
flex-direction: row;
|
||
align-items: center;
|
||
justify-content: center;
|
||
}
|
||
|
||
.c-upl-hint {
|
||
font-size: 23.6rpx;
|
||
color: #9CA3AF;
|
||
margin-top: 4rpx;
|
||
}
|
||
|
||
/* ---------- 底部 ---------- */
|
||
.c-foot-draft {
|
||
flex: 0 0 220rpx;
|
||
margin-right: 18rpx;
|
||
}
|
||
|
||
.c-foot-t {
|
||
font-size: 31.4rpx;
|
||
font-weight: bold;
|
||
color: #1F2937;
|
||
}
|
||
|
||
.c-foot-t2 {
|
||
font-size: 32.4rpx;
|
||
font-weight: bold;
|
||
color: #FFFFFF;
|
||
}
|
||
</style> |