<script setup lang="ts">
|
import { onMounted,ref } from 'vue';
|
import { requestClient } from '#/api/request';
|
import type { MesWmMaterialStockApi } from '#/api/mes/wm/materialstock';
|
import { errorText } from '#/mobile/api';
|
import { getWarehouseSimpleList, type MesWmWarehouseApi } from '#/api/mes/wm/warehouse';
|
import { getWarehouseLocationSimpleList, type MesWmWarehouseLocationApi } from '#/api/mes/wm/warehouse/location';
|
import { getWarehouseAreaSimpleList, type MesWmWarehouseAreaApi } from '#/api/mes/wm/warehouse/area';
|
import Picker from './picker.vue';
|
defineOptions({name:'MobileStock'});
|
const positionLoading=ref(false);
|
let positionRequest=0;
|
const warehouses=ref<MesWmWarehouseApi.Warehouse[]>([]),locations=ref<MesWmWarehouseLocationApi.WarehouseLocation[]>([]),areas=ref<MesWmWarehouseAreaApi.WarehouseArea[]>([]);
|
const warehouseId=ref<number>(),locationId=ref<number>(),areaId=ref<number>();
|
async function changeWarehouse(){
|
const ticket=++positionRequest,id=warehouseId.value;locationId.value=undefined;areaId.value=undefined;locations.value=[];areas.value=[];positionLoading.value=!!id;
|
try{if(id){const list=await getWarehouseLocationSimpleList(id);if(ticket===positionRequest)locations.value=list;}}
|
catch(e){if(ticket===positionRequest)error.value=errorText(e);}finally{if(ticket===positionRequest)positionLoading.value=false;}
|
}
|
async function changeLocation(){
|
const ticket=++positionRequest,id=locationId.value;areaId.value=undefined;areas.value=[];positionLoading.value=!!id;
|
try{if(id){const list=await getWarehouseAreaSimpleList(id);if(ticket===positionRequest)areas.value=list;}}
|
catch(e){if(ticket===positionRequest)error.value=errorText(e);}finally{if(ticket===positionRequest)positionLoading.value=false;}
|
}
|
const keyword=ref(''),batch=ref(''),page=ref(1),busy=ref(false),error=ref(''),total=ref(0),rows=ref<MesWmMaterialStockApi.MaterialStock[]>([]),expanded=ref<number>();
|
type Filters={itemName?:string;batchCode?:string;warehouseId?:number;locationId?:number;areaId?:number};
|
let applied:Filters={};
|
async function load(reset=false){
|
if(busy.value)return;busy.value=true;error.value='';const nextPage=reset?1:page.value+1;
|
const filters=reset?{itemName:keyword.value.trim()||undefined,batchCode:batch.value.trim()||undefined,warehouseId:warehouseId.value,locationId:locationId.value,areaId:areaId.value}:applied;
|
try{
|
const r=await requestClient.get<{list:MesWmMaterialStockApi.MaterialStock[];total:number}>('/mes/wm/material-stock/page',{params:{pageNo:nextPage,pageSize:20,...filters}});
|
rows.value=reset?r.list:[...rows.value,...r.list];total.value=r.total;page.value=nextPage;
|
applied=filters;
|
}catch(e){error.value=errorText(e);}finally{busy.value=false;}
|
}
|
onMounted(async()=>{void load(true);try{warehouses.value=await getWarehouseSimpleList();}catch(e){error.value=errorText(e);}});
|
</script>
|
<template>
|
<form class="m-panel" @submit.prevent="load(true)">
|
<label class="m-field"><span>品种名称</span><input v-model="keyword" placeholder="搜索品种"/></label>
|
<label class="m-field"><span>批次号</span><input v-model="batch" placeholder="可选,输入批次号"/></label>
|
<details><summary class="m-link">按仓库 / 库位筛选</summary>
|
<Picker v-model="warehouseId" label="仓库" :options="warehouses" :disabled="busy" allow-empty placeholder="全部仓库" @update:model-value="changeWarehouse"/>
|
<Picker v-model="locationId" label="库区" :options="locations" :disabled="busy||!warehouseId" :loading="positionLoading" allow-empty placeholder="全部库区" @update:model-value="changeLocation"/>
|
<Picker v-model="areaId" label="库位" :options="areas" :disabled="busy||!locationId" :loading="positionLoading" allow-empty placeholder="全部库位"/>
|
</details>
|
<button class="m-primary" :disabled="busy">{{busy?'正在查询…':'查询库存'}}</button>
|
</form>
|
<p v-if="error" class="m-error" role="alert">{{error}}</p>
|
<p v-if="!rows.length&&!busy" class="m-empty">暂无匹配库存</p>
|
<article v-for="row in rows" :key="row.id" class="m-panel">
|
<h2 class="m-title">{{row.itemName}}</h2><p><strong>{{row.availableQuantity??row.quantity??0}}</strong> {{row.unitMeasureName}} <span class="m-muted">{{row.availableQuantity!=null?'可用':'在库'}}</span></p>
|
<p class="m-muted">{{row.warehouseName}} / {{row.locationName}} / {{row.areaName}}</p><p class="m-muted">批次:{{row.batchCode||'未录入'}}</p>
|
<button class="m-detail-toggle" :aria-expanded="expanded===row.id" @click="expanded=expanded===row.id?undefined:row.id"> {{expanded===row.id?'收起明细':'查看数量明细'}}</button>
|
<dl v-if="expanded===row.id"><div class="m-row"><dt>在库数量</dt><dd>{{row.quantity}} {{row.unitMeasureName}}</dd></div><div class="m-row"><dt>冻结数量</dt><dd>{{row.frozenQuantity??0}}</dd></div><div class="m-row"><dt>占用数量</dt><dd>{{row.reservedQuantity??0}}</dd></div><div class="m-row"><dt>入库时间</dt><dd>{{row.receiptTime||'未录入'}}</dd></div></dl>
|
</article>
|
<button v-if="rows.length<total" class="m-secondary" :disabled="busy" @click="load()">加载更多</button>
|
</template>
|