@@ -248,13 +264,21 @@ const statistics = ref([
trend: 8.3,
icon: 'Document',
iconColor: 'text-green-500'
+ },
+ {
+ label: '薪资总支出',
+ value: '100,000',
+ trend: 12.5,
+ icon: 'Money',
+ iconColor: 'text-blue-500'
}
]);
-const setStatistics = ({ totalDuration, totalRecords, averageDuration }) => {
+const setStatistics = ({ totalDuration, totalRecords, averageDuration, totalWage }) => {
statistics.value[0].value = totalRecords
statistics.value[1].value = averageDuration
statistics.value[2].value = totalDuration
+ statistics.value[3].value = totalWage
}
const setTrend = ({ attendanceData, overtimeData, travelData, xAxis }) => {
@@ -295,7 +319,7 @@ const setTrend = ({ attendanceData, overtimeData, travelData, xAxis }) => {
const setHeatmap = ({ heatmapData, hours, days }) => {
console.log(heatmapData, hours, days, heatmapChart.value)
-
+
// 计算数据中的最大值和最小值,用于visualMap映射
const values = heatmapData.map(item => item[2]);
const minValue = Math.min(...values);
@@ -444,6 +468,85 @@ const setDuration = ({ durationData, xAxis }) => {
});
}
+// 新增:1. 薪资支出趋势折线图配置
+const setSalaryTrend = ({ xAxis, salaryData }) => {
+ salaryTrendChart.value.setOption({
+ animation: false,
+ tooltip: {
+ trigger: 'axis',
+ formatter: '{b}
薪资支出:¥{c}', // 显示日期+薪资(带¥符号)
+ axisPointer: { type: 'shadow' }
+ },
+ legend: { data: ['薪资支出'], left: 'center', top: 0 },
+ grid: { left: '3%', right: '4%', bottom: '3%', containLabel: true },
+ xAxis: {
+ type: 'category',
+ data: xAxis,
+ axisLabel: { rotate: 30, margin: 10 } // 日期旋转避免重叠
+ },
+ yAxis: {
+ type: 'value',
+ name: '薪资(元)',
+ min: 0,
+ axisLabel: { formatter: '¥{value}' } // y轴显示¥符号
+ },
+ series: [
+ {
+ name: '薪资支出',
+ type: 'line',
+ data: salaryData,
+ lineStyle: { color: '#e11d48', width: 2 }, // 红色系(区分其他图表)
+ symbol: 'circle',
+ symbolSize: 6,
+ itemStyle: { color: '#e11d48' }
+ }
+ ]
+ });
+};
+
+// 新增:2. 薪资支出按人柱状图配置(横向,参考出勤排行)
+const setSalaryRank = ({ users, salaries }) => {
+ salaryRankChart.value.setOption({
+ animation: false,
+ tooltip: {
+ trigger: 'axis',
+ formatter: '{b}
总薪资:¥{c}',
+ axisPointer: { type: 'shadow' }
+ },
+ legend: { data: ['总薪资'], left: 'left', top: 0 },
+ grid: { left: '15%', right: '8%', bottom: '3%', containLabel: true }, // 左移适配用户名
+ xAxis: {
+ type: 'value',
+ name: '薪资(元)',
+ min: 0,
+ axisLabel: { formatter: '¥{value}' }
+ },
+ yAxis: {
+ type: 'category',
+ data: users,
+ axisLabel: { rotate: 0, margin: 15 }
+ },
+ series: [
+ {
+ name: '总薪资',
+ type: 'bar',
+ data: salaries,
+ itemStyle: {
+ color: '#e11d48',
+ borderRadius: [0, 4, 4, 0] // 横向柱状图右侧圆角
+ },
+ label: {
+ show: true,
+ position: 'right',
+ formatter: '¥{c}', // 标签显示¥符号
+ fontSize: 12
+ }
+ }
+ ]
+ });
+};
+
+
const getTypeIcon = (type) => {
const icons = {
attendance: Check,
@@ -461,6 +564,8 @@ const trendChart = ref(null);
const durationChart = ref(null);
const heatmapChart = ref(null);
const attendanceRankChart = ref(null);
+const salaryTrendChart = ref(null); // 薪资支出趋势折线图
+const salaryRankChart = ref(null); // 薪资支出按人柱状图
const initCharts = () => {
trendChart.value = echarts.init(document.querySelector('#trendChart'));
@@ -468,6 +573,8 @@ const initCharts = () => {
attendanceRankChart.value = echarts.init(document.querySelector('#attendanceRankChart'));
durationChart.value = echarts.init(document.querySelector('#durationChart'));
heatmapChart.value = echarts.init(document.querySelector('#heatmapChart'));
+ salaryTrendChart.value = echarts.init(document.querySelector('#salaryTrendChart'));
+ salaryRankChart.value = echarts.init(document.querySelector('#salaryRankChart'));
};
const updateCharts = () => {
@@ -480,6 +587,11 @@ const updateCharts = () => {
setAttendanceRank(attendanceRankData)
const durationData = formatters.duration(list.value)
setDuration(durationData)
+ // 新增:更新薪资图表
+ const salaryTrendData = formatters.salaryTrend(list.value);
+ setSalaryTrend(salaryTrendData);
+ const salaryRankData = formatters.salaryRank(list.value);
+ setSalaryRank(salaryRankData);
}
const formatters = {
@@ -489,11 +601,13 @@ const formatters = {
const totalRecords = list.length
// 可能出现NAN,所以需要处理
const averageDuration = totalRecords > 0 ? (totalDuration / totalRecords).toFixed(2) : 0
+ const totalWage = list.reduce((acc, item) => acc + item.wage, 0)
console.log(totalDuration, totalRecords, averageDuration)
return {
totalDuration,
totalRecords,
- averageDuration
+ averageDuration,
+ totalWage
}
},
trend: (list) => {
@@ -693,6 +807,55 @@ const formatters = {
xAxis: Object.keys(map),
durationData: Object.values(map)
}
+ },
+ // 新增:1. 薪资支出趋势数据格式化(按日期分组)
+ salaryTrend: (list) => {
+ const salaryData = [];
+ const dateMap = new Map(); // 去重日期并排序
+
+ // 按日期累加薪资
+ list.forEach(item => {
+ const { recordDate, wage = 0 } = item; // 兼容无wage字段的情况
+ if (!dateMap.has(recordDate)) dateMap.set(recordDate, true);
+
+ const exists = salaryData.find(i => i.date === recordDate);
+ if (exists) {
+ exists.salary += parseFloat(wage);
+ } else {
+ salaryData.push({ date: recordDate, salary: parseFloat(wage) });
+ }
+ });
+
+ // 日期排序,确保折线图顺序正确
+ const xAxis = Array.from(dateMap.keys()).sort();
+ // 补全缺失日期的薪资(为0)
+ const filledSalaryData = xAxis.map(date => {
+ const item = salaryData.find(i => i.date === date);
+ return item ? item.salary.toFixed(2) : 0;
+ });
+
+ return { xAxis, salaryData: filledSalaryData };
+ },
+
+ // 新增:2. 薪资支出按人排行数据格式化(取前10)
+ salaryRank: (list) => {
+ const userSalary = {};
+
+ // 按用户名累加总薪资
+ list.forEach(item => {
+ const { nickName, wage = 0 } = item;
+ userSalary[nickName] = (userSalary[nickName] || 0) + parseFloat(wage);
+ });
+
+ // 按薪资降序排序,取前10名
+ const sortedUsers = Object.entries(userSalary)
+ .sort((a, b) => b[1] - a[1])
+ .slice(0, 10);
+
+ return {
+ users: sortedUsers.map(item => item[0]), // 用户名
+ salaries: sortedUsers.map(item => item[1].toFixed(2)) // 总薪资(保留2位小数)
+ };
}
}