<script setup lang="ts">
|
import { computed, onMounted, ref } from 'vue';
|
import { onBeforeRouteLeave, useRoute } from 'vue-router';
|
import { useUserStore } from '@vben/stores';
|
import { bindBagCodePallet, getBagCode, getBagCodePage } from '#/api/mes/pro/bagCode';
|
import { bindPalletBatch, getPallet, getPalletPage } from '#/api/mes/pro/pallet';
|
import { getBatchPage } from '#/api/mes/pro/batch';
|
import { can } from '#/mobile/access';
|
import { lookup, errorText, writeBlockReason, type TraceResult, type TraceObject } from '#/mobile/api';
|
import Scanner from './scanner.vue';
|
defineOptions({name:'MobileQuery'});
|
const route=useRoute(),key=route.path.split('/')[2]||'bags';
|
const expected={bags:'bag',pallets:'pallet',batches:'batch'}[key];
|
const busy=ref(false),error=ref(''),notice=ref(''),detail=ref<TraceResult>(),rows=ref<TraceObject[]>([]),page=ref(1),total=ref(0),filter=ref('');
|
const binding=ref(false),target=ref<TraceResult>(),bags=ref<TraceResult[]>([]);
|
type PendingBind={kind:string;ids:number[];targetId:number};
|
const pending=ref<PendingBind>();
|
const storageKey='mom-mobile-bind-'+useUserStore().userInfo?.id+'-'+key;
|
const canBind=computed(()=>key==='bags'?can('mes:pro-bag-code:update')&&can('mes:pro-pallet:query'):key==='pallets'&&can('mes:pro-pallet:update')&&can('mes:pro-batch:query'));
|
let appliedFilter='';
|
async function load(reset=false){
|
if(busy.value)return;busy.value=true;error.value='';
|
const nextPage=reset?1:page.value+1;
|
const search=reset?filter.value.trim():appliedFilter;
|
try{
|
const params={pageNo:nextPage,pageSize:20,code:search||undefined};
|
const r=await (key==='bags'?getBagCodePage(params):key==='pallets'?getPalletPage(params):getBatchPage(params));
|
rows.value=reset?r.list:[...rows.value,...r.list];total.value=r.total;page.value=nextPage;
|
appliedFilter=search;
|
}catch(e){error.value=errorText(e);}finally{busy.value=false;}
|
}
|
onMounted(()=>{try{pending.value=JSON.parse(sessionStorage.getItem(storageKey)||'null')||undefined;}catch{sessionStorage.removeItem(storageKey);}load(true);});
|
async function identify(code:string){
|
if(busy.value||pending.value)return;busy.value=true;error.value='';notice.value='';
|
try{
|
const r=await lookup(code);
|
if(binding.value&&writeBlockReason(r))throw new Error(writeBlockReason(r));
|
if(!binding.value){
|
if(r.type!==expected)throw new Error('该码类型与当前查询页面不一致,请使用对应查询入口');
|
detail.value=r;
|
}else if(!target.value){
|
if(r.type!==(key==='bags'?'pallet':'batch'))throw new Error(key==='bags'?'请先扫描目标托盘':'请先扫描目标批次');
|
target.value=r;
|
}else{
|
if(r.type!==expected)throw new Error('请扫描'+(key==='bags'?'袋码':'托盘码'));
|
if(key==='bags'){
|
if(r.data.palletId)throw new Error('该袋码已绑定托盘,请核实后操作');
|
if(r.data.batchId&&target.value.data.batchId&&r.data.batchId!==target.value.data.batchId)throw new Error('袋码与目标托盘批次不一致');
|
if(bags.value.some(b=>b.data.id===r.data.id))throw new Error('本次已经采集该袋码');
|
bags.value.push(r);
|
}else{
|
if(r.data.batchId)throw new Error('该托盘已绑定批次');
|
detail.value=r;
|
}
|
}
|
}catch(e){error.value=errorText(e);}finally{busy.value=false;}
|
}
|
function startBind(){binding.value=true;detail.value=undefined;target.value=undefined;bags.value=[];notice.value='';error.value='';}
|
function clearPending(){pending.value=undefined;sessionStorage.removeItem(storageKey);}
|
async function submitBind(){
|
if(busy.value||!target.value?.data.id)return;
|
const ids=key==='bags'?bags.value.map(b=>b.data.id!):detail.value?.data.id?[detail.value.data.id]:[];
|
if(!ids.length)return;
|
const operation={kind:key,ids,targetId:target.value.data.id};
|
pending.value=operation;sessionStorage.setItem(storageKey,JSON.stringify(operation));busy.value=true;error.value='';
|
try{
|
if(key==='bags')await bindBagCodePallet({ids,palletId:operation.targetId});
|
else await bindPalletBatch({ids,batchId:operation.targetId});
|
clearPending();binding.value=false;bags.value=[];detail.value=undefined;notice.value='绑定成功,已更新对象的绑定关系。';
|
}catch(e){error.value='绑定结果待确认:'+errorText(e)+'。请先核实,勿重复提交。';}
|
finally{busy.value=false;}
|
}
|
async function verifyBind(){
|
if(!pending.value||busy.value)return;busy.value=true;error.value='';
|
try{
|
const p=pending.value;
|
const states=await Promise.all(p.ids.map(id=>p.kind==='bags'?getBagCode(id):getPallet(id)));
|
const all=states.every(s=>p.kind==='bags'?('palletId'in s&&s.palletId===p.targetId):s.batchId===p.targetId);
|
const none=states.every(s=>p.kind==='bags'?(!('palletId'in s)||!s.palletId):!s.batchId);
|
if(all){notice.value='已核实:所有对象绑定成功。';clearPending();binding.value=false;bags.value=[];detail.value=undefined;}
|
else if(none){notice.value='已核实:对象仍未绑定。请重新查询对象后采集提交。';clearPending();binding.value=false;bags.value=[];detail.value=undefined;}
|
else error.value='对象绑定结果不一致,请联系负责人核对。当前保留待确认操作。';
|
}catch(e){error.value=errorText(e);}finally{busy.value=false;}
|
}
|
onBeforeRouteLeave(()=>!busy.value&&(!binding.value||!!pending.value||window.confirm('离开将放弃本次未提交的绑定清单,是否继续?')));
|
</script>
|
<template>
|
<p v-if="notice" class="m-success" role="status">{{notice}}</p>
|
<p v-if="error" class="m-error" role="alert">{{error}}</p>
|
<div v-if="pending" class="m-panel"><h2 class="m-title">绑定结果待确认</h2><p>本次 {{pending.ids.length}} 个对象,目标编号 {{pending.targetId}}。</p><button class="m-primary" :disabled="busy" @click="verifyBind">核实绑定结果</button></div>
|
<template v-else>
|
<template v-if="binding">
|
<h2 class="m-title">{{!target?(key==='bags'?'先扫描目标托盘':'先扫描目标批次'):(key==='bags'?'逐袋采集':'扫描待绑定托盘')}}</h2>
|
<div v-if="target" class="m-panel"><p>目标:{{target.code}}</p><p class="m-muted">{{target.data.itemName||'未录入品种'}} · {{target.data.batchCode||'未绑定批次'}}</p></div>
|
</template>
|
<Scanner :disabled="busy" @code="identify"/>
|
<div v-if="detail&&!binding" class="m-panel">
|
<h2 class="m-title">{{detail.data.itemName||'对象详情'}}</h2>
|
<p v-if="detail.type === 'batch'" class="m-status">{{detail.data.status === 30 ? '已完成' : detail.data.status === 20 ? '生产中' : detail.data.status === 40 ? '已作废' : '草稿'}} · 已入库 {{detail.data.inboundBagQuantity || 0}} 袋</p>
|
<p v-else class="m-status">{{detail.data.inboundTime ? '已入库' : '未入库'}}<span v-if="detail.type === 'pallet'"> · 待入库 {{detail.data.pendingBagQuantity ?? '—'}} 袋</span></p>
|
<p v-if="writeBlockReason(detail)" class="m-warning">{{writeBlockReason(detail)}}</p>
|
<dl><div class="m-row"><dt>码值</dt><dd>{{detail.code}}</dd></div><div class="m-row"><dt>批次</dt><dd>{{detail.data.batchCode||detail.data.tempCode||'未绑定'}}</dd></div><div class="m-row"><dt>托盘</dt><dd>{{detail.data.palletCode||'—'}}</dd></div><div class="m-row"><dt>生产日期</dt><dd>{{detail.data.produceDate||'未录入'}}</dd></div><div class="m-row"><dt>产线</dt><dd>{{detail.data.lineName||'未录入'}}</dd></div></dl>
|
<dl v-if="detail.type==='bag'"><div class="m-row"><dt>追溯码</dt><dd>{{detail.data.code}}</dd></div><div class="m-row"><dt>业务码</dt><dd>{{detail.data.businessCode||'未生成'}}</dd></div></dl>
|
<dl v-else><div class="m-row"><dt>业务码</dt><dd>{{detail.data.businessCode||'未生成'}}</dd></div></dl>
|
</div>
|
<template v-if="binding">
|
<p v-if="key==='bags'" class="m-muted">已采集 {{bags.length}} 袋,尚未提交绑定</p>
|
<article v-for="(bag,i) in bags" :key="bag.data.id" class="m-record"><p>{{bag.code}}</p><button class="m-detail-toggle" :disabled="busy" @click="bags.splice(i,1)">移除</button></article>
|
<p v-if="key==='pallets'&&detail">待绑定托盘:{{detail.code}}</p>
|
<div class="m-action-bar"><button class="m-primary" :disabled="busy||!target||(key==='bags'?!bags.length:!detail)" @click="submitBind">{{busy?'正在绑定…':'确认绑定'}}</button><button class="m-secondary" :disabled="busy" @click="binding=false;bags=[];target=undefined;detail=undefined">取消本次绑定</button></div>
|
</template>
|
<template v-else>
|
<button v-if="canBind" class="m-primary" :disabled="busy" @click="startBind">{{key==='bags'?'开始袋码绑定托盘':'开始托盘绑定批次'}}</button>
|
<h2 class="m-section-title">对象列表</h2>
|
<form class="m-stack" @submit.prevent="load(true)"><label class="m-field"><span>按码值查询</span><input v-model="filter" placeholder="输入码值" /></label><button class="m-secondary" :disabled="busy">查询列表</button></form>
|
<p v-if="!rows.length&&!busy" class="m-empty">暂无匹配对象</p>
|
<button v-for="row in rows" :key="row.id" class="m-feature" style="width:100%;background:transparent;border-top:0;border-left:0;border-right:0;text-align:left" :disabled="busy" @click="identify(row.code||row.tempCode||'')"><div><strong>{{row.itemName||row.code||row.tempCode}}</strong><small>{{row.code||row.tempCode}} · {{row.batchCode||'点击查看详情'}}</small></div></button>
|
<button v-if="rows.length<total" class="m-secondary" :disabled="busy" @click="load()">{{busy?'正在加载…':'加载更多'}}</button>
|
</template>
|
</template>
|
</template>
|