版本更新

This commit is contained in:
Zhang.WH
2025-09-03 15:54:41 +08:00
parent 0b93d16b64
commit 8f82322d10
3290 changed files with 154339 additions and 23829 deletions

View File

@@ -0,0 +1,738 @@
<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.value"
:label="item.label"
:value="item.value"
/>
</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 } 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 || []
queryParams.value.wardId = res.data.wardListOptions[0].value
changeWardLocationId(res.data.wardListOptions[0].value)
getList()
})
})
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>