```
docs(release-notes): 添加住院护士站划价功能说明和发版记录 - 新增住院护士站划价服务流程说明文档,详细描述了从参数预处理到结果响应的五大阶段流程 - 包含耗材类医嘱和诊疗活动类医嘱的差异化处理逻辑 - 添加完整的发版内容记录,涵盖新增菜单功能和各模块优化点 - 记录了住院相关功能的新增和门诊业务流程的修复 ```
This commit is contained in:
@@ -0,0 +1,136 @@
|
||||
<template>
|
||||
<div class="nurse-nav-bar" :style="{ background: background }">
|
||||
<div class="nav-scroll">
|
||||
<div
|
||||
v-for="nav in navs"
|
||||
:key="nav.path"
|
||||
class="nav-tab"
|
||||
:class="{ 'is-active': currentPath === nav.path, disabled: nav.disabled }"
|
||||
:style="nav.accent ? { '--accent-color': nav.accent } : null"
|
||||
@click="() => handleNav(nav)"
|
||||
>
|
||||
<div class="nav-icon" v-if="nav.icon">
|
||||
<component :is="nav.icon" />
|
||||
</div>
|
||||
<span class="nav-label">{{ nav.label }}</span>
|
||||
<span v-if="nav.desc" class="nav-desc">{{ nav.desc }}</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { computed } from 'vue';
|
||||
import { useRoute, useRouter } from 'vue-router';
|
||||
|
||||
const props = defineProps({
|
||||
navs: {
|
||||
type: Array,
|
||||
default: () => [],
|
||||
},
|
||||
activePath: {
|
||||
type: String,
|
||||
default: '',
|
||||
},
|
||||
background: {
|
||||
type: String,
|
||||
default: '#f8f9fb',
|
||||
},
|
||||
});
|
||||
|
||||
const emit = defineEmits(['navigate']);
|
||||
|
||||
const route = useRoute();
|
||||
const router = useRouter();
|
||||
|
||||
const currentPath = computed(() => props.activePath || route.path);
|
||||
|
||||
function handleNav(nav) {
|
||||
if (nav.disabled) return;
|
||||
emit('navigate', nav);
|
||||
if (nav.path && nav.path !== currentPath.value) {
|
||||
router.push(nav.path);
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.nurse-nav-bar {
|
||||
--theme-color: var(--el-color-primary, #409eff);
|
||||
height: 40px;
|
||||
border-bottom: 1px solid #e4e7ed;
|
||||
padding: 0 12px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.nav-scroll {
|
||||
display: flex;
|
||||
gap: 8px;
|
||||
align-items: center;
|
||||
flex-wrap: wrap;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.nav-tab {
|
||||
height: 32px;
|
||||
padding: 0 14px;
|
||||
border-radius: 16px;
|
||||
background: #fff;
|
||||
border: 1px solid transparent;
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 6px;
|
||||
cursor: pointer;
|
||||
transition: all 0.15s ease;
|
||||
position: relative;
|
||||
color: #1f2d3d;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.nav-tab:hover {
|
||||
border-color: rgba(64, 158, 255, 0.45);
|
||||
color: var(--theme-color);
|
||||
background: rgba(64, 158, 255, 0.08);
|
||||
}
|
||||
|
||||
.nav-tab.is-active {
|
||||
background: rgba(64, 158, 255, 0.18);
|
||||
border-color: var(--theme-color);
|
||||
color: var(--theme-color);
|
||||
box-shadow: 0 3px 8px rgba(64, 158, 255, 0.12);
|
||||
}
|
||||
|
||||
.nav-tab.disabled {
|
||||
opacity: 0.5;
|
||||
cursor: not-allowed;
|
||||
box-shadow: none;
|
||||
}
|
||||
|
||||
.nav-icon {
|
||||
width: 18px;
|
||||
height: 18px;
|
||||
border-radius: 50%;
|
||||
background: rgba(64, 158, 255, 0.12);
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
color: var(--accent-color, var(--theme-color));
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.nav-tab.is-active .nav-icon {
|
||||
background: rgba(255, 255, 255, 0.8);
|
||||
color: var(--theme-color);
|
||||
}
|
||||
|
||||
.nav-label {
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.nav-desc {
|
||||
font-size: 12px;
|
||||
color: #909399;
|
||||
}
|
||||
</style>
|
||||
|
||||
23
openhis-ui-vue3/src/views/inpatientNurse/components/api.js
Normal file
23
openhis-ui-vue3/src/views/inpatientNurse/components/api.js
Normal file
@@ -0,0 +1,23 @@
|
||||
import request from '@/utils/request'
|
||||
|
||||
/**
|
||||
* 获取住院患者列表
|
||||
*/
|
||||
export function getPatientList(queryParams) {
|
||||
return request({
|
||||
url: '/nurse-station/advice-process/inpatient',
|
||||
method: 'get',
|
||||
params: queryParams
|
||||
})
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取当前登录人管理病区
|
||||
*/
|
||||
export function getWardList(queryParams) {
|
||||
return request({
|
||||
url: '/app-common/practitioner-ward',
|
||||
method: 'get',
|
||||
params: queryParams
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,305 @@
|
||||
<template>
|
||||
<div>
|
||||
<div>
|
||||
<el-input placeholder="住院号/姓名" v-model="searchKey" @keyup.enter="handleSearch">
|
||||
<template #append>
|
||||
<el-button icon="Search" @click="handleSearch" />
|
||||
</template>
|
||||
</el-input>
|
||||
</div>
|
||||
<el-tree
|
||||
ref="treeRef"
|
||||
:key="treeKey"
|
||||
:load="loadNode"
|
||||
lazy
|
||||
show-checkbox
|
||||
node-key="id"
|
||||
default-expand-all
|
||||
:props="{ label: 'name', children: 'children' }"
|
||||
@node-click="handleNodeClick"
|
||||
@check="handleCheckChange"
|
||||
>
|
||||
<template #default="{ node, data }">
|
||||
<div class="custom-tree-node" v-if="node.level === 2">
|
||||
<span>{{ data.bedName + ' / ' + node.label }}</span>
|
||||
<span class="tree-node-actions">
|
||||
{{ data.genderEnum_enumText + ' / ' + data.age }}
|
||||
</span>
|
||||
</div>
|
||||
</template>
|
||||
</el-tree>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { getPatientList, getWardList } from './api';
|
||||
import { updatePatientInfoList } from './store/patient';
|
||||
import { ref, nextTick, inject, defineExpose } from 'vue';
|
||||
|
||||
const treeRef = ref(null);
|
||||
const searchKey = ref('');
|
||||
const treeKey = ref(0); // 用于强制重新渲染树组件
|
||||
const wardList = ref([]); // 存储病区列表
|
||||
const patientDataCache = ref(new Map()); // 缓存患者数据:key为wardId,value为患者列表
|
||||
|
||||
const handleGetPrescription = inject('handleGetPrescription');
|
||||
|
||||
/**
|
||||
* 加载树节点数据
|
||||
* @param {Object} node - 树节点对象
|
||||
* @param {Function} resolve - 解析函数,用于返回子节点数据
|
||||
*/
|
||||
function loadNode(node, resolve) {
|
||||
// 根节点:加载所有病区
|
||||
if (node.level === 0) {
|
||||
loadWardList(resolve);
|
||||
}
|
||||
// 病区节点:加载该病区下的患者列表
|
||||
else if (node.level === 1) {
|
||||
const wardId = node.data.id;
|
||||
loadPatientList(wardId, resolve);
|
||||
}
|
||||
// 其他层级返回空数组
|
||||
else {
|
||||
resolve([]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载病区列表
|
||||
* @param {Function} resolve - 解析函数
|
||||
*/
|
||||
function loadWardList(resolve) {
|
||||
getWardList()
|
||||
.then((res) => {
|
||||
// 格式化病区数据
|
||||
const wards = Array.isArray(res) ? res : res.data || [];
|
||||
wardList.value = wards;
|
||||
|
||||
// 将病区数据转换为树节点格式
|
||||
const wardNodes = wards.map((ward) => ({
|
||||
id: ward.id,
|
||||
name: ward.name || ward.wardName || '',
|
||||
leaf: false, // 病区节点不是叶子节点
|
||||
...ward,
|
||||
}));
|
||||
|
||||
resolve(wardNodes);
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error('加载病区列表失败:', error);
|
||||
resolve([]);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载指定病区的患者列表
|
||||
* @param {String|Number} wardId - 病区ID
|
||||
* @param {Function} resolve - 解析函数
|
||||
*/
|
||||
function loadPatientList(wardId, resolve) {
|
||||
// 构建缓存键(包含搜索条件)
|
||||
const cacheKey = `${wardId}_${searchKey.value || ''}`;
|
||||
|
||||
// 如果缓存中存在且搜索条件为空,直接使用缓存(搜索时需要重新加载)
|
||||
if (!searchKey.value && patientDataCache.value.has(cacheKey)) {
|
||||
const cachedPatients = patientDataCache.value.get(cacheKey);
|
||||
resolve(cachedPatients);
|
||||
return;
|
||||
}
|
||||
|
||||
// 调用接口获取患者列表
|
||||
getPatientList({
|
||||
wardId: wardId,
|
||||
searchKey: searchKey.value || '',
|
||||
})
|
||||
.then((res) => {
|
||||
// 处理返回的患者数据
|
||||
const records = res?.data?.records || [];
|
||||
|
||||
// 格式化患者数据
|
||||
const patients = records.map((item) => ({
|
||||
id: item.id || item.encounterId,
|
||||
name: item.patientName || '',
|
||||
leaf: true, // 患者节点是叶子节点
|
||||
...item,
|
||||
}));
|
||||
|
||||
// 缓存患者数据(仅在没有搜索条件时缓存)
|
||||
if (!searchKey.value) {
|
||||
patientDataCache.value.set(cacheKey, patients);
|
||||
}
|
||||
|
||||
resolve(patients);
|
||||
})
|
||||
.catch((error) => {
|
||||
console.error('加载患者列表失败:', error);
|
||||
resolve([]);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 重新加载所有病区的患者列表
|
||||
*/
|
||||
function reloadAllPatients() {
|
||||
if (!treeRef.value) return;
|
||||
|
||||
// 清除患者数据缓存
|
||||
patientDataCache.value.clear();
|
||||
|
||||
nextTick(() => {
|
||||
const rootNode = treeRef.value?.store?.root;
|
||||
if (!rootNode?.childNodes) return;
|
||||
|
||||
// 遍历所有已展开的病区节点,重新加载患者列表
|
||||
rootNode.childNodes.forEach((wardNode) => {
|
||||
if (wardNode && wardNode.level === 1 && wardNode.key) {
|
||||
reloadWardNodePatients(wardNode);
|
||||
}
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 重新加载指定病区节点的患者列表
|
||||
* @param {Object} wardNode - 病区节点对象
|
||||
*/
|
||||
function reloadWardNodePatients(wardNode) {
|
||||
if (!wardNode?.key || !treeRef.value) return;
|
||||
|
||||
const wardId = wardNode.data.id;
|
||||
const cacheKey = `${wardId}_${searchKey.value || ''}`;
|
||||
|
||||
// 清除该病区的缓存
|
||||
patientDataCache.value.delete(cacheKey);
|
||||
|
||||
// 清除旧的子节点
|
||||
treeRef.value.updateKeyChildren(wardNode.key, []);
|
||||
|
||||
// 重置节点状态
|
||||
wardNode.loaded = false;
|
||||
wardNode.childNodes = [];
|
||||
|
||||
// 重新加载患者列表
|
||||
loadPatientList(wardId, (children) => {
|
||||
treeRef.value.updateKeyChildren(wardNode.key, children);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 重新加载指定病区的患者列表
|
||||
* @param {String|Number} wardId - 病区ID
|
||||
*/
|
||||
function reloadWardPatients(wardId) {
|
||||
if (!treeRef.value) return;
|
||||
|
||||
nextTick(() => {
|
||||
const rootNode = treeRef.value?.store?.root;
|
||||
if (!rootNode?.childNodes) return;
|
||||
|
||||
// 查找对应的病区节点
|
||||
const wardNode = rootNode.childNodes.find((node) => node.data.id === wardId);
|
||||
if (wardNode) {
|
||||
reloadWardNodePatients(wardNode);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* 手动触发加载节点
|
||||
* @param {Object} node - 节点对象,如果为null则重新加载整个树
|
||||
*/
|
||||
function manualLoadNode(node = null) {
|
||||
if (!node) {
|
||||
// 重新加载整个树
|
||||
treeKey.value += 1;
|
||||
patientDataCache.value.clear();
|
||||
} else if (node.key && treeRef.value) {
|
||||
// 重新加载指定节点
|
||||
if (node.level === 1) {
|
||||
// 病区节点:重新加载患者列表
|
||||
reloadWardNodePatients(node);
|
||||
} else if (node.level === 0) {
|
||||
// 根节点:重新加载病区列表
|
||||
loadWardList((wards) => {
|
||||
treeRef.value.updateKeyChildren(node.key, wards);
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取所有选中的叶子节点(患者节点)
|
||||
* @returns {Array} 患者节点数组
|
||||
*/
|
||||
function getCheckedLeafNodes() {
|
||||
if (!treeRef.value) return [];
|
||||
|
||||
// 获取所有选中的节点数据
|
||||
const checkedNodes = treeRef.value.getCheckedNodes() || [];
|
||||
|
||||
// 只返回叶子节点(患者节点)
|
||||
return checkedNodes.filter((node) => node.leaf === true);
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理节点选中状态变化
|
||||
* @param {Object} data - 节点数据
|
||||
* @param {Object} checked - 选中状态对象
|
||||
*/
|
||||
function handleCheckChange(data, checked) {
|
||||
const checkedPatients = getCheckedLeafNodes();
|
||||
updatePatientInfoList(checkedPatients);
|
||||
|
||||
// 调用父组件的方法获取处方列表
|
||||
if (handleGetPrescription && typeof handleGetPrescription === 'function') {
|
||||
handleGetPrescription();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理节点点击事件
|
||||
* @param {Object} data - 节点数据
|
||||
* @param {Object} node - 节点对象
|
||||
*/
|
||||
function handleNodeClick(data, node) {
|
||||
// 节点点击处理逻辑(可根据需要添加)
|
||||
}
|
||||
|
||||
/**
|
||||
* 处理搜索
|
||||
*/
|
||||
function handleSearch() {
|
||||
// 清除缓存(搜索时需要重新加载)
|
||||
patientDataCache.value.clear();
|
||||
|
||||
// 重新加载所有已展开病区的患者列表
|
||||
reloadAllPatients();
|
||||
}
|
||||
|
||||
// 暴露方法供外部调用
|
||||
defineExpose({
|
||||
manualLoadNode,
|
||||
reloadAllPatients,
|
||||
reloadWardPatients,
|
||||
handleSearch,
|
||||
});
|
||||
</script>
|
||||
|
||||
<style scoped lang="scss">
|
||||
.custom-tree-node {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.tree-node-actions {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
:deep(.el-tree-node__content) {
|
||||
height: 35px;
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,12 @@
|
||||
// 选择患者信息
|
||||
export const patientInfo = ref()
|
||||
export function updatePatientInfo(info) {
|
||||
patientInfo.value = info
|
||||
}
|
||||
|
||||
// 多选患者
|
||||
export const patientInfoList = ref([])
|
||||
export function updatePatientInfoList(info) {
|
||||
patientInfoList.value = info
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user