对接统计

This commit is contained in:
wangchengming 2025-07-09 22:50:55 +08:00
parent c705229583
commit 5d41259aa4
5 changed files with 440 additions and 80 deletions

View File

@ -77,4 +77,13 @@ export function getBusUserrLabelRecord(query) {
method: 'post',
data: query
})
}
// 高压资质统计
export function getBusUserrLabelStatistics(query) {
return request({
url: '/admin/busUserLabel/statistics',
method: 'post',
data: query
})
}

View File

@ -132,3 +132,13 @@ export function reApplyBusUserQualification(query) {
data: query
})
}
// 培训需求/审批统计
export function getQualificationStatistics(query) {
return request({
url: '/admin/busUserQualification/statistics',
method: 'post',
data: query
})
}

View File

@ -71,4 +71,13 @@ export function exportVehicleTemplate(data) {
responseType: 'blob',
data
})
}
// 车型培训统计
export function vehicleModelTrainStatistics(query) {
return request({
url: '/admin/vehicleModelTraining/statistics',
method: 'post',
data: query
})
}

View File

@ -0,0 +1,85 @@
<template>
<div class="app-page-container">
<breadcrumb id="breadcrumb-container" class="breadcrumb-container" />
<div class="lib-container clearBoth">
<div class="optionBtn">
<el-button type="primary" class="qualificationApplyBtn" @click="handleOpenAddForm">
<img :src="addIcon" class="custom-icon" />
新增
</el-button>
</div>
</div>
</div>
</template>
<script setup>
import { ref } from 'vue'
import { useRouter } from 'vue-router'
import Breadcrumb from '@/components/Breadcrumb'
import addIcon from '@/assets/images/addIcon.png'
const router = useRouter()
const queryParams = ref({
pageNum: 1,
pageSize: 10,
userName: undefined,
deptId: undefined,
qualification: undefined
})
</script>
<style lang='scss' scoped>
.app-main {
min-height: 100vh !important;
width: 100%;
position: relative;
overflow: hidden;
background-color: #F8F8F8;
}
.app-page-container {
width: 100%;
height: 100%;
overflow-y: auto;
padding-bottom: 0px;
}
.lib-container {
height: calc(100vh - 140px);
width: 1200px;
margin: 0 auto;
background: #F8F8F8;
padding: 0;
}
.clearBoth {
clear: both;
}
.optionBtn {
padding-bottom: 12px;
}
.qualificationApplyBtn {
// width: 84px !important;
height: 32px;
border-radius: 4px 4px 4px 4px;
background: #4276d1;
font-family: Microsoft YaHei;
font-weight: 400;
font-size: 18px;
text-align: center;
color: #FFFFFF;
}
.qualificationApplyBtn:hover {
background: #4276d1;
}
.custom-icon {
margin-right: 10px;
}
</style>

View File

