生产监控看板

This commit is contained in:
朱菊兰 2025-04-25 10:33:07 +08:00
parent 2c1e1845f4
commit 446fa9a0d1
12 changed files with 354 additions and 114 deletions

View File

@ -21,6 +21,7 @@ import {
handleTree, handleTree,
addBeginAndEndTime, addBeginAndEndTime,
divide, divide,
formatThousands
} from '@/utils/ruoyi'; } from '@/utils/ruoyi';
import Pagination from '@/components/Pagination'; import Pagination from '@/components/Pagination';
// 自定义表格工具扩展 // 自定义表格工具扩展
@ -45,6 +46,7 @@ Vue.prototype.resetForm = resetForm;
Vue.prototype.getDictDatas = getDictDatas; Vue.prototype.getDictDatas = getDictDatas;
Vue.prototype.getDictDatas2 = getDictDatas2; Vue.prototype.getDictDatas2 = getDictDatas2;
Vue.prototype.getDictDataLabel = getDictDataLabel; Vue.prototype.getDictDataLabel = getDictDataLabel;
Vue.prototype.formatThousands = formatThousands;
Vue.prototype.DICT_TYPE = DICT_TYPE; Vue.prototype.DICT_TYPE = DICT_TYPE;
Vue.prototype.handleTree = handleTree; Vue.prototype.handleTree = handleTree;
Vue.prototype.addBeginAndEndTime = addBeginAndEndTime; Vue.prototype.addBeginAndEndTime = addBeginAndEndTime;

View File

@ -295,3 +295,15 @@ export function getPath(path) {
} }
return Math.floor(divisor/dividend*100)/100; return Math.floor(divisor/dividend*100)/100;
} }
// 通用千分位格式化函数
export function formatThousands(value) {
if (value === null || value === undefined) return '0'
// 清理已有逗号并转为数字
const numValue = Number(String(value).replace(/,/g, ''))
if (isNaN(numValue)) return '0'
// 支持小数处理
return numValue.toLocaleString('en-US')
}

View File

@ -7,18 +7,20 @@
<div class='dataBox' style='top:50px'> <div class='dataBox' style='top:50px'>
<p> <p>
<span class='text'>总投入片数</span> <span class='text'>总投入片数</span>
<span class='precent precentG'>-18%</span> <span class='precent' :class='{precentR:monthData?.inputNumChange>=0,precentG:monthData?.inputNumChange<0}'>{{monthData?.inputNumChange || '-'}}%</span>
<img src="../../../../assets/images/dataBoard/arrDown.png" alt="" width='5' height='15'> <img v-show='monthData?.inputNumChange<0' src="../../../../assets/images/dataBoard/arrDown.png" alt="" width='5' height='15'>
<img v-show='monthData?.inputNumChange>=0' src="../../../../assets/images/dataBoard/arrUp.png" alt="" width='5' height='15'>
</p> </p>
<p class='num'>261,938,984</p> <p class='num'>{{monthData?.inputNum ? formatThousands(monthData.inputNum) : '-'}}</p>
</div> </div>
<div class='dataBox'style='top:180px'> <div class='dataBox'style='top:180px'>
<p> <p>
<span class='text'>总生产片数</span> <span class='text'>总生产片数</span>
<span class='precent precentR'>+18%</span> <span class='precent' :class='{precentR:monthData?.outputNumChange>=0,precentG:monthData?.outputNumChange<0}'>{{monthData?.outputNumChange || '-'}}%</span>
<img src="../../../../assets/images/dataBoard/arrUp.png" alt="" width='5' height='15'> <img v-show='monthData?.outputNumChange<0' src="../../../../assets/images/dataBoard/arrDown.png" alt="" width='5' height='15'>
<img v-show='monthData?.outputNumChange>=0' src="../../../../assets/images/dataBoard/arrUp.png" alt="" width='5' height='15'>
</p> </p>
<p class='num'>82,261,938,984</p> <p class='num'>{{monthData?.outputNum ? formatThousands(monthData.outputNum) : '-'}}</p>
</div> </div>
</div> </div>
</template> </template>
@ -26,8 +28,25 @@
<script> <script>
export default { export default {
name: 'CenterBottomL', name: 'CenterBottomL',
props: {
dataObj: {
type: Object,
default: () => {}
}
},
watch: {
dataObj(val) {
val.monthAndLastMonth && val.monthAndLastMonth.forEach(item => {
if (item.dataType === "本月") {
this.monthData = item
}
})
}
},
data() { data() {
return {} return {
monthData:{}
}
}, },
methods: {} methods: {}
} }

