This commit is contained in:
2026-09-24 16:25:22 +08:00
commit 7428184f01
1198 changed files with 314515 additions and 0 deletions
+432
View File
@@ -0,0 +1,432 @@
<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>