@ -7,24 +7,15 @@
<el-row class="myRow">
<el-col :span="20">
<el-form-item label="部门" prop="deptId">
<el-select v-model="queryParams.deptId" placeholder="请选择部门" clearable
style="width: 160px">
<el-option label="Zone one" value="shanghai" />
<el-option label="Zone two" value="beijing" />
</el-select>
<el-tree-select v-model="queryParams.deptId" :data="enabledDeptOptions"
:props="{ value: 'id', label: 'label', children: 'children' }" value-key="id"
placeholder="请选择部门" check-strictly style="width: 230px" />
</el-form-item>
<el-form-item label="属地" prop="deptId">
<el-select v-model="queryParams.deptId" placeholder="请选择属地" clearable
style="width: 160px">
<el-option label="Zone one" value="shanghai" />
<el-option label="Zone two" value="beijing" />
</el-select>
</el-form-item>
<el-form-item label="资质" prop="deptId">
<el-select v-model="queryParams.deptId" placeholder="请选择资质" clearable
style="width: 160px">
<el-option label="Zone one" value="shanghai" />
<el-option label="Zone two" value="beijing" />
<el-select v-model="queryParams.dependencyId" placeholder="请选择属地" clearable
style="width: 200px">
<el-option v-for="item in busDependencyData" :key="item.id"
:label="item.dependencyName" :value="item.id" />
</el-select>
</el-form-item>
<el-form-item label="年度" prop="userName">
@ -44,20 +35,36 @@
<el-card>
<template #header>
<div class="card-header">
<span>当前角色饼图</span>
<span>高压资质饼图</span>
</div>
</template>
<div id="qualificationPer" ref="qualificationPer"></div>
<el-empty v-if="dataCake.length == 0" description="无数据" />
<div v-else id="qualificationPer" ref="qualificationPer"></div>
</el-card>
</el-col>
<el-col :span="12" class="rightChart">
<el-card>
<template #header>
<div class="card-header">
<span>培训审批</span>
</div>
</template>
<el-empty v-if="trainApprovalXData.length == 0" description="无数据" />
<div v-else id="qualificationApprovalBar" ref="qualificationApprovalBar"></div>
</el-card>
</el-col>
</el-row>
<el-row class="chartsRow">
<el-col :span="24">
<el-card>
<template #header>
<div class="card-header">
<span>培训需求</span>
</div>
</template>
<div id="qualificationBar" ref="qualificationBar"></div>
<el-empty v-if="trainxqXData.length == 0" description="无数据" />
<div v-else id="qualificationBar" ref="qualificationBar"></div>
</el-card>
</el-col>
</el-row>
@ -69,7 +76,8 @@
<span>车型培训情况</span>
</div>
</template>
<div id="vehicleTrainBar" ref="vehicleTrainBar"></div>
<el-empty v-if="vehicleModelTrainXData.length == 0" description="无数据" />
<div v-else id="vehicleTrainBar" ref="vehicleTrainBar"></div>
</el-card>
</el-col>
</el-row>
@ -77,44 +85,203 @@
</div>
</template>
<script setup>
import { onMounted, ref } from 'vue'
import { onMounted, ref, nextTick } from 'vue'
import { useRouter } from 'vue-router'
import { getBusUserrLabelStatistics } from "@/api/labelManage/labelManage"
import { getQualificationStatistics } from "@/api/qualification/myQualifications"
import { vehicleModelTrainStatistics } from "@/api/qualification/vehicleTrain"
import { getBusDependencyPage } from "@/api/system/dependency"
import { deptTreeSelect } from "@/api/system/user"
import Breadcrumb from '@/components/Breadcrumb'
import * as echarts from 'echarts'
const router = useRouter()
const qualificationPer = ref(null)
const qualificationBar = ref(null)
const vehicleTrainBar = ref(null)
const qualificationApprovalBar = ref(null)
const queryParams = ref({
userName: undefined,
dependencyId: undefined,
deptId: undefined,
qualification: undefined
year: undefined,
checkStatus: undefined
})
//
const dataCake = ref([])
//
const getLabelStatistics = () => {
const _queryParams = {
dependencyId: queryParams.value.dependencyId,
deptId: queryParams.value.deptId,
year: queryParams.value.year,
checkStatus: 2
}
getBusUserrLabelStatistics(_queryParams).then(res => {
if (res.code == 200) {
dataCake.value = res.data.map(item => ({
value: item.count,
name: item.labelName,
percentage: item.rate
}))
if (dataCake.value.length > 0) {
nextTick(() => {
initQualificationPer()
});
}
}
})
}
//
const trainApprovalXData = ref([])
const trainApprovalYData = ref([])
//
const getUserQualificationStatistics = () => {
const _queryParams = {
dependencyId: queryParams.value.dependencyId,
deptId: queryParams.value.deptId,
year: queryParams.value.year,
checkStatus: 2
}
trainApprovalXData.value = []
trainApprovalYData.value = []
getQualificationStatistics(_queryParams).then(res => {
if (res.code == 200) {
res.data.forEach(item => {
trainApprovalXData.value.push(item.qualificationName)
trainApprovalYData.value.push({ value: item.count, name: item.qualificationName })
});
if (trainApprovalXData.value.length > 0) {
nextTick(() => {
initQualificationApprovalBar()
});
}
}
})
}
//
const trainxqXData = ref([])
const trainxqYData = ref([])
//
const getUserQualificationxqStatistics = () => {
const _queryParams = {
dependencyId: queryParams.value.dependencyId,
deptId: queryParams.value.deptId,
year: queryParams.value.year
}
trainxqXData.value = []
trainxqYData.value = []
getQualificationStatistics(_queryParams).then(res => {
if (res.code == 200) {
res.data.forEach(item => {
trainxqXData.value.push(item.qualificationName)
trainxqYData.value.push({ value: item.count, name: item.qualificationName })
});
if (trainxqXData.value.length > 0) {
nextTick(() => {
initQualificationBar()
});
}
}
})
}
//
const vehicleModelTrainXData = ref([])
const vehicleModelTrainYData = ref([])
//
const getVehicleModelTrainStatistics = () => {
const _queryParams = {
dependencyId: queryParams.value.dependencyId,
deptId: queryParams.value.deptId,
year: queryParams.value.year
}
vehicleModelTrainXData.value = []
vehicleModelTrainYData.value = []
vehicleModelTrainStatistics(_queryParams).then(res => {
if (res.code == 200) {
res.data.forEach(item => {
vehicleModelTrainXData.value.push(item.vehicleModel)
vehicleModelTrainYData.value.push(item.count)
});
if (vehicleModelTrainXData.value.length > 0) {
nextTick(() => {
initVehicleTrainBar()
});
}
}
})
}
//
const busDependencyData = ref([])
//
const getBusDependencyData = () => {
getBusDependencyPage({
pageIndex: 1,
pageSize: 100,
}).then(response => {
if (response.code == 200) {
busDependencyData.value = response.data.list
}
})
}
//
const enabledDeptOptions = ref(undefined)
//
const getDeptTree = () => {
deptTreeSelect().then(res => {
enabledDeptOptions.value = filterDisabledDept(JSON.parse(JSON.stringify(res.data)))
})
}
/** 过滤禁用的部门 */
const filterDisabledDept = (deptList) => {
return deptList.filter(dept => {
if (dept.disabled) {
return false
}
if (dept.children && dept.children.length) {
dept.children = filterDisabledDept(dept.children)
}
return true
})
}
/** 搜索按钮操作 */
const handleQuery = () => { }
const handleQuery = () => {
getLabelStatistics()
getUserQualificationStatistics()
getUserQualificationxqStatistics()
getVehicleModelTrainStatistics()
}
/** 重置按钮操作 */
const resetQuery = () => {
queryParams.value = {
dependencyId: undefined,
deptId: undefined,
year: undefined
}
handleQuery()
}
onMounted(() => {
initQualificationPer();
initQualificationBar();
initVehicleTrainBar();
getDeptTree()
getBusDependencyData()
nextTick(() => {
handleQuery()
});
});
const dataCake = [
{ value: 2562, name: 'SH', percentage: '15.86%' },
{ value: 6000, name: 'ES', percentage: '37.15%' },
{ value: 2589, name: 'ESC', percentage: '16.03%' },
{ value: 4000, name: 'CESC', percentage: '24.77%' },
{ value: 1000, name: 'LLP', percentage: '6.19%' }
]
const _labelPerChart = ref(null)
//
const initQualificationPer = () => {
const _myChart = echarts.init(qualificationPer.value)
_myChart.setOption({
if (_labelPerChart.value) _labelPerChart.value.dispose();
_labelPerChart.value = echarts.init(qualificationPer.value)
_labelPerChart.value.setOption({
tooltip: {
trigger: 'item',
formatter: "{b} : {c} 人 ({d}%)" //
formatter: "{b} : {c} 人 ({d}%)" //
},
legend: {
orient: 'vertical',
@ -128,10 +295,10 @@ const initQualificationPer = () => {
},
formatter: function (name) {
let target, percentage;
for (let i = 0; i < dataCake.length; i++) {
if (dataCake[i].name === name) {
target = dataCake[i].value
percentage = dataCake[i].percentage
for (let i = 0; i < dataCake.value.length; i++) {
if (dataCake.value[i].name === name) {
target = dataCake.value[i].value
percentage = dataCake.value[i].percentage
}
}
let arr = [name + ' ', " " + target + "人 ", " " + percentage]
@ -150,7 +317,7 @@ const initQualificationPer = () => {
borderRadius: 10
},
center: ['30%', '45%'], //
data: dataCake,
data: dataCake.value,
//
label: {
show: false,
@ -172,14 +339,16 @@ const initQualificationPer = () => {
}
]
})
window.addEventListener("resize", () => {
_myChart.resize()
})
// window.addEventListener("resize", () => {
// _labelPerChart.value.resize()
// })
}
const initQualificationBar = () => {
const _myChart = echarts.init(qualificationBar.value)
_myChart.setOption({
//
const _qualificationApprovalBarChart = ref(null)
const initQualificationApprovalBar = () => {
if (_qualificationApprovalBarChart.value) _qualificationApprovalBarChart.value.dispose();
_qualificationApprovalBarChart.value = echarts.init(qualificationApprovalBar.value)
_qualificationApprovalBarChart.value.setOption({
tooltip: {
trigger: 'axis',
axisPointer: { type: 'shadow' }
@ -193,7 +362,7 @@ const initQualificationBar = () => {
},
yAxis: { // yAxis
type: 'category',
data: ['SH', 'ES', 'ESC', 'CESC', 'LLP'],
data: trainApprovalXData.value,
axisLabel: {
fontSize: 16,
color: '#898989'
@ -228,13 +397,7 @@ const initQualificationBar = () => {
name: '数值',
type: 'bar',
barWidth: '16',
data: [
{ value: 97, name: 'SH' },
{ value: 212, name: 'ES' },
{ value: 427, name: 'ESC' },
{ value: 935, name: 'CESC' },
{ value: 486, name: 'LLP' }
],
data: trainApprovalYData.value,
itemStyle: {
color: function (params) {
const colorList = ['#5470C6', '#91CC75', '#FAC858', '#EE6666', '#73C0DE'];
@ -263,14 +426,105 @@ const initQualificationBar = () => {
}
}]
})
window.addEventListener("resize", () => {
_myChart.resize()
})
// window.addEventListener("resize", () => {
// _qualificationApprovalBarChart.value.resize()
// })
}
//
const _qualificationBarChart = ref(null)
const initQualificationBar = () => {
if (_qualificationBarChart.value) _qualificationBarChart.value.dispose();
_qualificationBarChart.value = echarts.init(qualificationBar.value)
_qualificationBarChart.value.setOption({
tooltip: {
trigger: 'axis',
axisPointer: { type: 'shadow' }
},
grid: {
left: '3%',
right: '7%', //
top: '0',
bottom: '0',
containLabel: true
},
yAxis: { // yAxis
type: 'category',
data: trainxqXData.value,
axisLabel: {
fontSize: 16,
color: '#898989'
},
axisLine: {
lineStyle: {
color: '#F1F1F1'
}
},
axisTick: {
show: false, // 线
alignWithLabel: true
}
},
xAxis: { // xAxis
type: 'value',
axisLabel: {
fontSize: 12,
color: '#999',
formatter: '{value}',
show: false
},
splitLine: {
lineStyle: {
type: 'dashed',
color: '#eee',
},
show: false
},
},
series: [{
name: '数值',
type: 'bar',
barWidth: '16',
data: trainxqYData.value,
itemStyle: {
color: function (params) {
const colorList = ['#5470C6', '#91CC75', '#FAC858', '#EE6666', '#73C0DE'];
return colorList[params.dataIndex];
},
borderRadius: [0, 4, 4, 0] //
},
label: {
show: true,
position: 'right', //
formatter: '{c}',
fontSize: 14,
fontWeight: 'bold',
color: '#333'
},
emphasis: {
itemStyle: {
shadowBlur: 10,
shadowColor: 'rgba(0, 0, 0, 0.3)'
},
label: {
show: true,
fontSize: 16,
color: '#000'
}
}
}]
})
// window.addEventListener("resize", () => {
// _qualificationBarChart.value.resize()
// })
}
//
const _vehicleTrainBarChart = ref(null)
const initVehicleTrainBar = () => {
const _myChart = echarts.init(vehicleTrainBar.value)
_myChart.setOption({
if (_vehicleTrainBarChart.value) _vehicleTrainBarChart.value.dispose();
_vehicleTrainBarChart.value = echarts.init(vehicleTrainBar.value)
_vehicleTrainBarChart.value.setOption({
tooltip: {
trigger: 'axis',
axisPointer: {
@ -286,18 +540,7 @@ const initVehicleTrainBar = () => {
},
xAxis: {
type: 'category',
data: [
'BR 205 Plugin Hybrid',
'BR 222 Plugin Hybrid',
'V213 222 Plugin Hybrid',
'V266 Plugin Hybrid',
'BR 205 Plugin Hybrid',
'V213 222 Plugin Hybrid',
'V266 Plugin Hybrid',
'V223 Plugin Hybrid',
'V223 Plugin Hybrid',
'V223 Plugin Hybrid'
],
data: vehicleModelTrainXData.value,
axisLabel: {
interval: 0, //
rotate: 45, // 45
@ -342,7 +585,7 @@ const initVehicleTrainBar = () => {
name: '数值',
type: 'bar',
barWidth: '16',
data: [205, 222, 222, 266, 205, 222, 266, 223, 223, 223],
data: vehicleModelTrainYData.value,
itemStyle: {
// color: '#767C81',
@ -363,9 +606,9 @@ const initVehicleTrainBar = () => {
}
}]
})
window.addEventListener("resize", () => {
_myChart.resize()
})
// window.addEventListener("resize", () => {
// _vehicleTrainBarChart.value.resize()
// })
}
</script>
@ -494,14 +737,18 @@ const initVehicleTrainBar = () => {
height: 293px;
}
#qualificationBar {
#qualificationApprovalBar {
width: 100%;
height: 293px;
}
#qualificationBar {
width: 100%;
height: 400px;
}
#vehicleTrainBar {
width: 100%;
height: 400px;
}
</style>