feat(mes): 添加定时同步可生产数量到WMS及BOM项相关标准字段

- 在main.go中添加定时任务每10分钟同步可生产数量到WMS系统
- 为BomItem实体添加relatedStandard字段及相关CRUD方法
- 为InspectionRecord实体添加reportNo、materialCode、materialName等字段
- 更新ent schema确保新字段的验证和默认值设置
- 添加必要的数据库迁移和字段映射逻辑
This commit is contained in:
SunYF
2026-09-17 12:49:07 +08:00
parent b4b274301d
commit 1cc93795ce
191 changed files with 24504 additions and 1250 deletions
@@ -10,25 +10,28 @@ import {
getSyncStats,
getTaskCurrent,
getTighteningList,
getWip,
issueLineMove,
occupyLinePoint
occupyLinePoint,
submitTorque
} from '../api'
import { getUser } from '../utils/request'
export const state = reactive({
stationNo: 0,
screwCount: 0,
orderNo: '',
currentSn: '',
task: null,
points: [],
moves: [],
tightRows: [],
wip: [],
bindPanel: null,
stats: { pendingTorque: 0, pendingQueue: 0 },
stepValues: {},
stepChecks: {},
bindInputs: {},
startedAt: '',
lastError: ''
})
@@ -44,16 +47,41 @@ export const orderNo = computed({
set: (v) => { state.orderNo = v || '' }
})
export const stationNo = computed(() => state.stationNo)
export const screwCount = computed(() => state.screwCount)
export const stepValues = computed(() => state.stepValues)
export const stepChecks = computed(() => state.stepChecks)
export const bindInputs = computed(() => state.bindInputs)
export const tightRows = computed(() => state.tightRows)
export const wip = computed(() => state.wip)
// 进工位(上料)时刻——作业时长起点(P0-3),报工时随载荷上传 MES
export const startedAt = computed(() => state.startedAt)
// 当前步骤(5.1 工艺文件高亮):第一个尚未录入的手填步骤;全部录入后回退到首个步骤
export const currentStep = computed(() => {
const ms = manualSteps.value
const pending = ms.find((s) => {
const v = state.stepValues[s.id]
return v === null || v === undefined || v === ''
})
return pending || ms[0] || taskSteps.value[0] || null
})
export const currentStepId = computed(() => currentStep.value?.id || 0)
// ---------- 派生数据 ----------
export const taskSteps = computed(() => (Array.isArray(state.task?.steps) ? state.task.steps : []))
export const manualSteps = computed(() => taskSteps.value.filter((s) => s.collectType === 'MANUAL'))
export const autoSteps = computed(() => taskSteps.value.filter((s) => s.collectType === 'AUTO'))
// 拧紧工艺步骤(需要拧紧枪的步骤):应拧颗数 = 拧紧步骤数量,每个拧紧步骤录入一条扭矩数据
export const torqueSteps = computed(() => autoSteps.value.filter((s) => s.isTorque))
export const torqueStepCount = computed(() => torqueSteps.value.length)
// 某拧紧步骤已录入的记录(tightRows 已按当前工件 SN 过滤,按 stepId 匹配)
export const torqueRowOfStep = (stepId) =>
state.tightRows.find((r) => String(r.stepId) === String(stepId))
export const torqueDoneCount = computed(
() => torqueSteps.value.filter((s) => torqueRowOfStep(s.id)).length
)
export const missingTorqueSteps = computed(
() => torqueSteps.value.filter((s) => !torqueRowOfStep(s.id))
)
export const orderNos = computed(() => (Array.isArray(state.task?.orderNos) ? state.task.orderNos : []))
export const flowName = computed(() => state.task?.flow?.name || '-')
export const flowActive = computed(() => !!state.task?.flowActive)
@@ -74,10 +102,14 @@ export const beltPoints = computed(() =>
export const pointByNo = (no) => state.points.find((p) => p.pointNo === no)
export const autoTorqueOk = computed(() => state.tightRows.filter((r) => r.result === 'OK').length)
// 拧紧完成态:应拧颗数 = 拧紧工艺步骤数;全部拧紧步骤录入且无不合格才算完成。
export const autoTorqueState = computed(() => {
const total = state.tightRows.length
if (total === 0) return { ok: false, text: '拧紧' }
return autoTorqueOk.value >= total ? { ok: true, text: '合格' } : { ok: false, text: '不合格' }
const total = torqueStepCount.value
if (total === 0) return { ok: true, text: '拧紧步骤' }
if (autoNg.value) return { ok: false, text: '不合格' }
const done = torqueDoneCount.value
if (done < total) return { ok: false, text: `待拧紧 ${done}/${total}` }
return { ok: true, text: '合格' }
})
export const manualNg = computed(() =>
manualSteps.value.some((s) => {
@@ -112,7 +144,8 @@ export const canReport = computed(
!bindBlocking.value &&
missingSteps.value.length === 0 &&
missingChecks.value.length === 0 &&
(!autoSteps.value.some((s) => s.isTorque) || autoTorqueState.value.ok)
missingTorqueSteps.value.length === 0 &&
!autoNg.value
)
// ---------- 判定工具 ----------
@@ -152,8 +185,8 @@ export function stepDisplay(s) {
export async function loadConfig() {
try {
const cfg = await getConfig()
if (cfg?.stationNo > 0) state.stationNo = cfg.stationNo
if (cfg?.screwCount > 0) state.screwCount = cfg.screwCount
// 0=上线位、13=下线位为虚拟工位,需允许 stationNo=0(不能用 >0 否则上线位被当未配置)
if (cfg && typeof cfg.stationNo === 'number' && cfg.stationNo >= 0) state.stationNo = cfg.stationNo
} catch { /* 静默 */ }
}
@@ -185,14 +218,46 @@ export async function loadMoves() {
}
}
// loadTight 只加载「当前工件」的拧紧记录(按 SN 过滤):拧紧数据绑定工件,切换工件即切换明细。
export async function loadTight() {
if (!state.currentSn) {
state.tightRows = []
return
}
try {
state.tightRows = (await getTighteningList({ limit: 50 })) || []
const data = await getTighteningList({ sn: state.currentSn, limit: 100 })
state.tightRows = Array.isArray(data) ? data : (data?.list || [])
} catch {
state.tightRows = []
}
}
// submitTorqueStep 录入一条拧紧数据:工件走到拧紧工艺步骤、拧紧枪把扭矩值打入输入框回车时调用。
// OK/NG 由该步骤考核标准(扭矩 RANGE)判定;成功后刷新当前工件拧紧记录,返回判定结果。
export async function submitTorqueStep(step, torque, angle) {
if (!state.currentSn) throw new Error('请先上料工件再拧紧')
const t = Number(torque)
if (isNaN(t)) throw new Error('扭矩值无效')
const torqueCrits = (step?.criteria || []).filter((c) => /扭矩|扭力/.test(c.name || ''))
const useCrits = torqueCrits.length ? torqueCrits : (step?.criteria || [])
const result = useCrits.length && !useCrits.every((c) => criterionOk(c, t)) ? 'NG' : 'OK'
const idx = torqueSteps.value.findIndex((s) => s.id === step.id)
const screwNo = 'S' + (step.seq || idx + 1 || 1)
await submitTorque({
sn: state.currentSn,
workOrderNo: state.orderNo || '',
stepId: String(step.id),
stepName: step.name || '',
screwNo,
torque: t,
angle: Number(angle) || 0,
result,
operator: operatorName()
})
await loadTight()
return result
}
export async function loadBind() {
const code = Number(state.stationNo)
if (!state.currentSn || !code) {
@@ -216,13 +281,28 @@ export async function loadStats() {
} catch { /* 静默 */ }
}
// loadWip 拉取停在本工位的未完工在制品(5.2 默认展示)。
// stationNo 传本工位号;上线位(0)/下线位(13) 面板同样复用。
export async function loadWip(atStation) {
const code = atStation === undefined ? state.stationNo : atStation
try {
const data = await getWip({ stationNo: code })
state.wip = (data && data.list) ? data.list : (Array.isArray(data) ? data : [])
} catch {
state.wip = []
}
}
export function refreshAll() {
return Promise.all([loadTask(), loadTight(), loadStats(), loadPoints()])
return Promise.all([loadTask(), loadTight(), loadStats(), loadPoints(), loadWip()])
}
// 切换工件:清空步骤录入与绑定缓存,重新加载拧紧/绑定
export function setCurrentSn(sn) {
state.currentSn = sn || ''
const next = sn || ''
// 切换到不同工件(或清空)时重置进工位时刻,避免沿用上件的作业起点
if (next !== state.currentSn) state.startedAt = ''
state.currentSn = next
state.stepValues = {}
state.stepChecks = {}
state.bindInputs = {}
@@ -266,3 +346,9 @@ export async function moveOut(sn, toPoint) {
}
export const operatorName = () => user.name || user.username || ''
// markStationArrival 记录「进工位 / 上料到本工位」时刻(作业时长起点,P0-3)。
// 仅在尚未记录时写入,同一工件重复扫码/确认不会重置起点。
export function markStationArrival() {
if (!state.startedAt) state.startedAt = new Date().toISOString()
}