Files
bj_power/bj_power_workstation/frontend/src/views/Main.vue
T
SunYF 1cc93795ce feat(mes): 添加定时同步可生产数量到WMS及BOM项相关标准字段
- 在main.go中添加定时任务每10分钟同步可生产数量到WMS系统
- 为BomItem实体添加relatedStandard字段及相关CRUD方法
- 为InspectionRecord实体添加reportNo、materialCode、materialName等字段
- 更新ent schema确保新字段的验证和默认值设置
- 添加必要的数据库迁移和字段映射逻辑
2026-09-17 12:49:07 +08:00

252 lines
6.5 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="page">
<!-- 顶部:工位 / 工单 / 工件 / 同步状态 / 用户 -->
<header class="top-bar">
<div class="brand">工位终端</div>
<div class="chip">
<span class="lab">工位</span>
<b>{{ stationNo ? '工位 ' + stationNo + (stationName ? '·' + stationName : '') : '…' }}</b>
</div>
<div class="chip">
<span class="lab">工单</span>
<b>{{ orderNo || '未选择' }}</b>
</div>
<div class="chip">
<span class="lab">工件</span>
<b class="sn">{{ sn || '未上料' }}</b>
</div>
<div class="spacer"></div>
<el-button size="large" :type="pendingTotal ? 'warning' : 'default'" @click="manualFlush">
待同步 {{ pendingTotal }}
</el-button>
<el-button size="large" @click="wlRef?.open()">我的工作量</el-button>
<span class="user-name">{{ user.name || user.username || '-' }}</span>
<span class="clock">{{ clock }}</span>
<el-button size="large" type="danger" plain @click="logout">退出</el-button>
</header>
<!-- 不合格告警横幅 -->
<transition name="fade">
<div v-if="hasNg" class="alert-banner">⚠ 存在不合格,请复检!</div>
</transition>
<el-tabs v-model="tab" type="border-card" class="main-tabs">
<el-tab-pane v-if="isOnline" label="上线建档" name="online">
<OnlinePanel />
</el-tab-pane>
<el-tab-pane v-if="isOffline" label="下线完工" name="offline">
<OfflinePanel />
</el-tab-pane>
<el-tab-pane v-if="!isVirtual" label="作业" name="work">
<WorkPanel ref="workRef" />
</el-tab-pane>
<el-tab-pane label="物料 / 叫料" name="material">
<MaterialPanel />
</el-tab-pane>
<el-tab-pane v-if="!isVirtual" label="移料" name="move">
<MovePanel />
</el-tab-pane>
<el-tab-pane v-if="!isVirtual" label="工艺文件" name="pdf">
<PdfPanel />
</el-tab-pane>
<el-tab-pane label="巡检 / 异常" name="inspect">
<InspectPanel />
</el-tab-pane>
</el-tabs>
<WorkloadDialog ref="wlRef" />
<!-- 全局帮助:右下角悬浮「?」,内容随当前页签切换 -->
<PageHelp :data="helpData" />
</div>
</template>
<script setup>
import { computed, onBeforeUnmount, onMounted, ref } from 'vue'
import { useRouter } from 'vue-router'
import { ElMessage } from 'element-plus'
import WorkPanel from './panels/WorkPanel.vue'
import MaterialPanel from './panels/MaterialPanel.vue'
import MovePanel from './panels/MovePanel.vue'
import PdfPanel from './panels/PdfPanel.vue'
import InspectPanel from './panels/InspectPanel.vue'
import OnlinePanel from './panels/OnlinePanel.vue'
import OfflinePanel from './panels/OfflinePanel.vue'
import WorkloadDialog from './panels/WorkloadDialog.vue'
import PageHelp from '../components/PageHelp.vue'
import {
helpInspect,
helpMaterial,
helpMove,
helpOffline,
helpOnline,
helpPdf,
helpWorkstation
} from '../help'
import { stationLogout, syncFlush } from '../api'
import { clearAuth } from '../utils/request'
import {
hasNg,
loadConfig,
loadMoves,
loadPoints,
loadStats,
loadTight,
loadTask,
loadWip,
orderNo,
pendingTotal,
refreshAll,
sn,
state,
stationNo,
user
} from '../store/station'
const router = useRouter()
const tab = ref('work')
const workRef = ref()
const wlRef = ref()
const clock = ref('')
const stationName = computed(() => state.task?.stationName || '')
// 0=上线位、13=下线位为虚拟工位,分别展示上线建档 / 下线完工面板
const isOnline = computed(() => Number(stationNo.value) === 0)
const isOffline = computed(() => Number(stationNo.value) === 13)
const isVirtual = computed(() => isOnline.value || isOffline.value)
// 帮助内容随当前页签切换(全局唯一一个 PageHelp 悬浮按钮)
const HELP_MAP = {
online: helpOnline,
offline: helpOffline,
work: helpWorkstation,
material: helpMaterial,
move: helpMove,
pdf: helpPdf,
inspect: helpInspect
}
const helpData = computed(() => HELP_MAP[tab.value] || helpWorkstation)
let timerTask = null
let timerTight = null
let timerStats = null
let timerClock = null
function tickClock() {
const d = new Date()
const p = (n) => String(n).padStart(2, '0')
clock.value = `${d.getFullYear()}-${p(d.getMonth() + 1)}-${p(d.getDate())} ${p(d.getHours())}:${p(d.getMinutes())}:${p(d.getSeconds())}`
}
async function manualFlush() {
try {
const data = await syncFlush()
ElMessage.success(`本轮同步成功 ${data?.count ?? 0} 条`)
loadStats()
} catch (e) { /* 统一错误提示 */ }
}
function logout() {
stationLogout({ username: user?.username || '', stationNo: stationNo.value || 0 }).catch(() => {})
clearAuth()
router.push('/login')
}
onMounted(async () => {
tickClock()
await loadConfig()
// 虚拟工位默认落到对应面板;物理工位默认作业页
if (isOnline.value) tab.value = 'online'
else if (isOffline.value) tab.value = 'offline'
else tab.value = 'work'
refreshAll()
loadMoves()
workRef.value?.focusScan?.()
timerTask = setInterval(loadTask, 5000) // 任务 5 秒
timerTight = setInterval(loadTight, 2000) // 拧紧结果 2 秒
timerStats = setInterval(loadStats, 10000) // 同步统计 10 秒
timerClock = setInterval(() => {
tickClock()
loadPoints() // 点位台账 10 秒
loadMoves()
loadWip() // 在制品 10 秒
}, 10000)
})
onBeforeUnmount(() => {
clearInterval(timerTask)
clearInterval(timerTight)
clearInterval(timerStats)
clearInterval(timerClock)
})
</script>
<style scoped>
.page {
min-height: 100vh;
background: #f0f2f5;
}
.top-bar {
display: flex;
align-items: center;
gap: 14px;
padding: 10px 16px;
background: #fff;
box-shadow: 0 1px 6px rgba(0, 21, 41, 0.08);
flex-wrap: wrap;
}
.brand {
font-size: 20px;
font-weight: 700;
color: #1f6feb;
}
.chip {
display: flex;
align-items: baseline;
gap: 6px;
font-size: 15px;
}
.chip .lab {
color: #909399;
}
.chip b.sn {
color: #1f6feb;
}
.spacer {
flex: 1;
}
.user-name {
font-size: 15px;
}
.clock {
font-size: 15px;
color: #606266;
font-variant-numeric: tabular-nums;
}
.alert-banner {
background: #fef0f0;
color: #f56c6c;
text-align: center;
padding: 10px;
font-size: 18px;
font-weight: 700;
}
.main-tabs {
margin: 12px;
min-height: calc(100vh - 140px);
}
.main-tabs :deep(.el-tabs__content) {
padding: 12px;
}
.fade-enter-active,
.fade-leave-active {
transition: opacity 0.3s;
}
.fade-enter-from,
.fade-leave-to {
opacity: 0;
}
</style>