View File

@ -8,13 +8,13 @@
<p> <p>
<span class='text'>总投入片数</span> <span class='text'>总投入片数</span>
</p> </p>
<p class='num'>261,938,984</p> <p class='num'>{{lastMonthData?.inputNum ? formatThousands(lastMonthData.inputNum) : '-'}}</p>
</div> </div>
<div class='dataBox'style='top:180px'> <div class='dataBox'style='top:180px'>
<p> <p>
<span class='text'>总生产片数</span> <span class='text'>总生产片数</span>
</p> </p>
<p class='num'>82,261,938,984</p> <p class='num'>{{lastMonthData?.outputNum ? formatThousands(lastMonthData.outputNum) : '-'}}</p>
</div> </div>
</div> </div>
</template> </template>
@ -22,8 +22,25 @@
<script> <script>
export default { export default {
name: 'CenterBottomR', name: 'CenterBottomR',
props: {
dataObj: {
type: Object,
default: () => {}
}
},
watch: {
dataObj(val) {
val.monthAndLastMonth && val.monthAndLastMonth.forEach(item => {
if (item.dataType === "上月") {
this.lastMonthData = item
}
})
}
},
data() { data() {
return {} return {
lastMonthData:{}
}
}, },
methods: {} methods: {}
} }

View File

