845 lines
19 KiB
Plaintext
845 lines
19 KiB
Plaintext
<!-- 养老管理系统 - 管理员仪表板 (简化版) -->
|
|
<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> |