741 lines
20 KiB
Vue
741 lines
20 KiB
Vue
<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> |