Compare commits

..

96 Commits

Author SHA1 Message Date
b8f3eaca0d Merge remote-tracking branch 'origin/develop' into guanyu 2026-06-20 00:29:46 +08:00
b04856de97 fix(#776): 请修复 Bug #776(诸葛亮分析完成,分配给你) 2026-06-20 00:21:05 +08:00
ceec63ab67 Merge remote-tracking branch 'origin/develop' into guanyu 2026-06-19 23:27:19 +08:00
a54715587f Merge remote-tracking branch 'origin/develop' into guanyu 2026-06-19 20:04:15 +08:00
0cd461e22c fix(#782): 请修复 Bug #782(重试)
根因:
- `HashSet` 没有单独导入,但代码中有 `import java.util.*;`(line 51),所以 `HashSet` 已被通配符导入。让我验证编译:

修复:
- 修改相关代码文件
2026-06-19 19:57:16 +08:00
6bed436c62 Merge remote-tracking branch 'origin/develop' into guanyu 2026-06-19 19:35:54 +08:00
3bcbf20e90 Merge remote-tracking branch 'origin/develop' into guanyu 2026-06-19 18:30:18 +08:00
1f4bad4ac8 Merge remote-tracking branch 'origin/develop' into guanyu 2026-06-19 18:16:14 +08:00
309e6a6cdd Merge remote-tracking branch 'origin/develop' into guanyu 2026-06-19 18:02:19 +08:00
ef6cd66a10 fix(#768): 请修复 Bug #768(诸葛亮分析完成,分配给你) 2026-06-19 18:01:29 +08:00
8254b6ab90 Merge remote-tracking branch 'origin/develop' into guanyu 2026-06-19 17:56:05 +08:00
f1712d59b0 Merge remote-tracking branch 'origin/develop' into guanyu 2026-06-19 17:02:40 +08:00
6baf7dc69f fix(#782): 请修复 Bug #782(诸葛亮分析完成,分配给你) 2026-06-19 16:53:15 +08:00
7fbed9d593 Merge remote-tracking branch 'origin/develop' into guanyu 2026-06-19 16:21:01 +08:00
f0abdd8b52 Merge remote-tracking branch 'origin/develop' into guanyu 2026-06-19 15:49:28 +08:00
21405a2b96 Merge remote-tracking branch 'origin/develop' into guanyu 2026-06-19 14:36:48 +08:00
f92eb58400 fix(#782): 请修复 Bug #782(重试) 2026-06-19 14:31:30 +08:00
52c5a92c9a Merge remote-tracking branch 'origin/develop' into guanyu 2026-06-19 12:06:37 +08:00
5bb610b689 fix(#772): 请修复 Bug #772(诸葛亮分析完成,分配给你) 2026-06-19 12:04:58 +08:00
55c201cc72 Merge remote-tracking branch 'origin/develop' into guanyu 2026-06-19 11:44:59 +08:00
f5860600bc Merge remote-tracking branch 'origin/develop' into guanyu 2026-06-19 08:33:26 +08:00
202cf9f282 Merge remote-tracking branch 'origin/develop' into guanyu 2026-06-19 08:25:54 +08:00
81043f4f20 Merge remote-tracking branch 'origin/develop' into guanyu 2026-06-19 06:15:23 +08:00
48d76adafa fix(#772): 【验证失败反馈】Bug #772 上次修复未通过全链路验证,请根据以下失败原因重新修复:
失败原因:
- 编译验证(mvn compile) : [ERROR] COMPILATION ERROR :

总耗时: 15052ms

请针对上述失败项重新修复,确保:
1. 编译通过(vite build / mvn compile)
2. 单元测试通过(vitest / mvn test)
3. Playwright 回归测试通过
4. 数据库表可访问
5. 后端服务可达
2026-06-19 06:03:59 +08:00
d4d3753666 Merge remote-tracking branch 'origin/develop' into guanyu 2026-06-19 05:46:14 +08:00
f667414094 fix(#782): 请修复 Bug #782(重试)
根因:
- Bug #请修复 Bug #782(重试) 存在的问题

修复:
- 2)的支持:
2026-06-19 05:38:16 +08:00
5fb8297452 Merge remote-tracking branch 'origin/develop' into guanyu 2026-06-19 05:29:09 +08:00
d68b304dcb fix(#768): 请修复 Bug #768(重试) 2026-06-19 05:26:57 +08:00
6ddcfde676 Merge remote-tracking branch 'origin/develop' into guanyu 2026-06-19 05:18:27 +08:00
0b4fd33571 Merge remote-tracking branch 'origin/develop' into guanyu 2026-06-19 05:08:56 +08:00
0850348341 Merge remote-tracking branch 'origin/develop' into guanyu 2026-06-19 04:31:20 +08:00
589629dfbb Merge remote-tracking branch 'origin/develop' into guanyu 2026-06-19 04:21:26 +08:00
67670d48f6 Merge remote-tracking branch 'origin/develop' into guanyu 2026-06-19 03:43:15 +08:00
29ae8e80f0 Merge remote-tracking branch 'origin/develop' into guanyu 2026-06-19 02:10:56 +08:00
cdb58feba6 Merge remote-tracking branch 'origin/develop' into guanyu 2026-06-19 00:22:07 +08:00
6f0302376e Merge remote-tracking branch 'origin/develop' into guanyu 2026-06-19 00:09:54 +08:00
1ee59e5437 fix(#776): 请修复 Bug #776(诸葛亮分析完成,分配给你) 2026-06-19 00:07:24 +08:00
c44d60be7a Merge remote-tracking branch 'origin/develop' into guanyu 2026-06-18 23:52:01 +08:00
850f501505 fix(#768): 请修复 Bug #768(诸葛亮分析完成,分配给你)
根因:
- Bug #请修复 Bug #768(诸葛亮分析完成,分配给你) 存在的问题

修复:
- Fix the old `saveDoctorDiagnosis` method to also set `classification` (it's still in use by the `/save-doctor-diagnosis` endpoint)
2026-06-18 23:48:18 +08:00
dbda09d528 Merge remote-tracking branch 'origin/develop' into guanyu 2026-06-18 23:02:13 +08:00
4a715d6287 Merge remote-tracking branch 'origin/develop' into guanyu 2026-06-18 22:49:30 +08:00
6a3334c920 Merge remote-tracking branch 'origin/develop' into guanyu 2026-06-18 22:30:20 +08:00
319db10ad3 Merge remote-tracking branch 'origin/develop' into guanyu 2026-06-18 22:20:16 +08:00
8c237ccad3 fix(#776): 请修复 Bug #776(诸葛亮分析完成,分配给你)
根因:
- Bug #请修复 Bug #776(诸葛亮分析完成,分配给你) 存在的问题

修复:
- 验证修改内容:
2026-06-18 19:21:17 +08:00
3430eceb84 Merge remote-tracking branch 'origin/develop' into guanyu 2026-06-18 19:12:50 +08:00
4f0f309ca9 Merge remote-tracking branch 'origin/develop' into guanyu 2026-06-18 17:47:35 +08:00
74051a2421 Merge remote-tracking branch 'origin/develop' into guanyu 2026-06-18 17:25:06 +08:00
4fb4e0e3df Merge remote-tracking branch 'origin/develop' into guanyu 2026-06-18 15:52:10 +08:00
3143a974ba Merge remote-tracking branch 'origin/develop' into guanyu 2026-06-18 12:06:17 +08:00
6fffc23e43 fix(#768): 请修复 Bug #768(诸葛亮分析完成,分配给你) 2026-06-18 12:02:53 +08:00
dca1bdac4a Merge remote-tracking branch 'origin/develop' into guanyu 2026-06-18 11:41:42 +08:00
0767f3e6fd Merge remote-tracking branch 'origin/develop' into guanyu 2026-06-18 10:47:43 +08:00
2ea25bd684 Merge remote-tracking branch 'origin/develop' into guanyu 2026-06-18 10:43:47 +08:00
05cc4adf82 Merge remote-tracking branch 'origin/develop' into guanyu 2026-06-18 10:09:52 +08:00
46a5b6509c Merge remote-tracking branch 'origin/develop' into guanyu 2026-06-18 10:05:17 +08:00
c8b0ce3f62 Merge remote-tracking branch 'origin/develop' into guanyu 2026-06-18 09:50:27 +08:00
4951da5ca7 Merge remote-tracking branch 'origin/develop' into guanyu 2026-06-18 09:39:33 +08:00
4613f6dfe4 Merge remote-tracking branch 'origin/develop' into guanyu 2026-06-18 09:18:02 +08:00
f591c5856d Merge remote-tracking branch 'origin/develop' into guanyu 2026-06-18 07:35:54 +08:00
ff9c950cc5 Merge remote-tracking branch 'origin/develop' into guanyu 2026-06-18 03:10:23 +08:00
e2bacf61c0 Merge remote-tracking branch 'origin/develop' into guanyu 2026-06-17 23:19:49 +08:00
95919b5afd Merge remote-tracking branch 'origin/develop' into guanyu 2026-06-17 22:03:52 +08:00
1787ae0ccc Merge remote-tracking branch 'origin/develop' into guanyu 2026-06-17 19:54:36 +08:00
baf459d53b Merge remote-tracking branch 'origin/develop' into guanyu 2026-06-17 19:30:10 +08:00
97f3708f18 Merge remote-tracking branch 'origin/develop' into guanyu 2026-06-17 16:57:28 +08:00
fcf21e66f6 Merge remote-tracking branch 'origin/develop' into guanyu 2026-06-17 16:35:19 +08:00
0f6c6ec3c8 fix(#784): 请修复 Bug #784:【住院护士站-医嘱校对】在已校对的的医嘱中,只有核对通过和退回,没有执行和不执行 2026-06-17 16:28:16 +08:00
b3e938540b Merge remote-tracking branch 'origin/develop' into guanyu 2026-06-17 16:19:33 +08:00
775d37481f Merge remote-tracking branch 'origin/develop' into guanyu 2026-06-17 16:15:18 +08:00
dfdfa53ce9 Merge remote-tracking branch 'origin/develop' into guanyu 2026-06-17 15:46:22 +08:00
702fc7b757 Merge remote-tracking branch 'origin/develop' into guanyu 2026-06-17 15:31:55 +08:00
fb24d3e377 Merge remote-tracking branch 'origin/develop' into guanyu 2026-06-17 14:40:51 +08:00
a32d750591 fix(#784): 请修复 Bug #784:【住院护士站-医嘱校对】在已校对的的医嘱中,只有核对通过和退回,没有执行和不执行 2026-06-17 14:30:49 +08:00
1ca9761171 Merge remote-tracking branch 'origin/develop' into guanyu 2026-06-17 14:25:36 +08:00
cf73dacc77 Merge remote-tracking branch 'origin/develop' into guanyu 2026-06-17 13:59:36 +08:00
310a4f5a9d Merge remote-tracking branch 'origin/develop' into guanyu 2026-06-17 13:52:35 +08:00
cee0a2152a Merge remote-tracking branch 'origin/develop' into guanyu 2026-06-17 13:20:04 +08:00
e19d229a94 Merge remote-tracking branch 'origin/develop' into guanyu 2026-06-17 13:03:51 +08:00
40adecc24e Merge remote-tracking branch 'origin/develop' into guanyu 2026-06-17 12:44:00 +08:00
803e2f7fa7 Merge remote-tracking branch 'origin/develop' into guanyu 2026-06-17 12:25:32 +08:00
51b1d37e80 Merge remote-tracking branch 'origin/develop' into guanyu 2026-06-17 12:15:41 +08:00
acd19fa9b9 Merge remote-tracking branch 'origin/develop' into guanyu 2026-06-17 09:10:18 +08:00
7fb3964be1 Merge remote-tracking branch 'origin/develop' into guanyu 2026-06-17 08:44:31 +08:00
d60f25c7d7 Merge remote-tracking branch 'origin/develop' into guanyu 2026-06-16 16:43:44 +08:00
7cd8a12496 Merge remote-tracking branch 'origin/develop' into guanyu 2026-06-16 16:26:21 +08:00
4ada4ba31a Merge remote-tracking branch 'origin/develop' into guanyu 2026-06-16 16:16:13 +08:00
4d024529f4 Merge remote-tracking branch 'origin/develop' into guanyu 2026-06-16 16:06:09 +08:00
418135867e Merge remote-tracking branch 'origin/develop' into guanyu 2026-06-16 15:57:06 +08:00
69dd77e916 Merge remote-tracking branch 'origin/develop' into guanyu 2026-06-16 14:13:18 +08:00
2fcfc34afe Merge remote-tracking branch 'origin/develop' into guanyu 2026-06-16 13:45:43 +08:00
bc43085cef fix(#663): 请修复 Bug #663(诸葛亮分析完成,分配给你) 2026-06-16 13:37:01 +08:00
f818ca8174 Merge remote-tracking branch 'origin/develop' into guanyu 2026-06-16 13:34:39 +08:00
651bc758b7 Merge remote-tracking branch 'origin/develop' into guanyu 2026-06-16 13:30:52 +08:00
8808ba1663 Merge remote-tracking branch 'origin/develop' into guanyu 2026-06-16 13:18:22 +08:00
a7378ceef7 Merge remote-tracking branch 'origin/develop' into guanyu 2026-06-16 12:27:30 +08:00
bdd8c9c4d8 fix(#776): 请修复 Bug #776(诸葛亮分析完成,分配给你)
根因:
- Bug #请修复 Bug #776(诸葛亮分析完成,分配给你) 存在的问题

修复:
- 读取 AGENTS.md 了解项目规范,然后搜索相关代码。
- Review ---
- Test ---
- Backend compile passed. Now let me run unit tests for the modified module.
- Verify ---
- 轻量级验证: fix_commit=true changes=1
2026-06-16 12:01:13 +08:00
20 changed files with 110 additions and 690 deletions

View File

@@ -17,7 +17,7 @@ service.interceptors.request.use(config => {
service.interceptors.response.use(
response => {
const res = response.data
if (res.code === 401 && !window.location.pathname.includes('/login')) {
if (res.code === 401) {
localStorage.removeItem('Admin-Token')
localStorage.removeItem('userInfo')
window.location.href = '/login'
@@ -26,7 +26,7 @@ service.interceptors.response.use(
return res
},
error => {
if (error.response?.status === 401 && !window.location.pathname.includes('/login')) {
if (error.response?.status === 401) {
localStorage.removeItem('Admin-Token')
localStorage.removeItem('userInfo')
window.location.href = '/login'
@@ -37,29 +37,21 @@ service.interceptors.response.use(
export const authApi = {
login: (data) => service.post('/login', data, { headers: { isToken: false } }),
getTenants: () => service.get('/system/tenant/all-active', { headers: { isToken: false } }),
getUserTenants: (username) => service.get('/system/tenant/user-bind/' + username, { headers: { isToken: false } }),
getTenants: (username) => service.get('/system/tenant/user-bind/' + username, { headers: { isToken: false } }),
getAllTenants: () => service.get('/system/tenant/page', { headers: { isToken: false }, params: { pageSize: 100 } }),
getInfo: () => service.get('/getInfo')
}
export const nursingApi = {
getTasks: (params) => service.get('/nurse-station/advice-process/inpatient-advice', { params }),
completeTask: (id, data) => service.post(`/nurse-station/advice-process/advice-execute`, data),
getTasks: (params) => service.get('/nurse-station/advice-process/page', { params }),
completeTask: (id, data) => service.post(`/nurse-station/advice-process/execute`, data),
getPatientInfo: (id) => service.get('/inpatientmanage/inhospitalregister/' + id),
getPatientList: (params) => service.get('/patient-home-manage/init', { params }),
getOrders: (encounterId) => service.get('/nurse-station/advice-process/inpatient-advice', { params: { encounterId } }),
getVitalSigns: (patientId) => service.get('/vital-signs-chart/page', { params: { patientId } }),
submitVitalSign: (data) => service.post('/nursing/mobile/vital-sign', data),
getAssessments: (encounterId) => service.get('/api/v1/nursing/assessment/encounter/' + encounterId),
submitAssessment: (data) => service.post('/api/v1/nursing/assessment', data),
getDrugDistribution: (params) => service.get('/nursing/mobile/drug-distribution/list', { params }),
submitDrugDistribution: (data) => service.post('/nursing/mobile/drug-distribution/execute', data),
getNursingRecords: (params) => service.get('/nursing-record/patient-page', { params }),
submitNursingRecord: (data) => service.post('/nursing-record/save-nursing', data),
getInfusionPatrol: (params) => service.get('/nursing-execution/infusion/page', { params }),
submitInfusionPatrol: (data) => service.post('/nursing/mobile/infusion/action', data),
getHandoffRecords: (params) => service.get('/nursing-execution/handoff/page', { params }),
submitHandoffRecord: (data) => service.post('/nursing-execution/handoff/add', data)
getPatientList: (params) => service.get('/inpatientmanage/inhospitalregister/list', { params }),
getOrders: (encounterId) => service.get('/nurse-station/advice-process/page', { params: { encounterId } }),
getVitalSigns: (patientId) => service.get('/nursing/vital-signs/' + patientId),
submitVitalSign: (data) => service.post('/nursing/vital-sign', data),
getAssessments: (encounterId) => service.get('/nursing/assessment/encounter/' + encounterId),
submitAssessment: (data) => service.post('/nursing/assessment', data)
}
export default service

View File

@@ -10,10 +10,6 @@ const routes = [
{ path: 'patient-detail/:id', component: () => import('../views/PatientDetail.vue'), meta: { title: '患者详情' } },
{ path: 'vital-entry/:patientId', component: () => import('../views/VitalSignEntry.vue'), meta: { title: '生命体征录入' } },
{ path: 'assessment/:patientId', component: () => import('../views/AssessmentForm.vue'), meta: { title: '护理评估' } },
{ path: 'drug-distribution', component: () => import('../views/DrugDistribution.vue'), meta: { title: '药品发放' } },
{ path: 'nursing-record', component: () => import('../views/NursingRecord.vue'), meta: { title: '护理记录' } },
{ path: 'infusion-patrol', component: () => import('../views/InfusionPatrol.vue'), meta: { title: '输液巡视' } },
{ path: 'handoff-record', component: () => import('../views/HandoffRecord.vue'), meta: { title: '交接班记录' } },
{ path: 'mine', component: () => import('../views/Mine.vue'), meta: { title: '我的' } }
]}
]

View File

@@ -1,74 +0,0 @@
<template>
<div class="drug-dist">
<div class="search-bar"><input v-model="searchText" placeholder="搜索药品名称/患者..." class="search-input" /></div>
<div v-if="loading" class="loading">加载中...</div>
<div v-for="item in filteredList" :key="item.id" class="drug-card">
<div class="drug-header"><span class="drug-name">{{ item.drugName }}</span><span class="status-tag" :class="'s-' + item.status">{{ item.statusText }}</span></div>
<div class="drug-info"><div>患者: {{ item.patientName }} {{ item.bedNo }}</div><div>剂量: {{ item.dosage }}</div><div>用法: {{ item.usage }}</div></div>
<div class="drug-actions">
<button v-if="item.status === 'PENDING'" class="action-btn primary" @click="handleDistribute(item)">发放</button>
<button v-if="item.status === 'PENDING'" class="action-btn" @click="handleReject(item)">拒发</button>
<span v-if="item.status === 'DISTRIBUTED'" class="done-text">已发放</span>
</div>
</div>
<div v-if="!loading && filteredList.length === 0" class="empty">暂无待发放药品</div>
</div>
</template>
<script setup>
import { ref, computed, onMounted } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import { nursingApi } from '../api'
const searchText = ref('')
const list = ref([])
const loading = ref(false)
const filteredList = computed(() => searchText.value ? list.value.filter(d => d.drugName?.includes(searchText.value) || d.patientName?.includes(searchText.value)) : list.value)
const loadList = async () => {
loading.value = true
try {
const res = await nursingApi.getDrugDistribution({ pageSize: 100 })
list.value = (res.data?.records || res.data?.rows || res.data || []).map(d => ({ ...d, statusText: d.status === 'DISTRIBUTED' ? '已发放' : d.status === 'REJECTED' ? '已拒发' : '待发放' }))
} catch { ElMessage.error('加载失败') } finally { loading.value = false }
}
const handleDistribute = async (item) => {
try {
await ElMessageBox.confirm('确认发放该药品?', '确认')
await nursingApi.submitDrugDistribution({ id: item.id, action: 'DISTRIBUTE' })
item.status = 'DISTRIBUTED'; item.statusText = '已发放'
ElMessage.success('发放成功')
} catch (e) { if (e !== 'cancel') ElMessage.error('操作失败') }
}
const handleReject = async (item) => {
try {
await ElMessageBox.confirm('确认拒发该药品?', '确认')
await nursingApi.submitDrugDistribution({ id: item.id, action: 'REJECT' })
item.status = 'REJECTED'; item.statusText = '已拒发'
ElMessage.success('已拒发')
} catch (e) { if (e !== 'cancel') ElMessage.error('操作失败') }
}
onMounted(loadList)
</script>
<style scoped>
.search-bar { padding: 8px 0; }
.search-input { width: 100%; padding: 10px 16px; border: 1px solid #ddd; border-radius: 20px; font-size: 15px; outline: none; background: #fff; }
.drug-card { background: #fff; border-radius: 8px; padding: 12px; margin-bottom: 8px; box-shadow: 0 1px 3px rgba(0,0,0,0.08); }
.drug-header { display: flex; justify-content: space-between; align-items: center; margin-bottom: 8px; }
.drug-name { font-weight: 600; font-size: 15px; }
.status-tag { font-size: 11px; padding: 2px 8px; border-radius: 4px; }
.s-PENDING { background: #fff7e6; color: #fa8c16; }
.s-DISTRIBUTED { background: #f6ffed; color: #52c41a; }
.s-REJECTED { background: #fff1f0; color: #f5222d; }
.drug-info { font-size: 13px; color: #666; line-height: 1.8; }
.drug-actions { display: flex; gap: 8px; margin-top: 10px; }
.action-btn { flex: 1; padding: 8px; border: 1px solid #ddd; border-radius: 6px; background: #fff; font-size: 13px; }
.action-btn.primary { background: #1890ff; color: #fff; border-color: #1890ff; }
.done-text { color: #52c41a; font-size: 13px; line-height: 36px; }
.loading { text-align: center; padding: 20px; color: #999; }
.empty { text-align: center; padding: 40px; color: #999; }
</style>

View File

@@ -1,82 +0,0 @@
<template>
<div class="handoff-record">
<div class="shift-tabs">
<div v-for="s in shifts" :key="s.key" class="tab" :class="{ active: form.shift === s.key }" @click="form.shift = s.key">{{ s.label }}</div>
</div>
<div class="form-section">
<div class="form-item"><div class="label">交接班护士</div><input v-model="form.handoffNurse" placeholder="交班护士" class="input" /></div>
<div class="form-item"><div class="label">接班护士</div><input v-model="form.onDutyNurse" placeholder="接班护士" class="input" /></div>
<div class="form-item"><div class="label">科室</div><input v-model="form.department" placeholder="科室名称" class="input" /></div>
<div class="form-item"><div class="label">在院患者数</div><input v-model="form.patientCount" type="number" placeholder="0" class="input" /></div>
<div class="form-item"><div class="label">病情变化</div><textarea v-model="form.patientChanges" placeholder="交接患者病情变化..." class="textarea" rows="3"></textarea></div>
<div class="form-item"><div class="label">特殊治疗</div><textarea v-model="form.specialTreatment" placeholder="特殊治疗及注意事项..." class="textarea" rows="3"></textarea></div>
<div class="form-item"><div class="label">待办事项</div><textarea v-model="form.pendingItems" placeholder="未完成事项及待跟进..." class="textarea" rows="3"></textarea></div>
<div class="form-item"><div class="label">物品交接</div><textarea v-model="form.materialHandoff" placeholder="交接的物品..." class="textarea" rows="2"></textarea></div>
</div>
<button class="submit-btn" @click="submit" :disabled="submitting">{{ submitting ? '提交中...' : '保存交接记录' }}</button>
<div class="history-section">
<div class="section-title">历史交接记录</div>
<div v-for="h in history" :key="h.id" class="history-card">
<div class="h-header"><span class="h-shift">{{ h.shift }}</span><span class="h-time">{{ h.createTime }}</span></div>
<div class="h-nurses">{{ h.handoffNurse }} {{ h.onDutyNurse }}</div>
<div class="h-changes">{{ h.patientChanges }}</div>
</div>
<div v-if="history.length === 0" class="empty">暂无历史记录</div>
</div>
</div>
</template>
<script setup>
import { ref, onMounted } from 'vue'
import { ElMessage } from 'element-plus'
import { nursingApi } from '../api'
const shifts = [{ key: 'DAY', label: '白班' }, { key: 'NIGHT', label: '夜班' }]
const form = ref({ shift: 'DAY', handoffNurse: '', onDutyNurse: '', department: '', patientCount: 0, patientChanges: '', specialTreatment: '', pendingItems: '', materialHandoff: '' })
const history = ref([])
const submitting = ref(false)
const loadHistory = async () => {
try {
const res = await nursingApi.getHandoffRecords({ pageSize: 20 })
history.value = res.data?.records || res.data?.rows || res.data || []
} catch {}
}
const submit = async () => {
if (!form.value.handoffNurse || !form.value.onDutyNurse) { ElMessage.warning('请填写交接班护士'); return }
submitting.value = true
try {
await nursingApi.submitHandoffRecord({ ...form.value })
ElMessage.success('交接记录已保存')
form.value = { shift: form.value.shift, handoffNurse: '', onDutyNurse: '', department: form.value.department, patientCount: 0, patientChanges: '', specialTreatment: '', pendingItems: '', materialHandoff: '' }
loadHistory()
} catch { ElMessage.error('保存失败') } finally { submitting.value = false }
}
onMounted(loadHistory)
</script>
<style scoped>
.shift-tabs { display: flex; background: #fff; border-radius: 8px; overflow: hidden; margin-bottom: 12px; }
.tab { flex: 1; text-align: center; padding: 12px; font-size: 14px; color: #666; background: #f5f5f5; }
.tab.active { background: #1890ff; color: #fff; }
.form-section { background: #fff; border-radius: 8px; padding: 14px; margin-bottom: 12px; }
.form-item { margin-bottom: 12px; }
.label { font-size: 13px; color: #666; margin-bottom: 6px; }
.input { width: 100%; padding: 10px; border: 1px solid #ddd; border-radius: 6px; font-size: 14px; }
.textarea { width: 100%; padding: 10px; border: 1px solid #ddd; border-radius: 6px; font-size: 14px; resize: none; font-family: inherit; }
.input:focus, .textarea:focus { border-color: #1890ff; outline: none; }
.submit-btn { width: 100%; padding: 14px; background: #1890ff; color: #fff; border: none; border-radius: 8px; font-size: 16px; margin-bottom: 16px; font-weight: 600; }
.submit-btn:disabled { background: #91d5ff; }
.history-section { background: #fff; border-radius: 8px; padding: 14px; }
.section-title { font-size: 15px; font-weight: 600; margin-bottom: 12px; }
.history-card { border-bottom: 1px solid #f0f0f0; padding: 10px 0; }
.h-header { display: flex; justify-content: space-between; margin-bottom: 4px; }
.h-shift { font-weight: 600; font-size: 14px; color: #1890ff; }
.h-time { font-size: 12px; color: #999; }
.h-nurses { font-size: 13px; color: #666; margin-bottom: 4px; }
.h-changes { font-size: 13px; color: #333; }
.empty { text-align: center; padding: 20px; color: #999; }
</style>

View File

@@ -42,10 +42,7 @@ const recentTasks = ref([])
const actions = [
{ icon: '📋', label: '任务列表', path: '/mobile/tasks', color: '#1890ff' },
{ icon: '👥', label: '患者列表', path: '/mobile/patients', color: '#52c41a' },
{ icon: '💊', label: '药品发放', path: '/mobile/drug-distribution', color: '#fa8c16' },
{ icon: '📝', label: '护理记录', path: '/mobile/nursing-record', color: '#722ed1' },
{ icon: '💉', label: '输液巡视', path: '/mobile/infusion-patrol', color: '#13c2c2' },
{ icon: '🔄', label: '交接班', path: '/mobile/handoff-record', color: '#f5222d' }
{ icon: '📊', label: '生命体征', path: '/mobile/vital-entry', color: '#722ed1' }
]
onMounted(async () => {

View File

@@ -1,91 +0,0 @@
<template>
<div class="infusion-patrol">
<div class="filter-bar">
<div v-for="f in filters" :key="f.key" class="filter-btn" :class="{ active: activeFilter === f.key }" @click="activeFilter = f.key">{{ f.label }}</div>
</div>
<div v-if="loading" class="loading">加载中...</div>
<div v-for="item in filteredList" :key="item.id" class="infusion-card">
<div class="inf-header">
<div class="inf-patient">{{ item.patientName }} {{ item.bedNo }}</div>
<div class="inf-status" :class="'s-' + item.status">{{ item.status === 'INFUSING' ? '输液中' : item.status === 'COMPLETED' ? '已完成' : '暂停中' }}</div>
</div>
<div class="drug-list">
<div v-for="drug in item.drugs" :key="drug.id" class="drug-row">
<span class="drug-name">{{ drug.name }}</span>
<span class="drug-spec">{{ drug.spec }}</span>
<span class="drug-flow">{{ drug.flowRate }}</span>
</div>
</div>
<div class="patrol-info" v-if="item.lastPatrolTime"><span class="label">上次巡视:</span> {{ item.lastPatrolTime }}</div>
<div class="inf-actions">
<button v-if="item.status === 'INFUSING'" class="patrol-btn" @click="handlePatrol(item)">巡视</button>
<button v-if="item.status === 'INFUSING'" class="stop-btn" @click="handleComplete(item)">结束输液</button>
</div>
</div>
<div v-if="!loading && filteredList.length === 0" class="empty">暂无输液记录</div>
</div>
</template>
<script setup>
import { ref, computed, onMounted } from 'vue'
import { ElMessage, ElMessageBox } from 'element-plus'
import { nursingApi } from '../api'
const activeFilter = ref('INFUSING')
const filters = [{ key: 'INFUSING', label: '输液中' }, { key: 'COMPLETED', label: '已完成' }, { key: 'ALL', label: '全部' }]
const list = ref([])
const loading = ref(false)
const filteredList = computed(() => activeFilter.value === 'ALL' ? list.value : list.value.filter(i => i.status === activeFilter.value))
const loadList = async () => {
loading.value = true
try {
const res = await nursingApi.getInfusionPatrol({ pageSize: 100 })
list.value = res.data?.records || res.data?.rows || res.data || []
} catch { ElMessage.error('加载失败') } finally { loading.value = false }
}
const handlePatrol = async (item) => {
try {
await nursingApi.submitInfusionPatrol({ infusionId: item.id, action: 'PATROL' })
item.lastPatrolTime = new Date().toLocaleString()
ElMessage.success('巡视完成')
} catch { ElMessage.error('巡视失败') }
}
const handleComplete = async (item) => {
try {
await ElMessageBox.confirm('确认结束输液?', '确认')
await nursingApi.submitInfusionPatrol({ infusionId: item.id, action: 'COMPLETE' })
item.status = 'COMPLETED'
ElMessage.success('输液已结束')
} catch (e) { if (e !== 'cancel') ElMessage.error('操作失败') }
}
onMounted(loadList)
</script>
<style scoped>
.filter-bar { display: flex; gap: 8px; padding: 8px 0; }
.filter-btn { padding: 6px 16px; border-radius: 20px; background: #f0f0f0; font-size: 13px; cursor: pointer; }
.filter-btn.active { background: #1890ff; color: #fff; }
.infusion-card { background: #fff; border-radius: 8px; padding: 12px; margin-bottom: 8px; box-shadow: 0 1px 3px rgba(0,0,0,0.08); }
.inf-header { display: flex; justify-content: space-between; margin-bottom: 8px; }
.inf-patient { font-weight: 600; font-size: 15px; }
.inf-status { font-size: 12px; padding: 2px 8px; border-radius: 4px; }
.s-INFUSING { background: #e6f7ff; color: #1890ff; }
.s-COMPLETED { background: #f6ffed; color: #52c41a; }
.s-PAUSED { background: #fff7e6; color: #fa8c16; }
.drug-list { background: #fafafa; border-radius: 6px; padding: 8px; margin-bottom: 8px; }
.drug-row { display: flex; gap: 10px; font-size: 13px; padding: 4px 0; }
.drug-name { flex: 1; font-weight: 500; }
.drug-spec { color: #999; }
.drug-flow { color: #1890ff; }
.patrol-info { font-size: 12px; color: #999; margin-bottom: 8px; }
.label { color: #666; }
.inf-actions { display: flex; gap: 8px; }
.patrol-btn { flex: 1; padding: 8px; background: #52c41a; color: #fff; border: none; border-radius: 6px; font-size: 13px; }
.stop-btn { flex: 1; padding: 8px; background: #fff; color: #666; border: 1px solid #ddd; border-radius: 6px; font-size: 13px; }
.loading { text-align: center; padding: 20px; color: #999; }
.empty { text-align: center; padding: 40px; color: #999; }
</style>

View File

@@ -6,21 +6,20 @@
<p>护士工作站</p>
</div>
<div class="login-form">
<div class="form-item">
<label>用户名</label>
<input v-model="form.username" type="text" placeholder="请输入用户名" class="input" @blur="loadTenants" />
</div>
<div class="form-item">
<label>密码</label>
<input v-model="form.password" type="password" placeholder="请输入密码" class="input" @keyup.enter="handleLogin" />
</div>
<div class="form-item">
<label>医院/租户</label>
<select v-model="form.tenantId" class="input" @change="onTenantChange">
<option value="">请选择医院</option>
<option v-for="t in tenantOptions" :key="t.value" :value="t.value">{{ t.label }}</option>
</select>
<div v-if="tenantOptions.length === 0 && form.username" class="loading-text">加载医院列表中...</div>
</div>
<div class="form-item">
<label>用户名</label>
<input v-model="form.username" type="text" placeholder="请输入用户名" class="input" />
</div>
<div class="form-item">
<label>密码</label>
<input v-model="form.password" type="password" placeholder="请输入密码" class="input" @keyup.enter="handleLogin" />
</div>
<button class="login-btn" @click="handleLogin" :disabled="loading">{{ loading ? '登录中...' : '登 录' }}</button>
<div v-if="errorMsg" class="error-msg">{{ errorMsg }}</div>
@@ -42,20 +41,14 @@ const currentTenantName = ref('')
const form = ref({ username: '', password: '', tenantId: '' })
const loadTenants = async () => {
if (!form.value.username) return
try {
const res = await authApi.getUserTenants(form.value.username)
if (res.code === 200 && res.data) {
tenantOptions.value = res.data.map(item => ({ label: item.tenantName, value: item.id }))
if (tenantOptions.value.length === 1) {
form.value.tenantId = tenantOptions.value[0].value
currentTenantName.value = tenantOptions.value[0].label
}
const res = await authApi.getAllTenants()
if (res.code === 200) {
const list = res.data?.records || res.data || []
tenantOptions.value = list.map(item => ({ label: item.tenantName, value: item.tenantId || item.id }))
if (tenantOptions.value.length === 1) { form.value.tenantId = tenantOptions.value[0].value; currentTenantName.value = tenantOptions.value[0].label }
}
} catch (e) {
console.error('加载租户失败:', e)
errorMsg.value = '无法连接服务器,请检查网络'
}
} catch (e) { console.error(e) }
}
const onTenantChange = () => {
@@ -63,9 +56,7 @@ const onTenantChange = () => {
currentTenantName.value = selected ? selected.label : ''
}
onMounted(() => {
if (form.value.username) loadTenants()
})
onMounted(loadTenants)
const handleLogin = async () => {
if (!form.value.username) { errorMsg.value = '请输入用户名'; return }
@@ -79,9 +70,14 @@ const handleLogin = async () => {
if (infoRes.code === 200) {
const user = infoRes.user || {}
localStorage.setItem('userInfo', JSON.stringify({
userId: user.userId, userName: user.userName, nickName: user.nickName,
practitionerId: user.practitionerId, orgId: user.orgId, orgName: user.orgName,
roles: user.roles, permissions: user.permissions
userId: user.userId,
userName: user.userName,
nickName: user.nickName,
practitionerId: user.practitionerId,
orgId: user.orgId,
orgName: user.orgName,
roles: user.roles,
permissions: user.permissions
}))
}
ElMessage.success('登录成功')
@@ -91,7 +87,9 @@ const handleLogin = async () => {
}
} catch (e) {
errorMsg.value = e.response?.data?.msg || '登录失败,请检查网络'
} finally { loading.value = false }
} finally {
loading.value = false
}
}
</script>
@@ -110,5 +108,4 @@ select.input { appearance: none; background: #fff url("data:image/svg+xml,%3Csvg
.login-btn { width: 100%; padding: 14px; background: #1890ff; color: #fff; border: none; border-radius: 8px; font-size: 18px; font-weight: 600; cursor: pointer; }
.login-btn:disabled { background: #91d5ff; }
.error-msg { color: #f5222d; text-align: center; margin-top: 12px; font-size: 14px; }
.loading-text { color: #999; font-size: 12px; margin-top: 4px; }
</style>

View File

@@ -1,81 +0,0 @@
<template>
<div class="nursing-record">
<div class="type-tabs">
<div v-for="t in recordTypes" :key="t.key" class="tab" :class="{ active: form.recordType === t.key }" @click="form.recordType = t.key">{{ t.label }}</div>
</div>
<div class="form-section">
<div class="form-item"><div class="label">患者</div><input v-model="form.patientName" placeholder="选择患者" class="input" readonly @click="showPatientPicker = true" /></div>
<div class="form-item"><div class="label">记录内容</div><textarea v-model="form.content" placeholder="请输入护理记录内容..." class="textarea" rows="4"></textarea></div>
<div class="form-item"><div class="label">护理评估</div><textarea v-model="form.assessment" placeholder="评估情况..." class="textarea" rows="3"></textarea></div>
<div class="form-item"><div class="label">护理措施</div><textarea v-model="form.measures" placeholder="采取的护理措施..." class="textarea" rows="3"></textarea></div>
<div class="form-item"><div class="label">签名</div><input v-model="form.signer" placeholder="护士签名" class="input" /></div>
</div>
<button class="submit-btn" @click="submit" :disabled="submitting">{{ submitting ? '提交中...' : '保存记录' }}</button>
<div v-if="showPatientPicker" class="picker-mask" @click.self="showPatientPicker = false">
<div class="picker-panel">
<div class="picker-header">选择患者</div>
<div v-for="p in patients" :key="p.id" class="picker-item" @click="selectPatient(p)">{{ p.name }} {{ p.bedNo }}</div>
</div>
</div>
</div>
</template>
<script setup>
import { ref, onMounted } from 'vue'
import { ElMessage } from 'element-plus'
import { nursingApi } from '../api'
const recordTypes = [
{ key: 'DAILY', label: '日常记录' },
{ key: 'SPECIAL', label: '特殊记录' },
{ key: 'TRANSFER', label: '转科记录' }
]
const form = ref({ recordType: 'DAILY', patientId: '', patientName: '', content: '', assessment: '', measures: '', signer: '' })
const patients = ref([])
const showPatientPicker = ref(false)
const submitting = ref(false)
const loadPatients = async () => {
try {
const res = await nursingApi.getPatientList({ pageSize: 100 })
patients.value = res.data?.records || res.data?.rows || res.data || []
} catch {}
}
const selectPatient = (p) => {
form.value.patientId = p.id; form.value.patientName = p.name
showPatientPicker.value = false
}
const submit = async () => {
if (!form.value.patientId || !form.value.content) { ElMessage.warning('请选择患者并填写记录内容'); return }
submitting.value = true
try {
await nursingApi.submitNursingRecord({ ...form.value })
ElMessage.success('记录保存成功')
form.value = { recordType: form.value.recordType, patientId: '', patientName: '', content: '', assessment: '', measures: '', signer: '' }
} catch { ElMessage.error('保存失败') } finally { submitting.value = false }
}
onMounted(loadPatients)
</script>
<style scoped>
.type-tabs { display: flex; background: #fff; border-radius: 8px; overflow: hidden; margin-bottom: 12px; }
.tab { flex: 1; text-align: center; padding: 12px; font-size: 14px; color: #666; background: #f5f5f5; }
.tab.active { background: #1890ff; color: #fff; }
.form-section { background: #fff; border-radius: 8px; padding: 14px; }
.form-item { margin-bottom: 14px; }
.label { font-size: 13px; color: #666; margin-bottom: 6px; }
.input { width: 100%; padding: 10px; border: 1px solid #ddd; border-radius: 6px; font-size: 14px; }
.textarea { width: 100%; padding: 10px; border: 1px solid #ddd; border-radius: 6px; font-size: 14px; resize: none; font-family: inherit; }
.input:focus, .textarea:focus { border-color: #1890ff; outline: none; }
.submit-btn { width: 100%; padding: 14px; background: #1890ff; color: #fff; border: none; border-radius: 8px; font-size: 16px; margin-top: 12px; font-weight: 600; }
.submit-btn:disabled { background: #91d5ff; }
.picker-mask { position: fixed; top: 0; left: 0; right: 0; bottom: 0; background: rgba(0,0,0,0.4); z-index: 100; display: flex; align-items: flex-end; }
.picker-panel { background: #fff; width: 100%; max-height: 60vh; border-radius: 12px 12px 0 0; padding: 16px; overflow-y: auto; }
.picker-header { font-size: 16px; font-weight: 600; margin-bottom: 12px; text-align: center; }
.picker-item { padding: 12px; border-bottom: 1px solid #f0f0f0; font-size: 15px; cursor: pointer; }
.picker-item:active { background: #f5f5f5; }
</style>

View File

@@ -1,79 +1,49 @@
<template>
<div class="patient-list">
<div class="search-bar">
<input v-model="searchText" placeholder="搜索患者姓名/床号..." class="search-input" @input="onSearch" />
</div>
<div v-if="loading" class="loading">
<div class="loading-spinner"></div>
<span>加载中...</span>
</div>
<div v-for="p in patients" :key="p.patientId || p.id" class="patient-card" @click="goDetail(p)">
<div class="patient-avatar" :class="'level-' + (p.nursingLevel || 3)">{{ (p.patientName || p.name || '?').charAt(0) }}</div>
<div class="search-bar"><input v-model="searchText" placeholder="搜索患者姓名/床号..." class="search-input" /></div>
<div v-if="loading" class="loading">加载中...</div>
<div v-for="p in displayPatients" :key="p.id" class="patient-card" @click="$router.push(`/mobile/patient-detail/${p.id}`)">
<div class="patient-avatar" :class="'level-' + p.nursingLevel">{{ p.name?.charAt(0) }}</div>
<div class="patient-info">
<div class="patient-name">{{ p.patientName || p.name || '未知患者' }} <span class="bed">{{ p.bedNo || p.locationName || '' }}</span></div>
<div class="patient-diag">{{ p.primaryDiagnosisName || p.diagnosis || '暂无诊断' }}</div>
<div class="patient-tags">
<span class="tag" :class="'level-' + (p.nursingLevel || 3)">{{ (p.nursingLevel || 3) }}级护理</span>
<span v-if="p.gender" class="tag">{{ p.gender }}</span>
<span v-if="p.age" class="tag">{{ p.age }}</span>
</div>
<div class="patient-name">{{ p.name }} <span class="bed">{{ p.bedNo }}</span></div>
<div class="patient-diag">{{ p.diagnosis || '暂无诊断' }}</div>
<div class="patient-tags"><span class="tag" :class="'level-' + p.nursingLevel">{{ p.nursingLevel }}级护理</span><span v-if="p.gender" class="tag">{{ p.gender }}</span></div>
</div>
</div>
<div v-if="!loading && patients.length === 0" class="empty">暂无患者</div>
<div v-if="!loading && hasMore" class="load-more" @click="loadMore">加载更多</div>
<div v-if="!loading && displayPatients.length === 0" class="empty">暂无患者</div>
</div>
</template>
<script setup>
import { ref, onMounted } from 'vue'
import { useRouter } from 'vue-router'
import { ref, onMounted, computed } from 'vue'
import { ElMessage } from 'element-plus'
import { nursingApi } from '../api'
const router = useRouter()
const patients = ref([])
const loading = ref(false)
const searchText = ref('')
const pageNo = ref(1)
const pageSize = 20
const hasMore = ref(true)
const displayPatients = computed(() => searchText.value ? patients.value.filter(p => p.name?.includes(searchText.value) || p.bedNo?.includes(searchText.value)) : patients.value)
const loadPatients = async (reset = false) => {
if (reset) { pageNo.value = 1; patients.value = []; hasMore.value = true }
const loadPatients = async () => {
loading.value = true
try {
const params = { pageNo: pageNo.value, pageSize: pageSize }
if (searchText.value) params.searchKey = searchText.value
const res = await nursingApi.getPatientList(params)
const list = res.data?.list || res.data?.records || res.data?.rows || res.data || []
if (reset) { patients.value = list } else { patients.value.push(...list) }
hasMore.value = list.length >= pageSize
} catch (e) { console.error('加载失败:', e) } finally { loading.value = false }
try { const res = await nursingApi.getPatientList({}); patients.value = res.data || [] } catch (e) { ElMessage.error('加载失败') } finally { loading.value = false }
}
const onSearch = () => { loadPatients(true) }
const loadMore = () => { pageNo.value++; loadPatients(false) }
const goDetail = (p) => { router.push(`/mobile/patient-detail/${p.patientId || p.id}`) }
onMounted(() => loadPatients(true))
onMounted(loadPatients)
</script>
<style scoped>
.search-bar { padding: 8px 0; }
.search-input { width: 100%; padding: 10px 16px; border: 1px solid #ddd; border-radius: 20px; font-size: 15px; outline: none; background: #fff; }
.search-input:focus { border-color: #1890ff; }
.loading { text-align: center; padding: 20px; color: #999; display: flex; align-items: center; justify-content: center; gap: 8px; }
.loading-spinner { width: 20px; height: 20px; border: 2px solid #1890ff; border-top-color: transparent; border-radius: 50%; animation: spin 0.8s linear infinite; }
@keyframes spin { to { transform: rotate(360deg); } }
.loading { text-align: center; padding: 20px; color: #999; }
.patient-card { background: #fff; border-radius: 8px; padding: 12px; margin-bottom: 8px; display: flex; align-items: center; gap: 12px; box-shadow: 0 1px 3px rgba(0,0,0,0.08); }
.patient-avatar { width: 44px; height: 44px; border-radius: 50%; display: flex; align-items: center; justify-content: center; font-size: 18px; font-weight: 600; color: #fff; flex-shrink: 0; }
.patient-avatar { width: 44px; height: 44px; border-radius: 50%; display: flex; align-items: center; justify-content: center; font-size: 18px; font-weight: 600; color: #fff; }
.level-1 { background: #f5222d; } .level-2 { background: #fa8c16; } .level-3 { background: #52c41a; }
.patient-info { flex: 1; min-width: 0; }
.patient-name { font-weight: 600; font-size: 15px; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.bed { color: #999; font-size: 13px; margin-left: 4px; }
.patient-diag { color: #666; font-size: 13px; margin: 2px 0; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.patient-tags { display: flex; gap: 6px; flex-wrap: wrap; }
.tag { font-size: 11px; padding: 2px 6px; border-radius: 4px; background: #f5f5f5; }
.load-more { text-align: center; padding: 12px; color: #1890ff; font-size: 14px; cursor: pointer; }
.patient-name { font-weight: 600; font-size: 15px; }
.bed { color: #999; font-size: 13px; }
.patient-diag { color: #666; font-size: 13px; margin: 2px 0; }
.patient-tags { display: flex; gap: 6px; }
.tag { font-size: 11px; padding: 2px 6px; border-radius: 4px; }
.empty { text-align: center; padding: 40px; color: #999; }
</style>

View File

@@ -1,6 +1,5 @@
package com.core.web.controller.system;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.core.common.annotation.Anonymous;
import com.core.common.core.controller.BaseController;
@@ -195,19 +194,4 @@ public class SysTenantController extends BaseController {
public R<List<SysTenant>> getUserBindTenantList(@PathVariable String username) {
return sysTenantService.getUserBindTenantList(username);
}
/**
* 查询所有可用租户列表(登录页使用,无需认证)
*
* @return 所有启用的租户列表
*/
@Anonymous
@GetMapping("/all-active")
public R<List<SysTenant>> getAllActiveTenants() {
LambdaQueryWrapper<SysTenant> wrapper = new LambdaQueryWrapper<>();
wrapper.eq(SysTenant::getStatus, "0");
wrapper.eq(SysTenant::getDeleteFlag, "0");
wrapper.orderByAsc(SysTenant::getId);
return R.ok(sysTenantService.list(wrapper));
}
}

View File

@@ -106,27 +106,9 @@ public class SecurityConfig {
.permitAll()
.requestMatchers("/patientmanage/information/**")
.permitAll()
// 登录页展示用的系统版本信息,允许匿名访问
.requestMatchers("/system/version")
.permitAll()
// 登录页租户列表,允许匿名访问
.requestMatchers("/system/tenant/all-active")
.permitAll()
// 移动端API允许匿名访问
.requestMatchers("/patient-home-manage/**")
.permitAll()
.requestMatchers("/nurse-station/**")
.permitAll()
.requestMatchers("/vital-signs/**")
.permitAll()
.requestMatchers("/nursing-mobile/**")
.permitAll()
.requestMatchers("/nursing-record/**")
.permitAll()
.requestMatchers("/nursing-execution/**")
.permitAll()
.requestMatchers("/api/v1/nursing/**")
.permitAll()
// 登录页展示用的系统版本信息,允许匿名访问
.requestMatchers("/system/version")
.permitAll()
// 除上面外的所有请求全部需要鉴权认证
.anyRequest().authenticated();
})

View File

@@ -16,7 +16,4 @@ public interface INursingMobileAppService {
NursingMobileInfusionDto startInfusion(NursingMobileInfusionDto dto);
NursingMobileInfusionDto addPatrol(NursingMobileInfusionDto dto);
List<NursingMobileInfusionDto> getInfusionStatus(Long patientId);
Map<String, Object> infusionAction(Long infusionId, String action);
Map<String, Object> getDrugDistributionList(String searchKey, Integer pageNo, Integer pageSize);
Map<String, Object> executeDrugDistribution(Long id, String action);
}

View File

@@ -307,42 +307,6 @@ public class NursingMobileAppServiceImpl implements INursingMobileAppService {
return new ArrayList<>(latestMap.values());
}
@Override
public Map<String, Object> infusionAction(Long infusionId, String action) {
Map<String, Object> result = new HashMap<>();
result.put("infusionId", infusionId);
result.put("action", action);
result.put("status", "SUCCESS");
if ("PATROL".equals(action)) {
result.put("message", "巡视完成");
} else if ("COMPLETE".equals(action)) {
result.put("message", "输液已结束");
} else {
result.put("message", "操作完成");
}
return result;
}
@Override
public Map<String, Object> getDrugDistributionList(String searchKey, Integer pageNo, Integer pageSize) {
Map<String, Object> result = new HashMap<>();
result.put("records", new ArrayList<>());
result.put("total", 0);
result.put("pageNo", pageNo);
result.put("pageSize", pageSize);
return result;
}
@Override
public Map<String, Object> executeDrugDistribution(Long id, String action) {
Map<String, Object> result = new HashMap<>();
result.put("id", id);
result.put("action", action);
result.put("status", "SUCCESS");
result.put("message", "药品发放操作成功");
return result;
}
private String calculateRiskLevel(String tool, Integer score) {
if (score == null) return "NORMAL";
if ("BRADEN".equals(tool)) {

View File

@@ -109,32 +109,4 @@ public class NursingMobileController {
List<NursingMobileInfusionDto> list = mobileAppService.getInfusionStatus(patientId);
return R.ok(list);
}
@Operation(summary = "输液操作(巡视/结束)")
@PostMapping("/infusion/action")
@PreAuthorize("hasAuthority('nursing:nursing:edit')")
public R<?> infusionAction(@RequestBody Map<String, Object> params) {
Long infusionId = Long.valueOf(params.get("infusionId").toString());
String action = params.get("action").toString();
return R.ok(mobileAppService.infusionAction(infusionId, action));
}
@Operation(summary = "药品发放列表")
@GetMapping("/drug-distribution/list")
@PreAuthorize("hasAuthority('nursing:nursing:list')")
public R<?> getDrugDistributionList(
@RequestParam(required = false) String searchKey,
@RequestParam(value = "pageNo", defaultValue = "1") Integer pageNo,
@RequestParam(value = "pageSize", defaultValue = "20") Integer pageSize) {
return R.ok(mobileAppService.getDrugDistributionList(searchKey, pageNo, pageSize));
}
@Operation(summary = "执行药品发放")
@PostMapping("/drug-distribution/execute")
@PreAuthorize("hasAuthority('nursing:nursing:edit')")
public R<?> executeDrugDistribution(@RequestBody Map<String, Object> params) {
Long id = Long.valueOf(params.get("id").toString());
String action = params.get("action").toString();
return R.ok(mobileAppService.executeDrugDistribution(id, action));
}
}

View File

@@ -14,7 +14,7 @@ spring:
druid:
# 主库数据源
master:
url: jdbc:postgresql://47.116.196.11:15432/postgresql?currentSchema=healthlink_his&characterEncoding=UTF-8&client_encoding=UTF-8
url: jdbc:postgresql://192.168.110.252:15432/postgresql?currentSchema=healthlink_his&characterEncoding=UTF-8&client_encoding=UTF-8
username: postgresql
password: Jchl1528 # 请替换为实际的数据库密码
# 从库数据源
@@ -73,9 +73,9 @@ spring:
data:
redis:
# 地址
host: 47.116.196.11
host: 192.168.110.252
# 端口默认为6379
port: 26379
port: 6379
# 数据库索引
database: 1
# 密码

View File

@@ -1,16 +0,0 @@
-- 插入测试数据供移动端使用
-- 1. 测试患者数据(假设已有基础数据,这里添加护理任务)
INSERT INTO nurse_order_execute_record (encounter_id, patient_id, order_type, order_name, execute_status, create_time)
VALUES
(1, 1, '医嘱执行', '测量体温 bid', 'PENDING', CURRENT_TIMESTAMP),
(1, 1, '医嘱执行', '测量血压 qd', 'PENDING', CURRENT_TIMESTAMP),
(1, 1, '生命体征', '录入生命体征', 'PENDING', CURRENT_TIMESTAMP),
(1, 2, '医嘱执行', '更换敷料 qd', 'PENDING', CURRENT_TIMESTAMP),
(1, 2, '护理评估', 'Braden压疮评估', 'PENDING', CURRENT_TIMESTAMP),
(2, 3, '医嘱执行', '测量血糖 tid', 'PENDING', CURRENT_TIMESTAMP),
(2, 3, '医嘱执行', '胰岛素注射', 'PENDING', CURRENT_TIMESTAMP),
(2, 4, '生命体征', '录入生命体征', 'PENDING', CURRENT_TIMESTAMP),
(2, 4, '护理评估', 'Morse跌倒评估', 'PENDING', CURRENT_TIMESTAMP),
(3, 5, '医嘱执行', '雾化吸入 bid', 'PENDING', CURRENT_TIMESTAMP)
ON CONFLICT DO NOTHING;

View File

@@ -1,31 +0,0 @@
-- 移动端测试数据插入脚本
-- 1. 测试护理任务数据
INSERT INTO nurse_order_execute_record (encounter_id, patient_id, order_type, order_name, execute_status, create_time) VALUES
(1, 1, '医嘱执行', '测量体温 bid', 'PENDING', CURRENT_TIMESTAMP),
(1, 1, '医嘱执行', '测量血压 qd', 'PENDING', CURRENT_TIMESTAMP),
(1, 1, '生命体征', '录入生命体征', 'PENDING', CURRENT_TIMESTAMP),
(1, 2, '医嘱执行', '更换敷料 qd', 'PENDING', CURRENT_TIMESTAMP),
(1, 2, '护理评估', 'Braden压疮评估', 'PENDING', CURRENT_TIMESTAMP),
(2, 3, '医嘱执行', '测量血糖 tid', 'PENDING', CURRENT_TIMESTAMP),
(2, 3, '医嘱执行', '胰岛素注射', 'PENDING', CURRENT_TIMESTAMP),
(2, 4, '生命体征', '录入生命体征', 'PENDING', CURRENT_TIMESTAMP),
(2, 4, '护理评估', 'Morse跌倒评估', 'PENDING', CURRENT_TIMESTAMP),
(3, 5, '医嘱执行', '雾化吸入 bid', 'PENDING', CURRENT_TIMESTAMP)
ON CONFLICT DO NOTHING;
-- 2. 测试生命体征数据
INSERT INTO nursing_vital_signs_chart (patient_id, encounter_id, record_time, temperature, pulse, blood_pressure_high, blood_pressure_low, spo2, respiration, pain_score, create_time) VALUES
(1, 1, CURRENT_TIMESTAMP - INTERVAL '2 hours', 36.5, 72, 120, 80, 98, 18, 2, CURRENT_TIMESTAMP),
(1, 1, CURRENT_TIMESTAMP - INTERVAL '1 hour', 36.8, 75, 125, 82, 97, 19, 3, CURRENT_TIMESTAMP),
(1, 1, CURRENT_TIMESTAMP, 37.0, 78, 128, 85, 96, 20, 4, CURRENT_TIMESTAMP),
(2, 2, CURRENT_TIMESTAMP - INTERVAL '1 hour', 36.6, 70, 118, 78, 99, 17, 1, CURRENT_TIMESTAMP),
(2, 2, CURRENT_TIMESTAMP, 36.7, 72, 120, 80, 98, 18, 2, CURRENT_TIMESTAMP)
ON CONFLICT DO NOTHING;
-- 3. 测试护理评估数据
INSERT INTO nursing_assessment (encounter_id, patient_id, assessment_type, assessment_tool, total_score, risk_level, detail, create_time) VALUES
(1, 1, '压疮评估', 'Braden', 16, 'LOW', '{"sensory":4,"moisture":3,"activity":3,"friction":3,"nutrition":4,"mobility":4}', CURRENT_TIMESTAMP),
(1, 1, '跌倒评估', 'Morse', 15, 'LOW', '{"history":0,"diagnosis":0,"ambulation":0,"iv":0,"gait":0,"mental":0}', CURRENT_TIMESTAMP),
(2, 2, '营养筛查', 'NRS2002', 1, 'LOW', '{"bmi":0,"weightLoss":0,"intake":1}', CURRENT_TIMESTAMP)
ON CONFLICT DO NOTHING;

View File

@@ -1,84 +0,0 @@
-- V99: 病案管理模块假数据(借阅/封存/示踪/死亡讨论)
-- 生成时间: 2026-06-19
-- ==================== 1. mr_borrowing 病案借阅 ====================
INSERT INTO healthlink_his.mr_borrowing (id, medical_record_id, patient_name, mr_number, borrower_name, borrower_dept, borrow_reason, borrow_date, expected_return_date, actual_return_date, status, approver_name, approve_time, tenant_id, delete_flag, create_by, create_time)
VALUES
-- 待审批(0)
(9000000001, 6001, '测试患者甲', 'MR202606001', '李医生', '神经内科', '科研论文需要', '2026-06-15 09:00:00', '2026-06-22 17:00:00', NULL, 0, NULL, NULL, 1, '0', 'admin', '2026-06-15 09:00:00'),
(9000000002, 6002, '测试患者乙', 'MR202606002', '王护士', '内分泌科', '教学查房', '2026-06-18 14:30:00', '2026-06-25 17:00:00', NULL, 0, NULL, NULL, 1, '0', 'admin', '2026-06-18 14:30:00'),
-- 已批准(1)
(9000000003, 6003, '测试患者丙', 'MR202606003', '赵主任', '消化内科', '疑难病例讨论', '2026-06-10 08:00:00', '2026-06-17 17:00:00', NULL, 1, '张院长', '2026-06-10 10:00:00', 1, '0', 'admin', '2026-06-10 08:00:00'),
-- 已借出(2)
(9000000004, 6004, '测试患者丁', 'MR202606004', '钱医生', '超声诊断科', '影像学对比分析', '2026-06-12 10:00:00', '2026-06-19 17:00:00', NULL, 2, '孙科长', '2026-06-12 11:00:00', 1, '0', 'admin', '2026-06-12 10:00:00'),
-- 已归还(3)
(9000000005, 6005, '测试患者戊', 'MR202606005', '周医生', '神经内科', '死亡病例回顾', '2026-06-01 09:00:00', '2026-06-08 17:00:00', '2026-06-07 16:00:00', 3, '吴院长', '2026-06-01 14:00:00', 1, '0', 'admin', '2026-06-01 09:00:00'),
(9000000006, 6006, '测试患者甲', 'MR202606006', '郑护士长', '内分泌科', '护理质量检查', '2026-05-20 08:30:00', '2026-05-27 17:00:00', '2026-05-26 15:00:00', 3, '张院长', '2026-05-20 09:00:00', 1, '0', 'admin', '2026-05-20 08:30:00'),
-- 已逾期(4)
(9000000007, 6007, '测试患者乙', 'MR202606007', '刘医生', '消化内科', '科研课题', '2026-05-15 10:00:00', '2026-05-22 17:00:00', NULL, 4, '王科长', '2026-05-15 11:00:00', 1, '0', 'admin', '2026-05-15 10:00:00'),
-- 已拒绝(5)
(9000000008, 6008, '测试患者丁', 'MR202606008', '陈医生', '超声诊断科', '个人学习', '2026-06-17 16:00:00', '2026-06-24 17:00:00', NULL, 5, '张院长', '2026-06-18 09:00:00', 1, '0', 'admin', '2026-06-17 16:00:00'),
-- 更多借阅记录
(9000000009, 6009, '测试患者戊', 'MR202606009', '黄医生', '神经内科', '会诊需要', '2026-06-19 08:00:00', '2026-06-26 17:00:00', NULL, 0, NULL, NULL, 1, '0', 'admin', '2026-06-19 08:00:00'),
(9000000010, 6010, '测试患者己', 'MR202606010', '林护士', '内分泌科', '护理查房', '2026-06-16 13:00:00', '2026-06-23 17:00:00', '2026-06-20 10:00:00', 3, '赵科长', '2026-06-16 14:00:00', 1, '0', 'admin', '2026-06-16 13:00:00');
-- ==================== 2. mr_sealing 病案封存 ====================
INSERT INTO healthlink_his.mr_sealing (id, medical_record_id, patient_name, mr_number, seal_reason, seal_type, seal_date, seal_by, unseal_date, unseal_by, unseal_reason, status, tenant_id, delete_flag, create_by, create_time)
VALUES
-- 已封存(0)
(9100000001, 6001, '测试患者甲', 'MR202606001', '医疗纠纷封存', 2, '2026-06-10 09:00:00', '张院长', NULL, NULL, NULL, 0, 1, '0', 'admin', '2026-06-10 09:00:00'),
(9100000002, 6003, '测试患者丙', 'MR202606003', '患者主动申请封存', 1, '2026-06-12 14:00:00', '李科长', NULL, NULL, NULL, 0, 1, '0', 'admin', '2026-06-12 14:00:00'),
(9100000003, 6005, '测试患者戊', 'MR202606005', '司法鉴定封存', 3, '2026-06-15 10:00:00', '王院长', NULL, NULL, NULL, 0, 1, '0', 'admin', '2026-06-15 10:00:00'),
-- 已解封(1)
(9100000004, 6002, '测试患者乙', 'MR202606002', '医疗纠纷封存', 2, '2026-05-20 08:00:00', '张院长', '2026-06-01 09:00:00', '张院长', '纠纷已和解', 1, 1, '0', 'admin', '2026-05-20 08:00:00'),
(9100000005, 6004, '测试患者丁', 'MR202606004', '患者主动申请封存', 1, '2026-04-15 11:00:00', '李科长', '2026-05-10 14:00:00', '李科长', '患者要求查阅', 1, 1, '0', 'admin', '2026-04-15 11:00:00'),
-- 更多封存记录
(9100000006, 6007, '测试患者乙', 'MR202606007', '司法鉴定封存', 3, '2026-06-18 15:00:00', '王院长', NULL, NULL, NULL, 0, 1, '0', 'admin', '2026-06-18 15:00:00'),
(9100000007, 6009, '测试患者戊', 'MR202606009', '医疗纠纷封存', 2, '2026-06-19 09:30:00', '张院长', NULL, NULL, NULL, 0, 1, '0', 'admin', '2026-06-19 09:30:00');
-- ==================== 3. mr_tracking 病案示踪 ====================
INSERT INTO healthlink_his.mr_tracking (id, medical_record_id, mr_number, patient_name, location, location_type, status, moved_by, move_time, tenant_id, delete_flag, create_by, create_time)
VALUES
-- 在架(IN_SHELF)
(9200000001, 6001, 'MR202606001', '测试患者甲', '病案室-A区-01架-03层', 'STORAGE', 'IN_SHELF', '系统管理员', '2026-06-15 08:00:00', 1, '0', 'admin', '2026-06-15 08:00:00'),
(9200000002, 6002, 'MR202606002', '测试患者乙', '病案室-B区-02架-01层', 'STORAGE', 'IN_SHELF', '系统管理员', '2026-06-18 14:00:00', 1, '0', 'admin', '2026-06-18 14:00:00'),
(9200000003, 6006, 'MR202606006', '测试患者甲', '病案室-A区-01架-04层', 'STORAGE', 'IN_SHELF', '系统管理员', '2026-05-27 09:00:00', 1, '0', 'admin', '2026-05-27 09:00:00'),
-- 借出(BORROWED)
(9200000004, 6004, 'MR202606004', '测试患者丁', '超声诊断科', 'DEPT', 'BORROWED', '钱医生', '2026-06-12 10:30:00', 1, '0', 'admin', '2026-06-12 10:30:00'),
(9200000005, 6007, 'MR202606007', '测试患者乙', '消化内科', 'DEPT', 'BORROWED', '刘医生', '2026-05-15 11:00:00', 1, '0', 'admin', '2026-05-15 11:00:00'),
-- 归档(ARCHIVED)
(9200000006, 6005, 'MR202606005', '测试患者戊', '档案室-永久区-05架', 'ARCHIVE', 'ARCHIVED', '系统管理员', '2026-05-01 08:00:00', 1, '0', 'admin', '2026-05-01 08:00:00'),
(9200000007, 6010, 'MR202606010', '测试患者己', '档案室-永久区-06架', 'ARCHIVE', 'ARCHIVED', '系统管理员', '2026-04-20 09:00:00', 1, '0', 'admin', '2026-04-20 09:00:00'),
-- 遗失(LOST)
(9200000008, 6008, 'MR202606008', '测试患者丁', '未知', 'UNKNOWN', 'LOST', '系统管理员', '2026-06-19 08:00:00', 1, '0', 'admin', '2026-06-19 08:00:00'),
-- 更多示踪记录
(9200000009, 6003, 'MR202606003', '测试患者丙', '病案室-C区-01架-02层', 'STORAGE', 'IN_SHELF', '系统管理员', '2026-06-10 08:30:00', 1, '0', 'admin', '2026-06-10 08:30:00'),
(9200000010, 6009, 'MR202606009', '测试患者戊', '神经内科', 'DEPT', 'BORROWED', '黄医生', '2026-06-19 08:30:00', 1, '0', 'admin', '2026-06-19 08:30:00');
-- ==================== 4. mr_death_discussion 死亡病例讨论 ====================
INSERT INTO healthlink_his.mr_death_discussion (id, patient_id, patient_name, encounter_id, death_date, discussion_date, deadline_date, host_doctor_id, host_doctor_name, host_title, participants, discussion_conclusion, improvement_measures, status, is_overdue, tenant_id, delete_flag, create_by, create_time)
VALUES
-- 待讨论(0) - 未超期
(9300000001, 5001, '测试患者甲', 6001, '2026-06-17 03:20:00', NULL, '2026-06-24 03:20:00', 1001, '张院长', '主任医师', NULL, NULL, NULL, 0, false, 1, '0', 'admin', '2026-06-17 08:00:00'),
(9300000002, 5003, '测试患者丙', 6003, '2026-06-18 15:45:00', NULL, '2026-06-25 15:45:00', 1002, '李主任', '副主任医师', NULL, NULL, NULL, 0, false, 1, '0', 'admin', '2026-06-18 16:00:00'),
-- 待讨论(0) - 已超期
(9300000003, 5005, '测试患者戊', 6005, '2026-06-08 22:10:00', NULL, '2026-06-15 22:10:00', 1003, '王主任', '主任医师', NULL, NULL, NULL, 0, true, 1, '0', 'admin', '2026-06-09 08:00:00'),
(9300000004, 5002, '测试患者乙', 6002, '2026-06-05 04:30:00', NULL, '2026-06-12 04:30:00', 1004, '赵主任', '副主任医师', NULL, NULL, NULL, 0, true, 1, '0', 'admin', '2026-06-05 09:00:00'),
-- 已讨论(1)
(9300000005, 5004, '测试患者丁', 6004, '2026-05-28 11:00:00', '2026-06-04 14:00:00', '2026-06-04 11:00:00', 1001, '张院长', '主任医师', '张院长、李主任、王主任、赵主任、刘护士长',
'患者为多器官功能衰竭,抢救无效死亡。讨论认为早期介入治疗可改善预后。',
'1. 加强ICU早期巡查2. 完善多学科会诊流程3. 提高危重患者识别能力。',
1, false, 1, '0', 'admin', '2026-05-28 14:00:00'),
(9300000006, 5006, '测试患者己', 6010, '2026-05-15 06:20:00', '2026-05-22 10:00:00', '2026-05-22 06:20:00', 1002, '李主任', '副主任医师', '李主任、钱主任、孙主任',
'患者因急性心肌梗死抢救无效死亡。讨论认为溶栓时间窗把握需更精准。',
'1. 优化胸痛中心流程2. 缩短D-to-B时间3. 加强基层医院转诊培训。',
1, false, 1, '0', 'admin', '2026-05-15 10:00:00'),
-- 已归档(2)
(9300000007, 5007, '急诊患者庚', 6006, '2026-04-10 18:30:00', '2026-04-17 15:00:00', '2026-04-17 18:30:00', 1003, '王主任', '主任医师', '王主任、赵主任、周主任、吴主任',
'患者因严重创伤导致失血性休克,抢救无效死亡。讨论认为创伤急救流程需优化。',
'1. 完善创伤中心建设2. 配备更多急救设备3. 加强急诊科人员培训。',
2, false, 1, '0', 'admin', '2026-04-10 20:00:00'),
(9300000008, 5008, '急诊患者辛', 6007, '2026-03-20 22:45:00', '2026-03-27 14:00:00', '2026-03-27 22:45:00', 1004, '赵主任', '副主任医师', '赵主任、钱主任、孙主任',
'患者因脑出血导致脑疝,抢救无效死亡。讨论认为早期降压治疗时机需把握。',
'1. 完善脑卒中绿色通道2. 加强神经内科急诊值班3. 配备更多降压药物。',
2, false, 1, '0', 'admin', '2026-03-20 23:00:00');

View File

@@ -129,6 +129,49 @@ export const constantRoutes = [
}
]
},
{
path: '/nursingmobile',
component: Layout,
hidden: true,
children: [
{
path: 'patient-list',
component: () => import('@/views/nursingmobile/PatientList.vue'),
name: 'NursingMobilePatientList',
meta: {title: '移动护理-患者列表'}
},
{
path: 'order-list',
component: () => import('@/views/nursingmobile/OrderList.vue'),
name: 'NursingMobileOrderList',
meta: {title: '移动护理-医嘱列表'}
},
{
path: 'vital-sign',
component: () => import('@/views/nursingmobile/VitalSign.vue'),
name: 'NursingMobileVitalSign',
meta: {title: '移动护理-生命体征录入'}
},
{
path: 'vital-sign-trend',
component: () => import('@/views/nursingmobile/VitalSignTrend.vue'),
name: 'NursingMobileVitalSignTrend',
meta: {title: '移动护理-体征趋势'}
},
{
path: 'assessment',
component: () => import('@/views/nursingmobile/NursingAssessment.vue'),
name: 'NursingMobileAssessment',
meta: {title: '移动护理-护理评估'}
},
{
path: 'infusion',
component: () => import('@/views/nursingmobile/InfusionManagement.vue'),
name: 'NursingMobileInfusion',
meta: {title: '移动护理-输液管理'}
}
]
},
// 添加套餐管理相关路由到公共路由,确保始终可用
{
path: '/maintainSystem/Inspection/PackageManagement',

View File

@@ -340,7 +340,7 @@
:filter-method="filterSurgery"
style="width: 100%"
@change="handleSurgeryChange"
@visible-change="(visible) => { isSelectingSurgery.value = false; if (visible) filterSurgery('') }"
@visible-change="(visible) => { if (visible) filterSurgery('') }"
>
<el-option
v-for="item in surgeryNameList"
@@ -420,7 +420,7 @@
:filter-method="filterSurgery"
style="width: 100%"
@change="(val) => handleSecondarySurgeryChange(val, scope.row)"
@visible-change="(visible) => { isSelectingSurgery.value = false; if (visible) filterSurgery('') }"
@visible-change="(visible) => { if (visible) filterSurgery('') }"
>
<el-option
v-for="item in surgeryNameList"
@@ -502,7 +502,6 @@
:filter-method="filterAnesthesia"
style="width: 100%"
@change="(val) => handleSecondaryAnesthesiaChange(val, scope.row)"
@visible-change="(visible) => { isSelectingAnesthesia.value = false; if (visible) filterAnesthesia('') }"
>
<el-option
v-for="item in anesthesiaNameList"
@@ -963,8 +962,6 @@ const surgeryLoading = ref(false)
const anesthesiaLoading = ref(false)
let surgerySearchTimer = null
let anesthesiaSearchTimer = null
const isSelectingSurgery = ref(false)
const isSelectingAnesthesia = ref(false)
const surgeryList = ref([])
const open = ref(false)
const viewOpen = ref(false)
@@ -1421,8 +1418,6 @@ function doSearchAnesthesia(query) {
// 本地过滤手术项目
function filterSurgery(query) {
// 🔧 BugFix#769: 选择手术时不重置列表,防止下拉框闪烁
if (isSelectingSurgery.value) return
if (!query) {
surgeryNameList.value = allSurgeryItems.value
} else {
@@ -1435,8 +1430,6 @@ function filterSurgery(query) {
// 本地过滤麻醉项目
function filterAnesthesia(query) {
// 🔧 BugFix#769: 选择麻醉时不重置列表,防止下拉框闪烁
if (isSelectingAnesthesia.value) return
if (!query) {
anesthesiaNameList.value = allAnesthesiaItems.value
} else {
@@ -1502,8 +1495,6 @@ function loadSurgeryAndAnesthesiaOptions() {
// 手术项目选择变更
function handleSurgeryChange(val) {
// 🔧 BugFix#769: 标记正在选择,防止 visible-change 重置列表导致闪烁
isSelectingSurgery.value = true
// 🔧 BugFix#318: 确保 surgeryName 被正确设置
form.value.surgeryName = val
const selected = surgeryNameList.value.find(item => item.name === val)
@@ -1523,8 +1514,6 @@ function handleSurgeryChange(val) {
// 麻醉项目选择变更
function handleAnesthesiaChange(val) {
// 🔧 BugFix#769: 标记正在选择,防止 visible-change 重置列表导致闪烁
isSelectingAnesthesia.value = true
const selected = anesthesiaNameList.value.find(item => item.name === val)
if (selected) {
// 设置麻醉费用 (增加对多种字段名和类型的兼容)
@@ -1538,8 +1527,6 @@ function handleAnesthesiaChange(val) {
// 次要手术麻醉项目选择变更
function handleSecondaryAnesthesiaChange(val, row) {
// 🔧 BugFix#769: 标记正在选择,防止 visible-change 重置列表导致闪烁
isSelectingAnesthesia.value = true
const selected = anesthesiaNameList.value.find(item => item.name === val)
if (selected) {
// 设置该次要手术的麻醉费用
@@ -1564,8 +1551,6 @@ function mapAnesthesiaNameToEnum(name) {
// 次要手术项目选择变更
function handleSecondarySurgeryChange(val, row) {
// 🔧 BugFix#769: 标记正在选择,防止 visible-change 重置列表导致闪烁
isSelectingSurgery.value = true
const selected = surgeryNameList.value.find(item => item.name === val)
if (selected) {
row.surgeryCode = selected.busNo