Files
his/openhis-ui-vue3/src/views/inpatientNurse/inOut/components/bedAllocation.vue

855 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 class="bedAllocation-main">
<PendingPatientList
:list="patientList"
:active-id="activePatientId"
@item-click="handleCardClick"
@item-dblclick="handleCardDblClick"
@dragstart="handleDragStart"
@dragend="handleDragEnd"
/>
<div class="disabled-wrapper right-panel" @dragover="handleDragOver" @drop="handleDrop">
<div class="right-filter">
<Filter
:query-params="queryParams"
:form-items="filterItems"
@query="handleQuery"
@reset="resetQuery"
>
<template #bedStatus>
<el-select v-model="bedStatusFilter" clearable style="width: 240px">
<el-option label="全部" value="" />
<el-option
v-for="item in initInfoOptions.bedStatusOptions"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
</template>
</Filter>
</div>
<el-scrollbar class="right-scrollbar">
<div class="bedAllocation-search">
<div
v-for="item in filteredBadList"
:key="item.id"
class="search-item"
:class="{ 'drag-over': draggedOverBedId === item.bedId }"
@dragover="handleBedDragOver($event, item)"
@dragenter="handleBedDragEnter(item)"
@dragleave="handleBedDragLeave"
@drop="handleBedDrop($event, item)"
>
<div class="bed-card__body">
<div>
<div class="bed-card__title" :title="item.houseName + '-' + item.bedName">
{{ item.houseName + '-' + item.bedName }}
</div>
<div class="bed-tag" :class="getBedTagClass(item)">
{{ item.bedStatus_enumText }}
</div>
</div>
<div class="bed-card__sub">
<span class="bed-card__patient" :title="item.patientName || ''">
{{ item.patientName || '—' }}
</span>
<el-tag
v-if="item.encounterId"
size="small"
class="bed-card__age-tag"
effect="plain"
:class="{
'bed-card__age-tag-female': item.genderEnum_enumText === '女性',
'bed-card__age-tag-male': item.genderEnum_enumText === '男性',
}"
>
{{ item.genderEnum_enumText || '-' }}
<span v-if="item.age"> · {{ item.age }}</span>
</el-tag>
</div>
</div>
</div>
</div>
</el-scrollbar>
</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 Filter from '@/components/TableLayout/Filter.vue';
import { getCurrentInstance, onBeforeMount, onMounted, reactive, ref, computed } from 'vue';
import TransferInDialog from './transferInDialog.vue';
import SignEntryDialog from './signEntryDialog.vue';
import { getPendingInfo, getBedInfo, getInit, childLocationList, getPractitionerWard } from './api';
import { formatDate } from '@/utils/index';
import { init } from '../../../basicmanage/consumablesBinding/components/api';
import { ElMessage, ElMessageBox, ElLoading } from 'element-plus';
import PendingPatientList from '@/components/PendingPatientList/index.vue';
// 定义相关类型
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 initInfoOptions = ref<InitInfoOptions>({
encounterStatusOptions: [],
bedStatusOptions: [],
priorityOptions: [],
wardListOptions: [],
});
let loadingInstance: any = undefined;
// 入院病区loading
const selectHosLoding = ref(true);
// 入院病房loading
const selectHoouseLoding = ref(true);
// 新增床状态筛选字段
const bedStatusFilter = ref('');
const activePatientId = computed(() => {
const active = patientList.value?.find?.((it) => it?.active);
return active?.id || '';
});
const filterItems = computed(() => [
{
type: 'select',
label: '入院病区',
prop: 'wardId',
style: { width: '240px' },
multiple: false,
filterable: false,
options: (initInfoOptions.value.wardListOptions || []).map((i) => ({
label: i.name,
value: i.id,
})),
extraprops: { loading: selectHosLoding.value, clearable: false },
onChange: (value) => {
changeWardLocationId(value);
},
},
{
type: 'select',
label: '入院病房',
prop: 'houseId',
style: { width: '240px' },
multiple: false,
filterable: false,
options: (wardLocationList.value || []).map((i) => ({ label: i.name, value: i.id })),
extraprops: { loading: selectHoouseLoding.value, clearable: true },
onChange: () => onHosHouse(),
},
{
type: 'select',
label: '住院状态',
prop: 'encounterStatus',
style: { width: '240px' },
multiple: false,
filterable: false,
options: [
{ label: '全部', value: '' },
...(initInfoOptions.value.encounterStatusOptions || []).map((i) => ({
label: i.label,
value: i.value,
})),
],
extraprops: { clearable: true },
onChange: () => onHosStatus(),
},
{
type: 'custom',
label: '床位状态',
prop: 'bedStatus',
slot: 'bedStatus',
style: { width: '240px' },
},
{
type: 'input',
label: '住院号',
prop: 'searchKey',
style: { width: '240px' },
placeholder: '请输入住院号',
clearable: true,
},
]);
onBeforeMount(() => {});
const queryParams = ref<{
pageNo: number;
pageSize: number;
searchKey: string;
wardId?: string;
houseId: string;
encounterStatus: string;
bedStatus: string;
}>({
pageNo: 1,
pageSize: 50,
searchKey: '',
wardId: '',
houseId: '',
encounterStatus: '',
bedStatus: '', // 这个字段现在只用于床位查询,不再用于患者列表查询
});
const ininData = () => {
Promise.all([
getInit().then((res) => {
initInfoOptions.value = res.data;
priorityOptions.value = res.data.priorityOptions || [];
return res;
}),
getPractitionerWard().then((res) => {
selectHosLoding.value = false;
queryParams.value.wardId = res[0].id;
initInfoOptions.value.wardListOptions = res;
return changeWardLocationId(res[0].id, true); // 传入 true 表示初始化阶段,不调用 getList
}),
]).then(() => {
getList();
});
};
onMounted(() => {
ininData();
});
const refreshTap = () => {
ininData();
};
defineExpose({ state, refreshTap });
// 计算属性:根据床状态筛选条件过滤床位列表
const filteredBadList = computed(() => {
if (!bedStatusFilter.value) {
return badList.value;
}
return badList.value.filter((item) => item.bedStatus == bedStatusFilter.value);
});
const getList = () => {
loadingInstance = ElLoading.service({ fullscreen: true });
getPatientList();
// 床位查询不使用encounterStatus参数只使用基本的查询参数
const bedQueryParams = {
...queryParams.value,
encounterStatus: undefined, // 移除encounterStatus确保不影响床位列表查询
};
getBedInfo(bedQueryParams).then((res) => {
loadingInstance.close();
badList.value = res.data.records;
});
};
// 重置查询条件
function resetQuery() {
// 不重置入院病区
const resetParams = {
...queryParams.value,
};
queryParams.value = {
...resetParams,
pageNo: 1,
pageSize: 50,
searchKey: '',
houseId: '',
encounterStatus: '',
bedStatus: '',
};
bedStatusFilter.value = '';
getList();
}
// 入院病区下拉选
function changeWardLocationId(id: string | number, isInit = false) {
let params = {
locationId: id,
locationForm: 10,
};
queryParams.value.houseId = '';
selectHoouseLoding.value = true;
return childLocationList(params).then((res) => {
selectHoouseLoding.value = false;
wardLocationList.value = res;
if (!isInit) {
getList();
}
return res;
});
}
// 入院病房下拉选
const onHosHouse = () => {
getList();
};
// 住院状态下拉选
const onHosStatus = () => {
getList();
};
// 获新入院患者列表
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(() => {});
} 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,
houseName: bed.houseName,
};
// 显示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) => {
signEntryDialogVisible.value = true;
};
const selectBed = (row: any) => {
pendingInfo.value = row;
transferInDialogVisible.value = true;
};
</script>
<style lang="scss" scoped>
.bedAllocation-container {
height: 100%;
display: flex;
flex-direction: column;
.bedAllocation-main {
flex: 1 1 auto;
min-height: 0;
display: flex;
}
.right-panel {
border: 1px solid #eee;
position: relative;
display: flex;
flex-direction: column;
min-height: 0;
}
.right-filter {
flex: none;
padding: 8px;
}
.right-scrollbar {
flex: 1 1 auto;
min-height: 0;
}
.bedAllocation-search {
height: 100px;
width: 100%;
display: flex;
flex-wrap: wrap;
align-items: center;
.search-item {
width: 200px;
height: 100px;
display: flex;
align-items: flex-start;
gap: 10px;
padding: 12px 12px 10px;
margin: 8px;
border: 1px solid #eee;
border-radius: 8px;
background: #fff;
box-shadow: 0 2px 6px rgba(58, 69, 86, 0.12);
position: relative;
transition: background-color 0.2s ease, border-color 0.2s ease, box-shadow 0.2s ease,
transform 0.2s ease;
&:hover {
border-color: rgba(24, 144, 255, 0.35);
box-shadow: 0 6px 14px rgba(58, 69, 86, 0.16);
transform: translateY(-1px);
}
&.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: #f0f9ff; // 蓝色背景
border-color: #91d5ff;
}
&.green-bed {
background-color: #f6ffed; // 绿色背景
border-color: #b7eb8f;
}
.bed-tag {
position: absolute;
top: 12px;
right: 10px;
font-size: 12px;
line-height: 18px;
height: 18px;
padding: 0 8px;
border-radius: 999px;
font-weight: 600;
letter-spacing: 0.2px;
&.blue-tag {
background-color: #1890ff; // 蓝色标签
color: white;
}
&.green-tag {
background-color: #52c41a; // 绿色标签
color: white;
}
}
.bed-card__body {
flex: 1 1 auto;
height: 100%;
min-width: 0;
overflow: hidden;
display: flex;
flex-direction: column;
justify-content: space-between;
}
.bed-card__title {
color: #1f2d3d;
font-size: 14px;
font-weight: 600;
line-height: 20px;
white-space: normal;
overflow: visible;
word-wrap: break-word;
margin-right: 40px;
}
.bed-card__sub {
margin-top: 8px;
display: flex;
align-items: center;
justify-content: space-between;
gap: 8px;
min-width: 0;
}
.bed-card__patient {
flex: 1 1 auto;
min-width: 0;
color: #111827;
font-size: 14px;
font-weight: 600;
line-height: 18px;
white-space: normal;
overflow: visible;
word-break: break-word;
}
.bed-card__age-tag {
flex: none;
border-radius: 999px;
padding: 0 8px;
}
.bed-card__age-tag-female {
border-color: rgb(255, 55, 158);
color: rgb(255, 126, 184);
}
.bed-card__age-tag-male {
border-color: #91d5ff;
color: #1677ff;
}
}
}
.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;
.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;
}
.commentDisply {
display: flex;
align-items: center;
justify-content: center;
}
</style>