Files
frontend/apps/web-ele/src/views/mall/home/components/analysis-overview.vue
lrl 992f0bd2f0 refactor: 重构商场首页和统计页面组件
- 新等组件
- 优化 Work增 AnalysisOverview、AnalysisOverviewIconbenchQuickDataShow 组件的使用
- 更新图标使用方式,移除自定义 SVG 图标
-提升页面视觉效果 调整布局和样式,
2025-07-23 10:51:13 +08:00

175 lines
5.4 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<script setup lang="ts">
import type { AnalysisOverviewItem } from './data';
import { computed } from 'vue';
import { VbenCountToAnimator } from '@vben/common-ui';
import { IconifyIcon } from '@vben/icons';
interface Props {
items?: AnalysisOverviewItem[];
modelValue?: AnalysisOverviewItem[];
columnsNumber?: number;
}
defineOptions({
name: 'AnalysisOverview',
});
const props = withDefaults(defineProps<Props>(), {
items: () => [],
modelValue: () => [],
columnsNumber: 4,
});
const emit = defineEmits(['update:modelValue']);
const itemsData = computed({
get: () => (props.modelValue?.length ? props.modelValue : props.items),
set: (value) => emit('update:modelValue', value),
});
// 计算动态的grid列数类名
const gridColumnsClass = computed(() => {
const colNum = props.columnsNumber;
return {
'lg:grid-cols-1': colNum === 1,
'lg:grid-cols-2': colNum === 2,
'lg:grid-cols-3': colNum === 3,
'lg:grid-cols-4': colNum === 4,
'lg:grid-cols-5': colNum === 5,
'lg:grid-cols-6': colNum === 6,
};
});
// 计算环比增长率
const calculateGrowthRate = (
currentValue: number,
previousValue: number,
): { isPositive: boolean; rate: number } => {
if (previousValue === 0) {
return { rate: currentValue > 0 ? 100 : 0, isPositive: currentValue >= 0 };
}
const rate = ((currentValue - previousValue) / previousValue) * 100;
return { rate: Math.abs(rate), isPositive: rate >= 0 };
};
// 格式化增长率显示
const formatGrowthRate = (rate: number): string => {
return rate.toFixed(1);
};
</script>
<template>
<div class="grid grid-cols-1 gap-4 md:grid-cols-2" :class="gridColumnsClass">
<template v-for="item in itemsData" :key="item.title">
<el-card :title="item.title" class="w-full">
<template #header>
<div class="text-lg font-semibold">
<div class="flex items-center justify-between">
<div class="flex items-center">
<span>{{ item.title }}</span>
<span v-if="item.tooltip" class="ml-1 inline-block">
<el-tooltip>
<template #default>
<div
class="inline-flex h-4 w-4 translate-y-[-3px] items-center justify-center rounded-full bg-gray-200 text-xs font-bold text-gray-600"
>
!
</div>
</template>
<template #content>
{{ item.tooltip }}
</template>
</el-tooltip>
</span>
</div>
<el-tag>今日</el-tag>
</div>
</div>
</template>
<template #default>
<!-- 左右布局左边数字右边图标 -->
<div class="flex items-center justify-between">
<!-- 左侧数字显示 -->
<div class="flex-1">
<div class="flex items-baseline">
<!-- prefix 前缀 -->
<span
v-if="item.prefix"
class="mr-1 text-3xl font-medium text-gray-600"
>
{{ item.prefix }}
</span>
<!-- 数字动画 -->
<VbenCountToAnimator
:end-val="item.value"
:start-val="1"
class="text-3xl font-bold text-gray-900"
prefix=""
/>
</div>
</div>
<!-- 右侧环比增长率图标和数值 -->
<div
v-if="item.showGrowthRate && item.totalValue !== undefined"
class="flex items-center space-x-2 rounded-lg bg-gray-50 px-3 py-2"
>
<IconifyIcon
:icon="
calculateGrowthRate(item.value, item.totalValue).isPositive
? 'lucide:trending-up'
: 'lucide:trending-down'
"
class="size-5"
:class="[
calculateGrowthRate(item.value, item.totalValue).isPositive
? 'text-green-500'
: 'text-red-500',
]"
/>
<span
class="text-sm font-semibold"
:class="[
calculateGrowthRate(item.value, item.totalValue).isPositive
? 'text-green-500'
: 'text-red-500',
]"
>
{{
calculateGrowthRate(item.value, item.totalValue).isPositive
? '+'
: '-'
}}{{
formatGrowthRate(
calculateGrowthRate(item.value, item.totalValue).rate,
)
}}%
</span>
</div>
</div>
</template>
<template #footer v-if="item.totalTitle">
<div class="flex items-center justify-between">
<span>{{ item.totalTitle }}</span>
<VbenCountToAnimator
:end-val="item.totalValue"
:start-val="1"
prefix=""
/>
</div>
</template>
</el-card>
</template>
</div>
</template>
<style lang="scss" scoped>
/* 移除 el-card header 的下边框 */
:deep(.el-card__header) {
padding-bottom: 16px;
border-bottom: none !important;
}
</style>