✨ feat: 首页指标卡
This commit is contained in:
9
gear-ui3/src/api/oa/dashboard.js
Normal file
9
gear-ui3/src/api/oa/dashboard.js
Normal file
@@ -0,0 +1,9 @@
|
||||
import request from '@/utils/request'
|
||||
|
||||
// 查询首页概览数据
|
||||
export function overview() {
|
||||
return request({
|
||||
url: '/oa/dashboard/overview',
|
||||
method: 'get',
|
||||
})
|
||||
}
|
||||
204
gear-ui3/src/views/components/Statistic.vue
Normal file
204
gear-ui3/src/views/components/Statistic.vue
Normal file
@@ -0,0 +1,204 @@
|
||||
<template>
|
||||
<div class="dashboard-cards">
|
||||
<el-card v-for="(card, index) in dataCards" :key="index" class="stats-card">
|
||||
<div class="card-header">
|
||||
<div class="card-info">
|
||||
<h3 class="card-title">{{ card.title }}</h3>
|
||||
<!-- 第三个卡片不显示value -->
|
||||
<p class="card-value">{{ card.value }}</p>
|
||||
</div>
|
||||
<el-icon class="card-icon" :style="{ color: card.color }">
|
||||
<component :is="card.icon" />
|
||||
</el-icon>
|
||||
</div>
|
||||
<!-- 第四个卡片不显示图表 -->
|
||||
<div class="chart-container" v-if="index !== 3">
|
||||
<div :ref="el => chartRefs[index] = el" class="chart"></div>
|
||||
</div>
|
||||
</el-card>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup>
|
||||
import { ShoppingCart, Box, List, Monitor } from '@element-plus/icons-vue';
|
||||
import { overview } from '@/api/oa/dashboard';
|
||||
import * as echarts from 'echarts';
|
||||
import { ref, onMounted, nextTick } from 'vue';
|
||||
|
||||
const getColor = (index, alpha = 1) => {
|
||||
const colors = [
|
||||
`rgba(59, 130, 246, ${alpha})`,
|
||||
`rgba(34, 197, 94, ${alpha})`,
|
||||
`rgba(234, 179, 8, ${alpha})`,
|
||||
`rgba(168, 85, 247, ${alpha})`
|
||||
];
|
||||
return colors[index % colors.length];
|
||||
};
|
||||
|
||||
const initCharts = () => {
|
||||
nextTick(() => {
|
||||
// 只初始化前3个卡片的图表
|
||||
dataCards.value.slice(0, 3).forEach((card, index) => {
|
||||
const chart = echarts.init(chartRefs.value[index]);
|
||||
// 判断是否为第三个图表(index=2),使用柱状图配置
|
||||
const isBarChart = index === 2;
|
||||
|
||||
const option = {
|
||||
animation: false,
|
||||
grid: {
|
||||
left: 0,
|
||||
right: 0,
|
||||
top: 0,
|
||||
bottom: 0
|
||||
},
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
show: false
|
||||
},
|
||||
yAxis: {
|
||||
type: 'value',
|
||||
show: false
|
||||
},
|
||||
series: [{
|
||||
data: card.chartData,
|
||||
type: isBarChart ? 'bar' : 'line', // 第三个图表用柱状图,其他用折线图
|
||||
smooth: !isBarChart, // 柱状图不需要平滑效果
|
||||
showSymbol: false,
|
||||
lineStyle: !isBarChart ? { // 折线图样式(柱状图不需要)
|
||||
color: getColor(index)
|
||||
} : undefined,
|
||||
// 柱状图颜色配置
|
||||
itemStyle: isBarChart ? {
|
||||
color: getColor(index)
|
||||
} : undefined,
|
||||
// 折线图区域填充(柱状图不需要)
|
||||
areaStyle: !isBarChart ? {
|
||||
color: new echarts.graphic.LinearGradient(0, 0, 0, 1, [{
|
||||
offset: 0,
|
||||
color: getColor(index, 0.2)
|
||||
}, {
|
||||
offset: 1,
|
||||
color: getColor(index, 0.1)
|
||||
}])
|
||||
} : undefined
|
||||
}]
|
||||
};
|
||||
chart.setOption(option);
|
||||
});
|
||||
});
|
||||
};
|
||||
|
||||
|
||||
const dataCards = ref([
|
||||
{
|
||||
title: '本周订单总量',
|
||||
value: '2,384',
|
||||
icon: ShoppingCart,
|
||||
color: '#3B82F6', // blue-500
|
||||
chartData: [30, 40, 20, 50, 40, 60, 70]
|
||||
},
|
||||
{
|
||||
title: '本周薪资成本',
|
||||
value: '48',
|
||||
icon: List,
|
||||
color: '#F59E0B', // yellow-500
|
||||
chartData: [20, 40, 30, 50, 40, 60, 50]
|
||||
},
|
||||
{
|
||||
title: '库存排行',
|
||||
// 第三个卡片不需要value
|
||||
value: '-',
|
||||
icon: Box,
|
||||
color: '#10B981', // green-500
|
||||
chartData: [40, 30, 50, 40, 60, 50, 70]
|
||||
},
|
||||
{
|
||||
title: '系统状态',
|
||||
value: '正常',
|
||||
icon: Monitor,
|
||||
color: '#8B5CF6', // purple-500
|
||||
// 第四个卡片不需要图表数据
|
||||
chartData: []
|
||||
}
|
||||
]);
|
||||
|
||||
const chartRefs = ref([]);
|
||||
|
||||
onMounted(() => {
|
||||
overview().then(res => {
|
||||
dataCards.value[0].value = res.data.orderStatistics.weekOrderCount;
|
||||
dataCards.value[0].chartData = res.data.orderStatistics.weeklyTrend.map(item => item.value);
|
||||
|
||||
dataCards.value[1].value = res.data.salaryStatistics.weekSalary;
|
||||
dataCards.value[1].chartData = res.data.salaryStatistics.weeklyTrend.map(item => item.value);
|
||||
|
||||
// 处理库存排行数据
|
||||
dataCards.value[2].chartData = res.data.stockRanking.map(item => item.quantity);
|
||||
|
||||
initCharts();
|
||||
})
|
||||
})
|
||||
</script>
|
||||
|
||||
<style lang="scss">
|
||||
.dashboard-cards {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(4, 1fr);
|
||||
gap: 1.5rem;
|
||||
margin-bottom: 2rem;
|
||||
}
|
||||
|
||||
.stats-card {
|
||||
border-radius: 0.5rem !important;
|
||||
backdrop-filter: blur(4px);
|
||||
background-color: rgba(255, 255, 255, 0.8);
|
||||
border: 1px solid rgba(255, 255, 255, 0.2);
|
||||
transform: scale(1);
|
||||
transition: all 0.3s ease;
|
||||
|
||||
&:hover {
|
||||
transform: scale(1.02);
|
||||
}
|
||||
}
|
||||
|
||||
.card-header {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: flex-start;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.card-info {
|
||||
// 用于包裹标题和值
|
||||
}
|
||||
|
||||
.card-title {
|
||||
color: #6B7280; // gray-500
|
||||
font-size: 0.875rem;
|
||||
margin-bottom: 0.25rem;
|
||||
}
|
||||
|
||||
.card-value {
|
||||
font-size: 1.5rem;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.card-icon {
|
||||
font-size: 1.5rem;
|
||||
}
|
||||
|
||||
.chart-container {
|
||||
height: 3rem;
|
||||
width: 100%;
|
||||
|
||||
.chart {
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
}
|
||||
}
|
||||
|
||||
// 第四个卡片没有图表,调整一下底部边距
|
||||
.stats-card:nth-child(4) .card-header {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
</style>
|
||||
@@ -3,6 +3,8 @@
|
||||
<!-- 第一行:头像+欢迎语 -->
|
||||
<UserGreeting />
|
||||
|
||||
<Statistic />
|
||||
|
||||
<!-- 全部应用 -->
|
||||
<AllApplications @addFavorites="handleAddFavorites" />
|
||||
</div>
|
||||
@@ -11,6 +13,7 @@
|
||||
<script setup>
|
||||
import AllApplications from '@/components/AllApplications.vue';
|
||||
import UserGreeting from '@/views/components/Hello.vue';
|
||||
import Statistic from '@/views/components/Statistic.vue';
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
|
||||
Reference in New Issue
Block a user