Files
hospital-rescue-app-v2/pages/triage/victims.uvue
T
2026-09-24 16:25:22 +08:00

433 lines
11 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>
<hr-page page-bg="#F3F4F6" status-bg="#FFFFFF" nav-title="伤员查询" :xloading="true"
@right-click="onSort">
<template v-slot:right>
<x-icon name="arrow-up-down-line" color="#6B7280" font-size="17"></x-icon>
</template>
<!-- 离线条 -->
<view class="hr-offbar" @click="goOffline">
<x-icon name="cloud-off-line" color="#92400E" font-size="15"></x-icon>
<text class="vs-off-t">离线中 · 数据暂存本地</text>
<view class="hr-offbar-cnt">
<text class="vs-off-cnt-t">3</text>
</view>
<text class="hr-offbar-go">查看队列 </text>
</view>
<view class="hr-body">
<!-- ============ 搜索(xSearch ============ -->
<x-search v-model="keyword" placeholder="姓名 / 身份证 / 编号" round="24rpx" height="84rpx"
:border="searchBorder" input-bg-color="#FFFFFF" icon-color="#9CA3AF" font-color="#1F2937"
:show-cancel="false"></x-search>
<!-- ============ 双下拉筛选 ============ -->
<view class="vs-filter">
<view :class="levelIdx > 0 ? 'vs-sel vs-sel-on' : 'vs-sel'" @click="openSheet(0)">
<text class="vs-sel-l">检伤等级</text>
<text :class="levelIdx > 0 ? 'vs-sel-v vs-sel-v-on' : 'vs-sel-v'">{{ levelOptions[levelIdx] }}</text>
<x-icon name="arrow-down-s-line" color="#9CA3AF" font-size="14"></x-icon>
</view>
<view :class="statusIdx > 0 ? 'vs-sel vs-sel-on' : 'vs-sel'"
style="margin-left:8px" @click="openSheet(1)">
<text class="vs-sel-l">检伤状态</text>
<text :class="statusIdx > 0 ? 'vs-sel-v vs-sel-v-on' : 'vs-sel-v'">{{ statusOptions[statusIdx] }}</text>
<x-icon name="arrow-down-s-line" color="#9CA3AF" font-size="14"></x-icon>
</view>
</view>
<!-- ============ 伤员列表 ============ -->
<view style="margin-top:4px">
<view v-for="(v, i) in shown" :key="v.id" :class="cardClass(v.level)" @click="openDetail(v)">
<view class="hr-row">
<view :class="avatarClass(v.level)">
<text class="vs-avt-t">{{ v.name.substring(0, 1) }}</text>
</view>
<view class="hr-flex1">
<view class="hr-row">
<text class="vs-name">{{ v.name }}</text>
<hr-wb :level="v.level" small></hr-wb>
</view>
<text class="vs-code">{{ v.gender }} · {{ v.age }} 岁 · {{ v.code }}</text>
</view>
</view>
<view class="vs-inj">
<text class="vs-inj-t">伤情:{{ v.injury }}</text>
</view>
<view class="vs-bot">
<view class="vs-status" :style="statusStyle(v.statusTheme)">
<text class="vs-status-t" :style="statusTextStyle(v.statusTheme)">{{ v.status }}</text>
</view>
<view class="hr-flex1"></view>
<text class="vs-time">{{ v.createdAt }}</text>
</view>
</view>
</view>
<hr-empty v-if="shown.length == 0" icon="♡" title="没有匹配的伤员"
sub="换个筛选条件,或点击右上角扫码按腕带编号查询"></hr-empty>
<view v-if="shown.length > 0" class="vs-hint">
<text class="vs-hint-t">✦ 点击卡片查看伤员详情</text>
</view>
<view style="height:14px"></view>
</view>
<!-- ============ 筛选选择器 ============ -->
<template v-slot:overlay>
<view v-if="sheet >= 0" class="hr-mask" @click="closeSheet">
<view class="hr-sheet" @click="stop">
<view class="hr-sheet-h">
<text class="hr-sheet-h-b">{{ sheet == 0 ? '选择检伤等级' : '选择检伤状态' }}</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 currentOptions" :key="i" class="hr-sheet-opt"
:class="i == currentOptions.length - 1 ? 'hr-sheet-opt-last' : ''" @click="pickOption(i)">
<text :class="isPicked(i) ? 'hr-sheet-opt-t hr-sheet-opt-on' : 'hr-sheet-opt-t'">{{ o }}</text>
<x-icon v-if="isPicked(i)" name="check-line" color="#2563EB" font-size="17"></x-icon>
</view>
</view>
</view>
</template>
</hr-page>
</template>
<script lang="uts" setup>
import { ref, computed } from 'vue'
import { victimList } from '@/mock/index.uts'
import { HrVictim } from '@/mock/types.uts'
import { navTo } from '@/utils/nav.uts'
/**
* s04 伤员查询
* 支持按「检伤等级 / 检伤状态」双下拉筛选 + 关键字搜索 + 扫码查询。
*/
const levelOptions : string[] = ['全部', '危重', '重伤', '轻伤', '死亡']
const statusOptions : string[] = ['全部', '已检伤', '转运中', '待转运', '未转运']
const keyword = ref('')
const levelIdx = ref(0)
const statusIdx = ref(0)
const sheet = ref(-1)
const list = ref<HrVictim[]>(victimList)
/**
* xSearch 的 border 是「数组」prop,固定 3 项 = [宽度, 线型, 颜色],
* 少于 3 项内部直接返回 none。设计稿搜索框为 1px solid #E5E7EB。
*/
const searchBorder : string[] = ['1px', 'solid', '#E5E7EB']
const currentOptions = computed(() : string[] => {
return sheet.value == 0 ? levelOptions : statusOptions
})
const shown = computed(() : HrVictim[] => {
const out : HrVictim[] = []
for (let i = 0; i < list.value.length; i++) {
const v = list.value[i]
if (levelIdx.value > 0 && v.level != levelIdx.value) { continue }
if (statusIdx.value > 0) {
const label = statusOptions[statusIdx.value]
if (label == '转运中' && v.status.indexOf('转运中') < 0) { continue }
if (label == '待转运' && v.status.indexOf('待转运') < 0) { continue }
if (label == '未转运' && v.status.indexOf('未转运') < 0) { continue }
if (label == '已检伤' && v.status.indexOf('已转运') < 0 && v.status.indexOf('转运中') < 0) { continue }
}
if (keyword.value != '') {
const kw = keyword.value.toLowerCase()
const hit = v.name.toLowerCase().indexOf(kw) > -1 || v.code.toLowerCase().indexOf(kw) > -1
if (!hit) { continue }
}
out.push(v)
}
return out
})
function cardClass(level : number) : string {
return `vs-card vs-card-l${level}`
}
function avatarClass(level : number) : string {
return `vs-avt vs-avt-l${level}`
}
function statusStyle(theme : string) : string {
if (theme == 'pri') { return 'background-color:#EFF6FF;border:1px solid #BFDBFE' }
if (theme == 'amb') { return 'background-color:#FFFBEB;border:1px solid #FDE68A' }
if (theme == 'grn') { return 'background-color:#ECFDF5;border:1px solid #A7F3D0' }
return 'background-color:#F3F4F6;border:1px solid #E5E7EB'
}
function statusTextStyle(theme : string) : string {
if (theme == 'pri') { return 'color:#1D4ED8' }
if (theme == 'amb') { return 'color:#B45309' }
if (theme == 'grn') { return 'color:#047857' }
return 'color:#6B7280'
}
function openSheet(i : number) : void {
sheet.value = i
}
function closeSheet() : void {
sheet.value = -1
}
function stop() : void {
// 阻止点击面板时关闭
}
function isPicked(i : number) : boolean {
return sheet.value == 0 ? levelIdx.value == i : statusIdx.value == i
}
function pickOption(i : number) : void {
if (sheet.value == 0) { levelIdx.value = i } else { statusIdx.value = i }
sheet.value = -1
}
function openDetail(v : HrVictim) : void {
navTo('/pages/triage/detail?id=' + v.id)
}
function onSort() : void {
uni.showToast({ title: '按建档时间 / 分级排序', icon: 'none' })
}
function goOffline() : void {
navTo('/pages/triage/offline-sync')
}
</script>
<style>
.vs-off-t {
font-size: 26.8rpx;
font-weight: bold;
color: #92400E;
margin-left: 18rpx;
}
.vs-off-cnt-t {
font-size: 22.4rpx;
font-weight: bold;
color: #FFFFFF;
}
/* ---------- 搜索 ---------- */
.vs-srch {
display: flex;
flex-direction: row;
align-items: center;
background-color: #FFFFFF;
border: 2rpx solid #E5E7EB;
border-radius: 24rpx;
height: 84rpx;
padding: 0rpx 20rpx 0rpx 20rpx;
}
.vs-srch-input {
flex: 1;
font-size: 29.2rpx;
color: #1F2937;
height: 80rpx;
padding: 0rpx 16rpx 0rpx 16rpx;
}
.vs-srch-scan {
width: 52rpx;
height: 52rpx;
border-radius: 16rpx;
background-color: #EFF6FF;
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
}
.vs-scan-btn {
width: 84rpx;
height: 84rpx;
border-radius: 24rpx;
background-color: #EFF6FF;
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
margin-left: 16rpx;
}
/* ---------- 筛选 ---------- */
.vs-filter {
display: flex;
flex-direction: row;
margin-top: 20rpx;
}
.vs-sel {
flex: 1;
display: flex;
flex-direction: row;
align-items: center;
background-color: #FFFFFF;
border: 2rpx solid #E5E7EB;
border-radius: 22rpx;
padding: 18rpx 22rpx 18rpx 22rpx;
}
.vs-sel-on {
border: 2rpx solid #2563EB;
background-color: #EFF6FF;
}
.vs-sel-l {
font-size: 23.6rpx;
color: #9CA3AF;
margin-right: 14rpx;
}
.vs-sel-v {
flex: 1;
font-size: 28rpx;
font-weight: bold;
color: #1F2937;
lines: 1;
}
.vs-sel-v-on {
color: #2563EB;
}
/* ---------- 伤员卡 ---------- */
.vs-card {
position: relative;
background-color: #FFFFFF;
border: 2rpx solid #E5E7EB;
border-radius: 26rpx;
padding: 22rpx 24rpx 22rpx 32rpx;
margin-bottom: 18rpx;
overflow: hidden;
}
.vs-card-bar {
position: absolute;
left: 0rpx;
top: 0rpx;
bottom: 0rpx;
width: 8rpx;
}
.vs-card-l1 {
border-left: 8rpx solid #DC2626;
}
.vs-card-l2 {
border-left: 8rpx solid #F59E0B;
}
.vs-card-l3 {
border-left: 8rpx solid #10B981;
}
.vs-card-l4 {
border-left: 8rpx solid #1F2937;
}
.vs-avt {
width: 76rpx;
height: 76rpx;
border-radius: 22rpx;
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
margin-right: 20rpx;
}
.vs-avt-l1 {
background-color: #DC2626;
}
.vs-avt-l2 {
background-color: #D97706;
}
.vs-avt-l3 {
background-color: #059669;
}
.vs-avt-l4 {
background-color: #1F2937;
}
.vs-avt-t {
font-size: 33.6rpx;
font-weight: bold;
color: #FFFFFF;
}
.vs-name {
font-size: 30.2rpx;
font-weight: bold;
color: #1F2937;
margin-right: 12rpx;
}
.vs-code {
font-size: 23.6rpx;
color: #9CA3AF;
margin-top: 6rpx;
}
.vs-inj {
margin-top: 16rpx;
background-color: #F9FAFB;
border-radius: 16rpx;
padding: 12rpx 16rpx 12rpx 16rpx;
}
.vs-inj-t {
font-size: 24.6rpx;
color: #6B7280;
line-height: 1.5;
}
.vs-bot {
display: flex;
flex-direction: row;
align-items: center;
margin-top: 16rpx;
}
.vs-status {
border-radius: 18rpx;
padding: 8rpx 20rpx 8rpx 20rpx;
}
.vs-status-t {
font-size: 24.6rpx;
font-weight: bold;
}
.vs-time {
font-size: 23.6rpx;
color: #9CA3AF;
}
.vs-hint {
display: flex;
flex-direction: row;
align-items: center;
justify-content: center;
padding: 12rpx 0rpx 16rpx 0rpx;
}
.vs-hint-t {
font-size: 23.6rpx;
color: #9CA3AF;
}
</style>