<template>
|
<div class="app-container">
|
<!-- 搜索表单 -->
|
<el-form ref="queryForm" :model="queryParams" :inline="true" size="small">
|
<el-form-item label="人员姓名" prop="userName">
|
<el-input v-model="queryParams.userName" placeholder="请输入人员姓名" clearable style="width: 150px" @keyup.enter.native="handleQuery" />
|
</el-form-item>
|
<el-form-item label="部门" prop="dept">
|
<el-input v-model="queryParams.dept" placeholder="请输入部门" clearable style="width: 180px" @keyup.enter.native="handleQuery" />
|
</el-form-item>
|
<el-form-item label="时间范围" prop="timeRange">
|
<el-date-picker
|
v-model="timeRange"
|
type="daterange"
|
range-separator="-"
|
start-placeholder="开始时间"
|
end-placeholder="结束时间"
|
value-format="yyyy-MM-dd"
|
style="width: 240px"
|
/>
|
</el-form-item>
|
<el-form-item>
|
<el-button type="primary" icon="el-icon-search" @click="handleQuery">查询</el-button>
|
<el-button icon="el-icon-refresh" @click="resetQuery">重置</el-button>
|
<el-button type="success" icon="el-icon-download" @click="handleExport">导出</el-button>
|
</el-form-item>
|
</el-form>
|
|
<!-- Tab切换 -->
|
<el-tabs v-model="activeTab" @tab-click="handleTabChange">
|
<el-tab-pane label="人员工作统计" name="user">
|
<!-- 统计卡片 -->
|
<el-row :gutter="20" style="margin-bottom: 20px;">
|
<el-col :span="6">
|
<el-card shadow="hover">
|
<div class="stat-card">
|
<div class="stat-title">检测样品总数</div>
|
<div class="stat-value">{{ userStatistics.totalSamples || 0 }}</div>
|
</div>
|
</el-card>
|
</el-col>
|
<el-col :span="6">
|
<el-card shadow="hover">
|
<div class="stat-card">
|
<div class="stat-title">检测项目总数</div>
|
<div class="stat-value">{{ userStatistics.totalItems || 0 }}</div>
|
</div>
|
</el-card>
|
</el-col>
|
<el-col :span="6">
|
<el-card shadow="hover">
|
<div class="stat-card">
|
<div class="stat-title">平均检测及时率</div>
|
<div class="stat-value">{{ userStatistics.avgTimelyRate || 0 }}%</div>
|
</div>
|
</el-card>
|
</el-col>
|
<el-col :span="6">
|
<el-card shadow="hover">
|
<div class="stat-card">
|
<div class="stat-title">参与人员数</div>
|
<div class="stat-value">{{ userStatistics.userCount || 0 }}</div>
|
</div>
|
</el-card>
|
</el-col>
|
</el-row>
|
|
<!-- 数据表格 -->
|
<lims-table
|
:tableData="userTableData"
|
:column="userTableColumn"
|
:page="page"
|
:tableLoading="tableLoading"
|
@pagination="handlePagination"
|
/>
|
</el-tab-pane>
|
|
<el-tab-pane label="及时率统计" name="timely">
|
<!-- 及时率图表 -->
|
<el-row :gutter="20" style="margin-bottom: 20px;">
|
<el-col :span="12">
|
<el-card shadow="hover">
|
<div slot="header">样品负责人及时率</div>
|
<Echart
|
:xAxis="chargeTimelyXAxis"
|
:yAxis="chargeTimelyYAxis"
|
:series="chargeTimelySeries"
|
:tooltip="{ trigger: 'axis' }"
|
:grid="{ left: '3%', right: '4%', bottom: '3%', containLabel: true }"
|
:chartStyle="{ height: '350px' }"
|
/>
|
</el-card>
|
</el-col>
|
<el-col :span="12">
|
<el-card shadow="hover">
|
<div slot="header">试验员及时率</div>
|
<Echart
|
:xAxis="testerTimelyXAxis"
|
:yAxis="testerTimelyYAxis"
|
:series="testerTimelySeries"
|
:tooltip="{ trigger: 'axis' }"
|
:grid="{ left: '3%', right: '4%', bottom: '3%', containLabel: true }"
|
:chartStyle="{ height: '350px' }"
|
/>
|
</el-card>
|
</el-col>
|
</el-row>
|
|
<!-- 及时率数据表格 -->
|
<lims-table
|
:tableData="timelyTableData"
|
:column="timelyTableColumn"
|
:page="timelyPage"
|
:tableLoading="timelyTableLoading"
|
@pagination="handleTimelyPagination"
|
/>
|
</el-tab-pane>
|
|
<el-tab-pane label="工作趋势" name="trend">
|
<el-card shadow="hover">
|
<div slot="header">
|
<span>工作趋势图</span>
|
<el-radio-group v-model="trendType" size="mini" style="float: right;" @change="getTrendData">
|
<el-radio-button label="week">近一周</el-radio-button>
|
<el-radio-button label="month">近一月</el-radio-button>
|
<el-radio-button label="year">近一年</el-radio-button>
|
</el-radio-group>
|
</div>
|
<Echart
|
:xAxis="trendXAxis"
|
:yAxis="trendYAxis"
|
:series="trendSeries"
|
:tooltip="{ trigger: 'axis' }"
|
:legend="{ data: ['样品数', '项目数', '及时率'] }"
|
:grid="{ left: '3%', right: '4%', bottom: '3%', containLabel: true }"
|
:chartStyle="{ height: '400px' }"
|
/>
|
</el-card>
|
</el-tab-pane>
|
</el-tabs>
|
</div>
|
</template>
|
|
<script>
|
import Echart from '@/components/echarts/echarts.vue'
|
import limsTable from '@/components/Table/lims-table.vue'
|
import { getStatisticsByUser, getTimelyRate, getWorkTrend, exportWorkStatistics } from '@/api/report/workStatistics'
|
|
export default {
|
name: 'WorkStatistics',
|
components: { Echart, limsTable },
|
data() {
|
return {
|
queryParams: {},
|
timeRange: [],
|
activeTab: 'user',
|
tableLoading: false,
|
userTableData: [],
|
userStatistics: {},
|
page: { total: 0, size: 10, current: 1 },
|
userTableColumn: [
|
{ label: '人员姓名', prop: 'userName', minWidth: '100px' },
|
{ label: '部门', prop: 'dept', minWidth: '120px' },
|
{ label: '检测样品数', prop: 'sampleCount', minWidth: '100px' },
|
{ label: '检测项目数', prop: 'itemCount', minWidth: '100px' },
|
{ label: '及时完成数', prop: 'timelyCount', minWidth: '100px' },
|
{ label: '超期完成数', prop: 'overdueCount', minWidth: '100px' },
|
{ label: '及时率', prop: 'timelyRate', minWidth: '100px', formatData: (val) => `${val}%` },
|
{ label: '平均用时(h)', prop: 'avgTime', minWidth: '100px' }
|
],
|
// 及时率数据
|
timelyTableLoading: false,
|
timelyTableData: [],
|
timelyPage: { total: 0, size: 10, current: 1 },
|
timelyTableColumn: [
|
{ label: '人员姓名', prop: 'userName', minWidth: '100px' },
|
{ label: '角色', prop: 'roleType', minWidth: '100px' },
|
{ label: '负责样品数', prop: 'sampleCount', minWidth: '100px' },
|
{ label: '按时完成数', prop: 'timelyCount', minWidth: '100px' },
|
{ label: '超期数', prop: 'overdueCount', minWidth: '100px' },
|
{ label: '及时率', prop: 'timelyRate', minWidth: '100px', dataType: 'tag', formatData: (val) => `${val}%`, formatType: (val) => val >= 90 ? 'success' : val >= 70 ? 'warning' : 'danger' }
|
],
|
trendType: 'week',
|
// 图表配置
|
chargeTimelyXAxis: [{ type: 'category', data: [] }],
|
chargeTimelyYAxis: [{ type: 'value', max: 100 }],
|
chargeTimelySeries: [{ name: '及时率', type: 'bar', data: [] }],
|
testerTimelyXAxis: [{ type: 'category', data: [] }],
|
testerTimelyYAxis: [{ type: 'value', max: 100 }],
|
testerTimelySeries: [{ name: '及时率', type: 'bar', data: [] }],
|
trendXAxis: [{ type: 'category', data: [] }],
|
trendYAxis: [{ type: 'value' }, { type: 'value', max: 100, position: 'right' }],
|
trendSeries: [
|
{ name: '样品数', type: 'bar', data: [] },
|
{ name: '项目数', type: 'bar', data: [] },
|
{ name: '及时率', type: 'line', yAxisIndex: 1, data: [] }
|
]
|
}
|
},
|
mounted() {
|
this.getUserData()
|
},
|
methods: {
|
handleTabChange(tab) {
|
if (tab.name === 'user') {
|
this.getUserData()
|
} else if (tab.name === 'timely') {
|
this.getTimelyData()
|
} else if (tab.name === 'trend') {
|
this.getTrendData()
|
}
|
},
|
getUserData() {
|
this.tableLoading = true
|
const params = { ...this.queryParams }
|
if (this.timeRange && this.timeRange.length === 2) {
|
params.startTime = this.timeRange[0]
|
params.endTime = this.timeRange[1]
|
}
|
getStatisticsByUser({ ...params, ...this.page })
|
.then(res => {
|
this.userTableData = res.data.records || []
|
this.page.total = res.data.total || 0
|
this.userStatistics = res.data.statistics || {}
|
})
|
.finally(() => (this.tableLoading = false))
|
},
|
getTimelyData() {
|
this.timelyTableLoading = true
|
const params = { ...this.queryParams }
|
if (this.timeRange && this.timeRange.length === 2) {
|
params.startTime = this.timeRange[0]
|
params.endTime = this.timeRange[1]
|
}
|
getTimelyRate({ ...params, ...this.timelyPage })
|
.then(res => {
|
this.timelyTableData = res.data.records || []
|
this.timelyPage.total = res.data.total || 0
|
// 图表数据
|
const chargeData = (res.data.chargeList || []).slice(0, 10)
|
this.chargeTimelyXAxis[0].data = chargeData.map(item => item.userName)
|
this.chargeTimelySeries[0].data = chargeData.map(item => item.timelyRate)
|
const testerData = (res.data.testerList || []).slice(0, 10)
|
this.testerTimelyXAxis[0].data = testerData.map(item => item.userName)
|
this.testerTimelySeries[0].data = testerData.map(item => item.timelyRate)
|
})
|
.finally(() => (this.timelyTableLoading = false))
|
},
|
getTrendData() {
|
const params = { timeType: this.trendType }
|
if (this.timeRange && this.timeRange.length === 2) {
|
params.startTime = this.timeRange[0]
|
params.endTime = this.timeRange[1]
|
}
|
getWorkTrend(params).then(res => {
|
this.trendXAxis[0].data = res.data.dates || []
|
this.trendSeries[0].data = res.data.sampleCounts || []
|
this.trendSeries[1].data = res.data.itemCounts || []
|
this.trendSeries[2].data = res.data.timelyRates || []
|
})
|
},
|
handleQuery() {
|
if (this.activeTab === 'user') {
|
this.page.current = 1
|
this.getUserData()
|
} else if (this.activeTab === 'timely') {
|
this.timelyPage.current = 1
|
this.getTimelyData()
|
} else {
|
this.getTrendData()
|
}
|
},
|
resetQuery() {
|
this.queryParams = {}
|
this.timeRange = []
|
this.handleQuery()
|
},
|
handleExport() {
|
const params = { ...this.queryParams, type: this.activeTab }
|
if (this.timeRange && this.timeRange.length === 2) {
|
params.startTime = this.timeRange[0]
|
params.endTime = this.timeRange[1]
|
}
|
exportWorkStatistics(params).then(res => {
|
this.downloadFile(res, '工作统计.xlsx')
|
})
|
},
|
handlePagination({ page, limit }) {
|
this.page.current = page
|
this.page.size = limit
|
this.getUserData()
|
},
|
handleTimelyPagination({ page, limit }) {
|
this.timelyPage.current = page
|
this.timelyPage.size = limit
|
this.getTimelyData()
|
},
|
downloadFile(data, fileName) {
|
const blob = new Blob([data])
|
const url = window.URL.createObjectURL(blob)
|
const link = document.createElement('a')
|
link.href = url
|
link.download = fileName
|
link.click()
|
window.URL.revokeObjectURL(url)
|
}
|
}
|
}
|
</script>
|
|
<style scoped>
|
.stat-card {
|
text-align: center;
|
padding: 15px 0;
|
}
|
.stat-title {
|
font-size: 14px;
|
color: #909399;
|
}
|
.stat-value {
|
font-size: 28px;
|
font-weight: bold;
|
color: #303133;
|
margin-top: 10px;
|
}
|
</style>
|