index.vue 26 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850
  1. <template>
  2. <div class="container">
  3. <van-nav-bar
  4. title="拣货任务"
  5. left-arrow
  6. fixed
  7. placeholder
  8. @click-left="goBack"
  9. >
  10. <template #left>
  11. <van-icon name="arrow-left" size="25" />
  12. <div style="color: #fff" >返回</div>
  13. </template>
  14. <template #right>
  15. <div style="color: #fff" @click="onSelectMode({key:'picking'})">开始作业</div>
  16. <van-icon name="list-switch" size="25" @click="onClickRight" />
  17. </template>
  18. </van-nav-bar>
  19. <!-- <van-pull-refresh v-model="loading" @refresh="onRefresh" style="min-height: 93.1vh;">-->
  20. <div v-if="locationList.length>0">
  21. <div class="code">
  22. <div class="code-title">
  23. <div>{{ taskItem.length>0?pickingNo:'无效任务' }}</div>
  24. <div class="code-tips"><van-notice-bar :background="'none'" :speed="50" :text="tips()" /></div>
  25. <div class="code-count"><span>{{ quantity }}</span>/{{ allQuantity }}</div>
  26. </div>
  27. <van-field class="code-input"
  28. v-model="containerNo"
  29. readonly
  30. @click="onContainerNo(containerNo===''?2:scanType)"
  31. placeholder="拣货容器号" >
  32. <template #button>
  33. <van-button size="mini" type="primary" v-if="containerNo===''" plain @click="onContainerNo(2)" >设置拣货容器</van-button>
  34. <van-button size="mini" type="primary" v-else plain @click="onContainerNo(scanType)" >更换拣货容器</van-button>
  35. </template>
  36. </van-field>
  37. </div>
  38. <van-row class="list">
  39. <van-col span="8">
  40. <div class="left">
  41. <div class="item" ref="itemRefs" :class="index==activeIndex?'active':allPicking(location)"
  42. v-for="(location,index) in locationList"
  43. :key="index"
  44. @click="onLocation(index)">
  45. <span>{{ location.location }}</span><span class="recommend" v-if="nextLocation==location.location && allPicking(location)!=='allActive'" >推荐</span>
  46. </div>
  47. </div>
  48. </van-col>
  49. <van-col span="16">
  50. <div class="right">
  51. <div class="right-list" v-for="(item,index) in locationList[activeIndex].list"
  52. :class="activeClass(item)">
  53. <div>{{ ownerMap[item.owner] || item.owner }}</div>
  54. <div class="content" @click="onLotAtt(item)" >
  55. <div class="c-left">
  56. <div class="c-left-count">{{ item.expectedQuantity }}</div>
  57. <!-- <div class="c-left-tips">数量</div>-->
  58. </div>
  59. <div class="c-right">
  60. <div style="font-weight: bold;">{{ item.barcode }}</div>
  61. <div>{{ item.name }}</div>
  62. </div>
  63. <div style="width: 10px">
  64. <van-icon name="arrow" size="20" color="#666" />
  65. </div>
  66. </div>
  67. <van-field class="input" ref="scanCountRef" label-width="50px" v-model="item.count" type="number" :min="1"
  68. :max="item.expectedQuantity"
  69. @keydown.enter="onCount(item,0)"
  70. label="实拣数" placeholder="请输实拣数量"
  71. >
  72. <template #button>
  73. <van-button v-if="item.operationTime==null && item.count==0" size="mini" type="primary" plain @click="jump(item)" :loading="jumpLoading" loading-text="加载中...">跳过</van-button>
  74. </template>
  75. </van-field>
  76. <WaveInfo :binds="item.binds" />
  77. </div>
  78. </div>
  79. </van-col>
  80. </van-row>
  81. </div>
  82. <div v-if="locationList.length==0" >
  83. <van-pull-refresh v-model="loading" @refresh="onRefresh" style="min-height: 93.1vh;">
  84. <van-empty
  85. image-size="100"
  86. description="暂无拣货数据"
  87. />
  88. </van-pull-refresh>
  89. </div>
  90. <!-- </van-pull-refresh>-->
  91. <van-action-sheet
  92. v-model:show="modeTrueFalseBy"
  93. :actions="actions"
  94. cancel-text="取消"
  95. close-on-click-action
  96. @select="onSelectMode"
  97. />
  98. <!-- 拣货容器号-->
  99. <container-no-input ref="containerNoInputRef" @setContainer="setContainer" :selectTask="selectTask" />
  100. <!-- 拣货任务号-->
  101. <picking-no-input ref="pickingNoInputRef" @loadData="loadData" />
  102. <input-barcode ref="inputBarcodeRef" @setBarcode="_handlerScan" />
  103. <van-floating-bubble v-if="locationList.length>0" :gap="50" axis="xy" magnetic="x" @click="onContainerNo(containerNo===''?2:scanType)">容器</van-floating-bubble>
  104. <lot-att ref="lotAttRef" />
  105. <barcode-combine ref="barcodeCombineRef" @setCombine="setCombine" :container="containerNo" :matched-sku="matchedSku" />
  106. </div>
  107. </template>
  108. <script lang="ts" setup>
  109. import { computed, defineAsyncComponent, nextTick, onMounted, onUnmounted, ref, watch } from 'vue'
  110. import { showConfirmDialog, showDialog, showFailToast, showNotify, showToast } from 'vant'
  111. import { fetchPickingData, getPickingTask } from '@/views/outbound/picking/list/hooks/list'
  112. import { getOwnerList } from '@/hooks/basic'
  113. import { useRouter,useRoute } from 'vue-router'
  114. import { getListCombineSku, removePickingTask, setPickingDetail } from '@/api/picking'
  115. const router = useRouter()
  116. const route = useRoute()
  117. const store = useStore()
  118. const basic = basicStore()
  119. import { useStore } from '@/store/modules/user'
  120. import { goBack, getHeader, playVoicePickNum, scanError, scanSuccess, androidFocus } from '@/utils/android'
  121. import { closeListener, openListener, scanInit } from '@/utils/keydownListener'
  122. import BarcodeCombine from '@/views/outbound/picking/components/BarcodeCombine.vue'
  123. const PickingNoInput = defineAsyncComponent(() => import('@/views/outbound/picking/components/PickingNoInput.vue'))
  124. const ContainerNoInput = defineAsyncComponent(() => import('@/views/outbound/picking/components/ContainerNoInput.vue'))
  125. const InputBarcode = defineAsyncComponent(() => import('@/views/outbound/picking/components/InputBarcode.vue'))
  126. const LotAtt = defineAsyncComponent(() => import('@/views/outbound/picking/components/LotAtt.vue'))
  127. import { barcodeCombine } from '@/views/outbound/picking/list/hooks/barcodeCombine'
  128. import { formatDateTime } from '@/utils/date'
  129. import { isLot } from '@/views/outbound/picking/list/hooks/lotNum'
  130. import { barcodeToUpperCase, toMap } from '@/utils/dataType'
  131. import { containerDef } from '@/views/outbound/picking/list/hooks/containerDef'
  132. import { getCarrierList } from '@/hooks/basic/carrier'
  133. import { basicStore } from '@/store/modules/basic'
  134. import { closeLoading, showLoading } from '@/utils/loading'
  135. import WaveInfo from '@/views/outbound/picking/components/WaveInfo.vue'
  136. onUnmounted(() => {
  137. closeListener()
  138. })
  139. try {
  140. getHeader()
  141. }catch (error) {
  142. router.push('/login')
  143. }
  144. const warehouse = store.warehouse
  145. const containerNo = ref('')
  146. //容器号ref
  147. const scanCountRef = ref(null)
  148. onMounted(() => {
  149. openListener()
  150. setTimeout(() => {
  151. scanInit(_handlerScan)
  152. },300)
  153. })
  154. const pickingNo=ref('')
  155. //拣货容器号
  156. const containerNoTrueFalseBy=ref(false)
  157. // 操作面板
  158. const modeTrueFalseBy=ref(false)
  159. // 获取货主
  160. const { ownerMap, getOwnerData } = getOwnerList()
  161. // 获取货主
  162. const {carrierMap, getCarrierData } = getCarrierList()
  163. let taskMap = ref({})
  164. //组合后库位
  165. let locationList = ref([])
  166. //默认第一条数据激活
  167. let activeIndex = ref(0)
  168. //扫描类型
  169. const scanType = ref(1)
  170. //已经拣货任务
  171. const selectTask=ref([])
  172. const scanBarcode=ref('')
  173. //上一个条码
  174. const preBarcode=ref('')
  175. const taskItem=ref([])
  176. //匹配组合商品数据
  177. const matchedSku=ref([])
  178. // 切换容器号
  179. const containerNoInputRef=ref(null)
  180. const onContainerNo=(type)=>{
  181. setTimeout(()=>{
  182. containerNoInputRef.value?.show(containerNo.value,type)
  183. },300)
  184. }
  185. const allQuantity=ref(0)
  186. //加载数据
  187. const loadData = async (pickingCode,type) => {
  188. pickingNo.value=pickingCode
  189. //获取第一个拣货单号
  190. const { taskList,expectedQuantity }=await getPickingTask(warehouse,pickingCode)
  191. if(taskList.value.length==0){
  192. return
  193. }
  194. result()
  195. allQuantity.value=expectedQuantity.value
  196. taskItem.value=taskList.value
  197. // 对数组进行分组
  198. taskMap.value = {}
  199. taskItem.value.forEach(item => {
  200. if(item.operationTime){
  201. item.count=item.quantity
  202. }else{
  203. item.count=''
  204. }
  205. const { location } = item
  206. // 使用对象的简写形式初始化分组
  207. if (!taskMap.value[location]) {
  208. taskMap.value[location] = { location, list: [],scanLocation:'',scanBarcode:'' }
  209. }
  210. //将有操作时间的放到后边
  211. if (item.operationTime) {
  212. taskMap.value[location].list.push(item);
  213. } else {
  214. taskMap.value[location].list.unshift(item);
  215. }
  216. })
  217. locationList.value = Object.values(taskMap.value)
  218. // 将已拣货的数据放到一个数组
  219. selectTask.value = taskItem.value
  220. .filter(item => item.operationTime!=null) // 过滤出有 productionDate 且不为空的对象
  221. .map(item => {
  222. return {
  223. barcode: item.barcode,
  224. location: item.location,
  225. lotNum:item.lotNum,
  226. line:item.line,
  227. operationTime: item.operationTime,
  228. container: item.container,
  229. quantity:item.quantity
  230. };
  231. });
  232. if(selectTask.value.length>0){
  233. getDefContainer(selectTask.value)
  234. }else {
  235. if(type!=1) return
  236. scanBarcode.value=''
  237. containerNo.value=''
  238. androidFocus()
  239. onContainerNo(2)
  240. }
  241. }
  242. // 设置拣货容器
  243. const setContainer=(code,type)=>{
  244. containerNo.value=code
  245. if(type==4){
  246. onScan(3)
  247. }else {
  248. onScan(type)
  249. }
  250. }
  251. // 获取拣货任务号
  252. const getPickingCode= async()=>{
  253. const { pickingCode } = await fetchPickingData(warehouse)
  254. if(pickingCode!=null){
  255. pickingNo.value=pickingCode
  256. await loadData(pickingCode,1)
  257. }else {
  258. await router.push('/picking-task')
  259. }
  260. }
  261. if(route.query.code){
  262. loadData(route.query.code,1)
  263. }else {
  264. getPickingCode()
  265. }
  266. if(Object.keys(basic.carrierMap).length == 0){
  267. getCarrierData()
  268. }
  269. getOwnerData()
  270. // 计算已成功数量
  271. const quantity = computed(() => {
  272. return selectTask.value.reduce((sum, item) => {
  273. return Number(sum) + (Number(item.quantity) ?? 0)
  274. }, 0)
  275. })
  276. //如果有执行过的数据,将时间最近的容器号放到容器里
  277. const getDefContainer=(selectTask)=>{
  278. const closestItem=containerDef(selectTask)
  279. containerNo.value=closestItem.container
  280. onScan(2)
  281. }
  282. const messageTips=ref('')
  283. const tips = () => {
  284. if (scanType.value==1) {
  285. return '请扫描容器号'
  286. }else if(scanType.value==2){
  287. return '请扫描下一拣货库位'
  288. }else if(scanType.value==3){
  289. return '请扫描商品条码'
  290. }else if(scanType.value==4){
  291. return messageTips.value
  292. }
  293. }
  294. //切换不同库位
  295. const onLocation = (index) => {
  296. activeIndex.value = index
  297. nextTick(() => {
  298. onScan(2)
  299. })
  300. }
  301. const itemRefs = ref([]);
  302. // 监听 activeIndex 变化,滚动到指定位置
  303. watch(activeIndex, (newIndex) => {
  304. nextTick(() => {
  305. const item = itemRefs.value[newIndex];
  306. if (item) {
  307. item.scrollIntoView({
  308. behavior: 'smooth', // 平滑滚动
  309. block: 'start', // 滚动到顶部 nearest center
  310. });
  311. }
  312. });
  313. });
  314. //设置扫描类型 1容器、2库位、3条码
  315. const onScan = (type) => {
  316. nextTick(() => {
  317. scanType.value = type
  318. })
  319. }
  320. const barcodeCombineRef=ref(null)
  321. //扫描条码监听
  322. const _handlerScan=(code)=> {
  323. if(scanType.value===1){
  324. if(code.split('-')[0]!=='JH'){
  325. showToast({duration:5000,message:'请使用标准拣货容器'})
  326. return
  327. }
  328. containerNo.value=code
  329. onScan(2)
  330. return
  331. }
  332. scanBarcode.value=code
  333. const modelLocative=locationList.value[activeIndex.value]
  334. if(scanType.value===2){
  335. if(containerNo.value == '') {
  336. showToast({duration:5000,message:'请先扫描容器号'})
  337. return
  338. }
  339. if(taskMap.value[code.toUpperCase()]){
  340. nextLocation.value=''
  341. activeIndex.value = locationList.value.findIndex(item => item.location === barcodeToUpperCase(code))
  342. onScan(3)
  343. scanSuccess()
  344. }else{
  345. scanError()
  346. showToast({duration:5000,message:'无效库位!请检查扫描库位'})
  347. }
  348. }
  349. if(scanType.value===3 || scanType.value===4 ){
  350. preBarcode.value=scanBarcode.value
  351. // 将当前库位下的商品条码拿出对比
  352. const barcode = [...new Set(
  353. modelLocative.list
  354. .flatMap(item => [item.barcode, item.barcodeAs])
  355. .filter(value => value !== null )
  356. )]
  357. if (barcode.some(item => barcodeToUpperCase(item) === barcodeToUpperCase(code))) {
  358. //将匹配到的条码放到第一个
  359. modelLocative.list=modelLocative.list.reduce((list, item) => {
  360. if (barcodeToUpperCase(item.barcode) === barcodeToUpperCase(code)) {
  361. list.unshift(item);
  362. } else {
  363. list.push(item);
  364. }
  365. return list;
  366. }, [])
  367. const activeBarcode=modelLocative.list[0]
  368. playVoicePickNum(activeBarcode.expectedQuantity)
  369. if(activeBarcode.operationTime){
  370. showConfirmDialog({
  371. title: '温馨提示',
  372. message:
  373. `当前条码《${activeBarcode.barcode}》已经拣货成功是否确认重复提交?`,
  374. })
  375. .then(() => {
  376. activeBarcode.count=activeBarcode.expectedQuantity
  377. onScan(4)
  378. setTimeout(()=>{
  379. //匹配到条码扣减库存
  380. onCount(activeBarcode,0)
  381. },200)
  382. }).catch(()=>{
  383. const allOperationTimeExist = modelLocative.list.every(({ operationTime }) => operationTime);
  384. if(allOperationTimeExist){
  385. onScan(2)
  386. }
  387. })
  388. }else {
  389. activeBarcode.count=activeBarcode.expectedQuantity
  390. onScan(4)
  391. setTimeout(()=>{
  392. //匹配到条码扣减库存
  393. onCount(activeBarcode,0)
  394. },200)
  395. }
  396. }else{
  397. //查询组合条码
  398. matchedSku.value=[]
  399. getListCombineSku({combineSku:code, workEnvironment:'picking'}).then(res=>{
  400. if(res.data.length>0){
  401. const combineSkuMap=toMap(res.data,'barcode')
  402. const matchedSkuList=barcodeCombine(modelLocative.list,combineSkuMap)
  403. if(matchedSkuList.length>0){
  404. if(matchedSkuList.length==res.data.length){
  405. matchedSku.value=matchedSkuList
  406. barcodeCombineRef.value.show()
  407. }else{
  408. scanError()
  409. showDialog({
  410. title:'温馨提示',
  411. message:'组合商品与拣货任务不匹配,请检查组合商品配置!'
  412. })
  413. }
  414. }else {
  415. scanError()
  416. showDialog({
  417. title:'温馨提示',
  418. message:'组合商品与拣货任务不匹配,请检查组合商品配置!'
  419. })
  420. }
  421. }else {
  422. scanError()
  423. showToast({duration:5000,message:'无效条码!请检查扫描条码'})
  424. }
  425. })
  426. }
  427. }
  428. }
  429. // 进行条件验证
  430. const validate = (data,type) => {
  431. if (containerNo.value === '') {
  432. showToast({duration:5000,message:'请先扫描拣货容器'})
  433. return false;
  434. }
  435. if (scanType.value === 2 && type!==1) {
  436. showToast({duration:5000,message:'请先扫描库位'})
  437. return false;
  438. }
  439. if(data.quantity!==0 && scanType.value === 3){
  440. showToast({duration:5000,message:'请先扫描条码'})
  441. return false;
  442. }
  443. if (data.count < 0) {
  444. messageTips.value='拣货数量无效'
  445. showToast({duration:5000,message:'拣货数量无效'})
  446. return false;
  447. }
  448. if (data.count > data.expectedQuantity) {
  449. showToast({duration:5000,message:'拣货数量不能大于所需数量'})
  450. messageTips.value='拣货数量不能大于所需数量'
  451. return false;
  452. }
  453. if (isLot(selectTask.value, [data])) {
  454. messageTips.value='当前容器下已有其他批次产品,请更换容器号'
  455. scanBarcode.value=''
  456. showToast({duration:5000,message:'当前容器下已有其他批次产品,请更换容器号'})
  457. return false;
  458. }
  459. return true;
  460. }
  461. //输入数量
  462. const jumpLoading=ref(false);
  463. const onCount=(item,type)=>{
  464. const data=JSON.parse(JSON.stringify(item))
  465. data.operationTime=formatDateTime(new Date())
  466. data.container=containerNo.value
  467. const params=[data]
  468. if(type==0){
  469. data.quantity=data.count
  470. }else {
  471. data.quantity=0
  472. }
  473. // 验证数据的有效性
  474. if (!validate(data,type)) return;
  475. if(type!==0){
  476. jumpLoading.value=true
  477. }
  478. _setPickingDetail(params,type)
  479. }
  480. //组合商品
  481. const setCombine=(data)=>{
  482. if (isLot(selectTask.value, data)) {
  483. messageTips.value='当前容器下已有其他批次产品,请更换容器号'
  484. scanBarcode.value=''
  485. showToast({duration:5000,message:'当前容器下已有其他批次产品,请更换容器号'})
  486. return false;
  487. }
  488. _setPickingDetail(data,2)
  489. }
  490. const nextLocation=ref()
  491. const _setPickingDetail=(params,type)=>{
  492. const activeList=locationList.value[activeIndex.value]
  493. //扣减库存
  494. showLoading()
  495. setPickingDetail(params).then((res)=>{
  496. closeLoading()
  497. jumpLoading.value = false;
  498. if (res.data == false) {
  499. showDialog({
  500. title: '温馨提示',
  501. message: '该库位条码存在取消单,任务已被刷新,请将当前货品归还原库位'
  502. }).then(() => {
  503. loadData(pickingNo.value,1);
  504. });
  505. return;
  506. }
  507. //任务行号相同说明重复执行
  508. if (!selectTask.value.find(task => params.some(param => task.line === param.line))) {
  509. // 遍历 params 数组并处理每个元素
  510. params.forEach((param) => {
  511. const task = {
  512. barcode: param.barcode,
  513. location: param.location,
  514. lotNum: param.lotNum,
  515. line: param.line,
  516. operationTime: param.operationTime, // 使用每个对象的 operationTime
  517. container: param.container,
  518. quantity: param.quantity
  519. };
  520. selectTask.value.push(task);
  521. })
  522. }
  523. // 更新 locationList 中的 operationTime
  524. params.forEach((param, index) => {
  525. if (activeList.list[index]) {
  526. activeList.list[index].operationTime = param.operationTime;
  527. activeList.list[index].count = param.quantity;
  528. activeList.list[index].quantity = param.quantity;
  529. }
  530. });
  531. //验证库位里的所有商品是否都存在拣货时间
  532. const { list } = activeList
  533. const allOperationTimeExist = list.every(({ operationTime }) => operationTime); // 检查所有商品是否都有拣货时间
  534. if (!allOperationTimeExist) {
  535. onScan(3); // 如果有商品缺少拣货时间,直接调用 onScan(3)
  536. } else {
  537. nextLocation.value=''
  538. if(activeIndex.value<locationList.value.length-1){
  539. nextLocation.value=locationList.value[activeIndex.value+1].location
  540. showNotify({
  541. message: `当前库位商品已全部拣完,请扫描下一个库位${locationList.value[activeIndex.value+1].location}`,
  542. duration: 5000,
  543. type:'warning'
  544. });
  545. }
  546. if(type==2){
  547. const numberExist = list.every(({ expectedQuantity, quantity }) => expectedQuantity == quantity && quantity!=0); // 检查数量是否匹配
  548. onScan(numberExist ? 2 : 3)
  549. }else {
  550. onScan(2)
  551. }
  552. }
  553. //对有操作时间的进行排序放到下边
  554. list.sort((a, b) => {
  555. if (a.operationTime && !b.operationTime) {
  556. return 1;
  557. }
  558. if (!a.operationTime && b.operationTime) {
  559. return -1;
  560. }
  561. return 0;
  562. });
  563. if(type==1){
  564. locationList.value[activeIndex.value].list.forEach(items=>{
  565. if(items.line==params[0].line){
  566. items.count=0
  567. }
  568. })
  569. }
  570. scanSuccess()
  571. if(selectTask.value.length===taskItem.value.length){
  572. showConfirmDialog({
  573. title:'温馨提示',
  574. message:'任务已经完成,是否回到选择任务界面'
  575. }).then(() => {
  576. result()
  577. getPickingCode()
  578. }).catch(() => {
  579. loadData(pickingNo.value,1)
  580. })
  581. }
  582. }).catch((err)=> {
  583. closeLoading()
  584. scanBarcode.value = ''
  585. onScan(4)
  586. messageTips.value = err.message
  587. scanError()
  588. if(err.code=='ERR_NETWORK'){
  589. messageTips.value = '网络开小车了,请稍后重试!'
  590. }if(err.code=='ECONNABORTED'){
  591. messageTips.value = '请求超时,请稍后重试!'
  592. }else {
  593. showNotify({
  594. message: err.message,
  595. duration: 5000,
  596. });
  597. // showFailToast({duration:5000,message:err.message})
  598. }
  599. jumpLoading.value=false
  600. })
  601. }
  602. const result=()=>{
  603. scanBarcode.value=''
  604. containerNo.value=''
  605. selectTask.value=[]
  606. activeIndex.value=0
  607. scanType.value=1
  608. }
  609. // 跳过拣货
  610. const jump=(item)=>{
  611. item.quantity=0
  612. if (!validate(item,1)) return;
  613. showConfirmDialog({
  614. title:'温馨提示',
  615. message:'您正在进行缺货跳过操作,是否继续'
  616. }).then(() => {
  617. onCount(item,1)
  618. }).catch(() => {
  619. })
  620. }
  621. //切换模式
  622. const actions = [
  623. { name: '取消任务',key:'removeTask' },
  624. { name: '获取任务',key:'task' },
  625. // { name: '任务号作业' ,key:'picking'},
  626. { name: '检索条码' ,key:'inputBarcode'},
  627. ];
  628. const onClickRight = () => {
  629. modeTrueFalseBy.value=true
  630. }
  631. const pickingNoInputRef=ref(null)
  632. const inputBarcodeRef=ref(null)
  633. const onSelectMode= async (value) => {
  634. if(value.key=='task'){
  635. await router.push({name:'PickingTask',query:{type:'picking'}})
  636. }else if(value.key=='picking'){
  637. pickingNoInputRef.value?.show()
  638. }else if(value.key=='inputBarcode'){
  639. inputBarcodeRef.value?.show()
  640. }else if(value.key=='removeTask'){
  641. if(pickingNo.value){
  642. if(selectTask.value.length>0){
  643. showDialog({
  644. title: '温馨提示',
  645. message: '当前任务已有拣出货品不可还原任务!!',
  646. }).then(() => {});
  647. return
  648. }
  649. showDialog({
  650. title: '温馨提示',
  651. message: '您正在进行还原任务操作,是否继续?',
  652. }).then(() => {
  653. showLoading()
  654. removePickingTask({code:pickingNo.value}).then(res=>{
  655. router.push({name:'PickingTask'})
  656. showToast({duration:3000,message:'当前任务已取消正在跳转到获取任务页面,请稍后~'})
  657. // closeLoading()
  658. }).catch(() => {
  659. closeLoading()
  660. })
  661. });
  662. }
  663. }
  664. }
  665. const lotAttRef=ref(null)
  666. const onLotAtt=(item)=>{
  667. lotAttRef.value?.show(item)
  668. }
  669. //库位商品拣货完成高亮 allActive
  670. const allPicking = (location) => {
  671. const all = location.list.every(item => item.operationTime);
  672. if(all){
  673. return 'allActive'
  674. }else if(nextLocation.value==location.location){
  675. return 'nextActive'
  676. }
  677. }
  678. //库位商品高亮
  679. const activeClass=(item)=>{
  680. const modelLocative = locationList.value[activeIndex.value]
  681. if (
  682. modelLocative.location === item.location &&
  683. (
  684. barcodeToUpperCase(item.barcode) === barcodeToUpperCase(scanBarcode.value)||
  685. (item.barcodeAs=== barcodeToUpperCase(scanBarcode.value) && scanBarcode.value !== '')
  686. ) &&
  687. item.operationTime === null
  688. ) {
  689. return 'active'
  690. }
  691. else if(item.operationTime!=null){
  692. return 'allActive'
  693. }
  694. }
  695. //刷新页面
  696. const loading = ref(false)
  697. const onRefresh = () => {
  698. setTimeout(() => {
  699. loadData(pickingNo.value,1)
  700. showToast('刷新成功')
  701. loading.value = false
  702. }, 1000)
  703. }
  704. </script>
  705. <style scoped lang="sass">
  706. .container
  707. .code
  708. height: 67px
  709. padding: 5px 10px
  710. background: #e9f4ff
  711. box-sizing: border-box
  712. .code-title
  713. display: flex
  714. justify-content: space-between
  715. .code-tips
  716. color: #ed6a0c
  717. flex: 1
  718. .code-count
  719. font-size: 16px
  720. font-weight: bold
  721. span
  722. color: #0077ff
  723. .code-input
  724. font-size: 16px
  725. font-weight: bold
  726. background: #e9f4ff
  727. padding: 2px 10px
  728. border-bottom: 2px solid #0077ff
  729. .list
  730. overflow: scroll
  731. .left
  732. background: #e9f4ff
  733. padding: 2px 10px
  734. height: 80vh
  735. overflow: scroll
  736. box-sizing: border-box
  737. .item
  738. font-size: 14px
  739. background: #fff
  740. padding: 10px 5px
  741. margin-bottom: 6px
  742. border-radius: 8px 0 0 8px
  743. box-sizing: border-box
  744. word-wrap: break-word
  745. font-weight: bold
  746. position: relative
  747. .recommend
  748. position: absolute
  749. right: -10px
  750. top: 0
  751. font-size: 11px
  752. background: #f64e4e
  753. border-radius: 6px
  754. padding: 0 3px
  755. .active
  756. background: #1989fa
  757. color: #fff
  758. position: relative
  759. right: -10px
  760. .allActive
  761. background: #72dc41
  762. color: #fff
  763. .nextActive
  764. background: #ff8d29
  765. color: #fff
  766. .right
  767. height: 80vh
  768. overflow: scroll
  769. box-sizing: border-box
  770. text-align: left
  771. padding: 2px 0
  772. background: #fff
  773. .right-list
  774. padding: 10px
  775. margin-bottom: 5px
  776. border-bottom: 1px solid #D3D3D3
  777. border-radius: 0 8px 8px 0
  778. .content
  779. display: flex
  780. justify-content: space-between
  781. align-items: center
  782. .c-left
  783. width: 25%
  784. text-align: center
  785. position: relative
  786. .c-left-tips
  787. position: absolute
  788. left: 0
  789. right: 0
  790. top: 0
  791. bottom: 0
  792. color: #D3D3D3
  793. z-index: -10
  794. opacity: .7
  795. font-size: 24px
  796. .c-left-count
  797. font-weight: bold
  798. font-size: 30px
  799. .c-right
  800. flex: 0 0 75%
  801. max-width: 75%
  802. .right-list:last-child
  803. border-bottom: none
  804. .active
  805. background: #c7e3ff
  806. opacity: .8
  807. .allActive
  808. background: #72dc41
  809. opacity: .8
  810. .wave
  811. padding: 5px
  812. .wave-item
  813. display: flex
  814. justify-content: space-between
  815. font-size: 12px
  816. .input
  817. font-size: 14px
  818. font-weight: bold
  819. padding: 5px 10px
  820. background: none
  821. border-bottom: 2px solid #1989fa
  822. </style>