bonus-ui/src/views/home/components/MunicipalCompany.vue

653 lines
18 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<template>
<div class="app-container">
<div class="top">
<div class="top-item">
<el-card class="card-box">
<div class="title-tip">
<i class="el-icon-pie-chart icon-tip" />
<span
>数据总览 <span class="left-tip">平台设备总数{{ dataAll.totalSum }}</span></span
>
</div>
<!-- 饼图 -->
<div style="display: flex; justify-content: center">
<div ref="chartRef" style="width: 490px; height: 300px"></div>
</div>
<!-- <div style="display: flex; justify-content: space-between; margin-top: 15px">
<div class="left-tip">平台设备总数(不包含退役装备){{ dataAll.totalSum }}</div>
<div class="left-tip">装备总数{{ dataAll.maNum }}</div>
<div class="left-tip">工具总数{{ dataAll.toolNum }}</div>
</div> -->
</el-card>
</div>
<div class="top-item">
<el-card class="card-box">
<div class="title-tip">
<i class="el-icon-bangzhu icon-tip" />
<span>告警</span>
</div>
<div class="content">
<div class="content-item">
<div class="item-box">
<div class="num">{{ errData.maintainExpireMonthNum }}</div>
<div>一个月内维保到期数</div>
</div>
</div>
<div class="content-item">
<div class="item-box">
<div class="num">{{ errData.maintainOverdueNum }}</div>
<div>维保已超期数</div>
</div>
</div>
</div>
<div class="content">
<div class="content-item">
<div class="item-box">
<div class="num">{{ errData.useYearExpireMonthNum }}</div>
<div>一年内使用年限到期</div>
</div>
</div>
<div class="content-item">
<div class="item-box">
<div class="num">{{ errData.useYearOverdueNum }}</div>
<div>已超最大使用年限</div>
</div>
</div>
</div>
</el-card>
</div>
<div class="top-item">
<el-card class="card-box">
<div class="title-tip">
<i class="el-icon-date icon-tip" />
<span>待办</span>
</div>
<!-- 滚动列表 -->
<div class="scroll-wrapper" :class="{ 'is-hover': isHover}" ref="wrap" @mouseenter="pause" @mouseleave="resume">
<ul class="scroll-list" ref="list">
<li v-for="(item, index) in eventList" :key="item.id" :class="{ odd: index % 2 === 1 }">
<div
style="display: flex; align-items: center; justify-content: space-between; cursor: pointer"
@click="handleEvent(item)"
>
<div style="width: 65%">
<span>{{ index + 1 }}. </span>
<span
>您有一个单号为<span>{{ item.code }}</span
>的"{{ item.businessName }}"单据待审核
</span>
</div>
<div>{{ item.createTime }}</div>
</div>
</li>
</ul>
</div>
</el-card>
</div>
</div>
<div class="top">
<div class="top-item">
<el-card class="card-box">
<div class="title-tip">
<i class="el-icon-copy-document icon-tip" />
<span>当月出库数量</span>
</div>
<div class="content">
<div class="content-item content-item2">
<div class="item-box">
<div class="num2">{{ numData.selfUseApplyNum }}</div>
<div>自用申请数</div>
</div>
</div>
<div class="content-item content-item2">
<div class="item-box">
<div class="num2">{{ numData.selfUseAuditNum }}</div>
<div>自用装备数</div>
</div>
</div>
<div class="content-item content-item2">
<div class="item-box">
<div class="num2">{{ numData.selfUseOutNum }}</div>
<div>自用工具数</div>
</div>
</div>
</div>
<div class="content">
<div class="content-item content-item2">
<div class="item-box">
<div class="num2">{{ shareData.buyerNum }}</div>
<div>共享订单数</div>
</div>
</div>
<div class="content-item content-item2">
<div class="item-box">
<div class="num2">{{ shareData.sellerNum }}</div>
<div>共享装备数</div>
</div>
</div>
<div class="content-item content-item2">
<div class="item-box">
<div class="num2">{{ shareData.toolNum }}</div>
<div>共享工具数</div>
</div>
</div>
</div>
</el-card>
</div>
<div class="top-item">
<el-card class="card-box">
<div class="title-tip">
<i class="el-icon-set-up icon-tip" />
<span>当月退库数量</span>
</div>
<div class="content">
<div class="content-item content-item2">
<div class="item-box">
<div class="num2">{{ numData.returnApplyNum }}</div>
<div>退库申请数</div>
</div>
</div>
<div class="content-item content-item2">
<div class="item-box">
<div class="num2">{{ numData.returnAuditApplyNum }}</div>
<div>退库审核数</div>
</div>
</div>
</div>
<div class="content">
<div class="content-item content-item2">
<div class="item-box">
<div class="num2">{{ numData.returnAuditStoreNum }}</div>
<div>退库入库数</div>
</div>
</div>
<div class="content-item content-item2">
<div class="item-box">
<div class="num2">{{ numData.returnAuditRepairNum }}</div>
<div>退库维修数</div>
</div>
</div>
</div>
</el-card>
</div>
<div class="top-item">
<el-card class="card-box">
<div class="title-tip">
<i class="el-icon-setting icon-tip" />
<span>当月维修数量</span>
</div>
<div class="content">
<div class="content-item content-item2">
<div class="item-box">
<div class="num2">{{ numData.repairPendingNum }}</div>
<div>待维修数</div>
</div>
</div>
<div class="content-item content-item2">
<div class="item-box">
<div class="num2">{{ numData.repairQualifiedNum }}</div>
<div>维修合格数</div>
</div>
</div>
</div>
<div class="content">
<div class="content-item3" style="width: 48%">
<div class="item-box">
<div class="num2">{{ numData.repairRetireNum }}</div>
<div>维修退役数</div>
</div>
</div>
<!-- <div class="" style="flex: 1"> </div> -->
</div>
</el-card>
</div>
</div>
</div>
</template>
<script>
import * as echarts from 'echarts'
import { getDeviceNumAll, getDeviceNumByMonth, getMaQc, getApprover, getShare } from '@/api/basic/home'
export default {
name: 'MunicipalCompany',
data() {
return {
isLoading: false,
dataAll: {},
toolTotal: 60,
equipTotal: 50,
activeTab: true,
errData: {},
shareData: {},
eventList: [],
timer: null,
speed: 60,
isHover: false,
numData: {
selfUseApplyNum: 0, // 自用申请数
selfUseAuditNum: 0, // 自用审核数
selfUseOutNum: 0, // 自用出库数
returnApplyNum: 0, // 退库申请数
returnAuditApplyNum: 0, // 退库审核数
returnAuditStoreNum: 0, // 退库入库数
returnAuditRepairNum: 0, // 退库维修数
repairPendingNum: 0, // 待维修数
repairQualifiedNum: 0, // 维修合格数
repairRetireNum: 0, // 维修合格数
},
}
},
mounted() {
this.getEventList()
this.getDataAll()
this.getNumData()
this.getErrData()
this.getShareData()
},
beforeDestroy() {
clearInterval(this.timer)
},
methods: {
async getDataAll() {
try {
const res = await getDeviceNumAll()
this.dataAll = res.data
this.initChart()
} catch (error) {
console.log('🚀 ~ error:', error)
}
},
async getEventList() {
try {
const res = await getApprover()
this.eventList = res.data
this.startScroll()
} catch (error) {
console.log('🚀 ~ error:', error)
}
},
handleEvent(item) {
console.log('🚀 ~ item:', item)
let url = ''
if (item.businessName == '设备出库') {
url = '/business/addAudit'
} else if (item.businessName == '设备退库') {
url = '/business/warehouse/auditDetails'
} else if (item.businessName == '设备维修') {
url = '/equipment/repair/approval-detail'
} else if (item.businessName == '设备退役') {
this.$router.push({
name: 'RetireApplyAuditDetail',
params: { id: item.id },
query: { taskInfo: JSON.stringify(item) },
})
}
if (item.businessName == '设备退役') return
this.$router.push({
path: url,
query: {
id: item.id,
isEdit: true,
},
})
},
async getShareData() {
try {
const res = await getShare()
this.shareData = res.data
} catch (error) {
console.log('🚀 ~ error:', error)
}
},
async getNumData() {
try {
const res = await getDeviceNumByMonth()
this.numData = res.data
} catch (error) {
console.log('🚀 ~ error:', error)
}
},
async getErrData() {
try {
const res = await getMaQc()
this.errData = res.data
} catch (error) {
console.log('🚀 ~ error:', error)
}
},
handleActive() {
this.activeTab = !this.activeTab
if (!this.activeTab) {
this.$nextTick(() => {
this.initBarChart()
})
}
},
startScroll() {
const wrap = this.$refs.wrap
this.timer = setInterval(() => {
wrap.scrollTop++
// 如果滚到底部,则跳回 0实现无缝滚动
if (wrap.scrollTop >= wrap.scrollHeight - wrap.clientHeight) {
wrap.scrollTop = 0
}
}, this.speed)
},
pause() {
clearInterval(this.timer)
},
resume() {
this.startScroll()
},
initChart() {
const chartDom = this.$refs.chartRef
const chart = echarts.init(chartDom)
const option = {
tooltip: {
trigger: 'item',
formatter: (params) => {
return `
<div style="text-align:center;">
<div style="font-size:14px;font-weight:600;">
${params.name}
</div>
<div style="margin-top:4px;font-size:12px;color:#666;">
数量${params.value}
</div>
</div>
`
},
},
legend: {
orient: 'horizontal',
bottom: 0,
textStyle: {
fontSize: 14,
},
},
series: [
{
name: '数量',
type: 'pie',
radius: ['20%', '65%'],
label: {
show: true,
position: 'outside',
formatter: (params) => {
console.log('🚀 ~ params:', params)
return `{a|${params.percent || 0}%}\n{b|${params.name}}`
},
rich: {
a: {
fontSize: 16,
fontWeight: 'bold',
lineHeight: 20,
},
b: {
fontSize: 12,
color: '#666',
lineHeight: 18,
},
},
},
data: [
{ value: this.dataAll.maNum, name: '装备总数' },
{ value: this.dataAll.toolNum, name: '工具总数' },
],
emphasis: {
itemStyle: {
shadowBlur: 10,
shadowOffsetX: 0,
},
},
},
],
}
chart.setOption(option)
},
initBarChart() {
const barDom = this.$refs.barRef
const barChart = echarts.init(barDom)
const companyNames = this.tableList.map((item) => item.name)
const equipTotals = this.tableList.map((item) => item.equipTotal)
const toolTotals = this.tableList.map((item) => item.toolTotal)
const option = {
tooltip: { trigger: 'axis' },
legend: {
top: 0,
right: 20,
data: ['装备总数', '工具总数'],
},
grid: {
left: '3%',
right: '3%',
bottom: '3%',
containLabel: true,
},
xAxis: {
type: 'category',
data: companyNames,
axisLabel: {
fontSize: 13,
interval: 0, // 强制全部显示
lineHeight: 16,
formatter: function (name) {
const maxLen = 6 // 每行6字符可调整
if (name.length <= maxLen) return name
const rows = Math.ceil(name.length / maxLen)
let res = ''
for (let i = 0; i < rows; i++) {
res += name.substring(i * maxLen, (i + 1) * maxLen) + '\n'
}
return res
},
},
},
yAxis: {
type: 'value',
name: '数量(个)',
nameTextStyle: { fontSize: 14 },
},
series: [
{
name: '装备总数',
type: 'bar',
barWidth: 35,
barGap: '0%', // ← 两个柱子紧贴
barCategoryGap: '0%', // ← 类目间无空隙
itemStyle: {
color: '#5470C6',
borderRadius: [4, 4, 0, 0],
},
data: equipTotals,
},
{
name: '工具总数',
type: 'bar',
barWidth: 35,
barGap: '0%',
barCategoryGap: '0%',
itemStyle: {
color: '#91CC75',
borderRadius: [4, 4, 0, 0],
},
data: toolTotals,
},
],
}
barChart.setOption(option)
},
},
}
</script>
<style lang="scss" scoped>
.app-container {
background: #f2f2f2;
}
.top {
display: flex;
align-items: center;
justify-content: space-between;
gap: 20px;
margin-bottom: 10px;
.top-item {
width: 33%;
flex: 1;
}
.left-tip {
font-size: 13px;
background: #f5f5f5;
padding: 3px 10px;
border-radius: 5px;
color: #75787d;
}
}
.card-box {
width: 100%;
border-radius: 15px;
min-height: 380px;
height: calc(45vh - 36px);
}
.card-box2 {
width: 100%;
border-radius: 15px;
min-height: 430px;
height: calc(45vh - 36px);
}
.title-tip-tip {
font-weight: 900;
text-align: left;
}
.icon-tip {
margin-right: 10px;
font-weight: 900;
}
::v-deep .card-box {
padding: 0 !important;
}
.content {
width: 100%;
margin-top: 20px;
display: flex;
gap: 20px;
justify-content: center;
.content-item {
flex: 1;
border-radius: 8px;
height: 140px;
display: flex;
align-items: center;
overflow: hidden;
border-radius: 16px;
background: linear-gradient(180deg, #fff7f7 0%, #fff 60%);
box-shadow: 0 0 0.5px 0 rgba(0, 0, 0, 0.3), 0 1px 3px 0 rgba(0, 0, 0, 0.15);
.item-box {
flex: 1;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
color: #75787d;
font-size: 13px;
}
.num {
margin-bottom: 10px;
font-size: 21px;
color: red;
font-weight: 800;
}
}
.content-item3 {
border-radius: 8px;
height: 140px;
display: flex;
align-items: center;
overflow: hidden;
border-radius: 16px;
background: linear-gradient(180deg, #e8f9f3 0%, #fff 60.1%);
box-shadow: 0 0 0.5px 0 rgba(0, 0, 0, 0.3), 0 1px 3px 0 rgba(0, 0, 0, 0.15);
.item-box {
flex: 1;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
color: #75787d;
font-size: 13px;
}
.num {
margin-bottom: 10px;
font-size: 21px;
color: red;
font-weight: 800;
}
}
.content-item2 {
background: linear-gradient(180deg, #e8f9f3 0%, #fff 60.1%);
box-shadow: 0 0 0.5px 0 rgba(0, 0, 0, 0.3), 0 1px 3px 0 rgba(0, 0, 0, 0.15);
}
}
.num2 {
margin-bottom: 10px;
font-size: 21px;
color: #54a798;
font-weight: 800;
}
.scroll-wrapper {
margin-top: 10px;
height: 300px; /* 按你卡片内部高度调整 */
overflow: hidden;
position: relative;
}
.scroll-list {
padding: 0;
margin: 0;
list-style: none;
}
.is-hover {
overflow: auto;
}
.scroll-list li {
padding: 8px 12px;
font-size: 14px;
transition: background 0.3s;
}
/* 隔行背景色 */
.scroll-list li.odd {
background-color: #ebf8f3;
}
</style>