Merge remote-tracking branch 'origin/master'

This commit is contained in:
2025-09-18 10:03:42 +08:00
3 changed files with 216 additions and 0 deletions

View File

@@ -0,0 +1,9 @@
import request from '@/utils/request'
// 查询首页概览数据
export function overview() {
return request({
url: '/oa/dashboard/overview',
method: 'get',
})
}

View 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>

View File

@@ -3,6 +3,8 @@
<!-- 第一行头像+欢迎语 --> <!-- 第一行头像+欢迎语 -->
<UserGreeting /> <UserGreeting />
<Statistic />
<!-- 全部应用 --> <!-- 全部应用 -->
<AllApplications @addFavorites="handleAddFavorites" /> <AllApplications @addFavorites="handleAddFavorites" />
</div> </div>
@@ -11,6 +13,7 @@
<script setup> <script setup>
import AllApplications from '@/components/AllApplications.vue'; import AllApplications from '@/components/AllApplications.vue';
import UserGreeting from '@/views/components/Hello.vue'; import UserGreeting from '@/views/components/Hello.vue';
import Statistic from '@/views/components/Statistic.vue';
</script> </script>
<style scoped> <style scoped>