Files
his/openhis-ui-vue3/src/views/inpatientNurse/inOut/components/bedAllocation.vue
2025-09-25 10:36:59 +08:00

741 lines
20 KiB
Vue
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>
<div class="bedAllocation-container">
<div>
<el-form :model="queryParams" ref="queryRef" :inline="true">
<el-row style="margin-top: 8px;">
<el-form-item label="入院病区" prop="wardId" style="width: 240px">
<el-select v-model="queryParams.wardId" @change="changeWardLocationId">
<el-option
v-for="item in initInfoOptions.wardListOptions"
:key="item.id"
:label="item.name"
:value="item.id"
/>
</el-select>
</el-form-item>
<el-form-item label="入院病房" prop="houseId">
<el-select v-model="queryParams.houseId" style="width: 240px">
<el-option
v-for="item in wardLocationList"
:key="item.id"
:label="item.name"
:value="item.id"
/>
</el-select>
</el-form-item>
<el-form-item label="住院状态" prop="encounterStatus">
<el-select v-model="queryParams.encounterStatus" style="width: 240px">
<el-option label="全部" value="" />
<el-option
v-for="item in initInfoOptions.encounterStatusOptions"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</el-form-item>
<el-form-item label="床位状态" prop="bedStatus" style="width: 240px">
<el-select v-model="bedStatusFilter">
<el-option label="全部" value="" />
<el-option
v-for="item in initInfoOptions.bedStatusOptions"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</el-form-item>
<el-form-item>
<div style="margin-bottom: 10px">
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
</div>
</el-form-item>
</el-row>
</el-form>
</div>
<div style="display: flex; justify-content: space-between; height: 86vh">
<div style="width: 15%; height: 100%; border: 1px solid #eee; border-right: 0">
<div style="padding: 10px; border: 1px solid #eee; height: 50px; border-right: 0">
<span>新入院患者</span>
</div>
<div style="width: 100%; padding: 10px">
<el-input
v-model="queryParams.searchKey"
placeholder="请输入住院号"
clearable
style="width: 100%; margin-bottom: 10px"
@keyup.enter="handleQuery"
>
<template #append>
<el-button icon="Search" @click="handleQuery" />
</template>
</el-input>
<el-scrollbar height="700px">
<div
v-for="(item, index) in patientList"
:class="item.active ? 'patient-card actived' : 'patient-card'"
:key="item.id"
@click="handleCardClick(item, index)"
@dblclick="handleCardDblClick(item)"
draggable="true"
@dragstart="handleDragStart($event, item)"
@dragend="handleDragEnd"
>
<div class="main-info-container">
<div class="name-container">
<!-- 患者姓名 -->
<div class="name" style="max-width: 90px">
<el-text class="name" width="auto">{{ item.patientName || '未知' }}</el-text>
</div>
</div>
<div class="name-container">
<!-- 患者性别/年龄 -->
<div class="age">
<el-text class="name" width="auto">
{{ item.genderEnum_enumText }}/{{ item.age }}/{{ item.priorityEnum_enumText }}
</el-text>
</div>
</div>
<div class="patient-tag" :class="getPatientTagClass(item)">
{{ item.encounterStatus_enumText }}
</div>
</div>
<div class="doctor-parent-line" />
<div class="personal-info-container">
<div class="name-container">
<div class="name">
<el-text class="name" width="auto">入院时间</el-text>
<el-text class="name" width="auto">
{{ item.startTime ? formatDate(item.startTime) : '-' }}
</el-text>
</div>
</div>
</div>
<div class="personal-info-container">
<div class="name-container">
<el-text class="name" width="auto">入院科室</el-text>
<el-text class="name" width="auto">
{{ item.organizationName ? item.organizationName : '-' }}
</el-text>
</div>
</div>
<div class="personal-info-container">
<div class="name-container">
<el-text class="name" width="auto">住院号</el-text>
<el-text class="name" width="auto">
{{ item.busNo ? item.busNo : '-' }}
</el-text>
</div>
</div>
</div>
</el-scrollbar>
</div>
</div>
<div
class="disabled-wrapper"
style="width: 85%; border: 1px solid #eee; position: relative"
@dragover="handleDragOver"
@drop="handleDrop"
>
<div class="bedAllocation-search">
<div
v-for="item in filteredBadList"
:key="item.id"
class="search-item"
:class="{ 'drag-over': draggedOverBedId === item.bedId }"
style="padding: 10px; border: 1px solid #eee; margin: 5px; border-radius: 4px;"
@dragover="handleBedDragOver($event, item)"
@dragenter="handleBedDragEnter(item)"
@dragleave="handleBedDragLeave"
@drop="handleBedDrop($event, item)"
>
<div class="bed-tag" :class="getBedTagClass(item)">
{{ item.bedStatus_enumText }}
</div>
<div>
<div>
{{ item.houseName + '-' + item.bedName }}
</div>
<div style="margin-top: 6px;">
{{ item.patientName }}
<el-text class="name" width="auto" v-if="item.encounterId">
{{ item.genderEnum_enumText }}/{{ item.age }}
</el-text>
</div>
</div>
</div>
</div>
</div>
</div>
<TransferInDialog
v-model:visible="transferInDialogVisible"
:pendingInfo="pendingInfo"
:priorityOptions="priorityOptions"
@okAct="handleTransferInOk" />
<SignEntryDialog v-model:visible="signEntryDialogVisible" />
</div>
</template>
<script setup lang="ts">
import { getCurrentInstance, onBeforeMount, onMounted, reactive, ref, computed } from 'vue'
import { TransferInDialog, SignEntryDialog } from './index'
import { getPendingInfo, getBedInfo, getInit, childLocationList, getPractitionerWard } from './api'
import { formatDate } from '@/utils/index';
import { init } from '../../../basicmanage/consumablesBinding/components/api';
import { ElMessage, ElMessageBox } from 'element-plus'
// 定义相关类型
interface OptionItem {
id: string | number;
name: string;
}
interface OptionItemTwo {
value: string | number;
label: string;
}
interface InitInfoOptions {
encounterStatusOptions: OptionItemTwo[];
bedStatusOptions: OptionItemTwo[];
priorityOptions: OptionItem[];
wardListOptions: OptionItemTwo[]
}
const transferInDialogVisible = ref(false)
const signEntryDialogVisible = ref(false)
const state = reactive({})
const loading = ref(false)
const total = ref();
const badList = ref<any[]>([])
const patientList = ref<any[]>([]);
const pendingInfo = ref({})
const draggedPatient = ref<any>(null);
const wardLocationList = ref<OptionItem[]>([])
const draggedOverBedId = ref<number | null>(null)
const priorityOptions = ref<OptionItem[]>([])
const wardListOptions = ref<OptionItem[]>([])
const houseListOptions = ref<OptionItem[]>([])
const initInfoOptions = ref<InitInfoOptions>({
encounterStatusOptions: [],
bedStatusOptions: [],
priorityOptions: [],
wardListOptions: []
})
// 新增床状态筛选字段
const bedStatusFilter = ref('')
onBeforeMount(() => {})
const queryParams = ref({
pageNo: 1,
pageSize: 50,
searchKey: '',
wardId: '',
houseId: '',
encounterStatus: '',
bedStatus: '' // 这个字段现在只用于床位查询,不再用于患者列表查询
})
onMounted(() => {
getInit().then(res => {
initInfoOptions.value = res.data
priorityOptions.value = res.data.priorityOptions || []
getList()
})
getPractitionerWard().then(res => {
queryParams.value.wardId = res[0].id
initInfoOptions.value.wardListOptions = res
changeWardLocationId(res[0].id)
})
})
defineExpose({ state })
// 计算属性:根据床状态筛选条件过滤床位列表
const filteredBadList = computed(() => {
if (!bedStatusFilter.value) {
return badList.value
}
return badList.value.filter(item => item.bedStatus == bedStatusFilter.value)
})
const getList = () => {
getPatientList()
// 床位查询不使用encounterStatus参数只使用基本的查询参数
const bedQueryParams = {
...queryParams.value,
encounterStatus: undefined // 移除encounterStatus确保不影响床位列表查询
}
getBedInfo(bedQueryParams).then(res => {
badList.value = res.data.records
})
}
// 重置查询条件
function resetQuery() {
queryParams.value = {
pageNo: 1,
pageSize: 50,
searchKey: '',
wardId: '',
houseId: '',
encounterStatus: '',
bedStatus: ''
}
bedStatusFilter.value = ''
getList()
}
function changeWardLocationId(id) {
let params = {
locationId: id,
locationForm: 10
}
queryParams.value.houseId = ''
childLocationList(params).then(res => {
wardLocationList.value = res
})
}
// 获新入院患者列表
function getPatientList() {
// 为患者列表查询创建一个新的参数对象不包含bedStatus
const patientQueryParams = {
...queryParams.value,
bedStatus: undefined // 移除bedStatus确保不影响患者列表查询
}
getPendingInfo(patientQueryParams).then(res => {
loading.value = false
patientList.value = res.data.records
total.value = res.data.total;
})
}
const handleTransferInOk = () => {
transferInDialogVisible.value = false
getList()
}
function handleCardClick(item: any, index: number) {
}
// 双击患者卡片事件
function handleCardDblClick(item: any) {
if(item.encounterStatus == 2) {
ElMessage({
message: '请分配病床!',
type: 'warning',
grouping: true,
showClose: true,
})
}else {
pendingInfo.value = {
...item,
entranceType: 1
};
transferInDialogVisible.value = true;
}
}
// 拖拽开始事件
function handleDragStart(event: DragEvent, item: any) {
if (event.dataTransfer) {
event.dataTransfer.setData('text/plain', JSON.stringify(item));
draggedPatient.value = item;
// 设置拖拽样式
if (event.target instanceof HTMLElement) {
event.target.classList.add('dragging');
}
}
}
function handleQuery() {
getList()
}
// 拖拽结束事件
function handleDragEnd(event: DragEvent) {
// 清除拖拽样式
if (event.target instanceof HTMLElement) {
event.target.classList.remove('dragging');
}
// 清除高亮
draggedOverBedId.value = null;
}
// 拖拽过程中阻止默认行为
function handleDragOver(event: DragEvent) {
event.preventDefault();
}
// 拖拽放置事件
function handleDrop(event: DragEvent) {
event.preventDefault();
// 清除高亮
draggedOverBedId.value = null;
}
// 床位拖拽事件
function handleBedDragOver(event: DragEvent, bed: any) {
event.preventDefault();
}
// 床位拖拽进入事件
function handleBedDragEnter(bed: any) {
draggedOverBedId.value = bed.bedId;
}
// 床位拖拽离开事件
function handleBedDragLeave(event: DragEvent) {
// 避免子元素触发的dragleave事件清除高亮
if (event.target === event.currentTarget) {
draggedOverBedId.value = null;
}
}
// 床位放置事件
function handleBedDrop(event: DragEvent, bed: any) {
if(draggedPatient.value.encounterStatus == 2 && bed.bedStatus == 5) {
ElMessage({
message: '该床位已被占用!',
type: 'error',
grouping: true,
showClose: true,
})
}else {
if(draggedPatient.value.encounterStatus == 5) {
ElMessageBox.confirm('是否确认换床?')
.then(() => {
event.preventDefault();
// 清除高亮
draggedOverBedId.value = null;
if (draggedPatient.value) {
// 合并患者信息和床位信息
pendingInfo.value = {
...draggedPatient.value,
bedName: bed.bedName,
bedId: bed.bedId,
targetHouseId: bed.houseId,
entranceType: 2,
targetEncounterId: bed.encounterId,
houseName: bed.houseName
};
// 显示TransferInDialog对话框
transferInDialogVisible.value = true;
// 清空拖拽的患者信息
draggedPatient.value = null;
}
})
.catch(() => {
// catch error
})
}else {
event.preventDefault();
// 清除高亮
draggedOverBedId.value = null;
if (draggedPatient.value) {
// 合并患者信息和床位信息
pendingInfo.value = {
...draggedPatient.value,
bedName: bed.bedName,
bedId: bed.bedId,
targetHouseId: bed.houseId,
entranceType: 2,
targetEncounterId: bed.encounterId
};
// 显示TransferInDialog对话框
transferInDialogVisible.value = true;
// 清空拖拽的患者信息
draggedPatient.value = null;
}
}
}
}
// 根据bedStatus获取床位标签类
function getBedTagClass(item: any) {
if (item.bedStatus == 6) {
return 'blue-tag';
} else if (item.bedStatus == 5) {
return 'green-tag';
}
return '';
}
function getPatientTagClass(item: any) {
if (item.encounterStatus == 2) {
return 'blue-tag';
} else if (item.encounterStatus == 5) {
return 'green-tag';
}
return '';
}
const addSigns = (row: any) => {
// TODO 新增入院体征
signEntryDialogVisible.value = true
}
const selectBed = (row: any) => {
pendingInfo.value = row
// TODO 选床 入科
transferInDialogVisible.value = true
}
</script>
<style lang="scss" scoped>
.bedAllocation-container {
height: 100%;
display: flex;
flex-direction: column;
.bedAllocation-search {
height: 100px;
width: 100%;
display: flex;
flex-wrap: wrap;
align-items: center;
.search-item {
width: 200px;
height: 100px;
display: flex;
margin: 10px;
border-radius: 4px;
box-shadow: 0 2px 2px 0 rgba(57.55, 69.04, 86.28, 20%);
position: relative;
transition: all 0.2s ease;
&.drag-over {
background-color: #e6f7ff;
border-color: #1890ff;
box-shadow: 0 0 8px rgba(24, 144, 255, 0.6);
transform: scale(1.03);
}
&.blue-bed {
background-color: #e6f7ff; // 蓝色背景
border-color: #91d5ff;
}
&.green-bed {
background-color: #f6ffed; // 绿色背景
border-color: #b7eb8f;
}
.bed-tag {
position: absolute;
top: 0;
right: 0;
font-size: 14px;
padding: 2px 8px;
border-radius: 4px;
&.blue-tag {
background-color: #1890ff; // 蓝色标签
color: white;
}
&.green-tag {
background-color: #52c41a; // 绿色标签
color: white;
}
}
}
}
.bedAllocation-table {
flex: auto;
}
}
.patient-card {
width: 100%;
overflow: hidden;
background-color: #fff;
border: 1px solid;
border-color: #eee;
border-radius: 4px;
box-shadow: 0 2px 2px 0 rgba(57.55, 69.04, 86.28, 20%);
margin-bottom: 10px;
cursor: pointer;
transition: all 0.2s ease;
&.actived {
background-color: rgb(7, 155, 140, 5%);
border-color: var(--el-color-primary);
}
&.dragging {
opacity: 0.5;
transform: rotate(5deg);
}
.cross-dept {
height: 24px;
padding: 0 16px;
color: #fff;
font-size: 14px;
line-height: 24px;
background-color: #256d95;
}
.doctor-parent-line {
margin: 0 16px;
border-bottom: 1px dashed #ddd;
}
.personal-info-container {
display: flex;
align-items: center;
justify-content: space-between;
margin: 8px 0;
padding: 0 16px;
.name-container {
display: flex;
align-items: center;
height: 18px;
.name {
color: #333;
font-size: 14px;
}
.age {
margin-left: 10px;
color: #666;
font-size: 14px;
}
}
.change-department {
width: 58px;
height: 24px;
color: #5585e3;
font-size: 14px;
line-height: 24px;
text-align: center;
background: #e6edfb;
border-radius: 4px;
}
}
.dept {
margin-bottom: 4px;
padding: 0 16px;
display: flex;
justify-content: space-between;
align-items: center;
.doctor {
display: flex;
align-items: center;
height: 32px;
line-height: 32px;
.doctor_name {
display: flex;
align-items: center;
margin-left: 4px;
color: #333;
}
}
.deptNurseName {
display: flex;
align-items: center;
height: 32px;
color: #256d95;
line-height: 32px;
}
}
}
// 拖拽时的全局样式
.patient-card.dragging {
opacity: 0.6;
box-shadow: 0 5px 15px rgba(0, 0, 0, 0.2);
transform: rotate(3deg);
z-index: 100;
}
.main-info-container {
display: flex;
align-items: center;
justify-content: space-between;
height: 32px;
margin: 7px 0;
padding: 0 16px;
margin-right: 48px;
position: relative;
.patient-tag {
position: absolute;
top: -7px;
right: -48px;
font-size: 14px;
padding: 2px 8px;
border-radius: 4px;
&.blue-tag {
background-color: #1890ff; // 蓝色标签
color: white;
}
&.green-tag {
background-color: #52c41a; // 绿色标签
color: white;
}
}
.bed-container {
display: flex;
flex: 1;
align-items: center;
min-width: 0;
.bed {
flex-grow: 0;
flex-shrink: 1;
min-width: 0;
:deep(.bed-font) {
color: #333;
font-weight: 600;
font-size: 16px;
}
}
.bed_new {
flex-shrink: 0;
width: 10px;
height: 10px;
margin-left: 4px;
background: #29af6f;
border-radius: 50%;
}
}
.indepatient-code-container {
display: flex;
flex-shrink: 0;
align-items: center;
padding-left: 6px;
color: #666;
font-size: 14px;
.sign {
width: 24px;
height: 24px;
color: white;
line-height: 24px;
text-align: center;
border-radius: 50%;
user-select: none;
}
}
}
.pagination-container {
padding: 0px 20px !important;
}
::v-deep .el-form-item--default .el-form-item__label {
line-height: 44px;
}
</style>