整合前端
This commit is contained in:
322
ruoyi-ui/src/views/oa/finance/salary/dashboard/index.vue
Normal file
322
ruoyi-ui/src/views/oa/finance/salary/dashboard/index.vue
Normal file
@@ -0,0 +1,322 @@
|
||||
<template>
|
||||
<div style="padding:20px">
|
||||
<!-- 时间范围选择 -->
|
||||
<el-row class="mb8">
|
||||
<el-col :span="4">
|
||||
<el-date-picker
|
||||
v-model="selectedTime"
|
||||
type="month"
|
||||
placeholder="选择年月"
|
||||
value-format="yyyy-MM-01"
|
||||
>
|
||||
</el-date-picker>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<!-- 卡片数据展示 -->
|
||||
<el-row :gutter="20" v-loading="loading" style="margin-bottom: 20px">
|
||||
<el-col :span="24">
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="4" v-for="card in cards" :key="card.title">
|
||||
<statistic-card
|
||||
:title="card.title"
|
||||
:value="card.value"
|
||||
:sub-text="card.subText"
|
||||
:growth="card.growth"
|
||||
/>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<!-- 图表区域第一行 -->
|
||||
<el-row :gutter="20" style="margin-bottom: 20px">
|
||||
<el-col :span="12">
|
||||
<el-card v-loading="monthlyExpenseLoading">
|
||||
<div slot="header" class="clearfix">
|
||||
<span>月度支出汇总</span>
|
||||
</div>
|
||||
<bar-chart :data="monthlyExpenseData" />
|
||||
</el-card>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-card v-loading="salaryStructureLoading">
|
||||
<div slot="header" class="clearfix">
|
||||
<span>工资构成分析</span>
|
||||
</div>
|
||||
<pie-chart :data="salaryStructureData" :categories="salaryStructureCategories" />
|
||||
</el-card>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<!-- 图表区域第二行 -->
|
||||
<el-row :gutter="20" style="margin-bottom: 20px">
|
||||
<el-col :span="12">
|
||||
<el-card v-loading="trendLoading">
|
||||
<div slot="header" class="clearfix">
|
||||
<span>工资趋势分析</span>
|
||||
</div>
|
||||
<line-chart :data="trendData" />
|
||||
</el-card>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-card v-loading="insuranceLoading">
|
||||
<div slot="header" class="clearfix">
|
||||
<span>社保公积金统计</span>
|
||||
</div>
|
||||
<bar-chart :data="insuranceData" :type="'insurance'" />
|
||||
</el-card>
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<!-- 统计表格 -->
|
||||
<el-row :gutter="20">
|
||||
<el-col :span="12">
|
||||
<el-card v-loading="unitTableLoading">
|
||||
<div slot="header" class="clearfix">
|
||||
<span>单位统计</span>
|
||||
</div>
|
||||
<unit-table :data="unitTableData" :total="unitTableTotal" />
|
||||
</el-card>
|
||||
</el-col>
|
||||
<el-col :span="12">
|
||||
<el-card v-loading="deptTableLoading">
|
||||
<div slot="header" class="clearfix">
|
||||
<span>部门统计</span>
|
||||
</div>
|
||||
<department-table :data="deptTableData" :total="deptTableTotal" />
|
||||
</el-card>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import { getAllData } from '@/api/oa/finance/dashboard';
|
||||
import BarChart from './components/BarChart.vue';
|
||||
import DepartmentTable from './components/DepartmentTable.vue';
|
||||
import UnitTable from './components/UnitTable.vue';
|
||||
import LineChart from './components/LineChart.vue';
|
||||
import PieChart from './components/PieChart.vue';
|
||||
import StatisticCard from './components/StatisticCard.vue';
|
||||
|
||||
export default {
|
||||
name: 'SalaryDashboard',
|
||||
components: {
|
||||
StatisticCard,
|
||||
BarChart,
|
||||
PieChart,
|
||||
LineChart,
|
||||
DepartmentTable,
|
||||
UnitTable
|
||||
},
|
||||
created() {
|
||||
const now = new Date();
|
||||
this.selectedTime = `${now.getFullYear()}-${(now.getMonth() + 1).toString().padStart(2, '0')}-01`;
|
||||
},
|
||||
watch: {
|
||||
selectedTime: {
|
||||
handler(val) {
|
||||
if (val) {
|
||||
const date = new Date(val);
|
||||
const year = date.getFullYear();
|
||||
const month = (date.getMonth() + 1).toString().padStart(2, '0');
|
||||
const salaryPeriod = `${year}-${month}`;
|
||||
this.fetchData(salaryPeriod);
|
||||
}
|
||||
},
|
||||
immediate: true
|
||||
}
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
selectedTime: null,
|
||||
loading: false,
|
||||
monthlyExpenseLoading: false,
|
||||
salaryStructureLoading: false,
|
||||
trendLoading: false,
|
||||
insuranceLoading: false,
|
||||
unitTableLoading: false,
|
||||
deptTableLoading: false,
|
||||
cards: [],
|
||||
monthlyExpenseData: [],
|
||||
salaryStructureCategories: [],
|
||||
salaryStructureData: [],
|
||||
trendData: [],
|
||||
insuranceData: [],
|
||||
unitTableData: [],
|
||||
unitTableTotal: 0,
|
||||
deptTableData: [],
|
||||
deptTableTotal: 0,
|
||||
};
|
||||
},
|
||||
methods: {
|
||||
async fetchData(salaryPeriod) {
|
||||
this.loading = true;
|
||||
this.monthlyExpenseLoading = true;
|
||||
this.salaryStructureLoading = true;
|
||||
this.trendLoading = true;
|
||||
this.insuranceLoading = true;
|
||||
this.unitTableLoading = true;
|
||||
this.deptTableLoading = true;
|
||||
|
||||
try {
|
||||
const response = await getAllData({
|
||||
salaryPeriod: salaryPeriod
|
||||
});
|
||||
|
||||
if(response.code === 200) {
|
||||
const { data } = response;
|
||||
const { cardData, chartData, unitStats, deptStats } = data;
|
||||
|
||||
// 处理卡片数据
|
||||
this.processCardData(cardData);
|
||||
|
||||
// 处理图表数据
|
||||
this.processChartData(chartData);
|
||||
|
||||
// 处理表格数据
|
||||
this.processTableData(unitStats, deptStats);
|
||||
}
|
||||
} catch (error) {
|
||||
console.error('获取数据失败:', error);
|
||||
this.$message.error('获取数据失败,请稍后重试');
|
||||
} finally {
|
||||
this.loading = false;
|
||||
this.monthlyExpenseLoading = false;
|
||||
this.salaryStructureLoading = false;
|
||||
this.trendLoading = false;
|
||||
this.insuranceLoading = false;
|
||||
this.unitTableLoading = false;
|
||||
this.deptTableLoading = false;
|
||||
}
|
||||
},
|
||||
|
||||
// 处理卡片数据
|
||||
processCardData(cardData) {
|
||||
this.cards = [
|
||||
{
|
||||
title: '总实发工资',
|
||||
value: this.formatCurrency(cardData.totalNetSalary),
|
||||
subText: `较上月${this.getGrowthText(cardData.lastMonthNetSalaryRate)}`,
|
||||
growth: this.getGrowthDirection(cardData.lastMonthNetSalaryRate)
|
||||
},
|
||||
{
|
||||
title: '总应发工资',
|
||||
value: this.formatCurrency(cardData.totalGrossSalary),
|
||||
subText: `较上月${this.getGrowthText(cardData.lastMonthGrossSalaryRate)}`,
|
||||
growth: this.getGrowthDirection(cardData.lastMonthGrossSalaryRate)
|
||||
},
|
||||
{
|
||||
title: '单位总支出',
|
||||
value: this.formatCurrency(cardData.totalUnitExpense),
|
||||
subText: `较上月${this.getGrowthText(cardData.lastMonthUnitExpenseRate)}`,
|
||||
growth: this.getGrowthDirection(cardData.lastMonthUnitExpenseRate)
|
||||
},
|
||||
{
|
||||
title: '人均实发工资',
|
||||
value: this.formatCurrency(cardData.avgNetSalary),
|
||||
subText: `较上月${this.getGrowthText(cardData.lastMonthAvgNetSalaryRate)}`,
|
||||
growth: this.getGrowthDirection(cardData.lastMonthAvgNetSalaryRate)
|
||||
},
|
||||
{
|
||||
title: '同比增长率',
|
||||
value: `${cardData.yearOnYearGrowthRate || 0}%`,
|
||||
subText: '较去年同期',
|
||||
growth: this.getGrowthDirection(cardData.yearOnYearGrowthRate)
|
||||
},
|
||||
{
|
||||
title: '员工总数',
|
||||
value: `${cardData.totalEmployeeCount || 0}人`,
|
||||
subText: `共${cardData.unitCount || 0}个单位`,
|
||||
growth: 'neutral'
|
||||
}
|
||||
];
|
||||
},
|
||||
|
||||
// 处理图表数据
|
||||
processChartData(chartData) {
|
||||
// 月度支出数据
|
||||
this.monthlyExpenseData = chartData.monthlyExpenses?.map(item => ({
|
||||
month: `${item.month}月`,
|
||||
netSalary: item.totalNetSalary,
|
||||
grossSalary: item.totalGrossSalary,
|
||||
unitExpense: item.totalUnitExpense
|
||||
})) || [];
|
||||
|
||||
// 工资构成分析数据
|
||||
this.salaryStructureCategories = chartData.salaryStructures?.map(item => item.itemName) || [];
|
||||
this.salaryStructureData = chartData.salaryStructures?.map(item => ({
|
||||
name: item.itemName,
|
||||
value: item.totalAmount,
|
||||
percentage: item.percentage
|
||||
})) || [];
|
||||
|
||||
// 趋势分析数据
|
||||
if (chartData.trendData) {
|
||||
this.trendData = chartData.trendData.months?.map((month, index) => ({
|
||||
month: `${month}月`,
|
||||
netSalary: chartData.trendData.netSalaryTrend?.[index] || 0,
|
||||
grossSalary: chartData.trendData.grossSalaryTrend?.[index] || 0,
|
||||
avgNetSalary: chartData.trendData.avgNetSalaryTrend?.[index] || 0
|
||||
})) || [];
|
||||
}
|
||||
|
||||
// 社保公积金统计数据
|
||||
this.insuranceData = chartData.insuranceStats?.map(item => ({
|
||||
itemName: item.itemName,
|
||||
personal: item.personalTotal,
|
||||
enterprise: item.enterpriseTotal,
|
||||
total: item.total
|
||||
})) || [];
|
||||
},
|
||||
|
||||
// 处理表格数据
|
||||
processTableData(unitStats, deptStats) {
|
||||
// 单位统计数据
|
||||
this.unitTableData = unitStats?.rows?.map(item => ({
|
||||
unitName: item.unitName,
|
||||
employeeCount: item.employeeCount,
|
||||
totalNetSalary: this.formatCurrency(item.totalNetSalary),
|
||||
totalGrossSalary: this.formatCurrency(item.totalGrossSalary),
|
||||
totalUnitExpense: this.formatCurrency(item.totalUnitExpense),
|
||||
avgNetSalary: this.formatCurrency(item.avgNetSalary),
|
||||
yearOnYearGrowthRate: `${item.yearOnYearGrowthRate >= 0 ? '+' : ''}${item.yearOnYearGrowthRate}%`,
|
||||
salaryPeriod: item.salaryPeriod
|
||||
})) || [];
|
||||
this.unitTableTotal = unitStats?.total || 0;
|
||||
|
||||
// 部门统计数据
|
||||
this.deptTableData = deptStats?.rows?.map(item => ({
|
||||
deptName: item.deptName,
|
||||
employeeCount: item.employeeCount,
|
||||
totalNetSalary: this.formatCurrency(item.totalNetSalary),
|
||||
totalGrossSalary: this.formatCurrency(item.totalGrossSalary),
|
||||
avgNetSalary: this.formatCurrency(item.avgNetSalary),
|
||||
avgGrossSalary: this.formatCurrency(item.avgGrossSalary),
|
||||
yearOnYearGrowthRate: `${item.yearOnYearGrowthRate >= 0 ? '+' : ''}${item.yearOnYearGrowthRate}%`
|
||||
})) || [];
|
||||
this.deptTableTotal = deptStats?.total || 0;
|
||||
},
|
||||
|
||||
// 格式化货币
|
||||
formatCurrency(amount) {
|
||||
if (!amount) return '¥ 0';
|
||||
return `¥ ${Number(amount).toLocaleString()}`;
|
||||
},
|
||||
|
||||
// 获取增长文本
|
||||
getGrowthText(rate) {
|
||||
if (!rate) return '无变化';
|
||||
const direction = rate >= 0 ? '增长' : '下降';
|
||||
return `${direction} ${Math.abs(rate)}%`;
|
||||
},
|
||||
|
||||
// 获取增长方向
|
||||
getGrowthDirection(rate) {
|
||||
if (!rate) return 'neutral';
|
||||
return rate >= 0 ? 'up' : 'down';
|
||||
}
|
||||
}
|
||||
};
|
||||
</script>
|
||||
Reference in New Issue
Block a user