@ -10,8 +10,20 @@
<p><span class='eqTipTitle'>设备名称:</span><span class='eqTipNum'>{{eqTipMsg.name}}</span></p> <p><span class='eqTipTitle'>设备名称:</span><span class='eqTipNum'>{{eqTipMsg.name}}</span></p>
<p><span class='eqTipTitle'>进口数量:</span><span class='eqTipNum'>{{eqTipMsg.input}}</span></p> <p><span class='eqTipTitle'>进口数量:</span><span class='eqTipNum'>{{eqTipMsg.input}}</span></p>
<p><span class='eqTipTitle'>出口数量:</span><span class='eqTipNum'>{{eqTipMsg.output}}</span></p> <p><span class='eqTipTitle'>出口数量:</span><span class='eqTipNum'>{{eqTipMsg.output}}</span></p>
<p><span class='eqTipTitle'>报警状态:</span><span class='eqTipNum'><img :src='dotY' width='16'/>{{eqTipMsg.alarm}}</span></p> <p><span class='eqTipTitle'>报警状态:</span>
<p><span class='eqTipTitle'>在线状态:</span><span class='eqTipNum'><img :src='dotR' width='16'/>{{eqTipMsg.status}}</span></p> <span class='eqTipNum'>
<img v-show='eqTipMsg.alarm' :src='dotY' width='16'/>
<img v-show='!eqTipMsg.alarm' :src='dotG' width='16'/>
{{eqTipMsg.alarm?'报警':'未报警'}}
</span>
</p>
<p><span class='eqTipTitle'>在线状态:</span>
<span class='eqTipNum'>
<img v-show='eqTipMsg.status' :src='dotG' width='16'/>
<img v-show='!eqTipMsg.status' :src='dotR' width='16'/>
{{eqTipMsg.status?'在线':'离线'}}
</span>
</p>
</div> </div>
<!-- 设备 --> <!-- 设备 -->
<div class='eqBox'> <div class='eqBox'>
@ -28,22 +40,22 @@
<div class='centerTopTopBox'> <div class='centerTopTopBox'>
<div style='display: inline-block;'> <div style='display: inline-block;'>
<img src="../../../../assets/images/dataBoard/centerNumB.png" alt="" width='203'> <img src="../../../../assets/images/dataBoard/centerNumB.png" alt="" width='203'>
<p class='num'>88%</p> <p class='num'>{{dataObj?.productRate || '-'}}%</p>
<p class='title'>成品率</p> <p class='title'>成品率</p>
</div> </div>
<div style='display: inline-block;'> <div style='display: inline-block;'>
<img src="../../../../assets/images/dataBoard/centerNumY.png" alt="" width='261'> <img src="../../../../assets/images/dataBoard/centerNumY.png" alt="" width='261'>
<p class='num' style='width: 260px;padding-left: 20px;'>8,984</p> <p class='num' style='width: 260px;padding-left: 20px;'>{{dataObj?.todayProduct ? formatThousands(dataObj.todayProduct) : '-'}}</p>
<p class='title' style='color:#FFB625'>今日产量</p> <p class='title' style='color:#FFB625'>今日产量</p>
</div> </div>
<div style='display: inline-block;'> <div style='display: inline-block;'>
<img src="../../../../assets/images/dataBoard/centerNumY.png" alt="" width='261'> <img src="../../../../assets/images/dataBoard/centerNumY.png" alt="" width='261'>
<p class='num' style='width: 260px;padding-left: 20px;'>12,948,984</p> <p class='num' style='width: 260px;padding-left: 20px;'>{{dataObj?.monthProduct ? formatThousands(dataObj.monthProduct) : '-'}}</p>
<p class='title' style='color:#FFB625'>本月产量</p> <p class='title' style='color:#FFB625'>本月产量</p>
</div> </div>
<div style='display: inline-block;'> <div style='display: inline-block;'>
<img src="../../../../assets/images/dataBoard/centerNumB.png" alt="" width='203'> <img src="../../../../assets/images/dataBoard/centerNumB.png" alt="" width='203'>
<p class='num'>59</p> <p class='num'>{{dataObj?.alarmNum ? formatThousands(dataObj.alarmNum) : '-'}}</p>
<p class='title'>设备报警数</p> <p class='title'>设备报警数</p>
</div> </div>
</div> </div>
@ -59,7 +71,6 @@ export default {
left: 0, left: 0,
top: 0 top: 0
}, },
scaleNum:1,
dotY:require('../../../../assets/images/dataBoard/dotY.png'), dotY:require('../../../../assets/images/dataBoard/dotY.png'),
dotR:require('../../../../assets/images/dataBoard/dotR.png'), dotR:require('../../../../assets/images/dataBoard/dotR.png'),
dotG:require('../../../../assets/images/dataBoard/dotG.png'), dotG:require('../../../../assets/images/dataBoard/dotG.png'),
@ -192,11 +203,29 @@ export default {
scaleNum: { scaleNum: {
type: Number, type: Number,
default: 1 default: 1
},
dataObj: {
type: Object,
default: () => {}
} }
}, },
watch: { watch: {
scaleNum(val) { scaleNum(val) {
this.scaleNum = val this.scaleNum = val
},
dataObj(val) {
val.equipmentDets && val.equipmentDets.forEach(item => {
this.eqList.forEach(eq => {
if (eq.id == item.id) {
eq.name = item.name
eq.input = item.input
eq.output = item.output
eq.alarm = item.isError
eq.status = item.isRun
}
})
})
console.log(this.eqList)
} }
}, },
mounted() {}, mounted() {},
@ -205,10 +234,10 @@ export default {
handleMouseEnter(item, event) { handleMouseEnter(item, event) {
this.showTooltip = true; this.showTooltip = true;
this.eqTipMsg.name = item.name; this.eqTipMsg.name = item.name;
// this.eqTipMsg.input = item.input; this.eqTipMsg.input = item.input;
// this.eqTipMsg.output = item.output; this.eqTipMsg.output = item.output;
// this.eqTipMsg.alarm = item.alarm; this.eqTipMsg.alarm = item.alarm;
// this.eqTipMsg.status = item.status; this.eqTipMsg.status = item.status;
this.updateTooltipPosition(event); this.updateTooltipPosition(event);
}, },
handleMouseLeave() { handleMouseLeave() {
@ -288,7 +317,7 @@ p{
.eqBox { .eqBox {
span{ span{
display: inline-block; display: inline-block;
border: 1px solid red; // border: 1px solid red;
position: absolute; position: absolute;
} }
} }

View File

@ -1,7 +1,7 @@
<template> <template>
<header class="dataBoardHeader"> <header class="dataBoardHeader">
<div class='topTitle'></div> <div class='topTitle'></div>
<div class='time-chsoose'> <!-- <div class='time-chsoose'>
<span class='title'>时间选择</span> <span class='title'>时间选择</span>
<span class='time-show'>{{time}}</span> <span class='time-show'>{{time}}</span>
<el-date-picker <el-date-picker
@ -12,7 +12,7 @@
style='position: absolute;right: 0px;width: 119px;top:-2px;opacity: 0;' style='position: absolute;right: 0px;width: 119px;top:-2px;opacity: 0;'
> >
</el-date-picker> </el-date-picker>
</div> </div> -->
<div <div
type="text" type="text"
class="screen-btn" class="screen-btn"
@ -26,7 +26,7 @@
</template> </template>
<script> <script>
import moment from 'moment' // import moment from 'moment'
export default { export default {
name: 'DataBoardHeader', name: 'DataBoardHeader',
props: { props: {
@ -39,7 +39,7 @@ export default {
}, },
data() { data() {
return { return {
time: moment().format('YYYY-MM-DD') // time: moment().format('YYYY-MM-DD')
} }
}, },
computed: {}, computed: {},

View File

@ -4,6 +4,8 @@
<svg-icon icon-class="dataBoard2" class='icon'/> <svg-icon icon-class="dataBoard2" class='icon'/>
<span>投入产出及良品率</span> <span>投入产出及良品率</span>
</div> </div>
<div v-if='xData.length === 0' class='noData'>暂无数据</div>
<div v-else>
<div class="top_legend"> <div class="top_legend">
<span class="chart_legend_icon1">投入</span> <span class="chart_legend_icon1">投入</span>
<span class="chart_legend_icon2">产出</span> <span class="chart_legend_icon2">产出</span>
@ -11,21 +13,55 @@
</div> </div>
<div id='inOutputChart' style='width: 400px;height: 290px;'></div> <div id='inOutputChart' style='width: 400px;height: 290px;'></div>
</div> </div>
</div>
</template> </template>
<script> <script>
import * as echarts from 'echarts'; import * as echarts from 'echarts';
export default { export default {
name: 'LeftBottom', name: 'LeftBottom',
data() { props: {
return {} dataObj: {
type: Object,
default: () => {}
}
}, },
mounted() { watch: {
dataObj(val) {
this.xData = []
this.inputData = []
this.outputData = []
this.goodRateData = []
val.monthBar && val.monthBar.forEach(item => {
this.xData.push(item.dataType)
this.inputData.push(item.inputNum)
this.outputData.push(item.outputNum)
this.goodRateData.push(item.goodRate)
})
this.initChart(); this.initChart();
}
}, },
data() {
return {
chartDom: '',
chart: '',
xData:[],
inputData:[],
outputData:[],
goodRateData:[],
}
},
mounted() {},
methods: { methods: {
initChart() { initChart() {
var chartDom = document.getElementById('inOutputChart'); if (
var myChart = echarts.init(chartDom); this.chart !== null &&
this.chart !== '' &&
this.chart !== undefined
) {
this.chart.dispose() // Dom
}
this.chartDom = document.getElementById('inOutputChart')
this.chart = echarts.init(this.chartDom)
var option; var option;
option = { option = {
grid: { top: 40, right: 10, bottom: 5, left: 10, containLabel: true }, grid: { top: 40, right: 10, bottom: 5, left: 10, containLabel: true },
@ -34,12 +70,12 @@ export default {
}, },
xAxis: { xAxis: {
type: "category", type: "category",
data: ['1月','2月','3月','4月','5月','6月','7月','8月','9月','10月','11月','12月'], data: this.xData,
axisLabel: { axisLabel: {
color: "#fff", color: "#fff",
fontSize: 12, fontSize: 10,
interval: 0, interval: 0,
rotate:20 rotate:30
}, },
axisTick: { show: false }, axisTick: { show: false },
axisLine: { axisLine: {
@ -110,7 +146,7 @@ export default {
}, },
series: [ series: [
{ {
data: [1000,3000,2800,4200,5000,6000,7000,8000,9000,10000,11000,12000], data: this.inputData,
type: "bar", type: "bar",
barWidth: 10, barWidth: 10,
barGap:0, barGap:0,
@ -122,7 +158,7 @@ export default {
}, },
}, },
{ {
data:[800,2900,2700,4100,4800,4900,5900,6800,7900,8900,9900,10000], data:this.outputData,
type: "bar", type: "bar",
barWidth: 10, barWidth: 10,
itemStyle: { itemStyle: {
@ -133,7 +169,7 @@ export default {
}, },
}, },
{ {
data: [98,97,99,96,98,99,98,97,98,99,98,99], data:this.goodRateData,
type: "line", type: "line",
yAxisIndex: 1, yAxisIndex: 1,
symbol:'circle', symbol:'circle',
@ -149,7 +185,7 @@ export default {
} }
], ],
} }
option && myChart.setOption(option); option && this.chart.setOption(option);
} }
} }
} }
@ -230,6 +266,11 @@ export default {
} }
} }
} }
.noData {
font-size: 24px;
text-align: center;
padding-top: 100px;
}
</style> </style>
<style> <style>
.qhd-chart-tooltip { .qhd-chart-tooltip {

View File

@ -19,12 +19,12 @@
<div>总生产</div> <div>总生产</div>
</div> </div>
<div class='right-data' style="top:15px;"> <div class='right-data' style="top:15px;">
<p><span class='num'>932,261</span><span class='text'>片数</span></p> <p><span class='num'>{{todayData?.inputNum ? formatThousands(todayData.inputNum) : '-'}}</span><span class='text'>片数</span></p>
<p><span class='num'>894</span><span class='text'>面积/</span></p> <p><span class='num'>{{todayData?.inputArea ? formatThousands(todayData.inputArea) : '-'}}</span><span class='text'>面积/</span></p>
</div> </div>
<div class='right-data' style="top:132px;"> <div class='right-data' style="top:132px;">
<p><span class='num'>932,261</span><span class='text'>片数</span></p> <p><span class='num'>{{todayData?.outputNum ? formatThousands(todayData.outputNum) : '-'}}</span><span class='text'>片数</span></p>
<p><span class='num'>894</span><span class='text'>面积/</span></p> <p><span class='num'>{{todayData?.outputArea ? formatThousands(todayData.outputArea) : '-'}}</span><span class='text'>面积/</span></p>
</div> </div>
</div> </div>
<div class='title-split'> <div class='title-split'>
@ -42,12 +42,12 @@
<div>总生产</div> <div>总生产</div>
</div> </div>
<div class='right-data' style="top:15px;"> <div class='right-data' style="top:15px;">
<p><span class='num'>932,261</span><span class='text'>片数</span></p> <p><span class='num'>{{yesterdayData?.inputNum ? formatThousands(yesterdayData.inputNum) : '-'}}</span><span class='text'>片数</span></p>
<p><span class='num'>894</span><span class='text'>面积/</span></p> <p><span class='num'>{{yesterdayData?.inputArea ? formatThousands(yesterdayData.inputArea) : '-'}}</span><span class='text'>面积/</span></p>
</div> </div>
<div class='right-data' style="top:132px;"> <div class='right-data' style="top:132px;">
<p><span class='num'>932,261</span><span class='text'>片数</span></p> <p><span class='num'>{{yesterdayData?.outputNum ? formatThousands(yesterdayData.outputNum) : '-'}}</span><span class='text'>片数</span></p>
<p><span class='num'>894</span><span class='text'>面积/</span></p> <p><span class='num'>{{yesterdayData?.outputArea ? formatThousands(yesterdayData.outputArea) : '-'}}</span><span class='text'>面积/</span></p>
</div> </div>
</div> </div>
</div> </div>
@ -55,8 +55,28 @@
<script> <script>
export default { export default {
name: 'LeftTop', name: 'LeftTop',
props: {
dataObj: {
type: Object,
default: () => {}
}
},
watch: {
dataObj(val) {
val.todayAndYesterday && val.todayAndYesterday.forEach(item => {
if (item.dataType === "今日") {
this.todayData = item
} else {
this.yesterdayData = item
}
})
}
},
data() { data() {
return {} return {
todayData:{},
yesterdayData:{}
}
}, },
methods: {} methods: {}
} }

View File

@ -7,25 +7,11 @@
<div class='rankingLeft'> <div class='rankingLeft'>
<div class='rankingLeftTitle'>产量</div> <div class='rankingLeftTitle'>产量</div>
<div class='rankingTypeBox'> <div class='rankingTypeBox'>
<div> <div style='margin-bottom: 10px;' v-for='(item,index) in teamProductionRank' :key='index'>
<img src="../../../../assets//images//dataBoard/ranking1.png" alt="" width='50'> <img :src="require(`../../../../assets/images/dataBoard/ranking${index+1}.png`)" alt="" width="50">
<div class='rankingTextBox'> <div class='rankingTextBox'>
<p class='text1'>班组1/</p> <p class='text1'>{{item.name}}/</p>
<p class='text2'>261,938,984</p> <p class='text2'>{{item?.value ? formatThousands(item.value) : '-'}}</p>
</div>
</div>
<div style='margin: 10px 0;'>
<img src="../../../../assets//images//dataBoard/ranking2.png" alt="" width='50'>
<div class='rankingTextBox'>
<p class='text1'>班组1/</p>
<p class='text2'>261,938,984</p>
</div>
</div>
<div>
<img src="../../../../assets//images//dataBoard/ranking3.png" alt="" width='50'>
<div class='rankingTextBox'>
<p class='text1'>班组1/</p>
<p class='text2'>261,938,984</p>
</div> </div>
</div> </div>
</div> </div>
@ -35,14 +21,14 @@
成品率 成品率
</div> </div>
<div class='rankingTypeBox' style='left: 120px;'> <div class='rankingTypeBox' style='left: 120px;'>
<div> <div style='margin-bottom: 10px;' v-for='(item,index) in teamRateRank' :key='index'>
<img src="../../../../assets//images//dataBoard/ranking1.png" alt="" width='50'> <img :src="require(`../../../../assets/images/dataBoard/ranking${index+1}.png`)" alt="" width="50">
<div class='rankingTextBox'> <div class='rankingTextBox'>
<p class='text1'>班组1</p> <p class='text1'>{{item.name}}</p>
<p class='text2'>95<span style='font-size: 20px;'>.92%</span></p> <p class='text2'>{{Math.trunc(item.value)}}<span style='font-size: 20px;'>.{{item.value1}}%</span></p>
</div> </div>
</div> </div>
<div style='margin: 10px 0;'> <!-- <div>
<img src="../../../../assets//images//dataBoard/ranking2.png" alt="" width='50'> <img src="../../../../assets//images//dataBoard/ranking2.png" alt="" width='50'>
<div class='rankingTextBox'> <div class='rankingTextBox'>
<p class='text1'>班组1</p> <p class='text1'>班组1</p>
@ -55,7 +41,7 @@
<p class='text1'>班组1</p> <p class='text1'>班组1</p>
<p class='text2'>90<span style='font-size: 20px;'>.42%</span></p> <p class='text2'>90<span style='font-size: 20px;'>.42%</span></p>
</div> </div>
</div> </div> -->
</div> </div>
</div> </div>
</div> </div>
@ -63,11 +49,49 @@
<script> <script>
export default { export default {
name: 'RightBottom', name: 'RightBottom',
props: {
dataObj: {
type: Object,
default: () => {}
}
},
watch: {
dataObj(val) {
this.teamProductionRank = []
this.teamRateRank = []
val.teamProductionRank && val.teamProductionRank.forEach(item => {
let obj = {
name: item.name,
value: item.value,
rank: item.rank,
}
this.teamProductionRank.push(obj)
})
val.teamRateRank && val.teamRateRank.forEach(item => {
let obj = {
name: item.name,
value: item.value,
rank: item.rank,
value1: this.getPositiveDecimal(item.value)
}
this.teamRateRank.push(obj)
})
}
},
data() { data() {
return {} return {
teamProductionRank:[],
teamRateRank:[]
}
}, },
mounted() {}, mounted() {},
methods: {} methods: {
getPositiveDecimal(num) {
const str = Math.abs(num).toString();
const [, decimal] = str.split('.');
return decimal || '00';
}
}
} }
</script> </script>
<style lang='scss' scoped> <style lang='scss' scoped>

View File

@ -14,23 +14,26 @@
<div class='box' style='width: 105px;'> <div class='box' style='width: 105px;'>
<p class='name'> <p class='name'>
<span style='margin-right: 3px;'>当日</span> <span style='margin-right: 3px;'>当日</span>
<img src="../../../../assets/images/dataBoard/arrUp.png" alt="" width='5' height='15'> <img v-show='nokSumDet.today >= nokSumDet.yesterday' src="../../../../assets/images/dataBoard/arrUp.png" alt="" width='5' height='15'>
<img v-show='nokSumDet.today < nokSumDet.yesterday' src="../../../../assets/images/dataBoard/arrDown.png" alt="" width='5' height='15'>
</p> </p>
<p class='num'>283</p> <p class='num'>{{nokSumDet?.today ? formatThousands(nokSumDet.today) : '-'}}</p>
</div> </div>
<div class='box' style='width: 115px;'> <div class='box' style='width: 115px;'>
<p class='name'> <p class='name'>
<span style='margin-right: 3px;'>本月</span> <span style='margin-right: 3px;'>本月</span>
<img src="../../../../assets/images/dataBoard/arrDown.png" alt="" width='5' height='15'> <img v-show='nokSumDet.month >= nokSumDet.lastMonth' src="../../../../assets/images/dataBoard/arrUp.png" alt="" width='5' height='15'>
<img v-show='nokSumDet.month < nokSumDet.lastMonth' src="../../../../assets/images/dataBoard/arrDown.png" alt="" width='5' height='15'>
</p> </p>
<p class='num'>2,830</p> <p class='num'>{{nokSumDet?.month ? formatThousands(nokSumDet.month) : '-'}}</p>
</div> </div>
<div class='box' style='width: 110px;'> <div class='box' style='width: 110px;'>
<p class='name'> <p class='name'>
<span style='margin-right: 3px;'>本年</span> <span style='margin-right: 3px;'>本年</span>
<img src="../../../../assets/images/dataBoard/arrDown.png" alt="" width='5' height='15'> <img v-show='nokSumDet.year >= nokSumDet.lastYear' src="../../../../assets/images/dataBoard/arrUp.png" alt="" width='5' height='15'>
<img v-show='nokSumDet.year < nokSumDet.lastYear' src="../../../../assets/images/dataBoard/arrDown.png" alt="" width='5' height='15'>
</p> </p>
<p class='num'>32,830</p> <p class='num'>{{nokSumDet?.year ? formatThousands(nokSumDet.year) : '-'}}</p>
</div> </div>
</div> </div>
<div class='row' style='padding-top: 5px;'> <div class='row' style='padding-top: 5px;'>
@ -38,19 +41,19 @@
<p class='name'> <p class='name'>
<span>昨日</span> <span>昨日</span>
</p> </p>
<p class='num'>283</p> <p class='num'>{{nokSumDet?.yesterday ? formatThousands(nokSumDet.yesterday) : '-'}}</p>
</div> </div>
<div class='box' style='width: 115px;'> <div class='box' style='width: 115px;'>
<p class='name'> <p class='name'>
<span>上月</span> <span>上月</span>
</p> </p>
<p class='num'>2,830</p> <p class='num'>{{nokSumDet?.lastMonth ? formatThousands(nokSumDet.lastMonth) : '-'}}</p>
</div> </div>
<div class='box' style='width: 110px;'> <div class='box' style='width: 110px;'>
<p class='name'> <p class='name'>
<span>上年</span> <span>上年</span>
</p> </p>
<p class='num'>32,830</p> <p class='num'>{{nokSumDet?.lastYear ? formatThousands(nokSumDet.lastYear) : '-'}}</p>
</div> </div>
</div> </div>
</div> </div>
@ -73,29 +76,51 @@ const colors = [
]; ];
export default { export default {
name: 'RightTop', name: 'RightTop',
props: {
dataObj: {
type: Object,
default: () => {}
}
},
watch: {
dataObj(val) {
this.nokSumDet = val.nokSumDet
this.dataProps = []
val.nokPieDet && val.nokPieDet.forEach(item => {
let obj = {
value: item.num,
name: item.type
}
this.dataProps.push(obj)
})
this.initChart()
}
},
data() { data() {
return { return {
nokSumDet:{},
chartDom: '',
chart: '',
dataProps:[],
rangArrValue:[], rangArrValue:[],
dataList:[], dataList:[],
totalValue:0 totalValue:0
} }
}, },
mounted() { mounted() {},
this.initChart();
},
methods: { methods: {
initChart() { initChart() {
var chartDom = document.getElementById('defectSumChart'); if (
var myChart = echarts.init(chartDom); this.chart !== null &&
this.chart !== '' &&
this.chart !== undefined
) {
this.chart.dispose() // Dom
}
this.chartDom = document.getElementById('defectSumChart')
this.chart = echarts.init(this.chartDom)
var option; var option;
const dataProps = [ this.getPersonnelList(this.dataProps)
{value: 14,name:'缺陷1'},
{value: 20,name:'缺陷2'},
{value: 22,name:'缺陷3'},
{value: 14,name:'缺陷4'},
{value: 30,name:'缺陷5'}
]
this.getPersonnelList(dataProps)
option = { option = {
color: colors, color: colors,
graphic: [ graphic: [
@ -132,10 +157,6 @@ export default {
type: "pie", type: "pie",
radius: ["45%", "60%"], radius: ["45%", "60%"],
center: ["50%", "50%"], center: ["50%", "50%"],
// label: {
// formatter: "{d}%",
// color: "#fff",
// },
label:{ label:{
formatter:function(params){ formatter:function(params){
return `{color${params.dataIndex}|${params.percent}%}\n{style2|${params.name}}` return `{color${params.dataIndex}|${params.percent}%}\n{style2|${params.name}}`
@ -161,7 +182,7 @@ export default {
}, },
], ],
} }
option && myChart.setOption(option); option && this.chart.setOption(option);
}, },
getCoordinates(startArc, endArc) { getCoordinates(startArc, endArc) {
const posi = [ const posi = [

View File

@ -5,13 +5,13 @@
:is-full-screen="isFullScreen" :is-full-screen="isFullScreen"
@screenfullChange="screenfullChange" @screenfullChange="screenfullChange"
/> />
<LeftTop /> <LeftTop :dataObj='dataObj'/>
<LeftBottom /> <LeftBottom :dataObj='dataObj'/>
<CenterTop :scaleNum='scaleNum'/> <CenterTop :scaleNum='scaleNum' :dataObj='dataObj'/>
<CenterBottomL /> <CenterBottomL :dataObj='dataObj'/>
<CenterBottomR /> <CenterBottomR :dataObj='dataObj'/>
<RightTop /> <RightTop :dataObj='dataObj'/>
<RightBottom /> <RightBottom :dataObj='dataObj'/>
</div> </div>
</div> </div>
</template> </template>
@ -27,6 +27,8 @@ import RightTop from './components/RightTop.vue';
import RightBottom from './components/RightBottom.vue'; import RightBottom from './components/RightBottom.vue';
import { debounce } from '@/utils/debounce'; import { debounce } from '@/utils/debounce';
import screenfull from 'screenfull'; import screenfull from 'screenfull';
import { getAccessToken } from '@/utils/auth';
import store from "@/store";
export default { export default {
name: 'DataBoard', name: 'DataBoard',
components: { DataBoardHeader,LeftTop,LeftBottom,CenterTop,CenterBottomL,CenterBottomR,RightTop,RightBottom }, components: { DataBoardHeader,LeftTop,LeftBottom,CenterTop,CenterBottomL,CenterBottomR,RightTop,RightBottom },
@ -35,12 +37,14 @@ export default {
return { return {
isFullScreen: false, isFullScreen: false,
scaleNum: 0.8, scaleNum: 0.8,
dataObj:{}
}; };
}, },
created() { created() {
this.init() this.init()
}, },
mounted() { mounted() {
console.log('dataBoard mounted')
this.boxReset = debounce(() => { this.boxReset = debounce(() => {
this.resetSize() this.resetSize()
}, 300) }, 300)
@ -48,6 +52,10 @@ export default {
window.addEventListener('resize', () => { window.addEventListener('resize', () => {
this.boxReset() this.boxReset()
}) })
this.getData()
},
destroyed() {
console.log('dataBoard destroyed')
}, },
computed: { computed: {
sidebarOpened() { sidebarOpened() {
@ -60,6 +68,53 @@ export default {
} }
}, },
methods: { methods: {
getData() {
let _this = this;
const url = process.env.VUE_APP_BASE_API+'/admin-api/monitoring/message/subscribe/'+store.getters.userId+'-'+Date.now();
const token = getAccessToken()
const headers = new Headers({
'Authorization': `Bearer ${token}`,
'Content-Type': 'text/event-stream'
});
fetch(url, { headers })
.then(response => {
const reader = response.body.getReader();
const decoder = new TextDecoder();
const readStream = () => {
reader.read().then(({ done, value }) => {
if (done) {
console.log('SSE 连接关闭');
return;
}
const data = decoder.decode(value);
console.log('收到消息:', data);
if (_this.isValidData(data)){
_this.upDateMsg(data);
}
readStream(); //
}).catch(error => {
console.error('SSE 读取错误:', error);
});
};
readStream();
})
.catch(error => {
console.error('SSE 连接失败:', error);
});
},
isValidData (data) {
return data.trim().startsWith('data:{') && !data.includes('heartbeat');
},
upDateMsg(data) {
const jsonStr = data.replace(/^data:/, '').trim();
try {
const dataObj = JSON.parse(jsonStr);
this.dataObj = dataObj
console.log('dataObj',this.dataObj)
} catch (e) {
console.error('JSON 解析失败:', e);
}
},
change() { change() {
this.isFullScreen = screenfull.isFullscreen this.isFullScreen = screenfull.isFullscreen
}, },