From a5f5e2c3fa5953a5474e4ad5d504c23acab94900 Mon Sep 17 00:00:00 2001
From: ZN <zhang_12370@163.com>
Date: 星期五, 20 三月 2026 10:25:27 +0800
Subject: [PATCH] fix(analytics): 修正员工分析页面数据计算和样式问题
---
src/views/customerService/feedbackRegistration/components/formDia.vue | 6 +
src/views/personnelManagement/employeeRecord/index.vue | 26 +++++-
src/views/personnelManagement/analytics/index.vue | 167 +++++++++++++++++++++++++++++++++++++++--
3 files changed, 183 insertions(+), 16 deletions(-)
diff --git a/src/views/customerService/feedbackRegistration/components/formDia.vue b/src/views/customerService/feedbackRegistration/components/formDia.vue
index 71cd167..8f9bb91 100644
--- a/src/views/customerService/feedbackRegistration/components/formDia.vue
+++ b/src/views/customerService/feedbackRegistration/components/formDia.vue
@@ -365,7 +365,11 @@
// 鎵撳紑寮规
const openDialog =async (type, row) => {
// 璇锋眰澶氫釜鎺ュ彛锛岃幏鍙栨暟鎹�
- let res = await getAllCustomerList();
+ let res = await getAllCustomerList({
+ current: 1,
+ size: 1000,
+ total: 0,
+ });
if(res.records){
customerNameOptions.value = res.records.map(item => ({
label: item.customerName,
diff --git a/src/views/personnelManagement/analytics/index.vue b/src/views/personnelManagement/analytics/index.vue
index 9e6e449..db97619 100644
--- a/src/views/personnelManagement/analytics/index.vue
+++ b/src/views/personnelManagement/analytics/index.vue
@@ -86,12 +86,15 @@
import { ref, onMounted, onUnmounted, nextTick } from 'vue'
import { ElMessage } from 'element-plus'
import * as echarts from 'echarts'
+import dayjs from 'dayjs'
import {listDept} from "@/api/system/dept.js";
import {
findStaffAnalysisMonthlyTurnoverRateFor12Months,
findStaffLeaveReasonAnalysis,
findStaffAnalysisTotalStatistic
} from "@/api/personnelManagement/staffAnalytics.js";
+import { staffOnJobListPage } from "@/api/personnelManagement/staffOnJob.js";
+import { findStaffLeaveListPage } from "@/api/personnelManagement/staffLeave.js";
// 鍝嶅簲寮忔暟鎹�
const loading = ref(false)
@@ -148,6 +151,43 @@
const staffLeaveReasons = ref([])
// 12涓湀鍛樺伐娴佸姩娴佸け鐜囧垎鏋愭暟鎹�
const turnoverRateStatistics = ref([])
+const turnoverRateStatisticsRaw = ref([])
+const turnoverSeriesNormalized = ref(false)
+
+const staffCounts = ref({
+ onJobTotal: 0,
+ leaveTotal: 0,
+ totalStaff: 0,
+ leaveLast12Months: 0,
+ joinLeaveRatio: 1
+})
+
+const safeNum = (val) => {
+ const num = Number(val)
+ return Number.isFinite(num) ? num : 0
+}
+
+const round2 = (val) => {
+ const num = safeNum(val)
+ return Number(num.toFixed(2))
+}
+
+const getListTotal = (res) => safeNum(res?.data?.total ?? res?.data?.count ?? 0)
+
+const getLast12MonthRanges = () => {
+ const end = dayjs()
+ const start = end.subtract(11, 'month').startOf('month')
+ const ranges = []
+ for (let i = 0; i < 12; i++) {
+ const cur = start.add(i, 'month')
+ ranges.push({
+ month: cur.format('YYYY-MM'),
+ start: cur.startOf('month').format('YYYY-MM-DD'),
+ end: cur.endOf('month').format('YYYY-MM-DD')
+ })
+ }
+ return ranges
+}
// 鑾峰彇閮ㄩ棬鏁版嵁
const getDepartmentData = async () => {
@@ -177,7 +217,7 @@
try {
const res = await findStaffAnalysisMonthlyTurnoverRateFor12Months()
if (res && res.data) {
- turnoverRateStatistics.value = res.data || []
+ turnoverRateStatisticsRaw.value = res.data || []
}
} catch (error) {
console.error('鑾峰彇12涓湀鍛樺伐娴佸姩娴佸け鐜囧垎鏋愭暟鎹け璐�:', error)
@@ -186,14 +226,112 @@
const getStaffAnalysisTotalStatistic = async () => {
try {
- const res = await findStaffAnalysisTotalStatistic()
- if (res && res.data) {
- keyMetrics.value[0].value = res.data.totalFlowRate || 0
- keyMetrics.value[1].value = res.data.totalTurnoverRate || 0
- keyMetrics.value[2].value = res.data.currentOnJobCount || 0
+ const ranges = getLast12MonthRanges()
+ const leave12Range = {
+ leaveDateStart: ranges[0].start,
+ leaveDateEnd: ranges[ranges.length - 1].end
}
+
+ const [totalRes, onJobRes, leaveRes, leave12Res] = await Promise.all([
+ findStaffAnalysisTotalStatistic(),
+ staffOnJobListPage({ current: 1, size: 1, staffState: 1 }),
+ findStaffLeaveListPage({ current: 1, size: 1 }),
+ findStaffLeaveListPage({ current: 1, size: 1, ...leave12Range })
+ ])
+
+ const totalFlowRate = safeNum(totalRes?.data?.totalFlowRate)
+ const totalTurnoverRate = safeNum(totalRes?.data?.totalTurnoverRate)
+ const joinLeaveRatio =
+ totalTurnoverRate > 0 && totalFlowRate > 0 ? totalFlowRate / totalTurnoverRate : 1
+
+ const onJobTotal = getListTotal(onJobRes)
+ const leaveTotal = getListTotal(leaveRes)
+ const totalStaff = onJobTotal + leaveTotal
+ const leaveLast12Months = getListTotal(leave12Res)
+
+ staffCounts.value = {
+ onJobTotal,
+ leaveTotal,
+ totalStaff,
+ leaveLast12Months,
+ joinLeaveRatio: safeNum(joinLeaveRatio)
+ }
+
+ const turnoverRateNew =
+ totalStaff > 0 ? (leaveLast12Months / totalStaff) * 100 : 0
+ const flowRateNew =
+ totalStaff > 0 ? (leaveLast12Months * staffCounts.value.joinLeaveRatio / totalStaff) * 100 : 0
+
+ keyMetrics.value[0].value = round2(flowRateNew)
+ keyMetrics.value[1].value = round2(turnoverRateNew)
+ keyMetrics.value[2].value = onJobTotal || safeNum(totalRes?.data?.currentOnJobCount)
} catch (error) {
console.error('鑾峰彇鍛樺伐鍒嗘瀽鎬荤粺璁℃暟鎹け璐�:', error)
+ }
+}
+
+const getLeaveCountsByMonthForLast12Months = async () => {
+ const ranges = getLast12MonthRanges()
+ const tasks = ranges.map((r) =>
+ findStaffLeaveListPage({
+ current: 1,
+ size: 1,
+ leaveDateStart: r.start,
+ leaveDateEnd: r.end
+ })
+ )
+
+ const results = await Promise.allSettled(tasks)
+ const counts = {}
+ results.forEach((res, idx) => {
+ const month = ranges[idx].month
+ counts[month] = res.status === 'fulfilled' ? getListTotal(res.value) : 0
+ })
+ const sum = Object.values(counts).reduce((acc, cur) => acc + safeNum(cur), 0)
+ return { counts, sum }
+}
+
+const applyNormalizedTurnoverStatistics = async () => {
+ const raw = Array.isArray(turnoverRateStatisticsRaw.value) ? turnoverRateStatisticsRaw.value : []
+ if (raw.length === 0) {
+ turnoverRateStatistics.value = []
+ turnoverSeriesNormalized.value = false
+ return
+ }
+
+ const totalStaff = safeNum(staffCounts.value.totalStaff)
+ const leaveLast12Months = safeNum(staffCounts.value.leaveLast12Months)
+ const ratio = safeNum(staffCounts.value.joinLeaveRatio) || 1
+
+ if (totalStaff <= 0 || leaveLast12Months < 0) {
+ turnoverRateStatistics.value = raw
+ turnoverSeriesNormalized.value = false
+ return
+ }
+
+ try {
+ const { counts, sum } = await getLeaveCountsByMonthForLast12Months()
+ if (sum > leaveLast12Months) {
+ turnoverRateStatistics.value = raw
+ turnoverSeriesNormalized.value = false
+ return
+ }
+
+ turnoverRateStatistics.value = raw.map((item) => {
+ const monthKey = String(item?.month ?? '')
+ const leaveCount = safeNum(counts[monthKey])
+ const turnoverRate = totalStaff > 0 ? (leaveCount / totalStaff) * 100 : 0
+ const flowRate = totalStaff > 0 ? (leaveCount * ratio / totalStaff) * 100 : 0
+ return {
+ ...item,
+ flowRate: round2(flowRate),
+ turnoverRate: round2(turnoverRate)
+ }
+ })
+ turnoverSeriesNormalized.value = true
+ } catch (e) {
+ turnoverRateStatistics.value = raw
+ turnoverSeriesNormalized.value = false
}
}
@@ -238,6 +376,8 @@
getMonthlyTurnoverRateFor12Months(),
getStaffAnalysisTotalStatistic()
])
+
+ await applyNormalizedTurnoverStatistics()
await nextTick()
renderAllCharts()
@@ -317,10 +457,17 @@
data: months,
boundaryGap: false
},
- yAxis: {
- type: 'value',
- axisLabel: { formatter: '{value}%' }
- },
+ yAxis: turnoverSeriesNormalized.value
+ ? {
+ type: 'value',
+ axisLabel: { formatter: '{value}%' },
+ min: 0,
+ max: 100
+ }
+ : {
+ type: 'value',
+ axisLabel: { formatter: '{value}%' }
+ },
series: [
{
name: '娴佸姩鐜�',
diff --git a/src/views/personnelManagement/employeeRecord/index.vue b/src/views/personnelManagement/employeeRecord/index.vue
index db0d4d6..7c269bb 100644
--- a/src/views/personnelManagement/employeeRecord/index.vue
+++ b/src/views/personnelManagement/employeeRecord/index.vue
@@ -11,7 +11,7 @@
clearable
:prefix-icon="Search"
/>
- <span class="search_title">閮ㄩ棬锛�</span>
+ <span class="search_title search_title2">閮ㄩ棬锛�</span>
<el-tree-select
v-model="searchForm.sysDeptId"
:data="deptOptions"
@@ -20,9 +20,16 @@
style="width: 240px"
placeholder="璇烽�夋嫨"
/>
- <span style="margin-left: 10px" class="search_title">鍚堝悓缁撴潫鏃ユ湡锛�</span>
- <el-date-picker v-model="searchForm.entryDate" value-format="YYYY-MM-DD" format="YYYY-MM-DD" type="daterange"
- placeholder="璇烽�夋嫨" clearable @change="changeDaterange" />
+ <span class="search_title search_title2">鍏ヨ亴鏃ユ湡锛�</span>
+ <el-date-picker
+ v-model="searchForm.entryDateStart"
+ value-format="YYYY-MM-DD"
+ format="YYYY-MM-DD"
+ placeholder="璇烽�夋嫨"
+ />
+ <!-- <span style="margin-left: 10px" class="search_title">鍚堝悓缁撴潫鏃ユ湡锛�</span> -->
+ <!-- <el-date-picker v-model="searchForm.entryDate" value-format="YYYY-MM-DD" format="YYYY-MM-DD" type="daterange"
+ placeholder="璇烽�夋嫨" clearable @change="changeDaterange" /> -->
<el-button type="primary" @click="handleQuery" style="margin-left: 10px"
>鎼滅储</el-button
>
@@ -129,6 +136,11 @@
{
label: "鍑虹敓鏃ユ湡",
prop: "birthDate",
+ width: 120,
+ },
+ {
+ label: "鍏ヨ亴鏃ユ湡",
+ prop: "entryDate",
width: 120,
},
{
@@ -307,4 +319,8 @@
});
</script>
-<style scoped></style>
+<style scoped>
+.search_title2 {
+ margin-left: 10px;
+}
+</style>
--
Gitblit v1.9.3