| | |
| | | <script lang="ts" setup> |
| | | import type { AnalysisOverviewItem } from '../../../packages/effects/common-ui/src'; |
| | | import type { TabOption } from '../../../packages/types/src'; |
| | | import { computed } from 'vue'; |
| | | import { useRouter } from 'vue-router'; |
| | | |
| | | import { |
| | | AnalysisChartCard, |
| | | AnalysisChartsTabs, |
| | | AnalysisOverview, |
| | | } from '../../../packages/effects/common-ui/src'; |
| | | import { |
| | | SvgBellIcon, |
| | | SvgCakeIcon, |
| | | SvgCardIcon, |
| | | SvgDownloadIcon, |
| | | } from '../../../packages/icons/src'; |
| | | import { Page } from '@vben/common-ui'; |
| | | |
| | | import AnalyticsTrends from './analytics-trends.vue'; |
| | | import AnalyticsVisitsData from './analytics-visits-data.vue'; |
| | | import AnalyticsVisitsSales from './analytics-visits-sales.vue'; |
| | | import AnalyticsVisitsSource from './analytics-visits-source.vue'; |
| | | import AnalyticsVisits from './analytics-visits.vue'; |
| | | import { Col, Row } from 'ant-design-vue'; |
| | | |
| | | const overviewItems: AnalysisOverviewItem[] = [ |
| | | { |
| | | icon: SvgCardIcon, |
| | | title: '用户量', |
| | | totalTitle: '总用户量', |
| | | totalValue: 120_000, |
| | | value: 2000, |
| | | }, |
| | | { |
| | | icon: SvgCakeIcon, |
| | | title: '访问量', |
| | | totalTitle: '总访问量', |
| | | totalValue: 500_000, |
| | | value: 20_000, |
| | | }, |
| | | { |
| | | icon: SvgDownloadIcon, |
| | | title: '下载量', |
| | | totalTitle: '总下载量', |
| | | totalValue: 120_000, |
| | | value: 8000, |
| | | }, |
| | | { |
| | | icon: SvgBellIcon, |
| | | title: '使用量', |
| | | totalTitle: '总使用量', |
| | | totalValue: 50_000, |
| | | value: 5000, |
| | | }, |
| | | ]; |
| | | import KpiCards from './modules/kpi-cards.vue'; |
| | | import QuickActions from './modules/quick-actions.vue'; |
| | | import RecentApplications from './modules/recent-applications.vue'; |
| | | import TodoPanel from './modules/todo-panel.vue'; |
| | | |
| | | const chartTabs: TabOption[] = [ |
| | | { |
| | | label: '流量趋势', |
| | | value: 'trends', |
| | | }, |
| | | { |
| | | label: '月访问量', |
| | | value: 'visits', |
| | | }, |
| | | ]; |
| | | defineOptions({ name: 'DashboardAnalytics' }); |
| | | |
| | | // vite.config.ts 注入的公司 Logo 路径 |
| | | declare const __APP_LOGO__: string; |
| | | const brandLogo = __APP_LOGO__ || '/logos/JSSM.svg'; |
| | | |
| | | const router = useRouter(); |
| | | |
| | | const greeting = computed(() => { |
| | | const hour = new Date().getHours(); |
| | | if (hour < 6) return '夜深了'; |
| | | if (hour < 9) return '早上好'; |
| | | if (hour < 12) return '上午好'; |
| | | if (hour < 14) return '中午好'; |
| | | if (hour < 18) return '下午好'; |
| | | return '晚上好'; |
| | | }); |
| | | |
| | | const today = computed(() => { |
| | | const d = new Date(); |
| | | const weekNames = ['日', '一', '二', '三', '四', '五', '六']; |
| | | return `${d.getFullYear()}年${d.getMonth() + 1}月${d.getDate()}日 星期${weekNames[d.getDay()]}`; |
| | | }); |
| | | |
| | | function handleNavigate(name: string) { |
| | | router.push({ name }); |
| | | } |
| | | </script> |
| | | |
| | | <template> |
| | | <div class="p-5"> |
| | | <AnalysisOverview :items="overviewItems" /> |
| | | <AnalysisChartsTabs :tabs="chartTabs" class="mt-5"> |
| | | <template #trends> |
| | | <AnalyticsTrends /> |
| | | </template> |
| | | <template #visits> |
| | | <AnalyticsVisits /> |
| | | </template> |
| | | </AnalysisChartsTabs> |
| | | |
| | | <div class="mt-5 w-full md:flex"> |
| | | <AnalysisChartCard class="mt-5 md:mt-0 md:mr-4 md:w-1/3" title="访问数量"> |
| | | <AnalyticsVisitsData /> |
| | | </AnalysisChartCard> |
| | | <AnalysisChartCard class="mt-5 md:mt-0 md:mr-4 md:w-1/3" title="访问来源"> |
| | | <AnalyticsVisitsSource /> |
| | | </AnalysisChartCard> |
| | | <AnalysisChartCard class="mt-5 md:mt-0 md:w-1/3" title="访问来源"> |
| | | <AnalyticsVisitsSales /> |
| | | </AnalysisChartCard> |
| | | <Page auto-content-height> |
| | | <!-- 欢迎栏:藏青底 + 金色描边 --> |
| | | <div class="elite-hero mb-6"> |
| | | <img alt="" class="elite-hero-mark" :src="brandLogo" /> |
| | | <div class="relative z-10 flex flex-wrap items-center justify-between gap-4"> |
| | | <div> |
| | | <h1 class="elite-hero-title">{{ greeting }},欢迎回来</h1> |
| | | <p class="elite-hero-date">{{ today }}</p> |
| | | </div> |
| | | <div class="elite-status"> |
| | | <span class="elite-status-dot"></span> |
| | | 系统运行中 |
| | | </div> |
| | | </div> |
| | | </div> |
| | | </div> |
| | | |
| | | <!-- 系统指标 --> |
| | | <KpiCards @navigate="handleNavigate" /> |
| | | |
| | | <!-- 待办 + 快捷入口 --> |
| | | <Row :gutter="16" class="mb-4"> |
| | | <Col :lg="14" :md="24" :sm="24" :xl="14" :xs="24" class="mb-4"> |
| | | <TodoPanel @navigate="handleNavigate" /> |
| | | </Col> |
| | | <Col :lg="10" :md="24" :sm="24" :xl="10" :xs="24" class="mb-4"> |
| | | <QuickActions @navigate="handleNavigate" /> |
| | | </Col> |
| | | </Row> |
| | | |
| | | <!-- 最近课题申报 --> |
| | | <RecentApplications @navigate="handleNavigate" /> |
| | | </Page> |
| | | </template> |
| | | |
| | | <style scoped> |
| | | /* ===== 欢迎栏 ===== */ |
| | | .elite-hero { |
| | | position: relative; |
| | | overflow: hidden; |
| | | border-radius: 12px; |
| | | padding: 30px 34px; |
| | | background: |
| | | radial-gradient(circle at 88% 12%, rgba(212, 175, 55, 0.08), transparent 50%), |
| | | linear-gradient(135deg, #123156 0%, #0f2440 58%, #0b1c33 100%); |
| | | border: 1px solid rgba(212, 175, 55, 0.24); |
| | | box-shadow: 0 4px 18px rgba(15, 36, 64, 0.12); |
| | | } |
| | | |
| | | /* 右侧淡淡的徽标水印 */ |
| | | .elite-hero-mark { |
| | | position: absolute; |
| | | top: 50%; |
| | | right: 168px; |
| | | width: 118px; |
| | | height: 118px; |
| | | pointer-events: none; |
| | | opacity: 0.05; |
| | | transform: translateY(-50%); |
| | | } |
| | | |
| | | .elite-hero-title { |
| | | margin: 0; |
| | | font-size: 22px; |
| | | font-weight: 600; |
| | | letter-spacing: 2px; |
| | | color: #f5f0e4; |
| | | } |
| | | |
| | | .elite-hero-date { |
| | | margin: 8px 0 0; |
| | | font-size: 13px; |
| | | letter-spacing: 1px; |
| | | color: rgba(212, 175, 55, 0.85); |
| | | } |
| | | |
| | | .elite-status { |
| | | display: inline-flex; |
| | | align-items: center; |
| | | gap: 8px; |
| | | padding: 7px 16px; |
| | | font-size: 12.5px; |
| | | letter-spacing: 1px; |
| | | color: #f0e2b8; |
| | | background: rgba(212, 175, 55, 0.12); |
| | | border: 1px solid rgba(212, 175, 55, 0.38); |
| | | border-radius: 999px; |
| | | } |
| | | |
| | | /* 静态状态点,不做脉冲动画 */ |
| | | .elite-status-dot { |
| | | display: inline-flex; |
| | | width: 7px; |
| | | height: 7px; |
| | | border-radius: 999px; |
| | | background: #d4af37; |
| | | } |
| | | </style> |