feat: 重构BOM架构,新增基础设置与虚拟工位管理功能

本次重构删除原有BOM物料清单表,改用工单工艺组合×工艺物料清单作为唯一用料来源;新增系统基础设置表支持WMS地址、日志保留天数等配置,新增虚拟工位作业管理后台接口与前端页签控制功能,同时优化工位号校验逻辑、事件日志自动清理与工单用料查询能力。
This commit is contained in:
SunYF
2026-09-23 11:41:28 +08:00
parent d3eda6b588
commit f274da044c
74 changed files with 3637 additions and 5586 deletions
@@ -90,6 +90,10 @@ export const missingTorqueSteps = computed(
() => torqueSteps.value.filter((s) => !torqueRowOfStep(s.id))
)
export const orderNos = computed(() => (Array.isArray(state.task?.orderNos) ? state.task.orderNos : []))
// 终端可见页签(按工位配置;空=全部显示)。可选值 online/work/material/move/pdf/inspect/offline
export const visibleTabs = computed(() => (Array.isArray(state.task?.tabs) ? state.task.tabs : []))
// 页签是否显示:未配置(空数组)一律显示
export const hasTab = (name) => !visibleTabs.value.length || visibleTabs.value.includes(name)
export const flowName = computed(() => state.task?.flow?.name || '-')
export const flowActive = computed(() => !!state.task?.flowActive)
// 本工位绑定的工艺 IDstation.flowId,随任务下发):装机绑定/报工一律按 flowId 查询与上报
@@ -202,7 +206,8 @@ export async function loadConfig() {
}
export async function loadTask() {
if (!state.stationNo) return
// 0=上线位是合法工位,不能用 falsy 判断,否则上线位永远不拉任务
if (state.stationNo == null || state.stationNo === '') return
try {
state.task = await getTaskCurrent(state.stationNo)
if (orderNos.value.length && !orderNos.value.includes(state.orderNo)) {
@@ -311,7 +316,7 @@ export function refreshAll() {
// loadStationState 拉取本工位当天的停单状态(MES /api/internal/station/state),刷新横幅与按钮。
// 拉取失败保持现状,避免网络抖动误清停单横幅。
export async function loadStationState() {
if (!state.stationNo) return
if (state.stationNo == null || state.stationNo === '') return
try {
const list = await getStationState()
const rows = Array.isArray(list) ? list : (list?.list || [])
@@ -27,7 +27,7 @@
</div>
<div class="field">
<span class="lab">工位</span>
<span class="station-fixed">{{ fixedStation ? '工位 ' + fixedStation : '工位加载中…' }}</span>
<span class="station-fixed">{{ fixedStation != null ? '工位 ' + fixedStation : '工位加载中…' }}</span>
</div>
<el-button class="btn-login" type="primary" size="large" :loading="loading" @click="submit">
@@ -49,14 +49,16 @@ import { setAuth } from '../utils/request'
const router = useRouter()
const username = ref('')
const password = ref('')
const fixedStation = ref(0)
// null=未加载;0=上线位是合法工位,不能用 falsy 判断
const fixedStation = ref(null)
const loading = ref(false)
// 工位由服务端配置文件固定,一个工位一个终端,终端不允许修改工位
onMounted(async () => {
try {
const cfg = await getConfig()
if (cfg?.stationNo > 0) fixedStation.value = cfg.stationNo
// 0=上线位、13=下线位为合法虚拟工位,>=0 都算加载成功
if (typeof cfg?.stationNo === 'number' && cfg.stationNo >= 0) fixedStation.value = cfg.stationNo
} catch {
// 网络异常时保留 0,登录会提示配置未加载
}
@@ -67,7 +69,7 @@ async function submit() {
ElMessage.warning('请输入账号和密码')
return
}
if (!fixedStation.value) {
if (fixedStation.value == null) {
ElMessage.warning('工位配置未加载,请检查服务端配置文件后重启')
return
}
@@ -40,25 +40,25 @@
</transition>
<el-tabs v-model="tab" type="border-card" class="main-tabs">
<el-tab-pane v-if="isOnline" label="上线建档" name="online">
<el-tab-pane v-if="hasTab('online')" label="上线建档" name="online">
<OnlinePanel />
</el-tab-pane>
<el-tab-pane v-if="isOffline" label="下线完工" name="offline">
<el-tab-pane v-if="hasTab('offline')" label="下线完工" name="offline">
<OfflinePanel />
</el-tab-pane>
<el-tab-pane v-if="!isVirtual" label="作业" name="work">
<el-tab-pane v-if="hasTab('work')" label="作业" name="work">
<WorkPanel ref="workRef" @call-material="tab = 'material'" />
</el-tab-pane>
<el-tab-pane label="物料 / 叫料" name="material">
<el-tab-pane v-if="hasTab('material')" label="物料 / 叫料" name="material">
<MaterialPanel />
</el-tab-pane>
<el-tab-pane v-if="!isVirtual" label="移料" name="move">
<el-tab-pane v-if="hasTab('move')" label="移料" name="move">
<MovePanel />
</el-tab-pane>
<el-tab-pane v-if="!isVirtual" label="工艺文件" name="pdf">
<el-tab-pane v-if="hasTab('pdf')" label="工艺文件" name="pdf">
<PdfPanel />
</el-tab-pane>
<el-tab-pane label="巡检 / 异常" name="inspect">
<el-tab-pane v-if="hasTab('inspect')" label="巡检 / 异常" name="inspect">
<InspectPanel />
</el-tab-pane>
</el-tabs>
@@ -71,7 +71,7 @@
</template>
<script setup>
import { computed, onBeforeUnmount, onMounted, ref } from 'vue'
import { computed, onBeforeUnmount, onMounted, ref, watch } from 'vue'
import { useRouter } from 'vue-router'
import { ElMessage } from 'element-plus'
import WorkPanel from './panels/WorkPanel.vue'
@@ -96,6 +96,7 @@ import { stationLogout, syncFlush } from '../api'
import { clearAuth } from '../utils/request'
import {
hasNg,
hasTab,
loadConfig,
loadMoves,
loadPoints,
@@ -112,7 +113,8 @@ import {
sn,
state,
stationNo,
user
user,
visibleTabs
} from '../store/station'
const router = useRouter()
@@ -126,6 +128,13 @@ const isOnline = computed(() => Number(stationNo.value) === 0)
const isOffline = computed(() => Number(stationNo.value) === 13)
const isVirtual = computed(() => isOnline.value || isOffline.value)
// 任务下发页签配置后,当前页签若被隐藏则切到第一个可见页签
watch(visibleTabs, () => {
if (visibleTabs.value.length && !visibleTabs.value.includes(tab.value)) {
tab.value = visibleTabs.value[0]
}
})
// 帮助内容随当前页签切换(全局唯一一个 PageHelp 悬浮按钮)
const HELP_MAP = {
online: helpOnline,
@@ -166,9 +175,9 @@ function logout() {
onMounted(async () => {
tickClock()
await loadConfig()
// 虚拟工位默认落到对应面板;物理工位默认作业页
if (isOnline.value) tab.value = 'online'
else if (isOffline.value) tab.value = 'offline'
// 默认落点:按工位页签配置,优先上线建档/下线完工,否则作业页
if (hasTab('online')) tab.value = 'online'
else if (hasTab('offline')) tab.value = 'offline'
else tab.value = 'work'
refreshAll()
loadMoves()