653 lines
18 KiB
Vue
653 lines
18 KiB
Vue
<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>
|