This commit is contained in:
2026-03-22 14:41:42 +08:00
parent b34cb6a926
commit ccb6704528
5 changed files with 187 additions and 141 deletions

View File

@@ -14,7 +14,8 @@
"Bash(find node_modules/.pnpm -name *.mjs -path *vue-router*)",
"Bash(grep -r \"routeMeta\\\\|definePage\\\\|<route\" node_modules/.pnpm/vue-router*/node_modules/vue-router/dist/*.mjs)",
"Bash(grep -r \"route block\\\\|customBlock\\\\|defineCustomBlock\" node_modules/.pnpm/vue-router*/dist/*.mjs)",
"Bash(find node_modules/.pnpm -path *vue-router* -name *.mjs)"
"Bash(find node_modules/.pnpm -path *vue-router* -name *.mjs)",
"Bash(pnpm add:*)"
],
"additionalDirectories": [
"/Users/sion/Desktop/projects/monisuo/monisuo-admin/.git"

View File

@@ -53,6 +53,7 @@
"class-variance-authority": "^0.7.1",
"clsx": "^2.1.1",
"dayjs": "^1.11.20",
"decimal.js": "^10.6.0",
"echarts": "^6.0.0",
"embla-carousel-autoplay": "^8.6.0",
"embla-carousel-vue": "^8.6.0",

View File

@@ -50,6 +50,9 @@ importers:
dayjs:
specifier: ^1.11.20
version: 1.11.20
decimal.js:
specifier: ^10.6.0
version: 10.6.0
echarts:
specifier: ^6.0.0
version: 6.0.0
@@ -3431,6 +3434,9 @@ packages:
decache@4.6.2:
resolution: {integrity: sha512-2LPqkLeu8XWHU8qNCS3kcF6sCcb5zIzvWaAHYSvPfwhdd7mHuah29NssMzrTYyHN4F5oFy2ko9OBYxegtU0FEw==}
decimal.js@10.6.0:
resolution: {integrity: sha512-YpgQiITW3JXGntzdUmyUR1V812Hn8T1YVXhCu+wO3OpS4eU9l4YdD3qjyiKdV6mvV29zapkMeD390UVEf2lkUg==}
decode-named-character-reference@1.3.0:
resolution: {integrity: sha512-GtpQYB283KrPp6nRw50q3U9/VfOutZOe103qlN7BPP6Ad27xYnOIWv4lPzo8HCAL+mMZofJ9KEy30fq6MfaK6Q==}
@@ -10091,6 +10097,8 @@ snapshots:
callsite: 1.0.0
optional: true
decimal.js@10.6.0: {}
decode-named-character-reference@1.3.0:
dependencies:
character-entities: 2.0.2

View File

@@ -179,24 +179,24 @@ function formatCurrency(value: number): string {
<UiSpinner class="w-8 h-8" />
</div>
<div v-else class="space-y-6">
<div v-else class="grid gap-6">
<!-- 模块1: 盈利分析 -->
<section>
<h2 class="text-sm font-medium text-muted-foreground mb-3 flex items-center gap-2">
<section class="space-y-3">
<h2 class="text-sm font-medium text-muted-foreground flex items-center gap-2">
<Icon icon="lucide:dollar-sign" class="size-4" />
盈利分析
</h2>
<div class="grid gap-3 sm:grid-cols-2 lg:grid-cols-4">
<div class="grid gap-3 grid-cols-2 sm:grid-cols-4">
<UiCard v-for="item in profitMetrics" :key="item.label" class="hover:shadow-sm transition-shadow">
<UiCardContent class="pt-6">
<div class="flex items-center justify-between mb-3">
<Icon :icon="item.icon" class="size-5" :class="item.color" />
<UiCardContent class="p-4">
<div class="flex items-center justify-between mb-2">
<Icon :icon="item.icon" class="size-4" :class="item.color" />
<span class="text-xs text-muted-foreground">{{ item.rate }}</span>
</div>
<p class="text-xs text-muted-foreground">
<p class="text-xs text-muted-foreground truncate">
{{ item.label }}
</p>
<p class="text-2xl font-bold font-mono mt-1" :class="item.color">
<p class="text-lg sm:text-xl font-bold font-mono mt-1 truncate" :class="item.color">
{{ formatCurrency(item.value) }}
</p>
</UiCardContent>
@@ -205,45 +205,47 @@ function formatCurrency(value: number): string {
</section>
<!-- 模块2: 交易分析 -->
<section>
<h2 class="text-sm font-medium text-muted-foreground mb-3 flex items-center gap-2">
<section class="space-y-3">
<h2 class="text-sm font-medium text-muted-foreground flex items-center gap-2">
<Icon icon="lucide:trending-up" class="size-4" />
交易分析
</h2>
<UiCard>
<UiCardContent class="pt-6">
<VChart :option="tradeAnalysisOption" autoresize style="height: 280px" />
<UiCardContent class="p-4">
<VChart :option="tradeAnalysisOption" autoresize style="height: 260px" />
</UiCardContent>
</UiCard>
</section>
<!-- 模块3: 币种分布 + 用户分析 -->
<div class="grid gap-4 lg:grid-cols-3">
<section>
<h2 class="text-sm font-medium text-muted-foreground mb-3 flex items-center gap-2">
<div class="grid gap-6 lg:grid-cols-5">
<!-- 币种分布 -->
<section class="space-y-3 lg:col-span-2">
<h2 class="text-sm font-medium text-muted-foreground flex items-center gap-2">
<Icon icon="lucide:pie-chart" class="size-4" />
币种交易分布
</h2>
<UiCard>
<UiCardContent class="pt-6">
<VChart :option="coinDistributionOption" autoresize style="height: 240px" />
<UiCard class="h-[calc(100%-2rem)]">
<UiCardContent class="p-4">
<VChart :option="coinDistributionOption" autoresize style="height: 220px" />
</UiCardContent>
</UiCard>
</section>
<section class="lg:col-span-2">
<h2 class="text-sm font-medium text-muted-foreground mb-3 flex items-center gap-2">
<!-- 用户分析 -->
<section class="space-y-3 lg:col-span-3">
<h2 class="text-sm font-medium text-muted-foreground flex items-center gap-2">
<Icon icon="lucide:users" class="size-4" />
用户分析
</h2>
<div class="grid gap-4">
<div class="grid gap-3 sm:grid-cols-4">
<div class="space-y-3">
<div class="grid gap-3 grid-cols-2 sm:grid-cols-4">
<UiCard v-for="item in userMetrics" :key="item.label" class="hover:shadow-sm transition-shadow">
<UiCardContent class="pt-4 pb-4">
<p class="text-xs text-muted-foreground">
<UiCardContent class="p-4">
<p class="text-xs text-muted-foreground truncate">
{{ item.label }}
</p>
<p class="text-xl font-bold mt-1">
<p class="text-lg sm:text-xl font-bold mt-1">
{{ item.value }}
</p>
<p class="text-xs mt-1" :class="item.up ? 'text-green-600' : 'text-red-600'">
@@ -253,8 +255,8 @@ function formatCurrency(value: number): string {
</UiCard>
</div>
<UiCard>
<UiCardContent class="pt-6">
<VChart :option="userGrowthOption" autoresize style="height: 180px" />
<UiCardContent class="p-4">
<VChart :option="userGrowthOption" autoresize style="height: 160px" />
</UiCardContent>
</UiCard>
</div>
@@ -262,26 +264,26 @@ function formatCurrency(value: number): string {
</div>
<!-- 模块4: 风险指标 + 决策建议 -->
<div class="grid gap-4 lg:grid-cols-2">
<section>
<h2 class="text-sm font-medium text-muted-foreground mb-3 flex items-center gap-2">
<div class="grid gap-6 lg:grid-cols-2">
<section class="space-y-3">
<h2 class="text-sm font-medium text-muted-foreground flex items-center gap-2">
<Icon icon="lucide:shield" class="size-4" />
风险指标
</h2>
<div class="grid gap-3 sm:grid-cols-2">
<div class="grid gap-3 grid-cols-2">
<UiCard v-for="item in riskMetrics" :key="item.label" class="hover:shadow-sm transition-shadow">
<UiCardContent class="pt-6">
<UiCardContent class="p-4">
<div class="flex items-center justify-between mb-2">
<p class="text-sm font-medium">
<p class="text-xs font-medium truncate">
{{ item.label }}
</p>
<span class="text-xs text-muted-foreground">{{ item.threshold }}</span>
<span class="text-xs text-muted-foreground shrink-0">{{ item.threshold }}</span>
</div>
<div class="flex items-baseline gap-2">
<p class="text-3xl font-bold" :class="item.color">
<p class="text-2xl font-bold truncate" :class="item.color">
{{ item.value }}
</p>
<UiBadge v-if="item.status === 'warning'" variant="destructive">
<UiBadge v-if="item.status === 'warning'" variant="destructive" class="shrink-0">
需关注
</UiBadge>
</div>
@@ -290,8 +292,8 @@ function formatCurrency(value: number): string {
</div>
</section>
<section>
<h2 class="text-sm font-medium text-muted-foreground mb-3 flex items-center gap-2">
<section class="space-y-3">
<h2 class="text-sm font-medium text-muted-foreground flex items-center gap-2">
<Icon icon="lucide:lightbulb" class="size-4" />
决策建议
</h2>
@@ -299,16 +301,16 @@ function formatCurrency(value: number): string {
<div
v-for="item in suggestions"
:key="item.title"
class="p-4 rounded-lg border-l-4"
class="p-3 sm:p-4 rounded-lg border-l-4"
:class="item.color"
>
<div class="flex items-start gap-3">
<Icon :icon="item.icon" class="size-5 mt-0.5" />
<div>
<p class="font-medium">
<Icon :icon="item.icon" class="size-4 sm:size-5 mt-0.5 shrink-0" />
<div class="min-w-0">
<p class="font-medium text-sm">
{{ item.title }}
</p>
<p class="text-sm text-muted-foreground mt-1">
<p class="text-xs sm:text-sm text-muted-foreground mt-1">
{{ item.desc }}
</p>
</div>

View File

@@ -1,15 +1,20 @@
<script lang="ts" setup>
import Decimal from 'decimal.js'
import { Icon } from '@iconify/vue'
import VChart from 'vue-echarts'
import { useRouter } from 'vue-router'
import { BasicPage } from '@/components/global-layout'
import { useGetFinanceOverviewQuery } from '@/services/api/monisuo-admin.api'
import { useGetCashFlowTrendQuery, useGetFinanceOverviewQuery } from '@/services/api/monisuo-admin.api'
const router = useRouter()
const { data: overviewData, isLoading } = useGetFinanceOverviewQuery()
const { data: overviewData, isLoading: overviewLoading } = useGetFinanceOverviewQuery()
const { data: cashFlowData, isLoading: cashFlowLoading } = useGetCashFlowTrendQuery(6)
const isLoading = computed(() => overviewLoading.value || cashFlowLoading.value)
const overview = computed(() => overviewData.value?.data)
const cashFlow = computed(() => cashFlowData.value?.data || [])
// ========== 模块1: 资金概览 ==========
const fundMetrics = computed(() => [
@@ -37,32 +42,57 @@ const fundMetrics = computed(() => [
])
// ========== 模块2: 资金流动 ==========
const flowMetrics = computed(() => [
{
label: '累计充值',
value: overview.value?.totalDeposit || 0,
icon: 'lucide:arrow-down-circle',
color: 'text-green-600',
bgColor: 'bg-green-50 dark:bg-green-950',
trend: '+12.5%',
},
{
label: '累计提现',
value: overview.value?.totalWithdraw || 0,
icon: 'lucide:arrow-up-circle',
color: 'text-red-600',
bgColor: 'bg-red-50 dark:bg-red-950',
trend: '+8.3%',
},
{
label: '净流入',
value: (overview.value?.totalDeposit || 0) - (overview.value?.totalWithdraw || 0),
icon: 'lucide:trending-up',
color: 'text-emerald-600',
bgColor: 'bg-emerald-50 dark:bg-emerald-950',
trend: '+15.2%',
},
])
function calcGrowthRate(current: number, previous: number): string {
if (previous === 0)
return current > 0 ? '+100%' : '0%'
const rate = new Decimal(current).minus(previous).div(previous).mul(100).toDecimalPlaces(1)
const sign = rate.gte(0) ? '+' : ''
return `${sign}${rate}%`
}
const flowMetrics = computed(() => {
const flow = cashFlow.value
const len = flow.length
const thisMonth = len >= 1 ? flow[len - 1] : null
const lastMonth = len >= 2 ? flow[len - 2] : null
const depositTrend = thisMonth && lastMonth
? calcGrowthRate(thisMonth.deposit as number, lastMonth.deposit as number)
: '+0%'
const withdrawTrend = thisMonth && lastMonth
? calcGrowthRate(thisMonth.withdraw as number, lastMonth.withdraw as number)
: '+0%'
const netInflowTrend = thisMonth && lastMonth
? calcGrowthRate(thisMonth.netInflow as number, lastMonth.netInflow as number)
: '+0%'
return [
{
label: '累计充值',
value: overview.value?.totalDeposit || 0,
icon: 'lucide:arrow-down-circle',
color: 'text-green-600',
bgColor: 'bg-green-50 dark:bg-green-950',
trend: depositTrend,
},
{
label: '累计提现',
value: overview.value?.totalWithdraw || 0,
icon: 'lucide:arrow-up-circle',
color: 'text-red-600',
bgColor: 'bg-red-50 dark:bg-red-950',
trend: withdrawTrend,
},
{
label: '净流入',
value: (overview.value?.totalDeposit || 0) - (overview.value?.totalWithdraw || 0),
icon: 'lucide:trending-up',
color: 'text-emerald-600',
bgColor: 'bg-emerald-50 dark:bg-emerald-950',
trend: netInflowTrend,
},
]
})
// ========== 模块3: 资金趋势图 ==========
const trendChartOption = computed(() => ({
@@ -71,7 +101,7 @@ const trendChartOption = computed(() => ({
grid: { left: '3%', right: '4%', bottom: '15%', top: '5%', containLabel: true },
xAxis: {
type: 'category',
data: ['1月', '2月', '3月', '4月', '5月', '6月'],
data: cashFlow.value.map((t: any) => t.month),
},
yAxis: {
type: 'value',
@@ -82,7 +112,7 @@ const trendChartOption = computed(() => ({
name: '充值',
type: 'line',
smooth: true,
data: [320, 302, 301, 334, 390, 430],
data: cashFlow.value.map((t: any) => t.deposit),
itemStyle: { color: '#10b981' },
areaStyle: { color: 'rgba(16, 185, 129, 0.1)' },
},
@@ -90,7 +120,7 @@ const trendChartOption = computed(() => ({
name: '提现',
type: 'line',
smooth: true,
data: [120, 132, 101, 134, 90, 230],
data: cashFlow.value.map((t: any) => t.withdraw),
itemStyle: { color: '#ef4444' },
areaStyle: { color: 'rgba(239, 68, 68, 0.1)' },
},
@@ -98,23 +128,27 @@ const trendChartOption = computed(() => ({
}))
// ========== 模块4: 资金分布 ==========
const distributionOption = computed(() => ({
tooltip: { trigger: 'item', formatter: '{b}: {d}%' },
legend: { orient: 'vertical', right: '5%', top: 'center' },
series: [{
type: 'pie',
radius: ['50%', '75%'],
center: ['35%', '50%'],
avoidLabelOverlap: false,
itemStyle: { borderRadius: 8, borderColor: '#fff', borderWidth: 2 },
label: { show: true, position: 'inside', formatter: '{d}%', fontSize: 12 },
data: [
{ value: overview.value?.fundBalance || 50, name: '在管资金', itemStyle: { color: '#3b82f6' } },
{ value: overview.value?.tradeValue || 30, name: '交易账户', itemStyle: { color: '#8b5cf6' } },
{ value: 20, name: '冻结资金', itemStyle: { color: '#f59e0b' } },
],
}],
}))
const distributionOption = computed(() => {
const fundBalance = overview.value?.fundBalance || 0
const tradeValue = overview.value?.tradeValue || 0
return {
tooltip: { trigger: 'item', formatter: '{b}: {d}%' },
legend: { orient: 'vertical', right: '5%', top: 'center' },
series: [{
type: 'pie',
radius: ['50%', '75%'],
center: ['35%', '50%'],
avoidLabelOverlap: false,
itemStyle: { borderRadius: 8, borderColor: '#fff', borderWidth: 2 },
label: { show: true, position: 'inside', formatter: '{d}%', fontSize: 12 },
data: [
{ value: fundBalance, name: '在管资金', itemStyle: { color: '#3b82f6' } },
{ value: tradeValue, name: '交易账户', itemStyle: { color: '#8b5cf6' } },
],
}],
}
})
// ========== 模块5: 运营指标 ==========
const operationMetrics = computed(() => [
@@ -139,26 +173,26 @@ function navigateTo(path: string) {
<UiSpinner class="w-8 h-8" />
</div>
<div v-else class="space-y-6">
<div v-else class="grid gap-6">
<!-- 模块1: 资金概览 -->
<section>
<h2 class="text-sm font-medium text-muted-foreground mb-3 flex items-center gap-2">
<section class="space-y-3">
<h2 class="text-sm font-medium text-muted-foreground flex items-center gap-2">
<Icon icon="lucide:wallet" class="size-4" />
资金概览
</h2>
<div class="grid gap-3 sm:grid-cols-3">
<div class="grid gap-3 grid-cols-1 sm:grid-cols-3">
<UiCard v-for="item in fundMetrics" :key="item.label" class="hover:shadow-sm transition-shadow">
<UiCardContent class="pt-6">
<UiCardContent class="p-4">
<div class="flex items-center justify-between">
<div class="p-2.5 rounded-lg" :class="[item.bgColor]">
<Icon :icon="item.icon" class="size-5" :class="item.color" />
<div class="p-2 rounded-lg" :class="[item.bgColor]">
<Icon :icon="item.icon" class="size-4" :class="item.color" />
</div>
</div>
<div class="mt-3 space-y-1">
<p class="text-xs text-muted-foreground">
{{ item.label }}
</p>
<p class="text-2xl font-bold font-mono" :class="item.color">
<p class="text-lg sm:text-xl font-bold font-mono truncate" :class="item.color">
{{ formatCurrency(item.value) }}
</p>
</div>
@@ -168,17 +202,17 @@ function navigateTo(path: string) {
</section>
<!-- 模块2: 资金流动 -->
<section>
<h2 class="text-sm font-medium text-muted-foreground mb-3 flex items-center gap-2">
<section class="space-y-3">
<h2 class="text-sm font-medium text-muted-foreground flex items-center gap-2">
<Icon icon="lucide:git-compare" class="size-4" />
资金流动
</h2>
<div class="grid gap-3 sm:grid-cols-3">
<div class="grid gap-3 grid-cols-1 sm:grid-cols-3">
<UiCard v-for="item in flowMetrics" :key="item.label" class="hover:shadow-sm transition-shadow">
<UiCardContent class="pt-6">
<UiCardContent class="p-4">
<div class="flex items-center justify-between">
<div class="p-2.5 rounded-lg" :class="[item.bgColor]">
<Icon :icon="item.icon" class="size-5" :class="item.color" />
<div class="p-2 rounded-lg" :class="[item.bgColor]">
<Icon :icon="item.icon" class="size-4" :class="item.color" />
</div>
<span class="text-xs font-medium text-green-600">{{ item.trend }}</span>
</div>
@@ -186,7 +220,7 @@ function navigateTo(path: string) {
<p class="text-xs text-muted-foreground">
{{ item.label }}
</p>
<p class="text-2xl font-bold font-mono" :class="item.color">
<p class="text-lg sm:text-xl font-bold font-mono truncate" :class="item.color">
{{ formatCurrency(item.value) }}
</p>
</div>
@@ -196,55 +230,55 @@ function navigateTo(path: string) {
</section>
<!-- 模块3+4: 图表区域 -->
<div class="grid gap-4 lg:grid-cols-2">
<div class="grid gap-6 lg:grid-cols-2">
<!-- 资金趋势 -->
<section>
<h2 class="text-sm font-medium text-muted-foreground mb-3 flex items-center gap-2">
<section class="space-y-3">
<h2 class="text-sm font-medium text-muted-foreground flex items-center gap-2">
<Icon icon="lucide:trending-up" class="size-4" />
资金趋势
</h2>
<UiCard>
<UiCardContent class="pt-6">
<VChart :option="trendChartOption" autoresize style="height: 260px" />
<UiCardContent class="p-4">
<VChart :option="trendChartOption" autoresize style="height: 240px" />
</UiCardContent>
</UiCard>
</section>
<!-- 资金分布 -->
<section>
<h2 class="text-sm font-medium text-muted-foreground mb-3 flex items-center gap-2">
<section class="space-y-3">
<h2 class="text-sm font-medium text-muted-foreground flex items-center gap-2">
<Icon icon="lucide:pie-chart" class="size-4" />
资金分布
</h2>
<UiCard>
<UiCardContent class="pt-6">
<VChart :option="distributionOption" autoresize style="height: 260px" />
<UiCardContent class="p-4">
<VChart :option="distributionOption" autoresize style="height: 240px" />
</UiCardContent>
</UiCard>
</section>
</div>
<!-- 模块5: 运营指标 + 快捷入口 -->
<div class="grid gap-4 lg:grid-cols-3">
<div class="grid gap-6 lg:grid-cols-5">
<!-- 运营指标 -->
<section class="lg:col-span-1">
<h2 class="text-sm font-medium text-muted-foreground mb-3 flex items-center gap-2">
<section class="space-y-3 lg:col-span-2">
<h2 class="text-sm font-medium text-muted-foreground flex items-center gap-2">
<Icon icon="lucide:activity" class="size-4" />
运营指标
</h2>
<div class="grid gap-3 sm:grid-cols-2 lg:grid-cols-1">
<div class="grid gap-3 grid-cols-2">
<UiCard v-for="item in operationMetrics" :key="item.label" class="hover:shadow-sm transition-shadow">
<UiCardContent class="pt-6">
<UiCardContent class="p-4">
<div class="flex items-center justify-between">
<div>
<p class="text-xs text-muted-foreground">
<div class="min-w-0">
<p class="text-xs text-muted-foreground truncate">
{{ item.label }}
</p>
<p class="text-2xl font-bold mt-1">
<p class="text-lg sm:text-xl font-bold mt-1 truncate">
{{ item.value }}
</p>
</div>
<Icon :icon="item.icon" class="size-8 text-muted-foreground/30" />
<Icon :icon="item.icon" class="size-6 text-muted-foreground/30 shrink-0" />
</div>
</UiCardContent>
</UiCard>
@@ -252,19 +286,19 @@ function navigateTo(path: string) {
</section>
<!-- 快捷入口 -->
<section class="lg:col-span-2">
<h2 class="text-sm font-medium text-muted-foreground mb-3 flex items-center gap-2">
<section class="space-y-3 lg:col-span-3">
<h2 class="text-sm font-medium text-muted-foreground flex items-center gap-2">
<Icon icon="lucide:zap" class="size-4" />
快捷入口
</h2>
<div class="grid gap-3 sm:grid-cols-3">
<div class="grid gap-3 grid-cols-2 sm:grid-cols-4">
<UiCard
class="cursor-pointer hover:shadow-md hover:border-primary/50 transition-all"
@click="navigateTo('/monisuo/users')"
>
<UiCardContent class="pt-6 text-center">
<Icon icon="lucide:users" class="size-8 mx-auto mb-2 text-blue-600" />
<p class="font-medium">
<UiCardContent class="p-4 text-center">
<Icon icon="lucide:users" class="size-6 mx-auto mb-2 text-blue-600" />
<p class="text-sm font-medium">
用户管理
</p>
</UiCardContent>
@@ -274,9 +308,9 @@ function navigateTo(path: string) {
class="cursor-pointer hover:shadow-md hover:border-primary/50 transition-all"
@click="navigateTo('/monisuo/coins')"
>
<UiCardContent class="pt-6 text-center">
<Icon icon="lucide:coins" class="size-8 mx-auto mb-2 text-yellow-600" />
<p class="font-medium">
<UiCardContent class="p-4 text-center">
<Icon icon="lucide:coins" class="size-6 mx-auto mb-2 text-yellow-600" />
<p class="text-sm font-medium">
币种管理
</p>
</UiCardContent>
@@ -286,9 +320,9 @@ function navigateTo(path: string) {
class="cursor-pointer hover:shadow-md hover:border-primary/50 transition-all"
@click="navigateTo('/monisuo/orders')"
>
<UiCardContent class="pt-6 text-center">
<Icon icon="lucide:clipboard-check" class="size-8 mx-auto mb-2 text-green-600" />
<p class="font-medium">
<UiCardContent class="p-4 text-center">
<Icon icon="lucide:clipboard-check" class="size-6 mx-auto mb-2 text-green-600" />
<p class="text-sm font-medium">
订单审批
</p>
</UiCardContent>
@@ -298,9 +332,9 @@ function navigateTo(path: string) {
class="cursor-pointer hover:shadow-md hover:border-primary/50 transition-all"
@click="navigateTo('/monisuo/analytics')"
>
<UiCardContent class="pt-6 text-center">
<Icon icon="lucide:trending-up" class="size-8 mx-auto mb-2 text-purple-600" />
<p class="font-medium">
<UiCardContent class="p-4 text-center">
<Icon icon="lucide:trending-up" class="size-6 mx-auto mb-2 text-purple-600" />
<p class="text-sm font-medium">
业务分析
</p>
</UiCardContent>