Files
bj_power/bj_power_workstation/frontend/src/views/Main.vue
T
SunYF d609ff8a9e refactor: 重构工艺工序相关命名与数据模型
1.  全局替换"工序"为"工艺"统一术语,包括页面文案、枚举、注释
2.  重构工单与工件工艺数据模型:
    - 新增工艺组合表flow_material作为备料/齐套唯一源头
    - 新增工位状态表station_state支持自主停单/恢复接单
    - 移除station_process派工表,改用工单工艺组合作为路线唯一源头
    - 替换processCode为flowId作为工艺关联标识
    - 重构工件当前进度字段为currentStationNo
3.  删除冗余的静态备份资源文件
4.  调整物料选择组件默认启用仅显示激活物料
5.  优化工单创建/编辑逻辑,新增工艺组合校验与保存
2026-09-22 11:32:29 +08:00

272 lines
7.1 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="paused" class="pause-banner">
已停止接单{{ pauseReason ? '' + pauseReason : '' }}本工位暂停新的报工提交作业信息不受影响
</div>
</transition>
<!-- 不合格告警横幅 -->
<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" @call-material="tab = 'material'" />
</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,
loadStationState,
loadTight,
loadTask,
loadWip,
orderNo,
paused,
pauseReason,
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 秒
loadStationState() // 停单状态 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;
}
.pause-banner {
background: #f56c6c;
color: #fff;
text-align: center;
padding: 12px;
font-size: 20px;
font-weight: 700;
letter-spacing: 1px;
}
.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>