Files
his/openhis-ui-vue3/src/views/inpatientNurse/rollFee/index.vue
chenqi abc0674531 ```
docs(release-notes): 添加住院护士站划价功能说明和发版记录

- 新增住院护士站划价服务流程说明文档,详细描述了从参数预处理到结果响应的五大阶段流程
- 包含耗材类医嘱和诊疗活动类医嘱的差异化处理逻辑
- 添加完整的发版内容记录,涵盖新增菜单功能和各模块优化点
- 记录了住院相关功能的新增和门诊业务流程的修复
```
2025-12-25 14:13:14 +08:00

622 lines
17 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="app-container">
<el-row :gutter="10">
<el-form :model="queryParams" ref="queryRef" inline="true">
<el-form-item label="入院病区" prop="wardId" style="width: 240px">
<el-select
v-model="queryParams.wardId"
@change="changeWardLocationId"
filterable
clearable
>
<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"
@change="handleQuery"
filterable
clearable
>
<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"
@change="handleQuery"
filterable
clearable
>
<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" filterable clearable @change="handleQuery">
<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>
<el-button type="primary" icon="Search" @click="handleQuery">搜索</el-button>
<el-button icon="Refresh" @click="resetQuery">重置</el-button>
</el-form-item>
</el-form>
</el-row>
<el-row :gutter="10">
<el-col :span="4">
<el-card shadow="never">
<template #header>
<div class="card-header">
<span>新入院患者</span>
</div>
</template>
<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)"
>
<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 style="margin-left: 15px; color: #333; font-size: 14px">
{{ item.contractName }}
</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>
</el-card>
</el-col>
<el-col :span="20">
<el-row :gutter="10" class="mb8">
<el-col :span="1.5">
<el-button
type="primary"
plain
icon="Plus"
@click="handleAddFeeItemm"
:disabled="!selectPatient"
>
新增滚费项目
</el-button>
</el-col>
</el-row>
<el-table :data="rollInfoPageList" ref="table" border>
<el-table-column label="滚费项目" prop="definitionName" align="center" />
<el-table-column label="单位" prop="unitCodeName" align="center" />
<el-table-column label="数量" prop="quantity" align="center" />
<el-table-column label="状态" prop="statusEnum_enumText" align="center">
<template #default="scope">
<el-tag v-if="scope.row.statusEnum_enumText === '启用'" type="success">
{{ scope.row.statusEnum_enumText }}
</el-tag>
<el-tag v-else type="warning">
{{ scope.row.statusEnum_enumText }}
</el-tag>
</template>
</el-table-column>
<el-table-column label="操作" align="center">
<template #default="scope">
<el-button
link
type="success"
icon="Edit"
@click="handleEnable(scope.row)"
v-if="scope.row.statusEnum_enumText === '停用'"
>
启用
</el-button>
<el-button
link
type="warning"
icon="Edit"
@click="handleStop(scope.row)"
v-if="scope.row.statusEnum_enumText === '启用'"
>
停用
</el-button>
<el-button link icon="Edit" type="danger" @click="handleDelete(scope.row)">
删除
</el-button>
</template>
</el-table-column>
</el-table>
</el-col>
</el-row>
<el-dialog
title="新增滚费配置"
v-model="dialogVisible"
width="400px"
:before-close="handleClose"
append-to-body
>
<el-form ref="formRef" :model="form" :rules="rules" label-width="100px">
<el-form-item label="滚费项目" prop="instanceId">
<el-select
v-model="form.instanceId"
placeholder="请选择"
filterable
clearable
style="width: 100%"
@change="handleChageInstanceId"
>
<el-option
v-for="item in feeItemOptions"
:key="item.instanceId"
:label="item.instanceName"
:value="item.instanceId"
>
</el-option>
</el-select>
</el-form-item>
<el-form-item label="数量" prop="quantity">
<el-input-number
v-model="form.quantity"
placeholder="请输入数量"
controls-position="right"
style="width: 100%"
/>
</el-form-item>
<el-form-item label="单位" prop="unit">
<el-input v-model="form.unit" placeholder="请输入" disabled style="width: 100%" />
</el-form-item>
</el-form>
<template #footer>
<div class="dialog-footer">
<el-button @click="handleClose"> </el-button>
<el-button type="primary" @click="handleSubmit"> </el-button>
</div>
</template>
</el-dialog>
</div>
</template>
<script setup>
import { onBeforeMount, onMounted, reactive, ref, getCurrentInstance } from 'vue';
import {
getPendingInfo,
getBedInfo,
getInit,
childLocationList,
getPractitionerWard,
getDataSource,
saveInpatientRoll,
getRollInfoPage,
enableRoll,
stopRoll,
deleteRoll,
} from './components/api.js';
import { formatDate } from '@/utils/index';
import { ElMessage } from 'element-plus';
const { proxy } = getCurrentInstance();
const transferInDialogVisible = ref(false);
const dialogVisible = ref(false);
const state = reactive({});
const loading = ref(false);
const total = ref();
const badList = ref([]);
const patientList = ref([]);
const pendingInfo = ref({});
const wardLocationList = ref([]);
const priorityOptions = ref([]);
const initInfoOptions = ref({
encounterStatusOptions: [],
bedStatusOptions: [],
priorityOptions: [],
wardListOptions: [],
});
// 新增床状态筛选字段
const bedStatusFilter = ref('');
onBeforeMount(() => {});
const queryParams = ref({
pageNo: 1,
pageSize: 50,
searchKey: '',
wardId: '',
houseId: '',
encounterStatus: '',
bedStatus: '', // 这个字段现在只用于床位查询,不再用于患者列表查询
});
const formRef = ref(null);
const form = reactive({
instanceId: undefined,
unit: undefined,
quantity: 1,
});
const rules = {
instanceId: [{ required: true, message: '请选择滚费项目', trigger: 'change' }],
quantity: [
{ required: true, message: '请输入数量', trigger: 'blur' },
{ type: 'number', min: 0, message: '数量必须大于等于0', trigger: 'blur' },
],
};
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 feeItemOptions = ref([]);
async function getInitDataSourceOptions() {
const res = await getDataSource();
feeItemOptions.value = res.data;
}
const getList = () => {
getPatientList();
// 床位查询不使用encounterStatus参数只使用基本的查询参数
const bedQueryParams = {
...queryParams.value,
encounterStatus: undefined, // 移除encounterStatus确保不影响床位列表查询
};
getBedInfo(bedQueryParams).then((res) => {
badList.value = res.data.records;
});
};
function handleEnable(row) {
proxy
.$confirm('确定启用吗?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
})
.then(() => {
enableRoll([row.id]).then((res) => {
if (res.code == 200) {
proxy.$modal.msgSuccess('启用成功');
getRollInfoPageList();
}
});
});
}
function handleStop(row) {
proxy
.$confirm('确定停用吗?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
})
.then(() => {
stopRoll([row.id]).then((res) => {
if (res.code == 200) {
proxy.$modal.msgSuccess('停用成功');
getRollInfoPageList();
}
});
});
}
function handleDelete(row) {
proxy
.$confirm('确定删除吗?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
})
.then(() => {
deleteRoll([row.id]).then((res) => {
if (res.code == 200) {
proxy.$modal.msgSuccess('删除成功');
getRollInfoPageList();
}
});
});
}
// 重置查询条件
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;
});
handleQuery();
}
// 获新入院患者列表
function getPatientList() {
// 为患者列表查询创建一个新的参数对象不包含bedStatus
const patientQueryParams = {
...queryParams.value,
bedStatus: undefined, // 移除bedStatus确保不影响患者列表查询
};
getPendingInfo(patientQueryParams).then((res) => {
loading.value = false;
// 为每个患者项初始化 active 属性
patientList.value = res.data.records.map((item) => ({
...item,
active: false,
}));
total.value = res.data.total;
});
}
// 选中患者
const selectPatient = ref(null);
function handleCardClick(item, index) {
// 清除所有项的激活状态
patientList.value.forEach((patient) => {
patient.active = false;
});
// 设置当前项为激活状态
item.active = true;
console.log(item);
selectPatient.value = item;
getRollInfoPageList();
}
function handleChageInstanceId(value) {
form.unit = feeItemOptions.value.find(
(item) => item.instanceId === value
).permittedUnitCode_dictText;
}
function handleClose() {
dialogVisible.value = false;
// 重置表单
form.name = '';
form.unit = '';
form.quantity = 1;
formRef.value?.resetFields();
}
// 获取滚费信息列表
const rollInfoPageList = ref([]);
function getRollInfoPageList() {
const encounterId = selectPatient.value.encounterId;
loading.value = true;
getRollInfoPage(encounterId).then((res) => {
rollInfoPageList.value = res.data;
loading.value = false;
});
}
function handleSubmit() {
formRef.value?.validate((valid) => {
if (valid) {
const param = {
encounterId: selectPatient.value.encounterId,
instanceId: form.instanceId,
quantity: form.quantity,
};
proxy
.$confirm('确认保存吗?', '提示', {
confirmButtonText: '确定',
cancelButtonText: '取消',
type: 'warning',
})
.then(() => {
saveInpatientRoll(param).then((res) => {
ElMessage.success('新增成功');
handleClose();
getRollInfoPageList();
});
})
.catch(() => {});
} else {
ElMessage.warning('请完善表单信息');
}
});
}
// 双击患者卡片事件
function handleCardDblClick(item) {
if (item.encounterStatus == 2) {
ElMessage({
message: '请分配病床!',
type: 'warning',
grouping: true,
showClose: true,
});
} else {
pendingInfo.value = {
...item,
entranceType: 1,
};
transferInDialogVisible.value = true;
}
}
function handleQuery() {
getList();
}
function handleAddFeeItemm() {
dialogVisible.value = true;
getInitDataSourceOptions();
}
function getPatientTagClass(item) {
if (item.encounterStatus == 2) {
return 'blue-tag';
} else if (item.encounterStatus == 5) {
return 'green-tag';
}
return '';
}
</script>
<style lang="scss" scoped>
.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);
}
.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;
}
}
}
}
.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;
}
}
}
</style>