refactor: 重构工作台与首页仪表盘模块

- 调整dashboard工作台与首页的组件布局与模块迁移
- 统一卡片样式与hover交互效果
- 修复部分样式顺序与css语法问题
- 移除冗余的github corner组件与重复的页面模块
This commit is contained in:
zhangtao
2026-05-20 01:20:24 +08:00
parent 5533612a4c
commit b7304155ab
33 changed files with 493 additions and 1043 deletions
@@ -1,6 +1,6 @@
<!-- 卡片横幅组件 -->
<template>
<div class="fa-card-sm flex-c flex-col pb-6" :style="{ height: height }">
<div class="fa-card p-5 flex-c flex-col pb-6 h-full" :style="{ height: height }">
<div class="flex-c flex-col gap-4 text-center">
<div class="w-45">
<img :src="image" :alt="title" class="w-full h-full object-contain" loading="eager" />
@@ -76,7 +76,7 @@ interface CardBannerProps {
// 定义组件属性默认值
withDefaults(defineProps<CardBannerProps>(), {
height: "24rem",
height: "100%",
image: defaultIcon,
title: "",
description: "",
@@ -1,6 +1,6 @@
<!-- 数据列表卡片 -->
<template>
<div class="fa-card p-5">
<div class="fa-card p-5 h-[28.2rem] overflow-hidden">
<div class="pb-3.5">
<p class="text-lg font-medium">{{ title }}</p>
<p class="text-sm text-g-600">{{ subtitle }}</p>
@@ -1,7 +1,7 @@
<!-- 图片卡片 -->
<template>
<div class="w-full c-p" @click="handleClick">
<div class="fa-card overflow-hidden">
<div class="w-full c-p h-full" @click="handleClick">
<div class="fa-card overflow-hidden h-full">
<div class="relative w-full aspect-16/10 overflow-hidden">
<ElImage
:src="props.imageUrl"
@@ -1,6 +1,6 @@
<!-- 时间轴列表卡片 -->
<template>
<div class="fa-card p-5">
<div class="fa-card p-5 h-[28.2rem] overflow-hidden">
<div class="pb-3.5">
<p class="text-lg font-medium">{{ title }}</p>
<p class="text-sm text-g-600">{{ subtitle }}</p>
@@ -1,6 +1,9 @@
<!-- 地图图表 -->
<template>
<div class="relative w-full" :style="{ height: 'calc(100vh - 120px)' }">
<div
class="relative w-full h-100 p-5 mb-5 max-sm:mb-4"
:style="{ height: 'calc(100vh - 180px)' }"
>
<div v-if="isEmpty" class="h-full flex-cc">
<ElEmpty description="暂无地图数据" />
</div>
@@ -25,11 +25,7 @@
/>
</ElTooltip>
<ElTooltip content="关闭" placement="top">
<FaIconButton
class="core-overlay-icon-btn"
icon="ri:close-line"
@click="close"
/>
<FaIconButton class="core-overlay-icon-btn" icon="ri:close-line" @click="close" />
</ElTooltip>
</div>
</div>
@@ -141,8 +137,8 @@ const dialogAttrs = computed(() => {
transition: all 0.2s ease;
&:hover {
box-shadow: 0 4px 12px rgb(0 0 0 / 10%);
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
}
}
}
@@ -128,8 +128,8 @@ const drawerAttrs = computed(() => {
transition: all 0.2s ease;
&:hover {
box-shadow: 0 4px 12px rgb(0 0 0 / 10%);
transform: translateY(-2px);
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.1);
}
}
}
@@ -1,66 +0,0 @@
<!-- github 角标 -->
<template>
<a
href="https://github.com/fastapiadmin/FastapiAdmin"
target="_blank"
class="github-corner"
aria-label="View source on Github"
>
<svg
width="80"
height="80"
viewBox="0 0 250 250"
:style="'color: var(--el-color-white); fill: var(--el-color-primary)'"
aria-hidden="true"
>
<path d="M0,0 L115,115 L130,115 L142,142 L250,250 L250,0 Z" />
<path
d="M128.3,109.0 C113.8,99.7 119.0,89.6 119.0,89.6 C122.0,82.7 120.5,78.6 120.5,78.6 C119.2,72.0 123.4,76.3 123.4,76.3 C127.3,80.9 125.5,87.3 125.5,87.3 C122.9,97.6 130.6,101.9 134.4,103.2"
fill="currentColor"
:style="'transform-origin: 130px 106px'"
class="octo-arm"
/>
<path
d="M115.0,115.0 C114.9,115.1 118.7,116.5 119.8,115.4 L133.7,101.6 C136.9,99.2 139.9,98.4 142.2,98.6 C133.8,88.0 127.5,74.4 143.8,58.0 C148.5,53.4 154.0,51.2 159.7,51.0 C160.3,49.4 163.2,43.6 171.4,40.1 C171.4,40.1 176.1,42.5 178.8,56.2 C183.1,58.6 187.2,61.8 190.9,65.4 C194.5,69.0 197.7,73.2 200.1,77.6 C213.8,80.2 216.3,84.9 216.3,84.9 C212.7,93.1 206.9,96.0 205.4,96.6 C205.1,102.4 203.0,107.8 198.3,112.5 C181.9,128.9 168.3,122.5 157.7,114.1 C157.9,116.9 156.7,120.9 152.7,124.9 L141.0,136.5 C139.8,137.7 141.6,141.9 141.8,141.8 Z"
fill="currentColor"
class="octo-body"
/>
</svg>
</a>
</template>
<script setup lang="ts">
defineOptions({ name: "FaGithubCorner" });
</script>
<style scoped>
.github-corner:hover .octo-arm {
animation: octocat-wave 560ms ease-in-out;
}
@keyframes octocat-wave {
0%,
100% {
transform: rotate(0);
}
20%,
60% {
transform: rotate(-25deg);
}
40%,
80% {
transform: rotate(10deg);
}
}
@media (width <= 500px) {
.github-corner .octo-arm {
animation: octocat-wave 560ms ease-in-out;
}
.github-corner:hover .octo-arm {
animation: none;
}
}
</style>
+4 -4
View File
@@ -120,11 +120,11 @@ html.dark .el-card {
.el-dialog__title {
font-size: 16px;
}
}
}
.el-dialog__body {
position: relative; // 为了兼容 el-pagination 样式,需要设置 relative,不然会影响 el-pagination 的样式,比如 el-pagination__jump--small 会被影响,导致 el-pagination__jump--small 按钮无法点击,详见 URL_ADDRESS.com/element-plus/element-plus/issues/5684#issuecomment-1176299275;
padding: 12px 0px !important;
padding: 12px 0 !important;
}
.el-dialog.el-dialog-border {
@@ -154,11 +154,11 @@ html.dark .el-card {
.el-drawer__header {
padding: 12px 8px !important;
margin-bottom: 2px !important;
}
}
.el-drawer__body {
position: relative; // 为了兼容 el-pagination 样式,需要设置 relative,不然会影响 el-pagination 的样式,比如 el-pagination__jump--small 会被影响,导致 el-pagination__jump--small 按钮无法点击,详见 URL_ADDRESS.com/element-plus/element-plus/issues/5684#issuecomment-1176299275;
padding: 0px 8px !important;
padding: 0 8px !important;
}
// el-message 样式优化
+2 -2
View File
@@ -106,9 +106,9 @@
// 表格按钮悬浮效果
.hover-btn {
&:hover {
transform: translateY(-2px) scale(1.05);
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
box-shadow: 0 4px 12px rgb(0 0 0 / 15%);
filter: brightness(1.1);
transform: translateY(-2px) scale(1.05);
}
}
@@ -1,25 +1,34 @@
<template>
<div>
<!-- 业务分析模块 -->
<ElRow :gutter="20">
<ElCol :xl="14" :lg="15" :xs="24"><TodaySales /></ElCol>
<ElCol :xl="10" :lg="9" :xs="24"><VisitorInsights /></ElCol>
</ElRow>
<ElRow :gutter="20">
<ElCol :xl="10" :lg="10" :xs="24"><TotalRevenue /></ElCol>
<ElCol :xl="7" :lg="7" :xs="24"><CustomerSatisfaction /></ElCol>
<ElCol :xl="7" :lg="7" :xs="24"><TargetVsReality /></ElCol>
<!-- 左侧2个组件 -->
<ElCol :xl="6" :lg="6" :xs="24" class="flex flex-col gap-5">
<CustomerSatisfaction class="flex-1" />
<TotalRevenue class="flex-1" />
</ElCol>
<!-- 中间地图 -->
<ElCol :xl="12" :lg="12" :xs="24">
<div class="fa-card" style="min-height: 500px">
<FaMapChart />
</div>
</ElCol>
<!-- 右侧2个组件 -->
<ElCol :xl="6" :lg="6" :xs="24" class="flex flex-col gap-5">
<TargetVsReality class="flex-1" />
<TopProducts class="flex-1" />
</ElCol>
</ElRow>
<ElRow :gutter="20">
<ElCol :xl="10" :lg="10" :xs="24"><TopProducts /></ElCol>
<ElCol :xl="7" :lg="7" :xs="24"><SalesMappingByCountry /></ElCol>
<ElCol :xl="7" :lg="7" :xs="24"><VolumeServiceLevel /></ElCol>
</ElRow>
<!-- 图表演示区 -->
<ElRow :gutter="20">
<ElCol :xl="8" :lg="8" :xs="24"><SalesMappingByCountry /></ElCol>
<ElCol :xl="8" :lg="8" :xs="24"><VolumeServiceLevel /></ElCol>
<ElCol :xs="24" :md="12" :lg="8" class="mb-5">
<div class="fa-card">
<div class="pb-3.5"><span class="text-base font-medium">柱状图单数据</span></div>
@@ -247,11 +256,6 @@
</div>
</ElCol>
</ElRow>
<!-- 地图 -->
<div class="page-content" element-loading-text="加载中...">
<FaMapChart />
</div>
</div>
</template>
@@ -268,7 +272,7 @@ import TopProducts from "./modules/top-products.vue";
import SalesMappingByCountry from "./modules/sales-mapping-by-country.vue";
import VolumeServiceLevel from "./modules/volume-service-level.vue";
defineOptions({ name: "Analysis" });
defineOptions({ name: "DashboardAnalysis" });
const FaMapChart = defineAsyncComponent(() => import("@/components/charts/fa-map-chart/index.vue"));
@@ -1,5 +1,5 @@
<template>
<div class="fa-card h-82 p-5 mb-5 overflow-hidden max-sm:mb-4">
<div class="fa-card h-100 p-5 mb-5 max-sm:mb-4">
<div class="fa-card-header">
<div class="title">
<h4>热门产品</h4>
+95 -624
View File
@@ -1,131 +1,13 @@
<template>
<div class="dashboard-container">
<FaGithubCorner class="github-corner" />
<!-- 卡片组件演示区 workplace -->
<ElRow :gutter="20">
<ElCol :xs="24" class="mb-5">
<!-- 问候 + 外链 -->
<ElCard shadow="hover">
<div class="flex flex-wrap items-center gap-y-3">
<div class="flex min-w-0 flex-1 items-center">
<div class="size-20 shrink-0 rounded-full bg-g-100 flex items-center justify-center">
<ElAvatar
v-if="userStore.basicInfo.avatar"
size="large"
:src="userStore.basicInfo.avatar"
class="workplace-hero__avatar"
/>
<ElIcon v-else :size="40" class="text-g-600"><UserFilled /></ElIcon>
</div>
<div>
<div class="workplace-hero__greeting">
{{ timefix }}{{ currentUser.name }}{{ welcome }}
</div>
<ElText class="workplace-hero__meta">
<p class="text-sm text-g-600">
{{ currentUser.username }} · {{ currentUser.dept_name }} ·
{{ currentUser.description }}
</p>
</ElText>
</div>
</div>
<div class="hidden shrink-0 sm:block">
<div class="flex items-center space-x-6">
<div class="flex flex-col items-center">
<div class="flex items-center text-sm font-bold text-[#4080ff]">
<ElIcon class="mr-0.5"><Document /></ElIcon>
文档
</div>
<div class="mt-3 whitespace-nowrap">
<ElLink
href="https://blog.csdn.net/weixin_46768253/article/details/149569141"
target="_blank"
>
<FaSvgIcon :icon="resolveIconForFaSvgIcon('csdn')" class="text-lg" />
</ElLink>
</div>
</div>
<div class="flex flex-col items-center">
<div class="flex items-center text-sm font-bold text-[#ff9a2e]">
<ElIcon class="mr-0.5"><Folder /></ElIcon>
仓库
</div>
<div class="mt-3 whitespace-nowrap">
<ElLink href="https://gitee.com/fastapiadmin/FastapiAdmin" target="_blank">
<FaSvgIcon
:icon="resolveIconForFaSvgIcon('gitee')"
class="text-lg text-[#F76560]"
/>
</ElLink>
<ElDivider direction="vertical" />
<ElLink href="https://github.com/fastapiadmin/FastapiAdmin" target="_blank">
<FaSvgIcon
:icon="resolveIconForFaSvgIcon('github')"
class="text-lg text-[#4080FF]"
/>
</ElLink>
<ElDivider direction="vertical" />
<ElLink href="https://gitcode.com/qq_36002987/FastapiAdmin" target="_blank">
<FaSvgIcon
:icon="resolveIconForFaSvgIcon('gitcode')"
class="text-lg text-[#FF9A2E]"
/>
</ElLink>
</div>
</div>
</div>
</div>
<div class="w-full sm:hidden">
<div class="flex justify-end space-x-4 overflow-x-auto">
<ElLink href="https://gitee.com/fastapiadmin/FastapiAdmin" target="_blank">
<FaSvgIcon :icon="resolveIconForFaSvgIcon('gitee')" class="text-lg text-[#F76560]" />
</ElLink>
<ElDivider direction="vertical" />
<ElLink href="https://github.com/fastapiadmin/FastapiAdmin" target="_blank">
<FaSvgIcon :icon="resolveIconForFaSvgIcon('github')" class="text-lg text-[#4080FF]" />
</ElLink>
<ElDivider direction="vertical" />
<ElLink href="https://gitcode.com/qq_36002987/FastapiAdmin" target="_blank">
<FaSvgIcon
:icon="resolveIconForFaSvgIcon('gitcode')"
class="text-lg text-[#FF9A2E]"
/>
</ElLink>
</div>
</div>
</div>
</ElCard>
<Banner />
</ElCol>
<ElCol :xs="24" :sm="12" :md="12" class="mb-5">
<FaBasicBanner
title="数据中心运行状态"
subtitle="系统访问量同比增长 23%,所有服务运行稳定,数据监控正常。"
/>
</ElCol>
<ElCol :xs="24" :sm="12" :md="12" class="mb-5">
<FaBasicBanner
title="欢迎使用 Fastapi Admin"
subtitle="基于 Vue 3 + TypeScript + Element Plus 构建的现代化管理系统。"
titleColor="#333"
subtitleColor="#666"
boxStyle="bg-[#D4F1F7]!"
:buttonConfig="{
show: true,
text: '开始探索',
color: 'var(--fa-success)',
textColor: '#fff',
radius: '6px',
}"
@buttonClick="handleBannerDemoClick"
/>
</ElCol>
<ElCol :xs="24" class="mb-5">
<!-- CardList 概览统计含在线用户/UV/PV/访问量等 -->
<CardList />
</ElCol>
<ElCol :xs="24" :sm="12" :md="12" class="mb-5">
<AboutProject />
</ElCol>
<ElCol :xs="24" :sm="12" :md="12" class="mb-5">
<ElCard shadow="hover" class="flex flex-col h-full">
<template #header>
@@ -162,22 +44,18 @@
</div>
</div>
</template>
<ElRow v-if="quickLinks.length > 0" :gutter="12">
<ElRow v-if="quickLinks.length > 0" :gutter="4">
<ElCol
v-for="(item, index) in quickLinks"
:key="item.id || `${item.href}-${index}`"
:xs="24"
:sm="12"
:md="8"
:lg="8"
:xl="8"
class="mb-3"
:md="6"
:lg="6"
:xl="6"
class="mb-1"
>
<ElTooltip
placement="top"
:show-after="400"
:content="item.title"
>
<ElTooltip placement="top" :show-after="400" :content="item.title">
<div
class="workplace-quick-row workplace-quick-row--compact workplace-quick-row--chip"
role="button"
@@ -227,6 +105,7 @@
</ElEmpty>
</ElCard>
</ElCol>
<ElCol :xs="24" :sm="12" :md="12" class="mb-5">
<ElCard shadow="hover" class="workplace-surface workplace-ops-card flex flex-col h-full">
<template #header>
@@ -245,227 +124,19 @@
</ElCard>
</ElCol>
<ElCol :xs="24" :sm="12" :md="12" class="mb-5">
<ElCard shadow="hover" class="h-full flex flex-col">
<template #header>
<div class="flex items-center justify-between">
<span class="font-medium">最新动态</span>
<ElLink
type="primary"
underline="never"
href="https://gitee.com/fastapiadmin/FastapiAdmin/releases"
target="_blank"
>
完整记录
<ElIcon class="link-icon"><TopRight /></ElIcon>
</ElLink>
</div>
</template>
<ElScrollbar class="h-full">
<ElTimeline class="p-2">
<ElTimelineItem
v-for="(item, index) in vesionList"
:key="index"
:timestamp="item.date"
placement="top"
:color="index === 0 ? '#67C23A' : '#909399'"
:hollow="index !== 0"
>
<div class="version-item" :class="{ 'latest-item': index === 0 }">
<div class="flex items-center justify-between">
<ElText tag="strong">{{ item.title }}</ElText>
<ElTag v-if="item.tag" :type="index === 0 ? 'success' : 'info'" size="small">
{{ item.tag }}
</ElTag>
</div>
<ElText class="version-content">{{ item.content }}</ElText>
<div v-if="item.link">
<ElLink
:type="index === 0 ? 'primary' : 'info'"
:href="item.link"
target="_blank"
underline="never"
>
详情
<ElIcon class="link-icon"><TopRight /></ElIcon>
</ElLink>
</div>
</div>
</ElTimelineItem>
</ElTimeline>
</ElScrollbar>
</ElCard>
<NewUser />
</ElCol>
<ElCol
v-for="card in statsCards"
:key="`t-${card.id}`"
:xs="24"
:sm="12"
:md="6"
class="mb-5"
>
<FaStatsCard
:icon="card.icon"
:iconStyle="card.iconStyle"
:title="card.title"
:description="card.description"
:showArrow="card.showArrow"
/>
</ElCol>
<ElCol
v-for="card in statsCards"
:key="`n-${card.id}`"
:xs="24"
:sm="12"
:md="6"
class="mb-5"
>
<FaStatsCard
:icon="card.icon"
:iconStyle="card.iconStyle"
:title="card.title"
:description="card.description"
:count="card.count"
:decimals="0"
:showArrow="card.showArrow"
separator=","
/>
</ElCol>
<ElCol
v-for="card in statsCards"
:key="`c-${card.id}`"
:xs="24"
:sm="12"
:md="6"
class="mb-5"
>
<FaStatsCard
:icon="card.icon"
:iconStyle="card.customIconStyle"
:boxStyle="card.boxStyle"
:title="card.title"
:description="card.description"
:textColor="card.textColor"
:showArrow="card.showArrow"
/>
</ElCol>
<ElCol
v-for="card in progressCards"
:key="`p-${card.id}`"
:xs="24"
:sm="12"
:md="6"
class="mb-5"
>
<FaProgressCard :percentage="card.percentage" :title="card.title" :color="card.color" />
</ElCol>
<ElCol
v-for="card in progressCards"
:key="`pi-${card.id}`"
:xs="24"
:sm="12"
:md="6"
class="mb-5"
>
<FaProgressCard
:percentage="card.percentage"
:title="card.title"
:color="card.color"
:icon="card.icon"
:iconStyle="card.iconStyle"
/>
<ElCol :xs="24" :sm="12" :md="6" class="mb-5">
<HotProductsList />
</ElCol>
<ElCol :xs="24" :sm="12" :md="6" class="mb-5">
<FaLineChartCard
:isMiniChart="true"
:value="2545"
label="新用户"
date="过去7天"
:percentage="1.2"
:height="9.5"
:chartData="[120, 132, 101, 134, 90, 230, 210]"
/>
<TodoList />
</ElCol>
<ElCol :xs="24" :sm="12" :md="6" class="mb-5">
<FaBarChartCard
:isMiniChart="true"
:value="15480"
label="浏览量"
date="过去14天"
:percentage="-4.15"
:height="9.5"
barWidth="45%"
:chartData="[120, 100, 150, 140, 90, 120, 130]"
/>
<FaTimelineListCard :list="timelineData" title="最近交易" subtitle="2024年12月20日" />
</ElCol>
<ElCol :xs="24" :sm="12" :md="6" class="mb-5">
<FaLineChartCard
:isMiniChart="true"
:value="2545"
label="粉丝数"
date="过去30天"
:percentage="1.2"
:height="9.5"
:showAreaColor="true"
:chartData="[150, 180, 160, 200, 180, 220, 240]"
/>
</ElCol>
<ElCol :xs="24" :sm="12" :md="6" class="mb-5">
<FaDonutChartCard
:value="36358"
title="粉丝量"
:percentage="18"
percentageLabel="较去年"
:data="[50, 40]"
:height="9.5"
currentValue="2022"
previousValue="2021"
:radius="['50%', '70%']"
/>
</ElCol>
<ElCol :xs="24" :sm="12" :md="6" class="mb-5">
<FaLineChartCard
:value="2545"
label="新用户"
:percentage="1.2"
:height="11"
:chartData="[120, 132, 101, 134, 90, 230, 210]"
/>
</ElCol>
<ElCol :xs="24" :sm="12" :md="6" class="mb-5">
<FaBarChartCard
:value="15480"
label="浏览量"
:percentage="-4.15"
:height="11"
:chartData="[120, 100, 150, 140, 90, 120, 130, 110]"
/>
</ElCol>
<ElCol :xs="24" :sm="12" :md="6" class="mb-5">
<FaLineChartCard
:value="2545"
label="粉丝数"
:percentage="1.2"
:height="11"
:showAreaColor="true"
:chartData="[150, 180, 160, 200, 180, 220, 240]"
/>
</ElCol>
<ElCol :xs="24" :sm="12" :md="6" class="mb-5">
<FaDonutChartCard
:value="36358"
title="粉丝量"
:percentage="-18"
percentageLabel="较2021年"
:data="[70, 30]"
:height="11"
currentValue="12月"
previousValue="11月"
/>
</ElCol>
<ElCol :xs="24" :sm="12" :lg="8" class="mb-5">
<FaDataListCard :list="dataList" title="待办事项" subtitle="今日待处理任务" />
</ElCol>
<ElCol :xs="24" :sm="12" :lg="8" class="mb-5">
<FaDataListCard
:maxCount="4"
:list="dataList"
@@ -475,58 +146,25 @@
@more="handleMore"
/>
</ElCol>
<ElCol :xs="24" :sm="12" :lg="8" class="mb-5">
<FaTimelineListCard :list="timelineData" title="最近交易" subtitle="2024年12月20日" />
</ElCol>
<ElCol v-for="card in imageCards" :key="card.id" :xs="24" :sm="12" :md="6" class="mb-5">
<ElCol :xs="24" :sm="8" :md="6" class="mb-5">
<FaImageCard
:imageUrl="card.imageUrl"
:title="card.title"
:category="card.category"
:readTime="card.readTime"
:views="card.views"
:comments="card.comments"
:date="card.date"
@click="handleImageCardClick(card)"
:imageUrl="imageCards.imageUrl"
:title="imageCards.title"
:category="imageCards.category"
:readTime="imageCards.readTime"
:views="imageCards.views"
:comments="imageCards.comments"
:date="imageCards.date"
@click="handleImageCardClick(imageCards)"
/>
</ElCol>
<ElCol :xs="24" :sm="12" :md="12" :lg="6" class="mb-5">
<FaCardBanner
title="系统运行正常"
description="所有核心服务运行稳定,响应时间在正常范围内。"
/>
</ElCol>
<ElCol :xs="24" :sm="12" :md="12" :lg="6" class="mb-5">
<FaCardBanner
:image="bannerIcon2"
title="重要消息通知"
description="您有 3 条待处理的重要消息,请及时查看。"
:button="{
show: true,
text: '查看详情',
color: 'var(--fa-warning)',
textColor: '#fff',
}"
/>
</ElCol>
<ElCol :xs="24" :sm="12" :md="12" :lg="6" class="mb-5">
<FaCardBanner
:image="bannerIcon3"
title="数据分析报告"
description="本周业务数据分析报告已完成,请查看关键指标。"
:button="{
show: true,
text: '下载报告',
color: 'var(--fa-error)',
textColor: '#fff',
}"
/>
</ElCol>
<ElCol :xs="24" :sm="12" :md="12" :lg="6" class="mb-5">
<ElCol :xs="24" :sm="4" :md="6" class="mb-5">
<FaCardBanner
:image="bannerIcon4"
title="版本更新提醒"
description="Art Design Pro v2.1.0 已发布,包含性能优化和新功能。"
description="FastapiAdmin v3.0.0 已发布,包含优化和新功能。"
:button="{
show: true,
text: '立即更新',
@@ -538,6 +176,9 @@
@cancel="handleBannerDemoCancel"
/>
</ElCol>
<ElCol :xs="24" :sm="12" :md="12" class="mb-5">
<AboutProject />
</ElCol>
</ElRow>
</div>
</template>
@@ -545,100 +186,27 @@
<script setup lang="ts">
defineOptions({ name: "Home", inheritAttrs: false });
import { useUserStore } from "@stores";
import CardList from "../workplace/modules/card-list.vue";
import AboutProject from "../workplace/modules/about-project.vue";
import {
greetings,
resolveIconForFaSvgIcon,
quickStartManager,
QUICK_LINK_MAX,
type QuickLink,
} from "@utils";
import { ref, onMounted, onUnmounted } from "vue";
import { useI18n } from "vue-i18n";
import { useRouter } from "vue-router";
import {
Delete,
UserFilled,
Close,
Star,
QuestionFilled,
Document,
Folder,
TopRight,
} from "@element-plus/icons-vue";
import { Delete, Close, Star, QuestionFilled } from "@element-plus/icons-vue";
import { ElMessage, ElMessageBox } from "element-plus";
import { dayjs } from "element-plus";
import bannerIcon2 from "@imgs/3d/icon2.webp";
import bannerIcon3 from "@imgs/3d/icon3.webp";
import bannerIcon4 from "@imgs/3d/icon4.webp";
import cover1 from "@imgs/cover/img1.webp";
import cover2 from "@imgs/cover/img2.webp";
import cover3 from "@imgs/cover/img3.webp";
import cover4 from "@imgs/cover/img4.webp";
import { quickStartManager, QUICK_LINK_MAX, type QuickLink } from "@utils";
import FaCardBanner from "@/components/banners/fa-card-banner/index.vue";
import FaDonutChartCard from "@/components/cards/fa-donut-chart-card/index.vue";
import FaImageCard from "@/components/cards/fa-image-card/index.vue";
import FaDataListCard from "@/components/cards/fa-data-list-card/index.vue";
import FaLineChartCard from "@/components/cards/fa-line-chart-card/index.vue";
import FaStatsCard from "@/components/cards/fa-stats-card/index.vue";
import FaProgressCard from "@/components/cards/fa-progress-card/index.vue";
import FaBarChartCard from "@/components/cards/fa-bar-chart-card/index.vue";
import FaTimelineListCard from "@/components/cards/fa-timeline-list-card/index.vue";
import FaBasicBanner from "@/components/banners/fa-basic-banner/index.vue";
import HotProductsList from "./modules/hot-products-list.vue";
import Banner from "./modules/banner.vue";
import NewUser from "./modules/new-user.vue";
import TodoList from "./modules/todo-list.vue";
import CardList from "./modules/card-list.vue";
import AboutProject from "./modules/about-project.vue";
const timefix = greetings();
const userStore = useUserStore();
const { t } = useI18n();
const router = useRouter();
const welcome = "祝你开心每一天!";
interface VersionItem {
id: string;
title: string;
date: string;
content: string;
link: string;
tag?: string;
}
const vesionList = ref<VersionItem[]>([
{
id: "1",
title: "v3.2.1",
date: dayjs().format("YYYY-MM-DD HH:mm:ss"),
content: "优化性能,修复若干小bug。",
link: "https://gitee.com/fastapiadmin/FastapiAdmin/releases",
tag: "更新",
},
{
id: "2",
title: "v3.2.0",
date: dayjs().subtract(1, "day").format("YYYY-MM-DD HH:mm:ss"),
content: "新增用户行为分析功能。",
link: "https://gitee.com/fastapiadmin/FastapiAdmin/releases",
tag: "新功能",
},
{
id: "3",
title: "v3.1.0",
date: dayjs().subtract(3, "day").format("YYYY-MM-DD HH:mm:ss"),
content: "优化权限管理系统。",
link: "https://gitee.com/fastapiadmin/FastapiAdmin/releases",
tag: "优化",
},
]);
const currentUser = {
avatar:
userStore.basicInfo.avatar ||
"https://gw.alipayobjects.com/zos/rmsportal/BiazfanxmamNRoxxVxka.png",
name: userStore.basicInfo.name || "吴彦祖",
username: userStore.basicInfo.username || "账号信息",
description: userStore.basicInfo.description || "用户说明",
dept_name: userStore.basicInfo.dept_name || "软件专业部",
last_login: userStore.basicInfo.last_login || "2023-01-01 00:00:00",
};
const quickLinks = ref<QuickLink[]>(quickStartManager.getQuickLinks());
const handleQuickLinkClick = (item: QuickLink) => {
@@ -693,10 +261,6 @@ const updateQuickLinks = (links: QuickLink[]) => {
quickLinks.value = links;
};
// === 横幅组件演示 ===
function handleBannerDemoClick() {
console.log("banner clicked");
}
function handleBannerDemoConfirm() {
console.log("confirm clicked");
}
@@ -704,132 +268,17 @@ function handleBannerDemoCancel() {
console.log("cancel clicked");
}
// === 卡片演示数据 ← workplace ===
const statsCards = [
{
id: 1,
title: "销售产品",
count: 1235,
description: "鞋子、牛仔裤、派对服装、手表",
icon: "ri:bar-chart-box-line",
boxStyle: "bg-theme/10!",
customIconStyle: "text-theme! text-3xl!",
iconStyle: "bg-info",
textColor: "var(--theme-color)",
showArrow: false,
},
{
id: 2,
title: "活跃用户",
count: 5000,
description: "日活跃用户超过5,000+",
icon: "ri:account-box-2-line",
boxStyle: "bg-warning/10!",
customIconStyle: "text-warning! text-3xl!",
iconStyle: "bg-warning",
textColor: "var(--fa-warning)",
showArrow: false,
},
{
id: 3,
title: "总收入",
count: 35000,
description: "月收入超过¥350,000+",
icon: "ri:money-cny-box-line",
boxStyle: "bg-secondary/10!",
customIconStyle: "text-secondary! text-3xl!",
iconStyle: "bg-secondary",
textColor: "var(--fa-secondary)",
showArrow: false,
},
{
id: 4,
title: "客户评价",
count: 4800,
description: "平均评分4.8/5",
icon: "ri:message-3-line",
boxStyle: "bg-error/10!",
customIconStyle: "text-error! text-3xl!",
iconStyle: "bg-error",
textColor: "var(--fa-error)",
showArrow: false,
},
];
const progressCards = [
{
id: 1,
title: "完成进度",
percentage: 75,
color: "var(--fa-success)",
icon: "ri:arrow-up-circle-line",
iconStyle: "bg-success/12 text-success",
},
{
id: 2,
title: "项目进度",
percentage: 65,
color: "var(--theme-color)",
icon: "ri:twitch-line",
iconStyle: "bg-theme/12 text-theme",
},
{
id: 3,
title: "学习进度",
percentage: 45,
color: "var(--fa-error)",
icon: "ri:game-line",
iconStyle: "bg-error/12! text-error",
},
{
id: 4,
title: "任务进度",
percentage: 90,
color: "var(--fa-secondary)",
icon: "ri:flag-2-line",
iconStyle: "bg-secondary/12 text-secondary",
},
];
const imageCards = [
{
id: 1,
imageUrl: cover1,
title: "AI技术在医疗领域的创新应用与发展前景",
category: "社交",
readTime: "2分钟",
views: 9125,
comments: 3,
date: "12月19日 周一",
},
{
id: 2,
imageUrl: cover2,
title: "大数据分析助力企业决策的实践案例",
category: "技术",
readTime: "3分钟",
views: 7234,
comments: 5,
date: "12月20日 周二",
},
{
id: 3,
imageUrl: cover3,
title: "区块链技术在供应链管理中的应用",
category: "科技",
readTime: "4分钟",
views: 5678,
comments: 8,
date: "12月21日 周三",
},
{
id: 4,
imageUrl: cover4,
title: "云计算技术发展趋势与未来展望",
category: "云技术",
readTime: "5分钟",
views: 4321,
comments: 6,
date: "12月22日 周四",
},
];
const imageCards = {
id: 1,
imageUrl: cover2,
title: "大数据分析助力企业决策的实践案例",
category: "技术",
readTime: "3分钟",
views: 7234,
comments: 5,
date: "12月20日 周二",
};
const dataList = [
{
title: "新加坡之行",
@@ -883,12 +332,45 @@ const timelineData = [
code: "OR-9876",
},
{ time: "下午 17:00", status: "rgb(103, 232, 207)", content: "完成每日销售报表" },
{
time: "上午 09:30",
status: "rgb(73, 190, 255)",
content: "收到订单 #38291 支付 ¥385.90",
},
{
time: "上午 10:00",
status: "rgb(54, 158, 255)",
content: "新商品上架",
code: "SKU-3467",
},
{
time: "上午 12:00",
status: "rgb(103, 232, 207)",
content: "向供应商支付了 ¥6495.00",
},
{
time: "下午 14:30",
status: "rgb(255, 193, 7)",
content: "促销活动开始",
code: "PROMO-2023",
},
{
time: "下午 15:45",
status: "rgb(255, 105, 105)",
content: "订单取消提醒",
code: "ORD-9876",
},
{
time: "下午 17:00",
status: "rgb(103, 232, 207)",
content: "完成日销售报表",
},
];
function handleMore() {
ElMessage.info("查看更多");
}
function handleImageCardClick(card: (typeof imageCards)[0]) {
function handleImageCardClick(card: typeof imageCards) {
console.log("点击卡片:", card);
}
onMounted(() => {
@@ -927,14 +409,6 @@ onUnmounted(() => {
}
}
.github-corner {
position: absolute;
top: 0;
right: 0;
z-index: 1;
border: 0;
}
.version-item {
padding: 10px 12px;
background: var(--el-fill-color-lighter);
@@ -994,10 +468,6 @@ onUnmounted(() => {
letter-spacing: 0.02em;
}
.workplace-hero__avatar {
border: 2px solid var(--el-bg-color);
}
.workplace-hero__greeting {
margin-bottom: 6px;
font-size: 20px;
@@ -1051,8 +521,9 @@ onUnmounted(() => {
}
.workplace-quick-row--compact {
min-height: 44px;
padding: 6px 8px 6px 4px;
gap: 12px;
min-height: 24px;
padding: 0 6px;
}
.workplace-quick-row {
@@ -1060,7 +531,7 @@ onUnmounted(() => {
display: flex;
gap: 10px;
align-items: center;
min-height: 56px;
min-height: 40px;
padding: 8px 10px 8px 6px;
cursor: pointer;
outline: none;
@@ -1088,7 +559,7 @@ onUnmounted(() => {
flex-shrink: 0;
align-self: stretch;
width: 3px;
min-height: 36px;
min-height: 24px;
border-radius: 2px;
}
@@ -1097,12 +568,12 @@ onUnmounted(() => {
flex-shrink: 0;
align-items: center;
justify-content: center;
width: 40px;
height: 40px;
font-size: 22px;
width: 26px;
height: 26px;
font-size: 14px;
line-height: 1;
background: var(--el-color-primary-light-9);
border-radius: 10px;
border-radius: 6px;
}
.workplace-quick-row__text {
@@ -1116,7 +587,7 @@ onUnmounted(() => {
text-overflow: ellipsis;
font-size: 13px;
font-weight: 600;
line-height: 1.35;
line-height: 1.3;
color: var(--el-text-color-primary);
white-space: nowrap;
}
@@ -1147,8 +618,8 @@ onUnmounted(() => {
opacity 0.2s;
.workplace-quick-row--chip & {
width: 26px;
height: 26px;
width: 20px;
height: 20px;
}
&:disabled {
@@ -1,5 +1,5 @@
<template>
<div class="fa-card p-5 flex-b">
<div class="fa-card p-5 flex-b h-full">
<div>
<h2 class="text-2xl font-medium">关于项目</h2>
<p class="text-g-700 mt-1">{{ systemName }} 是一款兼具设计美学与高效开发的后台系统</p>
@@ -1,7 +1,8 @@
<template>
<FaBasicBanner
class="justify-center h-53! max-sm:pt-8! max-sm:h-48!"
:title="`欢迎回来 ${userInfo?.username}`"
:title="bannerTitle"
:subtitle="bannerSubtitle"
boxStyle="bg-theme/10!"
titleColor="var(--fa-gray-900)"
:decoration="false"
@@ -11,7 +12,10 @@
}"
:buttonConfig="{
show: false,
text: '',
text: '开始探索',
color: 'var(--fa-success)',
textColor: '#fff',
radius: '6px',
}"
:imageConfig="{
src: bannerCover,
@@ -20,46 +24,49 @@
}"
@click="handleBannerClick"
>
<div class="flex mt-6">
<div class="mr-8 pr-8 border-r border-g-400 dark:border-g-300/60">
<p class="text-3xl">
<FaCountTo
class="number box-title"
:target="2340"
:duration="1500"
prefix="¥"
separator=","
/>
<FaSvgIcon icon="ri:arrow-right-up-line" class="text-xl text-success relative -top-2" />
</p>
<p class="mt-1 text-sm text-g-700">今日销售额</p>
</div>
<div class="mr-8">
<p class="text-3xl">
<FaCountTo class="number box-title" :target="35" :duration="1500" suffix="%" />
<FaSvgIcon icon="ri:arrow-right-up-line" class="text-xl text-success relative -top-2" />
</p>
<p class="mt-1 text-sm text-g-700">较昨日</p>
<div class="flex items-center gap-4 mt-2">
<ElAvatar v-if="currentUser.avatar" :size="56" :src="currentUser.avatar" />
<ElIcon v-else :size="56" class="text-g-500"><UserFilled /></ElIcon>
<div>
<div class="text-lg font-semibold text-g-800">{{ currentUser.name }}</div>
<div class="text-sm text-g-600">
{{ currentUser.dept_name }} · {{ currentUser.description }} · {{ currentUser.last_login }}
</div>
</div>
</div>
</FaBasicBanner>
</template>
<script setup lang="ts">
import { computed } from "vue";
import bannerCover from "@imgs/login/lf_icon2.webp";
import FaBasicBanner from "@/components/banners/fa-basic-banner/index.vue";
import { useUserStore } from "@stores";
import { greetings } from "@utils";
import { UserFilled } from "@element-plus/icons-vue";
const userStore = useUserStore();
/**
* 获取当前用户信息
*/
const userInfo = computed(() => userStore.basicInfo);
/**
* 处理横幅点击事件
*/
const handleBannerClick = (): void => {
// TODO: 添加横幅点击处理逻辑
console.log("banner clicked");
};
const timefix = greetings();
const welcome = "祝你开心每一天!";
const currentUser = {
avatar: userStore.basicInfo.avatar || "",
name: userInfo.value.name || "吴彦祖",
username: userInfo.value.username || "账号信息",
description: userInfo.value.description || "用户说明",
dept_name: userInfo.value.dept_name || "软件专业部",
last_login: userInfo.value.last_login || "2023-01-01 00:00:00",
};
const bannerTitle = `欢迎回来 ${currentUser.name}${currentUser.username} ${timefix} ${welcome}`;
const bannerSubtitle = `基于 FastAPI + Vue3 + TypeScript 构建的企业级中后台解决方案,支持多端开发。`;
</script>
<style scoped lang="scss"></style>
@@ -1,5 +1,5 @@
<template>
<div class="fa-card p-5 h-128 overflow-hidden">
<div class="fa-card p-5 overflow-hidden flex flex-col h-full">
<div class="fa-card-header">
<div class="title">
<h4>新用户</h4>
@@ -1,57 +0,0 @@
<template>
<FaTimelineListCard
:list="timelineData"
title="最近交易"
subtitle="今日订单动态"
class="h-[28.2rem]"
/>
</template>
<script setup lang="ts">
interface TimelineItem {
time: string;
status: string;
content: string;
code?: string;
}
/**
* 最近交易时间线数据
* 记录今日订单处理、商品上架、支付等关键活动
*/
const timelineData: TimelineItem[] = [
{
time: "上午 09:30",
status: "rgb(73, 190, 255)",
content: "收到订单 #38291 支付 ¥385.90",
},
{
time: "上午 10:00",
status: "rgb(54, 158, 255)",
content: "新商品上架",
code: "SKU-3467",
},
{
time: "上午 12:00",
status: "rgb(103, 232, 207)",
content: "向供应商支付了 ¥6495.00",
},
{
time: "下午 14:30",
status: "rgb(255, 193, 7)",
content: "促销活动开始",
code: "PROMO-2023",
},
{
time: "下午 15:45",
status: "rgb(255, 105, 105)",
content: "订单取消提醒",
code: "ORD-9876",
},
{
time: "下午 17:00",
status: "rgb(103, 232, 207)",
content: "完成日销售报表",
},
];
</script>
@@ -1,5 +1,5 @@
<template>
<div class="fa-card h-128 p-5">
<div class="fa-card p-5 h-[28.2rem] overflow-hidden">
<div class="fa-card-header">
<div class="title">
<h4>代办事项</h4>
@@ -18,7 +18,11 @@
:key="index"
>
<div>
<p class="text-sm">{{ item.username }}</p>
<p class="text-sm">
<span class="text-g-800 font-medium">{{ item.username }}</span>
<span class="mx-2 text-g-600">{{ item.type }}</span>
<span class="text-theme">{{ item.target }}</span>
</p>
<p class="text-g-500 mt-1">{{ item.date }}</p>
</div>
<ElCheckbox v-model="item.complate" />
@@ -33,6 +37,8 @@ interface TodoItem {
username: string;
date: string;
complate: boolean;
type: string;
target: string;
}
/**
@@ -41,34 +47,46 @@ interface TodoItem {
*/
const list = reactive<TodoItem[]>([
{
username: "查看今天工作内容",
username: "张无忌",
date: "上午 09:30",
complate: true,
type: "查看",
target: "查看今天工作内容",
},
{
username: "回复邮件",
username: "赵敏",
date: "上午 10:30",
complate: true,
type: "回复",
target: "回复邮件",
},
{
username: "工作汇报整理",
username: "小昭",
date: "上午 11:00",
complate: true,
type: "工作",
target: "工作汇报整理",
},
{
username: "产品需求会议",
username: "金毛狮王",
date: "下午 02:00",
complate: false,
type: "会议",
target: "产品需求会议",
},
{
username: "整理会议内容",
username: "金花婆婆",
date: "下午 03:30",
complate: false,
type: "会议",
target: "整理会议内容",
},
{
username: "明天工作计划",
username: "张三丰",
date: "下午 06:30",
complate: false,
type: "计划",
target: "明天工作计划",
},
]);
</script>
@@ -1,80 +0,0 @@
<template>
<div class="fa-card p-5 h-full flex flex-col">
<div class="pb-3.5">
<p class="text-lg font-medium">最近活动</p>
<p class="text-sm text-g-600">订单处理状态</p>
</div>
<ElScrollbar :style="{ height: '264px' }">
<div v-for="(item, index) in dataList" :key="index" class="flex-c py-3">
<div v-if="item.icon" class="flex-cc mr-3 size-10 rounded-lg" :class="item.class">
<FaSvgIcon :icon="item.icon" class="text-xl" />
</div>
<div class="flex-1">
<div class="mb-1 text-sm">{{ item.title }}</div>
<div class="text-xs text-g-500">{{ item.status }}</div>
</div>
<div class="ml-3 text-xs text-g-500">{{ item.time }}</div>
</div>
</ElScrollbar>
<ElButton class="mt-[17px] w-full text-center" v-ripple @click="handleMore">查看更多</ElButton>
</div>
</template>
<script setup lang="ts">
interface TransactionItem {
title: string;
status: string;
time: string;
class: string;
icon: string;
}
/**
* 最近活动数据列表
* 展示订单处理、退款申请、投诉处理等业务活动状态
*/
const dataList: TransactionItem[] = [
{
title: "新订单 #38291",
status: "待处理",
time: "5分钟",
class: "bg-theme/12 text-theme",
icon: "ri:shopping-bag-4-line",
},
{
title: "退款申请 #12845",
status: "处理中",
time: "10分钟",
class: "bg-secondary/12 text-secondary",
icon: "ri:profile-line",
},
{
title: "客户投诉处理",
status: "待处理",
time: "15分钟",
class: "bg-warning/12 text-warning",
icon: "ri:customer-service-2-line",
},
{
title: "库存不足提醒",
status: "紧急",
time: "20分钟",
class: "bg-danger/12 text-danger",
icon: "ri:box-1-line",
},
{
title: "订单 #29384 已发货",
status: "已完成",
time: "20分钟",
class: "bg-success/12 text-success",
icon: "ri:shopping-bag-3-line",
},
];
/**
* 处理查看更多按钮点击事件
*/
const handleMore = (): void => {
// TODO: 添加查看更多逻辑
};
</script>
@@ -2,43 +2,197 @@
<div class="workplace-container">
<!-- 电商模块 -->
<div class="card-group">
<ElRow :gutter="20">
<ElCol :sm="24" :md="24" :lg="16"><Banner /></ElCol>
<ElCol :sm="12" :md="12" :lg="4"><TotalOrderVolume /></ElCol>
<ElCol :sm="12" :md="12" :lg="4"><TotalProducts /></ElCol>
<ElRow :gutter="24">
<ElCol :sm="12" :md="12" :lg="6"><TotalOrderVolume /></ElCol>
<ElCol :sm="12" :md="12" :lg="6"><TotalProducts /></ElCol>
<ElCol :sm="24" :md="12" :lg="6"><ProductSales /></ElCol>
<ElCol :sm="24" :md="12" :lg="6"><SalesGrowth /></ElCol>
</ElRow>
<ElRow :gutter="20">
<ElCol :sm="12" :md="12" :lg="8"><SalesTrend /></ElCol>
<ElCol :sm="12" :md="12" :lg="8"><SalesClassification /></ElCol>
<ElCol :sm="24" :md="24" :lg="8">
<ElRow :gutter="20" class="card-row">
<ElCol :sm="24" :md="12" :lg="12"><ProductSales /></ElCol>
<ElCol :sm="24" :md="12" :lg="12"><SalesGrowth /></ElCol>
<ElCol :span="24"><CartConversionRate /></ElCol>
</ElRow>
</ElCol>
<ElRow :gutter="24">
<ElCol :span="24"><CartConversionRate /></ElCol>
</ElRow>
<ElRow :gutter="24">
<ElCol :sm="12" :md="12" :lg="6"><SalesTrend /></ElCol>
<ElCol :sm="12" :md="12" :lg="6"><SalesClassification /></ElCol>
<ElCol :sm="24" :md="12" :lg="6"><HotCommodity /></ElCol>
<ElCol :sm="24" :md="12" :lg="6"><AnnualSales /></ElCol>
</ElRow>
</div>
<!-- 用户分析 -->
<div class="card-group">
<ElRow :gutter="20">
<ElCol :sm="24" :md="12" :lg="10"><ActiveUser /></ElCol>
<ElCol :sm="24" :md="12" :lg="14"><SalesOverview /></ElCol>
</ElRow>
<ElRow :gutter="20">
<ElCol :sm="24" :md="24" :lg="12"><NewUser /></ElCol>
<ElCol :sm="24" :md="12" :lg="6"><Dynamic /></ElCol>
<ElCol :sm="24" :md="12" :lg="6"><TodoList /></ElCol>
</ElRow>
<ElRow :gutter="20">
<ElCol :sm="24" :md="12" :lg="8"><HotCommodity /></ElCol>
<ElCol :sm="24" :md="12" :lg="8"><AnnualSales /></ElCol>
<ElCol :sm="24" :md="24" :lg="8"><TransactionList /></ElCol>
</ElRow>
<ElRow :gutter="20">
<ElCol :md="24" :lg="8"><RecentTransaction /></ElCol>
<ElCol :md="24" :lg="16" class="no-margin-bottom"><HotProductsList /></ElCol>
<ElRow :gutter="24">
<ElCol :sm="24" :md="12" :lg="12" class="mb-5"><ActiveUser /></ElCol>
<ElCol :sm="24" :md="12" :lg="12" class="mb-5"><SalesOverview /></ElCol>
<ElCol
v-for="card in statsCards"
:key="`t-${card.id}`"
:xs="24"
:sm="12"
:md="6"
class="mb-5"
>
<FaStatsCard
:icon="card.icon"
:iconStyle="card.iconStyle"
:title="card.title"
:description="card.description"
:showArrow="card.showArrow"
/>
</ElCol>
<ElCol
v-for="card in statsCards"
:key="`n-${card.id}`"
:xs="24"
:sm="12"
:md="6"
class="mb-5"
>
<FaStatsCard
:icon="card.icon"
:iconStyle="card.iconStyle"
:title="card.title"
:description="card.description"
:count="card.count"
:decimals="0"
:showArrow="card.showArrow"
separator=","
/>
</ElCol>
<ElCol
v-for="card in statsCards"
:key="`c-${card.id}`"
:xs="24"
:sm="12"
:md="6"
class="mb-5"
>
<FaStatsCard
:icon="card.icon"
:iconStyle="card.customIconStyle"
:boxStyle="card.boxStyle"
:title="card.title"
:description="card.description"
:textColor="card.textColor"
:showArrow="card.showArrow"
/>
</ElCol>
<ElCol
v-for="card in progressCards"
:key="`p-${card.id}`"
:xs="24"
:sm="12"
:md="6"
class="mb-5"
>
<FaProgressCard :percentage="card.percentage" :title="card.title" :color="card.color" />
</ElCol>
<ElCol
v-for="card in progressCards"
:key="`pi-${card.id}`"
:xs="24"
:sm="12"
:md="6"
class="mb-5"
>
<FaProgressCard
:percentage="card.percentage"
:title="card.title"
:color="card.color"
:icon="card.icon"
:iconStyle="card.iconStyle"
/>
</ElCol>
<ElCol :xs="24" :sm="12" :md="6" class="mb-5">
<FaLineChartCard
:isMiniChart="true"
:value="2545"
label="新用户"
date="过去7天"
:percentage="1.2"
:height="9.5"
:chartData="[120, 132, 101, 134, 90, 230, 210]"
/>
</ElCol>
<ElCol :xs="24" :sm="12" :md="6" class="mb-5">
<FaBarChartCard
:isMiniChart="true"
:value="15480"
label="浏览量"
date="过去14天"
:percentage="-4.15"
:height="9.5"
barWidth="45%"
:chartData="[120, 100, 150, 140, 90, 120, 130]"
/>
</ElCol>
<ElCol :xs="24" :sm="12" :md="6" class="mb-5">
<FaLineChartCard
:isMiniChart="true"
:value="2545"
label="粉丝数"
date="过去30天"
:percentage="1.2"
:height="9.5"
:showAreaColor="true"
:chartData="[150, 180, 160, 200, 180, 220, 240]"
/>
</ElCol>
<ElCol :xs="24" :sm="12" :md="6" class="mb-5">
<FaDonutChartCard
:value="36358"
title="粉丝量"
:percentage="18"
percentageLabel="较去年"
:data="[50, 40]"
:height="9.5"
currentValue="2022"
previousValue="2021"
:radius="['50%', '70%']"
/>
</ElCol>
<ElCol :xs="24" :sm="12" :md="6" class="mb-5">
<FaLineChartCard
:value="2545"
label="新用户"
:percentage="1.2"
:height="11"
:chartData="[120, 132, 101, 134, 90, 230, 210]"
/>
</ElCol>
<ElCol :xs="24" :sm="12" :md="6" class="mb-5">
<FaBarChartCard
:value="15480"
label="浏览量"
:percentage="-4.15"
:height="11"
:chartData="[120, 100, 150, 140, 90, 120, 130, 110]"
/>
</ElCol>
<ElCol :xs="24" :sm="12" :md="6" class="mb-5">
<FaLineChartCard
:value="2545"
label="粉丝数"
:percentage="1.2"
:height="11"
:showAreaColor="true"
:chartData="[150, 180, 160, 200, 180, 220, 240]"
/>
</ElCol>
<ElCol :xs="24" :sm="12" :md="6" class="mb-5">
<FaDonutChartCard
:value="36358"
title="粉丝量"
:percentage="-18"
percentageLabel="较2021年"
:data="[70, 30]"
:height="11"
currentValue="12月"
previousValue="11月"
/>
</ElCol>
</ElRow>
</div>
</div>
@@ -47,25 +201,107 @@
<script setup lang="ts">
import ActiveUser from "./modules/active-user.vue";
import SalesOverview from "./modules/sales-overview.vue";
import NewUser from "./modules/new-user.vue";
import Dynamic from "./modules/dynamic-stats.vue";
import TodoList from "./modules/todo-list.vue";
import TotalOrderVolume from "./modules/total-order-volume.vue";
import TotalProducts from "./modules/total-products.vue";
import SalesTrend from "./modules/sales-trend.vue";
import SalesClassification from "./modules/sales-classification.vue";
import HotCommodity from "./modules/hot-commodity.vue";
import AnnualSales from "./modules/annual-sales.vue";
import ProductSales from "./modules/product-sales.vue";
import SalesGrowth from "./modules/sales-growth.vue";
import CartConversionRate from "./modules/cart-conversion-rate.vue";
import FaLineChartCard from "@/components/cards/fa-line-chart-card/index.vue";
import FaStatsCard from "@/components/cards/fa-stats-card/index.vue";
import FaProgressCard from "@/components/cards/fa-progress-card/index.vue";
import FaBarChartCard from "@/components/cards/fa-bar-chart-card/index.vue";
import FaDonutChartCard from "@/components/cards/fa-donut-chart-card/index.vue";
import Banner from "../home/modules/banner.vue";
import TotalOrderVolume from "../home/modules/total-order-volume.vue";
import TotalProducts from "../home/modules/total-products.vue";
import SalesTrend from "../home/modules/sales-trend.vue";
import SalesClassification from "../home/modules/sales-classification.vue";
import TransactionList from "../home/modules/transaction-list.vue";
import HotCommodity from "../home/modules/hot-commodity.vue";
import RecentTransaction from "../home/modules/recent-transaction.vue";
import AnnualSales from "../home/modules/annual-sales.vue";
import ProductSales from "../home/modules/product-sales.vue";
import SalesGrowth from "../home/modules/sales-growth.vue";
import CartConversionRate from "../home/modules/cart-conversion-rate.vue";
import HotProductsList from "../home/modules/hot-products-list.vue";
defineOptions({ name: "DashboardWorkplace" });
defineOptions({ name: "Console" });
const statsCards = [
{
id: 1,
title: "销售产品",
count: 1235,
description: "鞋子、牛仔裤、派对服装、手表",
icon: "ri:bar-chart-box-line",
boxStyle: "bg-theme/10!",
customIconStyle: "text-theme! text-3xl!",
iconStyle: "bg-info",
textColor: "var(--theme-color)",
showArrow: false,
},
{
id: 2,
title: "活跃用户",
count: 5000,
description: "日活跃用户超过5,000+",
icon: "ri:account-box-2-line",
boxStyle: "bg-warning/10!",
customIconStyle: "text-warning! text-3xl!",
iconStyle: "bg-warning",
textColor: "var(--fa-warning)",
showArrow: false,
},
{
id: 3,
title: "总收入",
count: 35000,
description: "月收入超过¥350,000+",
icon: "ri:money-cny-box-line",
boxStyle: "bg-secondary/10!",
customIconStyle: "text-secondary! text-3xl!",
iconStyle: "bg-secondary",
textColor: "var(--fa-secondary)",
showArrow: false,
},
{
id: 4,
title: "客户评价",
count: 4800,
description: "平均评分4.8/5",
icon: "ri:message-3-line",
boxStyle: "bg-error/10!",
customIconStyle: "text-error! text-3xl!",
iconStyle: "bg-error",
textColor: "var(--fa-error)",
showArrow: false,
},
];
const progressCards = [
{
id: 1,
title: "完成进度",
percentage: 75,
color: "var(--fa-success)",
icon: "ri:arrow-up-circle-line",
iconStyle: "bg-success/12 text-success",
},
{
id: 2,
title: "项目进度",
percentage: 65,
color: "var(--theme-color)",
icon: "ri:twitch-line",
iconStyle: "bg-theme/12 text-theme",
},
{
id: 3,
title: "学习进度",
percentage: 45,
color: "var(--fa-error)",
icon: "ri:game-line",
iconStyle: "bg-error/12! text-error",
},
{
id: 4,
title: "任务进度",
percentage: 90,
color: "var(--fa-secondary)",
icon: "ri:flag-2-line",
iconStyle: "bg-secondary/12 text-secondary",
},
];
</script>
<style scoped>
@@ -1,5 +1,5 @@
<template>
<div class="fa-card p-5 h-[28.2rem]">
<div class="fa-card p-5 h-112">
<div class="fa-card-header">
<div class="title">
<h4>年度销售额</h4>
@@ -1,6 +1,6 @@
<template>
<FaLineChartCard
class=""
class="fa-card h-53 p-5 pt-3.5"
:value="2545"
label="购物车转化率"
:percentage="1.2"
@@ -1,82 +0,0 @@
<template>
<div class="fa-card h-128 p-5">
<div class="fa-card-header">
<div class="title">
<h4>动态</h4>
<p>
新增
<span class="text-success">+6</span>
</p>
</div>
</div>
<div class="h-9/10 mt-2 overflow-hidden">
<ElScrollbar>
<div
class="h-17.5 leading-17.5 border-b border-g-300 text-sm overflow-hidden last:border-b-0"
v-for="(item, index) in list"
:key="index"
>
<span class="text-g-800 font-medium">{{ item.username }}</span>
<span class="mx-2 text-g-600">{{ item.type }}</span>
<span class="text-theme">{{ item.target }}</span>
</div>
</ElScrollbar>
</div>
</div>
</template>
<script setup lang="ts">
interface DynamicItem {
username: string;
type: string;
target: string;
}
/**
* 用户动态列表
* 记录用户的关注、发文、提问、兑换等各类活动
*/
const list = reactive<DynamicItem[]>([
{
username: "中小鱼",
type: "关注了",
target: "誶誶淰",
},
{
username: "何小荷",
type: "发表文章",
target: "Vue3 + Typescript + Vite 项目实战笔记",
},
{
username: "中小鱼",
type: "关注了",
target: "誶誶淰",
},
{
username: "何小荷",
type: "发表文章",
target: "Vue3 + Typescript + Vite 项目实战笔记",
},
{
username: "誶誶淰",
type: "提出问题",
target: "主题可以配置吗",
},
{
username: "发呆草",
type: "兑换了物品",
target: "《奇特的一生》",
},
{
username: "甜筒",
type: "关闭了问题",
target: "发呆草",
},
{
username: "冷月呆呆",
type: "兑换了物品",
target: "《高效人士的七个习惯》",
},
]);
</script>
@@ -1,5 +1,5 @@
<template>
<div class="fa-card p-5 h-[28.2rem]">
<div class="fa-card p-5 h-112">
<div class="fa-card-header">
<div class="title">
<h4>热销商品</h4>
@@ -1,5 +1,5 @@
<template>
<div class="fa-card p-5 pt-3.5 h-46">
<div class="fa-card h-53 p-5 pt-3.5">
<div class="fa-card-header">
<div class="title">
<h4 class="text-2xl">
@@ -1,5 +1,5 @@
<template>
<div class="fa-card p-5 h-105">
<div class="fa-card p-5 h-112">
<div class="fa-card-header">
<div class="title">
<h4>销售分类</h4>
@@ -1,5 +1,5 @@
<template>
<div class="fa-card p-5 pt-3.5 h-46">
<div class="fa-card h-53 p-5 pt-3.5">
<div class="fa-card-header">
<div class="title">
<h4 class="text-2xl">
@@ -1,5 +1,5 @@
<template>
<div class="fa-card p-5 h-105">
<div class="fa-card p-5 h-112">
<div class="fa-card-header">
<div class="title">
<h4>销售趋势</h4>
+1 -1
View File
@@ -509,8 +509,8 @@ onUnmounted(() => {
}
.chart-container {
flex: 1;
display: flex;
flex: 1;
align-items: center;
justify-content: center;
min-height: 300px;
@@ -966,9 +966,9 @@ function deleteEdge(edgeId: string, getEdges: () => Edge[], setEdges: (edges: Ed
.canvas-container {
flex: 1;
min-height: 400px;
width: 100%;
height: 100%;
min-height: 400px;
overflow: hidden;
}