You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

468 lines
10 KiB

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

<template>
<div class="dashboard-container">
<!-- 页面头部 -->
<div class="page-header">
<h1 class="page-title">
<el-icon :size="24"><Odometer /></el-icon>
首页
</h1>
<p class="page-subtitle">欢迎回来这里是您的资金使用概览</p>
</div>
<!-- 统计卡片 -->
<div class="stats-grid">
<el-card
v-for="(stat, index) in stats"
:key="index"
class="stat-card"
shadow="hover"
>
<div class="stat-card-header">
<div class="stat-card-title">{{ stat.title }}</div>
<div class="stat-card-icon" :class="stat.color">
<el-icon :size="24"><component :is="getIcon(stat.icon)" /></el-icon>
</div>
</div>
<div class="stat-card-value">{{ stat.value }}</div>
<div class="stat-card-change" :class="stat.trendType">
<el-icon><ArrowUp v-if="stat.trendType === 'up'" /><ArrowDown v-else /></el-icon>
<span>{{ stat.trend }}</span>
</div>
</el-card>
</div>
<!-- 主要内容区域 -->
<div class="content-grid">
<!-- 待办事项列表 -->
<el-card class="todo-card">
<template #header>
<div class="card-header">
<el-icon><List /></el-icon>
<span>待办事项</span>
</div>
</template>
<el-table :data="todoList" style="width: 100%">
<el-table-column label="申请类型" min-width="200">
<template #default="scope">
<div class="type-cell">
<el-icon :size="16" :color="getTypeColor(scope.row.typeIcon)">
<component :is="getIcon(scope.row.typeIcon)" />
</el-icon>
<span>{{ scope.row.type }}</span>
</div>
</template>
</el-table-column>
<el-table-column prop="applicant" label="申请人" width="100" />
<el-table-column prop="amount" label="金额" width="120">
<template #default="scope">
<span style="color: #409eff; font-weight: 600;">{{ scope.row.amount }}</span>
</template>
</el-table-column>
<el-table-column prop="status" label="状态" width="100">
<template #default="scope">
<el-tag :type="getStatusType(scope.row.status)">
{{ scope.row.status }}
</el-tag>
</template>
</el-table-column>
<el-table-column prop="submitTime" label="提交时间" width="160" />
<el-table-column label="操作" width="100">
<template #default="scope">
<el-button
:type="scope.row.status === '已批准' ? 'success' : 'primary'"
link
size="small"
@click="handleAction(scope.row)"
>
{{ scope.row.status === '已批准' ? '支付' : '查看' }}
</el-button>
</template>
</el-table-column>
</el-table>
</el-card>
<!-- 快速操作和进度 -->
<div class="right-column">
<!-- 快速操作 -->
<el-card class="quick-actions-card">
<template #header>
<div class="card-header">
<el-icon><Lightning /></el-icon>
<span>快速操作</span>
</div>
</template>
<div class="quick-actions">
<div
v-for="action in actions"
:key="action.id"
class="quick-action-btn"
@click="handleQuickAction(action)"
>
<el-icon :size="32" :color="getActionColor(action.color)">
<component :is="getIcon(action.icon)" />
</el-icon>
<span>{{ action.title }}</span>
</div>
</div>
</el-card>
<!-- 审批进度 -->
<el-card class="progress-card">
<template #header>
<div class="card-header">
<el-icon><TrendCharts /></el-icon>
<span>本月审批进度</span>
</div>
</template>
<div class="progress-list">
<div
v-for="(item, index) in progress"
:key="index"
class="progress-item"
>
<div class="progress-label">
<span>{{ item.category }}</span>
<strong>{{ item.percentage }}%</strong>
</div>
<el-progress
:percentage="item.percentage"
:color="getProgressColor(item.color)"
:stroke-width="8"
/>
</div>
</div>
</el-card>
</div>
</div>
</div>
</template>
<script setup>
import { ref, onMounted } from 'vue'
import { useRouter } from 'vue-router'
import { ElMessage } from 'element-plus'
import {
Odometer,
DocumentChecked,
Money,
CircleCheck,
ArrowUp,
ArrowDown,
List,
Lightning,
TrendCharts,
ShoppingBag,
Promotion,
ChatLineRound,
Document,
Tools,
MagicStick
} from '@element-plus/icons-vue'
import {
dashboardStats,
todoTableData,
quickActions,
approvalProgress
} from '@/mock'
const router = useRouter()
const stats = ref([])
const todoList = ref([])
const actions = ref([])
const progress = ref([])
const iconMap = {
DocumentChecked,
Money,
CircleCheck,
ShoppingBag,
Promotion,
ChatLineRound,
Document,
Tools,
MagicStick
}
const getIcon = (iconName) => {
return iconMap[iconName] || Document
}
const getTypeColor = (iconName) => {
const colorMap = {
ShoppingBag: '#409eff',
Promotion: '#909399',
ChatLineRound: '#67c23a',
Document: '#e6a23c',
Tools: '#909399'
}
return colorMap[iconName] || '#409eff'
}
const getStatusType = (status) => {
const statusMap = {
'待审批': 'warning',
'已批准': 'success',
'处理中': 'primary',
'已拒绝': 'danger'
}
return statusMap[status] || ''
}
const getActionColor = (color) => {
const colorMap = {
primary: '#409eff',
success: '#67c23a',
info: '#909399',
warning: '#e6a23c'
}
return colorMap[color] || '#409eff'
}
const getProgressColor = (color) => {
const colorMap = {
primary: '#409eff',
success: '#67c23a',
info: '#909399',
warning: '#e6a23c'
}
return colorMap[color] || '#409eff'
}
const handleAction = (row) => {
if (row.status === '已批准') {
ElMessage.info(`跳转到支付页面: ${row.type}`)
router.push('/payment/direct-payment')
} else {
ElMessage.info(`查看详情: ${row.type}`)
}
}
const handleQuickAction = (action) => {
ElMessage.info(`快速操作: ${action.title}`)
if (action.route) {
router.push(action.route)
}
}
onMounted(async () => {
// 模拟加载数据
await new Promise(resolve => setTimeout(resolve, 300))
stats.value = dashboardStats
todoList.value = todoTableData
actions.value = quickActions
progress.value = approvalProgress
})
</script>
<style scoped>
.dashboard-container {
padding: 20px;
background: #f5f7fa;
min-height: 100%;
}
.page-header {
background: white;
padding: 25px 30px;
border-radius: 10px;
margin-bottom: 20px;
box-shadow: 0 2px 10px rgba(0, 0, 0, 0.05);
}
.page-title {
font-size: 24px;
font-weight: 600;
color: #333;
margin: 0 0 5px 0;
display: flex;
align-items: center;
gap: 10px;
}
.page-subtitle {
color: #666;
font-size: 14px;
margin: 0;
}
.stats-grid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
gap: 20px;
margin-bottom: 20px;
}
.stat-card {
transition: transform 0.3s, box-shadow 0.3s;
}
.stat-card:hover {
transform: translateY(-5px);
box-shadow: 0 5px 20px rgba(0, 0, 0, 0.1);
}
.stat-card-header {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 15px;
}
.stat-card-title {
font-size: 14px;
color: #666;
font-weight: 500;
}
.stat-card-icon {
width: 50px;
height: 50px;
border-radius: 10px;
display: flex;
align-items: center;
justify-content: center;
color: white;
}
.stat-card-icon.primary {
background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
}
.stat-card-icon.success {
background: linear-gradient(135deg, #11998e 0%, #38ef7d 100%);
}
.stat-card-icon.warning {
background: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);
}
.stat-card-icon.info {
background: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%);
}
.stat-card-value {
font-size: 32px;
font-weight: 700;
color: #333;
margin-bottom: 5px;
}
.stat-card-change {
font-size: 14px;
display: flex;
align-items: center;
gap: 5px;
}
.stat-card-change.positive {
color: #10b981;
}
.stat-card-change.negative {
color: #ef4444;
}
.content-grid {
display: grid;
grid-template-columns: 2fr 1fr;
gap: 20px;
}
.todo-card {
min-height: 500px;
}
.card-header {
display: flex;
align-items: center;
gap: 8px;
font-weight: 600;
font-size: 16px;
color: #333;
}
.type-cell {
display: flex;
align-items: center;
gap: 8px;
}
.right-column {
display: flex;
flex-direction: column;
gap: 20px;
}
.quick-actions-card {
min-height: 200px;
}
.quick-actions {
display: grid;
grid-template-columns: repeat(2, 1fr);
gap: 15px;
}
.quick-action-btn {
padding: 20px;
border: 2px solid #e5e7eb;
border-radius: 10px;
text-align: center;
cursor: pointer;
transition: all 0.3s;
display: flex;
flex-direction: column;
align-items: center;
gap: 10px;
}
.quick-action-btn:hover {
border-color: #667eea;
background-color: #f0f0ff;
transform: translateY(-2px);
}
.quick-action-btn span {
font-weight: 500;
font-size: 14px;
color: #333;
}
.progress-card {
min-height: 250px;
}
.progress-list {
display: flex;
flex-direction: column;
gap: 20px;
}
.progress-item {
margin-bottom: 0;
}
.progress-label {
display: flex;
justify-content: space-between;
margin-bottom: 8px;
font-size: 14px;
color: #666;
}
.progress-label strong {
color: #333;
}
@media (max-width: 768px) {
.content-grid {
grid-template-columns: 1fr;
}
.stats-grid {
grid-template-columns: 1fr;
}
}
</style>