Files
akmon/pages/ec/nurse/dashboard.uvue
2026-01-20 08:04:15 +08:00

1124 lines
27 KiB
Plaintext
Raw Permalink 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>
<view class="nurse-dashboard">
<!-- Header -->
<view class="header">
<text class="header-title">护士工作台</text>
<text class="header-subtitle">{{ currentTime }}</text>
<view class="header-actions">
<button class="action-btn urgent" @click="showUrgentTasks">
<text class="btn-text">🚨 紧急任务</text>
</button>
<button class="action-btn" @click="showVitalSignsEntry">
<text class="btn-text"> 记录体征</text>
</button>
</view>
</view>
<!-- Stats Cards -->
<view class="stats-section">
<view class="stat-card">
<view class="stat-icon">👥</view>
<view class="stat-content">
<text class="stat-number">{{ stats.assigned_patients }}</text>
<text class="stat-label">负责患者</text>
</view>
</view>
<view class="stat-card">
<view class="stat-icon">✅</view>
<view class="stat-content">
<text class="stat-number">{{ stats.completed_tasks }}</text>
<text class="stat-label">今日完成</text>
</view>
</view>
<view class="stat-card">
<view class="stat-icon">⏰</view>
<view class="stat-content">
<text class="stat-number">{{ stats.pending_tasks }}</text>
<text class="stat-label">待处理</text>
</view>
</view>
<view class="stat-card urgent">
<view class="stat-icon">⚠️</view>
<view class="stat-content">
<text class="stat-number">{{ stats.urgent_tasks }}</text>
<text class="stat-label">紧急处理</text>
</view>
</view>
</view>
<!-- Quick Actions -->
<view class="quick-actions">
<text class="section-title">快速操作</text>
<view class="actions-grid">
<button class="quick-action-btn" @click="showPatientList">
<text class="action-icon">👥</text>
<text class="action-text">患者列表</text>
</button>
<button class="quick-action-btn" @click="showVitalSigns">
<text class="action-icon">❤️</text>
<text class="action-text">生命体征</text>
</button>
<button class="quick-action-btn" @click="showMedicationTasks">
<text class="action-icon">💊</text>
<text class="action-text">用药管理</text>
</button>
<button class="quick-action-btn" @click="showCareRecords">
<text class="action-icon">📋</text>
<text class="action-text">护理记录</text>
</button>
<button class="quick-action-btn" @click="showNursingPlans">
<text class="action-icon">📅</text>
<text class="action-text">护理计划</text>
</button>
<button class="quick-action-btn" @click="showIncidentReports">
<text class="action-icon">⚠️</text>
<text class="action-text">事件报告</text>
</button>
</view>
</view>
<!-- Today's Tasks -->
<view class="tasks-section">
<view class="section-header">
<text class="section-title">今日任务 ({{ todayTasks.length }})</text>
<view class="task-filters">
<button
class="filter-btn"
:class="{ active: taskFilter === 'all' }"
@click="setTaskFilter('all')"
>
<text class="filter-text">全部</text>
</button>
<button
class="filter-btn"
:class="{ active: taskFilter === 'pending' }"
@click="setTaskFilter('pending')"
>
<text class="filter-text">待处理</text>
</button>
<button
class="filter-btn"
:class="{ active: taskFilter === 'urgent' }"
@click="setTaskFilter('urgent')"
>
<text class="filter-text">紧急</text>
</button>
</view>
</view>
<scroll-view class="tasks-list" scroll-y="true" :style="{ height: '400px' }">
<view
v-for="task in filteredTasks"
:key="task.id"
class="task-item"
:class="{
'urgent': task.priority === 'urgent',
'high': task.priority === 'high',
'completed': task.status === 'completed',
'overdue': isTaskOverdue(task)
}"
@click="openTask(task)"
>
<view class="task-header">
<view class="task-info">
<text class="task-title">{{ task.task_name }}</text>
<text class="task-patient">{{ task.elder_name }}</text>
</view>
<view class="task-meta">
<text class="task-time">{{ formatTime(task.scheduled_time) }}</text>
<text class="task-priority" :class="task.priority">{{ getPriorityText(task.priority) }}</text>
</view>
</view>
<view class="task-content">
<text class="task-description">{{ task.description }}</text>
<text class="task-type">{{ getTaskTypeText(task.task_type) }}</text>
</view>
<view class="task-actions">
<button
v-if="task.status === 'pending'"
class="start-btn"
@click.stop="startTask(task)"
>
<text class="btn-text">开始</text>
</button>
<button
v-if="task.status === 'in_progress'"
class="complete-btn"
@click.stop="completeTask(task)"
>
<text class="btn-text">完成</text>
</button>
<button class="detail-btn" @click.stop="viewTaskDetail(task)">
<text class="btn-text">详情</text>
</button>
</view>
</view>
<view v-if="filteredTasks.length === 0" class="empty-state">
<text class="empty-text">暂无任务</text>
</view>
</scroll-view>
</view>
<!-- Patient Status Overview -->
<view class="patients-section">
<view class="section-header">
<text class="section-title">患者状态概览</text>
<button class="view-all-btn" @click="showAllPatients">
<text class="btn-text">查看全部</text>
</button>
</view>
<scroll-view class="patients-list" scroll-y="true" :style="{ height: '300px' }">
<view
v-for="patient in assignedPatients"
:key="patient.id"
class="patient-item"
:class="{ 'alert': hasHealthAlert(patient) }"
@click="viewPatientDetail(patient)"
>
<view class="patient-avatar">
<image
v-if="patient.profile_picture"
:src="patient.profile_picture"
class="avatar-image"
/>
<text v-else class="avatar-text">{{ getPatientInitial(patient.name) }}</text>
</view>
<view class="patient-info">
<text class="patient-name">{{ patient.name }}</text>
<text class="patient-room">{{ patient.room_number }}床</text>
<text class="patient-status">{{ getHealthStatusText(patient.health_status) }}</text>
</view>
<view class="patient-indicators">
<view v-if="hasVitalSignsAlert(patient)" class="indicator vital-alert">
<text class="indicator-text">⚠️</text>
</view>
<view v-if="hasMedicationDue(patient)" class="indicator medication-due">
<text class="indicator-text">💊</text>
</view>
<view v-if="hasHealthAlert(patient)" class="indicator health-alert">
<text class="indicator-text">🚨</text>
</view>
</view>
</view>
<view v-if="assignedPatients.length === 0" class="empty-state">
<text class="empty-text">暂无分配的患者</text>
</view>
</scroll-view>
</view>
<!-- Recent Activities -->
<view class="activities-section">
<view class="section-header">
<text class="section-title">最近活动</text>
<button class="view-all-btn" @click="showAllActivities">
<text class="btn-text">查看全部</text>
</button>
</view>
<scroll-view class="activities-list" scroll-y="true" :style="{ height: '250px' }">
<view
v-for="activity in recentActivities"
:key="activity.id"
class="activity-item"
@click="viewActivityDetail(activity)"
>
<view class="activity-icon">
<text class="icon-text">{{ getActivityIcon(activity.record_type) }}</text>
</view>
<view class="activity-content">
<text class="activity-title">{{ activity.description }}</text>
<text class="activity-patient">{{ activity.elder_name }}</text>
<text class="activity-time">{{ formatDateTime(activity.created_at) }}</text>
</view>
<view class="activity-type">
<text class="type-text">{{ getRecordTypeText(activity.record_type) }}</text>
</view>
</view>
<view v-if="recentActivities.length === 0" class="empty-state">
<text class="empty-text">暂无活动记录</text>
</view>
</scroll-view>
</view>
</view>
</template>
<script setup lang="uts">
import { ref, computed, onMounted, onUnmounted } from 'vue'
import supa from '@/components/supadb/aksupainstance.uts'
import {
formatTime,
formatDate,
formatDateTime,
getCurrentTimeString,
getTodayStart,
getTodayEnd,
getRecentDate,
getPriorityText,
getTaskTypeText,
getRecordTypeText,
getHealthStatusText
} from '../types_new.uts'
// 数据类型定义
type NurseStats = {
assigned_patients: number
completed_tasks: number
pending_tasks: number
urgent_tasks: number
}
type CareTask = {
id: string
elder_id: string
elder_name: string
caregiver_id: string
task_type: string
task_name: string
description: string
scheduled_time: string
estimated_duration: number
status: string
priority: string
completion_notes: string
completed_at: string
created_at: string
}
type Elder = {
id: string
name: string
room_number: string
bed_number: string
health_status: string
care_level: string
profile_picture: string
status: string
created_at: string
}
type CareRecord = {
id: string
elder_id: string
elder_name: string
caregiver_id: string
record_type: string
description: string
timestamp: string
duration: number
notes: string
rating: number
created_at: string
}
// 响应式数据
const currentTime = ref<string>('')
const stats = ref<NurseStats>({
assigned_patients: 0,
completed_tasks: 0,
pending_tasks: 0,
urgent_tasks: 0
})
const todayTasks = ref<Array<CareTask>>([])
const assignedPatients = ref<Array<Elder>>([])
const recentActivities = ref<Array<CareRecord>>([])
const taskFilter = ref<string>('all')
let timeInterval: number = 0
// 计算属性
const filteredTasks = computed<Array<CareTask>>(() => {
switch (taskFilter.value) {
case 'pending':
return todayTasks.value.filter(task => task.status === 'pending')
case 'urgent':
return todayTasks.value.filter(task => task.priority === 'urgent')
default:
return todayTasks.value
}
})
// 生命周期
onMounted(() => {
loadData()
updateCurrentTime()
timeInterval = setInterval(updateCurrentTime, 60000)
})
onUnmounted(() => {
if (timeInterval) {
clearInterval(timeInterval)
}
})
// 更新当前时间
const updateCurrentTime = () => {
currentTime.value = getCurrentTimeString()
}
// 加载数据
const loadData = async () => {
await Promise.all([
loadStats(),
loadTodayTasks(),
loadAssignedPatients(),
loadRecentActivities()
])
}
// 加载统计数据
const loadStats = async () => {
try {
// 分配的患者数量
const patientsResult = await supa
.from('ec_elders')
.select('*', { count: 'exact' })
.eq('assigned_nurse', getUserId())
.eq('status', 'active')
.executeAs<Elder[]>()
// 今日完成任务数
const completedResult = await supa
.from('ec_care_tasks')
.select('*', { count: 'exact' })
.eq('caregiver_id', getUserId())
.eq('status', 'completed')
.gte('completed_at', getTodayStart())
.lte('completed_at', getTodayEnd())
.executeAs<CareTask[]>()
// 待处理任务数
const pendingResult = await supa
.from('ec_care_tasks')
.select('*', { count: 'exact' })
.eq('caregiver_id', getUserId())
.eq('status', 'pending')
.executeAs<CareTask[]>()
// 紧急任务数
const urgentResult = await supa
.from('ec_care_tasks')
.select('*', { count: 'exact' })
.eq('caregiver_id', getUserId())
.eq('priority', 'urgent')
.in('status', ['pending', 'in_progress'])
.executeAs<CareTask[]>()
stats.value = {
assigned_patients: patientsResult.count ?? 0,
completed_tasks: completedResult.count ?? 0,
pending_tasks: pendingResult.count ?? 0,
urgent_tasks: urgentResult.count ?? 0
}
} catch (error) {
console.error('加载统计数据失败:', error)
}
}
// 加载今日任务
const loadTodayTasks = async () => {
try {
const result = await supa
.from('ec_care_tasks')
.select(`
id,
elder_id,
elder_name,
caregiver_id,
task_type,
task_name,
description,
scheduled_time,
estimated_duration,
status,
priority,
completion_notes,
completed_at,
created_at
`)
.eq('caregiver_id', getUserId())
.gte('scheduled_time', getTodayStart())
.lte('scheduled_time', getTodayEnd())
.order('scheduled_time', { ascending: true })
.executeAs<CareTask[]>()
if (result.error === null && result.data !== null) {
todayTasks.value = result.data
}
} catch (error) {
console.error('加载今日任务失败:', error)
}
}
// 加载分配的患者
const loadAssignedPatients = async () => {
try {
const result = await supa
.from('ec_elders')
.select(`
id,
name,
room_number,
bed_number,
health_status,
care_level,
profile_picture,
status,
created_at
`)
.eq('assigned_nurse', getUserId())
.eq('status', 'active')
.order('room_number', { ascending: true })
.executeAs<Elder[]>()
if (result.error === null && result.data !== null) {
assignedPatients.value = result.data
}
} catch (error) {
console.error('加载分配患者失败:', error)
}
}
// 加载最近活动
const loadRecentActivities = async () => {
try {
const result = await supa
.from('ec_care_records')
.select(`
id,
elder_id,
elder_name,
caregiver_id,
record_type,
description,
timestamp,
duration,
notes,
rating,
created_at
`)
.eq('caregiver_id', getUserId())
.gte('created_at', getRecentDate(3))
.order('created_at', { ascending: false })
.limit(10)
.executeAs<CareRecord[]>()
if (result.error === null && result.data !== null) {
recentActivities.value = result.data
}
} catch (error) {
console.error('加载最近活动失败:', error)
}
}
// 辅助函数
const getUserId = (): string => {
// 从全局状态或存储中获取当前用户ID
return 'current_nurse_id'
}
const isTaskOverdue = (task: CareTask): boolean => {
if (task.status === 'completed') return false
const now = new Date()
const scheduledTime = new Date(task.scheduled_time)
return now > scheduledTime
}
const getPatientInitial = (name: string): string => {
return name.charAt(0).toUpperCase()
}
const hasHealthAlert = (patient: Elder): boolean => {
// 这里应该查询患者是否有活跃的健康提醒
return Math.random() > 0.8 // 示例
}
const hasVitalSignsAlert = (patient: Elder): boolean => {
// 这里应该查询患者是否有异常的生命体征
return Math.random() > 0.9 // 示例
}
const hasMedicationDue = (patient: Elder): boolean => {
// 这里应该查询患者是否有即将到期的用药
return Math.random() > 0.7 // 示例
}
const getActivityIcon = (recordType: string): string => {
const iconMap = new Map([
['medication', '💊'],
['hygiene', '🛁'],
['mobility', '🚶'],
['nutrition', '🍽️'],
['social', '👥'],
['medical', '🏥'],
['vital_signs', '❤️'],
['activity', '🎯']
])
return iconMap.get(recordType) ?? '📋'
}
// 事件处理
const setTaskFilter = (filter: string) => {
taskFilter.value = filter
}
const showUrgentTasks = () => {
taskFilter.value = 'urgent'
}
const showVitalSignsEntry = () => {
uni.navigateTo({ url: '/pages/ec/nurse/vital-signs-entry' })
}
const showPatientList = () => {
uni.navigateTo({ url: '/pages/ec/nurse/patient-list' })
}
const showVitalSigns = () => {
uni.navigateTo({ url: '/pages/ec/nurse/vital-signs' })
}
const showMedicationTasks = () => {
uni.navigateTo({ url: '/pages/ec/nurse/medication-tasks' })
}
const showCareRecords = () => {
uni.navigateTo({ url: '/pages/ec/nurse/care-records' })
}
const showNursingPlans = () => {
uni.navigateTo({ url: '/pages/ec/nurse/nursing-plans' })
}
const showIncidentReports = () => {
uni.navigateTo({ url: '/pages/ec/nurse/incident-reports' })
}
const showAllPatients = () => {
uni.navigateTo({ url: '/pages/ec/nurse/patient-list' })
}
const showAllActivities = () => {
uni.navigateTo({ url: '/pages/ec/nurse/activities' })
}
const openTask = (task: CareTask) => {
uni.navigateTo({
url: `/pages/ec/nurse/task-detail?id=${task.id}`
})
}
const startTask = async (task: CareTask) => {
try {
await supa
.from('ec_care_tasks')
.update({ status: 'in_progress' })
.eq('id', task.id)
.executeAs<any>()
// 更新本地状态
let taskIndex = -1
for (let i: Int = 0; i < todayTasks.value.length; i++) {
if (todayTasks.value[i].id === task.id) {
taskIndex = i
break
}
}
if (taskIndex >= 0) {
todayTasks.value[taskIndex].status = 'in_progress'
}
uni.showToast({ title: '任务已开始', icon: 'success' })
} catch (error) {
console.error('开始任务失败:', error)
uni.showToast({ title: '操作失败', icon: 'error' })
}
}
const completeTask = async (task: CareTask) => {
try {
await supa
.from('ec_care_tasks')
.update({
status: 'completed',
completed_at: new Date().toISOString()
})
.eq('id', task.id)
.executeAs<any>()
// 更新本地状态
let taskIndex = -1
for (let i: Int = 0; i < todayTasks.value.length; i++) {
if (todayTasks.value[i].id === task.id) {
taskIndex = i
break
}
}
if (taskIndex >= 0) {
todayTasks.value[taskIndex].status = 'completed'
todayTasks.value[taskIndex].completed_at = new Date().toISOString()
}
// 更新统计
stats.value.completed_tasks++
stats.value.pending_tasks = Math.max(0, stats.value.pending_tasks - 1)
uni.showToast({ title: '任务已完成', icon: 'success' })
} catch (error) {
console.error('完成任务失败:', error)
uni.showToast({ title: '操作失败', icon: 'error' })
}
}
const viewTaskDetail = (task: CareTask) => {
uni.navigateTo({
url: `/pages/ec/nurse/task-detail?id=${task.id}`
})
}
const viewPatientDetail = (patient: Elder) => {
uni.navigateTo({
url: `/pages/ec/nurse/patient-detail?id=${patient.id}`
})
}
const viewActivityDetail = (activity: CareRecord) => {
uni.navigateTo({
url: `/pages/ec/nurse/activity-detail?id=${activity.id}`
})
}
</script>
<style lang="scss">
/* uts-android 兼容性重构:
1. 移除所有嵌套选择器、伪类(如 :last-child、&.xxx全部 class 扁平化。
2. 所有间距用 margin-right/margin-bottom 控制,禁止 gap、flex-wrap、嵌套。
3. 所有布局 display: flex禁止 grid、gap、伪类。
4. 组件间距、分隔线全部用 border/margin 控制。
5. 新增.is-last、.is-active、.is-urgent、.is-high、.is-completed、.is-overdue 等辅助 class。
*/
.nurse-dashboard {
padding: 20px;
background-color: #f5f5f5;
min-height: 600px;
}
.header {
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: center;
margin-bottom: 30px;
padding: 20px;
background: linear-gradient(135deg, #56ccf2 0%, #2f80ed 100%);
border-radius: 15px;
color: white;
}
.header-title {
font-size: 24px;
font-weight: bold;
}
.header-subtitle {
font-size: 14px;
opacity: 0.8;
margin-top: 5px;
}
.header-actions {
display: flex;
flex-direction: row;
}
.action-btn {
padding: 8px 16px;
border-radius: 20px;
border: 2px solid rgba(255, 255, 255, 0.3);
background-color: rgba(255, 255, 255, 0.1);
color: white;
margin-right: 10px;
}
.action-btn.is-last {
margin-right: 0;
}
.action-btn.is-urgent {
background-color: #ff4757;
border-color: #ff4757;
}
.btn-text {
font-size: 14px;
font-weight: 500;
}
.stats-section {
display: flex;
flex-direction: row;
flex-wrap: wrap;
margin-bottom: 30px;
}
.stat-card {
flex: 1 1 200rpx;
min-width: 120px;
padding: 20px;
background-color: white;
border-radius: 12px;
margin-right: 15px;
margin-bottom: 15px;
display: flex;
align-items: center;
}
.stat-card.is-last {
margin-right: 0;
}
.stat-card.is-urgent {
background: linear-gradient(135deg, #ff6b6b 0%, #ee5a6f 100%);
color: white;
}
.stat-icon {
font-size: 24px;
width: 40px;
height: 40px;
display: flex;
align-items: center;
justify-content: center;
background-color: rgba(0, 0, 0, 0.1);
border-radius: 50%;
margin-right: 15px;
}
.stat-content {
flex: 1;
}
.stat-number {
font-size: 28px;
font-weight: bold;
display: block;
}
.stat-label {
font-size: 14px;
opacity: 0.7;
margin-top: 5px;
}
.quick-actions {
margin-bottom: 30px;
}
.section-title {
font-size: 18px;
font-weight: 600;
margin-bottom: 15px;
color: #333;
}
.actions-grid {
display: flex;
flex-direction: row;
flex-wrap: wrap;
}
.quick-action-btn {
flex: 1;
min-width: 120px;
padding: 20px;
background-color: white;
border-radius: 12px;
margin-right: 15px;
margin-bottom: 15px;
border: none;
display: flex;
flex-direction: column;
align-items: center;
}
.quick-action-btn.is-last {
margin-right: 0;
}
.action-icon {
font-size: 24px;
}
.action-text {
font-size: 14px;
color: #666;
}
.tasks-section, .patients-section, .activities-section {
margin-bottom: 30px;
}
.section-header {
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: center;
margin-bottom: 15px;
}
.task-filters {
display: flex;
flex-direction: row;
}
.filter-btn {
padding: 6px 12px;
border-radius: 15px;
border: 1px solid #ddd;
background-color: white;
color: #666;
margin-right: 8px;
}
.filter-btn.is-active {
background-color: #2f80ed;
color: white;
border-color: #2f80ed;
}
.filter-btn.is-last {
margin-right: 0;
}
.filter-text {
font-size: 12px;
}
.view-all-btn {
padding: 6px 12px;
border-radius: 15px;
border: 1px solid #ddd;
background-color: white;
color: #666;
}
.tasks-list, .patients-list, .activities-list {
background-color: white;
border-radius: 12px;
}
.task-item {
padding: 20px;
border-bottom: 1px solid #f0f0f0;
}
.task-item.is-last {
border-bottom: none;
}
.task-item.is-urgent {
background-color: #fff2f0;
border-left: 4px solid #ff4d4f;
}
.task-item.is-high {
background-color: #fffbf0;
border-left: 4px solid #faad14;
}
.task-item.is-completed {
opacity: 0.6;
background-color: #f6ffed;
}
.task-item.is-overdue {
background-color: #fff1f0;
border-left: 4px solid #f5222d;
}
.task-header {
display: flex;
flex-direction: row;
justify-content: space-between;
align-items: flex-start;
margin-bottom: 10px;
}
.task-info {
flex: 1;
}
.task-title {
font-size: 16px;
font-weight: 600;
color: #333;
display: block;
}
.task-patient {
font-size: 14px;
color: #666;
margin-top: 5px;
display: block;
}
.task-meta {
text-align: right;
}
.task-time {
font-size: 14px;
color: #666;
display: block;
}
.task-priority {
font-size: 12px;
margin-top: 5px;
display: block;
}
.task-priority.is-urgent { color: #ff4d4f; }
.task-priority.is-high { color: #faad14; }
.task-priority.is-medium { color: #1890ff; }
.task-priority.is-low { color: #52c41a; }
.task-content {
margin-bottom: 15px;
}
.task-description {
font-size: 14px;
color: #555;
display: block;
margin-bottom: 5px;
}
.task-type {
font-size: 12px;
color: #888;
}
.task-actions {
display: flex;
flex-direction: row;
margin-bottom: 10px;
}
.start-btn, .complete-btn, .detail-btn {
padding: 6px 12px;
border-radius: 15px;
border: none;
font-size: 12px;
color: white;
margin-right: 10px;
}
.start-btn {
background-color: #52c41a;
}
.complete-btn {
background-color: #1890ff;
}
.detail-btn {
background-color: #d9d9d9;
color: #666;
}
.patient-item {
padding: 15px;
border-bottom: 1px solid #f0f0f0;
display: flex;
flex-direction: row;
align-items: center;
margin-bottom: 10px;
}
.patient-item.is-last {
border-bottom: none;
}
.patient-item.is-alert {
background-color: #fff7e6;
border-left: 4px solid #faad14;
}
.patient-avatar {
width: 50px;
height: 50px;
border-radius: 50%;
background-color: #f0f0f0;
display: flex;
align-items: center;
justify-content: center;
overflow: hidden;
margin-right: 15px;
}
.avatar-image {
width: 100%;
height: 100%;
object-fit: cover;
}
.avatar-text {
font-size: 18px;
font-weight: 600;
color: #666;
}
.patient-info {
flex: 1;
}
.patient-name {
font-size: 16px;
font-weight: 600;
color: #333;
display: block;
}
.patient-room {
font-size: 14px;
color: #666;
margin-top: 5px;
display: block;
}
.patient-status {
font-size: 12px;
color: #888;
margin-top: 5px;
display: block;
}
.patient-indicators {
display: flex;
flex-direction: row;
margin-left: 10px;
}
.indicator {
width: 24px;
height: 24px;
border-radius: 50%;
display: flex;
align-items: center;
justify-content: center;
margin-right: 5px;
}
.indicator.is-last {
margin-right: 0;
}
.indicator.is-vital-alert {
background-color: #fff7e6;
}
.indicator.is-medication-due {
background-color: #e6f7ff;
}
.indicator.is-health-alert {
background-color: #fff2f0;
}
.activity-item {
padding: 15px;
border-bottom: 1px solid #f0f0f0;
display: flex;
flex-direction: row;
align-items: center;
margin-bottom: 10px;
}
.activity-item.is-last {
border-bottom: none;
}
.activity-icon {
width: 40px;
height: 40px;
border-radius: 50%;
background-color: #f0f0f0;
display: flex;
align-items: center;
justify-content: center;
margin-right: 15px;
}
.icon-text {
font-size: 18px;
}
.activity-content {
flex: 1;
}
.activity-title {
font-size: 14px;
color: #333;
display: block;
}
.activity-patient {
font-size: 13px;
color: #666;
margin-top: 3px;
display: block;
}
.activity-time {
font-size: 12px;
color: #888;
margin-top: 3px;
display: block;
}
.activity-type {
margin-left: 10px;
}
.type-text {
font-size: 12px;
color: #888;
background-color: #f5f5f5;
padding: 2px 8px;
border-radius: 10px;
}
.empty-state {
padding: 40px;
text-align: center;
}
.empty-text {
font-size: 14px;
color: #999;
}
</style>