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

497 lines
13 KiB
Vue
Raw Normal View History

2025-12-11 20:03:52 +08:00
<template>
<div class="app-container">
<div class="top">
<div class="top-item">
<el-card class="card-box">
<div class="title">
<i class="el-icon-pie-chart icon-tip" />
<span>数据总览</span>
</div>
<!-- 饼图 -->
<div style="display: flex; justify-content: center">
<div ref="chartRef" style="width: 490px; height: 270px"></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">
<i class="el-icon-bangzhu icon-tip" />
<span>使用情况</span>
</div>
<div class="content">
<div class="content-item" style="background: #f3fbf8">
<div class="line" style="background: #79d1b8" />
<div class="item-box">
<div>在库数量</div>
<div class="num">{{ useData.availableNum }}</div>
</div>
</div>
<div class="content-item" style="background: #f2f6fe">
<div class="line" style="background: #9dbefa" />
<div class="item-box">
<div>自用数量</div>
<div class="num">{{ useData.inNum }}</div>
</div>
</div>
<div class="content-item" style="background: #f3fbf8">
<div class="line" style="background: #79d1b8" />
<div class="item-box">
<div>共享数量</div>
<div class="num">{{ useData.shareNum }}</div>
</div>
</div>
</div>
<div class="content">
<div class="content-item" style="background: #fdf1ea">
<div class="line" style="background: #f2ab7f" />
<div class="item-box">
<div>维修数量</div>
<div class="num">{{ useData.repairNum }}</div>
</div>
</div>
<div class="content-item" style="background: #fbedeb">
<div class="line" style="background: #e56d74" />
<div class="item-box">
<div>退役数量</div>
<div class="num">{{ useData.scrapNum }}</div>
</div>
</div>
<div style="flex: 1"></div>
</div>
</el-card>
</div>
<div class="top-item">
<el-card class="card-box">
<div class="title">
<i class="el-icon-date icon-tip" />
<span>实时事件</span>
</div>
<!-- 滚动列表 -->
<div class="scroll-wrapper" 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">
<div style="width: 65%">
<span>{{ index + 1 }}. </span>
<span>{{ item.deptAbbreviation }} </span>
<span>{{ item.applyUser }} </span>
<span>{{ item.businessName }}</span>
</div>
<div>{{ item.applyTime }}</div>
</div>
</li>
</ul>
</div>
</el-card>
</div>
</div>
<div class="bottom">
<el-card class="card-box2">
<div style="display: flex; justify-content: space-between; align-items: center; margin-bottom: 15px">
<div class="title">
<i class="el-icon-reading icon-tip" />
<span>装备对比</span>
</div>
<div style="display: flex; justify-content: space-between; align-items: center">
<el-button type="primary" size="mini" @click="handleActive">图表分析</el-button>
<i class="el-icon-download icon-tip" style="margin-left: 10px; cursor: pointer" @click="handleExport" />
</div>
</div>
<el-table
v-if="activeTab"
v-loading="isLoading"
:data="tableList"
highlight-current-row
border
stripe
style="width: 100%"
>
<el-table-column type="index" width="55" label="序号" align="center" />
<el-table-column
v-for="(column, index) in tableColumns"
show-overflow-tooltip
:key="index"
:label="column.label"
:prop="column.prop"
align="center"
>
</el-table-column>
</el-table>
<!-- 柱状图 -->
<div v-else ref="barRef" style="width: 100%; height: 340px; margin-top: 20px"></div>
</el-card>
</div>
</div>
</template>
<script>
import * as echarts from 'echarts'
import { getDeviceNumAll, getUseDeviceNum, getReal, getDeviceByDept } from '@/api/basic/home'
export default {
name: 'ProvincialCompany',
data() {
return {
isLoading: false,
toolTotal: 60,
equipTotal: 50,
activeTab: true,
dataAll: {
maNum: 0,
maPercent: 0,
toolNum: 0,
toolPercent: 0,
totalSum: 0,
},
useData: {
availableNum: 0,
inNum: 0,
repairNum: 0,
scrapNum: 0,
shareNum: 0,
},
eventList: [],
timer: null,
speed: 30,
tableColumns: [
{ label: '公司名称', prop: 'companyName' },
{ label: '装备总数', prop: 'maNum' },
{ label: '装备总价值', prop: 'maCost' },
{ label: '工具总数', prop: 'toolNum' },
{ label: '编码工具总数', prop: 'toolCodeNum' },
{ label: '数量工具总数', prop: 'toolNoCodeNum' },
],
tableList: [],
}
},
mounted() {
this.getDataAll()
this.getUseData()
this.getEventList()
this.getList()
},
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 getUseData() {
try {
const res = await getUseDeviceNum()
this.useData = res.data
} catch (error) {
console.log('🚀 ~ error:', error)
}
},
async getEventList() {
try {
const res = await getReal()
this.eventList = res.data
this.startScroll()
} catch (error) {
console.log('🚀 ~ error:', error)
}
},
async getList() {
try {
const res = await getDeviceByDept()
this.tableList = 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()
},
handleExport() {
this.download('material-mall/index/export', {}, `装备对比_${new Date().getTime()}.xlsx`)
},
initChart() {
const chartDom = this.$refs.chartRef
const chart = echarts.init(chartDom)
const option = {
legend: {
orient: 'horizontal',
bottom: 0,
textStyle: {
fontSize: 14,
},
},
series: [
{
name: '数量',
type: 'pie',
radius: ['20%', '65%'],
label: {
show: true,
position: 'outside',
formatter: (params) => {
return `{a|${params.percent}%}\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.companyName)
const equipTotals = this.tableList.map((item) => item.maNum)
const toolTotals = this.tableList.map((item) => item.toolNum)
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 = 3 // 每行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 {
flex: 1;
}
.left-tip {
font-size: 13px;
background: #f5f5f5;
padding: 3px 10px;
border-radius: 5px;
color: #75787d;
}
}
.card-box {
width: 100%;
border-radius: 15px;
height: 360px;
}
.card-box2 {
width: 100%;
border-radius: 15px;
min-height: 430px;
}
.title {
font-weight: 900;
}
.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;
.content-item {
flex: 1;
border-radius: 8px;
height: 90px;
/* background: #f3fbf8; */
display: flex;
align-items: center;
overflow: hidden;
.line {
width: 4px;
height: 90px;
/* background: #79d1b8; */
}
.item-box {
flex: 1;
display: flex;
flex-direction: column;
justify-content: center;
align-items: center;
color: #75787d;
font-size: 13px;
}
.num {
margin-top: 10px;
font-size: 20px;
color: #333;
font-weight: 800;
}
}
}
.scroll-wrapper {
margin-top: 10px;
height: 300px;
overflow: hidden;
position: relative;
}
.scroll-list {
padding: 0;
margin: 0;
list-style: none;
}
.scroll-list li {
padding: 8px 12px;
font-size: 14px;
transition: background 0.3s;
}
/* 隔行背景色 */
.scroll-list li.odd {
background-color: #ebf8f3;
}
</style>