890 lines
26 KiB
Vue
890 lines
26 KiB
Vue
<template>
|
||
<div class="home-container">
|
||
<!-- 顶部欢迎区域 -->
|
||
<div class="welcome-section">
|
||
<div class="welcome-content">
|
||
<div class="greeting">
|
||
<h1>{{ getGreeting() }},{{ userStore.nickName || userStore.name }}</h1>
|
||
<p>欢迎使用{{ userStore.hospitalName || '医院' }}管理系统</p>
|
||
</div>
|
||
<div class="role-badge" v-if="userStore.roles.length > 0">
|
||
<el-tag :type="getRoleTagType(userStore.roles[0])" size="large">
|
||
{{ getRoleName(userStore.roles[0]) }}
|
||
</el-tag>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 关键数据统计 -->
|
||
<div class="stats-grid">
|
||
<div
|
||
v-for="stat in currentStats"
|
||
:key="stat.key"
|
||
class="stat-card"
|
||
:class="`stat-${stat.type}`"
|
||
@click="handleStatClick(stat)"
|
||
>
|
||
<div class="stat-icon">
|
||
<el-icon :size="36" :color="stat.iconColor">
|
||
<component :is="stat.icon" />
|
||
</el-icon>
|
||
</div>
|
||
<div class="stat-content">
|
||
<div class="stat-value">{{ stat.value }}</div>
|
||
<div class="stat-label">{{ stat.label }}</div>
|
||
<div class="stat-trend" v-if="stat.trend">
|
||
<span :class="stat.trend > 0 ? 'trend-up' : 'trend-down'">
|
||
{{ stat.trend > 0 ? '↑' : '↓' }} {{ Math.abs(stat.trend) }}%
|
||
</span>
|
||
<span class="trend-label">较昨日</span>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 快捷功能入口 -->
|
||
<div class="quick-access-section">
|
||
<div class="section-header">
|
||
<h3>快捷功能</h3>
|
||
<el-button text type="primary" @click="showAllFunctions">查看全部</el-button>
|
||
</div>
|
||
<div class="quick-access-grid">
|
||
<div
|
||
v-for="func in quickAccess"
|
||
:key="func.key"
|
||
class="quick-access-card"
|
||
@click="handleQuickAccess(func)"
|
||
>
|
||
<div class="quick-icon">
|
||
<el-icon :size="28" :color="func.iconColor">
|
||
<component :is="func.icon" />
|
||
</el-icon>
|
||
</div>
|
||
<div class="quick-label">{{ func.label }}</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 待办事项 -->
|
||
<div class="todo-section" v-if="todoList.length > 0">
|
||
<div class="section-header">
|
||
<h3>待办事项</h3>
|
||
<el-badge :value="todoList.length" class="todo-badge">
|
||
<el-button text type="primary" @click="showAllTodos">查看全部</el-button>
|
||
</el-badge>
|
||
</div>
|
||
<div class="todo-list">
|
||
<div
|
||
v-for="todo in todoList.slice(0, 5)"
|
||
:key="todo.id"
|
||
class="todo-item"
|
||
:class="`priority-${todo.priority}`"
|
||
@click="handleTodoClick(todo)"
|
||
>
|
||
<div class="todo-icon">
|
||
<el-icon :size="20">
|
||
<component :is="todo.icon" />
|
||
</el-icon>
|
||
</div>
|
||
<div class="todo-content">
|
||
<div class="todo-title">{{ todo.title }}</div>
|
||
<div class="todo-desc">{{ todo.desc }}</div>
|
||
</div>
|
||
<div class="todo-time">{{ todo.time }}</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
|
||
<!-- 今日日程 -->
|
||
<div class="schedule-section">
|
||
<div class="section-header">
|
||
<h3>今日日程</h3>
|
||
<el-button text type="primary" @click="showSchedule">管理日程</el-button>
|
||
</div>
|
||
<div class="schedule-list">
|
||
<div
|
||
v-for="item in scheduleList"
|
||
:key="item.id"
|
||
class="schedule-item"
|
||
>
|
||
<div class="schedule-time">{{ item.time }}</div>
|
||
<div class="schedule-content">
|
||
<div class="schedule-title">{{ item.title }}</div>
|
||
<div class="schedule-location">
|
||
<el-icon><Location /></el-icon>
|
||
<span>{{ item.location }}</span>
|
||
</div>
|
||
</div>
|
||
<el-tag :type="item.type" size="small">{{ item.tag }}</el-tag>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</div>
|
||
</template>
|
||
|
||
<script setup name="Home">
|
||
import { ref, computed, onMounted } from 'vue'
|
||
import { useRouter } from 'vue-router'
|
||
import useUserStore from '@/store/modules/user'
|
||
import { getHomeStatistics } from '@/api/home'
|
||
import {
|
||
User,
|
||
Document,
|
||
Calendar,
|
||
Money,
|
||
Warning,
|
||
TrendCharts,
|
||
Monitor,
|
||
Clock,
|
||
ChatDotRound,
|
||
Files,
|
||
Box,
|
||
Operation,
|
||
Location,
|
||
Notification,
|
||
DataLine,
|
||
ShoppingCart,
|
||
Wallet,
|
||
Van,
|
||
Bell,
|
||
Setting,
|
||
Search
|
||
} from '@element-plus/icons-vue'
|
||
|
||
const userStore = useUserStore()
|
||
const router = useRouter()
|
||
|
||
// 统计数据(从后端获取)
|
||
const statisticsData = ref({
|
||
totalPatients: 0,
|
||
yesterdayPatients: 0,
|
||
patientTrend: 0,
|
||
todayRevenue: '¥ 0',
|
||
yesterdayRevenue: '¥ 0',
|
||
revenueTrend: 0,
|
||
todayAppointments: 0,
|
||
yesterdayAppointments: 0,
|
||
appointmentTrend: 0,
|
||
pendingApprovals: 0
|
||
})
|
||
|
||
// 不同角色的统计数据配置
|
||
const roleStatsConfig = {
|
||
admin: [
|
||
{ key: 'totalPatients', label: '在院患者', icon: User, type: 'primary', iconColor: '#409eff' },
|
||
{ key: 'todayRevenue', label: '今日收入', icon: Money, type: 'success', iconColor: '#67c23a' },
|
||
{ key: 'appointments', label: '今日预约', icon: Calendar, type: 'warning', iconColor: '#e6a23c' },
|
||
{ key: 'pendingApprovals', label: '待审核', icon: Document, type: 'danger', iconColor: '#f56c6c' }
|
||
],
|
||
doctor: [
|
||
{ key: 'myPatients', label: '我的患者', icon: User, type: 'primary', iconColor: '#409eff' },
|
||
{ key: 'todayAppointments', label: '今日门诊', icon: Calendar, type: 'success', iconColor: '#67c23a' },
|
||
{ key: 'pendingRecords', label: '待写病历', icon: Document, type: 'warning', iconColor: '#e6a23c' },
|
||
{ key: 'prescriptions', label: '今日处方', icon: Box, type: 'info', iconColor: '#909399' }
|
||
],
|
||
nurse: [
|
||
{ key: 'wardPatients', label: '病房患者', icon: User, type: 'primary', iconColor: '#409eff' },
|
||
{ key: 'todayTreatments', label: '今日治疗', icon: Operation, type: 'success', iconColor: '#67c23a' },
|
||
{ key: 'vitalSigns', label: '待测体征', icon: Monitor, type: 'warning', iconColor: '#e6a23c' },
|
||
{ key: 'drugDistribution', label: '发药次数', icon: Box, type: 'info', iconColor: '#909399' }
|
||
],
|
||
pharmacist: [
|
||
{ key: 'todayPrescriptions', label: '今日处方', icon: Box, type: 'primary', iconColor: '#409eff' },
|
||
{ key: 'pendingReview', label: '待审核', icon: Document, type: 'warning', iconColor: '#e6a23c' },
|
||
{ key: 'outOfStock', label: '缺货药品', icon: Warning, type: 'danger', iconColor: '#f56c6c' },
|
||
{ key: 'nearExpiry', label: '近效期', icon: Clock, type: 'info', iconColor: '#909399' }
|
||
],
|
||
cashier: [
|
||
{ key: 'todayPayments', label: '今日缴费', icon: Money, type: 'primary', iconColor: '#409eff' },
|
||
{ key: 'refundRequests', label: '退费申请', icon: Document, type: 'warning', iconColor: '#e6a23c' },
|
||
{ key: 'pendingInvoices', label: '待开发票', icon: Files, type: 'info', iconColor: '#909399' },
|
||
{ key: 'insuranceClaims', label: '医保结算', icon: Wallet, type: 'success', iconColor: '#67c23a' }
|
||
]
|
||
}
|
||
|
||
// 不同角色的快捷功能配置
|
||
const roleQuickAccessConfig = {
|
||
admin: [
|
||
{ key: 'patient', label: '患者管理', icon: User, iconColor: '#409eff', route: '/patientmanagement' },
|
||
{ key: 'appointment', label: '预约管理', icon: Calendar, iconColor: '#67c23a', route: '/appoinmentmanage' },
|
||
{ key: 'doctor', label: '医生管理', icon: User, iconColor: '#e6a23c', route: '/doctorstation' },
|
||
{ key: 'surgery', label: '手术管理', icon: Operation, iconColor: '#f56c6c', route: '/surgerymanage' },
|
||
{ key: 'drug', label: '药品管理', icon: Box, iconColor: '#909399', route: '/pharmacymanagement' },
|
||
{ key: 'statistic', label: '数据统计', icon: TrendCharts, iconColor: '#409eff', route: '/monitor' },
|
||
{ key: 'invoice', label: '发票管理', icon: Files, iconColor: '#67c23a', route: '/basicmanage/InvoiceManagement' },
|
||
{ key: 'system', label: '系统设置', icon: Setting, iconColor: '#909399', route: '/system' }
|
||
],
|
||
doctor: [
|
||
{ key: 'outpatient', label: '门诊接诊', icon: ChatDotRound, iconColor: '#409eff', route: '/doctorstation' },
|
||
{ key: 'emr', label: '病历管理', icon: Document, iconColor: '#67c23a', route: '/doctorstation/doctorphrase' },
|
||
{ key: 'prescription', label: '开立处方', icon: Box, iconColor: '#e6a23c', route: '/clinicmanagement/ePrescribing' },
|
||
{ key: 'history', label: '历史处方', icon: Clock, iconColor: '#f56c6c', route: '/clinicmanagement/historicalPrescription' },
|
||
{ key: 'schedule', label: '排班管理', icon: Calendar, iconColor: '#909399', route: '/appoinmentmanage/deptManage' },
|
||
{ key: 'inquiry', label: '患者查询', icon: Search, iconColor: '#409eff', route: '/patientmanagement' }
|
||
],
|
||
nurse: [
|
||
{ key: 'ward', label: '病房管理', icon: User, iconColor: '#409eff', route: '/inpatientNurse/inpatientNurseStation' },
|
||
{ key: 'execution', label: '医嘱执行', icon: Operation, iconColor: '#67c23a', route: '/inpatientNurse/medicalOrderExecution' },
|
||
{ key: 'proofread', label: '医嘱核对', icon: Document, iconColor: '#e6a23c', route: '/inpatientNurse/medicalOrderProofread' },
|
||
{ key: 'drugCollect', label: '领药管理', icon: Box, iconColor: '#f56c6c', route: '/inpatientNurse/medicineCollect' },
|
||
{ key: 'tpr', label: '体温单', icon: Monitor, iconColor: '#909399', route: '/inpatientNurse/tprsheet' },
|
||
{ key: 'nursing', label: '护理记录', icon: ChatDotRound, iconColor: '#409eff', route: '/inpatientNurse/nursingRecord' }
|
||
],
|
||
pharmacist: [
|
||
{ key: 'dispensing', label: '发药管理', icon: Box, iconColor: '#409eff', route: '/pharmacymanagement' },
|
||
{ key: 'prescription', label: '处方审核', icon: Document, iconColor: '#67c23a', route: '/pharmacymanagement' },
|
||
{ key: 'inventory', label: '库存管理', icon: Van, iconColor: '#e6a23c', route: '/medicineStorage' },
|
||
{ key: 'purchase', label: '采购管理', icon: ShoppingCart, iconColor: '#f56c6c', route: '/medicineStorage' },
|
||
{ key: 'warning', label: '效期预警', icon: Warning, iconColor: '#f56c6c', route: '/medicationmanagement/statisticalManagement/statisticalManagement' },
|
||
{ key: 'statistics', label: '用药统计', icon: DataLine, iconColor: '#909399', route: '/monitor' }
|
||
],
|
||
cashier: [
|
||
{ key: 'registration', label: '挂号收费', icon: Money, iconColor: '#409eff', route: '/charge/outpatientregistration' },
|
||
{ key: 'clinicCharge', label: '门诊收费', icon: Wallet, iconColor: '#67c23a', route: '/charge/cliniccharge' },
|
||
{ key: 'refund', label: '退费管理', icon: Document, iconColor: '#e6a23c', route: '/charge/clinicrefund' },
|
||
{ key: 'invoice', label: '发票打印', icon: Files, iconColor: '#f56c6c', route: '/basicmanage/InvoiceManagement' },
|
||
{ key: 'record', label: '收费记录', icon: Clock, iconColor: '#909399', route: '/charge/clinicRecord' },
|
||
{ key: 'insurance', label: '医保结算', icon: Bell, iconColor: '#409eff', route: '/ybmanagement' }
|
||
]
|
||
}
|
||
|
||
// 待办事项
|
||
const todoList = ref([
|
||
{ id: 1, title: '审核处方申请', desc: '张医生提交的5条处方待审核', priority: 'high', icon: Document, time: '10分钟前' },
|
||
{ id: 2, title: '确认患者入院', desc: '李某某(男,45岁)已办理入院', priority: 'medium', icon: User, time: '30分钟前' },
|
||
{ id: 3, title: '处理投诉反馈', desc: '患者家属对服务态度的投诉', priority: 'high', icon: ChatDotRound, time: '1小时前' },
|
||
{ id: 4, title: '药品效期预警', desc: '阿莫西林等3种药品即将过期', priority: 'low', icon: Warning, time: '2小时前' },
|
||
{ id: 5, title: '月度报表审核', desc: '11月份门诊收入报表待审核', priority: 'medium', icon: Files, time: '3小时前' }
|
||
])
|
||
|
||
// 今日日程
|
||
const scheduleList = ref([
|
||
{ id: 1, time: '09:00', title: '科室晨会', location: '第一会议室', type: 'info', tag: '日常' },
|
||
{ id: 2, time: '10:30', title: '病例讨论', location: '第二会议室', type: 'primary', tag: '会议' },
|
||
{ id: 3, time: '14:00', title: '专家查房', location: '住院部3楼', type: 'warning', tag: '重要' },
|
||
{ id: 4, time: '16:00', title: '新药培训', location: '培训中心', type: 'success', tag: '培训' }
|
||
])
|
||
|
||
// 获取问候语
|
||
const getGreeting = () => {
|
||
const hour = new Date().getHours()
|
||
if (hour < 6) return '夜深了'
|
||
if (hour < 9) return '早上好'
|
||
if (hour < 12) return '上午好'
|
||
if (hour < 14) return '中午好'
|
||
if (hour < 17) return '下午好'
|
||
if (hour < 19) return '傍晚好'
|
||
return '晚上好'
|
||
}
|
||
|
||
// 获取角色名称
|
||
const getRoleName = (role) => {
|
||
const roleMap = {
|
||
admin: '管理员',
|
||
doctor: '医生',
|
||
nurse: '护士',
|
||
pharmacist: '药剂师',
|
||
cashier: '收费员'
|
||
}
|
||
return roleMap[role] || role
|
||
}
|
||
|
||
// 获取角色标签类型
|
||
const getRoleTagType = (role) => {
|
||
const typeMap = {
|
||
admin: 'danger',
|
||
doctor: 'primary',
|
||
nurse: 'success',
|
||
pharmacist: 'warning',
|
||
cashier: 'info'
|
||
}
|
||
return typeMap[role] || ''
|
||
}
|
||
|
||
// 根据角色获取当前统计数据
|
||
const currentStats = computed(() => {
|
||
const role = userStore.roles[0] || 'admin'
|
||
const baseConfig = roleStatsConfig[role] || roleStatsConfig.admin
|
||
|
||
// 合并配置和实际数据
|
||
return baseConfig.map(stat => {
|
||
const statWith = { ...stat }
|
||
|
||
// 根据不同的 key 获取对应的值
|
||
switch (stat.key) {
|
||
case 'totalPatients':
|
||
statWith.value = statisticsData.value.totalPatients
|
||
statWith.trend = statisticsData.value.patientTrend
|
||
break
|
||
case 'todayRevenue':
|
||
statWith.value = statisticsData.value.todayRevenue
|
||
statWith.trend = statisticsData.value.revenueTrend
|
||
break
|
||
case 'appointments':
|
||
statWith.value = statisticsData.value.todayAppointments
|
||
statWith.trend = statisticsData.value.appointmentTrend
|
||
break
|
||
case 'pendingApprovals':
|
||
statWith.value = statisticsData.value.pendingApprovals
|
||
break
|
||
case 'myPatients':
|
||
statWith.value = statisticsData.value.totalPatients
|
||
statWith.trend = statisticsData.value.patientTrend
|
||
break
|
||
case 'todayAppointments':
|
||
statWith.value = statisticsData.value.todayAppointments
|
||
statWith.trend = statisticsData.value.appointmentTrend
|
||
break
|
||
case 'pendingRecords':
|
||
statWith.value = statisticsData.value.pendingApprovals
|
||
break
|
||
case 'prescriptions':
|
||
statWith.value = statisticsData.value.todayAppointments
|
||
statWith.trend = statisticsData.value.appointmentTrend
|
||
break
|
||
case 'wardPatients':
|
||
statWith.value = statisticsData.value.totalPatients
|
||
statWith.trend = statisticsData.value.patientTrend
|
||
break
|
||
case 'todayTreatments':
|
||
statWith.value = statisticsData.value.todayAppointments
|
||
statWith.trend = statisticsData.value.appointmentTrend
|
||
break
|
||
case 'vitalSigns':
|
||
statWith.value = statisticsData.value.pendingApprovals
|
||
break
|
||
case 'drugDistribution':
|
||
statWith.value = statisticsData.value.todayAppointments
|
||
statWith.trend = statisticsData.value.appointmentTrend
|
||
break
|
||
case 'todayPrescriptions':
|
||
statWith.value = statisticsData.value.todayAppointments
|
||
statWith.trend = statisticsData.value.appointmentTrend
|
||
break
|
||
case 'pendingReview':
|
||
statWith.value = statisticsData.value.pendingApprovals
|
||
break
|
||
case 'outOfStock':
|
||
statWith.value = statisticsData.value.pendingApprovals
|
||
break
|
||
case 'nearExpiry':
|
||
statWith.value = statisticsData.value.pendingApprovals
|
||
break
|
||
case 'todayPayments':
|
||
statWith.value = statisticsData.value.todayRevenue
|
||
statWith.trend = statisticsData.value.revenueTrend
|
||
break
|
||
case 'refundRequests':
|
||
statWith.value = statisticsData.value.pendingApprovals
|
||
break
|
||
case 'pendingInvoices':
|
||
statWith.value = statisticsData.value.pendingApprovals
|
||
break
|
||
case 'insuranceClaims':
|
||
statWith.value = statisticsData.value.todayAppointments
|
||
statWith.trend = statisticsData.value.appointmentTrend
|
||
break
|
||
default:
|
||
statWith.value = '0'
|
||
statWith.trend = 0
|
||
}
|
||
|
||
return statWith
|
||
})
|
||
})
|
||
|
||
// 根据角色获取快捷功能
|
||
const quickAccess = computed(() => {
|
||
const role = userStore.roles[0] || 'admin'
|
||
return roleQuickAccessConfig[role] || roleQuickAccessConfig.admin
|
||
})
|
||
|
||
// 处理统计卡片点击
|
||
const handleStatClick = (stat) => {
|
||
console.log('Stat clicked:', stat)
|
||
// 根据不同的统计项跳转到相应的详情页面
|
||
if (stat.key === 'totalPatients' || stat.key === 'myPatients' || stat.key === 'wardPatients') {
|
||
// 在院患者/我的患者/病房患者 - 跳转到患者管理页面
|
||
router.push('/patient/patientmgr')
|
||
} else if (stat.key === 'todayRevenue' || stat.key === 'todayPayments') {
|
||
// 跳转到收费页面
|
||
router.push('/charge/cliniccharge')
|
||
} else if (stat.key === 'appointments') {
|
||
// 跳转到预约管理页面
|
||
router.push('/appoinmentmanage')
|
||
} else if (stat.key === 'todayAppointments') {
|
||
// 跳转到今日门诊模块
|
||
router.push('/doctorstation/today-outpatient')
|
||
} else if (stat.key === 'pendingApprovals' || stat.key === 'pendingReview') {
|
||
// 跳转到待审核页面
|
||
router.push('/clinicmanagement/ePrescribing')
|
||
}
|
||
}
|
||
|
||
// 处理快捷功能点击
|
||
const handleQuickAccess = (func) => {
|
||
if (func.route) {
|
||
router.push(func.route)
|
||
}
|
||
}
|
||
|
||
// 处理待办事项点击
|
||
const handleTodoClick = (todo) => {
|
||
console.log('Todo clicked:', todo)
|
||
// 跳转到相应的处理页面
|
||
}
|
||
|
||
// 显示全部功能
|
||
const showAllFunctions = () => {
|
||
// 跳转到功能菜单页面
|
||
}
|
||
|
||
// 显示全部待办
|
||
const showAllTodos = () => {
|
||
// 跳转到待办事项页面
|
||
}
|
||
|
||
// 管理日程
|
||
const showSchedule = () => {
|
||
// 跳转到日程管理页面
|
||
}
|
||
|
||
// 获取统计数据
|
||
const fetchStatsData = async () => {
|
||
try {
|
||
const res = await getHomeStatistics()
|
||
if (res.data) {
|
||
statisticsData.value = res.data
|
||
console.log('统计数据:', statisticsData.value)
|
||
}
|
||
} catch (error) {
|
||
console.error('获取统计数据失败:', error)
|
||
}
|
||
}
|
||
|
||
// 获取待办事项(实际应用中应该从API获取)
|
||
const fetchTodoList = async () => {
|
||
// TODO: 调用API获取真实数据
|
||
console.log('Fetching todo list...')
|
||
}
|
||
|
||
// 获取日程数据(实际应用中应该从API获取)
|
||
const fetchScheduleList = async () => {
|
||
// TODO: 调用API获取真实数据
|
||
console.log('Fetching schedule list...')
|
||
}
|
||
|
||
onMounted(() => {
|
||
fetchStatsData()
|
||
fetchTodoList()
|
||
fetchScheduleList()
|
||
})
|
||
</script>
|
||
|
||
<style scoped lang="scss">
|
||
.home-container {
|
||
padding: 20px;
|
||
background: #f5f7fa;
|
||
min-height: calc(100vh - 120px);
|
||
}
|
||
|
||
/* 顶部欢迎区域 */
|
||
.welcome-section {
|
||
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
|
||
border-radius: 12px;
|
||
padding: 30px;
|
||
margin-bottom: 20px;
|
||
color: white;
|
||
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
|
||
|
||
.welcome-content {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
|
||
.greeting {
|
||
h1 {
|
||
font-size: 28px;
|
||
font-weight: 600;
|
||
margin: 0 0 8px 0;
|
||
}
|
||
|
||
p {
|
||
font-size: 16px;
|
||
opacity: 0.9;
|
||
margin: 0;
|
||
}
|
||
}
|
||
|
||
.role-badge {
|
||
:deep(.el-tag) {
|
||
font-size: 16px;
|
||
padding: 12px 24px;
|
||
height: auto;
|
||
background: rgba(255, 255, 255, 0.2);
|
||
border: 1px solid rgba(255, 255, 255, 0.3);
|
||
color: white;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
/* 统计卡片网格 */
|
||
.stats-grid {
|
||
display: grid;
|
||
grid-template-columns: repeat(auto-fit, minmax(260px, 1fr));
|
||
gap: 16px;
|
||
margin-bottom: 20px;
|
||
|
||
.stat-card {
|
||
background: white;
|
||
border-radius: 12px;
|
||
padding: 24px;
|
||
display: flex;
|
||
align-items: center;
|
||
gap: 16px;
|
||
cursor: pointer;
|
||
transition: all 0.3s ease;
|
||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
|
||
border-left: 4px solid transparent;
|
||
|
||
&:hover {
|
||
transform: translateY(-4px);
|
||
box-shadow: 0 8px 24px rgba(0, 0, 0, 0.1);
|
||
}
|
||
|
||
&.stat-primary {
|
||
border-left-color: #409eff;
|
||
}
|
||
|
||
&.stat-success {
|
||
border-left-color: #67c23a;
|
||
}
|
||
|
||
&.stat-warning {
|
||
border-left-color: #e6a23c;
|
||
}
|
||
|
||
&.stat-danger {
|
||
border-left-color: #f56c6c;
|
||
}
|
||
|
||
&.stat-info {
|
||
border-left-color: #909399;
|
||
}
|
||
|
||
.stat-icon {
|
||
width: 60px;
|
||
height: 60px;
|
||
border-radius: 12px;
|
||
display: flex;
|
||
align-items: center;
|
||
justify-content: center;
|
||
background: #f5f7fa;
|
||
}
|
||
|
||
.stat-content {
|
||
flex: 1;
|
||
|
||
.stat-value {
|
||
font-size: 24px;
|
||
font-weight: 600;
|
||
color: #303133;
|
||
margin-bottom: 4px;
|
||
}
|
||
|
||
.stat-label {
|
||
font-size: 14px;
|
||
color: #909399;
|
||
margin-bottom: 8px;
|
||
}
|
||
|
||
.stat-trend {
|
||
font-size: 12px;
|
||
|
||
.trend-up {
|
||
color: #67c23a;
|
||
margin-right: 4px;
|
||
}
|
||
|
||
.trend-down {
|
||
color: #f56c6c;
|
||
margin-right: 4px;
|
||
}
|
||
|
||
.trend-label {
|
||
color: #c0c4cc;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
/* 快捷功能区域 */
|
||
.quick-access-section {
|
||
background: white;
|
||
border-radius: 12px;
|
||
padding: 24px;
|
||
margin-bottom: 20px;
|
||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
|
||
|
||
.section-header {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
margin-bottom: 20px;
|
||
|
||
h3 {
|
||
font-size: 18px;
|
||
font-weight: 600;
|
||
color: #303133;
|
||
margin: 0;
|
||
}
|
||
}
|
||
|
||
.quick-access-grid {
|
||
display: grid;
|
||
grid-template-columns: repeat(auto-fit, minmax(140px, 1fr));
|
||
gap: 16px;
|
||
|
||
.quick-access-card {
|
||
display: flex;
|
||
flex-direction: column;
|
||
align-items: center;
|
||
justify-content: center;
|
||
padding: 24px 16px;
|
||
border-radius: 8px;
|
||
background: #f5f7fa;
|
||
cursor: pointer;
|
||
transition: all 0.3s ease;
|
||
|
||
&:hover {
|
||
background: #ecf5ff;
|
||
transform: translateY(-4px);
|
||
|
||
.quick-label {
|
||
color: #409eff;
|
||
}
|
||
}
|
||
|
||
.quick-icon {
|
||
margin-bottom: 12px;
|
||
}
|
||
|
||
.quick-label {
|
||
font-size: 14px;
|
||
color: #606266;
|
||
text-align: center;
|
||
transition: color 0.3s;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
/* 待办事项区域 */
|
||
.todo-section {
|
||
background: white;
|
||
border-radius: 12px;
|
||
padding: 24px;
|
||
margin-bottom: 20px;
|
||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
|
||
|
||
.section-header {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
margin-bottom: 20px;
|
||
|
||
h3 {
|
||
font-size: 18px;
|
||
font-weight: 600;
|
||
color: #303133;
|
||
margin: 0;
|
||
}
|
||
}
|
||
|
||
.todo-list {
|
||
.todo-item {
|
||
display: flex;
|
||
align-items: center;
|
||
padding: 16px;
|
||
border-radius: 8px;
|
||
margin-bottom: 12px;
|
||
background: #f5f7fa;
|
||
cursor: pointer;
|
||
transition: all 0.3s ease;
|
||
border-left: 3px solid transparent;
|
||
|
||
&:hover {
|
||
background: #ecf5ff;
|
||
}
|
||
|
||
&.priority-high {
|
||
border-left-color: #f56c6c;
|
||
}
|
||
|
||
&.priority-medium {
|
||
border-left-color: #e6a23c;
|
||
}
|
||
|
||
&.priority-low {
|
||
border-left-color: #67c23a;
|
||
}
|
||
|
||
&:last-child {
|
||
margin-bottom: 0;
|
||
}
|
||
|
||
.todo-icon {
|
||
margin-right: 12px;
|
||
color: #909399;
|
||
}
|
||
|
||
.todo-content {
|
||
flex: 1;
|
||
|
||
.todo-title {
|
||
font-size: 14px;
|
||
color: #303133;
|
||
font-weight: 500;
|
||
margin-bottom: 4px;
|
||
}
|
||
|
||
.todo-desc {
|
||
font-size: 12px;
|
||
color: #909399;
|
||
}
|
||
}
|
||
|
||
.todo-time {
|
||
font-size: 12px;
|
||
color: #c0c4cc;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
/* 日程区域 */
|
||
.schedule-section {
|
||
background: white;
|
||
border-radius: 12px;
|
||
padding: 24px;
|
||
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.05);
|
||
|
||
.section-header {
|
||
display: flex;
|
||
justify-content: space-between;
|
||
align-items: center;
|
||
margin-bottom: 20px;
|
||
|
||
h3 {
|
||
font-size: 18px;
|
||
font-weight: 600;
|
||
color: #303133;
|
||
margin: 0;
|
||
}
|
||
}
|
||
|
||
.schedule-list {
|
||
.schedule-item {
|
||
display: flex;
|
||
align-items: center;
|
||
padding: 16px;
|
||
border-radius: 8px;
|
||
margin-bottom: 12px;
|
||
background: #f5f7fa;
|
||
transition: all 0.3s ease;
|
||
|
||
&:hover {
|
||
background: #ecf5ff;
|
||
}
|
||
|
||
&:last-child {
|
||
margin-bottom: 0;
|
||
}
|
||
|
||
.schedule-time {
|
||
width: 80px;
|
||
font-size: 18px;
|
||
font-weight: 600;
|
||
color: #409eff;
|
||
}
|
||
|
||
.schedule-content {
|
||
flex: 1;
|
||
margin: 0 16px;
|
||
|
||
.schedule-title {
|
||
font-size: 14px;
|
||
color: #303133;
|
||
font-weight: 500;
|
||
margin-bottom: 4px;
|
||
}
|
||
|
||
.schedule-location {
|
||
display: flex;
|
||
align-items: center;
|
||
font-size: 12px;
|
||
color: #909399;
|
||
|
||
.el-icon {
|
||
margin-right: 4px;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
/* 响应式设计 */
|
||
@media (max-width: 1200px) {
|
||
.stats-grid {
|
||
grid-template-columns: repeat(2, 1fr);
|
||
}
|
||
|
||
.quick-access-grid {
|
||
grid-template-columns: repeat(4, 1fr);
|
||
}
|
||
}
|
||
|
||
@media (max-width: 768px) {
|
||
.home-container {
|
||
padding: 12px;
|
||
}
|
||
|
||
.welcome-section {
|
||
padding: 20px;
|
||
|
||
.welcome-content {
|
||
flex-direction: column;
|
||
align-items: flex-start;
|
||
gap: 16px;
|
||
|
||
.greeting {
|
||
h1 {
|
||
font-size: 22px;
|
||
}
|
||
|
||
p {
|
||
font-size: 14px;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
|
||
.stats-grid {
|
||
grid-template-columns: 1fr;
|
||
}
|
||
|
||
.quick-access-grid {
|
||
grid-template-columns: repeat(3, 1fr);
|
||
}
|
||
}
|
||
|
||
@media (max-width: 480px) {
|
||
.quick-access-grid {
|
||
grid-template-columns: repeat(2, 1fr);
|
||
}
|
||
}
|
||
</style>
|