1069 lines
29 KiB
Plaintext
1069 lines
29 KiB
Plaintext
<!-- 老人档案管理 - 重构版本 -->
|
||
<template>
|
||
<view class="elder-profile">
|
||
<!-- Header -->
|
||
<view class="header">
|
||
<text class="header-title">老人档案</text>
|
||
<view class="header-actions">
|
||
<button class="action-btn" @click="showAddElder">
|
||
<text class="btn-text">➕ 新增档案</text>
|
||
</button>
|
||
<button class="action-btn" @click="showBulkImport">
|
||
<text class="btn-text">📁 批量导入</text>
|
||
</button>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- Quick Stats -->
|
||
<view class="stats-section">
|
||
<view class="stat-card">
|
||
<view class="stat-icon">👥</view>
|
||
<view class="stat-content">
|
||
<text class="stat-number">{{ stats.total_elders }}</text>
|
||
<text class="stat-label">总人数</text>
|
||
</view>
|
||
</view>
|
||
<view class="stat-card active">
|
||
<view class="stat-icon">🏠</view>
|
||
<view class="stat-content">
|
||
<text class="stat-number">{{ stats.active_elders }}</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.new_admissions }}</text>
|
||
<text class="stat-label">本月新入</text>
|
||
</view>
|
||
</view>
|
||
<view class="stat-card warning">
|
||
<view class="stat-icon">⚠️</view>
|
||
<view class="stat-content">
|
||
<text class="stat-number">{{ stats.health_alerts }}</text>
|
||
<text class="stat-label">健康提醒</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- Search & Filter -->
|
||
<view class="filters-section">
|
||
<view class="filter-row">
|
||
<view class="search-group">
|
||
<input
|
||
class="search-input"
|
||
placeholder="搜索姓名、身份证或房间号"
|
||
v-model="searchKeyword"
|
||
@input="onSearchInput"
|
||
/>
|
||
<button class="search-btn" @click="performSearch">
|
||
<text class="search-text">🔍</text>
|
||
</button>
|
||
</view>
|
||
<view class="filter-group">
|
||
<text class="filter-label">护理等级</text>
|
||
<picker
|
||
:value="selectedCareLevelIndex"
|
||
:range="careLevelOptions"
|
||
range-key="label"
|
||
@change="onCareLevelChange"
|
||
>
|
||
<text class="picker-text">{{ selectedCareLevel?.label || '全部等级' }}</text>
|
||
</picker>
|
||
</view>
|
||
<view class="filter-group">
|
||
<text class="filter-label">在院状态</text>
|
||
<picker
|
||
:value="selectedStatusIndex"
|
||
:range="statusOptions"
|
||
range-key="label"
|
||
@change="onStatusChange"
|
||
>
|
||
<text class="picker-text">{{ selectedStatus?.label || '全部状态' }}</text>
|
||
</picker>
|
||
</view>
|
||
</view>
|
||
<view class="filter-row">
|
||
<view class="filter-toggles">
|
||
<button
|
||
class="toggle-btn"
|
||
:class="{ active: showHealthAlertsOnly }"
|
||
@click="toggleHealthAlerts"
|
||
>
|
||
<text class="toggle-text">仅健康异常</text>
|
||
</button>
|
||
<button
|
||
class="toggle-btn"
|
||
:class="{ active: showBirthdaysOnly }"
|
||
@click="toggleBirthdays"
|
||
>
|
||
<text class="toggle-text">近期生日</text>
|
||
</button>
|
||
</view>
|
||
<button class="refresh-btn" @click="refreshData">
|
||
<text class="refresh-text">🔄 刷新</text>
|
||
</button>
|
||
</view>
|
||
</view>
|
||
|
||
<!-- Elder List -->
|
||
<view class="elders-section">
|
||
<view class="section-header">
|
||
<text class="section-title">档案列表 ({{ filteredElders.length }})</text>
|
||
<view class="sort-options">
|
||
<button
|
||
class="sort-btn"
|
||
:class="{ active: sortBy === 'name' }"
|
||
@click="setSortBy('name')"
|
||
>
|
||
<text class="sort-text">姓名</text>
|
||
</button>
|
||
<button
|
||
class="sort-btn"
|
||
:class="{ active: sortBy === 'admission_date' }"
|
||
@click="setSortBy('admission_date')"
|
||
>
|
||
<text class="sort-text">入院时间</text>
|
||
</button>
|
||
<button
|
||
class="sort-btn"
|
||
:class="{ active: sortBy === 'age' }"
|
||
@click="setSortBy('age')"
|
||
>
|
||
<text class="sort-text">年龄</text>
|
||
</button>
|
||
</view>
|
||
</view>
|
||
|
||
<scroll-view class="elders-list" scroll-y="true" :style="{ height: '600px' }">
|
||
<view
|
||
v-for="elder in filteredElders"
|
||
:key="elder.id"
|
||
class="elder-item"
|
||
:class="getElderStatusClass(elder)"
|
||
@click="viewElderDetail(elder)"
|
||
>
|
||
<view class="elder-avatar">
|
||
<text class="avatar-text">{{ elder.name.charAt(0) }}</text>
|
||
</view>
|
||
|
||
<view class="elder-info">
|
||
<view class="elder-header">
|
||
<text class="elder-name">{{ elder.name }}</text>
|
||
<view class="elder-tags">
|
||
<text class="tag care-level">{{ getCareLevelText(elder.care_level) }}</text>
|
||
<text class="tag gender" :class="elder.gender">{{ elder.gender === 'male' ? '男' : '女' }}</text>
|
||
<text v-if="hasHealthAlert(elder)" class="tag alert">异常</text>
|
||
<text v-if="isBirthdaySoon(elder)" class="tag birthday">生日</text>
|
||
</view>
|
||
</view>
|
||
|
||
<view class="elder-details">
|
||
<view class="detail-row">
|
||
<text class="detail-label">年龄:</text>
|
||
<text class="detail-value">{{ getAge(elder.birthday) }}岁</text>
|
||
<text class="detail-label">房间:</text>
|
||
<text class="detail-value">{{ elder.room_number || '未分配' }}</text>
|
||
</view>
|
||
<view class="detail-row">
|
||
<text class="detail-label">入院:</text>
|
||
<text class="detail-value">{{ formatDate(elder.admission_date) }}</text>
|
||
<text class="detail-label">护理员:</text>
|
||
<text class="detail-value">{{ elder.caregiver_name || '未分配' }}</text>
|
||
</view>
|
||
<view class="detail-row">
|
||
<text class="detail-label">联系人:</text>
|
||
<text class="detail-value">{{ elder.emergency_contact || '未设置' }}</text>
|
||
<text class="detail-label">电话:</text>
|
||
<text class="detail-value">{{ elder.emergency_phone || '未设置' }}</text>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<view class="elder-actions">
|
||
<button class="action-btn small" @click.stop="viewHealthRecord(elder)">
|
||
<text class="btn-text">📊</text>
|
||
</button>
|
||
<button class="action-btn small" @click.stop="viewCareRecord(elder)">
|
||
<text class="btn-text">📋</text>
|
||
</button>
|
||
<button class="action-btn small" @click.stop="editElder(elder)">
|
||
<text class="btn-text">✏️</text>
|
||
</button>
|
||
<button class="action-btn small warning" @click.stop="showTransferModal(elder)">
|
||
<text class="btn-text">🔄</text>
|
||
</button>
|
||
</view>
|
||
</view>
|
||
</scroll-view>
|
||
</view>
|
||
|
||
<!-- Add Elder Modal -->
|
||
<view v-if="showAddModal" class="modal-overlay" @click="hideAddModal">
|
||
<view class="modal-content large" @click.stop>
|
||
<view class="modal-header">
|
||
<text class="modal-title">新增老人档案</text>
|
||
<button class="close-btn" @click="hideAddModal">
|
||
<text class="close-text">✕</text>
|
||
</button>
|
||
</view>
|
||
|
||
<view class="modal-body">
|
||
<view class="form-section">
|
||
<text class="section-title">基本信息</text>
|
||
<view class="form-row">
|
||
<view class="form-group">
|
||
<text class="form-label">姓名 *</text>
|
||
<input
|
||
class="form-input"
|
||
placeholder="请输入老人姓名"
|
||
v-model="newElder.name"
|
||
/>
|
||
</view>
|
||
<view class="form-group">
|
||
<text class="form-label">性别 *</text>
|
||
<picker
|
||
:value="newGenderIndex"
|
||
:range="genderOptions"
|
||
range-key="label"
|
||
@change="onNewGenderChange"
|
||
>
|
||
<text class="picker-text">{{ newGender?.label || '选择性别' }}</text>
|
||
</picker>
|
||
</view>
|
||
</view>
|
||
|
||
<view class="form-row">
|
||
<view class="form-group">
|
||
<text class="form-label">身份证号 *</text>
|
||
<input
|
||
class="form-input"
|
||
placeholder="请输入身份证号"
|
||
v-model="newElder.id_card"
|
||
/>
|
||
</view>
|
||
<view class="form-group">
|
||
<text class="form-label">出生日期 *</text>
|
||
<picker mode="date" @change="onNewBirthdayChange">
|
||
<text class="picker-text">{{ newElder.birthday || '选择日期' }}</text>
|
||
</picker>
|
||
</view>
|
||
</view>
|
||
|
||
<view class="form-row">
|
||
<view class="form-group">
|
||
<text class="form-label">联系电话</text>
|
||
<input
|
||
class="form-input"
|
||
placeholder="请输入联系电话"
|
||
v-model="newElder.phone"
|
||
/>
|
||
</view>
|
||
<view class="form-group">
|
||
<text class="form-label">民族</text>
|
||
<input
|
||
class="form-input"
|
||
placeholder="请输入民族"
|
||
v-model="newElder.nationality"
|
||
/>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<view class="form-section">
|
||
<text class="section-title">护理信息</text>
|
||
<view class="form-row">
|
||
<view class="form-group">
|
||
<text class="form-label">护理等级 *</text>
|
||
<picker
|
||
:value="newCareLevelIndex"
|
||
:range="careLevelOptions"
|
||
range-key="label"
|
||
@change="onNewCareLevelChange"
|
||
>
|
||
<text class="picker-text">{{ newCareLevel?.label || '选择护理等级' }}</text>
|
||
</picker>
|
||
</view>
|
||
<view class="form-group">
|
||
<text class="form-label">房间分配</text>
|
||
<picker
|
||
:value="newRoomIndex"
|
||
:range="roomOptions"
|
||
range-key="name"
|
||
@change="onNewRoomChange"
|
||
>
|
||
<text class="picker-text">{{ newRoom?.name || '选择房间' }}</text>
|
||
</picker>
|
||
</view>
|
||
</view>
|
||
|
||
<view class="form-row">
|
||
<view class="form-group">
|
||
<text class="form-label">入院日期 *</text>
|
||
<picker mode="date" @change="onNewAdmissionDateChange">
|
||
<text class="picker-text">{{ newElder.admission_date || '选择日期' }}</text>
|
||
</picker>
|
||
</view>
|
||
<view class="form-group">
|
||
<text class="form-label">月护理费</text>
|
||
<input
|
||
class="form-input"
|
||
type="number"
|
||
placeholder="请输入月护理费"
|
||
v-model="newElder.monthly_fee"
|
||
/>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<view class="form-section">
|
||
<text class="section-title">联系人信息</text>
|
||
<view class="form-row">
|
||
<view class="form-group">
|
||
<text class="form-label">紧急联系人</text>
|
||
<input
|
||
class="form-input"
|
||
placeholder="请输入联系人姓名"
|
||
v-model="newElder.emergency_contact"
|
||
/>
|
||
</view>
|
||
<view class="form-group">
|
||
<text class="form-label">联系人电话</text>
|
||
<input
|
||
class="form-input"
|
||
placeholder="请输入联系人电话"
|
||
v-model="newElder.emergency_phone"
|
||
/>
|
||
</view>
|
||
</view>
|
||
|
||
<view class="form-row">
|
||
<view class="form-group">
|
||
<text class="form-label">联系人关系</text>
|
||
<input
|
||
class="form-input"
|
||
placeholder="如:女儿、儿子"
|
||
v-model="newElder.emergency_relationship"
|
||
/>
|
||
</view>
|
||
<view class="form-group">
|
||
<text class="form-label">联系人地址</text>
|
||
<input
|
||
class="form-input"
|
||
placeholder="请输入联系人地址"
|
||
v-model="newElder.emergency_address"
|
||
/>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<view class="form-section">
|
||
<text class="section-title">健康信息</text>
|
||
<view class="form-group">
|
||
<text class="form-label">既往病史</text>
|
||
<textarea
|
||
class="form-textarea"
|
||
placeholder="请输入既往病史信息"
|
||
v-model="newElder.medical_history"
|
||
/>
|
||
</view>
|
||
<view class="form-group">
|
||
<text class="form-label">过敏史</text>
|
||
<textarea
|
||
class="form-textarea"
|
||
placeholder="请输入过敏史信息"
|
||
v-model="newElder.allergies"
|
||
/>
|
||
</view>
|
||
<view class="form-group">
|
||
<text class="form-label">备注</text>
|
||
<textarea
|
||
class="form-textarea"
|
||
placeholder="其他需要注意的事项"
|
||
v-model="newElder.notes"
|
||
/>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
|
||
<view class="modal-footer">
|
||
<button class="cancel-btn" @click="hideAddModal">
|
||
<text class="btn-text">取消</text>
|
||
</button>
|
||
<button class="confirm-btn" @click="saveElder" :disabled="!isFormValid">
|
||
<text class="btn-text">保存</text>
|
||
</button>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</view>
|
||
</template>
|
||
|
||
<script setup lang="uts">
|
||
import { ref, computed, onMounted } from 'vue'
|
||
import { formatDate, getAge, getCareLevelText } from '../types.uts'
|
||
import type { Elder, HealthStats } from '../types.uts'
|
||
|
||
// Extended Elder type with additional fields
|
||
type ExtendedElder = Elder & {
|
||
caregiver_name?: string | null
|
||
emergency_contact?: string | null
|
||
emergency_phone?: string | null
|
||
emergency_relationship?: string | null
|
||
emergency_address?: string | null
|
||
medical_history?: string | null
|
||
allergies?: string | null
|
||
notes?: string | null
|
||
phone?: string | null
|
||
}
|
||
|
||
// Data
|
||
const elders = ref<ExtendedElder[]>([])
|
||
const stats = ref<HealthStats>({
|
||
total_elders: 0,
|
||
active_elders: 0,
|
||
new_admissions: 0,
|
||
health_alerts: 0,
|
||
total_equipment: 0,
|
||
online_equipment: 0,
|
||
maintenance_needed: 0,
|
||
faulty_equipment: 0,
|
||
total_records_today: 0,
|
||
abnormal_readings: 0,
|
||
pending_reviews: 0,
|
||
critical_alerts: 0,
|
||
today_visitors: 0,
|
||
current_visitors: 0,
|
||
scheduled_visits: 0,
|
||
pending_approvals: 0
|
||
})
|
||
|
||
// Filters
|
||
const searchKeyword = ref('')
|
||
const selectedCareLevelIndex = ref(-1)
|
||
const selectedStatusIndex = ref(-1)
|
||
const showHealthAlertsOnly = ref(false)
|
||
const showBirthdaysOnly = ref(false)
|
||
const sortBy = ref('name')
|
||
|
||
// Options
|
||
const careLevelOptions = ref([
|
||
{ value: 'self_care', label: '自理' },
|
||
{ value: 'assisted', label: '半护理' },
|
||
{ value: 'full_care', label: '全护理' },
|
||
{ value: 'dementia', label: '失智护理' }
|
||
])
|
||
|
||
const statusOptions = ref([
|
||
{ value: 'active', label: '在院' },
|
||
{ value: 'discharged', label: '已出院' },
|
||
{ value: 'hospitalized', label: '住院中' },
|
||
{ value: 'deceased', label: '已故' }
|
||
])
|
||
|
||
const genderOptions = ref([
|
||
{ value: 'male', label: '男' },
|
||
{ value: 'female', label: '女' }
|
||
])
|
||
|
||
const roomOptions = ref<any[]>([])
|
||
|
||
// Modal states
|
||
const showAddModal = ref(false)
|
||
|
||
// Form data
|
||
const newElder = ref<ExtendedElder>({
|
||
id: '',
|
||
user_id: '',
|
||
facility_id: '',
|
||
care_unit_id: '',
|
||
elder_code: '',
|
||
name: '',
|
||
id_card: '',
|
||
gender: '',
|
||
birthday: '',
|
||
nationality: '',
|
||
religion: '',
|
||
marital_status: '',
|
||
education: '',
|
||
occupation: '',
|
||
admission_date: '',
|
||
care_level: '',
|
||
room_number: '',
|
||
bed_number: '',
|
||
payment_method: '',
|
||
monthly_fee: null,
|
||
deposit: null,
|
||
status: 'active',
|
||
created_at: '',
|
||
updated_at: '',
|
||
phone: '',
|
||
emergency_contact: '',
|
||
emergency_phone: '',
|
||
emergency_relationship: '',
|
||
emergency_address: '',
|
||
medical_history: '',
|
||
allergies: '',
|
||
notes: ''
|
||
})
|
||
|
||
const newGenderIndex = ref(-1)
|
||
const newCareLevelIndex = ref(-1)
|
||
const newRoomIndex = ref(-1)
|
||
|
||
// Computed
|
||
const selectedCareLevel = computed<any>(() => {
|
||
return selectedCareLevelIndex.value >= 0 ? careLevelOptions.value[selectedCareLevelIndex.value] : null
|
||
})
|
||
|
||
const selectedStatus = computed<any>(() => {
|
||
return selectedStatusIndex.value >= 0 ? statusOptions.value[selectedStatusIndex.value] : null
|
||
})
|
||
|
||
const newGender = computed<any>(() => {
|
||
return newGenderIndex.value >= 0 ? genderOptions.value[newGenderIndex.value] : null
|
||
})
|
||
|
||
const newCareLevel = computed<any>(() => {
|
||
return newCareLevelIndex.value >= 0 ? careLevelOptions.value[newCareLevelIndex.value] : null
|
||
})
|
||
|
||
const newRoom = computed<any>(() => {
|
||
return newRoomIndex.value >= 0 ? roomOptions.value[newRoomIndex.value] : null
|
||
})
|
||
|
||
const filteredElders = computed<ExtendedElder[]>(() => {
|
||
let filtered = elders.value
|
||
|
||
// Search filter
|
||
if (searchKeyword.value.trim() !== '') {
|
||
const keyword = searchKeyword.value.toLowerCase()
|
||
filtered = filtered.filter(elder =>
|
||
elder.name.toLowerCase().includes(keyword) ||
|
||
(elder.id_card && elder.id_card.toLowerCase().includes(keyword)) ||
|
||
(elder.room_number && elder.room_number.toLowerCase().includes(keyword))
|
||
)
|
||
}
|
||
|
||
// Care level filter
|
||
if (selectedCareLevel.value) {
|
||
filtered = filtered.filter(elder => elder.care_level === selectedCareLevel.value.value)
|
||
}
|
||
|
||
// Status filter
|
||
if (selectedStatus.value) {
|
||
filtered = filtered.filter(elder => elder.status === selectedStatus.value.value)
|
||
}
|
||
|
||
// Health alerts filter
|
||
if (showHealthAlertsOnly.value) {
|
||
filtered = filtered.filter(elder => hasHealthAlert(elder))
|
||
}
|
||
|
||
// Birthdays filter
|
||
if (showBirthdaysOnly.value) {
|
||
filtered = filtered.filter(elder => isBirthdaySoon(elder))
|
||
}
|
||
|
||
// Sort
|
||
filtered.sort((a, b) => {
|
||
switch (sortBy.value) {
|
||
case 'name':
|
||
return a.name.localeCompare(b.name)
|
||
case 'admission_date':
|
||
return new Date(b.admission_date || '').getTime() - new Date(a.admission_date || '').getTime()
|
||
case 'age':
|
||
return getAge(b.birthday) - getAge(a.birthday)
|
||
default:
|
||
return 0
|
||
}
|
||
})
|
||
|
||
return filtered
|
||
})
|
||
|
||
const isFormValid = computed<boolean>(() => {
|
||
return newElder.value.name.trim() !== '' &&
|
||
newElder.value.id_card !== '' &&
|
||
newElder.value.gender !== '' &&
|
||
newElder.value.birthday !== '' &&
|
||
newElder.value.admission_date !== '' &&
|
||
newElder.value.care_level !== ''
|
||
})
|
||
|
||
// Methods
|
||
const loadElders = async (): Promise<void> => {
|
||
try {
|
||
const response = await supa.executeAs('rpc/get_elders_with_details', {})
|
||
if (response.success && response.data) {
|
||
elders.value = response.data as ExtendedElder[]
|
||
}
|
||
} catch (error) {
|
||
console.error('加载老人档案失败:', error)
|
||
}
|
||
}
|
||
|
||
const loadStats = async (): Promise<void> => {
|
||
try {
|
||
const response = await supa.executeAs('rpc/get_elder_stats', {})
|
||
if (response.success && response.data && response.data.length > 0) {
|
||
stats.value = response.data[0] as HealthStats
|
||
}
|
||
} catch (error) {
|
||
console.error('加载统计数据失败:', error)
|
||
}
|
||
}
|
||
|
||
const loadRooms = async (): Promise<void> => {
|
||
try {
|
||
const response = await supa.executeAs('select', {
|
||
table: 'rooms',
|
||
select: 'id, name, floor, bed_count, occupied_beds',
|
||
order: 'floor, name'
|
||
})
|
||
if (response.success && response.data) {
|
||
roomOptions.value = response.data
|
||
}
|
||
} catch (error) {
|
||
console.error('加载房间列表失败:', error)
|
||
}
|
||
}
|
||
|
||
const getElderStatusClass = (elder: ExtendedElder): string => {
|
||
const classes = ['elder-item']
|
||
|
||
if (hasHealthAlert(elder)) {
|
||
classes.push('has-alert')
|
||
}
|
||
|
||
if (isBirthdaySoon(elder)) {
|
||
classes.push('has-birthday')
|
||
}
|
||
|
||
switch (elder.status) {
|
||
case 'active':
|
||
classes.push('status-active')
|
||
break
|
||
case 'discharged':
|
||
classes.push('status-discharged')
|
||
break
|
||
case 'hospitalized':
|
||
classes.push('status-hospitalized')
|
||
break
|
||
case 'deceased':
|
||
classes.push('status-deceased')
|
||
break
|
||
}
|
||
|
||
return classes.join(' ')
|
||
}
|
||
|
||
const hasHealthAlert = (elder: ExtendedElder): boolean => {
|
||
// This would be determined by checking health alerts for this elder
|
||
// For now, return false as placeholder
|
||
return false
|
||
}
|
||
|
||
const isBirthdaySoon = (elder: ExtendedElder): boolean => {
|
||
if (!elder.birthday) return false
|
||
|
||
const today = new Date()
|
||
const birthday = new Date(elder.birthday)
|
||
birthday.setFullYear(today.getFullYear())
|
||
|
||
const daysDiff = Math.ceil((birthday.getTime() - today.getTime()) / (1000 * 60 * 60 * 24))
|
||
return daysDiff >= 0 && daysDiff <= 7
|
||
}
|
||
|
||
// Event handlers
|
||
const onSearchInput = (): void => {
|
||
// Real-time search handled by computed
|
||
}
|
||
|
||
const performSearch = (): void => {
|
||
// Manual search trigger
|
||
}
|
||
|
||
const onCareLevelChange = (e: any): void => {
|
||
selectedCareLevelIndex.value = e.detail.value
|
||
}
|
||
|
||
const onStatusChange = (e: any): void => {
|
||
selectedStatusIndex.value = e.detail.value
|
||
}
|
||
|
||
const toggleHealthAlerts = (): void => {
|
||
showHealthAlertsOnly.value = !showHealthAlertsOnly.value
|
||
}
|
||
|
||
const toggleBirthdays = (): void => {
|
||
showBirthdaysOnly.value = !showBirthdaysOnly.value
|
||
}
|
||
|
||
const setSortBy = (field: string): void => {
|
||
sortBy.value = field
|
||
}
|
||
|
||
const refreshData = async (): Promise<void> => {
|
||
await Promise.all([
|
||
loadElders(),
|
||
loadStats(),
|
||
loadRooms()
|
||
])
|
||
}
|
||
|
||
const viewElderDetail = (elder: ExtendedElder): void => {
|
||
console.log('查看老人详情:', elder)
|
||
}
|
||
|
||
const viewHealthRecord = (elder: ExtendedElder): void => {
|
||
console.log('查看健康记录:', elder)
|
||
}
|
||
|
||
const viewCareRecord = (elder: ExtendedElder): void => {
|
||
console.log('查看护理记录:', elder)
|
||
}
|
||
|
||
const editElder = (elder: ExtendedElder): void => {
|
||
console.log('编辑老人档案:', elder)
|
||
}
|
||
|
||
const showTransferModal = (elder: ExtendedElder): void => {
|
||
console.log('转院/转房:', elder)
|
||
}
|
||
|
||
// Modal methods
|
||
const showAddElder = (): void => {
|
||
newElder.value = {
|
||
id: '',
|
||
user_id: '',
|
||
facility_id: '',
|
||
care_unit_id: '',
|
||
elder_code: '',
|
||
name: '',
|
||
id_card: '',
|
||
gender: '',
|
||
birthday: '',
|
||
nationality: '',
|
||
religion: '',
|
||
marital_status: '',
|
||
education: '',
|
||
occupation: '',
|
||
admission_date: '',
|
||
care_level: '',
|
||
room_number: '',
|
||
bed_number: '',
|
||
payment_method: '',
|
||
monthly_fee: null,
|
||
deposit: null,
|
||
status: 'active',
|
||
created_at: '',
|
||
updated_at: '',
|
||
phone: '',
|
||
emergency_contact: '',
|
||
emergency_phone: '',
|
||
emergency_relationship: '',
|
||
emergency_address: '',
|
||
medical_history: '',
|
||
allergies: '',
|
||
notes: ''
|
||
}
|
||
newGenderIndex.value = -1
|
||
newCareLevelIndex.value = -1
|
||
newRoomIndex.value = -1
|
||
showAddModal.value = true
|
||
}
|
||
|
||
const hideAddModal = (): void => {
|
||
showAddModal.value = false
|
||
}
|
||
|
||
const showBulkImport = (): void => {
|
||
console.log('批量导入档案')
|
||
}
|
||
|
||
const onNewGenderChange = (e: any): void => {
|
||
newGenderIndex.value = e.detail.value
|
||
if (newGender.value) {
|
||
newElder.value.gender = newGender.value.value
|
||
}
|
||
}
|
||
|
||
const onNewCareLevelChange = (e: any): void => {
|
||
newCareLevelIndex.value = e.detail.value
|
||
if (newCareLevel.value) {
|
||
newElder.value.care_level = newCareLevel.value.value
|
||
}
|
||
}
|
||
|
||
const onNewRoomChange = (e: any): void => {
|
||
newRoomIndex.value = e.detail.value
|
||
if (newRoom.value) {
|
||
newElder.value.room_number = newRoom.value.name
|
||
}
|
||
}
|
||
|
||
const onNewBirthdayChange = (e: any): void => {
|
||
newElder.value.birthday = e.detail.value
|
||
}
|
||
|
||
const onNewAdmissionDateChange = (e: any): void => {
|
||
newElder.value.admission_date = e.detail.value
|
||
}
|
||
|
||
const saveElder = async (): Promise<void> => {
|
||
if (!isFormValid.value) return
|
||
|
||
try {
|
||
const response = await supa.executeAs('insert', {
|
||
table: 'elders',
|
||
data: {
|
||
name: newElder.value.name,
|
||
id_card: newElder.value.id_card,
|
||
gender: newElder.value.gender,
|
||
birthday: newElder.value.birthday,
|
||
nationality: newElder.value.nationality,
|
||
admission_date: newElder.value.admission_date,
|
||
care_level: newElder.value.care_level,
|
||
room_number: newElder.value.room_number,
|
||
monthly_fee: newElder.value.monthly_fee,
|
||
status: 'active'
|
||
}
|
||
})
|
||
|
||
if (response.success) {
|
||
hideAddModal()
|
||
await loadElders()
|
||
await loadStats()
|
||
}
|
||
} catch (error) {
|
||
console.error('保存老人档案失败:', error)
|
||
}
|
||
}
|
||
|
||
// Lifecycle
|
||
onMounted(async () => {
|
||
await refreshData()
|
||
})
|
||
</script>
|
||
|
||
<style lang="scss">
|
||
// 这里包含完整的样式,类似于之前的页面
|
||
// 为了节省空间,省略了详细的样式代码
|
||
// 实际使用时需要补充完整的样式
|
||
|
||
.elder-profile {
|
||
padding: 20px;
|
||
background: #f5f7fa;
|
||
min-height: 100vh;
|
||
|
||
.header {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
margin-bottom: 24px;
|
||
padding: 20px;
|
||
background: white;
|
||
border-radius: 12px;
|
||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.1);
|
||
|
||
.header-title {
|
||
font-size: 24px;
|
||
font-weight: 600;
|
||
color: #1a202c;
|
||
}
|
||
|
||
.header-actions {
|
||
display: flex;
|
||
gap: 12px;
|
||
|
||
.action-btn {
|
||
padding: 8px 16px;
|
||
background: #4a90e2;
|
||
color: white;
|
||
border: none;
|
||
border-radius: 8px;
|
||
font-size: 14px;
|
||
|
||
.btn-text {
|
||
color: white;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
.elder-item {
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 16px;
|
||
padding: 16px;
|
||
background: white;
|
||
border-radius: 12px;
|
||
margin-bottom: 12px;
|
||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||
cursor: pointer;
|
||
|
||
&:hover {
|
||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
|
||
}
|
||
|
||
&.has-alert {
|
||
border-left: 4px solid #ef4444;
|
||
}
|
||
|
||
&.has-birthday {
|
||
border-left: 4px solid #f59e0b;
|
||
}
|
||
|
||
.elder-avatar {
|
||
width: 60px;
|
||
height: 60px;
|
||
border-radius: 50%;
|
||
background: #4a90e2;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
|
||
.avatar-text {
|
||
color: white;
|
||
font-size: 24px;
|
||
font-weight: 600;
|
||
}
|
||
}
|
||
|
||
.elder-info {
|
||
flex: 1;
|
||
|
||
.elder-header {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
margin-bottom: 8px;
|
||
|
||
.elder-name {
|
||
font-size: 18px;
|
||
font-weight: 600;
|
||
color: #1a202c;
|
||
}
|
||
|
||
.elder-tags {
|
||
display: flex;
|
||
gap: 6px;
|
||
|
||
.tag {
|
||
padding: 2px 8px;
|
||
border-radius: 12px;
|
||
font-size: 12px;
|
||
font-weight: 500;
|
||
|
||
&.care-level {
|
||
background: #dbeafe;
|
||
color: #1e40af;
|
||
}
|
||
|
||
&.gender.male {
|
||
background: #bfdbfe;
|
||
color: #1e40af;
|
||
}
|
||
|
||
&.gender.female {
|
||
background: #fce7f3;
|
||
color: #be185d;
|
||
}
|
||
|
||
&.alert {
|
||
background: #fee2e2;
|
||
color: #dc2626;
|
||
}
|
||
|
||
&.birthday {
|
||
background: #fef3c7;
|
||
color: #d97706;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
.elder-details {
|
||
.detail-row {
|
||
display: flex;
|
||
gap: 16px;
|
||
margin-bottom: 4px;
|
||
font-size: 14px;
|
||
|
||
.detail-label {
|
||
color: #6b7280;
|
||
min-width: 50px;
|
||
}
|
||
|
||
.detail-value {
|
||
color: #374151;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
.elder-actions {
|
||
display: flex;
|
||
gap: 8px;
|
||
|
||
.action-btn {
|
||
padding: 6px 12px;
|
||
background: #f3f4f6;
|
||
border: none;
|
||
border-radius: 6px;
|
||
font-size: 14px;
|
||
|
||
&.small {
|
||
padding: 4px 8px;
|
||
}
|
||
|
||
&.warning {
|
||
background: #fef3c7;
|
||
color: #d97706;
|
||
}
|
||
|
||
.btn-text {
|
||
color: #374151;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
.modal-content.large {
|
||
width: 95%;
|
||
max-width: 800px;
|
||
max-height: 90vh;
|
||
}
|
||
|
||
.form-section {
|
||
margin-bottom: 24px;
|
||
padding-bottom: 20px;
|
||
border-bottom: 1px solid #e5e7eb;
|
||
|
||
&:last-child {
|
||
border-bottom: none;
|
||
}
|
||
|
||
.section-title {
|
||
display: block;
|
||
font-size: 16px;
|
||
font-weight: 600;
|
||
color: #1a202c;
|
||
margin-bottom: 16px;
|
||
}
|
||
}
|
||
|
||
.form-row {
|
||
display: flex;
|
||
gap: 16px;
|
||
margin-bottom: 16px;
|
||
|
||
.form-group {
|
||
flex: 1;
|
||
}
|
||
}
|
||
}
|
||
</style>
|
||
|