refactor(功能模块): 将"时间银行"重命名为"互助中心"并完善相关功能
重构项目中的"时间银行"模块,统一更名为"互助中心",涉及前端路由、组件、文档及多处文本替换。新增互助中心页面功能,包括: 1. 通证兑换服务弹窗 2. 互助任务列表展示 3. 区块链存证交互流程 同时优化护理记录页面,增加筛选、导出功能,完善监管端仪表盘交互
This commit is contained in:
@@ -26,15 +26,18 @@
|
||||
<div class="flex items-center space-x-4">
|
||||
<div class="relative">
|
||||
<span class="absolute left-3 top-1/2 -translate-y-1/2 text-gray-400 text-xs">🔍</span>
|
||||
<input type="text" placeholder="搜索老人姓名/房间号..." class="pl-9 pr-4 py-2 bg-gray-50 border-none rounded-lg text-sm focus:ring-2 focus:ring-blue-500 w-64">
|
||||
<input type="text" v-model="searchQuery" placeholder="搜索老人姓名/房间号..." class="pl-9 pr-4 py-2 bg-gray-50 border-none rounded-lg text-sm focus:ring-2 focus:ring-blue-500 w-64 transition-all">
|
||||
</div>
|
||||
<select class="bg-gray-50 border-none rounded-lg text-sm px-4 py-2 focus:ring-2 focus:ring-blue-500">
|
||||
<option>全部记录类型</option>
|
||||
<option>生活护理</option>
|
||||
<option>医疗协助</option>
|
||||
<option>心理慰藉</option>
|
||||
<option>日常巡查</option>
|
||||
<select v-model="filterType" class="bg-gray-50 border-none rounded-lg text-sm px-4 py-2 focus:ring-2 focus:ring-blue-500 transition-all cursor-pointer">
|
||||
<option value="all">全部记录类型</option>
|
||||
<option value="生活护理">生活护理</option>
|
||||
<option value="医疗协助">医疗协助</option>
|
||||
<option value="心理慰藉">心理慰藉</option>
|
||||
<option value="日常巡查">日常巡查</option>
|
||||
</select>
|
||||
<button @click="exportRecords" class="px-4 py-2 bg-gray-50 hover:bg-gray-100 text-gray-600 rounded-lg text-sm font-bold transition-colors border border-gray-100 flex items-center">
|
||||
<span class="mr-2">📥</span> 导出数据
|
||||
</button>
|
||||
</div>
|
||||
<div class="flex items-center space-x-2">
|
||||
<span class="text-xs text-gray-400">数据状态:</span>
|
||||
@@ -49,60 +52,63 @@
|
||||
<div class="grid grid-cols-1 lg:grid-cols-3 gap-8">
|
||||
<div class="lg:col-span-2 space-y-6">
|
||||
<h3 class="text-lg font-bold text-gray-800 flex items-center px-1">
|
||||
<span class="mr-3 text-2xl">📅</span> 日常护理日志
|
||||
<span class="mr-3 text-2xl">📅</span> 日常护理日志
|
||||
<span class="ml-3 text-xs bg-gray-100 text-gray-500 px-2 py-1 rounded-full">{{ filteredRecords.length }} 条记录</span>
|
||||
</h3>
|
||||
<div v-for="record in records" :key="record.id"
|
||||
class="bg-white rounded-3xl border border-gray-100 shadow-sm hover:shadow-xl hover:-translate-y-1 transition-all group overflow-hidden">
|
||||
<div class="flex">
|
||||
<!-- Side Indicator -->
|
||||
<div class="w-2.5" :class="getTypeColor(record.type)"></div>
|
||||
|
||||
<div class="flex-1 p-6 md:p-8 flex flex-col md:flex-row md:items-center justify-between gap-6">
|
||||
<div class="flex items-center space-x-6">
|
||||
<!-- Elder Info -->
|
||||
<div class="flex items-center space-x-4">
|
||||
<div class="w-16 h-16 rounded-2xl bg-gray-50 flex items-center justify-center text-3xl shadow-inner border border-gray-100">
|
||||
{{ record.avatar }}
|
||||
<TransitionGroup name="list" tag="div" class="space-y-6">
|
||||
<div v-for="record in filteredRecords" :key="record.id"
|
||||
class="bg-white rounded-3xl border border-gray-100 shadow-sm hover:shadow-xl hover:-translate-y-1 transition-all group overflow-hidden">
|
||||
<div class="flex">
|
||||
<!-- Side Indicator -->
|
||||
<div class="w-2.5" :class="getTypeColor(record.type)"></div>
|
||||
|
||||
<div class="flex-1 p-6 md:p-8 flex flex-col md:flex-row md:items-center justify-between gap-6">
|
||||
<div class="flex items-center space-x-6">
|
||||
<!-- Elder Info -->
|
||||
<div class="flex items-center space-x-4">
|
||||
<div class="w-16 h-16 rounded-2xl bg-gray-50 flex items-center justify-center text-3xl shadow-inner border border-gray-100">
|
||||
{{ record.avatar }}
|
||||
</div>
|
||||
<div>
|
||||
<div class="font-black text-gray-900 text-lg leading-tight">{{ record.elder }}</div>
|
||||
<div class="text-sm text-gray-400 font-bold mt-0.5">{{ record.room }}</div>
|
||||
</div>
|
||||
</div>
|
||||
<div>
|
||||
<div class="font-black text-gray-900 text-lg leading-tight">{{ record.elder }}</div>
|
||||
<div class="text-sm text-gray-400 font-bold mt-0.5">{{ record.room }}</div>
|
||||
|
||||
<div class="hidden md:block h-12 w-px bg-gray-100"></div>
|
||||
|
||||
<!-- Content -->
|
||||
<div class="flex-1 min-w-[300px]">
|
||||
<div class="flex items-center space-x-3 mb-2">
|
||||
<span class="text-xs font-black px-3 py-1 rounded-full uppercase tracking-wider" :class="getTypeBadge(record.type)">
|
||||
{{ record.type }}
|
||||
</span>
|
||||
<span class="text-lg font-black text-gray-800">{{ record.title }}</span>
|
||||
</div>
|
||||
<p class="text-sm text-gray-500 leading-relaxed font-medium">{{ record.content }}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="hidden md:block h-12 w-px bg-gray-100"></div>
|
||||
|
||||
<!-- Content -->
|
||||
<div class="flex-1 min-w-[300px]">
|
||||
<div class="flex items-center space-x-3 mb-2">
|
||||
<span class="text-xs font-black px-3 py-1 rounded-full uppercase tracking-wider" :class="getTypeBadge(record.type)">
|
||||
{{ record.type }}
|
||||
</span>
|
||||
<span class="text-lg font-black text-gray-800">{{ record.title }}</span>
|
||||
</div>
|
||||
<p class="text-sm text-gray-500 leading-relaxed font-medium">{{ record.content }}</p>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- Blockchain & Time Info -->
|
||||
<div class="flex items-center justify-between md:justify-end space-x-6 border-t md:border-t-0 pt-4 md:pt-0">
|
||||
<div class="text-right">
|
||||
<div class="text-xs font-black text-gray-300 uppercase tracking-widest mb-1">Entry Time</div>
|
||||
<div class="text-sm font-bold text-gray-600">{{ record.time }}</div>
|
||||
<!-- Blockchain & Time Info -->
|
||||
<div class="flex items-center justify-between md:justify-end space-x-6 border-t md:border-t-0 pt-4 md:pt-0">
|
||||
<div class="text-right">
|
||||
<div class="text-xs font-black text-gray-300 uppercase tracking-widest mb-1">Entry Time</div>
|
||||
<div class="text-sm font-bold text-gray-600">{{ record.time }}</div>
|
||||
</div>
|
||||
|
||||
<div class="bg-blue-50/80 px-4 py-2 rounded-xl border border-blue-100/50 hidden xl:block">
|
||||
<div class="text-[10px] text-blue-400 font-black uppercase tracking-widest mb-0.5">Chain Hash</div>
|
||||
<div class="text-xs font-mono text-blue-600 font-bold">{{ record.hash }}</div>
|
||||
</div>
|
||||
|
||||
<button class="w-12 h-12 bg-gray-50 hover:bg-blue-600 hover:text-white rounded-2xl transition-all flex items-center justify-center group/btn shadow-sm">
|
||||
<span class="text-gray-400 group-hover/btn:text-white text-xl transition-colors">➡️</span>
|
||||
</button>
|
||||
</div>
|
||||
|
||||
<div class="bg-blue-50/80 px-4 py-2 rounded-xl border border-blue-100/50 hidden xl:block">
|
||||
<div class="text-[10px] text-blue-400 font-black uppercase tracking-widest mb-0.5">Chain Hash</div>
|
||||
<div class="text-xs font-mono text-blue-600 font-bold">{{ record.hash }}</div>
|
||||
</div>
|
||||
|
||||
<button class="w-12 h-12 bg-gray-50 hover:bg-blue-600 hover:text-white rounded-2xl transition-all flex items-center justify-center group/btn shadow-sm">
|
||||
<span class="text-gray-400 group-hover/btn:text-white text-xl transition-colors">➡️</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</TransitionGroup>
|
||||
</div>
|
||||
|
||||
<!-- Blockchain Audit Logs (From Global Store) -->
|
||||
@@ -186,15 +192,15 @@
|
||||
<div class="grid grid-cols-2 gap-4">
|
||||
<div>
|
||||
<label class="block text-xs font-bold text-gray-400 uppercase mb-1.5">老人姓名</label>
|
||||
<select class="w-full bg-gray-50 border-none rounded-xl text-sm px-4 py-3">
|
||||
<option>张大爷 (302室)</option>
|
||||
<option>李奶奶 (105室)</option>
|
||||
<option>王阿姨 (208室)</option>
|
||||
<select v-model="newRecord.elder" class="w-full bg-gray-50 border-none rounded-xl text-sm px-4 py-3">
|
||||
<option value="张大爷 (302室)">张大爷 (302室)</option>
|
||||
<option value="李奶奶 (105室)">李奶奶 (105室)</option>
|
||||
<option value="王阿姨 (208室)">王阿姨 (208室)</option>
|
||||
</select>
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-xs font-bold text-gray-400 uppercase mb-1.5">记录类型</label>
|
||||
<select class="w-full bg-gray-50 border-none rounded-xl text-sm px-4 py-3">
|
||||
<select v-model="newRecord.type" class="w-full bg-gray-50 border-none rounded-xl text-sm px-4 py-3">
|
||||
<option>生活护理</option>
|
||||
<option>医疗协助</option>
|
||||
<option>康复训练</option>
|
||||
@@ -203,11 +209,11 @@
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-xs font-bold text-gray-400 uppercase mb-1.5">记录标题</label>
|
||||
<input type="text" placeholder="例如: 完成晨间洗漱" class="w-full bg-gray-50 border-none rounded-xl text-sm px-4 py-3">
|
||||
<input type="text" v-model="newRecord.title" placeholder="例如: 完成晨间洗漱" class="w-full bg-gray-50 border-none rounded-xl text-sm px-4 py-3">
|
||||
</div>
|
||||
<div>
|
||||
<label class="block text-xs font-bold text-gray-400 uppercase mb-1.5">详细内容</label>
|
||||
<textarea rows="3" placeholder="请详细描述护理过程及观察到的状况..." class="w-full bg-gray-50 border-none rounded-xl text-sm px-4 py-3"></textarea>
|
||||
<textarea v-model="newRecord.content" rows="3" placeholder="请详细描述护理过程及观察到的状况..." class="w-full bg-gray-50 border-none rounded-xl text-sm px-4 py-3"></textarea>
|
||||
</div>
|
||||
<div class="bg-blue-50 p-3 rounded-xl border border-blue-100">
|
||||
<div class="flex items-center space-x-2">
|
||||
@@ -230,93 +236,141 @@
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ref } from 'vue'
|
||||
import { ref, computed } from 'vue'
|
||||
import { useGlobalStore } from '../../stores/global'
|
||||
|
||||
const store = useGlobalStore()
|
||||
const showAddModal = ref(false)
|
||||
const searchQuery = ref('')
|
||||
const filterType = ref('all')
|
||||
|
||||
const recordStats = [
|
||||
{ label: '今日新增记录', value: '42', trend: '+12%' },
|
||||
{ label: '待审计记录', value: '5', trend: '-20%' },
|
||||
{ label: '本月服务总时数', value: '1,280h', trend: '+5%' },
|
||||
{ label: '家属满意度', value: '4.9', trend: '稳定' }
|
||||
]
|
||||
|
||||
const records = ref([
|
||||
{
|
||||
id: 1,
|
||||
elder: '张大爷',
|
||||
room: '302室',
|
||||
avatar: '👴',
|
||||
type: '医疗协助',
|
||||
title: '按时服用降压药',
|
||||
content: '观察血压 135/85 mmHg,精神状态良好,无不适反应。',
|
||||
time: '今天 08:15',
|
||||
staff: '王护理员',
|
||||
hash: '0x8f2a...9b1c'
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
elder: '李奶奶',
|
||||
room: '105室',
|
||||
avatar: '👵',
|
||||
type: '生活护理',
|
||||
title: '完成午餐进食',
|
||||
content: '进食量 250g,摄入水分 150ml,自主进食过程顺畅。',
|
||||
time: '今天 12:30',
|
||||
staff: '张护理员',
|
||||
hash: '0x3d4e...1f9a'
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
elder: '王阿姨',
|
||||
room: '208室',
|
||||
avatar: '👩🦳',
|
||||
type: '康复训练',
|
||||
title: '室内行走练习',
|
||||
content: '在护理员辅助下行走 200 米,步态较稳,心率正常。',
|
||||
time: '昨天 16:45',
|
||||
staff: '李康复师',
|
||||
hash: '0x7a8b...2c3d'
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
elder: '赵大爷',
|
||||
room: '401室',
|
||||
avatar: '👴',
|
||||
type: '心理慰藉',
|
||||
title: '情感交流与谈心',
|
||||
content: '与其交流家乡趣事,老人情绪明显好转,积极参加后续活动。',
|
||||
time: '昨天 10:20',
|
||||
staff: '张护理员',
|
||||
hash: '0x5e4d...3f2b'
|
||||
}
|
||||
const recordStats = ref([
|
||||
{ label: '今日记录总数', value: '42', trend: '+12%', trendUp: true },
|
||||
{ label: '异常护理项', value: '3', trend: '-1', trendUp: false },
|
||||
{ label: '区块链确认', value: '100%', trend: '稳定', trendUp: true },
|
||||
{ label: '服务满意度', value: '4.9', trend: '+0.1', trendUp: true },
|
||||
])
|
||||
|
||||
const records = ref([
|
||||
{
|
||||
id: 1,
|
||||
elder: '王建国',
|
||||
room: '201室',
|
||||
avatar: '👴',
|
||||
type: '生活护理',
|
||||
title: '完成晨间助浴与更衣',
|
||||
content: '老人精神状态良好,配合度高。助浴过程中检查皮肤无异常,已更换干净衣物。',
|
||||
time: '08:30',
|
||||
hash: '0x7a8...9b2'
|
||||
},
|
||||
{
|
||||
id: 2,
|
||||
elder: '李秀兰',
|
||||
room: '203室',
|
||||
avatar: '👵',
|
||||
type: '医疗协助',
|
||||
title: '协助服用降压药',
|
||||
content: '血压测量结果 135/85,遵医嘱协助服用降压药一粒。观察15分钟无不良反应。',
|
||||
time: '09:15',
|
||||
hash: '0x3c4...1f9'
|
||||
},
|
||||
{
|
||||
id: 3,
|
||||
elder: '张德福',
|
||||
room: '205室',
|
||||
avatar: '<27>',
|
||||
type: '日常巡查',
|
||||
title: '房间环境安全检查',
|
||||
content: '地面干燥无积水,呼叫器功能正常,通风状况良好。',
|
||||
time: '10:00',
|
||||
hash: '0x8d2...4e6'
|
||||
},
|
||||
{
|
||||
id: 4,
|
||||
elder: '赵淑芬',
|
||||
room: '202室',
|
||||
avatar: '<27>',
|
||||
type: '心理慰藉',
|
||||
title: '情绪疏导与陪伴',
|
||||
content: '老人因想念子女情绪低落,陪同聊天30分钟,情绪明显好转。',
|
||||
time: '11:20',
|
||||
hash: '0x5b1...8a3'
|
||||
},
|
||||
])
|
||||
|
||||
const filteredRecords = computed(() => {
|
||||
return records.value.filter(record => {
|
||||
const matchQuery = record.elder.includes(searchQuery.value) || record.room.includes(searchQuery.value)
|
||||
const matchType = filterType.value === 'all' || record.type === filterType.value
|
||||
return matchQuery && matchType
|
||||
})
|
||||
})
|
||||
|
||||
const exportRecords = () => {
|
||||
const csvContent = "data:text/csv;charset=utf-8,"
|
||||
+ "ID,老人,房间,类型,标题,时间,Hash\n"
|
||||
+ filteredRecords.value.map(e => `${e.id},${e.elder},${e.room},${e.type},${e.title},${e.time},${e.hash}`).join("\n");
|
||||
|
||||
const encodedUri = encodeURI(csvContent);
|
||||
const link = document.createElement("a");
|
||||
link.setAttribute("href", encodedUri);
|
||||
link.setAttribute("download", "nursing_records_export.csv");
|
||||
document.body.appendChild(link);
|
||||
link.click();
|
||||
document.body.removeChild(link);
|
||||
|
||||
alert(`成功导出 ${filteredRecords.value.length} 条护理记录!`)
|
||||
}
|
||||
|
||||
const newRecord = ref({
|
||||
elder: '',
|
||||
type: '生活护理',
|
||||
title: '',
|
||||
content: ''
|
||||
})
|
||||
|
||||
const submitRecord = () => {
|
||||
// Simulate Blockchain Transaction
|
||||
const newId = records.value.length + 1
|
||||
const mockHash = '0x' + Math.random().toString(16).slice(2, 10) + '...' + Math.random().toString(16).slice(2, 6)
|
||||
|
||||
records.value.unshift({
|
||||
id: newId,
|
||||
elder: newRecord.value.elder || '未知老人',
|
||||
room: '待定',
|
||||
avatar: '👤',
|
||||
type: newRecord.value.type,
|
||||
title: newRecord.value.title || '新提交护理记录',
|
||||
content: newRecord.value.content,
|
||||
time: new Date().toLocaleTimeString([], {hour: '2-digit', minute:'2-digit'}),
|
||||
hash: mockHash
|
||||
})
|
||||
|
||||
showAddModal.value = false
|
||||
newRecord.value = { elder: '', type: '生活护理', title: '', content: '' }
|
||||
|
||||
// Show success toast (simulated)
|
||||
alert(`记录已上链存证!\nHash: ${mockHash}`)
|
||||
}
|
||||
|
||||
const getTypeColor = (type) => {
|
||||
switch (type) {
|
||||
case '医疗协助': return 'bg-red-500'
|
||||
case '生活护理': return 'bg-green-500'
|
||||
case '康复训练': return 'bg-blue-500'
|
||||
case '心理慰藉': return 'bg-purple-500'
|
||||
default: return 'bg-gray-500'
|
||||
const colors = {
|
||||
'生活护理': 'bg-blue-500',
|
||||
'医疗协助': 'bg-red-500',
|
||||
'心理慰藉': 'bg-purple-500',
|
||||
'日常巡查': 'bg-green-500'
|
||||
}
|
||||
return colors[type] || 'bg-gray-500'
|
||||
}
|
||||
|
||||
const getTypeBadge = (type) => {
|
||||
switch (type) {
|
||||
case '医疗协助': return 'bg-red-50 text-red-600'
|
||||
case '生活护理': return 'bg-green-50 text-green-600'
|
||||
case '康复训练': return 'bg-blue-50 text-blue-600'
|
||||
case '心理慰藉': return 'bg-purple-50 text-purple-600'
|
||||
default: return 'bg-gray-50 text-gray-600'
|
||||
const badges = {
|
||||
'生活护理': 'bg-blue-100 text-blue-600',
|
||||
'医疗协助': 'bg-red-100 text-red-600',
|
||||
'心理慰藉': 'bg-purple-100 text-purple-600',
|
||||
'日常巡查': 'bg-green-100 text-green-600'
|
||||
}
|
||||
}
|
||||
|
||||
const submitRecord = () => {
|
||||
alert('记录已成功提交!\n1. 区块链存证已完成\n2. 已同步至家属端 APP\n3. 系统已自动计算本次服务积分')
|
||||
showAddModal.value = false
|
||||
return badges[type] || 'bg-gray-100 text-gray-600'
|
||||
}
|
||||
</script>
|
||||
|
||||
@@ -337,4 +391,20 @@ const submitRecord = () => {
|
||||
.animate-bounce-in {
|
||||
animation: bounce-in 0.3s cubic-bezier(0.175, 0.885, 0.32, 1.275);
|
||||
}
|
||||
|
||||
.list-move,
|
||||
.list-enter-active,
|
||||
.list-leave-active {
|
||||
transition: all 0.5s ease;
|
||||
}
|
||||
|
||||
.list-enter-from,
|
||||
.list-leave-to {
|
||||
opacity: 0;
|
||||
transform: translateX(30px);
|
||||
}
|
||||
|
||||
.list-leave-active {
|
||||
position: absolute;
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user