|
|
@@ -0,0 +1,513 @@
|
|
|
+<template>
|
|
|
+ <div class="container">
|
|
|
+ <div class="task">
|
|
|
+ <div class="top">
|
|
|
+ <div class="nav-bar">
|
|
|
+ <van-nav-bar title="移库任务-上架" left-arrow @click-left="goBack" @click-right="onClickRight" >
|
|
|
+ <template #left>
|
|
|
+ <van-icon name="arrow-left" size="25" />
|
|
|
+ <div style="color: #fff;height: 46px;padding-right:20px;line-height: 46px">返回</div>
|
|
|
+ </template>
|
|
|
+ <template #right>
|
|
|
+ <div class="nav-right" style="color: #fff">重置</div>
|
|
|
+ </template>
|
|
|
+ </van-nav-bar>
|
|
|
+ </div>
|
|
|
+ <div class="content">
|
|
|
+ <div class="take-delivery">
|
|
|
+ <div class="take-info">
|
|
|
+ <div class="take-info-no">
|
|
|
+ <div class="info-no-title">
|
|
|
+ <div>任务号:{{ code || '--' }}</div>
|
|
|
+ <div v-if="taskDetail.expectQuantity">
|
|
|
+ <div><span style="font-size: 13px">任务数:</span><span
|
|
|
+ style="color: #0077ff;font-weight: bold;">{{ taskDetail.doneQuantity || 0
|
|
|
+ }}/{{ taskDetail.expectQuantity || 0 }}</span></div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <van-progress v-if="taskDetail.expectQuantity && taskDetail.doneQuantity/taskDetail.expectQuantity>=0"
|
|
|
+ :percentage="((taskDetail.doneQuantity/taskDetail.expectQuantity)*100).toFixed(2)"
|
|
|
+ stroke-width="4" />
|
|
|
+ <div class="take-barcode">
|
|
|
+ <div style="display: flex;justify-content: space-between;padding-right: 10px;">
|
|
|
+ <div style="flex: 1">
|
|
|
+ <van-notice-bar :background="'none'" :speed="50" :text="tips" />
|
|
|
+ </div>
|
|
|
+ <div>箱规:{{activeBarcode.packId || '--'}}</div>
|
|
|
+ </div>
|
|
|
+ <div class="barcode-input">
|
|
|
+ <van-search
|
|
|
+ ref="searchRef"
|
|
|
+ v-model.lazy="searchBarcode"
|
|
|
+ placeholder="请扫描商品条码"
|
|
|
+ @search="_handlerScan(searchBarcode)"
|
|
|
+ label="商品条码:"
|
|
|
+ left-icon=""
|
|
|
+ :class="[scanType===2?'search-input-barcode':'','van-hairline--bottom']"
|
|
|
+ @focus="scanType=2"
|
|
|
+ autocomplete="off"
|
|
|
+ >
|
|
|
+ </van-search>
|
|
|
+ </div>
|
|
|
+ <div class="barcode-input">
|
|
|
+ <van-search
|
|
|
+ ref="searchRef"
|
|
|
+ v-model.lazy="searchLocation"
|
|
|
+ placeholder="请扫描移入库位"
|
|
|
+ @search="_handlerScan(searchLocation)"
|
|
|
+ label="移入库位:"
|
|
|
+ left-icon=""
|
|
|
+ :class="[scanType===3?'search-input-barcode':'','van-hairline--bottom']"
|
|
|
+ @focus="scanType=3"
|
|
|
+ autocomplete="off"
|
|
|
+ >
|
|
|
+ </van-search>
|
|
|
+ </div>
|
|
|
+ <div class="barcode-input">
|
|
|
+ <van-search
|
|
|
+ ref="numberRef"
|
|
|
+ v-model="count"
|
|
|
+ placeholder="请输入移入数量"
|
|
|
+ type="number"
|
|
|
+ label="移入数量:"
|
|
|
+ left-icon=""
|
|
|
+ autocomplete="off"
|
|
|
+ show-action
|
|
|
+ :min="1"
|
|
|
+ :class="[scanType===4?'search-input-barcode':'','van-hairline--bottom']"
|
|
|
+ @focus="scanType=4"
|
|
|
+ :max="activeBarcode.id?activeBarcode.remainUpQuantity:10000"
|
|
|
+ @search="onConfirm"
|
|
|
+ >
|
|
|
+ <template #action >
|
|
|
+ <div style="display: flex; align-items: center;flex-direction: column;margin-left: 20px" v-if="activeBarcode.id">
|
|
|
+ <div style="font-size: 14px">待移入:{{activeBarcode.remainUpQuantity}}</div>
|
|
|
+ </div>
|
|
|
+ </template>
|
|
|
+ </van-search>
|
|
|
+ </div>
|
|
|
+ <div class="barcode-input" v-if="activeBarcode.id" >
|
|
|
+ <van-search
|
|
|
+ v-model="activeBarcode.name"
|
|
|
+ readonly
|
|
|
+ label="商品名称:"
|
|
|
+ left-icon=""
|
|
|
+ >
|
|
|
+ </van-search>
|
|
|
+ </div>
|
|
|
+ <div class="lot" v-if="activeBarcode.id">
|
|
|
+ <div class="lot-list">
|
|
|
+ <div>生产:{{activeBarcode.productDate}}</div>
|
|
|
+ <div>失效:{{activeBarcode.invalidDate}}</div>
|
|
|
+ <div>质量:{{activeBarcode.quality}}</div>
|
|
|
+ <div>属性仓:{{activeBarcode.attribute}}</div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div class="take-button">
|
|
|
+ <van-button type="primary" size="large" style="height: 36px" @click="onConfirm" >上架</van-button>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <van-divider :style="{ color: '#333', borderColor: '#1989fa', padding: '0 16px',margin:'5px 0' }">推荐库位
|
|
|
+ </van-divider>
|
|
|
+ <div class="move-stock-list">
|
|
|
+ <table class="task-table">
|
|
|
+ <thead>
|
|
|
+ <tr>
|
|
|
+ <th style="width: 100px;">库位</th>
|
|
|
+ <th style="width: 100px;">类型</th>
|
|
|
+ <th style="width: 50px">数量</th>
|
|
|
+ </tr>
|
|
|
+ </thead>
|
|
|
+ <tbody>
|
|
|
+ <tr v-for="(item, index) in locationList" :key="index" v-if="locationList.length>0" >
|
|
|
+ <td>{{ item.location }}</td>
|
|
|
+ <td>{{ locationType[item.type] || item.type }}</td>
|
|
|
+ <td>{{ item.quantity ||'空' }}</td>
|
|
|
+ </tr>
|
|
|
+ <tr v-else>
|
|
|
+ <td colspan="3">
|
|
|
+ <div>暂无数据</div>
|
|
|
+ </td>
|
|
|
+ </tr>
|
|
|
+ </tbody>
|
|
|
+ </table>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <div class="tab-bar">
|
|
|
+ <van-tabbar :fixed="false" route>
|
|
|
+ <van-tabbar-item replace to="/move-list" icon="description">列表</van-tabbar-item>
|
|
|
+ <van-tabbar-item replace @click.prevent="linkTask()" icon="descending">下架</van-tabbar-item>
|
|
|
+ <van-tabbar-item replace to="/move-putaway" icon="ascending">上架</van-tabbar-item>
|
|
|
+ </van-tabbar>
|
|
|
+ </div>
|
|
|
+ </div>
|
|
|
+ <!-- 单据选择-->
|
|
|
+ <van-action-sheet v-model:show="lotBarcodeTrueFalseBy" cancel-text="取消" description="请选择商品批次" close-on-click-action>
|
|
|
+ <van-cell-group>
|
|
|
+ <van-cell v-for="item in matchBarcodeList" @click="setBarcode(item)" >
|
|
|
+ <template #title>
|
|
|
+ {{ item.barcode }}(数量:{{ item.remainUpQuantity }})
|
|
|
+ </template>
|
|
|
+ <template #label>
|
|
|
+ <div>生产日期:{{ item.productDate || '--' }}</div>
|
|
|
+ <div>失效日期:{{ item.invalidDate || '--' }}</div>
|
|
|
+ <div>质量状态:{{ item.quality || '--' }}</div>
|
|
|
+ <div>属性仓:{{ item.attribute || '--' }}</div>
|
|
|
+ </template>
|
|
|
+ </van-cell>
|
|
|
+ </van-cell-group>
|
|
|
+ </van-action-sheet>
|
|
|
+ </div>
|
|
|
+</template>
|
|
|
+<script setup>
|
|
|
+import { ref, computed,onMounted, onUnmounted } from 'vue'
|
|
|
+import { androidFocus, getHeader, goBack, scanError, scanSuccess } from '@/utils/android'
|
|
|
+import { useStore } from '@/store/modules/user'
|
|
|
+import { useRouter, useRoute } from 'vue-router'
|
|
|
+import { getMoveTaskUp, moveTaskDown, moveTaskUp } from '@/api/transferMove/index'
|
|
|
+import { closeLoading, showLoading } from '@/utils/loading'
|
|
|
+import { barcodeToUpperCase } from '@/utils/dataType.js'
|
|
|
+import { showConfirmDialog, showNotify } from 'vant'
|
|
|
+import { getRecommendedLocation } from '@/api/haikang/index'
|
|
|
+import { closeListener, openListener, scanInit } from '@/utils/keydownListener.js'
|
|
|
+
|
|
|
+try {
|
|
|
+ getHeader()
|
|
|
+ androidFocus()
|
|
|
+} catch (error) {
|
|
|
+}
|
|
|
+// 页面初始化
|
|
|
+onMounted(() => {
|
|
|
+ openListener()
|
|
|
+ scanInit(_handlerScan)
|
|
|
+ loadData()
|
|
|
+})
|
|
|
+const router = useRouter()
|
|
|
+const storeUser = useStore()
|
|
|
+const route = useRoute()
|
|
|
+const warehouse = storeUser.warehouse
|
|
|
+const code = route.query.code
|
|
|
+//商品条码
|
|
|
+const searchBarcode = ref('')
|
|
|
+//库位
|
|
|
+const searchLocation = ref('')
|
|
|
+//提示
|
|
|
+const tips=ref('请扫描商品条码')
|
|
|
+//库位类型
|
|
|
+const locationType = {
|
|
|
+ 'EA': '件拣货库位',
|
|
|
+ 'AP': '补充拣货位',
|
|
|
+ 'CS': '箱拣货库位',
|
|
|
+ 'HP': '快拣补货位',
|
|
|
+ 'PC': '箱/件合并拣货库位',
|
|
|
+ 'PT': '播种库位',
|
|
|
+ 'RS': '存储库位',
|
|
|
+ 'SS': '理货站',
|
|
|
+ 'ST': '过渡库位',
|
|
|
+ 'WB': '组装工作区',
|
|
|
+}
|
|
|
+const count = ref('')
|
|
|
+const scanType = ref(2)
|
|
|
+const taskDetail = ref({})
|
|
|
+const taskList = ref([])
|
|
|
+const locationList = ref([])
|
|
|
+//待移入列表
|
|
|
+const orderList = computed(() => {
|
|
|
+ return taskList.value
|
|
|
+ .filter(item => item.remainUpQuantity !== 0)
|
|
|
+})
|
|
|
+//匹配列表
|
|
|
+const matchBarcodeList = ref([])
|
|
|
+//当前条码
|
|
|
+const activeBarcode = ref({})
|
|
|
+//批次选择
|
|
|
+const lotBarcodeTrueFalseBy = ref(false)
|
|
|
+const _handlerScan = async (code) => {
|
|
|
+ if (!code) return
|
|
|
+ const checkBarcode = barcodeToUpperCase(code)
|
|
|
+ if (scanType.value == 2) {
|
|
|
+ const barcode = [...new Set(
|
|
|
+ orderList.value
|
|
|
+ .flatMap(item => [item.barcode, item.barcode2, item.sku])
|
|
|
+ .filter(value => value !== null && value !== '' && value !== undefined),
|
|
|
+ )]
|
|
|
+ count.value=''
|
|
|
+ if (barcode.some(item => barcodeToUpperCase(item) === checkBarcode)) {
|
|
|
+ taskList.value = await barcodeMatching(checkBarcode)
|
|
|
+ matchBarcodeList.value = orderList.value.filter(item => ((barcodeToUpperCase(item.barcode) === checkBarcode || barcodeToUpperCase(item.sku) == checkBarcode || (item.barcode2 ? barcodeToUpperCase(item.barcode2) === checkBarcode : item.barcode2 === checkBarcode))))
|
|
|
+ if (matchBarcodeList.value.length == 1) {
|
|
|
+ activeBarcode.value = matchBarcodeList.value[0]
|
|
|
+ searchLocation.value = ''
|
|
|
+ _getLocation(activeBarcode.value)
|
|
|
+ } else if (matchBarcodeList.value.length > 1) {
|
|
|
+ tips.value='请选择产品批次'
|
|
|
+ scanError()
|
|
|
+ lotBarcodeTrueFalseBy.value = true
|
|
|
+ activeBarcode.value = {}
|
|
|
+ searchLocation.value = ''
|
|
|
+ locationList.value=[]
|
|
|
+ }
|
|
|
+ }else {
|
|
|
+ scanError()
|
|
|
+ tips.value=`${code}-商品条码不匹配,请重新扫描`
|
|
|
+ showNotify({ type: 'warning', duration: 3000, message: `${code}-商品条码不匹配,请重新扫描` })
|
|
|
+ searchBarcode.value = ''
|
|
|
+ searchLocation.value = ''
|
|
|
+ activeBarcode.value = {}
|
|
|
+ locationList.value=[]
|
|
|
+ }
|
|
|
+ }else if(scanType.value == 3){
|
|
|
+ scanSuccess()
|
|
|
+ searchLocation.value= barcodeToUpperCase(checkBarcode)
|
|
|
+ tips.value='请输入移入数量'
|
|
|
+ scanType.value=4
|
|
|
+ count.value=1
|
|
|
+ }
|
|
|
+}
|
|
|
+const onConfirm=()=>{
|
|
|
+ if(!activeBarcode.value.id ){
|
|
|
+ scanError()
|
|
|
+ tips.value='请扫描商品条码'
|
|
|
+ showNotify({ type: 'warning', duration: 3000, message: '请扫描商品条码' })
|
|
|
+ return
|
|
|
+ }
|
|
|
+ if(!searchLocation.value){
|
|
|
+ scanError()
|
|
|
+ tips.value='请扫描移入库位'
|
|
|
+ showNotify({ type: 'warning', duration: 3000, message: '请扫描移入库位' })
|
|
|
+ return
|
|
|
+ }
|
|
|
+ if(!count.value || count.value==0){
|
|
|
+ scanError()
|
|
|
+ tips.value='请输入移入数量'
|
|
|
+ showNotify({ type: 'warning', duration: 3000, message: '请输入移入数量' })
|
|
|
+ return
|
|
|
+ }
|
|
|
+ if(count.value<activeBarcode.value.remainUpQuantity){
|
|
|
+ showConfirmDialog({
|
|
|
+ title: '温馨提示', message: '移入数量小于待移入数量是否确认上架?',
|
|
|
+ }).then(() => {
|
|
|
+ _moveTaskDown()
|
|
|
+ }).catch(() => {
|
|
|
+ scanType.value=4
|
|
|
+ });
|
|
|
+ return
|
|
|
+ }
|
|
|
+ _moveTaskDown()
|
|
|
+}
|
|
|
+//上架
|
|
|
+const _moveTaskDown=()=>{
|
|
|
+ showLoading()
|
|
|
+ const data={id:activeBarcode.value.id,container:'*',doQty:count.value,location:searchLocation.value}
|
|
|
+ moveTaskUp(data).then(res => {
|
|
|
+ showNotify({ type: 'success', duration: 3000, message: '上架成功,正在刷新数据' })
|
|
|
+ loadData()
|
|
|
+ }).catch(err=>{
|
|
|
+ scanError()
|
|
|
+ }).finally(() => {
|
|
|
+
|
|
|
+ closeLoading()
|
|
|
+ })
|
|
|
+}
|
|
|
+const setBarcode=(item)=>{
|
|
|
+ activeBarcode.value=item
|
|
|
+ lotBarcodeTrueFalseBy.value=false;
|
|
|
+ _getLocation(item)
|
|
|
+}
|
|
|
+const _getLocation=(item)=>{
|
|
|
+ const params = { warehouse, lotNum:item.lotNum, owner:item.customerId}
|
|
|
+ showLoading()
|
|
|
+ getRecommendedLocation(params).then(res => {
|
|
|
+ scanSuccess()
|
|
|
+ locationList.value=res.data
|
|
|
+ tips.value='请扫描移入库位'
|
|
|
+ scanType.value=3
|
|
|
+ searchLocation.value=''
|
|
|
+ }).catch(err=>{
|
|
|
+ locationList.value=[]
|
|
|
+ scanError()
|
|
|
+ tips.value=err.message
|
|
|
+ }).finally(f=>{
|
|
|
+ closeLoading()
|
|
|
+ })
|
|
|
+}
|
|
|
+const onClickRight=()=>{
|
|
|
+ showConfirmDialog({
|
|
|
+ title: '温馨提示', message: '是否重置扫描商品数据?',
|
|
|
+ }).then(() => {
|
|
|
+ loadData()
|
|
|
+ }).catch(() => {});
|
|
|
+}
|
|
|
+//条码匹配放到前边
|
|
|
+const barcodeMatching = (checkBarcode) => {
|
|
|
+ const matchingItems = [];
|
|
|
+ const nonMatchingItems = [];
|
|
|
+ taskList.value.forEach(item => {
|
|
|
+ const isMatchingBarcode =
|
|
|
+ barcodeToUpperCase(item.barcode) === checkBarcode ||
|
|
|
+ checkBarcode === barcodeToUpperCase(item.sku) ||
|
|
|
+ (item.barcode2 && barcodeToUpperCase(item.barcode2) === checkBarcode) ||
|
|
|
+ (item.barcode2 === checkBarcode);
|
|
|
+
|
|
|
+ if (isMatchingBarcode) {
|
|
|
+ matchingItems.push(item); // 匹配条形码的项
|
|
|
+ } else {
|
|
|
+ nonMatchingItems.push(item); // 不匹配的项
|
|
|
+ }
|
|
|
+ });
|
|
|
+ return [...matchingItems, ...nonMatchingItems];
|
|
|
+};
|
|
|
+
|
|
|
+const loadData = async () => {
|
|
|
+ if (code == 0) return
|
|
|
+ showLoading()
|
|
|
+ getMoveTaskUp(code).then(res => {
|
|
|
+ taskDetail.value = res.data
|
|
|
+ if (res.data.itemList.length > 0) {
|
|
|
+ // scanSuccess()
|
|
|
+ taskList.value = res.data.itemList
|
|
|
+ activeBarcode.value={}
|
|
|
+ searchLocation.value=''
|
|
|
+ searchBarcode.value=''
|
|
|
+ count.value=''
|
|
|
+ tips.value='请扫描商品条码'
|
|
|
+ locationList.value=[]
|
|
|
+ scanType.value=2
|
|
|
+ }
|
|
|
+ }).catch(err=>{
|
|
|
+ scanError()
|
|
|
+ }).finally(() => {
|
|
|
+ closeLoading()
|
|
|
+ })
|
|
|
+}
|
|
|
+// 进入任务
|
|
|
+const linkTask=()=>{
|
|
|
+ router.push({ name: 'MoveDown', query: { code } });
|
|
|
+}
|
|
|
+onUnmounted(() => {
|
|
|
+ closeListener()
|
|
|
+})
|
|
|
+window.onRefresh = loadData
|
|
|
+</script>
|
|
|
+
|
|
|
+<style scoped lang="sass">
|
|
|
+.container
|
|
|
+ width: 100%
|
|
|
+
|
|
|
+ .task
|
|
|
+ display: flex
|
|
|
+ flex-direction: column
|
|
|
+ height: 100vh
|
|
|
+
|
|
|
+ .top
|
|
|
+ flex: 1
|
|
|
+ display: flex
|
|
|
+ flex-direction: column
|
|
|
+
|
|
|
+ .nav-bar
|
|
|
+ height: 46px
|
|
|
+
|
|
|
+ .content
|
|
|
+ flex: 1
|
|
|
+ font-size: 14px
|
|
|
+ .lot
|
|
|
+ font-size: 12px
|
|
|
+ padding: 5px
|
|
|
+ color: #666
|
|
|
+ .lot-list
|
|
|
+ display: flex
|
|
|
+ justify-content: space-between
|
|
|
+ .take-delivery
|
|
|
+ .take-info
|
|
|
+ padding: 6px 10px
|
|
|
+ background: linear-gradient(160deg, #cfe1ff 20%, white 50%, white 100%)
|
|
|
+ display: flex
|
|
|
+ flex-direction: column
|
|
|
+ text-align: left
|
|
|
+
|
|
|
+ .take-info-no
|
|
|
+ flex: 1
|
|
|
+
|
|
|
+ .info-no-title
|
|
|
+ font-size: 17px
|
|
|
+ font-width: 500
|
|
|
+ display: flex
|
|
|
+ justify-content: space-between
|
|
|
+ align-items: center
|
|
|
+
|
|
|
+ .take-barcode
|
|
|
+ margin-top: 10px
|
|
|
+ text-align: left
|
|
|
+ background: #FFFFFF
|
|
|
+
|
|
|
+ .barcode-input
|
|
|
+ ::v-deep(.van-search)
|
|
|
+ padding: 0
|
|
|
+
|
|
|
+ ::v-deep(.van-search__field)
|
|
|
+ border-bottom: 2px solid #ffffff
|
|
|
+
|
|
|
+ ::v-deep(.van-search__content)
|
|
|
+ background: #fff
|
|
|
+
|
|
|
+ ::v-deep(.van-field__control)
|
|
|
+ font-size: 15px
|
|
|
+
|
|
|
+ ::v-deep(.van-search__label)
|
|
|
+ font-size: 15px
|
|
|
+
|
|
|
+ .search-input-barcode
|
|
|
+ ::v-deep(.van-search__field)
|
|
|
+ border-bottom: 2px solid #0077ff
|
|
|
+ z-index: 2
|
|
|
+
|
|
|
+ .take-button
|
|
|
+ padding: 10px 20px 0 20px
|
|
|
+
|
|
|
+ .move-stock-list
|
|
|
+ width: 100%
|
|
|
+ overflow-y: auto
|
|
|
+ max-height: 60vh
|
|
|
+ min-height: 100px
|
|
|
+
|
|
|
+ .move-button
|
|
|
+ background: #1989fa
|
|
|
+ color: #fff
|
|
|
+ width: 100%
|
|
|
+ height: 30px
|
|
|
+ font-size: 15px
|
|
|
+ line-height: 30px
|
|
|
+ font-weight: bold
|
|
|
+
|
|
|
+ .task-table, .task-table-bin, .task-table-box
|
|
|
+ width: 100%
|
|
|
+ table-layout: fixed
|
|
|
+ border-collapse: collapse
|
|
|
+ font-size: 13px
|
|
|
+
|
|
|
+ .task-table th, .task-table-bin th, .task-table td, .task-table-bin td, .task-table-box th, .task-table-box td
|
|
|
+ text-align: center
|
|
|
+ border: 1px solid #ccc
|
|
|
+ word-wrap: break-word
|
|
|
+ word-break: break-all
|
|
|
+
|
|
|
+ .task-table thead, .task-table-bin thead, .task-table-box thead
|
|
|
+ background-color: #3f8dff
|
|
|
+ position: sticky
|
|
|
+ top: 0
|
|
|
+ color: white
|
|
|
+ font-size: 15px
|
|
|
+
|
|
|
+ .task-table-bin thead
|
|
|
+ background-color: #3f8dff
|
|
|
+
|
|
|
+ .task-table-bin tbody
|
|
|
+ background: #cde7ff
|
|
|
+
|
|
|
+ .tab-bar
|
|
|
+ height: 50px
|
|
|
+</style>
|