| 1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132 |
- <template>
- <view>
- <!-- 勤务组织标题 -->
- <view class="section-title">
- <text class="title-text">勤务组织</text>
- </view>
- <view class="duty-organization mobile-section">
- <!-- 出勤人次分析 -->
- <view class="attendance-analysis">
- <view class="analysis-header">
- <text class="header-text">出勤人次分析</text>
- </view>
- <!-- 统计卡片 -->
- <view class="stat-card">
- <view class="stat-content">
- <text class="stat-text">
- {{ attendanceData && attendanceData.length > 0 && attendanceData[attendanceData.length - 1] ?
- attendanceData[attendanceData.length - 1].deptName : '' }}出勤
- <text class="highlight">{{ attendanceData && attendanceData.length > 0 &&
- attendanceData[attendanceData.length - 1] ? attendanceData[attendanceData.length - 1].currentValue : 0
- }}</text>
- 人次,同比
- <text
- :class="getTrendClass(attendanceData && attendanceData.length > 0 && attendanceData[attendanceData.length - 1] ? attendanceData[attendanceData.length - 1].yearOnYearValue : 0)">
- {{ formatRate(attendanceData && attendanceData.length > 0 && attendanceData[attendanceData.length - 1] ?
- attendanceData[attendanceData.length - 1].yearOnYearValue : 0) }}
- </text>
- <template v-if="queryForm.dateRangeQueryType !== 'YEAR'">
- ,环比
- <text
- :class="getTrendClass(attendanceData && attendanceData.length > 0 && attendanceData[attendanceData.length - 1] ? attendanceData[attendanceData.length - 1].chainRatioValue : 0)">
- {{ formatRate(attendanceData && attendanceData.length > 0 && attendanceData[attendanceData.length - 1]
- ?
- attendanceData[attendanceData.length - 1].chainRatioValue : 0) }}
- </text>
- </template>
- </text>
- </view>
- </view>
- <!-- 主管数据卡片 -->
- <view class="dept-cards">
- <template v-if="attendanceData && attendanceData.length > 0">
- <view v-for="(item, index) in attendanceData.slice(0, 3)" :key="index" class="dept-card">
- <view class="dept-name">{{ item.deptName || loginUserName }}</view>
- <view class="dept-count">
- <text class="count-number">{{ item.currentValue || 0 }}</text>
- <text class="count-unit">人次</text>
- </view>
- <view class="dept-trend">
- <view class="trend-item">
- <text>同比</text>
- <text :class="getTrendClass(item.yearOnYearValue)">{{ formatRate(item.yearOnYearValue) }}</text>
- </view>
- <template v-if="queryForm.dateRangeQueryType !== 'YEAR'">
- <view class="trend-item">
- <text>环比</text>
- <text :class="getTrendClass(item.chainRatioValue)">{{ formatRate(item.chainRatioValue) }}</text>
- </view>
- </template>
- </view>
- </view>
- </template>
- <template v-else>
- <!-- 空数据时显示占位卡片 -->
- <view v-for="i in 3" :key="i" class="dept-card">
- <view class="dept-name">暂无数据</view>
- <view class="dept-count">
- <text class="count-number">0</text>
- <text class="count-unit">人次</text>
- </view>
- <view class="dept-trend">
- <view class="trend-item">
- <text>同比</text>
- <text class="trend-neutral">0%</text>
- </view>
- <template v-if="queryForm.dateRangeQueryType !== 'YEAR'">
- <view class="trend-item">
- <text>环比</text>
- <text class="trend-neutral">0%</text>
- </view>
- </template>
- </view>
- </view>
- </template>
- </view>
- <!-- 图表区域 -->
- <view class="chart-container">
- <view ref="attendanceBarChartRef" class="echarts-chart"></view>
- </view>
- </view>
- <!-- 资质等级分布 -->
- <view class="qualification-distribution" v-show="!isUserType">
- <view class="distribution-header">
- <text class="header-text">资质等级分布</text>
- </view>
- <!-- 资质等级分布内容区域 -->
- <view class="qualification-content">
- <!-- 上方:资质等级统计 -->
- <view class="qualification-section">
- <!-- 描述卡片 -->
- <view class="stat-card" v-if="qualificationPieDescriptionPart1">
- <view class="stat-content">
- {{ qualificationPieDescriptionPart1 || '资质等级分布数据加载中...' }}
- </view>
- </view>
- <!-- 饼图 -->
- <view class="chart-container">
- <view ref="pieChartRef" class="echarts-chart"></view>
- </view>
- </view>
- <!-- 中间:资质分布趋势 -->
- <view class="qualification-section" v-show="isStationType">
- <!-- 描述卡片 -->
- <view class="stat-card" v-if="qualificationPieDescriptionPart2">
- <view class="stat-content">
- {{ qualificationPieDescriptionPart2 || '资质分布趋势数据加载中...' }}
- </view>
- </view>
- <!-- 柱状图 -->
- <view class="chart-container">
- <view ref="barChartRef" class="echarts-chart"></view>
- </view>
- </view>
- <!-- 下方:班组人员资质等级表格 -->
- <view class="qualification-section" v-show="isTeamsType">
- <statistic-table :columns="teamsTableColumns" :data="teamsQualificationData" blankData="未知" />
- </view>
- </view>
- </view>
- <!-- 资质能力 -->
- <view class="qualification-ability" v-show="isUserType">
- <view class="ability-header">
- <text class="header-text">资质能力</text>
- </view>
- <!-- 统计卡片 -->
- <view class="stat-card">
- <view class="stat-content">资质等级:{{ user && user.qualificationLevel ? user.qualificationLevel : '未知' }}</view>
- <view class="stat-content">可上岗岗位:{{user && user.sysPostList ? user.sysPostList.map(item =>
- item.postName).join('、')
- : '未知'}}</view>
- </view>
- </view>
- </view>
- </view>
- </template>
- <script>
- import * as echarts from 'echarts'
- import { getCalculate, getCalculateTrendData, getQualificationPieChart, getQualificationBarChart } from '@/api/qualityControlAnalysisReport/qualityControlAnalysisReport'
- import StatisticTable from '@/components/statistic-table/statistic-table.vue'
- export default {
- name: 'DutyOrganization',
- components: {
- StatisticTable
- },
- props: {
- queryForm: {
- type: Object,
- default: () => ({})
- }
- },
- data() {
- return {
- attendanceData: [],
- trendData: [],
- qualificationPieData: [],
- qualificationBarData: [],
- teamsQualificationData: [],
- teamsTableColumns: [
- { props: 'deptName', title: '姓名' },
- { props: 'qualificationLevel', title: '等级' }
- ],
- qualificationPieDescriptionPart1: '',
- qualificationPieDescriptionPart2: '',
- user: {},
- loading: false,
- pieChart: null,
- barChart: null,
- attendanceBarChart: null
- }
- },
- computed: {
- loginUserName() {
- return this.$store.state?.user?.userInfo?.nickName
- },
- // 计算属性:检查是否为USER类型
- isUserType() {
- const roles = this.$store.state?.user?.roles || []
- // 如果是班组长且选择了个人视图,则视为用户类型
- if (roles.includes('banzuzhang') && this.queryForm.scopedType === 'USER') {
- return true
- }
- return roles.includes('SecurityCheck')
- },
- // 计算属性:检查是否为STATION类型
- isStationType() {
- const roles = this.$store.state?.user?.roles || []
- return roles.includes('test') || roles.includes('zhijianke')
- },
- // 计算属性:检查是否为TEAMS类型
- isTeamsType() {
- const roles = this.$store.state?.user?.roles || []
- return roles.includes('banzuzhang') && this.queryForm.scopedType === 'TEAMS'
- }
- },
- mounted() {
- this.loadData()
- this.$nextTick(() => {
- this.initCharts()
- })
- },
- beforeDestroy() {
- this.disposeCharts()
- },
- watch: {
- queryForm: {
- handler() {
- this.loadData()
- },
- deep: true
- },
- },
- methods: {
- // 初始化图表
- initCharts() {
- // 资质等级饼图
- if (this.$refs.pieChartRef) {
- this.initPieChart()
- }
- // 资质等级柱状图
- if (this.$refs.barChartRef) {
- this.initBarChart()
- }
- // 出勤人次柱状图
- if (this.$refs.attendanceBarChartRef) {
- this.initAttendanceBarChart()
- }
- },
- // 初始化饼图
- initPieChart() {
- const pieOptions = {
- tooltip: {
- trigger: 'item',
- formatter: '{a} <br/>{b}: {c}人 ({d}%)'
- },
- legend: {
- orient: 'vertical',
- left: 'left',
- top: 'center',
- textStyle: {
- color: '#333',
- fontSize: 12
- },
- show: false
- },
- color: ['#FF6B6B', '#4ECDC4', '#45B7D1', '#96CEB4', '#FECA57'],
- series: [
- {
- name: '资质等级分布',
- type: 'pie',
- radius: '50%',
- center: ['50%', '50%'],
- avoidLabelOverlap: false,
- itemStyle: {
- borderRadius: 10,
- borderColor: '#fff',
- borderWidth: 2
- },
- label: {
- show: true,
- formatter: '{b}: {c}人 ({d}%)',
- fontSize: 12
- },
- emphasis: {
- label: {
- show: true,
- fontSize: 14,
- fontWeight: 'bold'
- },
- itemStyle: {
- shadowBlur: 10,
- shadowOffsetX: 0,
- shadowColor: 'rgba(0, 0, 0, 0.5)'
- }
- },
- labelLine: {
- show: true,
- length: 10,
- length2: 20
- },
- data: []
- }
- ]
- }
- // 在uni-app中使用uni.createCanvasContext或第三方图表库
- this.pieChart = this.createEChartsInstance(this.$refs.pieChartRef, pieOptions)
- },
- // 初始化出勤人次柱状图
- initAttendanceBarChart() {
- const attendanceBarOptions = {
- tooltip: {
- trigger: 'axis',
- axisPointer: {
- type: 'shadow'
- },
- formatter: function (params) {
- let result = `${params[0].axisValue}<br/>`
- params.forEach(param => {
- result += `${param.seriesName}: <span style="color:${param.color};font-weight:bold">${param.data}</span>人<br/>`
- })
- return result
- }
- },
- legend: {
- data: ['安检一大队', '安检二大队', '安检三大队', '全站'],
- top: 0,
- show: true,
- },
- grid: {
- left: '3%',
- right: '4%',
- bottom: '0%',
- top: '80',
- containLabel: true
- },
- xAxis: {
- type: 'category',
- data: [],
- axisLine: {
- lineStyle: {
- color: '#999'
- }
- },
- axisLabel: {
- fontSize: 12,
- interval: 0
- }
- },
- yAxis: {
- type: 'value',
- name: '人数',
- nameTextStyle: {
- fontSize: 12,
- color: '#666'
- },
- axisLine: {
- lineStyle: {
- color: '#999'
- }
- },
- axisLabel: {
- fontSize: 11,
- margin: 8,
- color: '#666'
- },
- splitLine: {
- lineStyle: {
- color: '#f5f5f5',
- type: 'dashed'
- }
- },
- minInterval: 1,
- splitNumber: 6
- },
- series: [
- {
- name: '全站',
- type: 'bar',
- barWidth: '10%',
- itemStyle: {
- color: '#5470C6'
- },
- data: []
- },
- {
- name: '安检一大队',
- type: 'bar',
- barWidth: '10%',
- itemStyle: {
- color: '#FF6B6B'
- },
- data: []
- },
- {
- name: '安检二大队',
- type: 'bar',
- barWidth: '10%',
- itemStyle: {
- color: '#4ECDC4'
- },
- data: []
- },
- {
- name: '安检三大队',
- type: 'bar',
- barWidth: '10%',
- itemStyle: {
- color: '#FFD166'
- },
- data: []
- }
- ]
- }
- this.attendanceBarChart = this.createEChartsInstance(this.$refs.attendanceBarChartRef, attendanceBarOptions)
- },
- // 初始化资质等级柱状图
- initBarChart() {
- const barOptions = {
- tooltip: {
- trigger: 'axis',
- axisPointer: {
- type: 'shadow'
- },
- formatter: function (params) {
- let result = `${params[0].axisValue}<br/>`
- params.forEach(param => {
- result += `${param.seriesName}: <span style="color:${param.color};font-weight:bold">${param.data}</span>人<br/>`
- })
- return result
- }
- },
- legend: {
- data: ['安检一大队', '安检二大队', '安检三大队'],
- top: 0
- },
- grid: {
- left: '3%',
- right: '4%',
- bottom: '3%',
- top: '40%',
- containLabel: true
- },
- xAxis: {
- type: 'category',
- data: [],
- axisLine: {
- lineStyle: {
- color: '#999'
- }
- },
- axisLabel: {
- fontSize: 12
- }
- },
- yAxis: {
- type: 'value',
- name: '人数',
- axisLine: {
- lineStyle: {
- color: '#999'
- }
- },
- splitLine: {
- lineStyle: {
- color: '#f0f0f0'
- }
- }
- },
- series: [
- ]
- }
- this.barChart = this.createEChartsInstance(this.$refs.barChartRef, barOptions)
- },
- // 创建ECharts实例(适配uni-app环境)
- createEChartsInstance(chartRef, options) {
- if (!chartRef) return null
- // 获取DOM元素
- const chartDom = chartRef.$el || chartRef
- if (!chartDom) return null
- // 创建ECharts实例
- const chartInstance = echarts.init(chartDom)
- // 设置初始选项
- if (options) {
- chartInstance.setOption(options)
- }
- return chartInstance
- },
- // 销毁图表
- disposeCharts() {
- if (this.pieChart) {
- this.pieChart.dispose()
- this.pieChart = null
- }
- if (this.attendanceBarChart) {
- this.attendanceBarChart.dispose()
- this.attendanceBarChart = null
- }
- },
- processQueryParams(queryParams) {
- const processedParams = { ...queryParams }
- if (processedParams.dateRangeQueryType === 'YEAR') {
- processedParams.yearOnYear = true
- } else {
- processedParams.chainRatio = true
- processedParams.yearOnYear = true
- }
- processedParams.deptId = ['TEAMS', 'DEPARTMENT', 'BRIGADE', 'MANAGER'].includes(processedParams.scopedType) ? processedParams.scopedId : '';
- if (processedParams.scopedType == 'TEAMS') {
- delete processedParams.userId;
- }
- processedParams.userId = ['USER'].includes(processedParams.scopedType) ? processedParams.scopedId : '';
- if (processedParams.scopedType == 'USER') {
- delete processedParams.deptId;
- }
- return {
- ...processedParams,
- }
- },
- // 加载数据
- async loadData() {
- if (this.loading) return
- this.loading = true
- const processedParams = this.processQueryParams(this.queryForm)
- try {
- // 加载资质等级柱状图数据(如果是STATION类型)
- if (this.isStationType) {
- const barResponse = await getQualificationBarChart(processedParams)
- if (barResponse.code === 200) {
- this.qualificationBarData = barResponse.data?.brigades || []
- }
- }
- // 加载班组人员资质等级数据(如果是TEAMS类型)
- if (this.isTeamsType) {
- const barResponse = await getQualificationBarChart(processedParams)
- if (barResponse.code === 200) {
- this.teamsQualificationData = barResponse.data?.brigades || []
- }
- }
- if (this.isUserType) {
- // 获取资质等级分布柱状图数据
- const barResponse = await getQualificationBarChart(processedParams)
- console.log('资质等级分布柱状图数据:', barResponse.data.brigades)
- this.user = barResponse?.data?.brigades[0]
- }
- // 加载出勤人次分析数据
- const attendanceResponse = await getCalculate(processedParams)
- this.attendanceData = attendanceResponse || []
- // 加载出勤人次趋势数据
- const trendResponse = await getCalculateTrendData(processedParams)
- this.trendData = trendResponse || []
- // 加载资质等级分布数据
- if (!this.isUserType) {
- const qualificationResponse = await getQualificationPieChart(processedParams)
- if (qualificationResponse.code === 200) {
- this.qualificationPieData = qualificationResponse.data?.data || []
- this.qualificationPieDescriptionPart1 = this.formatQualificationDescriptionPart1(this.qualificationPieData)
- this.qualificationPieDescriptionPart2 = this.formatQualificationDescriptionPart2(this.qualificationPieData)
- }
- } else {
- this.qualificationPieData = []
- this.qualificationPieDescriptionPart1 = ''
- this.qualificationPieDescriptionPart2 = ''
- }
- // 检查是否为用户类型
- this.checkUserType()
- setTimeout(() => {
- // 更新图表数据
- this.updateChartsWithData()
- }, 0);
- } catch (error) {
- console.error('加载勤务组织数据失败:', error)
- // 模拟数据作为备选
- } finally {
- this.loading = false
- }
- },
- // 其他部门类型的出勤人次柱状图更新函数
- updateAttendanceBarChartForOtherTypes() {
- if (this.trendData && Array.isArray(this.trendData) && this.attendanceBarChart) {
- const trendList = this.trendData
- // 提取横坐标数据(timeLabel字段)
- const xAxisData = trendList.map(item => item.timeLabel || '未知时间')
- // 提取总体数据
- const allData = trendList.map(item => item.overall || 0)
- // 更新图表配置
- const newOptions = {
- legend: {
- show: false
- },
- grid: {
- left: '3%',
- right: '4%',
- bottom: '0%',
- top: '20%',
- containLabel: true
- },
- xAxis: {
- data: xAxisData
- },
- series: [
- {
- name: '总体',
- type: 'bar',
- barWidth: '10%',
- itemStyle: {
- color: '#5470C6'
- },
- data: allData
- }
- ]
- }
- this.attendanceBarChart.setOption(newOptions)
- } else {
- // 无数据时清空图表
- const newOptions = {
- legend: {
- show: false
- },
- xAxis: {
- data: []
- },
- series: []
- }
- this.attendanceBarChart.setOption(newOptions)
- }
- },
- // 更新图表数据
- updateChartsWithData() {
- // 更新出勤人次柱状图 - 根据部门类型选择不同的更新函数
- if (this.isStationType) {
- // 站级别:使用原来的逻辑,显示多个主管数据
- this.updateAttendanceBarChart()
- } else {
- // 其他部门类型:使用新的函数,只显示总体数据
- this.updateAttendanceBarChartForOtherTypes()
- }
- // 更新资质等级分布饼图
- this.updateQualificationPieChart()
- // 更新资质等级柱状图(如果是STATION类型)
- if (this.isStationType) {
- this.updateQualificationBarChart()
- }
- },
- // 更新出勤人次柱状图
- updateAttendanceBarChart() {
- if (this.trendData && Array.isArray(this.trendData) && this.attendanceBarChart) {
- const trendList = this.trendData
- // 提取横坐标数据(timeLabel字段)
- const xAxisData = trendList.map(item => item.timeLabel || '未知时间')
- // 提取各主管数据
- const allData = trendList.map(item => item.overall || 0)
- const dept1Data = trendList.map(item => item.data1 || 0)
- const dept2Data = trendList.map(item => item.data2 || 0)
- const dept3Data = trendList.map(item => item.data3 || 0)
- // 更新图表配置
- const newOptions = {
- legend: { show: !!this.isStationType },
- xAxis: {
- data: xAxisData
- },
- series: [
- {
- name: '全站',
- type: 'bar',
- barWidth: '10%',
- itemStyle: {
- color: '#5470C6'
- },
- data: allData
- },
- {
- name: '安检一大队',
- type: 'bar',
- barWidth: '10%',
- itemStyle: {
- color: '#FF6B6B'
- },
- data: dept1Data
- },
- {
- name: '安检二大队',
- type: 'bar',
- barWidth: '10%',
- itemStyle: {
- color: '#4ECDC4'
- },
- data: dept2Data
- },
- {
- name: '安检三大队',
- type: 'bar',
- barWidth: '10%',
- itemStyle: {
- color: '#FFD166'
- },
- data: dept3Data
- }
- ]
- }
- this.attendanceBarChart.setOption(newOptions)
- }
- },
- // 更新资质等级分布饼图
- updateQualificationPieChart() {
- if (this.qualificationPieData && Array.isArray(this.qualificationPieData) && this.pieChart) {
- // 转换数据格式
- const formattedData = this.qualificationPieData.map(item => ({
- name: item.levelName || '未知等级',
- value: item.count || 0
- })).filter(item => item.value > 0)
- // 更新图表配置
- const newOptions = {
- series: [
- {
- data: formattedData
- }
- ]
- }
- this.pieChart.setOption(newOptions)
- }
- },
- // 更新资质等级柱状图
- updateQualificationBarChart() {
- if (this.qualificationBarData && Array.isArray(this.qualificationBarData) && this.barChart) {
- const barData = this.qualificationBarData
- // 提取所有唯一的等级名称(横坐标)
- const allLevelNames = []
- barData.forEach(dept => {
- if (dept.levelCounts && Array.isArray(dept.levelCounts)) {
- dept.levelCounts.forEach(level => {
- if (level.levelName && !allLevelNames.includes(level.levelName)) {
- allLevelNames.push(level.levelName)
- }
- })
- }
- })
- // 按等级顺序排序(一级、二级、三级、四级、五级)
- const levelOrder = ['一级', '二级', '三级', '四级', '五级']
- const sortedLevelNames = allLevelNames.sort((a, b) => {
- return levelOrder.indexOf(a) - levelOrder.indexOf(b)
- })
- // 为每个主管创建数据系列
- const seriesData = []
- const colors = ['#FF6B6B', '#4ECDC4', '#FFD166', '#9B59B6', '#3498DB']
- barData.forEach((dept, index) => {
- if (dept.deptName && dept.levelCounts) {
- // 为每个等级查找对应的数量
- const levelCounts = sortedLevelNames.map(levelName => {
- const levelData = dept.levelCounts.find(level => level.levelName === levelName)
- return levelData ? levelData.count || 0 : 0
- })
- seriesData.push({
- name: dept.deptName,
- type: 'bar',
- barWidth: '15%',
- itemStyle: {
- color: colors[index % colors.length]
- },
- label: {
- show: false,
- position: 'top',
- formatter: '{c}人'
- },
- data: levelCounts
- })
- }
- })
- // 更新图表配置
- const newOptions = {
- xAxis: {
- data: sortedLevelNames
- },
- legend: {
- data: barData.map(dept => dept.deptName).filter(name => name)
- },
- series: seriesData
- }
- this.barChart.setOption(newOptions)
- } else {
- // 无数据时清空图表
- const newOptions = {
- xAxis: {
- data: []
- },
- legend: {
- data: []
- },
- series: []
- }
- this.barChart.setOption(newOptions)
- }
- },
- // 格式化资质等级描述第一部分
- formatQualificationDescriptionPart1(data) {
- if (!Array.isArray(data)) {
- return '当前部门资质等级分布均衡,高级资质占比35%,中级资质占比45%,初级资质占比20%'
- }
- // 找出占比最高的等级
- const highestLevel = data.reduce((max, item) => {
- return (item.count || 0) > (max.count || 0) ? item : max
- }, { levelName: '高级', count: 0 })
- // 计算总人数
- const totalCount = data.reduce((sum, item) => sum + (item.count || 0), 0)
- // 计算占比
- const highestPercentage = totalCount > 0 ? ((highestLevel.count / totalCount) * 100).toFixed(2) : '0.0'
- const deptName = this.attendanceData && this.attendanceData.length > 0 && this.attendanceData[this.attendanceData.length - 1] ?
- this.attendanceData[this.attendanceData.length - 1].deptName : ''
- return `${deptName}资质等级以"${highestLevel.levelName || '高级'}"为主(占比为${highestPercentage}%)`
- },
- // 格式化资质等级描述第二部分
- formatQualificationDescriptionPart2(data) {
- if (this.qualificationBarData.length == 0) {
- return ''
- }
- // 从资质柱状图数据中找出"一级"人员最多的科室
- let topDeptForLevel1 = ''
- let level1Count = 0
- let totalDeptCount = 0
- let allDeptNames = []
- if (this.qualificationBarData && Array.isArray(this.qualificationBarData)) {
- // 找出"一级"人员最多的科室
- let maxLevel1Count = 0
- let maxDeptName = ''
- let totalCountForDept = 0
- this.qualificationBarData.forEach(dept => {
- allDeptNames.push(dept.deptName)
- if (dept.levelCounts && Array.isArray(dept.levelCounts)) {
- const level1Data = dept.levelCounts.find(level => level.levelName === '高级')
- const deptTotalCount = dept.levelCounts.reduce((sum, level) => sum + (level.count || 0), 0)
- if (level1Data && level1Data.count > maxLevel1Count) {
- maxLevel1Count = level1Data.count
- maxDeptName = dept.deptName || ''
- totalCountForDept = deptTotalCount
- }
- }
- })
- topDeptForLevel1 = maxDeptName
- level1Count = maxLevel1Count
- totalDeptCount = totalCountForDept
- }
- // 生成第二部分描述文字
- return `全站资质等级为"高级"的人员集中在${topDeptForLevel1}(${level1Count}人)${topDeptForLevel1}的人员规模(共${totalDeptCount}人)高于${allDeptNames.filter(name => name !== topDeptForLevel1).join(', ')}`
- },
- // 检查是否为用户类型(已废弃,使用computed属性替代)
- checkUserType() {
- // 此方法已废弃,角色判断现在通过computed属性实现
- },
- // 获取趋势样式类
- getTrendClass(value) {
- if (value > 0) return 'trend-up'
- if (value < 0) return 'trend-down'
- return 'trend-neutral'
- },
- // 格式化比率显示
- formatRate(rate) {
- if (rate === null || rate === undefined) return '0%'
- const numRate = parseFloat(rate)
- if (numRate > 0) {
- return `+${numRate.toFixed(2)}%`
- } else if (numRate < 0) {
- return `${numRate.toFixed(2)}%`
- } else {
- return '--%'
- }
- },
- }
- }
- </script>
- <style lang="scss" scoped>
- .duty-organization {
- background: #fff;
- border-radius: 16rpx;
- }
- .section-title {
- margin: 24rpx 0;
- .title-text {
- font-size: 32rpx;
- font-weight: bold;
- color: #333;
- }
- }
- .attendance-analysis,
- .qualification-distribution,
- .qualification-ability {
- padding: 20rpx;
- border: 1px solid #e8e8e8;
- border-radius: 10rpx;
- margin-bottom: 32rpx;
- }
- .qualification-content {
- display: flex;
- flex-direction: column;
- gap: 32rpx;
- }
- .qualification-section {
- display: flex;
- flex-direction: column;
- gap: 16rpx;
- }
- .analysis-header,
- .distribution-header,
- .ability-header {
- margin-bottom: 16rpx;
- .header-text {
- font-size: 28rpx;
- font-weight: 600;
- color: #333;
- }
- }
- .stat-card {
- background: #f8f9fa;
- border-radius: 12rpx;
- padding: 20rpx;
- margin-bottom: 16rpx;
- .stat-content {
- font-size: 26rpx;
- line-height: 1.5;
- color: #666;
- .highlight {
- color: #1890ff;
- font-weight: 600;
- margin: 0 8rpx;
- }
- }
- }
- .dept-cards {
- display: flex;
- flex-direction: row;
- gap: 16rpx;
- margin-bottom: 24rpx;
- }
- .dept-card {
- flex: 1;
- background: #f8f9fa;
- border-radius: 12rpx;
- padding: 20rpx;
- .dept-name {
- font-size: 26rpx;
- font-weight: 600;
- color: #333;
- margin-bottom: 12rpx;
- }
- .dept-count {
- display: flex;
- align-items: baseline;
- margin-bottom: 12rpx;
- .count-number {
- font-size: 32rpx;
- font-weight: bold;
- color: #1890ff;
- margin-right: 8rpx;
- }
- .count-unit {
- font-size: 24rpx;
- color: #999;
- }
- }
- .dept-trend {
- display: flex;
- gap: 16rpx;
- flex-direction: column;
- .trend-item {
- font-size: 24rpx;
- color: #666;
- .trend-up {
- color: #f5222d;
- }
- .trend-down {
- color: #52c41a;
- }
- .trend-neutral {
- color: #999;
- }
- }
- }
- }
- .chart-container {
- background: #f8f9fa;
- border-radius: 12rpx;
- padding: 20rpx;
- height: 500rpx;
- .echarts-chart {
- width: 100%;
- height: 100%;
- }
- }
- .trend-up {
- color: #f5222d;
- }
- .trend-down {
- color: #52c41a;
- }
- .trend-neutral {
- color: #999;
- }
- </style>
|