Browse Source

feat(board): 添加IFS移库失败看板功能

- 新增ifsCallErrorLogBoard API接口用于获取看板数据
- 创建ifsTransferErrorBoard.vue组件实现看板展示功能
- 配置路由映射到新的看板页面
- 实现表格数据显示、自动刷新等功能
- 添加重试操作按钮支持手动重试处理
- 设置定时刷新机制每30秒更新一次数据
master
常熟吴彦祖 2 weeks ago
parent
commit
350cc9b497
  1. 582
      src/views/modules/board/ifsTransferErrorBoard.vue
  2. 245
      src/views/modules/board/ifsTransferErrorBoard_old.vue

582
src/views/modules/board/ifsTransferErrorBoard.vue

@ -1,220 +1,438 @@
<template> <template>
<div class="mod-config">
<div style="text-align: center">
<h1>IFS移库失败看板</h1>
<div class="picking-board-screen">
<div class="bg-decoration">
<div class="decoration-line line-1"></div>
<div class="decoration-line line-2"></div>
<div class="decoration-line line-3"></div>
<div class="decoration-circle circle-1"></div>
<div class="decoration-circle circle-2"></div>
</div>
<div class="screen-header">
<div class="header-logo">
<img src="~@/assets/img/cclbai.png" alt="CCL Logo" class="logo-img">
</div>
<div class="header-decoration left"></div>
<div class="header-center">
<div class="title-glow"></div>
<h1 class="screen-title">IFS移库失败看板</h1>
<div class="title-subtitle">IFS Transfer Error</div>
</div>
<div class="header-decoration right"></div>
<div class="header-time">
<div class="time-icon"></div>
<div class="time-text">{{ currentTime }}</div>
</div> </div>
<div class="board2">
<!-- @mouseenter.native="mouseEnter"-->
<!-- @mouseleave.native="mouseLeave"-->
<el-table
:height="height"
:data="tableData"
ref="wt_table"
border
:row-class-name="tableRowClassName"
style="width: 100%;">
<el-table-column
v-for="(item,index) in columnList" :key="index"
:sortable="item.columnSortable"
:prop="item.columnProp"
:header-align="item.headerAlign"
:show-overflow-tooltip="item.showOverflowTooltip"
:align="item.align"
:fixed="item.fixed==''?false:item.fixed"
:min-width="item.columnWidth"
:label="item.columnLabel">
<template slot-scope="scope">
<span v-if="item.columnProp === 'processStatus'">
<el-tag
:type="getStatusType(scope.row.processStatus)"
size="small">
{{ getStatusLabel(scope.row.processStatus) }}
</el-tag>
</span>
<span v-else-if="item.columnProp === 'operate'">
<el-button
v-if="scope.row.processStatus === 'PENDING'"
type="text"
size="small"
@click="showDetailDialog(scope.row)">
查看详情
</el-button>
<!-- rqrq - 使用全局重试状态控制按钮 -->
<el-button
v-if="scope.row.processStatus === 'PENDING'"
type="text"
size="small"
style="color: #E6A23C;"
:loading="retryingRowId === scope.row.id"
:disabled="retryingRowId !== null"
@click="handleRetry(scope.row)">
{{ retryingRowId === scope.row.id ? '重试中...' : '重试' }}
</el-button>
<span v-else>-</span>
</span>
<span v-else>{{ scope.row[item.columnProp] }}</span>
</template>
</el-table-column>
</el-table>
</div> </div>
<div class="screen-content">
<div class="picking-panel">
<div class="panel-table">
<table class="data-table">
<thead>
<tr>
<th style="width: 120px;">物料编号</th>
<th style="width: 120px;">批次号</th>
<th style="width: 100px;">源库位</th>
<th style="width: 100px;">目标库位</th>
<th style="width: 80px;">数量</th>
<th style="min-width: 220px;">标签</th>
<th style="width: 160px;">创建时间</th>
</tr>
</thead>
<tbody>
<tr v-for="(item, idx) in tableData" :key="idx">
<td class="text-center">{{ item.partNo }}</td>
<td class="text-center">{{ item.lotBatchNo }}</td>
<td class="text-center">{{ item.sourceLocation }}</td>
<td class="text-center">{{ item.destLocation }}</td>
<td class="text-center">{{ item.qty }}</td>
<td class="text-center text-break">{{ item.serialNos }}</td>
<td class="text-center">{{ item.createdAt }}</td>
</tr>
</tbody>
</table>
</div>
</div>
</div>
<div class="screen-footer">
<div class="footer-line"></div>
</div>
</div> </div>
</template> </template>
<script> <script>
let rollstop = ''
let rolltimer = ''//
let refresher = '' //
import {
ifsCallErrorLogBoard
} from '@/api/warehouse/ifsCallErrorLog.js'
export default {
name: 'soLiuhuaBoard',
data () {
import { ifsCallErrorLogBoard } from '@/api/warehouse/ifsCallErrorLog.js'
let refresher = null
export default {
name: 'ifsTransferErrorBoard',
data() {
return { return {
currentTime: '',
timeInterval: null,
refreshCheckInterval: null,
pageIndex: 1, pageIndex: 1,
totalPage: 1, totalPage: 1,
height: 200,
tableData: [],
//
// refreshTime: 5,
// rollTime: 5,
// rollPx: 1,
columnList: [
{
columnProp: "partNo",
headerAlign: "center",
align: "center",
columnLabel: "物料编号",
columnWidth: 120,
columnSortable: false,
showOverflowTooltip: true,
fixed: ""
},
{
columnProp: "lotBatchNo",
headerAlign: "center",
align: "center",
columnLabel: "批次号",
columnWidth: 120,
columnSortable: false,
showOverflowTooltip: true,
fixed: ""
},
{
columnProp: "sourceLocation",
headerAlign: "center",
align: "center",
columnLabel: "源库位",
columnWidth: 80,
columnSortable: false,
showOverflowTooltip: true,
fixed: ""
},
{
columnProp: "destLocation",
headerAlign: "center",
align: "center",
columnLabel: "目标库位",
columnWidth: 80,
columnSortable: false,
showOverflowTooltip: true,
fixed: ""
},
{
columnProp: "qty",
headerAlign: "center",
align: "center",
columnLabel: "数量",
columnWidth: 80,
columnSortable: false,
showOverflowTooltip: true,
fixed: ""
tableData: []
}
}, },
{
columnProp: "serialNos",
headerAlign: "center",
align: "center",
columnLabel: "标签",
columnWidth: 260,
columnSortable: false,
showOverflowTooltip: true,
fixed: ""
created() {
this.search()
this.refreshTable()
}, },
{
columnProp: "createdAt",
headerAlign: "center",
align: "center",
columnLabel: "创建时间",
columnWidth: 150,
columnSortable: false,
showOverflowTooltip: true,
fixed: ""
mounted() {
this.updateTime()
this.timeInterval = setInterval(() => {
this.updateTime()
}, 1000)
this.startRefreshCheck()
}, },
],
beforeDestroy() {
if (this.timeInterval) {
clearInterval(this.timeInterval)
}
if (this.refreshCheckInterval) {
clearInterval(this.refreshCheckInterval)
}
if (refresher) {
clearInterval(refresher)
refresher = null
} }
},
mounted () {
this.$nextTick(() => {
this.height = window.innerHeight - 80
})
// this.autoRoll()
}, },
methods: { methods: {
tableRowClassName ({row, rowIndex}) {
return ''
},
search () {
let inData= {number:this.pageIndex};
ifsCallErrorLogBoard(inData).then(({data}) => {
this.tableData = data.rows;
this.totalPage= data.maxPage;
if(this.pageIndex+1>data.maxPage){
this.pageIndex=1
}else {
this.pageIndex=this.pageIndex+1
search() {
const inData = { number: this.pageIndex }
ifsCallErrorLogBoard(inData).then(({ data }) => {
if (data && data.rows) {
this.tableData = data.rows
this.totalPage = data.maxPage != null ? data.maxPage : 1
if (this.pageIndex + 1 > this.totalPage) {
this.pageIndex = 1
} else {
this.pageIndex = this.pageIndex + 1
}
} else {
this.tableData = []
} }
}).catch(() => {
this.tableData = []
}) })
}, },
refreshTable () {
refreshTable() {
if (refresher) {
clearInterval(refresher)
}
refresher = setInterval(() => { refresher = setInterval(() => {
this.search() this.search()
}, 30000) }, 30000)
},
startRefreshCheck() {
this.refreshCheckInterval = setInterval(() => {
this.checkAndRefreshPage()
}, 60000)
this.checkAndRefreshPage()
},
checkAndRefreshPage() {
const now = new Date()
if (now.getHours() === 5 && now.getMinutes() === 10) {
location.reload()
} }
}, },
created () {
this.search()
this.refreshTable()
updateTime() {
const now = new Date()
const year = now.getFullYear()
const month = String(now.getMonth() + 1).padStart(2, '0')
const day = String(now.getDate()).padStart(2, '0')
const hours = String(now.getHours()).padStart(2, '0')
const minutes = String(now.getMinutes()).padStart(2, '0')
const seconds = String(now.getSeconds()).padStart(2, '0')
const weekDays = ['星期日', '星期一', '星期二', '星期三', '星期四', '星期五', '星期六']
const weekDay = weekDays[now.getDay()]
this.currentTime = `${year}-${month}-${day} ${hours}:${minutes}:${seconds} ${weekDay}`
} }
} }
}
</script> </script>
<style >
<style scoped lang="scss">
/* 与 modules/dashboard/robot-picking-material.vue 大屏表格样式一致 - rqrq */
.picking-board-screen {
width: 100vw;
height: 100vh;
background: linear-gradient(135deg, #5f8cc3 0%, #749cc8 100%);
position: relative;
overflow: hidden;
font-family: 'Microsoft YaHei', 'PingFang SC', Arial, sans-serif;
}
.bg-decoration {
display: none;
}
.screen-header {
position: relative;
height: 60px;
display: flex;
align-items: center;
justify-content: center;
padding: 0 40px;
z-index: 10;
border-bottom: 2px solid rgba(23, 179, 163, 0.4);
background: linear-gradient(180deg, rgba(23, 179, 163, 0.08) 0%, transparent 100%);
}
.header-logo {
position: absolute;
left: 20px;
top: 50%;
transform: translateY(-50%);
z-index: 20;
}
.logo-img {
height: 40px;
width: auto;
filter: drop-shadow(0 2px 8px rgba(0, 0, 0, 0.3));
transition: all 0.3s ease;
.board2 .el-table .cell {
line-height: 13px;
&:hover {
filter: drop-shadow(0 4px 12px rgba(23, 179, 163, 0.5));
transform: scale(1.05);
}
}
.header-decoration {
display: none;
}
.header-center {
position: relative;
text-align: center;
}
.title-glow {
display: none;
}
.screen-title {
position: relative;
font-size: 34px;
font-weight: bold;
color: #ffffff;
margin: 0;
letter-spacing: 3px;
text-shadow: 0 0 20px rgba(23, 179, 163, 0.5);
}
.title-subtitle {
font-size: 12px; font-size: 12px;
height: 13px;
padding: 0px;
color: rgba(255, 255, 255, 0.8);
letter-spacing: 2px;
margin-top: 3px;
font-family: Arial, sans-serif;
text-transform: uppercase;
}
.header-time {
position: absolute;
right: 10px;
top: 50%;
transform: translateY(-50%);
display: flex;
align-items: center;
gap: 12px;
padding: 12px 20px;
border-radius: 8px;
backdrop-filter: blur(10px);
}
.time-icon {
font-size: 14px;
color: #17b3a3;
}
.time-text {
font-size: 16px;
color: #ffffff;
font-family: 'Consolas', 'Courier New', monospace;
font-weight: 500;
letter-spacing: 1px;
}
.screen-content {
position: relative;
z-index: 1;
padding: 8px 15px;
height: calc(100vh - 60px);
overflow-y: auto;
&::-webkit-scrollbar {
width: 6px;
}
&::-webkit-scrollbar-track {
background: rgba(23, 179, 163, 0.1);
}
&::-webkit-scrollbar-thumb {
background: rgba(23, 179, 163, 0.5);
border-radius: 3px;
&:hover {
background: rgba(23, 179, 163, 0.7);
}
}
}
.picking-panel {
width: 100%;
height: calc(100vh - 80px);
background: rgba(70, 90, 120, 0.9);
backdrop-filter: blur(10px);
border: 1px solid rgba(23, 179, 163, 0.5);
border-radius: 12px;
box-shadow:
0 8px 32px rgba(0, 0, 0, 0.4),
inset 0 1px 0 rgba(255, 255, 255, 0.1);
overflow: hidden;
transition: all 0.3s ease;
display: flex;
flex-direction: column;
&:hover {
border-color: rgba(23, 179, 163, 0.5);
box-shadow:
0 12px 48px rgba(0, 0, 0, 0.5),
0 0 30px rgba(23, 179, 163, 0.2);
transform: translateY(-2px);
}
}
.panel-table {
padding: 10px;
flex: 1;
overflow-y: auto;
&::-webkit-scrollbar {
width: 6px;
}
&::-webkit-scrollbar-track {
background: rgba(23, 179, 163, 0.05);
} }
.board2 .el-table .success-row {
background: #1bb61b;
&::-webkit-scrollbar-thumb {
background: rgba(23, 179, 163, 0.3);
border-radius: 3px;
&:hover {
background: rgba(23, 179, 163, 0.5);
}
} }
.board2 .el-table .false-row {
/*background: #cbcb14;*/
background: #db1212;
}
.data-table {
width: 100%;
border-collapse: separate;
border-spacing: 0;
thead {
tr {
background: linear-gradient(135deg, rgba(23, 179, 163, 0.25) 0%, rgba(23, 179, 163, 0.15) 100%);
}
th {
padding: 5px 6px;
color: #fcfdfd;
font-size: 16px;
font-weight: bold;
text-align: center;
border-bottom: 2px solid rgba(23, 179, 163, 0.4);
text-shadow: 0 0 10px rgba(100, 216, 203, 0.5);
white-space: nowrap;
&:first-child {
border-top-left-radius: 8px;
}
&:last-child {
border-top-right-radius: 8px;
}
}
}
tbody {
tr {
background: rgba(60, 80, 105, 0.6);
transition: all 0.3s ease;
&:nth-child(even) {
background: rgba(70, 90, 115, 0.7);
}
&:hover {
background: rgba(23, 179, 163, 0.15);
box-shadow: 0 4px 12px rgba(23, 179, 163, 0.2);
}
&:last-child {
td:first-child {
border-bottom-left-radius: 8px;
}
td:last-child {
border-bottom-right-radius: 8px;
}
}
}
td {
padding: 4px 6px;
color: rgba(255, 255, 255, 0.9);
font-size: 15px;
border-bottom: 1px solid rgba(23, 179, 163, 0.15);
&.text-center {
text-align: center;
}
&.text-break {
word-break: break-all;
white-space: normal;
}
}
}
}
.screen-footer {
display: none;
}
@media screen and (max-width: 1600px) {
.screen-title {
font-size: 30px;
letter-spacing: 3px;
}
}
@media screen and (min-width: 2560px) {
.screen-title {
font-size: 38px;
}
.data-table {
thead th {
font-size: 20px;
padding: 6px 12px;
}
tbody td {
font-size: 20px;
padding: 4px 12px;
} }
.board2 .el-table .yellow-row{
background: #ffff00;
} }
}
</style> </style>

245
src/views/modules/board/ifsTransferErrorBoard_old.vue

@ -0,0 +1,245 @@
<template>
<div class="mod-config">
<div style="text-align: center">
<h1>IFS移库失败看板</h1>
</div>
<div class="board2">
<!-- @mouseenter.native="mouseEnter"-->
<!-- @mouseleave.native="mouseLeave"-->
<el-table
:height="height"
:data="tableData"
ref="wt_table"
border
:row-class-name="tableRowClassName"
style="width: 100%;">
<el-table-column
v-for="(item,index) in columnList" :key="index"
:sortable="item.columnSortable"
:prop="item.columnProp"
:header-align="item.headerAlign"
:show-overflow-tooltip="item.showOverflowTooltip"
:align="item.align"
:fixed="item.fixed==''?false:item.fixed"
:min-width="item.columnWidth"
:label="item.columnLabel">
<template slot-scope="scope">
<span v-if="item.columnProp === 'processStatus'">
<el-tag
:type="getStatusType(scope.row.processStatus)"
size="small">
{{ getStatusLabel(scope.row.processStatus) }}
</el-tag>
</span>
<span v-else-if="item.columnProp === 'operate'">
<el-button
v-if="scope.row.processStatus === 'PENDING'"
type="text"
size="small"
@click="showDetailDialog(scope.row)">
查看详情
</el-button>
<!-- rqrq - 使用全局重试状态控制按钮 -->
<el-button
v-if="scope.row.processStatus === 'PENDING'"
type="text"
size="small"
style="color: #E6A23C;"
:loading="retryingRowId === scope.row.id"
:disabled="retryingRowId !== null"
@click="handleRetry(scope.row)">
{{ retryingRowId === scope.row.id ? '重试中...' : '重试' }}
</el-button>
<span v-else>-</span>
</span>
<span v-else>{{ scope.row[item.columnProp] }}</span>
</template>
</el-table-column>
</el-table>
</div>
</div>
</template>
<script>
let rollstop = ''
let rolltimer = ''//
let refresher = '' //
import {
ifsCallErrorLogBoard
} from '@/api/warehouse/ifsCallErrorLog.js'
export default {
name: 'soLiuhuaBoard',
data () {
return {
pageIndex: 1,
totalPage: 1,
height: 200,
tableData: [],
//
// refreshTime: 5,
// rollTime: 5,
// rollPx: 1,
columnList: [
{
columnProp: "partNo",
headerAlign: "center",
align: "center",
columnLabel: "物料编号",
columnWidth: 120,
columnSortable: false,
showOverflowTooltip: true,
fixed: ""
},
{
columnProp: "lotBatchNo",
headerAlign: "center",
align: "center",
columnLabel: "批次号",
columnWidth: 120,
columnSortable: false,
showOverflowTooltip: true,
fixed: ""
},
{
columnProp: "sourceLocation",
headerAlign: "center",
align: "center",
columnLabel: "源库位",
columnWidth: 80,
columnSortable: false,
showOverflowTooltip: true,
fixed: ""
},
{
columnProp: "destLocation",
headerAlign: "center",
align: "center",
columnLabel: "目标库位",
columnWidth: 80,
columnSortable: false,
showOverflowTooltip: true,
fixed: ""
},
{
columnProp: "qty",
headerAlign: "center",
align: "center",
columnLabel: "数量",
columnWidth: 80,
columnSortable: false,
showOverflowTooltip: true,
fixed: ""
},
{
columnProp: "serialNos",
headerAlign: "center",
align: "center",
columnLabel: "标签",
columnWidth: 260,
columnSortable: false,
showOverflowTooltip: true,
fixed: ""
},
{
columnProp: "createdAt",
headerAlign: "center",
align: "center",
columnLabel: "创建时间",
columnWidth: 150,
columnSortable: false,
showOverflowTooltip: true,
fixed: ""
},
],
}
},
mounted () {
this.$nextTick(() => {
this.height = window.innerHeight - 80
})
//
this.startRefreshCheck()
// this.autoRoll()
},
methods: {
tableRowClassName ({row, rowIndex}) {
return ''
},
search () {
let inData= {number:this.pageIndex};
ifsCallErrorLogBoard(inData).then(({data}) => {
this.tableData = data.rows;
this.totalPage= data.maxPage;
if(this.pageIndex+1>data.maxPage){
this.pageIndex=1
}else {
this.pageIndex=this.pageIndex+1
}
})
},
startRefreshCheck() {
console.log('[机械臂拣选看板] 已启动定时刷新检查,将在每天凌晨5:00自动刷新页面')
//
this.refreshCheckInterval = setInterval(() => {
this.checkAndRefreshPage()
}, 60000) // 60 = 1
//
this.checkAndRefreshPage()
},
/**
* 检查当前时间如果是凌晨5点则刷新页面
*/
checkAndRefreshPage() {
const now = new Date()
const hours = now.getHours()
const minutes = now.getMinutes()
// 5:00-5:10
if (hours === 5 && minutes === 10) {
console.log('[机械臂拣选看板] 到达凌晨5:10,自动刷新页面')
location.reload()
}
},
refreshTable () {
refresher = setInterval(() => {
this.search()
}, 30000)
}
},
created () {
this.search()
this.refreshTable()
}
}
</script>
<style >
.board2 .el-table .cell {
line-height: 13px;
font-size: 12px;
height: 13px;
padding: 0px;
}
.board2 .el-table .success-row {
background: #1bb61b;
}
.board2 .el-table .false-row {
/*background: #cbcb14;*/
background: #db1212;
}
.board2 .el-table .yellow-row{
background: #ffff00;
}
</style>
Loading…
Cancel
Save