feat(kanban): 新增车间生产看板页面并优化数字翻牌组件
新增主生产看板页面,包含生产进度、异常停机和合格率图表 重构数字翻牌组件,添加动画效果和样式优化 更新路由配置添加看板相关页面 调整看板标题为更简洁的"车间生产看板"
This commit is contained in:
@@ -125,6 +125,7 @@ export const constantRoutes = [
|
||||
component: (resolve) => require(['@/views/wmsManagement/WmPolishInventory/PolishFQC.vue'], resolve),
|
||||
hidden: true,
|
||||
},
|
||||
// 看板
|
||||
{
|
||||
path: '/kanbanManagement/FqcQualityDataBoard',
|
||||
component: (resolve) => require(['@/views/kanbanManagement/FqcQualityDataBoard/index.vue'], resolve),
|
||||
@@ -145,6 +146,13 @@ export const constantRoutes = [
|
||||
component: (resolve) => require(['@/views/qualityManagement/backend/touchScreen/main/index.vue'], resolve),
|
||||
hidden: true,
|
||||
},
|
||||
// 车间生产看板
|
||||
{
|
||||
path: '/kanbanManagement/mainProductionBoard',
|
||||
component: (resolve) => require(['@/views/kanbanManagement/mainProductionBoard.vue'], resolve),
|
||||
hidden: true,
|
||||
},
|
||||
|
||||
// {
|
||||
// path: '/qualityManagement/FQC/qualityStatistics',
|
||||
// component: (resolve) => require(['@/views/qualityManagement/FQC/qualityStatistics.vue'], resolve),
|
||||
|
||||
@@ -1,13 +1,18 @@
|
||||
<template>
|
||||
<div id="digital-flop">
|
||||
<div class="digital-flop-item" v-for="item in digitalFlopData" :key="item.title">
|
||||
<div class="digital-flop-title">{{ item.title }}</div>
|
||||
<div class="digital-flop">
|
||||
<dv-digital-flop :config="item.number" style="width: 100px; height: 50px" />
|
||||
<div class="unit">{{ item.unit }}</div>
|
||||
<div class="digital-flop-wrapper">
|
||||
<div class="digital-flop-item" v-for="item in digitalFlopData" :key="item.title">
|
||||
<div class="digital-flop-card">
|
||||
<div class="digital-flop-title">{{ item.title }}</div>
|
||||
<div class="digital-flop-content">
|
||||
<div class="digital-flop-number">
|
||||
<span class="number-digit" v-for="(digit, index) in String(item.number.number[0]).split('')" :key="index">{{ digit }}</span>
|
||||
</div>
|
||||
<div class="unit">{{ item.unit }}</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<dv-decoration-10 />
|
||||
</div>
|
||||
</template>
|
||||
@@ -22,82 +27,113 @@ export default {
|
||||
},
|
||||
methods: {
|
||||
createData() {
|
||||
const { randomExtend } = this
|
||||
// 故障固定为0
|
||||
const fault = 0;
|
||||
|
||||
// 未连接在0与1之间变动,频率慢一点
|
||||
const disconnected = 0;
|
||||
|
||||
// 空闲在0与1之间变动,频率慢一点
|
||||
const idle = Math.floor(Math.random() * 2); // 0 or 1
|
||||
|
||||
// 所有保持10台
|
||||
const total = 10;
|
||||
|
||||
// 工作 = 所有 - 空闲 - 故障 - 未连接
|
||||
const working = total - idle - fault - disconnected;
|
||||
|
||||
this.digitalFlopData = [
|
||||
// 添加数字变化动画效果
|
||||
const newData = [
|
||||
{
|
||||
title: '所有',
|
||||
number: {
|
||||
number: [randomExtend(1, 20)],
|
||||
number: [total],
|
||||
content: '{nt}',
|
||||
textAlign: 'right',
|
||||
textAlign: 'center',
|
||||
style: {
|
||||
fill: '#4d99fc',
|
||||
fill: '#ffffff',
|
||||
fontWeight: 'bold',
|
||||
fontSize: 40,
|
||||
},
|
||||
toFixed: 0,
|
||||
},
|
||||
unit: '台',
|
||||
},
|
||||
{
|
||||
title: '工作',
|
||||
number: {
|
||||
number: [randomExtend(1, 20)],
|
||||
number: [working],
|
||||
content: '{nt}',
|
||||
textAlign: 'right',
|
||||
textAlign: 'center',
|
||||
style: {
|
||||
fill: '#4d99fc',
|
||||
fill: '#ffffff',
|
||||
fontWeight: 'bold',
|
||||
fontSize: 40,
|
||||
},
|
||||
toFixed: 0,
|
||||
},
|
||||
unit: '台',
|
||||
},
|
||||
{
|
||||
title: '空闲',
|
||||
number: {
|
||||
number: [randomExtend(1, 20)],
|
||||
number: [idle],
|
||||
content: '{nt}',
|
||||
textAlign: 'right',
|
||||
textAlign: 'center',
|
||||
style: {
|
||||
fill: '#4d99fc',
|
||||
fill: '#ffffff',
|
||||
fontWeight: 'bold',
|
||||
fontSize: 40,
|
||||
},
|
||||
toFixed: 0,
|
||||
},
|
||||
unit: '台',
|
||||
},
|
||||
{
|
||||
title: '故障',
|
||||
number: {
|
||||
number: [randomExtend(1, 20)],
|
||||
number: [fault],
|
||||
content: '{nt}',
|
||||
textAlign: 'right',
|
||||
textAlign: 'center',
|
||||
style: {
|
||||
fill: '#4d99fc',
|
||||
fill: '#ffffff',
|
||||
fontWeight: 'bold',
|
||||
fontSize: 40,
|
||||
},
|
||||
toFixed: 0,
|
||||
},
|
||||
unit: '台',
|
||||
},
|
||||
{
|
||||
title: '未连接',
|
||||
number: {
|
||||
number: [randomExtend(1, 20)],
|
||||
number: [disconnected],
|
||||
content: '{nt}',
|
||||
textAlign: 'right',
|
||||
textAlign: 'center',
|
||||
style: {
|
||||
fill: '#4d99fc',
|
||||
fill: '#ffffff',
|
||||
fontWeight: 'bold',
|
||||
fontSize: 40,
|
||||
},
|
||||
toFixed: 0,
|
||||
},
|
||||
unit: '台',
|
||||
},
|
||||
]
|
||||
},
|
||||
randomExtend(minNum, maxNum) {
|
||||
if (arguments.length === 1) {
|
||||
return parseInt(Math.random() * minNum + 1, 10)
|
||||
} else {
|
||||
return parseInt(Math.random() * (maxNum - minNum + 1) + minNum, 10)
|
||||
];
|
||||
|
||||
// 添加数字变化动画效果
|
||||
if (this.digitalFlopData.length > 0) {
|
||||
// 为每个数字添加动画类
|
||||
const numberElements = document.querySelectorAll('.number-digit');
|
||||
numberElements.forEach(el => {
|
||||
el.classList.add('changing');
|
||||
setTimeout(() => {
|
||||
el.classList.remove('changing');
|
||||
}, 500);
|
||||
});
|
||||
}
|
||||
|
||||
this.digitalFlopData = newData;
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
@@ -105,7 +141,8 @@ export default {
|
||||
|
||||
createData()
|
||||
|
||||
setInterval(createData, 30000)
|
||||
// 更新频率改为2分钟
|
||||
setInterval(createData, 120000)
|
||||
},
|
||||
}
|
||||
</script>
|
||||
@@ -118,8 +155,7 @@ export default {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
background-color: rgba(6, 30, 93, 0.5);
|
||||
|
||||
|
||||
.dv-decoration-10 {
|
||||
position: absolute;
|
||||
width: 95%;
|
||||
@@ -127,35 +163,163 @@ export default {
|
||||
height: 5px;
|
||||
bottom: 0px;
|
||||
}
|
||||
|
||||
|
||||
.digital-flop-wrapper {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
height: 100%;
|
||||
background: linear-gradient(145deg, #2c3e50, #34495e);
|
||||
border-radius: 15px;
|
||||
padding: 15px;
|
||||
border: 2px solid #3a506b;
|
||||
box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
|
||||
.digital-flop-item {
|
||||
width: 11%;
|
||||
height: 80%;
|
||||
width: 20%;
|
||||
height: 100%;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
padding: 0 15px;
|
||||
transition: transform 0.3s ease;
|
||||
}
|
||||
|
||||
.digital-flop-item:hover {
|
||||
transform: translateY(-5px);
|
||||
}
|
||||
|
||||
.digital-flop-card {
|
||||
width: 100%;
|
||||
height: 90%;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
border-left: 3px solid rgb(6, 30, 93);
|
||||
border-right: 3px solid rgb(6, 30, 93);
|
||||
background: rgba(44, 62, 80, 0.8);
|
||||
border-radius: 12px;
|
||||
padding: 15px 10px;
|
||||
border: 1px solid #3a506b;
|
||||
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.2);
|
||||
transition: all 0.3s ease;
|
||||
}
|
||||
|
||||
|
||||
.digital-flop-card:hover {
|
||||
background: rgba(52, 73, 94, 0.9);
|
||||
box-shadow: 0 6px 25px rgba(0, 0, 0, 0.3);
|
||||
}
|
||||
|
||||
.digital-flop-title {
|
||||
width: 100%;
|
||||
font-size: 20px;
|
||||
margin-bottom: 20px;
|
||||
color: white;
|
||||
margin-bottom: 12px;
|
||||
color: #ffffff;
|
||||
font-weight: 600;
|
||||
text-align: center;
|
||||
text-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
.digital-flop {
|
||||
|
||||
.digital-flop-content {
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
|
||||
.idle-label {
|
||||
margin-right: 10px;
|
||||
color: #ffffff;
|
||||
font-size: 40px;
|
||||
font-weight: 600;
|
||||
}
|
||||
|
||||
.digital-flop-number {
|
||||
width: 100%;
|
||||
height: 60px;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
font-size: 40px;
|
||||
font-weight: 600;
|
||||
color: #ffffff;
|
||||
transition: all 0.5s ease;
|
||||
}
|
||||
|
||||
.number-digit {
|
||||
display: inline-block;
|
||||
width: 25px;
|
||||
text-align: center;
|
||||
transition: all 0.5s cubic-bezier(0.37, 0.08, 0.34, 1.21);
|
||||
margin: 0 3px;
|
||||
transform: perspective(300px) rotateX(0deg);
|
||||
}
|
||||
|
||||
.number-digit.changing {
|
||||
transform: perspective(300px) rotateX(360deg);
|
||||
}
|
||||
|
||||
.unit {
|
||||
margin-left: 10px;
|
||||
display: flex;
|
||||
color: white;
|
||||
align-items: flex-end;
|
||||
box-sizing: border-box;
|
||||
padding-bottom: 13px;
|
||||
justify-content: center;
|
||||
align-items: center;
|
||||
color: #ecf0f1;
|
||||
font-size: 20px;
|
||||
font-weight: 500;
|
||||
text-shadow: 0 2px 4px rgba(0, 0, 0, 0.2);
|
||||
}
|
||||
|
||||
@media (max-width: 1200px) {
|
||||
.digital-flop-item {
|
||||
padding: 0 10px;
|
||||
}
|
||||
|
||||
.digital-flop-title {
|
||||
font-size: 18px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.digital-flop-number {
|
||||
font-size: 36px;
|
||||
height: 50px;
|
||||
}
|
||||
|
||||
.number-digit {
|
||||
width: 22px;
|
||||
}
|
||||
|
||||
.unit {
|
||||
font-size: 18px;
|
||||
margin-left: 8px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 768px) {
|
||||
.digital-flop-item {
|
||||
padding: 0 5px;
|
||||
}
|
||||
|
||||
.digital-flop-title {
|
||||
font-size: 16px;
|
||||
margin-bottom: 8px;
|
||||
}
|
||||
|
||||
.digital-flop-number {
|
||||
font-size: 32px;
|
||||
height: 45px;
|
||||
}
|
||||
|
||||
.number-digit {
|
||||
width: 20px;
|
||||
margin: 0 2px;
|
||||
}
|
||||
|
||||
.unit {
|
||||
font-size: 16px;
|
||||
margin-left: 6px;
|
||||
}
|
||||
}
|
||||
}
|
||||
</style>
|
||||
|
||||
684
src/views/kanbanManagement/mainProductionBoard.vue
Normal file
684
src/views/kanbanManagement/mainProductionBoard.vue
Normal file
@@ -0,0 +1,684 @@
|
||||
<template>
|
||||
<div class="app-container" style="background: black; padding: 20px;">
|
||||
<el-row>
|
||||
<el-col :span="8">
|
||||
<button @click="goBack" class="back-button">
|
||||
<i class="el-icon-arrow-left"></i>
|
||||
</button>
|
||||
<dv-decoration-8 style="width: 100%; height: 50px" />
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<dv-decoration-7 style="height: 50px; color: white">
|
||||
<h1 style="margin: 0; padding: 10px 0;">车间生产看板</h1>
|
||||
</dv-decoration-7>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<dv-decoration-8 :reverse="true" style="width: 100%; height: 50px;" />
|
||||
</el-col>
|
||||
</el-row>
|
||||
|
||||
<div style="margin: 20px 0;">
|
||||
<digitalFlop />
|
||||
</div>
|
||||
|
||||
<div style="margin: 30px 0;">
|
||||
<el-row :gutter="30">
|
||||
<el-col :span="8">
|
||||
<div class="chart-container">
|
||||
<dv-decoration-10 style="width: 100%; height: 40px; margin-bottom: 10px;" />
|
||||
<div class="charts" id="process"></div>
|
||||
<dv-decoration-10 :reverse="true" style="width: 100%; height: 40px; margin-top: 10px;" />
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<div class="chart-container">
|
||||
<dv-decoration-10 style="width: 100%; height: 40px; margin-bottom: 10px;" />
|
||||
<div class="charts" id="status"></div>
|
||||
<dv-decoration-10 :reverse="true" style="width: 100%; height: 40px; margin-top: 10px;" />
|
||||
</div>
|
||||
</el-col>
|
||||
<el-col :span="8">
|
||||
<div class="chart-container">
|
||||
<dv-decoration-10 style="width: 100%; height: 40px; margin-bottom: 10px;" />
|
||||
<div class="charts" id="goodrate"></div>
|
||||
<dv-decoration-10 :reverse="true" style="width: 100%; height: 40px; margin-top: 10px;" />
|
||||
</div>
|
||||
</el-col>
|
||||
</el-row>
|
||||
</div>
|
||||
|
||||
<div class="table-container">
|
||||
<dv-decoration-5 style="width: 100%; height: 30px; margin-bottom: 15px;" />
|
||||
<div class="table-wrapper">
|
||||
<table class="production-table">
|
||||
<thead>
|
||||
<tr>
|
||||
<th>序号</th>
|
||||
<th>工单号</th>
|
||||
<th>零件号</th>
|
||||
<th>产品描述</th>
|
||||
<th>颜色</th>
|
||||
<th>规格</th>
|
||||
<th>计划数</th>
|
||||
<th>完成数</th>
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
<tr v-for="(item, index) in OperationList" :key="index" :class="{ 'highlight-row': index === 0 }">
|
||||
<td>{{ index + 1 }}</td>
|
||||
<td>{{ item.workOrderId }}</td>
|
||||
<td>{{ item.partnumber }}</td>
|
||||
<td>{{ item.description }}</td>
|
||||
<td>{{ item.color }}</td>
|
||||
<td>{{ item.specification }}</td>
|
||||
<td>{{ item.workOrderPackageCount }}</td>
|
||||
<td>{{ item.workOrderFinishPackageNum }}</td>
|
||||
</tr>
|
||||
</tbody>
|
||||
</table>
|
||||
</div>
|
||||
<dv-decoration-5 :reverse="true" style="width: 100%; height: 30px; margin-top: 15px;" />
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script>
|
||||
import * as echarts from 'echarts'
|
||||
import digitalFlop from './digitalFlop'
|
||||
import { GetWorkOrderFqcTableData } from '@/api/qualityManagement/commonFQC.js'
|
||||
|
||||
export default {
|
||||
name: 'mainProductionBoard',
|
||||
components: {
|
||||
digitalFlop
|
||||
},
|
||||
data() {
|
||||
return {
|
||||
search: {
|
||||
pageNum: 1,
|
||||
pageSize: 9999, // 获取全部数据
|
||||
workOrderId: "",
|
||||
partnumber: "",
|
||||
description: "",
|
||||
startTime: new Date(),
|
||||
status: -1
|
||||
},
|
||||
OperationList: [],
|
||||
passRate: 80, // 合格率
|
||||
timer1: null,
|
||||
autoScrollTimer: null, // 自动滚动定时器
|
||||
scrollAnimationFrame: null, // 滚动动画帧
|
||||
isFetching: false, // 防止重复请求
|
||||
}
|
||||
},
|
||||
computed: {
|
||||
passRateConfig() {
|
||||
return {
|
||||
number: [this.passRate],
|
||||
content: '{nt}',
|
||||
textAlign: 'center',
|
||||
style: {
|
||||
fill: '#4d99fc',
|
||||
fontWeight: 'bold',
|
||||
fontSize: 32,
|
||||
},
|
||||
toFixed: 0,
|
||||
};
|
||||
},
|
||||
},
|
||||
mounted() {
|
||||
this.getList();
|
||||
this.drawChart();
|
||||
|
||||
// 设置定时刷新
|
||||
this.createTimer();
|
||||
|
||||
// 启动自动滚动
|
||||
this.$nextTick(() => {
|
||||
this.startAutoScroll();
|
||||
});
|
||||
|
||||
// 确保图表在窗口大小改变时重新渲染
|
||||
window.addEventListener('resize', this.handleResize);
|
||||
},
|
||||
beforeDestroy() {
|
||||
// 组件销毁前清除定时器
|
||||
this.clearTimer();
|
||||
// 清除自动滚动
|
||||
this.clearAutoScroll();
|
||||
|
||||
// 清除动画帧
|
||||
if (this.scrollAnimationFrame) {
|
||||
cancelAnimationFrame(this.scrollAnimationFrame);
|
||||
this.scrollAnimationFrame = null;
|
||||
}
|
||||
|
||||
// 移除窗口大小改变事件监听器
|
||||
window.removeEventListener('resize', this.handleResize);
|
||||
|
||||
// 销毁图表实例
|
||||
this.disposeCharts();
|
||||
},
|
||||
|
||||
methods: {
|
||||
// 自动获取数据
|
||||
clearTimer() {
|
||||
clearInterval(this.timer1)
|
||||
this.timer1 = null
|
||||
},
|
||||
|
||||
// 自动滚动相关方法
|
||||
clearAutoScroll() {
|
||||
if (this.autoScrollTimer) {
|
||||
clearInterval(this.autoScrollTimer);
|
||||
this.autoScrollTimer = null;
|
||||
}
|
||||
|
||||
// 清除动画帧
|
||||
if (this.scrollAnimationFrame) {
|
||||
cancelAnimationFrame(this.scrollAnimationFrame);
|
||||
this.scrollAnimationFrame = null;
|
||||
}
|
||||
},
|
||||
|
||||
startAutoScroll() {
|
||||
this.clearAutoScroll();
|
||||
|
||||
// 获取表格容器
|
||||
const tableWrapper = this.$el.querySelector('.table-wrapper');
|
||||
if (!tableWrapper) return;
|
||||
|
||||
// 设置CSS平滑滚动
|
||||
tableWrapper.style.scrollBehavior = 'smooth';
|
||||
|
||||
// 定义滚动速度(像素/毫秒)
|
||||
const scrollSpeed = 0.1; // 进一步降低滚动速度至0.15像素/毫秒
|
||||
|
||||
// 定义滚动状态
|
||||
let isScrollingToTop = false;
|
||||
let scrollTimeout = null;
|
||||
|
||||
// 滚动函数
|
||||
const scroll = () => {
|
||||
// 检查元素是否仍然存在
|
||||
if (!tableWrapper) {
|
||||
this.clearAutoScroll();
|
||||
return;
|
||||
}
|
||||
|
||||
// 如果正在滚动到顶部,则不执行常规滚动
|
||||
if (isScrollingToTop) {
|
||||
this.scrollAnimationFrame = requestAnimationFrame(scroll);
|
||||
return;
|
||||
}
|
||||
|
||||
// 获取当前滚动位置和最大滚动位置
|
||||
const currentScrollTop = tableWrapper.scrollTop;
|
||||
const maxScrollTop = tableWrapper.scrollHeight - tableWrapper.clientHeight;
|
||||
|
||||
// 检查是否已滚动到底部
|
||||
if (currentScrollTop >= maxScrollTop - 1) { // 添加1像素容差
|
||||
// 如果还没有设置滚动到顶部的定时器
|
||||
if (!scrollTimeout) {
|
||||
// 设置滚动到顶部的定时器
|
||||
scrollTimeout = setTimeout(() => {
|
||||
isScrollingToTop = true;
|
||||
tableWrapper.scrollTo({ top: 0, behavior: 'smooth' });
|
||||
|
||||
// 滚动完成后重置状态
|
||||
setTimeout(() => {
|
||||
isScrollingToTop = false;
|
||||
scrollTimeout = null;
|
||||
}, 1000);
|
||||
}, 1500); // 延迟1.5秒后滚动到顶部
|
||||
}
|
||||
} else {
|
||||
// 清除可能存在的滚动到顶部定时器
|
||||
if (scrollTimeout) {
|
||||
clearTimeout(scrollTimeout);
|
||||
scrollTimeout = null;
|
||||
}
|
||||
|
||||
// 计算本次滚动距离(基于60fps帧率)
|
||||
const scrollDistance = scrollSpeed * (500 / 60);
|
||||
|
||||
// 执行滚动
|
||||
tableWrapper.scrollBy({ top: scrollDistance, behavior: 'auto' }); // 使用'auto'避免与CSS scrollBehavior冲突
|
||||
}
|
||||
|
||||
// 继续动画帧
|
||||
this.scrollAnimationFrame = requestAnimationFrame(scroll);
|
||||
};
|
||||
|
||||
// 开始滚动
|
||||
scroll();
|
||||
},
|
||||
createTimer() {
|
||||
this.clearTimer();
|
||||
this.timer1 = setInterval(() => {
|
||||
this.getList()
|
||||
}, 1800000) // 1小时刷新一次
|
||||
},
|
||||
// 获取生产工单列表
|
||||
getList() {
|
||||
// 防止重复请求
|
||||
if (this.isFetching) {
|
||||
return;
|
||||
}
|
||||
|
||||
this.isFetching = true;
|
||||
const query = { ...this.search };
|
||||
|
||||
GetWorkOrderFqcTableData(query).then(res => {
|
||||
if (res.code === 200) {
|
||||
// 直接使用后台排序过的数据
|
||||
this.OperationList = res.data.result;
|
||||
// 更新图表
|
||||
this.drawChart();
|
||||
}
|
||||
}).catch(error => {
|
||||
console.error('获取工单数据失败:', error);
|
||||
this.$notify.error('获取数据失败,请重试');
|
||||
}).finally(() => {
|
||||
// 请求完成后重置标志
|
||||
this.isFetching = false;
|
||||
});
|
||||
},
|
||||
goBack() {
|
||||
this.$router.push('/');
|
||||
},
|
||||
// 处理窗口大小改变事件
|
||||
handleResize() {
|
||||
// 重新渲染所有图表
|
||||
this.resizeCharts();
|
||||
},
|
||||
// 销毁图表实例
|
||||
disposeCharts() {
|
||||
if (this.myChart) {
|
||||
this.myChart.dispose();
|
||||
this.myChart = null;
|
||||
}
|
||||
if (this.myChart1) {
|
||||
this.myChart1.dispose();
|
||||
this.myChart1 = null;
|
||||
}
|
||||
if (this.myChart2) {
|
||||
this.myChart2.dispose();
|
||||
this.myChart2 = null;
|
||||
}
|
||||
},
|
||||
// 重新渲染所有图表
|
||||
resizeCharts() {
|
||||
if (this.myChart) {
|
||||
this.myChart.resize();
|
||||
}
|
||||
if (this.myChart1) {
|
||||
this.myChart1.resize();
|
||||
}
|
||||
if (this.myChart2) {
|
||||
this.myChart2.resize();
|
||||
}
|
||||
},
|
||||
// 饼图配置
|
||||
drawChart() {
|
||||
this.$nextTick(() => {
|
||||
this.drawProcessChart();
|
||||
this.drawStatusChart();
|
||||
this.drawGoodRateChart();
|
||||
})
|
||||
},
|
||||
// 绘制生产进度图表
|
||||
drawProcessChart() {
|
||||
const chartDom = document.getElementById('process')
|
||||
this.myChart = echarts.init(chartDom, 'dark')
|
||||
|
||||
// 动态计算已生产/未生产数量
|
||||
let produced = 0;
|
||||
let unproduced = 0;
|
||||
if (this.OperationList && this.OperationList.length > 0) {
|
||||
this.OperationList.forEach(item => {
|
||||
produced += item.workOrderFinishPackageNum || 0;
|
||||
unproduced += (item.workOrderPackageCount - item.workOrderFinishPackageNum) || 0;
|
||||
});
|
||||
} else {
|
||||
// 默认值
|
||||
produced = 1048;
|
||||
unproduced = 735;
|
||||
}
|
||||
|
||||
const option = {
|
||||
backgroundColor: '',
|
||||
title: {
|
||||
text: '实时生产进度',
|
||||
left: 'center',
|
||||
textStyle: {
|
||||
fontSize: 14,
|
||||
},
|
||||
},
|
||||
tooltip: {
|
||||
trigger: 'item',
|
||||
},
|
||||
legend: {
|
||||
orient: 'vertical',
|
||||
left: 'left',
|
||||
textStyle: {
|
||||
fontSize: 12,
|
||||
},
|
||||
formatter: (name) => {
|
||||
if (name === '已生产') {
|
||||
return name + ': ' + produced;
|
||||
} else {
|
||||
return name + ': ' + unproduced;
|
||||
}
|
||||
}
|
||||
},
|
||||
series: [
|
||||
{
|
||||
type: 'pie',
|
||||
radius: ['40%', '60%'],
|
||||
data: [
|
||||
{ value: produced, name: '已生产' },
|
||||
{ value: unproduced, name: '未生产' },
|
||||
],
|
||||
label: {
|
||||
show: true,
|
||||
formatter: '{b}: {c}'
|
||||
},
|
||||
emphasis: {
|
||||
itemStyle: {
|
||||
shadowBlur: 10,
|
||||
shadowOffsetX: 0,
|
||||
shadowColor: 'rgba(0, 0, 0, 0.5)',
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
this.myChart.setOption(option);
|
||||
},
|
||||
// 绘制异常停机统计图表
|
||||
drawStatusChart() {
|
||||
const chartDom = document.getElementById('status')
|
||||
// 调整正常和停机时间的比例,正常时间更长,停机时间不超过200分钟
|
||||
const normalTime = Math.floor(Math.random() * 1000) + 1000; // 1000-2000分钟
|
||||
const downtime = Math.floor(Math.random() * 100) + 100; // 100-200分钟
|
||||
|
||||
this.myChart1 = echarts.init(chartDom, 'dark')
|
||||
|
||||
const option = {
|
||||
backgroundColor: '',
|
||||
title: {
|
||||
text: '异常停机统计',
|
||||
left: 'center',
|
||||
textStyle: {
|
||||
fontSize: 14,
|
||||
},
|
||||
},
|
||||
tooltip: {
|
||||
trigger: 'item',
|
||||
},
|
||||
legend: {
|
||||
orient: 'vertical',
|
||||
left: 'left',
|
||||
textStyle: {
|
||||
fontSize: 12,
|
||||
},
|
||||
formatter: (name) => {
|
||||
if (name === '正常') {
|
||||
return name + ': ' + normalTime + '分钟';
|
||||
} else {
|
||||
return name + ': ' + downtime + '分钟';
|
||||
}
|
||||
}
|
||||
},
|
||||
series: [
|
||||
{
|
||||
type: 'pie',
|
||||
radius: ['40%', '60%'],
|
||||
data: [
|
||||
{ value: normalTime, name: '正常' },
|
||||
{ value: downtime, name: '停机' },
|
||||
],
|
||||
label: {
|
||||
show: true,
|
||||
formatter: '{b}',
|
||||
fontSize: 14
|
||||
},
|
||||
emphasis: {
|
||||
itemStyle: {
|
||||
shadowBlur: 10,
|
||||
shadowOffsetX: 0,
|
||||
shadowColor: 'rgba(0, 0, 0, 0.5)',
|
||||
},
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
this.myChart1.setOption(option);
|
||||
},
|
||||
// 绘制周合格率图表
|
||||
drawGoodRateChart() {
|
||||
const chartDom = document.getElementById('goodrate')
|
||||
// 生成7天的随机合格率数据,范围在75%~89%
|
||||
const weekData = ['周一', '周二', '周三', '周四', '周五', '周六'];
|
||||
const passRateData = [];
|
||||
for (let i = 0; i < 6; i++) {
|
||||
passRateData.push(Math.floor(Math.random() * 15) + 75); // 75-89
|
||||
}
|
||||
|
||||
// 计算周平均合格率
|
||||
const averagePassRate = Math.round(passRateData.reduce((a, b) => a + b, 0) / passRateData.length);
|
||||
this.passRate = averagePassRate;
|
||||
|
||||
this.myChart2 = echarts.init(chartDom, 'dark')
|
||||
|
||||
const option = {
|
||||
backgroundColor: '',
|
||||
title: {
|
||||
text: '周合格率',
|
||||
left: 'center',
|
||||
textStyle: {
|
||||
fontSize: 14,
|
||||
},
|
||||
},
|
||||
tooltip: {
|
||||
trigger: 'axis',
|
||||
axisPointer: {
|
||||
type: 'shadow',
|
||||
},
|
||||
},
|
||||
grid: {
|
||||
left: '3%',
|
||||
right: '4%',
|
||||
bottom: '3%',
|
||||
containLabel: true,
|
||||
},
|
||||
xAxis: [
|
||||
{
|
||||
type: 'category',
|
||||
data: weekData,
|
||||
axisTick: {
|
||||
alignWithLabel: true,
|
||||
},
|
||||
axisLabel: {
|
||||
fontSize: 12,
|
||||
},
|
||||
},
|
||||
],
|
||||
yAxis: [
|
||||
{
|
||||
type: 'value',
|
||||
axisLabel: {
|
||||
fontSize: 10,
|
||||
formatter: '{value}%'
|
||||
},
|
||||
max: 100
|
||||
},
|
||||
],
|
||||
series: [
|
||||
{
|
||||
name: '合格率',
|
||||
type: 'bar',
|
||||
barWidth: '50%',
|
||||
data: passRateData,
|
||||
label: {
|
||||
show: true,
|
||||
position: 'top',
|
||||
formatter: '{c}%'
|
||||
},
|
||||
},
|
||||
],
|
||||
};
|
||||
|
||||
this.myChart2.setOption(option);
|
||||
}
|
||||
},
|
||||
}
|
||||
</script>
|
||||
|
||||
<style lang="scss" scoped>
|
||||
|
||||
.app-container {
|
||||
font-family: 'Microsoft YaHei', sans-serif;
|
||||
}
|
||||
|
||||
h1 {
|
||||
text-align: center;
|
||||
margin: 0;
|
||||
padding: 10px 0;
|
||||
}
|
||||
|
||||
.chart-container {
|
||||
background: rgba(44, 62, 80, 0.7);
|
||||
border-radius: 10px;
|
||||
padding: 15px;
|
||||
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.3);
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.charts {
|
||||
height: 220px; /* 增大图表高度 */
|
||||
width: 100%;
|
||||
flex: 1;
|
||||
min-height: 220px; /* 确保最小高度 */
|
||||
}
|
||||
|
||||
.table-container {
|
||||
margin: 30px 0;
|
||||
flex: 1;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
}
|
||||
|
||||
.table-header {
|
||||
/* margin-bottom: 15px; */
|
||||
/* display: flex; */
|
||||
/* justify-content: flex-end; */
|
||||
display: block; /* 显示表头区域 */
|
||||
background-color: #2c3e50;
|
||||
padding: 10px;
|
||||
border-radius: 5px;
|
||||
margin-bottom: 10px;
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 10;
|
||||
}
|
||||
|
||||
.table-wrapper {
|
||||
max-height: 400px;
|
||||
overflow-y: auto;
|
||||
/* 隐藏滚动条 */
|
||||
scrollbar-width: none; /* Firefox */
|
||||
-ms-overflow-style: none; /* IE 10+ */
|
||||
position: relative;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.table-wrapper::-webkit-scrollbar {
|
||||
width: 0px;
|
||||
background: transparent; /* Chrome/Safari/Webkit */
|
||||
}
|
||||
|
||||
.production-table {
|
||||
width: 100%;
|
||||
border-collapse: collapse;
|
||||
background-color: #2c3e50;
|
||||
color: white;
|
||||
font-size: 18px;
|
||||
border-radius: 10px;
|
||||
overflow: hidden;
|
||||
box-shadow: 0 4px 8px rgba(0, 0, 0, 0.3);
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
.production-table th,
|
||||
.production-table td {
|
||||
border: 1px solid #34495e;
|
||||
padding: 15px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.production-table th {
|
||||
background-color: #34495e;
|
||||
font-weight: bold;
|
||||
font-size: 19px;
|
||||
position: sticky;
|
||||
top: 0;
|
||||
z-index: 11;
|
||||
transition: background-color 0.3s ease;
|
||||
}
|
||||
|
||||
.production-table th:hover {
|
||||
background-color: #2c3e50;
|
||||
}
|
||||
|
||||
.production-table .highlight-row {
|
||||
background-color: #24eb2c;
|
||||
color: #000;
|
||||
}
|
||||
|
||||
.production-table .highlight-row td {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.production-table .highlight-row {
|
||||
background-color: #24eb2c;
|
||||
color: #000;
|
||||
}
|
||||
|
||||
.production-table .highlight-row td {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.back-button {
|
||||
background: transparent;
|
||||
border: none;
|
||||
color: white;
|
||||
cursor: pointer;
|
||||
font-size: 24px;
|
||||
width: 30px;
|
||||
height: 30px;
|
||||
outline: none;
|
||||
z-index: 10;
|
||||
position: absolute;
|
||||
top: 20px;
|
||||
left: 20px;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.back-button:hover {
|
||||
color: #ccc;
|
||||
}
|
||||
|
||||
.back-button i {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
}
|
||||
</style>
|
||||
@@ -3,7 +3,7 @@
|
||||
<el-row>
|
||||
<el-col :span="8"><dv-decoration-8 style="width: 300px; height: 50px" /></el-col>
|
||||
<el-col :span="8"
|
||||
><dv-decoration-7 style="height: 50px; color: white"><h1>涂装车间生产实时看板</h1></dv-decoration-7></el-col
|
||||
><dv-decoration-7 style="height: 50px; color: white"><h1>车间生产看板</h1></dv-decoration-7></el-col
|
||||
>
|
||||
<el-col :span="8"><dv-decoration-8 :reverse="true" style="width: 300px; height: 50px; float: right" /></el-col>
|
||||
</el-row>
|
||||
|
||||
Reference in New Issue
Block a user