feat:洗棒大屏页面布局
This commit is contained in:
parent
4f373edb48
commit
bb52129444
|
|
@ -0,0 +1,402 @@
|
|||
<template>
|
||||
<el-container class="dashboard">
|
||||
<el-header class="header">
|
||||
<div class="title">洗棒大屏</div>
|
||||
<div class="timer">{{ currentDay }} {{ currentTime }}</div>
|
||||
</el-header>
|
||||
<el-main style="padding:6px;overflow:hidden">
|
||||
<el-row style="height:92vh">
|
||||
<!-- 左栏:产品已完成数 + 产品种类占比 -->
|
||||
<el-col :span="6" style="height:100%;display:flex;flex-direction:column;gap:6px;padding-right:3px">
|
||||
<div style="height:12vh">
|
||||
<dv-border-box-12 style="height:100%">
|
||||
<div class="numBlock_title">产品已完成数</div>
|
||||
<div class="numBlock_value">{{ finishedCount }}</div>
|
||||
</dv-border-box-12>
|
||||
</div>
|
||||
<div style="flex:1;position:relative">
|
||||
<dv-border-box-1 style="height:100%">
|
||||
<div class="chartBlockTitle">产品种类占比</div>
|
||||
<dv-active-ring-chart :config="config_ring" style="height:calc(100% - 5vh);width:100%"/>
|
||||
</dv-border-box-1>
|
||||
</div>
|
||||
</el-col>
|
||||
|
||||
<!-- 中栏:设备实时曲线轮播 -->
|
||||
<el-col :span="12" style="height:100%;padding:0 3px">
|
||||
<dv-border-box-1 style="height:100%;display:flex;flex-direction:column">
|
||||
<div class="chartBlockTitle">
|
||||
设备实时曲线
|
||||
<span class="eq-name-badge">{{ currentEqName }}</span>
|
||||
<span class="interval-tip">每5s采集 · 每20s切换</span>
|
||||
</div>
|
||||
<div class="eq-dots">
|
||||
<span
|
||||
v-for="(eq, i) in equipmentList"
|
||||
:key="eq.id"
|
||||
class="eq-dot"
|
||||
:class="{ active: i === currentEqIndex }"
|
||||
></span>
|
||||
</div>
|
||||
<scEcharts :option="chartOption_curve" style="flex:1;width:96%;margin:0 2%"/>
|
||||
<div class="curve-stats">
|
||||
<span>当前值:<em class="val-cur">{{ curStat.cur }}</em></span>
|
||||
<span>最大值:<em class="val-max">{{ curStat.max }}</em></span>
|
||||
<span>最小值:<em class="val-min">{{ curStat.min }}</em></span>
|
||||
<span>采集点:<em class="val-cur">{{ curStat.count }}</em></span>
|
||||
</div>
|
||||
</dv-border-box-1>
|
||||
</el-col>
|
||||
|
||||
<!-- 右栏:人员周产量 -->
|
||||
<el-col :span="6" style="height:100%;padding-left:3px">
|
||||
<dv-border-box-1 style="height:100%;display:flex;flex-direction:column">
|
||||
<div class="chartBlockTitle">人员周产量</div>
|
||||
<scEcharts :option="chartOption_right" style="flex:1;width:96%;margin:0 2%"/>
|
||||
</dv-border-box-1>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</el-main>
|
||||
</el-container>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
const myColor = ['#1089E7', '#F57474', '#56D0E3', '#F8B448', '#8B78F6'];
|
||||
|
||||
export default {
|
||||
name: 'XiboDept',
|
||||
data() {
|
||||
return {
|
||||
currentTime: '',
|
||||
currentDay: '',
|
||||
dayInterval: null,
|
||||
|
||||
// 产品已完成数
|
||||
finishedCount: 0,
|
||||
|
||||
// 产品种类占比
|
||||
config_ring: {
|
||||
data: [],
|
||||
digitalFlopStyle: { fontSize: 14, fill: '#fff' },
|
||||
},
|
||||
|
||||
// 设备列表 & 轮播状态
|
||||
equipmentList: [],
|
||||
currentEqIndex: 0,
|
||||
chartDataMap: {},
|
||||
cdInterval: null,
|
||||
carouselInterval: null,
|
||||
|
||||
// 折线图(设备曲线)
|
||||
chartOption_curve: {
|
||||
backgroundColor: '',
|
||||
tooltip: { trigger: 'axis', axisPointer: { type: 'cross' } },
|
||||
grid: { left: '4%', right: '4%', top: '8%', bottom: '12%', containLabel: true },
|
||||
xAxis: {
|
||||
type: 'category',
|
||||
data: [],
|
||||
boundaryGap: false,
|
||||
axisLabel: { color: '#7ecfcf', fontSize: 9 },
|
||||
axisLine: { lineStyle: { color: 'rgba(83,198,243,0.3)' } },
|
||||
splitLine: { show: false },
|
||||
},
|
||||
yAxis: {
|
||||
type: 'value',
|
||||
axisLabel: { color: '#7ecfcf', fontSize: 10 },
|
||||
splitLine: { lineStyle: { color: 'rgba(83,198,243,0.1)', type: 'dashed' } },
|
||||
},
|
||||
series: [{
|
||||
type: 'line',
|
||||
data: [],
|
||||
smooth: true,
|
||||
lineStyle: { color: '#00e5ff', width: 2 },
|
||||
itemStyle: { color: '#00e5ff' },
|
||||
symbol: 'none',
|
||||
areaStyle: {
|
||||
color: {
|
||||
type: 'linear', x: 0, y: 0, x2: 0, y2: 1,
|
||||
colorStops: [
|
||||
{ offset: 0, color: 'rgba(0,229,255,0.3)' },
|
||||
{ offset: 1, color: 'rgba(0,229,255,0)' },
|
||||
],
|
||||
},
|
||||
},
|
||||
}],
|
||||
},
|
||||
|
||||
// 曲线统计信息
|
||||
curStat: { cur: '--', max: '--', min: '--', count: 0 },
|
||||
|
||||
// 人员周产量(模拟数据)
|
||||
chartOption_right: {
|
||||
backgroundColor: '',
|
||||
tooltip: { trigger: 'axis', axisPointer: { type: 'shadow' } },
|
||||
grid: { top: '2%', left: '2%', right: '14%', bottom: '2%', containLabel: true },
|
||||
xAxis: {
|
||||
type: 'value',
|
||||
axisLabel: { color: '#7ecfcf', fontSize: 9 },
|
||||
splitLine: { lineStyle: { color: 'rgba(83,198,243,0.1)', type: 'dashed' } },
|
||||
},
|
||||
yAxis: {
|
||||
type: 'category',
|
||||
inverse: true,
|
||||
data: ['张三', '李四', '王五', '赵六', '钱七', '孙八', '周九', '吴十'],
|
||||
axisLabel: { color: '#aef', fontSize: 10 },
|
||||
axisLine: { show: false },
|
||||
axisTick: { show: false },
|
||||
},
|
||||
series: [{
|
||||
type: 'bar',
|
||||
barWidth: 14,
|
||||
data: [920, 796, 734, 661, 594, 508, 438, 360],
|
||||
itemStyle: {
|
||||
borderRadius: [0, 4, 4, 0],
|
||||
color: (params) => myColor[params.dataIndex % 5],
|
||||
},
|
||||
label: { show: true, position: 'right', color: '#fff', fontSize: 10 },
|
||||
}],
|
||||
},
|
||||
};
|
||||
},
|
||||
computed: {
|
||||
currentEqName() {
|
||||
return this.equipmentList[this.currentEqIndex]?.name || '--';
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.showTime();
|
||||
this.dayInterval = setInterval(this.showTime, 1000);
|
||||
this.getFinishedCount();
|
||||
this.getMaterialRatio();
|
||||
this.initEquipmentCurve();
|
||||
},
|
||||
beforeUnmount() {
|
||||
clearInterval(this.dayInterval);
|
||||
clearInterval(this.cdInterval);
|
||||
clearInterval(this.carouselInterval);
|
||||
},
|
||||
methods: {
|
||||
showTime() {
|
||||
this.currentTime = this.$TOOL.dateFormat(new Date(), 'hh:mm:ss');
|
||||
this.currentDay = this.$TOOL.dateFormat(new Date(), 'yyyy年MM月dd日');
|
||||
},
|
||||
|
||||
async getFinishedCount() {
|
||||
try {
|
||||
const mgroups = await this.$API.mtm.mgroup.list.req({
|
||||
belong_dept__name: '拉丝排板班组',
|
||||
page: 0,
|
||||
});
|
||||
if (!mgroups || mgroups.length === 0) return;
|
||||
let total = 0;
|
||||
for (const mg of mgroups) {
|
||||
const res = await this.$API.wpm.wmaterial.list.req({
|
||||
tag: 'done',
|
||||
page_size: 1,
|
||||
mgroup: mg.id,
|
||||
});
|
||||
total += res.count || 0;
|
||||
}
|
||||
this.finishedCount = total;
|
||||
} catch (e) {
|
||||
console.error('getFinishedCount error', e);
|
||||
}
|
||||
},
|
||||
|
||||
getMaterialRatio() {
|
||||
this.$API.bi.dataset.exec.req('materialCount', {}).then((res) => {
|
||||
if (!res.data2?.ds0?.length) return;
|
||||
const list = res.data2.ds0.filter(
|
||||
(item) => item.dept_name === '拉丝排板班组'
|
||||
);
|
||||
const nameMap = {};
|
||||
list.forEach((item) => {
|
||||
nameMap[item.material_name] =
|
||||
(nameMap[item.material_name] || 0) + (item.count || 0);
|
||||
});
|
||||
this.config_ring.data = Object.entries(nameMap).map(([name, value]) => ({
|
||||
name,
|
||||
value,
|
||||
}));
|
||||
});
|
||||
},
|
||||
|
||||
async initEquipmentCurve() {
|
||||
try {
|
||||
const res = await this.$API.em.equipment.list.req({
|
||||
belong_dept__name: '拉丝排板班组',
|
||||
page: 0,
|
||||
});
|
||||
this.equipmentList = (res || []).filter((eq) => eq.cd_req_addr);
|
||||
if (this.equipmentList.length === 0) return;
|
||||
this.equipmentList.forEach((eq) => {
|
||||
this.chartDataMap[eq.id] = [];
|
||||
});
|
||||
this.startCdPoll();
|
||||
this.startCarousel();
|
||||
} catch (e) {
|
||||
console.error('initEquipmentCurve error', e);
|
||||
}
|
||||
},
|
||||
|
||||
startCdPoll() {
|
||||
if (this.cdInterval) clearInterval(this.cdInterval);
|
||||
this.pollOnce();
|
||||
this.cdInterval = setInterval(this.pollOnce, 5000);
|
||||
},
|
||||
|
||||
pollOnce() {
|
||||
const eq = this.equipmentList[this.currentEqIndex];
|
||||
if (!eq) return;
|
||||
this.$API.em.cd.req({ method: eq.cd_req_addr }).then((res) => {
|
||||
let numVal;
|
||||
if (typeof res === 'number') {
|
||||
numVal = res;
|
||||
} else if (typeof res === 'object' && res !== null) {
|
||||
const first = Object.values(res)[0];
|
||||
numVal = parseFloat(first);
|
||||
}
|
||||
if (isNaN(numVal)) return;
|
||||
const time = this.$TOOL.dateFormat(new Date(), 'hh:mm:ss');
|
||||
const points = this.chartDataMap[eq.id];
|
||||
points.push({ time, value: numVal });
|
||||
if (points.length > 60) points.shift();
|
||||
this.refreshChart(points);
|
||||
}).catch((e) => console.warn('cd poll error:', e));
|
||||
},
|
||||
|
||||
refreshChart(points) {
|
||||
this.chartOption_curve.xAxis.data = points.map((p) => p.time);
|
||||
this.chartOption_curve.series[0].data = points.map((p) => p.value);
|
||||
if (points.length > 0) {
|
||||
const values = points.map((p) => p.value);
|
||||
this.curStat = {
|
||||
cur: values[values.length - 1].toFixed(2),
|
||||
max: Math.max(...values).toFixed(2),
|
||||
min: Math.min(...values).toFixed(2),
|
||||
count: values.length,
|
||||
};
|
||||
}
|
||||
},
|
||||
|
||||
startCarousel() {
|
||||
if (this.carouselInterval) clearInterval(this.carouselInterval);
|
||||
this.carouselInterval = setInterval(() => {
|
||||
this.currentEqIndex =
|
||||
(this.currentEqIndex + 1) % this.equipmentList.length;
|
||||
this.curStat = { cur: '--', max: '--', min: '--', count: 0 };
|
||||
this.startCdPoll();
|
||||
const eq = this.equipmentList[this.currentEqIndex];
|
||||
this.refreshChart(this.chartDataMap[eq.id]);
|
||||
}, 20000);
|
||||
},
|
||||
},
|
||||
};
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
@font-face {
|
||||
font-family: 'myfont';
|
||||
src: url('../../../utils/youShe.ttf');
|
||||
}
|
||||
.dashboard {
|
||||
background-image: url('/public/img/photon_bg.png');
|
||||
background-size: cover;
|
||||
color: #fff;
|
||||
font-family: 'Microsoft Yahei' !important;
|
||||
}
|
||||
.header {
|
||||
background: url('/public/img/header_bgv.png');
|
||||
background-size: 100% 100%;
|
||||
height: 8vh;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
position: relative;
|
||||
border-bottom: none;
|
||||
}
|
||||
.title {
|
||||
font-family: 'myfont';
|
||||
font-size: 4vh;
|
||||
font-weight: bold;
|
||||
color: rgb(149, 255, 255);
|
||||
letter-spacing: 10px;
|
||||
}
|
||||
.timer {
|
||||
position: absolute;
|
||||
left: 20px;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
font-size: 14px;
|
||||
color: rgb(149, 255, 255);
|
||||
}
|
||||
.numBlock_title {
|
||||
height: 4vh;
|
||||
width: 100%;
|
||||
font-size: 1.5vh;
|
||||
line-height: 4vh;
|
||||
text-align: center;
|
||||
}
|
||||
.numBlock_value {
|
||||
height: 6vh;
|
||||
width: 100%;
|
||||
line-height: 6vh;
|
||||
text-align: center;
|
||||
font-size: 3.5vh;
|
||||
font-family: 'myfont';
|
||||
color: #00e5ff;
|
||||
}
|
||||
.chartBlockTitle {
|
||||
width: 90%;
|
||||
height: 5vh;
|
||||
margin: auto;
|
||||
line-height: 6vh;
|
||||
text-align: left;
|
||||
padding-left: 10px;
|
||||
font-size: 1.6vh;
|
||||
font-weight: 600;
|
||||
border-bottom: 1px solid rgb(83 198 243);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
}
|
||||
.eq-name-badge {
|
||||
font-size: 1.4vh;
|
||||
color: #00e5ff;
|
||||
font-weight: bold;
|
||||
}
|
||||
.interval-tip {
|
||||
font-size: 1.1vh;
|
||||
color: rgba(149,255,255,0.5);
|
||||
margin-left: auto;
|
||||
}
|
||||
.eq-dots {
|
||||
display: flex;
|
||||
gap: 6px;
|
||||
padding: 4px 16px;
|
||||
}
|
||||
.eq-dot {
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
border-radius: 50%;
|
||||
background: rgba(83, 198, 243, 0.25);
|
||||
border: 1px solid rgba(83, 198, 243, 0.5);
|
||||
display: inline-block;
|
||||
}
|
||||
.eq-dot.active {
|
||||
background: #00e5ff;
|
||||
box-shadow: 0 0 6px #00e5ff;
|
||||
}
|
||||
.curve-stats {
|
||||
display: flex;
|
||||
gap: 20px;
|
||||
padding: 4px 16px 8px;
|
||||
font-size: 1.2vh;
|
||||
color: #7ecfcf;
|
||||
border-top: 1px solid rgba(83, 198, 243, 0.2);
|
||||
}
|
||||
.curve-stats em { font-style: normal; font-weight: bold; }
|
||||
.val-cur { color: #00e5ff; }
|
||||
.val-max { color: #F8B448; }
|
||||
.val-min { color: #56D0E3; }
|
||||
</style>
|
||||
Loading…
Reference in New Issue