| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299 |
- <template>
- <div class="dashboard-container">
- <!-- Statistics Cards -->
- <el-row :gutter="20">
- <el-col :span="6">
- <el-card shadow="hover" class="stat-card">
- <div class="stat-content">
- <div class="stat-icon news-bg">
- <el-icon><Reading /></el-icon>
- </div>
- <div class="stat-info">
- <div class="stat-value">{{ stats.news }}</div>
- <div class="stat-label">新闻动态</div>
- </div>
- </div>
- </el-card>
- </el-col>
- <el-col :span="6">
- <el-card shadow="hover" class="stat-card">
- <div class="stat-content">
- <div class="stat-icon case-bg">
- <el-icon><Suitcase /></el-icon>
- </div>
- <div class="stat-info">
- <div class="stat-value">{{ stats.cases }}</div>
- <div class="stat-label">成功案例</div>
- </div>
- </div>
- </el-card>
- </el-col>
- <el-col :span="6">
- <el-card shadow="hover" class="stat-card">
- <div class="stat-content">
- <div class="stat-icon partner-bg">
- <el-icon><User /></el-icon>
- </div>
- <div class="stat-info">
- <div class="stat-value">{{ stats.partners }}</div>
- <div class="stat-label">合作伙伴</div>
- </div>
- </div>
- </el-card>
- </el-col>
- <el-col :span="6">
- <el-card shadow="hover" class="stat-card">
- <div class="stat-content">
- <div class="stat-icon user-bg">
- <el-icon><Setting /></el-icon>
- </div>
- <div class="stat-info">
- <div class="stat-value">{{ stats.users }}</div>
- <div class="stat-label">系统用户</div>
- </div>
- </div>
- </el-card>
- </el-col>
- </el-row>
- <!-- Charts and Recent Activity -->
- <el-row :gutter="20" class="mt-20">
- <el-col :span="16">
- <el-card shadow="hover" class="chart-card">
- <template #header>
- <div class="card-header">
- <span>数据概览</span>
- </div>
- </template>
- <div ref="chartRef" style="width: 100%; height: 350px;"></div>
- </el-card>
- </el-col>
- <el-col :span="8">
- <el-card shadow="hover" class="activity-card">
- <template #header>
- <div class="card-header">
- <span>最新新闻</span>
- <el-button text type="primary" @click="$router.push('/news')">查看更多</el-button>
- </div>
- </template>
- <div class="activity-list">
- <div v-for="item in recentNews" :key="item.id" class="activity-item">
- <span class="activity-title">{{ item.title }}</span>
- <span class="activity-time">{{ item.publish_time }}</span>
- </div>
- <el-empty v-if="recentNews.length === 0" description="暂无数据" />
- </div>
- </el-card>
- </el-col>
- </el-row>
-
- <!-- Quick Actions -->
- <el-row :gutter="20" class="mt-20">
- <el-col :span="24">
- <el-card shadow="hover">
- <template #header>
- <div class="card-header">
- <span>快捷操作</span>
- </div>
- </template>
- <div class="quick-actions">
- <el-button type="primary" plain icon="Plus" @click="$router.push('/news')">发布新闻</el-button>
- <el-button type="success" plain icon="Plus" @click="$router.push('/cases/education')">添加案例</el-button>
- <el-button type="warning" plain icon="Plus" @click="$router.push('/partners')">添加伙伴</el-button>
- <el-button type="info" plain icon="Picture" @click="$router.push('/carousels/home')">轮播管理</el-button>
- </div>
- </el-card>
- </el-col>
- </el-row>
- </div>
- </template>
- <script setup>
- import { ref, onMounted, reactive } from 'vue'
- import api from '../api'
- import * as echarts from 'echarts'
- const stats = reactive({
- news: 0,
- cases: 0,
- partners: 0,
- users: 0
- })
- const recentNews = ref([])
- const chartRef = ref(null)
- const fetchData = async () => {
- try {
- // Parallel fetching for performance
- const [newsRes, partnersRes, usersRes, eduCasesRes, servCasesRes] = await Promise.all([
- api.get('/news'),
- api.get('/partners'),
- api.get('/users'),
- api.get('/cases', { params: { section: 'education' } }),
- api.get('/cases', { params: { section: 'service' } })
- ])
- stats.news = newsRes.data.length
- stats.partners = partnersRes.data.length
- stats.users = usersRes.data.length
- stats.cases = eduCasesRes.data.length + servCasesRes.data.length
- // Recent news (last 5)
- recentNews.value = newsRes.data.slice(-5).reverse()
- initChart()
- } catch (error) {
- console.error('Failed to fetch dashboard data', error)
- }
- }
- const initChart = () => {
- if (!chartRef.value) return
-
- const myChart = echarts.init(chartRef.value)
- const option = {
- tooltip: {
- trigger: 'item'
- },
- legend: {
- top: '5%',
- left: 'center'
- },
- series: [
- {
- name: '数据分布',
- type: 'pie',
- radius: ['40%', '70%'],
- avoidLabelOverlap: false,
- itemStyle: {
- borderRadius: 10,
- borderColor: '#fff',
- borderWidth: 2
- },
- label: {
- show: false,
- position: 'center'
- },
- emphasis: {
- label: {
- show: true,
- fontSize: 20,
- fontWeight: 'bold'
- }
- },
- labelLine: {
- show: false
- },
- data: [
- { value: stats.news, name: '新闻动态' },
- { value: stats.cases, name: '成功案例' },
- { value: stats.partners, name: '合作伙伴' },
- { value: stats.users, name: '系统用户' }
- ]
- }
- ]
- }
- myChart.setOption(option)
-
- // Responsive resize
- window.addEventListener('resize', () => {
- myChart.resize()
- })
- }
- onMounted(() => {
- fetchData()
- })
- </script>
- <style scoped>
- .dashboard-container {
- padding: 0;
- }
- .mt-20 {
- margin-top: 20px;
- }
- .stat-card {
- border: none;
- border-radius: 8px;
- overflow: hidden;
- }
- .stat-content {
- display: flex;
- align-items: center;
- }
- .stat-icon {
- width: 60px;
- height: 60px;
- border-radius: 50%;
- display: flex;
- align-items: center;
- justify-content: center;
- margin-right: 15px;
- font-size: 30px;
- color: #fff;
- }
- .news-bg { background: linear-gradient(135deg, #1890ff 0%, #36cfc9 100%); }
- .case-bg { background: linear-gradient(135deg, #ff7a45 0%, #ffc53d 100%); }
- .partner-bg { background: linear-gradient(135deg, #722ed1 0%, #b37feb 100%); }
- .user-bg { background: linear-gradient(135deg, #52c41a 0%, #95de64 100%); }
- .stat-info {
- flex: 1;
- }
- .stat-value {
- font-size: 24px;
- font-weight: bold;
- color: #333;
- line-height: 1.2;
- }
- .stat-label {
- font-size: 14px;
- color: #888;
- margin-top: 5px;
- }
- .chart-card {
- height: 450px;
- }
- .activity-card {
- height: 450px;
- display: flex;
- flex-direction: column;
- }
- .card-header {
- display: flex;
- justify-content: space-between;
- align-items: center;
- font-weight: bold;
- }
- .activity-list {
- flex: 1;
- overflow-y: auto;
- }
- .activity-item {
- display: flex;
- justify-content: space-between;
- padding: 12px 0;
- border-bottom: 1px solid #f0f0f0;
- }
- .activity-item:last-child {
- border-bottom: none;
- }
- .activity-title {
- color: #333;
- overflow: hidden;
- text-overflow: ellipsis;
- white-space: nowrap;
- max-width: 70%;
- }
- .activity-time {
- color: #999;
- font-size: 12px;
- }
- .quick-actions {
- display: flex;
- gap: 15px;
- }
- </style>
|