|
@@ -0,0 +1,842 @@
|
|
|
|
|
+<template>
|
|
|
|
|
+ <div class="container">
|
|
|
|
|
+ <van-nav-bar title="智能仓收货" left-arrow fixed placeholder @click-left="goBack">
|
|
|
|
|
+ <template #left>
|
|
|
|
|
+ <van-icon name="arrow-left" size="25" />
|
|
|
|
|
+ <div style="color: #fff">返回</div>
|
|
|
|
|
+ </template>
|
|
|
|
|
+ </van-nav-bar>
|
|
|
|
|
+ <div class="take-delivery">
|
|
|
|
|
+ <div class="take-info">
|
|
|
|
|
+ <div class="take-info-no">
|
|
|
|
|
+ <div class="info-no-title">
|
|
|
|
|
+ <div>任务号:{{ taskInfo.taskNo || '--' }}</div>
|
|
|
|
|
+ <div>
|
|
|
|
|
+ <van-button type="primary" size="mini" plain @click="switchTask">切换任务</van-button>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div class="info-no-tips">
|
|
|
|
|
+ <div>货主:<span style="color: #333;font-weight: bold;">{{ taskInfo.customerName || '--' }}</span></div>
|
|
|
|
|
+ <div>任务数:<span style="color: #0077ff;font-weight: bold;">{{ taskInfo.receivedQty || 0 }}/{{ taskInfo.expectedQty || 0}}</span></div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div class="take-info-number">
|
|
|
|
|
+ <div class="info-number-left">
|
|
|
|
|
+ <div class="number-left-box">
|
|
|
|
|
+ <div>开始时间:</div>
|
|
|
|
|
+ <div class="left-box-title">{{ currentTime }}</div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div class="number-left-box">
|
|
|
|
|
+ <div>已用时:</div>
|
|
|
|
|
+ <div class="left-box-title">{{ formattedTime }}</div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <van-progress v-if="taskInfo.receivedQty/taskInfo.expectedQty>=0" :percentage="((taskInfo.receivedQty/taskInfo.expectedQty)*100).toFixed(2)" stroke-width="4" />
|
|
|
|
|
+ <div class="take-barcode">
|
|
|
|
|
+ <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"
|
|
|
|
|
+ @input="onAsnCancel"
|
|
|
|
|
+ @clear="reset"
|
|
|
|
|
+ >
|
|
|
|
|
+ </van-search>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div class="barcode-input">
|
|
|
|
|
+ <van-search
|
|
|
|
|
+ ref="containerNoRef"
|
|
|
|
|
+ v-model.lazy="containerNo"
|
|
|
|
|
+ placeholder="请扫描库位号"
|
|
|
|
|
+ @search="_handlerScan(containerNo)"
|
|
|
|
|
+ label=" 库位号:"
|
|
|
|
|
+ left-icon=""
|
|
|
|
|
+ :class="[scanType===5?'search-input-barcode':'','van-hairline--bottom']"
|
|
|
|
|
+ @focus="scanType=5"
|
|
|
|
|
+ autocomplete="off"
|
|
|
|
|
+ @input="onAsnCancel"
|
|
|
|
|
+ >
|
|
|
|
|
+ </van-search>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div class="barcode-input">
|
|
|
|
|
+ <van-search
|
|
|
|
|
+ ref="numberRef"
|
|
|
|
|
+ v-model="searchCount"
|
|
|
|
|
+ placeholder="请输入收货数量"
|
|
|
|
|
+ type="number"
|
|
|
|
|
+ label="收货数量:"
|
|
|
|
|
+ left-icon=""
|
|
|
|
|
+ autocomplete="off"
|
|
|
|
|
+ show-action
|
|
|
|
|
+ :min="1"
|
|
|
|
|
+ :max="asnInfo.asnNo?asnInfo.expectedQuantity-asnInfo.receivedQuantity:10000"
|
|
|
|
|
+ @search="isCheck()"
|
|
|
|
|
+ :class="[scanType===4?'search-input-barcode':'','van-hairline--bottom']"
|
|
|
|
|
+ @focus="scanType=4"
|
|
|
|
|
+ >
|
|
|
|
|
+ <template #action>
|
|
|
|
|
+ <div style="display: flex; align-items: center;flex-direction: column;margin-left: 20px"
|
|
|
|
|
+ v-if="asnInfo.asnNo">
|
|
|
|
|
+ <div style="height: 20px;font-size: 12px">已收/预计</div>
|
|
|
|
|
+ <div style="font-size: 16px;font-weight: bold">{{ asnInfo.receivedQuantity}}/{{ asnInfo.expectedQuantity }}
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </template>
|
|
|
|
|
+ </van-search>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div class="take-lot" v-if="lotData.length>0">
|
|
|
|
|
+ <van-cell-group>
|
|
|
|
|
+ <div class="take-lot-title">批次信息</div>
|
|
|
|
|
+ <van-cell v-for="(item,i) in lotData" :key="i" :is-link="item.field!=='lotAtt05'"
|
|
|
|
|
+ @click="onLot(item)">
|
|
|
|
|
+ <template #title>
|
|
|
|
|
+ <van-icon v-if="item.require" name="warning-o" color="#ed6a0c" />
|
|
|
|
|
+ <span class="custom-title">{{ item.label }}</span>
|
|
|
|
|
+ </template>
|
|
|
|
|
+ <template #value>
|
|
|
|
|
+ <div>{{ item.mapping }}</div>
|
|
|
|
|
+ </template>
|
|
|
|
|
+ </van-cell>
|
|
|
|
|
+ </van-cell-group>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <div class="take-button">
|
|
|
|
|
+ <van-button type="primary" size="large" round style="height: 36px" @click="onConfirm">完成收货</van-button>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ </div>
|
|
|
|
|
+ <!-- 条码输入组件 -->
|
|
|
|
|
+ <input-barcode :back="back" @setBarcode="setBarcode" ref="inputBarcodeRef" />
|
|
|
|
|
+ <!-- 单据选择-->
|
|
|
|
|
+ <van-action-sheet v-model:show="asnDetailsTrueFalseBy" cancel-text="取消" description="请选择具体单据" close-on-click-action>
|
|
|
|
|
+ <van-cell-group>
|
|
|
|
|
+ <van-cell v-for="item in asnDetailsList" @click="onDetailActive(item)">
|
|
|
|
|
+ <template #title>
|
|
|
|
|
+ {{ item.asnNo }}({{ item.customerId }}-{{ item.expectedQuantity }}件)
|
|
|
|
|
+ </template>
|
|
|
|
|
+ </van-cell>
|
|
|
|
|
+ </van-cell-group>
|
|
|
|
|
+ </van-action-sheet>
|
|
|
|
|
+ <!-- 商品物理属性-->
|
|
|
|
|
+ <attribute ref="attributeRef" @set-attribute="setAttribute" />
|
|
|
|
|
+ <!-- 商品批次属性-->
|
|
|
|
|
+ <lot-date ref="lotDateRef" @select-lot-date="selectLotDate" />
|
|
|
|
|
+ <!-- 唯一码-->
|
|
|
|
|
+ <unique-code-input ref="uniqueCodeRef"
|
|
|
|
|
+ v-model:uniqueCodeList="uniqueCodeList"
|
|
|
|
|
+ v-model:scanType="scanType"
|
|
|
|
|
+ v-model:checkAllType="checkAllType"
|
|
|
|
|
+ :searchCount="searchCount"
|
|
|
|
|
+ :asnInfo="asnInfo"
|
|
|
|
|
+ @setUniqueCode="onConfirm"
|
|
|
|
|
+
|
|
|
|
|
+ />
|
|
|
|
|
+ <van-action-sheet
|
|
|
|
|
+ v-model:show="lotQualityTrueFalseBy"
|
|
|
|
|
+ cancel-text="取消"
|
|
|
|
|
+ close-on-click-action
|
|
|
|
|
+ description="请选择质量状态"
|
|
|
|
|
+ >
|
|
|
|
|
+ <van-cell-group>
|
|
|
|
|
+ <van-cell v-for="(value,key) in lotQualityMap" @click="onSelectLotQuality(key)">
|
|
|
|
|
+ <template #title>
|
|
|
|
|
+ {{value}}
|
|
|
|
|
+ </template>
|
|
|
|
|
+ </van-cell>
|
|
|
|
|
+ </van-cell-group>
|
|
|
|
|
+ </van-action-sheet>
|
|
|
|
|
+</template>
|
|
|
|
|
+
|
|
|
|
|
+<script setup>
|
|
|
|
|
+import { onMounted, onUnmounted, ref } from 'vue'
|
|
|
|
|
+import { androidFocus, getHeader, goBack, scanError, scanSuccess } from '@/utils/android'
|
|
|
|
|
+import InputBarcode from '@/views/outbound/picking/components/InputBarcode.vue'
|
|
|
|
|
+import { closeListener, openListener, scanInit } from '@/utils/keydownListener'
|
|
|
|
|
+import { useRouter } from 'vue-router'
|
|
|
|
|
+import {
|
|
|
|
|
+ calculateShelfLife, getCommodityRule,
|
|
|
|
|
+ getIReceivingTask,
|
|
|
|
|
+ getProductAttribute, getProductLot,
|
|
|
|
|
+ getReceivingAsnDetails, setBotReceiving,
|
|
|
|
|
+ setProductAttribute,
|
|
|
|
|
+} from '@/api/takeDelivery/index'
|
|
|
|
|
+import { closeLoading, showLoading } from '@/utils/loading'
|
|
|
|
|
+import { useStore } from '@/store/modules/user'
|
|
|
|
|
+import { showNotify, showToast } from 'vant'
|
|
|
|
|
+import { isAttribute } from '@/views/inbound/takeDelivery/task/hooks/attribute'
|
|
|
|
|
+import Attribute from '@/views/inbound/takeDelivery/components/Attribute.vue'
|
|
|
|
|
+import LotDate from '@/views/inbound/takeDelivery/components/LotDate.vue'
|
|
|
|
|
+import UniqueCodeInput from '@/views/inbound/takeDelivery/components/UniqueCodeInput.vue'
|
|
|
|
|
+import { barcodeToUpperCase, toMap } from '@/utils/dataType'
|
|
|
|
|
+import { getCurrentTime } from '@/utils/date'
|
|
|
|
|
+
|
|
|
|
|
+const router = useRouter()
|
|
|
|
|
+const store = useStore()
|
|
|
|
|
+try {
|
|
|
|
|
+ getHeader()
|
|
|
|
|
+ androidFocus()
|
|
|
|
|
+} catch (error) {
|
|
|
|
|
+ router.push('/login')
|
|
|
|
|
+}
|
|
|
|
|
+const warehouse = store.warehouse
|
|
|
|
|
+//开单任务号
|
|
|
|
|
+const taskNo = ref('')
|
|
|
|
|
+//容器号
|
|
|
|
|
+const containerNo = ref('')
|
|
|
|
|
+//商品条码
|
|
|
|
|
+const searchBarcode = ref('')
|
|
|
|
|
+//收货数量
|
|
|
|
|
+const searchCount = ref('')
|
|
|
|
|
+//收货详情
|
|
|
|
|
+const taskInfo = ref({ receivedQty: 0, expectedQty: 0 })
|
|
|
|
|
+//开始时间
|
|
|
|
|
+const currentTime = ref('--')
|
|
|
|
|
+const scanType = ref(2)
|
|
|
|
|
+
|
|
|
|
|
+const type=localStorage.getItem('checkAllType')?JSON.parse(localStorage.getItem('checkAllType')):true
|
|
|
|
|
+const checkAllType=ref(type)
|
|
|
|
|
+// 页面初始化
|
|
|
|
|
+onMounted(() => {
|
|
|
|
|
+ openListener()
|
|
|
|
|
+ scanInit(_handlerScan)
|
|
|
|
|
+ loadData()
|
|
|
|
|
+
|
|
|
|
|
+})
|
|
|
|
|
+/**
|
|
|
|
|
+ * 计算时分秒
|
|
|
|
|
+ */
|
|
|
|
|
+// 时器的总秒数
|
|
|
|
|
+let totalSeconds = ref(0)
|
|
|
|
|
+//时分秒
|
|
|
|
|
+const formattedTime = ref('00:00:00')
|
|
|
|
|
+let windowTimer = null // 计时器的引用
|
|
|
|
|
+const updateFormattedTime = () => {
|
|
|
|
|
+ let hours = Math.floor(totalSeconds.value / 3600)
|
|
|
|
|
+ let minutes = Math.floor((totalSeconds.value % 3600) / 60)
|
|
|
|
|
+ let seconds = totalSeconds.value % 60
|
|
|
|
|
+ formattedTime.value = `${String(hours).padStart(2, '0')}:${String(minutes).padStart(2, '0')}:${String(seconds).padStart(2, '0')}`
|
|
|
|
|
+}
|
|
|
|
|
+// 启动计时器
|
|
|
|
|
+const startTimer = () => {
|
|
|
|
|
+ if (!windowTimer) {
|
|
|
|
|
+ windowTimer = setInterval(() => {
|
|
|
|
|
+ totalSeconds.value++
|
|
|
|
|
+ updateFormattedTime()
|
|
|
|
|
+ }, 1000)
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// 停止计时器
|
|
|
|
|
+const stopTimer = () => {
|
|
|
|
|
+ if (windowTimer) {
|
|
|
|
|
+ clearInterval(windowTimer)
|
|
|
|
|
+ windowTimer = null
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+const back = ref(true)
|
|
|
|
|
+const inputBarcodeType = ref('task')
|
|
|
|
|
+//输入框组件
|
|
|
|
|
+const inputBarcodeRef = ref(null)
|
|
|
|
|
+const oldSearchBarcode = ref('')
|
|
|
|
|
+// 设置容器号
|
|
|
|
|
+const setBarcode = (code, type) => {
|
|
|
|
|
+ if (inputBarcodeType.value === 'lot') {
|
|
|
|
|
+ lotData.value.forEach((lot) => {
|
|
|
|
|
+ if (lot.field == lotField.value) {
|
|
|
|
|
+ lot.mapping = code
|
|
|
|
|
+ }
|
|
|
|
|
+ })
|
|
|
|
|
+ return
|
|
|
|
|
+ }
|
|
|
|
|
+ showLoading()
|
|
|
|
|
+ if(!type){ //切换任务时初始化计时器
|
|
|
|
|
+ stopTimer()
|
|
|
|
|
+ formattedTime.value="00:00:00"
|
|
|
|
|
+ totalSeconds.value=0
|
|
|
|
|
+ }
|
|
|
|
|
+ getIReceivingTask({ taskNo:code,version:'V6' }).then(res=>{
|
|
|
|
|
+ back.value = true
|
|
|
|
|
+ if(res.data.receivedQty==res.data.expectedQty && res.data.expectedQty>0 ){
|
|
|
|
|
+ if(type){
|
|
|
|
|
+ reset()
|
|
|
|
|
+ taskNo.value=''
|
|
|
|
|
+ taskInfo.value={}
|
|
|
|
|
+ switchTask()
|
|
|
|
|
+ }else {
|
|
|
|
|
+ taskInfo.value=res.data
|
|
|
|
|
+ taskNo.value=code
|
|
|
|
|
+ }
|
|
|
|
|
+ containerNo.value=''
|
|
|
|
|
+ stopTimer()
|
|
|
|
|
+ }else {
|
|
|
|
|
+ if(!type){//切换任务成功重启计时器
|
|
|
|
|
+ currentTime.value=getCurrentTime()
|
|
|
|
|
+ startTimer()
|
|
|
|
|
+ containerNo.value=''
|
|
|
|
|
+ }
|
|
|
|
|
+ taskInfo.value=res.data
|
|
|
|
|
+ taskNo.value=code
|
|
|
|
|
+ }
|
|
|
|
|
+ scanType.value=2
|
|
|
|
|
+ uniqueCodeList.value=[]
|
|
|
|
|
+ scanSuccess()
|
|
|
|
|
+ }).catch(err=>{
|
|
|
|
|
+ inputBarcodeRef.value?.show('', '请扫描开单任务号',err.message)
|
|
|
|
|
+ scanError()
|
|
|
|
|
+ }).finally(()=>{
|
|
|
|
|
+ reset()
|
|
|
|
|
+ closeLoading()
|
|
|
|
|
+ })
|
|
|
|
|
+}
|
|
|
|
|
+// setBarcode('BSSH20250605000006')
|
|
|
|
|
+
|
|
|
|
|
+//切换任务
|
|
|
|
|
+const switchTask = () => {
|
|
|
|
|
+ inputBarcodeType.value = 'switchTask'
|
|
|
|
|
+ back.value = false
|
|
|
|
|
+ inputBarcodeRef.value?.show('', `请扫描开单任务号`,'')
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+//asn单多条明细展示
|
|
|
|
|
+const asnDetailsTrueFalseBy = ref(false)
|
|
|
|
|
+const asnDetailsList = ref([])
|
|
|
|
|
+const asnInfo = ref({})
|
|
|
|
|
+const reset = () => {
|
|
|
|
|
+ asnInfo.value = {}
|
|
|
|
|
+ lotData.value = []
|
|
|
|
|
+ searchCount.value = ''
|
|
|
|
|
+ searchBarcode.value = ''
|
|
|
|
|
+ oldSearchBarcode.value = ''
|
|
|
|
|
+ containerNo.value=''
|
|
|
|
|
+ uniqueCodeList.value = []
|
|
|
|
|
+}
|
|
|
|
|
+// 选择单据
|
|
|
|
|
+const onDetailActive = (item) => {
|
|
|
|
|
+ asnInfo.value = item
|
|
|
|
|
+ asnDetailsTrueFalseBy.value = false
|
|
|
|
|
+ searchCount.value=1
|
|
|
|
|
+ scanType.value=5
|
|
|
|
|
+ // searchCount.value = asnInfo.value.expectedQuantity - asnInfo.value.receivedQuantity
|
|
|
|
|
+ _getProductAttribute(item)
|
|
|
|
|
+ _getProductLot(item)
|
|
|
|
|
+ _getCommodityRule(item)
|
|
|
|
|
+}
|
|
|
|
|
+const onAsnCancel = () => {
|
|
|
|
|
+ if (searchBarcode.value === '' || (oldSearchBarcode.value.length != searchBarcode.value.length && oldSearchBarcode.value != '')) {
|
|
|
|
|
+ asnInfo.value = {}
|
|
|
|
|
+ lotData.value = []
|
|
|
|
|
+ searchCount.value = ''
|
|
|
|
|
+ containerNo.value=''
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+const uniqueCodeList = ref([])
|
|
|
|
|
+
|
|
|
|
|
+// 扫描条码监听
|
|
|
|
|
+const _handlerScan = (code) => {
|
|
|
|
|
+ if (scanType.value == 2) {
|
|
|
|
|
+ searchBarcode.value = code
|
|
|
|
|
+ oldSearchBarcode.value = code
|
|
|
|
|
+ const params = { warehouse, barcode: code, asnNos: taskInfo.value?.asnNos.join(',') }
|
|
|
|
|
+ showLoading()
|
|
|
|
|
+ getReceivingAsnDetails(params).then(res => {
|
|
|
|
|
+ uniqueCodeList.value=[]
|
|
|
|
|
+ asnDetailsList.value = res.data
|
|
|
|
|
+ if (res.data.length > 0) {
|
|
|
|
|
+ scanSuccess()
|
|
|
|
|
+ closeLoading()
|
|
|
|
|
+ if (res.data.length == 1) {
|
|
|
|
|
+ const item = res.data[0]
|
|
|
|
|
+ asnInfo.value = item
|
|
|
|
|
+ // searchCount.value = item.expectedQuantity - item.receivedQuantity
|
|
|
|
|
+ searchCount.value=1
|
|
|
|
|
+ _getProductAttribute(item)
|
|
|
|
|
+ _getProductLot(item)
|
|
|
|
|
+ _getCommodityRule(item)
|
|
|
|
|
+ scanType.value=5
|
|
|
|
|
+ }
|
|
|
|
|
+ if (res.data.length > 1) {
|
|
|
|
|
+ asnInfo.value = {}
|
|
|
|
|
+ lotData.value = []
|
|
|
|
|
+ searchCount.value = ''
|
|
|
|
|
+ uniqueCodeList.value = []
|
|
|
|
|
+ asnDetailsTrueFalseBy.value = true
|
|
|
|
|
+ }
|
|
|
|
|
+ } else {
|
|
|
|
|
+ scanError()
|
|
|
|
|
+ showNotify({ type: 'danger', duration: 3000, message: `暂未查询到条码《${code}》信息请重试` })
|
|
|
|
|
+ reset()
|
|
|
|
|
+ closeLoading()
|
|
|
|
|
+ }
|
|
|
|
|
+ }).catch(() => {
|
|
|
|
|
+ scanError()
|
|
|
|
|
+ closeLoading()
|
|
|
|
|
+ })
|
|
|
|
|
+ } else if (scanType.value == 3) {
|
|
|
|
|
+ if (code) {
|
|
|
|
|
+ const uniqueCodeScanType = uniqueCodeRef.value?.uniqueCodeScanType
|
|
|
|
|
+ if (checkAllType.value && uniqueCodeScanType === 'barcode') {
|
|
|
|
|
+ const barcode = Array.from(new Set([asnInfo.value.barcode, asnInfo.value.barcode2, asnInfo.value.sku].filter(Boolean)));
|
|
|
|
|
+ if (barcode.some(item => barcodeToUpperCase(item) === barcodeToUpperCase(code))) {
|
|
|
|
|
+ scanSuccess();
|
|
|
|
|
+ uniqueCodeRef.value.uniqueCodeScanType = 'unique'
|
|
|
|
|
+ uniqueCodeRef.value.uniqueBarcode = code
|
|
|
|
|
+ return;
|
|
|
|
|
+ } else {
|
|
|
|
|
+ showNotify({ type: 'danger', duration: 3000, message: `${code}-商品条码不匹配,请重新扫描` })
|
|
|
|
|
+ uniqueCodeRef.value.uniqueBarcode = ''
|
|
|
|
|
+ scanError();
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ // 如果是通过唯一码扫描,并且没有扫描条码
|
|
|
|
|
+ if (uniqueCodeScanType === 'unique' && ( uniqueCodeRef.value.uniqueBarcode == '' && checkAllType.value)) {
|
|
|
|
|
+ showNotify({ type: 'danger', duration: 3000, message: '请先扫描商品条码' });
|
|
|
|
|
+ uniqueCodeRef.value.uniqueCodeScanType = 'barcode';
|
|
|
|
|
+ scanError();
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ const uniqueRegExp = uniqueRuleMap.value['sku'] ? uniqueRuleMap.value['sku'] : uniqueRuleMap.value['all']
|
|
|
|
|
+ const isValidCode = new RegExp(uniqueRegExp).test(code)
|
|
|
|
|
+ if (!isValidCode) {
|
|
|
|
|
+ scanError()
|
|
|
|
|
+ showNotify({ type: 'danger', duration: 3000, message: `唯一码《${code}》不符合规则、请重新扫描` })
|
|
|
|
|
+ return
|
|
|
|
|
+ }
|
|
|
|
|
+ if (uniqueCodeList.value.includes(code)) {
|
|
|
|
|
+ scanError()
|
|
|
|
|
+ showNotify({ type: 'danger', duration: 3000, message: `唯一码《${code}》已存在列表内请重新扫描` })
|
|
|
|
|
+ return
|
|
|
|
|
+ }
|
|
|
|
|
+ scanSuccess()
|
|
|
|
|
+ uniqueCodeList.value.unshift(code)
|
|
|
|
|
+ uniqueCodeRef.value.uniqueBarcode=''
|
|
|
|
|
+ if(checkAllType.value){
|
|
|
|
|
+ uniqueCodeRef.value.uniqueCodeScanType='barcode'
|
|
|
|
|
+ }else {
|
|
|
|
|
+ uniqueCodeRef.value.uniqueCodeScanType='unique'
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ }
|
|
|
|
|
+ }else if(scanType.value==5){
|
|
|
|
|
+ containerNo.value=barcodeToUpperCase(code)
|
|
|
|
|
+ scanSuccess()
|
|
|
|
|
+ // isCheck()
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+/**
|
|
|
|
|
+ * 物理属性
|
|
|
|
|
+ */
|
|
|
|
|
+const attributeRef = ref(null)
|
|
|
|
|
+const attributeMap = ref({})
|
|
|
|
|
+const attributeTrueFalseBy = ref(true)
|
|
|
|
|
+// 获取商品物理属性
|
|
|
|
|
+const _getProductAttribute = (item) => {
|
|
|
|
|
+ const params = { warehouse: item.warehouse, owner: item.customerId, barcode: item.sku }
|
|
|
|
|
+ getProductAttribute(params).then(res => {
|
|
|
|
|
+ attributeMap.value = res.data
|
|
|
|
|
+ const isAttributeInfo = isAttribute(res.data)
|
|
|
|
|
+ if (isAttributeInfo.length > 0) {
|
|
|
|
|
+ attributeTrueFalseBy.value = false
|
|
|
|
|
+ attributeRef.value?.show(isAttributeInfo, res.data)
|
|
|
|
|
+ } else {
|
|
|
|
|
+ attributeTrueFalseBy.value = true
|
|
|
|
|
+ }
|
|
|
|
|
+ })
|
|
|
|
|
+}
|
|
|
|
|
+// 设置商品物理属性
|
|
|
|
|
+const setAttribute = (data) => {
|
|
|
|
|
+ const params = { warehouse, owner: taskInfo.value.customerId, sku: attributeMap.value.sku }
|
|
|
|
|
+ showLoading()
|
|
|
|
|
+ setProductAttribute(params, { ...attributeMap.value, ...data }).then(res => {
|
|
|
|
|
+ showToast({ duration: 3000, message: '商品物理属性设置成功' })
|
|
|
|
|
+ attributeTrueFalseBy.value = true
|
|
|
|
|
+ scanSuccess()
|
|
|
|
|
+ }).catch(err => {
|
|
|
|
|
+ closeLoading()
|
|
|
|
|
+ scanError()
|
|
|
|
|
+ })
|
|
|
|
|
+}
|
|
|
|
|
+/**
|
|
|
|
|
+ * 物理属性 end
|
|
|
|
|
+ */
|
|
|
|
|
+
|
|
|
|
|
+/**
|
|
|
|
|
+ * 商品批次属性
|
|
|
|
|
+ */
|
|
|
|
|
+// 获取商品批次属性
|
|
|
|
|
+const lotData = ref([])
|
|
|
|
|
+const _getProductLot = (item) => {
|
|
|
|
|
+ const params = { warehouse: item.warehouse, owner: item.customerId, barcode: item.sku }
|
|
|
|
|
+ getProductLot(params).then(res => {
|
|
|
|
|
+ res.data.forEach((lot) => {
|
|
|
|
|
+ const lotField = lot.field
|
|
|
|
|
+ if (lotField.startsWith('lotAtt') && lotField.length === 8) {
|
|
|
|
|
+ lot.mapping = item[lotField]
|
|
|
|
|
+ }
|
|
|
|
|
+ })
|
|
|
|
|
+ lotData.value = res.data
|
|
|
|
|
+ _calculateShelfLife(item, lotData.value)
|
|
|
|
|
+ })
|
|
|
|
|
+}
|
|
|
|
|
+// 计算效期
|
|
|
|
|
+const _calculateShelfLife = (item, lotData) => {
|
|
|
|
|
+ if ((item.lotAtt01 && item.lotAtt02) || (!item.lotAtt01 && !item.lotAtt02)) return
|
|
|
|
|
+ const params = {
|
|
|
|
|
+ customer: item.customerId,
|
|
|
|
|
+ barcode: item.sku,
|
|
|
|
|
+ date: item.lotAtt01 || item.lotAtt02,
|
|
|
|
|
+ isExpiryDate: item.lotAtt01 ? true : false,
|
|
|
|
|
+ }
|
|
|
|
|
+ calculateShelfLife(params).then(date => {
|
|
|
|
|
+ lotData.forEach((lot) => {
|
|
|
|
|
+ if ((lot.field === 'lotAtt01' && !params.isExpiryDate) ||
|
|
|
|
|
+ (lot.field === 'lotAtt02' && params.isExpiryDate)) {
|
|
|
|
|
+ lot.mapping = date.data
|
|
|
|
|
+ }
|
|
|
|
|
+ })
|
|
|
|
|
+ })
|
|
|
|
|
+}
|
|
|
|
|
+//日期选择
|
|
|
|
|
+const lotField = ref('')
|
|
|
|
|
+const lotDateRef = ref(null)
|
|
|
|
|
+const lotQualityTrueFalseBy=ref(false)
|
|
|
|
|
+const lotQualityMap=ref({})
|
|
|
|
|
+const onLot = (item) => {
|
|
|
|
|
+ lotField.value = item.field
|
|
|
|
|
+ if (item.field == 'lotAtt05' ) return
|
|
|
|
|
+ if ( item.field == 'lotAtt08'){
|
|
|
|
|
+ lotQualityMap.value=JSON.parse(item.format)
|
|
|
|
|
+ lotQualityTrueFalseBy.value = true
|
|
|
|
|
+ return
|
|
|
|
|
+ }
|
|
|
|
|
+ if (item.type == 'Date') {
|
|
|
|
|
+ lotDateRef.value?.show(item.label, item.mapping)
|
|
|
|
|
+ return
|
|
|
|
|
+ }
|
|
|
|
|
+ if (item.type == 'String') {
|
|
|
|
|
+ inputBarcodeType.value = 'lot'
|
|
|
|
|
+ back.value = false
|
|
|
|
|
+ inputBarcodeRef.value?.show('', `请扫描${item.label}`,'')
|
|
|
|
|
+ return
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+//设置质量状态
|
|
|
|
|
+const onSelectLotQuality=(key)=>{
|
|
|
|
|
+ lotData.value.forEach((lot) => {
|
|
|
|
|
+ if (lot.field == lotField.value) {
|
|
|
|
|
+ lot.mapping = key
|
|
|
|
|
+ }
|
|
|
|
|
+ })
|
|
|
|
|
+ lotQualityTrueFalseBy.value=false
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// 设置日期
|
|
|
|
|
+const selectLotDate = (date) => {
|
|
|
|
|
+ const lotMap = toMap(lotData.value, 'field', 'mapping')
|
|
|
|
|
+ if ((lotField.value === 'lotAtt01' || lotField.value === 'lotAtt02') &&
|
|
|
|
|
+ lotMap['lotAtt01'] == '' || lotMap['lotAtt01'] == null && lotMap['lotAtt02'] == '' || lotMap['lotAtt02'] == null) {
|
|
|
|
|
+ const params = {
|
|
|
|
|
+ customer: asnInfo.value.customerId,
|
|
|
|
|
+ barcode: asnInfo.value.sku,
|
|
|
|
|
+ date,
|
|
|
|
|
+ isExpiryDate: lotField.value === 'lotAtt01' ? true : false,
|
|
|
|
|
+ }
|
|
|
|
|
+ calculateShelfLife(params).then(res => {
|
|
|
|
|
+ if(res.data){
|
|
|
|
|
+ lotData.value.forEach((lot) => {
|
|
|
|
|
+ if (lot.field === 'lotAtt01') {
|
|
|
|
|
+ lot.mapping = (lotField.value === 'lotAtt01') ? date : res.data
|
|
|
|
|
+ } else if (lot.field === 'lotAtt02') {
|
|
|
|
|
+ lot.mapping = (lotField.value === 'lotAtt02') ? date : res.data
|
|
|
|
|
+ }
|
|
|
|
|
+ })
|
|
|
|
|
+ }else {
|
|
|
|
|
+ lotData.value.forEach((lot) => {
|
|
|
|
|
+ if (lot.field === lotField.value) {
|
|
|
|
|
+ lot.mapping = date
|
|
|
|
|
+ }
|
|
|
|
|
+ })
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ })
|
|
|
|
|
+ }
|
|
|
|
|
+ lotData.value.forEach((lot) => {
|
|
|
|
|
+ if (lot.field === lotField.value) {
|
|
|
|
|
+ lot.mapping = date
|
|
|
|
|
+ }
|
|
|
|
|
+ })
|
|
|
|
|
+ inputBarcodeType.value = 'task'
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+/**
|
|
|
|
|
+ * 商品批次属性end
|
|
|
|
|
+ */
|
|
|
|
|
+/**
|
|
|
|
|
+ * 唯一码
|
|
|
|
|
+ */
|
|
|
|
|
+const uniqueCodeRef = ref(null)
|
|
|
|
|
+//规则列表
|
|
|
|
|
+const uniqueRuleList = ref([])
|
|
|
|
|
+const uniqueRuleMap = ref({})
|
|
|
|
|
+// 获取商品规则
|
|
|
|
|
+const _getCommodityRule = (item) => {
|
|
|
|
|
+ const params = { customer: item.customerId, sku: item.sku, input: 'RECEIVING' }
|
|
|
|
|
+ getCommodityRule(params).then(res => {
|
|
|
|
|
+ uniqueRuleList.value = res.data
|
|
|
|
|
+ res.data.forEach((item, index) => {
|
|
|
|
|
+ if (item.sku == '') {
|
|
|
|
|
+ item.type = 'all'
|
|
|
|
|
+ } else {
|
|
|
|
|
+ item.type = 'sku'
|
|
|
|
|
+ }
|
|
|
|
|
+ })
|
|
|
|
|
+ uniqueRuleMap.value = toMap(res.data, 'type', 'uniqueRegExp')
|
|
|
|
|
+ })
|
|
|
|
|
+}
|
|
|
|
|
+/**
|
|
|
|
|
+ * 唯一码end
|
|
|
|
|
+ */
|
|
|
|
|
+const containerNoRef = ref(null)
|
|
|
|
|
+const numberRef = ref(null)
|
|
|
|
|
+// 完成收货校验
|
|
|
|
|
+const isCheck = () => {
|
|
|
|
|
+ if (!asnInfo.value.asnNo) {
|
|
|
|
|
+ scanError()
|
|
|
|
|
+ showToast({ duration: 3000, message: '请先查询商品收货信息' })
|
|
|
|
|
+ return false
|
|
|
|
|
+ }
|
|
|
|
|
+ //商品物理属性判断
|
|
|
|
|
+ if (!attributeTrueFalseBy.value) {
|
|
|
|
|
+ const isAttributeInfo = isAttribute(attributeMap.value)
|
|
|
|
|
+ if (isAttributeInfo.length > 0) {
|
|
|
|
|
+ attributeTrueFalseBy.value = false
|
|
|
|
|
+ attributeRef.value?.show(isAttributeInfo, attributeMap.value)
|
|
|
|
|
+ }
|
|
|
|
|
+ return false
|
|
|
|
|
+ }
|
|
|
|
|
+ // //商品批次属性判断
|
|
|
|
|
+ const incompleteLot = lotData.value.find(lot => lot.require && !lot.mapping)
|
|
|
|
|
+ if (incompleteLot) {
|
|
|
|
|
+ scanError()
|
|
|
|
|
+ showToast({ duration: 3000, message: `请先补充${incompleteLot.label}` })
|
|
|
|
|
+ return false
|
|
|
|
|
+ }
|
|
|
|
|
+ const lotMap = toMap(lotData.value, 'field', 'mapping')
|
|
|
|
|
+ const productionDate = lotMap['lotAtt01'] ? new Date(lotMap['lotAtt01']) : null
|
|
|
|
|
+ const expirationDate = lotMap['lotAtt02'] ? new Date(lotMap['lotAtt02']) : null
|
|
|
|
|
+ const currentDate = new Date()
|
|
|
|
|
+// 如果存在失效日期
|
|
|
|
|
+ if (expirationDate) {
|
|
|
|
|
+ // 如果有生产日期,进行有效性检查
|
|
|
|
|
+ if (productionDate && expirationDate <= productionDate) {
|
|
|
|
|
+ scanError()
|
|
|
|
|
+ showToast({ duration: 3000, message: `失效日期不能小于等于生产日期` })
|
|
|
|
|
+ return false
|
|
|
|
|
+ }
|
|
|
|
|
+ // 检查失效日期是否小于当前日期
|
|
|
|
|
+ if (expirationDate <= currentDate) {
|
|
|
|
|
+ scanError()
|
|
|
|
|
+ showToast({ duration: 3000, message: `失效日期不能小于等于当前日期` })
|
|
|
|
|
+ return false
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ if(productionDate){
|
|
|
|
|
+ // 如果有生产日期,进行有效性检查
|
|
|
|
|
+ if (expirationDate && productionDate >= expirationDate) {
|
|
|
|
|
+ scanError()
|
|
|
|
|
+ showToast({ duration: 3000, message: `生产日期不能大于失效日期` })
|
|
|
|
|
+ return false
|
|
|
|
|
+ }
|
|
|
|
|
+ // 检查生产日期是否小于当前日期
|
|
|
|
|
+ if (productionDate >= currentDate) {
|
|
|
|
|
+ scanError()
|
|
|
|
|
+ showToast({ duration: 3000, message: `生产日期不能大于等于当前日期` })
|
|
|
|
|
+ return false
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ if (searchCount.value == '') {
|
|
|
|
|
+ numberRef.value?.focus()
|
|
|
|
|
+ scanError()
|
|
|
|
|
+ showToast({ duration: 3000, message: '请先输入收货数量' })
|
|
|
|
|
+ return false
|
|
|
|
|
+ }
|
|
|
|
|
+ if (containerNo.value == '') {
|
|
|
|
|
+ scanError()
|
|
|
|
|
+ containerNoRef.value?.focus()
|
|
|
|
|
+ showToast({ duration: 3000, message: '请先扫描库位号' })
|
|
|
|
|
+ scanType.value=5
|
|
|
|
|
+ return false
|
|
|
|
|
+ }
|
|
|
|
|
+ // 唯一码收集
|
|
|
|
|
+ if (uniqueRuleList.value.length > 0 && uniqueCodeList.value.length != searchCount.value) {
|
|
|
|
|
+ scanType.value = 3
|
|
|
|
|
+ uniqueCodeRef.value?.show('', '请扫描唯一码', `收货数量:${searchCount.value}`, uniqueRuleMap.value)
|
|
|
|
|
+ return false
|
|
|
|
|
+ }
|
|
|
|
|
+ return true
|
|
|
|
|
+}
|
|
|
|
|
+// 收货
|
|
|
|
|
+const onConfirm = () => {
|
|
|
|
|
+ if(isCheck()){
|
|
|
|
|
+ const lotMap = toMap(lotData.value, 'field', 'mapping')
|
|
|
|
|
+ const { asnLineNo, asnNo, warehouse } = asnInfo.value
|
|
|
|
|
+ const data = {
|
|
|
|
|
+ asnLineNo,
|
|
|
|
|
+ asnNo,
|
|
|
|
|
+ containerId: containerNo.value,
|
|
|
|
|
+ quantity: searchCount.value,
|
|
|
|
|
+ warehouse,
|
|
|
|
|
+ serialNos: uniqueCodeList.value.length > 0 ? uniqueCodeList.value : undefined,
|
|
|
|
|
+ ...lotMap,
|
|
|
|
|
+ }
|
|
|
|
|
+ showLoading()
|
|
|
|
|
+ inputBarcodeType.value='task'
|
|
|
|
|
+ setBotReceiving(data).then(res => {
|
|
|
|
|
+ scanSuccess()
|
|
|
|
|
+ showNotify({ type: 'success', duration: 3000, message: `${searchBarcode.value}收货完成,请继续收货!`})
|
|
|
|
|
+ setBarcode(taskNo.value, '2')
|
|
|
|
|
+ reset()
|
|
|
|
|
+ taskInfo.value={}
|
|
|
|
|
+ scanType.value=2
|
|
|
|
|
+ closeLoading()
|
|
|
|
|
+ }).catch(err => {
|
|
|
|
|
+ if(err.message.includes('序列号已存在')){
|
|
|
|
|
+ scanType.value = 3
|
|
|
|
|
+ uniqueCodeRef.value?.show('', '请扫描唯一码', `收货数量:${searchCount.value},${err.message}`, uniqueRuleMap.value)
|
|
|
|
|
+ }
|
|
|
|
|
+ scanError()
|
|
|
|
|
+ closeLoading()
|
|
|
|
|
+ })
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+
|
|
|
|
|
+// 数据刷新
|
|
|
|
|
+const loadData = () => {
|
|
|
|
|
+ if (!taskNo.value) {
|
|
|
|
|
+ inputBarcodeRef.value?.show('', '请扫描开单任务号','')
|
|
|
|
|
+ return
|
|
|
|
|
+ } else {
|
|
|
|
|
+ // currentTime.value=getCurrentTime()
|
|
|
|
|
+ // startTimer()
|
|
|
|
|
+ }
|
|
|
|
|
+}
|
|
|
|
|
+onUnmounted(() => {
|
|
|
|
|
+ closeListener()
|
|
|
|
|
+ stopTimer()
|
|
|
|
|
+})
|
|
|
|
|
+window.onRefresh = loadData
|
|
|
|
|
+
|
|
|
|
|
+</script>
|
|
|
|
|
+<style scoped lang="sass">
|
|
|
|
|
+.take-delivery
|
|
|
|
|
+ .take-info
|
|
|
|
|
+ padding: 6px 10px
|
|
|
|
|
+ background: #e0eeff
|
|
|
|
|
+ display: flex
|
|
|
|
|
+ flex-direction: column
|
|
|
|
|
+ text-align: left
|
|
|
|
|
+
|
|
|
|
|
+ .take-info-no
|
|
|
|
|
+ flex: 1
|
|
|
|
|
+
|
|
|
|
|
+ .info-no-title
|
|
|
|
|
+ font-size: 18px
|
|
|
|
|
+ display: flex
|
|
|
|
|
+ justify-content: space-between
|
|
|
|
|
+ align-items: center
|
|
|
|
|
+
|
|
|
|
|
+ .info-no-tips
|
|
|
|
|
+ font-size: 14px
|
|
|
|
|
+ color: #666666
|
|
|
|
|
+ display: flex
|
|
|
|
|
+ justify-content: space-between
|
|
|
|
|
+ padding: 6px 0
|
|
|
|
|
+
|
|
|
|
|
+ .take-info-number
|
|
|
|
|
+ flex: 1
|
|
|
|
|
+ border-top: 1.5px solid #ffffff
|
|
|
|
|
+ display: flex
|
|
|
|
|
+ justify-content: space-between
|
|
|
|
|
+ color: #666
|
|
|
|
|
+ font-size: 14px
|
|
|
|
|
+
|
|
|
|
|
+ .info-number-left
|
|
|
|
|
+ flex: 1
|
|
|
|
|
+ display: flex
|
|
|
|
|
+ justify-content: space-between
|
|
|
|
|
+ align-items: center
|
|
|
|
|
+
|
|
|
|
|
+ .number-left-box
|
|
|
|
|
+ flex: 1
|
|
|
|
|
+ display: flex
|
|
|
|
|
+ justify-content: center
|
|
|
|
|
+ align-items: center
|
|
|
|
|
+
|
|
|
|
|
+ .left-box-title
|
|
|
|
|
+ font-size: 14px
|
|
|
|
|
+ font-weight: bold
|
|
|
|
|
+ color: #000
|
|
|
|
|
+ line-height: 34px
|
|
|
|
|
+
|
|
|
|
|
+ .info-number-right
|
|
|
|
|
+ width: 0%
|
|
|
|
|
+ text-align: center
|
|
|
|
|
+
|
|
|
|
|
+ .van-search
|
|
|
|
|
+ padding: 0
|
|
|
|
|
+
|
|
|
|
|
+ .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
|
|
|
|
|
+ font-weight: bold
|
|
|
|
|
+
|
|
|
|
|
+ ::v-deep(.van-search__label)
|
|
|
|
|
+ font-size: 15px
|
|
|
|
|
+ font-weight: bold
|
|
|
|
|
+
|
|
|
|
|
+ .search-input-barcode
|
|
|
|
|
+ ::v-deep(.van-search__field)
|
|
|
|
|
+ border-bottom: 2px solid #0077ff
|
|
|
|
|
+ z-index: 2
|
|
|
|
|
+
|
|
|
|
|
+ .take-lot
|
|
|
|
|
+ text-align: left
|
|
|
|
|
+ margin-top: 5px
|
|
|
|
|
+
|
|
|
|
|
+ ::v-deep(.van-cell)
|
|
|
|
|
+ padding: 5px 8px
|
|
|
|
|
+
|
|
|
|
|
+ .take-lot-title
|
|
|
|
|
+ font-size: 15px
|
|
|
|
|
+ font-weight: bold
|
|
|
|
|
+ padding: 0 5px
|
|
|
|
|
+ border-left: 3px solid #1989fa
|
|
|
|
|
+ color: #333
|
|
|
|
|
+ margin-bottom: 3px
|
|
|
|
|
+
|
|
|
|
|
+ .take-button
|
|
|
|
|
+ padding: 20px
|
|
|
|
|
+</style>
|