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

497 lines
13 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">
<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>