feat: 新增商品统计组件和优化数据处理逻辑

- 引入商品排行和商品概况组件,展示商品相关统计信息
- 更新商品统计 API,支持时间范围查询和数据格式化
- 优化数据加载逻辑,提升用户体验
- 添加日期范围选择器,增强统计数据的灵活性
This commit is contained in:
lrl
2025-07-17 09:53:04 +08:00
parent 73a73ac312
commit 4620ede9b9
9 changed files with 587 additions and 29 deletions

View File

@@ -22,3 +22,18 @@ export { default as cloneDeep } from 'lodash.clonedeep';
export { default as get } from 'lodash.get';
export { default as isEqual } from 'lodash.isequal';
export { default as set } from 'lodash.set';
/**
* 构建排序字段
* @param prop 字段名称
* @param order 顺序
*/
export const buildSortingField = ({
prop,
order,
}: {
order: 'ascending' | 'descending';
prop: string;
}) => {
return { field: prop, order: order === 'ascending' ? 'asc' : 'desc' };
};

View File

@@ -9,6 +9,10 @@ import {
CardFooter,
CardHeader,
CardTitle,
Tooltip,
TooltipContent,
TooltipProvider,
TooltipTrigger,
VbenCountToAnimator,
VbenIcon,
} from '@vben-core/shadcn-ui';
@@ -16,6 +20,7 @@ import {
interface Props {
items?: AnalysisOverviewItem[];
modelValue?: AnalysisOverviewItem[];
columnsNumber?: number;
}
defineOptions({
@@ -25,6 +30,7 @@ defineOptions({
const props = withDefaults(defineProps<Props>(), {
items: () => [],
modelValue: () => [],
columnsNumber: 4,
});
const emit = defineEmits(['update:modelValue']);
@@ -33,14 +39,45 @@ 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,
};
});
</script>
<template>
<div class="grid grid-cols-1 gap-4 md:grid-cols-2 lg:grid-cols-4">
<div class="grid grid-cols-1 gap-4 md:grid-cols-2" :class="gridColumnsClass">
<template v-for="item in itemsData" :key="item.title">
<Card :title="item.title" class="w-full">
<CardHeader>
<CardTitle class="text-xl">{{ item.title }}</CardTitle>
<CardTitle class="text-xl">
<div class="flex items-center">
<span>{{ item.title }}</span>
<span v-if="item.tooltip" class="ml-1 inline-block">
<TooltipProvider>
<Tooltip>
<TooltipTrigger>
<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>
</TooltipTrigger>
<TooltipContent>{{ item.tooltip }}</TooltipContent>
</Tooltip>
</TooltipProvider>
</span>
</div>
</CardTitle>
</CardHeader>
<CardContent class="flex items-center justify-between">

View File

@@ -6,6 +6,7 @@ interface AnalysisOverviewItem {
totalTitle?: string;
totalValue?: number;
value: number;
tooltip?: string;
}
interface WorkbenchProjectItem {

View File

@@ -0,0 +1,33 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 400 300">
<!-- 样式定义 -->
<style>
.eye-outline { fill: #0D47A1; }
.eye-white { fill: #BBDEFB; }
.eye-iris { fill: #2196F3; }
.eye-pupil { fill: #000000; }
.eye-highlight { fill: #FFFFFF; }
.eye-shadow { fill: #1565C0; }
</style>
<!-- 眼睛外轮廓 -->
<path class="eye-outline" d="M200,250c-80,0-160-60-200-100c40-40,120-100,200-100s160,60,200,100C360,190,280,250,200,250z"/>
<!-- 眼睛白色部分 -->
<path class="eye-white" d="M200,70c70,0,140,50,180,80c-40,30-110,80-180,80s-140-50-180-80C60,120,130,70,200,70z"/>
<!-- 眼睑阴影 -->
<path class="eye-shadow" d="M200,90c-60,0-120,40-160,60c40,20,100,60,160,60s120-40,160-60C320,130,260,90,200,90z"/>
<!-- 虹膜 -->
<circle class="eye-iris" cx="200" cy="150" r="60"/>
<!-- 瞳孔 - 确保是明显的黑色圆形 -->
<circle class="eye-pupil" cx="200" cy="150" r="25"/>
<!-- 高光 -->
<circle class="eye-highlight" cx="180" cy="130" r="12"/>
<!-- 装饰线条 -->
<path class="eye-highlight" d="M100,110c10-5,30-15,40-20c3-1,2-5-1-4c-10,5-30,15-40,20C96,107,97,111,100,110z"/>
<path class="eye-highlight" d="M300,190c2-5,5-10,10-15c10-10,20-20,30-25c2-1,0-5-2-4c-15,10-30,30-40,45C297,193,299,195,300,190z"/>
</svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

View File

@@ -10,6 +10,7 @@ const SvgDownloadIcon = createIconifyIcon('svg:download');
const SvgCardIcon = createIconifyIcon('svg:card');
const SvgBellIcon = createIconifyIcon('svg:bell');
const SvgCakeIcon = createIconifyIcon('svg:cake');
const SvgEyeIcon = createIconifyIcon('svg:eye');
const SvgAntdvLogoIcon = createIconifyIcon('svg:antdv-logo');
/** AI */
@@ -44,6 +45,7 @@ export {
SvgCakeIcon,
SvgCardIcon,
SvgDownloadIcon,
SvgEyeIcon,
SvgGptIcon,
SvgMockIcon,
SvgWalletIcon,