fix(mobile): 修复移动端核心功能问题
- 新增 getPatientList API 调用正确的患者列表接口 - PatientDetail: Promise.all 并发加载患者信息/医嘱/体征/评估 - 所有页面添加 loading 状态和 ElMessage 错误提示 - 任务完成添加 ElMessageBox 确认对话框 - TaskList 添加刷新按钮 - Mine 退出登录添加确认对话框
This commit is contained in:
@@ -19,7 +19,9 @@ request.interceptors.response.use(
|
||||
export const nursingApi = {
|
||||
getTasks: (params) => request.get('/mp/nursing/tasks', { params }),
|
||||
completeTask: (id, data) => request.post(`/mp/nursing/tasks/${id}/complete`, data),
|
||||
getPatientList: (params) => request.get('/mp/nursing/patient/list', { params }),
|
||||
getPatientInfo: (id) => request.get(`/mp/nursing/patient/${id}`),
|
||||
getOrders: (patientId) => request.get(`/mp/nursing/orders/${patientId}`),
|
||||
getVitalSigns: (patientId) => request.get(`/mp/nursing/vital-signs/${patientId}`),
|
||||
submitVitalSign: (data) => request.post('/mp/nursing/vital-sign', data),
|
||||
getAssessments: (patientId) => request.get(`/mp/nursing/assessments/${patientId}`),
|
||||
|
||||
@@ -61,11 +61,19 @@ const riskLevel = computed(() => {
|
||||
})
|
||||
const riskLevelText = computed(() => ({ HIGH: '高风险', MEDIUM: '中风险', LOW: '低风险' }[riskLevel.value]))
|
||||
|
||||
const loading = ref(false)
|
||||
|
||||
const submit = async () => {
|
||||
loading.value = true
|
||||
try {
|
||||
await nursingApi.submitAssessment({ patientId: route.params.patientId, assessmentType: selectedType.value, totalScore: totalScore.value, riskLevel: riskLevel.value, detail: JSON.stringify(formData.value) })
|
||||
ElMessage.success('评估提交成功')
|
||||
} catch (e) { ElMessage.error('提交失败') }
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
ElMessage.error('提交失败')
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
@@ -17,7 +17,15 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
const logout = () => { localStorage.removeItem('token'); window.location.href = '/login' }
|
||||
import { ElMessageBox } from 'element-plus'
|
||||
|
||||
const logout = async () => {
|
||||
try {
|
||||
await ElMessageBox.confirm('确认退出登录?', '提示', { confirmButtonText: '确认', cancelButtonText: '取消' })
|
||||
localStorage.removeItem('token')
|
||||
window.location.href = '/login'
|
||||
} catch {}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
@@ -1,40 +1,44 @@
|
||||
<template>
|
||||
<div class="patient-detail">
|
||||
<div class="patient-header">
|
||||
<div class="avatar">{{ patient.name?.charAt(0) }}</div>
|
||||
<div class="info">
|
||||
<div class="name">{{ patient.name }} <span class="bed">{{ patient.bedNo }}床</span></div>
|
||||
<div class="diag">{{ patient.diagnosis }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div class="tabs">
|
||||
<div v-for="tab in tabs" :key="tab.key" class="tab" :class="{ active: activeTab === tab.key }" @click="activeTab = tab.key">{{ tab.label }}</div>
|
||||
</div>
|
||||
<div class="tab-content">
|
||||
<div v-if="activeTab === 'orders'">
|
||||
<div v-for="order in orders" :key="order.id" class="order-item">
|
||||
<div class="order-name">{{ order.orderName }}</div>
|
||||
<div class="order-dose">{{ order.dosage }} {{ order.frequency }}</div>
|
||||
<button v-if="order.status === 'PENDING'" class="exec-btn" @click="executeOrder(order)">执行</button>
|
||||
<div v-if="loading" class="empty">加载中...</div>
|
||||
<template v-else>
|
||||
<div class="patient-header">
|
||||
<div class="avatar">{{ patient.name?.charAt(0) }}</div>
|
||||
<div class="info">
|
||||
<div class="name">{{ patient.name }} <span class="bed">{{ patient.bedNo }}床</span></div>
|
||||
<div class="diag">{{ patient.diagnosis }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="activeTab === 'vitals'">
|
||||
<div class="vital-grid">
|
||||
<div class="vital-item" v-for="v in latestVitals" :key="v.key">
|
||||
<div class="vital-value" :class="v.status">{{ v.value }}</div>
|
||||
<div class="vital-label">{{ v.label }}</div>
|
||||
<div class="tabs">
|
||||
<div v-for="tab in tabs" :key="tab.key" class="tab" :class="{ active: activeTab === tab.key }" @click="activeTab = tab.key">{{ tab.label }}</div>
|
||||
</div>
|
||||
<div class="tab-content">
|
||||
<div v-if="activeTab === 'orders'">
|
||||
<div v-for="order in orders" :key="order.id" class="order-item">
|
||||
<div class="order-name">{{ order.orderName }}</div>
|
||||
<div class="order-dose">{{ order.dosage }} {{ order.frequency }}</div>
|
||||
<button v-if="order.status === 'PENDING'" class="exec-btn" @click="executeOrder(order)">执行</button>
|
||||
</div>
|
||||
<div v-if="orders.length === 0" class="empty">暂无医嘱</div>
|
||||
</div>
|
||||
<button class="add-btn" @click="$router.push(`/mobile/vital-entry/${$route.params.id}`)">录入体征</button>
|
||||
</div>
|
||||
<div v-if="activeTab === 'assessments'">
|
||||
<div v-for="a in assessments" :key="a.id" class="assess-item">
|
||||
<div class="assess-type">{{ a.assessmentType }}</div>
|
||||
<div class="assess-score">评分: {{ a.totalScore }} <span :class="'risk-' + a.riskLevel">{{ a.riskLevel }}</span></div>
|
||||
<div v-if="activeTab === 'vitals'">
|
||||
<div class="vital-grid">
|
||||
<div class="vital-item" v-for="v in latestVitals" :key="v.key">
|
||||
<div class="vital-value" :class="v.status">{{ v.value }}</div>
|
||||
<div class="vital-label">{{ v.label }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<button class="add-btn" @click="$router.push(`/mobile/vital-entry/${$route.params.id}`)">录入体征</button>
|
||||
</div>
|
||||
<div v-if="activeTab === 'assessments'">
|
||||
<div v-for="a in assessments" :key="a.id" class="assess-item">
|
||||
<div class="assess-type">{{ a.assessmentType }}</div>
|
||||
<div class="assess-score">评分: {{ a.totalScore }} <span :class="'risk-' + a.riskLevel">{{ a.riskLevel }}</span></div>
|
||||
</div>
|
||||
<button class="add-btn" @click="$router.push(`/mobile/assessment/${$route.params.id}`)">新建评估</button>
|
||||
</div>
|
||||
<button class="add-btn" @click="$router.push(`/mobile/assessment/${$route.params.id}`)">新建评估</button>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
@@ -42,6 +46,7 @@
|
||||
import { ref, onMounted } from 'vue'
|
||||
import { useRoute } from 'vue-router'
|
||||
import { nursingApi } from '../api'
|
||||
import { ElMessage } from 'element-plus'
|
||||
|
||||
const route = useRoute()
|
||||
const patient = ref({})
|
||||
@@ -49,18 +54,40 @@ const orders = ref([])
|
||||
const latestVitals = ref([])
|
||||
const assessments = ref([])
|
||||
const activeTab = ref('orders')
|
||||
const loading = ref(false)
|
||||
const tabs = [{ key: 'orders', label: '医嘱' }, { key: 'vitals', label: '体征' }, { key: 'assessments', label: '评估' }]
|
||||
|
||||
onMounted(async () => {
|
||||
const id = route.params.id
|
||||
loading.value = true
|
||||
try {
|
||||
const res = await nursingApi.getPatientInfo(id)
|
||||
patient.value = res.data || {}
|
||||
} catch (e) { console.error(e) }
|
||||
const [patientRes, ordersRes, vitalsRes, assessRes] = await Promise.all([
|
||||
nursingApi.getPatientInfo(id),
|
||||
nursingApi.getOrders(id),
|
||||
nursingApi.getVitalSigns(id),
|
||||
nursingApi.getAssessments(id)
|
||||
])
|
||||
patient.value = patientRes.data || {}
|
||||
orders.value = ordersRes.data || []
|
||||
latestVitals.value = vitalsRes.data || []
|
||||
assessments.value = assessRes.data || []
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
ElMessage.error('加载患者数据失败')
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
})
|
||||
|
||||
const executeOrder = async (order) => {
|
||||
try { await nursingApi.completeTask(order.id, { result: '执行完成' }); order.status = 'COMPLETED' } catch (e) { console.error(e) }
|
||||
try {
|
||||
await nursingApi.completeTask(order.id, { result: '执行完成' })
|
||||
order.status = 'COMPLETED'
|
||||
ElMessage.success('医嘱执行成功')
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
ElMessage.error('医嘱执行失败')
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
@@ -3,33 +3,47 @@
|
||||
<div class="search-bar">
|
||||
<input v-model="searchText" placeholder="搜索患者姓名/床号..." class="search-input" />
|
||||
</div>
|
||||
<div v-for="p in filteredPatients" :key="p.id" class="patient-card" @click="$router.push(`/mobile/patient-detail/${p.id}`)">
|
||||
<div class="patient-avatar">{{ p.name?.charAt(0) }}</div>
|
||||
<div class="patient-info">
|
||||
<div class="patient-name">{{ p.name }} <span class="bed-no">{{ p.bedNo }}</span></div>
|
||||
<div class="patient-diag">{{ p.diagnosis || '暂无诊断' }}</div>
|
||||
<div class="patient-level">
|
||||
<span :class="'level-' + p.nursingLevel">{{ p.nursingLevel }}级护理</span>
|
||||
<div v-if="loading" class="empty">加载中...</div>
|
||||
<template v-else>
|
||||
<div v-for="p in filteredPatients" :key="p.id" class="patient-card" @click="$router.push(`/mobile/patient-detail/${p.id}`)">
|
||||
<div class="patient-avatar">{{ p.name?.charAt(0) }}</div>
|
||||
<div class="patient-info">
|
||||
<div class="patient-name">{{ p.name }} <span class="bed-no">{{ p.bedNo }}</span></div>
|
||||
<div class="patient-diag">{{ p.diagnosis || '暂无诊断' }}</div>
|
||||
<div class="patient-level">
|
||||
<span :class="'level-' + p.nursingLevel">{{ p.nursingLevel }}级护理</span>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="filteredPatients.length === 0" class="empty">暂无患者</div>
|
||||
<div v-if="filteredPatients.length === 0" class="empty">暂无患者</div>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted, computed } from 'vue'
|
||||
import { nursingApi } from '../api'
|
||||
import { ElMessage } from 'element-plus'
|
||||
|
||||
const patients = ref([])
|
||||
const searchText = ref('')
|
||||
const loading = ref(false)
|
||||
const filteredPatients = computed(() => {
|
||||
if (!searchText.value) return patients.value
|
||||
return patients.value.filter(p => p.name?.includes(searchText.value) || p.bedNo?.includes(searchText.value))
|
||||
})
|
||||
|
||||
onMounted(async () => {
|
||||
try { const res = await nursingApi.getPatientInfo('list'); patients.value = res.data || [] } catch (e) { console.error(e) }
|
||||
loading.value = true
|
||||
try {
|
||||
const res = await nursingApi.getPatientList()
|
||||
patients.value = res.data || []
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
ElMessage.error('加载患者列表失败')
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
})
|
||||
</script>
|
||||
|
||||
|
||||
@@ -1,25 +1,34 @@
|
||||
<template>
|
||||
<div class="task-list">
|
||||
<div v-for="(group, type) in groupedTasks" :key="type" class="task-group">
|
||||
<div class="group-header">{{ type }}</div>
|
||||
<div v-for="task in group" :key="task.id" class="task-card" @touchstart="swipeStart" @touchend="swipeEnd($event, task)">
|
||||
<div class="task-info">
|
||||
<div class="task-patient">{{ task.patientName }} - {{ task.bedNo }}</div>
|
||||
<div class="task-content">{{ task.taskContent }}</div>
|
||||
<div class="task-time">{{ task.dueTime }}</div>
|
||||
</div>
|
||||
<div class="task-status" :class="task.taskStatus">{{ statusText(task.taskStatus) }}</div>
|
||||
</div>
|
||||
<div class="task-header">
|
||||
<h3>护理任务</h3>
|
||||
<button class="refresh-btn" @click="loadTasks" :disabled="loading">{{ loading ? '刷新中...' : '刷新' }}</button>
|
||||
</div>
|
||||
<div v-if="tasks.length === 0" class="empty">暂无任务</div>
|
||||
<div v-if="loading" class="empty">加载中...</div>
|
||||
<template v-else>
|
||||
<div v-for="(group, type) in groupedTasks" :key="type" class="task-group">
|
||||
<div class="group-header">{{ type }}</div>
|
||||
<div v-for="task in group" :key="task.id" class="task-card" @touchstart="swipeStart" @touchend="swipeEnd($event, task)">
|
||||
<div class="task-info">
|
||||
<div class="task-patient">{{ task.patientName }} - {{ task.bedNo }}</div>
|
||||
<div class="task-content">{{ task.taskContent }}</div>
|
||||
<div class="task-time">{{ task.dueTime }}</div>
|
||||
</div>
|
||||
<div class="task-status" :class="task.taskStatus">{{ statusText(task.taskStatus) }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="tasks.length === 0" class="empty">暂无任务</div>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref, onMounted, computed } from 'vue'
|
||||
import { nursingApi } from '../api'
|
||||
import { ElMessage, ElMessageBox } from 'element-plus'
|
||||
|
||||
const tasks = ref([])
|
||||
const loading = ref(false)
|
||||
const groupedTasks = computed(() => {
|
||||
const groups = {}
|
||||
tasks.value.forEach(t => {
|
||||
@@ -33,7 +42,16 @@ const groupedTasks = computed(() => {
|
||||
const statusText = (s) => ({ PENDING: '待完成', IN_PROGRESS: '进行中', COMPLETED: '已完成' }[s] || s)
|
||||
|
||||
const loadTasks = async () => {
|
||||
try { const res = await nursingApi.getTasks({ status: 'PENDING' }); tasks.value = res.data || [] } catch (e) { console.error(e) }
|
||||
loading.value = true
|
||||
try {
|
||||
const res = await nursingApi.getTasks({ status: 'PENDING' })
|
||||
tasks.value = res.data || []
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
ElMessage.error('加载任务失败')
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
|
||||
let startX = 0
|
||||
@@ -41,7 +59,17 @@ const swipeStart = (e) => { startX = e.touches[0].clientX }
|
||||
const swipeEnd = async (e, task) => {
|
||||
const diff = startX - e.changedTouches[0].clientX
|
||||
if (diff > 80 && task.taskStatus === 'PENDING') {
|
||||
try { await nursingApi.completeTask(task.id, { result: '完成' }); task.taskStatus = 'COMPLETED' } catch (e) { console.error(e) }
|
||||
try {
|
||||
await ElMessageBox.confirm('确认完成此任务?', '提示', { confirmButtonText: '确认', cancelButtonText: '取消' })
|
||||
await nursingApi.completeTask(task.id, { result: '完成' })
|
||||
task.taskStatus = 'COMPLETED'
|
||||
ElMessage.success('任务已完成')
|
||||
} catch (e) {
|
||||
if (e !== 'cancel') {
|
||||
console.error(e)
|
||||
ElMessage.error('任务完成失败')
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -50,6 +78,10 @@ onMounted(loadTasks)
|
||||
|
||||
<style scoped>
|
||||
.task-list { padding: 0; }
|
||||
.task-header { display: flex; justify-content: space-between; align-items: center; padding: 8px 0; }
|
||||
.task-header h3 { margin: 0; font-size: 16px; }
|
||||
.refresh-btn { background: #1890ff; color: #fff; border: none; padding: 6px 16px; border-radius: 4px; font-size: 14px; }
|
||||
.refresh-btn:disabled { background: #ccc; }
|
||||
.group-header { padding: 8px 16px; font-size: 14px; color: #666; background: #f0f0f0; margin: 8px 0; border-radius: 4px; }
|
||||
.task-card { background: #fff; border-radius: 8px; padding: 12px 16px; margin-bottom: 8px; display: flex; justify-content: space-between; align-items: center; box-shadow: 0 1px 3px rgba(0,0,0,0.1); }
|
||||
.task-patient { font-weight: 600; font-size: 16px; }
|
||||
|
||||
@@ -45,11 +45,19 @@ const painLabel = computed(() => {
|
||||
return '重度疼痛'
|
||||
})
|
||||
|
||||
const loading = ref(false)
|
||||
|
||||
const submit = async () => {
|
||||
loading.value = true
|
||||
try {
|
||||
await nursingApi.submitVitalSign({ ...formData.value, patientId: route.params.patientId })
|
||||
ElMessage.success('提交成功')
|
||||
} catch (e) { ElMessage.error('提交失败') }
|
||||
} catch (e) {
|
||||
console.error(e)
|
||||
ElMessage.error('提交失败')
|
||||
} finally {
|
||||
loading.value = false
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
|
||||
Reference in New Issue
Block a user