Initial commit of akmon project
This commit is contained in:
845
pages/ec/admin/dashboard.uvue
Normal file
845
pages/ec/admin/dashboard.uvue
Normal file
@@ -0,0 +1,845 @@
|
||||
<!-- 养老管理系统 - 管理员仪表板 (简化版) -->
|
||||
<template>
|
||||
<view class="admin-dashboard">
|
||||
<view class="header">
|
||||
<text class="title">养老管理系统</text>
|
||||
<text class="welcome">管理员,{{ currentTime }}</text>
|
||||
</view>
|
||||
|
||||
<!-- 数据概览卡片 -->
|
||||
<view class="overview-section">
|
||||
<view class="overview-card" v-for="(card, idx) in overviewCards" :key="idx" :class="{ 'is-last': idx === overviewCards.length - 1 }" @click="navTo(card.navurl)" >
|
||||
<view class="card-icon">{{ card.icon }}</view>
|
||||
<view class="card-content">
|
||||
<text class="card-number">{{ card.number }}</text>
|
||||
<text class="card-label">{{ card.label }}</text>
|
||||
</view>
|
||||
<view v-if="card.trend !== undefined" class="card-trend" :class="card.trend >= 0 ? 'positive' : 'negative'">
|
||||
<text class="trend-text">{{ card.trend >= 0 ? '+' : '' }}{{ card.trend }}</text>
|
||||
</view>
|
||||
<view v-if="card.status !== undefined" class="card-status">
|
||||
<text class="status-text">{{ card.status }}</text>
|
||||
</view>
|
||||
<view v-if="card.alert !== undefined && card.alert > 0" class="card-alert">
|
||||
<text class="alert-text">需要处理</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 快速操作区 -->
|
||||
<view class="actions-section">
|
||||
<text class="section-title">快速操作</text>
|
||||
<view class="actions-grid">
|
||||
<view class="action-card" @click="navigateToElderManagement">
|
||||
<view class="action-icon">👴</view>
|
||||
<text class="action-title">老人管理</text>
|
||||
<text class="action-desc">档案、健康、护理</text>
|
||||
</view>
|
||||
<view class="action-card" @click="navigateToCaregiverManagement">
|
||||
<view class="action-icon">👩⚕️</view>
|
||||
<text class="action-title">员工管理</text>
|
||||
<text class="action-desc">排班、绩效、培训</text>
|
||||
</view>
|
||||
<view class="action-card" @click="navigateToHealthMonitoring">
|
||||
<view class="action-icon">💊</view>
|
||||
<text class="action-title">健康监测</text>
|
||||
<text class="action-desc">体征、用药、预警</text>
|
||||
</view>
|
||||
<view class="action-card is-last" @click="navigateToServiceRecords">
|
||||
<view class="action-icon">📋</view>
|
||||
<text class="action-title">服务记录</text>
|
||||
<text class="action-desc">护理、餐饮、活动</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 紧急提醒列表 -->
|
||||
<view class="alerts-section">
|
||||
<view class="section-header">
|
||||
<text class="section-title">紧急提醒</text>
|
||||
<text class="section-more" @click="navigateToAlerts">查看全部</text>
|
||||
</view>
|
||||
<view class="alerts-list">
|
||||
<view v-for="alert in urgentAlerts" :key="alert.id" class="alert-item" :class="alert.severity"
|
||||
@click="handleAlert(alert)">
|
||||
<view class="alert-icon">
|
||||
<text class="icon-text">{{ getAlertIconDisplay(alert.severity ?? '') }}</text>
|
||||
</view>
|
||||
<view class="alert-content">
|
||||
<text class="alert-title">{{ alert.title ?? '' }}</text>
|
||||
<text class="alert-elder">{{ alert.elder_name ?? '未知' }}</text>
|
||||
<text class="alert-time">{{ formatDateTimeDisplay(alert.created_at ?? '') }}</text>
|
||||
</view>
|
||||
<view class="alert-actions">
|
||||
<button class="alert-btn" @click.stop="acknowledgeAlert(alert)">处理</button>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 今日护理任务 -->
|
||||
<view class="tasks-section">
|
||||
<view class="section-header">
|
||||
<text class="section-title">今日护理任务</text>
|
||||
<text class="section-more" @click="navigateToTasks">查看全部</text>
|
||||
</view>
|
||||
<view class="tasks-list">
|
||||
<view v-for="task in todayTasks" :key="task.id" class="task-item" :class="task.status"
|
||||
@click="viewTaskDetail(task)">
|
||||
<view class="task-info">
|
||||
<text class="task-title">{{ task.task_name }}</text> <text
|
||||
class="task-elder">{{ task.elder_name ?? '未知' }}</text>
|
||||
<text class="task-time">{{ formatTimeDisplay(task.scheduled_time ?? '') }}</text>
|
||||
</view>
|
||||
<view class="task-status">
|
||||
<view class="status-badge" :class="task.status">
|
||||
<text class="badge-text">{{ getTaskStatusTextDisplay(task.status ?? '') }}</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="task-caregiver">
|
||||
<text class="caregiver-name">{{ task.caregiver_name ?? '未分配' }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 最近活动记录 -->
|
||||
<view class="activities-section">
|
||||
<view class="section-header">
|
||||
<text class="section-title">最近活动</text>
|
||||
<text class="section-more" @click="navigateToActivities">查看全部</text>
|
||||
</view>
|
||||
<view class="activities-list">
|
||||
<view v-for="activity in recentActivities" :key="activity.id" class="activity-item">
|
||||
<view class="activity-avatar">
|
||||
<text class="avatar-text">{{ activity.elder_name?.charAt(0)??'--' }}</text>
|
||||
</view>
|
||||
<view class="activity-content">
|
||||
<text class="activity-title">{{ activity.description ?? '' }}</text>
|
||||
<text
|
||||
class="activity-meta">{{ (activity.elder_name ?? '未知') + ' · ' + formatDateTimeDisplay(activity.created_at ?? '') }}</text>
|
||||
</view>
|
||||
<view class="activity-type">
|
||||
<text class="type-tag"
|
||||
:class="activity.record_type">{{ getRecordTypeTextDisplay(activity.record_type ?? '') }}</text>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
|
||||
<script setup lang="uts">
|
||||
import { ref, onMounted, computed } from 'vue'
|
||||
import supa from '@/components/supadb/aksupainstance.uts'
|
||||
import type { Elder, HealthAlert, CareTask, CareRecord, DashboardStats } from '../types.uts'
|
||||
import { formatDateTime as formatDateTimeUtil, formatTime as formatTimeUtil, getAlertIcon as getAlertIconUtil, getTaskStatusText as getTaskStatusTextUtil, getRecordTypeText as getRecordTypeTextUtil } from '../types.uts' // 将函数作为方法暴露给模板
|
||||
function formatDateTimeDisplay(dateTime : string | null) : string {
|
||||
if (dateTime == null) return ''
|
||||
return formatDateTimeUtil(dateTime)
|
||||
}
|
||||
|
||||
function formatTimeDisplay(time : string | null) : string {
|
||||
if (time == null) return ''
|
||||
return formatTimeUtil(time)
|
||||
}
|
||||
|
||||
function getAlertIconDisplay(severity : string) : string {
|
||||
if (severity == null) return '❓'
|
||||
return getAlertIconUtil(severity)
|
||||
}
|
||||
|
||||
function getTaskStatusTextDisplay(status : string) : string {
|
||||
if (status == null) return '未知'
|
||||
return getTaskStatusTextUtil(status)
|
||||
}
|
||||
|
||||
function getRecordTypeTextDisplay(type : string | null) : string {
|
||||
return getRecordTypeTextUtil(type)
|
||||
}
|
||||
|
||||
// 响应式数据
|
||||
const currentTime = ref<string>('')
|
||||
const stats = ref<DashboardStats>({
|
||||
total_elders: 0,
|
||||
total_caregivers: 0,
|
||||
on_duty_caregivers: 0,
|
||||
occupancy_rate: 0,
|
||||
available_beds: 0,
|
||||
urgent_alerts: 0,
|
||||
elders_trend: 0
|
||||
})
|
||||
|
||||
// 数据列表
|
||||
const urgentAlerts = ref<Array<HealthAlert>>([])
|
||||
const todayTasks = ref<Array<CareTask>>([])
|
||||
const recentActivities = ref<Array<CareRecord>>([])
|
||||
|
||||
// 更新当前时间
|
||||
const updateCurrentTime = () => {
|
||||
const now = new Date()
|
||||
const hours = now.getHours().toString().padStart(2, '0')
|
||||
const minutes = now.getMinutes().toString().padStart(2, '0')
|
||||
currentTime.value = `今天 ${hours}:${minutes}`
|
||||
}
|
||||
// 获取今天开始和结束时间
|
||||
const getTodayRange = () : UTSJSONObject => {
|
||||
const today = new Date()
|
||||
const startDate = new Date(today.getFullYear(), today.getMonth(), today.getDate())
|
||||
const endDate = new Date(today.getFullYear(), today.getMonth(), today.getDate() + 1)
|
||||
return {
|
||||
start: startDate.toISOString(),
|
||||
end: endDate.toISOString()
|
||||
} as UTSJSONObject
|
||||
}
|
||||
|
||||
// 加载统计数据
|
||||
const loadStatistics = async () => {
|
||||
try {
|
||||
// 加载老人总数
|
||||
const eldersResult = await supa
|
||||
.from('ec_elders')
|
||||
.select('*', { count: 'exact' })
|
||||
.eq('status', 'active')
|
||||
.executeAs<Elder>()
|
||||
|
||||
if (eldersResult.error === null) {
|
||||
stats.value.total_elders = eldersResult.total ?? 0
|
||||
}
|
||||
|
||||
// 加载护理员总数
|
||||
const caregiversResult = await supa
|
||||
.from('ak_users')
|
||||
.select('*', { count: 'exact' })
|
||||
.eq('role', 'caregiver')
|
||||
.eq('status', 'active')
|
||||
.executeAs<any>()
|
||||
if (caregiversResult.error === null) {
|
||||
stats.value.total_caregivers = caregiversResult.total ?? 0
|
||||
stats.value.on_duty_caregivers = Math.floor((caregiversResult.total ?? 0) * 0.7) // 假设70%在班
|
||||
}
|
||||
|
||||
// 计算入住率
|
||||
const facilityResult = await supa
|
||||
.from('ec_facilities')
|
||||
.select('capacity, current_occupancy', {})
|
||||
.single()
|
||||
.executeAs<UTSJSONObject>()
|
||||
|
||||
if (facilityResult.error === null && facilityResult.data !== null) {
|
||||
let facilityData = facilityResult.data
|
||||
// 先判断是否为数组
|
||||
if (Array.isArray(facilityData) && facilityData.length > 0) {
|
||||
facilityData = facilityData[0]
|
||||
}
|
||||
let capacity = 0
|
||||
let occupancy = 0
|
||||
if (facilityData && typeof facilityData.get === 'function') {
|
||||
capacity = facilityData.get('capacity') as number ?? 0
|
||||
occupancy = facilityData.get('current_occupancy') as number ?? 0
|
||||
} else if (facilityData) {
|
||||
capacity = (facilityData['capacity'] as number) ?? 0
|
||||
occupancy = (facilityData['current_occupancy'] as number) ?? 0
|
||||
}
|
||||
if (capacity > 0) {
|
||||
stats.value.occupancy_rate = Math.round((occupancy / capacity) * 100)
|
||||
stats.value.available_beds = capacity - occupancy
|
||||
}
|
||||
}
|
||||
|
||||
// 加载紧急提醒数量
|
||||
const alertsResult = await supa
|
||||
.from('ec_health_alerts')
|
||||
.select('*', { count: 'exact' })
|
||||
.in('severity', ['high', 'critical'])
|
||||
.eq('status', 'active')
|
||||
.executeAs<HealthAlert>()
|
||||
if (alertsResult.error === null) {
|
||||
stats.value.urgent_alerts = alertsResult.total ?? 0
|
||||
}
|
||||
|
||||
} catch (error) {
|
||||
console.error('加载统计数据失败:', error)
|
||||
}
|
||||
}
|
||||
|
||||
// 加载紧急提醒列表
|
||||
const loadUrgentAlerts = async () => {
|
||||
try {
|
||||
|
||||
const result = await supa
|
||||
.from('ec_health_alerts')
|
||||
.select('id, title, severity, elder_id, created_at, status, ec_elders!ec_health_alerts_elder_id_fkey(name)', {})
|
||||
.in('severity', ['high', 'critical'])
|
||||
.eq('status', 'active')
|
||||
.order('created_at', { ascending: false })
|
||||
.limit(5)
|
||||
.executeAs<Array<HealthAlert>>()
|
||||
if (result.error === null && result.data !== null) {
|
||||
urgentAlerts.value = result.data as Array<HealthAlert>
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('加载紧急提醒失败:', error)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// 加载今日任务列表
|
||||
const loadTodayTasks = async () => {
|
||||
try {
|
||||
const todayRange = getTodayRange()
|
||||
const start = todayRange.get('start') as string
|
||||
const end = todayRange.get('end') as string
|
||||
|
||||
const result = await supa
|
||||
.from('ec_care_tasks')
|
||||
.select(`
|
||||
id,
|
||||
task_name,
|
||||
elder_name,
|
||||
scheduled_time,
|
||||
status,
|
||||
priority,
|
||||
caregiver_name
|
||||
`, {})
|
||||
.gte('scheduled_time', start).lt('scheduled_time', end)
|
||||
.order('scheduled_time', { ascending: true })
|
||||
.limit(8)
|
||||
.executeAs<Array<CareTask>>()
|
||||
|
||||
if (result.error === null && result.data !== null) {
|
||||
todayTasks.value = result.data as Array<CareTask>
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('加载今日任务失败:', error)
|
||||
}
|
||||
}
|
||||
|
||||
// 加载最近活动记录
|
||||
const loadRecentActivities = async () => {
|
||||
try {
|
||||
const threeDaysAgo = new Date()
|
||||
threeDaysAgo.setDate(threeDaysAgo.getDate() - 3)
|
||||
const result = await supa
|
||||
.from('ec_care_records')
|
||||
.select('id, description, ec_care_records_elder_id_fkey(name) , record_type, created_at', {})
|
||||
.gte('created_at', threeDaysAgo.toISOString())
|
||||
.order('created_at', { ascending: false })
|
||||
.limit(5)
|
||||
.executeAs<Array<CareRecord>>()
|
||||
if (result.error === null && result.data !== null) {
|
||||
recentActivities.value = result.data as Array<CareRecord>
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('加载最近活动失败:', error)
|
||||
}
|
||||
}
|
||||
|
||||
// 处理提醒
|
||||
const handleAlert = (alert : HealthAlert) => {
|
||||
uni.navigateTo({
|
||||
url: `/pages/ec/alerts/detail?id=${alert.id}`
|
||||
})
|
||||
}
|
||||
|
||||
const acknowledgeAlert = async (alert : HealthAlert) => {
|
||||
try {
|
||||
await supa
|
||||
.from('ec_health_alerts')
|
||||
.update({ status: 'acknowledged' })
|
||||
.eq('id', alert.id)
|
||||
.executeAs<any>()
|
||||
|
||||
// 重新加载数据
|
||||
loadUrgentAlerts()
|
||||
loadStatistics()
|
||||
} catch (error) {
|
||||
console.error('处理提醒失败:', error)
|
||||
}
|
||||
}
|
||||
|
||||
const viewTaskDetail = (task : CareTask) => {
|
||||
uni.navigateTo({
|
||||
url: `/pages/ec/tasks/detail?id=${task.id}`
|
||||
})
|
||||
}
|
||||
|
||||
// 导航函数
|
||||
const navigateToElderManagement = () => {
|
||||
uni.navigateTo({
|
||||
url: '/pages/ec/admin/elder-management'
|
||||
})
|
||||
}
|
||||
|
||||
const navigateToCaregiverManagement = () => {
|
||||
uni.navigateTo({
|
||||
url: '/pages/ec/admin/caregiver-management'
|
||||
})
|
||||
}
|
||||
|
||||
const navigateToHealthMonitoring = () => {
|
||||
uni.navigateTo({
|
||||
url: '/pages/ec/admin/health-monitoring'
|
||||
})
|
||||
}
|
||||
|
||||
const navigateToServiceRecords = () => {
|
||||
uni.navigateTo({
|
||||
url: '/pages/ec/admin/service-records'
|
||||
})
|
||||
}
|
||||
|
||||
const navigateToAlerts = () => {
|
||||
uni.navigateTo({
|
||||
url: '/pages/ec/health/ecalert'
|
||||
})
|
||||
}
|
||||
|
||||
const navigateToTasks = () => {
|
||||
uni.navigateTo({
|
||||
url: '/pages/ec/tasks/list'
|
||||
})
|
||||
}
|
||||
|
||||
const navigateToActivities = () => {
|
||||
uni.navigateTo({
|
||||
url: '/pages/ec/activity/management'
|
||||
})
|
||||
}
|
||||
|
||||
// 数据概览卡片数据
|
||||
const overviewCards = computed(() => [
|
||||
{
|
||||
icon: '👥',
|
||||
number: stats.value.total_elders,
|
||||
label: '入住老人',
|
||||
trend: stats.value.elders_trend,
|
||||
navurl: '/pages/ec/admin/elder-management'
|
||||
},
|
||||
{
|
||||
icon: '👨⚕️',
|
||||
number: stats.value.total_caregivers,
|
||||
label: '护理人员',
|
||||
status: `${stats.value.on_duty_caregivers} 在班`,
|
||||
navurl: '/pages/ec/admin/caregiver-management'
|
||||
},
|
||||
{
|
||||
icon: '🏥',
|
||||
number: stats.value.occupancy_rate + '%',
|
||||
label: '入住率',
|
||||
status: `${stats.value.available_beds} 空床`,
|
||||
navurl: '/pages/ec/admin/health-monitoring'
|
||||
},
|
||||
{
|
||||
icon: '⚡',
|
||||
number: stats.value.urgent_alerts,
|
||||
label: '紧急提醒',
|
||||
alert: stats.value.urgent_alerts,
|
||||
navurl: '/pages/ec/health/ecalert-history'
|
||||
}
|
||||
])
|
||||
|
||||
// 生命周期
|
||||
onMounted(() => {
|
||||
updateCurrentTime()
|
||||
loadStatistics()
|
||||
loadUrgentAlerts()
|
||||
loadTodayTasks()
|
||||
loadRecentActivities()
|
||||
|
||||
// 定时更新时间
|
||||
setInterval(() => {
|
||||
updateCurrentTime()
|
||||
}, 60000) // 每分钟更新一次
|
||||
})
|
||||
|
||||
function navTo(url: string | undefined) {
|
||||
if (url) {
|
||||
uni.navigateTo({ url })
|
||||
}
|
||||
}
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.admin-dashboard {
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
min-height: 100vh;
|
||||
background-color: #f5f5f5;
|
||||
padding: 20px;
|
||||
}
|
||||
|
||||
.header {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 20px;
|
||||
}
|
||||
|
||||
.title {
|
||||
font-size: 24px;
|
||||
font-weight: bold;
|
||||
color: #333;
|
||||
}
|
||||
|
||||
.welcome {
|
||||
font-size: 14px;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.overview-section {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-wrap: wrap;
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
.overview-card {
|
||||
flex: 1 1 160px;
|
||||
min-width: 140px;
|
||||
max-width: 220px;
|
||||
background-color: #fff;
|
||||
border-radius: 8px;
|
||||
padding: 16px 10px;
|
||||
margin-right: 12px;
|
||||
margin-bottom: 12px;
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.overview-card.is-last {
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
.card-icon {
|
||||
font-size: 32px;
|
||||
margin-right: 15px;
|
||||
}
|
||||
|
||||
.card-content {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.card-number {
|
||||
font-size: 24px;
|
||||
font-weight: bold;
|
||||
color: #333;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.card-label {
|
||||
font-size: 14px;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.card-trend {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.positive {
|
||||
color: #52c41a;
|
||||
}
|
||||
|
||||
.negative {
|
||||
color: #ff4d4f;
|
||||
}
|
||||
|
||||
.card-status {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.status-text {
|
||||
font-size: 12px;
|
||||
color: #1890ff;
|
||||
background-color: #e6f7ff;
|
||||
padding: 4px 8px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.card-alert {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.alert-text {
|
||||
font-size: 12px;
|
||||
color: #ff4d4f;
|
||||
background-color: #fff2f0;
|
||||
padding: 4px 8px;
|
||||
border-radius: 4px;
|
||||
}
|
||||
|
||||
.actions-section {
|
||||
margin-bottom: 30px;
|
||||
}
|
||||
|
||||
.section-title {
|
||||
font-size: 18px;
|
||||
font-weight: bold;
|
||||
color: #333;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.actions-grid {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
flex-wrap: wrap;
|
||||
gap: 12px;
|
||||
}
|
||||
|
||||
.action-card {
|
||||
flex: 1 1 140px;
|
||||
min-width: 110px;
|
||||
max-width: 180px;
|
||||
background-color: #fff;
|
||||
border-radius: 8px;
|
||||
padding: 14px 8px;
|
||||
margin-right: 0;
|
||||
margin-bottom: 0;
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
text-align: center;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.action-card.is-last {
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
.action-icon {
|
||||
font-size: 32px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.action-title {
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
color: #333;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.action-desc {
|
||||
font-size: 12px;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
.alerts-section,
|
||||
.tasks-section,
|
||||
.activities-section {
|
||||
background-color: #fff;
|
||||
border-radius: 8px;
|
||||
padding: 20px;
|
||||
margin-bottom: 20px;
|
||||
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.section-header {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
margin-bottom: 15px;
|
||||
}
|
||||
|
||||
.section-more {
|
||||
font-size: 14px;
|
||||
color: #1890ff;
|
||||
}
|
||||
|
||||
.alert-item {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
padding: 15px;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
}
|
||||
|
||||
.alert-item.is-last {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.alert-icon {
|
||||
margin-right: 15px;
|
||||
}
|
||||
|
||||
.icon-text {
|
||||
font-size: 24px;
|
||||
}
|
||||
|
||||
.alert-content {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.alert-title {
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
color: #333;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.alert-elder {
|
||||
font-size: 14px;
|
||||
color: #666;
|
||||
margin-bottom: 3px;
|
||||
}
|
||||
|
||||
.alert-time {
|
||||
font-size: 12px;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.alert-btn {
|
||||
background-color: #1890ff;
|
||||
color: #fff;
|
||||
border: none;
|
||||
border-radius: 4px;
|
||||
padding: 8px 16px;
|
||||
font-size: 14px;
|
||||
}
|
||||
|
||||
.task-item {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
padding: 15px;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
}
|
||||
|
||||
.task-item.is-last {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.task-info {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.task-title {
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
color: #333;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.task-elder {
|
||||
font-size: 14px;
|
||||
color: #666;
|
||||
margin-bottom: 3px;
|
||||
}
|
||||
|
||||
.task-time {
|
||||
font-size: 12px;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.status-badge {
|
||||
padding: 4px 8px;
|
||||
border-radius: 4px;
|
||||
font-size: 12px;
|
||||
}
|
||||
|
||||
.status-badge-pending {
|
||||
background-color: #fff7e6;
|
||||
color: #d48806;
|
||||
}
|
||||
|
||||
.status-badge-in_progress {
|
||||
background-color: #e6f7ff;
|
||||
color: #1890ff;
|
||||
}
|
||||
|
||||
.status-badge-completed {
|
||||
background-color: #f6ffed;
|
||||
color: #52c41a;
|
||||
}
|
||||
|
||||
.activity-item {
|
||||
display: flex;
|
||||
flex-direction: row;
|
||||
align-items: center;
|
||||
padding: 15px;
|
||||
border-bottom: 1px solid #f0f0f0;
|
||||
}
|
||||
|
||||
.activity-item.is-last {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.activity-avatar {
|
||||
width: 40px;
|
||||
height: 40px;
|
||||
border-radius: 20px;
|
||||
background-color: #1890ff;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
margin-right: 15px;
|
||||
}
|
||||
|
||||
.avatar-text {
|
||||
color: #fff;
|
||||
font-size: 16px;
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.activity-content {
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.activity-title {
|
||||
font-size: 14px;
|
||||
color: #333;
|
||||
margin-bottom: 5px;
|
||||
}
|
||||
|
||||
.activity-meta {
|
||||
font-size: 12px;
|
||||
color: #999;
|
||||
}
|
||||
|
||||
.type-tag {
|
||||
padding: 4px 8px;
|
||||
border-radius: 4px;
|
||||
font-size: 12px;
|
||||
background-color: #f0f0f0;
|
||||
color: #666;
|
||||
}
|
||||
|
||||
@media (max-width: 600px) {
|
||||
.overview-section {
|
||||
gap: 8px;
|
||||
}
|
||||
.overview-card {
|
||||
flex: 1 1 120px;
|
||||
min-width: 100px;
|
||||
max-width: 160px;
|
||||
padding: 10px 4px;
|
||||
margin-right: 6px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
.actions-grid {
|
||||
gap: 6px;
|
||||
}
|
||||
.action-card {
|
||||
flex: 1 1 90px;
|
||||
min-width: 80px;
|
||||
max-width: 120px;
|
||||
padding: 8px 2px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user