index.vue 41 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312
  1. <template>
  2. <div class="container">
  3. <van-nav-bar title="宝时丰收" left-arrow fixed placeholder @click-left="goBack">
  4. <template #left>
  5. <van-icon name="arrow-left" size="25" />
  6. <div style="color: #fff">返回</div>
  7. </template>
  8. </van-nav-bar>
  9. <div class="take-delivery">
  10. <div class="take-info">
  11. <div class="take-info-no">
  12. <div class="info-no-title">
  13. <div>任务号:{{ taskInfo.taskNo || '--' }}</div>
  14. <div>
  15. <van-button type="primary" size="mini" plain @click="switchTask">切换任务</van-button>
  16. </div>
  17. </div>
  18. <div class="info-no-tips">
  19. <div>货主:<span style="color: #333;font-weight: bold;">{{ taskInfo.customerName || '--' }}</span></div>
  20. <div>任务数:<span style="font-weight: bold"
  21. ><span
  22. :style="{
  23. color: (Number(taskInfo.receivedQty || 0) + Number(taskInfo.overReceiveQuantity || 0)) > Number(taskInfo.expectedQty || 0) ? '#ee0a24' : '#0077ff',
  24. }"
  25. >{{ Number(taskInfo.receivedQty || 0) + Number(taskInfo.overReceiveQuantity || 0) }}</span><span style="color: #0077ff">/{{ taskInfo.expectedQty || 0 }}</span></span></div>
  26. </div>
  27. </div>
  28. <div class="take-info-number">
  29. <div class="info-number-left">
  30. <div class="number-left-box">
  31. <div>开始时间</div>
  32. <div class="left-box-title">{{ currentTime }}</div>
  33. </div>
  34. <div class="number-left-box">
  35. <div>已用时</div>
  36. <div class="left-box-title">{{ formattedTime }}</div>
  37. </div>
  38. </div>
  39. <div class="info-number-right">
  40. <div>容器号</div>
  41. <div>
  42. <van-search
  43. ref="containerNoRef"
  44. v-model="containerNo"
  45. left-icon=""
  46. placeholder="请扫描容器号"
  47. autocomplete="off"
  48. @search="isCheck()"
  49. @focus="scanType=5"
  50. @blur="scanType=2"
  51. ></van-search>
  52. </div>
  53. </div>
  54. </div>
  55. </div>
  56. <van-progress
  57. v-if="Number(taskInfo.expectedQty) > 0"
  58. :percentage="Number((((Number(taskInfo.receivedQty || 0) + Number(taskInfo.overReceiveQuantity || 0)) / Number(taskInfo.expectedQty)) * 100).toFixed(2))"
  59. :color="(Number(taskInfo.receivedQty || 0) + Number(taskInfo.overReceiveQuantity || 0)) / Number(taskInfo.expectedQty) * 100 > 100 ? '#ee0a24' : '#1989fa'"
  60. stroke-width="4"
  61. />
  62. <div v-if="showOverReceiveTag" class="over-receive-hint" role="status">
  63. <span class="over-receive-hint__badge">超收</span>
  64. <span class="over-receive-hint__text">无待收行明细的收货</span>
  65. </div>
  66. <div class="take-barcode">
  67. <div class="barcode-input">
  68. <van-search
  69. ref="searchRef"
  70. v-model="searchBarcode"
  71. placeholder="请扫描商品条码"
  72. @search="_handlerScan(searchBarcode)"
  73. label="商品条码:"
  74. left-icon=""
  75. :class="[scanType===2?'search-input-barcode':'','van-hairline--bottom']"
  76. @focus="scanType=2"
  77. autocomplete="off"
  78. @input="onAsnCancel"
  79. @clear="reset"
  80. >
  81. </van-search>
  82. </div>
  83. <div class="barcode-input">
  84. <van-search
  85. ref="numberRef"
  86. v-model="searchCount"
  87. placeholder="请输入收货数量"
  88. type="number"
  89. label="收货数量:"
  90. left-icon=""
  91. autocomplete="off"
  92. show-action
  93. :min="1"
  94. :max="999999"
  95. @search="isCheck()"
  96. :class="[scanType===4?'search-input-barcode':'','van-hairline--bottom']"
  97. @focus="scanType=4"
  98. >
  99. <!-- :max="asnInfo.asnNo?asnInfo.expectedQuantity-asnInfo.receivedQuantity:10000" -->
  100. <template #action>
  101. <div style="display: flex; align-items: center;flex-direction: column;margin-left: 20px" v-if="asnInfo.asnNo">
  102. <div style="height: 20px;font-size: 12px">已收/预计</div>
  103. <div style="font-size: 16px;font-weight: bold;color: #323233">
  104. <span
  105. :style="{
  106. color:
  107. (Number(asnInfo.receivedQuantity || 0) + Number(asnInfo.overReceiveQuantity || 0)) > Number(asnInfo.expectedQuantity || 0)
  108. ? '#ee0a24'
  109. : '#323233',
  110. }"
  111. >{{ Number(asnInfo.receivedQuantity || 0) + Number(asnInfo.overReceiveQuantity || 0) }}</span>/{{ asnInfo.expectedQuantity }}
  112. </div>
  113. </div>
  114. </template>
  115. </van-search>
  116. </div>
  117. </div>
  118. <div class="take-lot" v-if="lotData.length>0">
  119. <van-cell-group>
  120. <div class="take-lot-title">批次信息</div>
  121. <van-cell v-for="(item,i) in lotData" :key="i" :is-link="item.field!=='lotAtt05'"
  122. @click="onLot(item)">
  123. <template #title>
  124. <van-icon v-if="item.require" name="warning-o" color="#ed6a0c" />
  125. <span class="custom-title">{{ item.label }}</span>
  126. </template>
  127. <template #value>
  128. <div>{{ lotMap[item.mapping] || item.mapping }} </div>
  129. </template>
  130. </van-cell>
  131. </van-cell-group>
  132. </div>
  133. <div class="take-button">
  134. <van-button type="primary" size="large" round style="height: 36px" @click="onConfirm(false)">完成收货</van-button>
  135. </div>
  136. </div>
  137. </div>
  138. <!-- 条码输入组件 -->
  139. <input-barcode :back="back" @setBarcode="setBarcode" ref="inputBarcodeRef" />
  140. <!-- 单据选择-->
  141. <van-action-sheet
  142. v-model:show="asnDetailsTrueFalseBy"
  143. cancel-text="取消"
  144. description="请选择具体单据"
  145. close-on-click-action
  146. @closed="onAsnActionSheetClosed"
  147. >
  148. <van-cell-group>
  149. <van-cell v-for="item in asnDetailsList" @click="onDetailActive(item)">
  150. <template #title>
  151. {{ item.asnNo }}({{ item.customerId }}-{{ item.expectedQuantity }}件)
  152. </template>
  153. </van-cell>
  154. </van-cell-group>
  155. </van-action-sheet>
  156. <!-- 商品物理属性-->
  157. <attribute ref="attributeRef" @set-attribute="setAttribute" />
  158. <!-- 商品批次属性-->
  159. <lot-date ref="lotDateRef" @select-lot-date="selectLotDate" />
  160. <!-- 组合商品-->
  161. <barcode-combine ref="barcodeCombineRef" @setCombine="setCombineReceiving" @cancel="onCombineCancel" :container="containerNo" :matched-sku="combineMatchedSku" />
  162. <!-- 唯一码-->
  163. <unique-code-input ref="uniqueCodeRef"
  164. v-model:uniqueCodeList="uniqueCodeList"
  165. v-model:scanType="scanType"
  166. v-model:checkAllType="checkAllType"
  167. :searchCount="searchCount"
  168. :combine-set-count="combineReceivingSetCount"
  169. :asnInfo="asnInfo"
  170. :resolve-panpass-codes="isCombinePanpass ? resolvePanpassScan : undefined"
  171. @setUniqueCode="onConfirm(false)"
  172. />
  173. <van-action-sheet
  174. v-model:show="lotQualityTrueFalseBy"
  175. cancel-text="取消"
  176. close-on-click-action
  177. :description="'请选择'+lotTitle"
  178. >
  179. <van-cell-group>
  180. <van-cell v-for="(value,key) in lotQualityMap" @click="onSelectLotQuality(key)">
  181. <template #title>
  182. {{value}}
  183. </template>
  184. </van-cell>
  185. </van-cell-group>
  186. </van-action-sheet>
  187. </template>
  188. <script setup>
  189. import { onMounted, onUnmounted, ref, computed } from 'vue'
  190. import { androidFocus, getHeader, goBack, scanError, scanSuccess } from '@/utils/android'
  191. import InputBarcode from '@/views/outbound/picking/components/InputBarcode.vue'
  192. import { closeListener, openListener, scanInit } from '@/utils/keydownListener.js'
  193. import { useRouter } from 'vue-router'
  194. import {
  195. calculateShelfLife, getCommodityRule,
  196. getIReceivingTask,
  197. getOwnerRule,
  198. getPanpassCodeRelation,
  199. getProductAttribute, getProductLot,
  200. getReceivingAsnDetails,
  201. setProductAttribute, setReceiving,
  202. } from '@/api/takeDelivery/index'
  203. import { getListCombineSku } from '@/api/picking'
  204. import { closeLoading, showLoading } from '@/utils/loading'
  205. import { useStore } from '@/store/modules/user'
  206. import { showNotify, showToast,showConfirmDialog} from 'vant'
  207. import { isAttribute } from '@/views/inbound/takeDelivery/task/hooks/attribute'
  208. import Attribute from '@/views/inbound/takeDelivery/components/Attribute.vue'
  209. import LotDate from '@/views/inbound/takeDelivery/components/LotDate.vue'
  210. import UniqueCodeInput from '@/views/inbound/takeDelivery/components/UniqueCodeInput.vue'
  211. import BarcodeCombine from '@/views/inbound/takeDelivery/components/BarcodeCombine.vue'
  212. import { receivingBarcodeCombine } from '@/views/inbound/takeDelivery/task/hooks/barcodeCombine'
  213. import { barcodeToUpperCase, toMap } from '@/utils/dataType'
  214. import { getCurrentTime } from '@/utils/date'
  215. const router = useRouter()
  216. const store = useStore()
  217. try {
  218. getHeader()
  219. androidFocus()
  220. } catch (error) {
  221. router.push('/login')
  222. }
  223. const warehouse = store.warehouse
  224. //开单任务号
  225. const taskNo = ref('')
  226. //容器号
  227. const containerNo = ref('')
  228. //商品条码
  229. const searchBarcode = ref('')
  230. //收货数量
  231. const searchCount = ref('')
  232. //收货详情
  233. const taskInfo = ref({ receivedQty: 0, expectedQty: 0 })
  234. //开始时间
  235. const currentTime = ref('--')
  236. const scanType = ref(2)
  237. //任务号下所有asn单数据
  238. const allAsnDetailList=ref([])
  239. const type=localStorage.getItem('checkAllType')?JSON.parse(localStorage.getItem('checkAllType')):true
  240. const checkAllType=ref(type)
  241. // 页面初始化
  242. onMounted(() => {
  243. openListener()
  244. scanInit(_handlerScan)
  245. loadData()
  246. })
  247. /**
  248. * 计算时分秒
  249. */
  250. // 时器的总秒数
  251. let totalSeconds = ref(0)
  252. //时分秒
  253. const formattedTime = ref('00:00:00')
  254. let windowTimer = null // 计时器的引用
  255. const updateFormattedTime = () => {
  256. let hours = Math.floor(totalSeconds.value / 3600)
  257. let minutes = Math.floor((totalSeconds.value % 3600) / 60)
  258. let seconds = totalSeconds.value % 60
  259. formattedTime.value = `${String(hours).padStart(2, '0')}:${String(minutes).padStart(2, '0')}:${String(seconds).padStart(2, '0')}`
  260. }
  261. // 启动计时器
  262. const startTimer = () => {
  263. if (!windowTimer) {
  264. windowTimer = setInterval(() => {
  265. totalSeconds.value++
  266. updateFormattedTime()
  267. }, 1000)
  268. }
  269. }
  270. // 停止计时器
  271. const stopTimer = () => {
  272. if (windowTimer) {
  273. clearInterval(windowTimer)
  274. windowTimer = null
  275. }
  276. }
  277. const back = ref(true)
  278. const inputBarcodeType = ref('task')
  279. const inputBarcodeRef = ref(null)
  280. const oldSearchBarcode = ref('')
  281. // 任务号/容器号:code 为任务号时拉取任务及ASN明细
  282. const setBarcode = (code, type) => {
  283. if (inputBarcodeType.value === 'lot') {
  284. lotData.value.forEach((lot) => {
  285. if (lot.field == lotField.value) {
  286. lot.mapping = code
  287. }
  288. })
  289. return
  290. }
  291. showLoading()
  292. if(!type){ //切换任务时初始化计时器
  293. stopTimer()
  294. formattedTime.value="00:00:00"
  295. totalSeconds.value=0
  296. }
  297. getIReceivingTask({ taskNo:code,version:'V6',warehouse }).then(res=>{
  298. back.value = true
  299. const data = res.data
  300. const applyTaskAfterFetch = () => {
  301. if (!type) {
  302. currentTime.value = getCurrentTime()
  303. startTimer()
  304. containerNo.value = ''
  305. }
  306. taskInfo.value = data
  307. taskNo.value = code
  308. scanType.value = 2
  309. uniqueCodeList.value = []
  310. parentSerialNoMap.value = {}
  311. const ownerCode = data?.customerId
  312. if (ownerCode) {
  313. getOwnerRule(ownerCode).then((ruleRes) => {
  314. ownerPanpassEnabled.value = !!ruleRes.data?.panpassEnabled
  315. }).catch(() => {
  316. ownerPanpassEnabled.value = false
  317. })
  318. } else {
  319. ownerPanpassEnabled.value = false
  320. }
  321. const params = { warehouse, asnNos: taskInfo.value?.asnNos.join(',') }
  322. getReceivingAsnDetails(params).then((detailRes) => {
  323. allAsnDetailList.value = detailRes.data
  324. })
  325. scanSuccess()
  326. }
  327. if (data.receivedQty == data.expectedQty && data.expectedQty > 0) {
  328. showConfirmDialog({
  329. title: '温馨提示',
  330. message: `${data.taskNo}任务已完成收货,是否继续收货?`,
  331. })
  332. .then(applyTaskAfterFetch)
  333. .catch(() => {
  334. reset()
  335. taskNo.value = ''
  336. taskInfo.value = {}
  337. containerNo.value = ''
  338. stopTimer()
  339. allAsnDetailList.value = []
  340. ownerPanpassEnabled.value = false
  341. scanType.value = 2
  342. uniqueCodeList.value = []
  343. parentSerialNoMap.value = {}
  344. if (type) {
  345. switchTask()
  346. } else {
  347. inputBarcodeType.value = 'task'
  348. back.value = true
  349. inputBarcodeRef.value?.show('', '请扫描开单任务号', '')
  350. }
  351. scanSuccess()
  352. })
  353. return
  354. }
  355. applyTaskAfterFetch()
  356. }).catch(err=>{
  357. ownerPanpassEnabled.value = false
  358. inputBarcodeRef.value?.show('', '请扫描开单任务号',err.message)
  359. scanError()
  360. }).finally(()=>{
  361. reset()
  362. closeLoading()
  363. })
  364. }
  365. // setBarcode('BSSH20260318000003')
  366. const switchTask = () => {
  367. inputBarcodeType.value = 'switchTask'
  368. back.value = false
  369. inputBarcodeRef.value?.show('', `请扫描开单任务号`,'')
  370. }
  371. //asn单多条明细展示
  372. const asnDetailsTrueFalseBy = ref(false)
  373. const asnDetailsList = ref([])
  374. const asnInfo = ref({})
  375. // 组合商品
  376. const barcodeCombineRef = ref(null)
  377. const combineMatchedSku = ref([])
  378. const combineAsnSelectList = ref([])
  379. const isCombineSelectMode = ref(false)
  380. const combineReceivingData = ref([]) // 确认实收数后暂存,完成收货时提交
  381. /** 组合商品收货套数 */
  382. const combineReceivingSetCount = ref(0)
  383. /** 重置组合收货状态 */
  384. function resetCombineProductState() {
  385. barcodeCombineRef.value?.hide?.()
  386. combineMatchedSku.value = []
  387. combineAsnSelectList.value = []
  388. isCombineSelectMode.value = false
  389. combineReceivingData.value = []
  390. combineReceivingSetCount.value = 0
  391. asnDetailsTrueFalseBy.value = false
  392. }
  393. /** 超收:多 ASN 选单已打开(无明细造行,选单中) */
  394. const overReceiveSheetActive = ref(false)
  395. const showOverReceiveTag = computed(
  396. () =>
  397. !!asnInfo.value?.isOverReceive
  398. || (overReceiveSheetActive.value && !!asnDetailsTrueFalseBy.value),
  399. )
  400. const onAsnActionSheetClosed = () => {
  401. if (overReceiveSheetActive.value && !asnInfo.value?.asnNo) {
  402. overReceiveSheetActive.value = false
  403. }
  404. }
  405. const reset = () => {
  406. resetCombineProductState()
  407. overReceiveSheetActive.value = false
  408. asnInfo.value = {}
  409. lotData.value = []
  410. lotMap.value={}
  411. searchCount.value = ''
  412. searchBarcode.value = ''
  413. oldSearchBarcode.value = ''
  414. uniqueCodeList.value = []
  415. parentSerialNoMap.value = {}
  416. }
  417. const onDetailActive = (item) => {
  418. if (isCombineSelectMode.value) {
  419. _onCombineAsnSelected(item)
  420. return
  421. }
  422. asnInfo.value = item
  423. overReceiveSheetActive.value = false
  424. asnDetailsTrueFalseBy.value = false
  425. searchCount.value=1
  426. _getProductAttribute(item)
  427. _getProductLot(item)
  428. _getCommodityRule(item)
  429. }
  430. const onAsnCancel = () => {
  431. if (searchBarcode.value === '' || (oldSearchBarcode.value.length != searchBarcode.value.length && oldSearchBarcode.value != '')) {
  432. resetCombineProductState()
  433. asnInfo.value = {}
  434. lotData.value = []
  435. lotMap.value={}
  436. searchCount.value = ''
  437. }
  438. }
  439. const uniqueCodeList = ref([])
  440. /** 外箱唯一码 -> 内件唯一码 */
  441. const parentSerialNoMap = ref({})
  442. /** 货主规则组合收货需扫外箱码获取内件唯一码 */
  443. const ownerPanpassEnabled = ref(false)
  444. /** 获取内件唯一码 */
  445. function parsePanpassChildCodes(apiRes) {
  446. const outer = apiRes?.data
  447. const childPayload =
  448. outer && typeof outer === 'object' && Array.isArray(outer.childCodes)
  449. ? outer
  450. : outer?.data
  451. if (childPayload == null || typeof childPayload !== 'object') return null
  452. const fromApi = childPayload.childCodes
  453. if (!Array.isArray(fromApi) || fromApi.length === 0) return null
  454. return fromApi.map(String).filter(Boolean)
  455. }
  456. function resetUniqueCodeDialogFocus() {
  457. const dialogCmp = uniqueCodeRef.value
  458. if (!dialogCmp) return
  459. dialogCmp.uniqueBarcode = ''
  460. // 组合收货弹窗内仅展示唯一码输入(条码行隐藏),高亮须保持为 unique
  461. const combineOnlyUnique = Number(combineReceivingSetCount.value) > 0
  462. dialogCmp.uniqueCodeScanType = combineOnlyUnique || !checkAllType.value ? 'unique' : 'barcode'
  463. }
  464. async function resolvePanpassScan(code) {
  465. showLoading()
  466. try {
  467. const customerId = asnInfo.value.customerId || taskInfo.value.customerId
  468. if (!taskNo.value || !customerId) {
  469. showNotify({ type: 'danger', duration: 3000, message: '缺少任务号或货主,无法校验兆信唯一码' })
  470. scanError()
  471. return null
  472. }
  473. const res = await getPanpassCodeRelation({
  474. docNo: taskNo.value,
  475. uniqueCode: code,
  476. customer: customerId,
  477. })
  478. const newPsId = String(res?.data?.data?.newPsId ?? '').trim()
  479. if (newPsId) {
  480. const expected = barcodeToUpperCase(newPsId)
  481. const match = [asnInfo.value.barcode, asnInfo.value.barcode2, asnInfo.value.sku, uniqueCodeRef.value?.uniqueBarcode]
  482. .filter(Boolean)
  483. .some((c) => barcodeToUpperCase(String(c)) === expected)
  484. if (!match) {
  485. showNotify({ type: 'danger', duration: 3000, message: `商品条码不一致:${newPsId},请检查` })
  486. scanError()
  487. return null
  488. }
  489. }
  490. const childCodes = parsePanpassChildCodes(res)
  491. if (!childCodes?.length) {
  492. const uniqueRegExp = uniqueRuleMap.value['sku']
  493. ? uniqueRuleMap.value['sku']
  494. : uniqueRuleMap.value['all']
  495. if (uniqueRegExp) {
  496. const isValidCode = new RegExp(uniqueRegExp).test(code)
  497. if (!isValidCode) {
  498. scanError()
  499. showNotify({ type: 'danger', duration: 3000, message: `唯一码《${code}》不符合规则、请重新扫描` })
  500. return null
  501. }
  502. }
  503. const prev = uniqueCodeList.value
  504. if (prev.includes(code)) {
  505. showNotify({ type: 'danger', duration: 3000, message: `唯一码《${code}》已存在列表内请重新扫描` })
  506. scanError()
  507. return null
  508. }
  509. if (prev.length + 1 > Number(searchCount.value)) {
  510. showNotify({ type: 'danger', duration: 3000, message: '唯一码数量超过收货数量' })
  511. scanError()
  512. return null
  513. }
  514. uniqueCodeList.value = [...prev, code]
  515. showNotify({ type: 'warning', duration: 3000, message: '未获取到内箱唯一码' })
  516. scanSuccess()
  517. return [code]
  518. }
  519. const prev = uniqueCodeList.value
  520. if (childCodes.some((childCode) => prev.includes(childCode))) {
  521. const duplicateCode = childCodes.find((childCode) => prev.includes(childCode))
  522. showNotify({ type: 'danger', duration: 3000, message: `唯一码《${duplicateCode}》已存在列表内请重新扫描` })
  523. scanError()
  524. return null
  525. }
  526. const merged = prev.length + childCodes.length
  527. if (merged > Number(searchCount.value)) {
  528. showNotify({ type: 'danger', duration: 3000, message: '唯一码数量超过收货数量' })
  529. scanError()
  530. return null
  531. }
  532. uniqueCodeList.value = [...prev, ...childCodes]
  533. const parentCode = String(code).trim()
  534. const mergedMap = { ...parentSerialNoMap.value }
  535. for (const cc of childCodes) {
  536. mergedMap[String(cc)] = parentCode
  537. }
  538. parentSerialNoMap.value = mergedMap
  539. scanSuccess()
  540. return childCodes
  541. } catch {
  542. scanError()
  543. return null
  544. } finally {
  545. closeLoading()
  546. }
  547. }
  548. /** 兆信开启且当前为组合商品:外箱码获取内件唯一码 */
  549. const isCombinePanpass = computed(
  550. () => ownerPanpassEnabled.value && combineReceivingData.value.length > 0,
  551. )
  552. /**
  553. * 无 ASN 行明细时(allAsnDetailList 无匹配/为空)的「超收」占位行:不依赖待收货列表校验,由任务号 + 选中 ASN 构造。
  554. */
  555. const buildOverReceiveAsnLine = (asnNo, code) => {
  556. const wh = taskInfo.value.warehouse || warehouse
  557. const skuU = barcodeToUpperCase(code)
  558. return {
  559. warehouse: wh,
  560. customerId: taskInfo.value.customerId,
  561. sku: skuU,
  562. barcode: skuU,
  563. asnNo,
  564. expectedQuantity: 0,
  565. receivedQuantity: 0,
  566. /** 与正常 ASN 行区分,用于页面「超收」标记与逻辑 */
  567. isOverReceive: true,
  568. }
  569. }
  570. /**
  571. * 组合接口查不到时
  572. */
  573. const _fallbackReceiveByProductLotProbe = (code) => {
  574. const { asnNos = [], warehouse, customerId } = taskInfo.value
  575. const item = { warehouse, customerId, sku: barcodeToUpperCase(code) }
  576. const continueOverReceiveLine = (line) => {
  577. overReceiveSheetActive.value = false
  578. asnInfo.value = line
  579. searchCount.value = 1
  580. _getProductAttribute(line)
  581. _getProductLot(line)
  582. _getCommodityRule(line)
  583. closeLoading()
  584. scanSuccess()
  585. }
  586. _getProductLot(item)
  587. .then(() => {
  588. if (lotData.value.length === 0) {
  589. closeLoading()
  590. scanError()
  591. return
  592. }
  593. if (asnNos.length === 0) {
  594. closeLoading()
  595. scanError()
  596. showNotify({ type: 'danger', duration: 3000, message: '任务无 ASN 单,无法超收' })
  597. return
  598. }
  599. if (asnNos.length > 1) {
  600. const synthetic = asnNos.map((asnNo) => buildOverReceiveAsnLine(asnNo, code))
  601. asnDetailsList.value = synthetic
  602. asnInfo.value = {}
  603. lotData.value = []
  604. lotMap.value = {}
  605. searchCount.value = ''
  606. uniqueCodeList.value = []
  607. parentSerialNoMap.value = {}
  608. overReceiveSheetActive.value = true
  609. asnDetailsTrueFalseBy.value = true
  610. closeLoading()
  611. scanSuccess()
  612. return
  613. }
  614. continueOverReceiveLine(buildOverReceiveAsnLine(asnNos[0], code))
  615. })
  616. .catch(() => {
  617. closeLoading()
  618. scanError()
  619. })
  620. }
  621. // 组合商品只支持1个
  622. const _handleCombineProduct = (code) => {
  623. showLoading()
  624. getListCombineSku({ combineSku: barcodeToUpperCase(code), workEnvironment: 'receiving' }).then((res) => {
  625. const _err = (msg) => { closeLoading(); scanError(); showNotify({ type: 'danger', duration: 3000, message: msg }); reset() }
  626. if (!res.data?.length) return _fallbackReceiveByProductLotProbe(code)
  627. if (res.data.length > 1) return _err('不支持多商品组合商品')
  628. const combineData = res.data
  629. const matchedList = receivingBarcodeCombine(allAsnDetailList.value, toMap(combineData, 'barcode'))
  630. if (!matchedList.length) return _fallbackReceiveByProductLotProbe(code)
  631. const asnGroupMap = matchedList.reduce((acc, detail) => {
  632. const key = detail.asnNo
  633. if (!acc[key]) acc[key] = { asnNo: detail.asnNo, customerId: detail.customerId, expectedQuantity: 0, list: [] }
  634. acc[key].list.push(detail)
  635. acc[key].expectedQuantity += (detail.expectedQuantity || 0) - (detail.receivedQuantity || 0)
  636. return acc
  637. }, {})
  638. const asnOptions = Object.values(asnGroupMap)
  639. if (asnOptions.length > 1) {
  640. isCombineSelectMode.value = true
  641. combineAsnSelectList.value = asnOptions
  642. combineMatchedSku.value = matchedList
  643. asnDetailsList.value = asnOptions.map((opt) => ({ asnNo: opt.asnNo, customerId: opt.customerId, expectedQuantity: opt.expectedQuantity }))
  644. asnDetailsTrueFalseBy.value = true
  645. } else {
  646. _showCombineDialog(matchedList)
  647. }
  648. closeLoading()
  649. scanSuccess()
  650. }).catch(() => { closeLoading(); scanError() })
  651. }
  652. const _showCombineDialog = (matchedList) => {
  653. combineMatchedSku.value = matchedList
  654. asnInfo.value = matchedList[0]
  655. _getProductAttribute(matchedList[0])
  656. _getProductLot(matchedList[0])
  657. _getCommodityRule(matchedList[0])
  658. barcodeCombineRef.value?.show()
  659. }
  660. // 组合商品取消:收货数量=1套总件数
  661. const onCombineCancel = () => {
  662. const total = combineMatchedSku.value.reduce((sum, row) => sum + (row.matchedJson?.quantity || 0), 0)
  663. searchCount.value = total ? String(total) : '1'
  664. combineReceivingData.value = []
  665. combineReceivingSetCount.value = 0
  666. }
  667. const _onCombineAsnSelected = (item) => {
  668. const selected = combineAsnSelectList.value.find((opt) => opt.asnNo === item.asnNo)
  669. if (selected?.list) _showCombineDialog(selected.list)
  670. asnDetailsTrueFalseBy.value = isCombineSelectMode.value = false
  671. combineAsnSelectList.value = []
  672. }
  673. // 组合商品确认实收数
  674. const setCombineReceiving = ({ dataList, count: setCount }) => {
  675. if (!dataList?.length) return
  676. const total = dataList.reduce((sum, row) => sum + (row.quantity || 0), 0)
  677. searchCount.value = String(total)
  678. combineReceivingData.value = dataList
  679. const sets = Number(setCount) > 0 ? Number(setCount) : 0
  680. combineReceivingSetCount.value = sets
  681. showNotify({ type: 'success', duration: 2000, message: `收货套数:${sets},共 ${total} 件,请点击完成收货提交` })
  682. }
  683. /** 批次拉取与效期计算的代际,避免快速切换商品时异步结果错乱 */
  684. let productLotGeneration = 0
  685. // 条码扫描:scanType 2商品/4数量/3唯一码/5容器
  686. const _handlerScan = (code) => {
  687. if (scanType.value == 2) {
  688. searchBarcode.value = code
  689. oldSearchBarcode.value = code
  690. resetCombineProductState()
  691. overReceiveSheetActive.value = false
  692. const upperCode = barcodeToUpperCase(code) || ''
  693. const clientMatched =
  694. allAsnDetailList.value.length > 0
  695. ? allAsnDetailList.value.filter((detail) => {
  696. const bars = [detail.barcode, detail.barcode2, detail.sku].filter(Boolean)
  697. return bars.some((bar) => bar && barcodeToUpperCase(bar) === upperCode)
  698. })
  699. : []
  700. asnDetailsList.value = clientMatched
  701. uniqueCodeList.value=[]
  702. parentSerialNoMap.value = {}
  703. if (asnDetailsList.value.length > 0) {
  704. scanSuccess()
  705. closeLoading()
  706. if (asnDetailsList.value.length == 1) {
  707. const item = asnDetailsList.value[0]
  708. asnInfo.value = item
  709. searchCount.value = 1
  710. _getProductAttribute(item)
  711. _getProductLot(item)
  712. _getCommodityRule(item)
  713. }
  714. if (asnDetailsList.value.length > 1) {
  715. asnInfo.value = {}
  716. lotData.value = []
  717. lotMap.value={}
  718. searchCount.value = ''
  719. uniqueCodeList.value = []
  720. parentSerialNoMap.value = {}
  721. asnDetailsTrueFalseBy.value = true
  722. }
  723. } else {
  724. _handleCombineProduct(code)
  725. }
  726. } else if (scanType.value == 3) {
  727. if (code) {
  728. const uniqueCodeScanType = uniqueCodeRef.value?.uniqueCodeScanType
  729. if (checkAllType.value && uniqueCodeScanType === 'barcode') {
  730. const barcode = Array.from(new Set([asnInfo.value.barcode, asnInfo.value.barcode2, asnInfo.value.sku].filter(Boolean)));
  731. if (barcode.some((bar) => barcodeToUpperCase(bar) === barcodeToUpperCase(code))) {
  732. scanSuccess();
  733. uniqueCodeRef.value.uniqueCodeScanType = 'unique'
  734. uniqueCodeRef.value.uniqueBarcode = code
  735. return;
  736. } else {
  737. showNotify({ type: 'danger', duration: 3000, message: `${code}-商品条码不匹配,请重新扫描` })
  738. uniqueCodeRef.value.uniqueBarcode = ''
  739. scanError();
  740. return;
  741. }
  742. }
  743. // 唯一码扫描
  744. if (
  745. !isCombinePanpass.value &&
  746. uniqueCodeScanType === 'unique' &&
  747. uniqueCodeRef.value?.uniqueBarcode == '' &&
  748. checkAllType.value
  749. ) {
  750. showNotify({ type: 'danger', duration: 3000, message: '请先扫描商品条码' });
  751. uniqueCodeRef.value.uniqueCodeScanType = 'barcode';
  752. scanError();
  753. return;
  754. }
  755. if (isCombinePanpass.value) {
  756. void resolvePanpassScan(code).then((panpassResult) => panpassResult != null && resetUniqueCodeDialogFocus())
  757. return
  758. }
  759. const uniqueRegExp = uniqueRuleMap.value['sku'] ? uniqueRuleMap.value['sku'] : uniqueRuleMap.value['all']
  760. const isValidCode = new RegExp(uniqueRegExp).test(code)
  761. if (!isValidCode) {
  762. scanError()
  763. showNotify({ type: 'danger', duration: 3000, message: `唯一码《${code}》不符合规则、请重新扫描` })
  764. return
  765. }
  766. if (uniqueCodeList.value.includes(code)) {
  767. scanError()
  768. showNotify({ type: 'danger', duration: 3000, message: `唯一码《${code}》已存在列表内请重新扫描` })
  769. return
  770. }
  771. scanSuccess()
  772. uniqueCodeList.value.unshift(code)
  773. resetUniqueCodeDialogFocus()
  774. }
  775. }else if(scanType.value==5){
  776. containerNo.value=code
  777. scanType.value=2
  778. }
  779. }
  780. // 物理属性
  781. const attributeRef = ref(null)
  782. const attributeMap = ref({})
  783. const attributeTrueFalseBy = ref(true)
  784. // 获取商品物理属性
  785. const _getProductAttribute = (item) => {
  786. const params = { warehouse: item.warehouse, owner: item.customerId, barcode: item.sku }
  787. getProductAttribute(params).then(res => {
  788. attributeMap.value = res.data
  789. const isAttributeInfo = isAttribute(res.data)
  790. if (isAttributeInfo.length > 0) {
  791. attributeTrueFalseBy.value = false
  792. attributeRef.value?.show(isAttributeInfo, res.data)
  793. } else {
  794. attributeTrueFalseBy.value = true
  795. }
  796. })
  797. }
  798. // 设置商品物理属性
  799. const setAttribute = (data) => {
  800. const params = { warehouse, owner: taskInfo.value.customerId, sku: attributeMap.value.sku }
  801. showLoading()
  802. setProductAttribute(params, { ...attributeMap.value, ...data }).then(res => {
  803. showToast({ duration: 3000, message: '商品物理属性设置成功' })
  804. attributeTrueFalseBy.value = true
  805. scanSuccess()
  806. }).catch(err => {
  807. closeLoading()
  808. scanError()
  809. })
  810. }
  811. // 批次属性
  812. const lotData = ref([])
  813. const lotMap = ref({})
  814. const _getProductLot = (item) => {
  815. const gen = ++productLotGeneration
  816. lotMap.value = {}
  817. lotData.value = []
  818. const params = { warehouse: item.warehouse, owner: item.customerId, barcode: item.sku }
  819. return getProductLot(params).then((res) => {
  820. if (gen !== productLotGeneration) return
  821. const rows = res.data
  822. if (!rows?.length) {
  823. lotData.value = []
  824. return
  825. }
  826. rows.forEach((lot) => {
  827. const lotField = lot.field
  828. if (lotField.startsWith('lotAtt') && lotField.length === 8) {
  829. lot.mapping = item[lotField]
  830. }
  831. if (lot.format) {
  832. const format = JSON.parse(lot.format)
  833. lotMap.value = { ...lotMap.value, ...format }
  834. }
  835. })
  836. lotData.value = rows.filter((row) => row.lotAttFlag !== '隐藏')
  837. _calculateShelfLife(item, lotData.value, gen)
  838. })
  839. }
  840. // 计算效期
  841. const _calculateShelfLife = (item, lotData, generation) => {
  842. if ((item.lotAtt01 && item.lotAtt02) || (!item.lotAtt01 && !item.lotAtt02)) return
  843. const params = {
  844. customer: item.customerId,
  845. barcode: item.sku,
  846. date: item.lotAtt01 || item.lotAtt02,
  847. isExpiryDate: item.lotAtt01 ? true : false,
  848. }
  849. calculateShelfLife(params).then((date) => {
  850. if (generation !== productLotGeneration) return
  851. lotData.forEach((lot) => {
  852. if ((lot.field === 'lotAtt01' && !params.isExpiryDate) ||
  853. (lot.field === 'lotAtt02' && params.isExpiryDate)) {
  854. lot.mapping = date.data
  855. }
  856. })
  857. })
  858. }
  859. //日期选择
  860. const lotField = ref('')
  861. const lotDateRef = ref(null)
  862. const lotQualityTrueFalseBy=ref(false)
  863. const lotQualityMap=ref({})
  864. const lotTitle=ref('质量状态')
  865. const onLot = (item) => {
  866. lotTitle.value=item.label
  867. lotField.value = item.field
  868. if (item.field == 'lotAtt05' ) return
  869. if (item.type == 'Enum' ){
  870. lotQualityMap.value=JSON.parse(item.format)
  871. lotQualityTrueFalseBy.value = true
  872. return
  873. }
  874. if (item.type == 'Date') {
  875. lotDateRef.value?.show(item.label, item.mapping)
  876. return
  877. }
  878. if (item.type == 'String') {
  879. inputBarcodeType.value = 'lot'
  880. back.value = false
  881. inputBarcodeRef.value?.show('', `请扫描${item.label}`,'')
  882. return
  883. }
  884. }
  885. //设置质量状态
  886. const onSelectLotQuality=(qualityKey)=>{
  887. lotData.value.forEach((lot) => {
  888. if (lot.field == lotField.value) {
  889. lot.mapping = qualityKey
  890. }
  891. })
  892. lotQualityTrueFalseBy.value=false
  893. }
  894. // 设置日期
  895. const selectLotDate = (date) => {
  896. const lotMap = toMap(lotData.value, 'field', 'mapping')
  897. if ((lotField.value === 'lotAtt01' || lotField.value === 'lotAtt02') &&
  898. lotMap['lotAtt01'] == '' || lotMap['lotAtt01'] == null && lotMap['lotAtt02'] == '' || lotMap['lotAtt02'] == null) {
  899. const params = {
  900. customer: asnInfo.value.customerId,
  901. barcode: asnInfo.value.sku,
  902. date,
  903. isExpiryDate: lotField.value === 'lotAtt01' ? true : false,
  904. }
  905. calculateShelfLife(params).then(res => {
  906. if(res.data){
  907. lotData.value.forEach((lot) => {
  908. if (lot.field === 'lotAtt01') {
  909. lot.mapping = (lotField.value === 'lotAtt01') ? date : res.data
  910. } else if (lot.field === 'lotAtt02') {
  911. lot.mapping = (lotField.value === 'lotAtt02') ? date : res.data
  912. }
  913. })
  914. }else {
  915. lotData.value.forEach((lot) => {
  916. if (lot.field === lotField.value) {
  917. lot.mapping = date
  918. }
  919. })
  920. }
  921. })
  922. }
  923. lotData.value.forEach((lot) => {
  924. if (lot.field === lotField.value) {
  925. lot.mapping = date
  926. }
  927. })
  928. inputBarcodeType.value = 'task'
  929. }
  930. // 唯一码
  931. const uniqueCodeRef = ref(null)
  932. //规则列表
  933. const uniqueRuleList = ref([])
  934. const uniqueRuleMap = ref({})
  935. // 获取商品规则
  936. const _getCommodityRule = (item) => {
  937. const params = { customer: item.customerId, sku: item.sku, input: 'RECEIVING' }
  938. getCommodityRule(params).then(res => {
  939. uniqueRuleList.value = res.data
  940. res.data.forEach((item, index) => {
  941. if (item.sku == '') {
  942. item.type = 'all'
  943. } else {
  944. item.type = 'sku'
  945. }
  946. })
  947. uniqueRuleMap.value = toMap(res.data, 'type', 'uniqueRegExp')
  948. }).catch(() => {
  949. closeLoading()
  950. scanError()
  951. })
  952. }
  953. const containerNoRef = ref(null)
  954. const numberRef = ref(null)
  955. // 完成收货前校验
  956. const isCheck = () => {
  957. if (!asnInfo.value.asnNo) {
  958. scanError()
  959. showToast({ duration: 3000, message: '请先查询商品收货信息' })
  960. return false
  961. }
  962. //商品物理属性判断
  963. if (!attributeTrueFalseBy.value) {
  964. const isAttributeInfo = isAttribute(attributeMap.value)
  965. if (isAttributeInfo.length > 0) {
  966. attributeTrueFalseBy.value = false
  967. attributeRef.value?.show(isAttributeInfo, attributeMap.value)
  968. }
  969. return false
  970. }
  971. // //商品批次属性判断(超收不校验 lotAtt05 必填)
  972. const incompleteLot = lotData.value.find(
  973. (lot) =>
  974. lot.require &&
  975. !lot.mapping &&
  976. !(asnInfo.value.isOverReceive && lot.field === 'lotAtt05'),
  977. )
  978. if (incompleteLot) {
  979. scanError()
  980. showToast({ duration: 3000, message: `请先补充${incompleteLot.label}` })
  981. return false
  982. }
  983. const lotMap = toMap(lotData.value, 'field', 'mapping')
  984. const productionDate = lotMap['lotAtt01'] ? new Date(lotMap['lotAtt01']) : null
  985. const expirationDate = lotMap['lotAtt02'] ? new Date(lotMap['lotAtt02']) : null
  986. const currentDate = new Date()
  987. // 如果存在失效日期
  988. if (expirationDate) {
  989. // 如果有生产日期,进行有效性检查
  990. if (productionDate && expirationDate <= productionDate) {
  991. scanError()
  992. showToast({ duration: 3000, message: `失效日期不能小于等于生产日期` })
  993. return false
  994. }
  995. }
  996. if(productionDate){
  997. // 如果有生产日期,进行有效性检查
  998. if (expirationDate && productionDate >= expirationDate) {
  999. scanError()
  1000. showToast({ duration: 3000, message: `生产日期不能大于失效日期` })
  1001. return false
  1002. }
  1003. // 检查生产日期是否小于当前日期
  1004. if (productionDate >= currentDate) {
  1005. scanError()
  1006. showToast({ duration: 3000, message: `生产日期不能大于等于当前日期` })
  1007. return false
  1008. }
  1009. }
  1010. if (searchCount.value == '') {
  1011. numberRef.value?.focus()
  1012. scanError()
  1013. showToast({ duration: 3000, message: '请先输入收货数量' })
  1014. return false
  1015. }
  1016. if (containerNo.value == '') {
  1017. scanError()
  1018. containerNoRef.value?.focus()
  1019. showToast({ duration: 3000, message: '请先输入容器号' })
  1020. return false
  1021. }
  1022. const needUnique = uniqueRuleList.value.length > 0 || isCombinePanpass.value
  1023. if (needUnique && uniqueCodeList.value.length != searchCount.value) {
  1024. scanType.value = 3
  1025. const tips = isCombinePanpass.value
  1026. ? `收货数量:${searchCount.value},请扫描组合外箱唯一码`
  1027. : `收货数量:${searchCount.value}`
  1028. uniqueCodeRef.value?.show('', '请扫描唯一码', tips, uniqueRuleMap.value)
  1029. return false
  1030. }
  1031. return true
  1032. }
  1033. // 完成收货
  1034. const onConfirm = (confirmOverReceive=false) => {
  1035. if(isCheck()){
  1036. const lotMap = toMap(lotData.value, 'field', 'mapping')
  1037. const { asnLineNo, asnNo, warehouse,customerId,sku} = asnInfo.value
  1038. const {taskNo: taskCode } = taskInfo.value
  1039. const serialParentMap = parentSerialNoMap.value
  1040. const data = {
  1041. asnLineNo,
  1042. asnNo,
  1043. containerId: containerNo.value,
  1044. quantity: searchCount.value,
  1045. warehouse,
  1046. customerId,
  1047. serialNos: uniqueCodeList.value.length > 0 ? uniqueCodeList.value : undefined,
  1048. ...(Object.keys(serialParentMap).length > 0 ? { parentSerialNoMap: { ...serialParentMap } } : {}),
  1049. ...lotMap,
  1050. taskNo:taskCode,
  1051. sku,
  1052. confirmOverReceive
  1053. }
  1054. showLoading()
  1055. inputBarcodeType.value='task'
  1056. setReceiving(data).then(res => {
  1057. console.log('res',res)
  1058. if(res.data.overReceive){
  1059. scanError()
  1060. showConfirmDialog({
  1061. title: '温馨提示',
  1062. message: '是否确认超收?',
  1063. }).then(() => {
  1064. onConfirm(true)
  1065. }).catch(() => {
  1066. })
  1067. }else{
  1068. scanSuccess()
  1069. showNotify({ type: 'success', duration: 3000, message: `${searchBarcode.value}收货完成,请继续收货!`})
  1070. setBarcode(taskNo.value, '2')
  1071. reset()
  1072. taskInfo.value={}
  1073. scanType.value=2
  1074. closeLoading()
  1075. }
  1076. }).catch(err => {
  1077. if(err.message.includes('序列号已存在')){
  1078. scanType.value = 3
  1079. uniqueCodeRef.value?.show('', '请扫描唯一码', `收货数量:${searchCount.value},${err.message}`, uniqueRuleMap.value)
  1080. }
  1081. scanError()
  1082. closeLoading()
  1083. }).finally(() => {
  1084. closeLoading()
  1085. })
  1086. }
  1087. }
  1088. // 数据刷新
  1089. const loadData = () => {
  1090. if (!taskNo.value) {
  1091. inputBarcodeRef.value?.show('', '请扫描开单任务号','')
  1092. return
  1093. }
  1094. }
  1095. onUnmounted(() => {
  1096. closeListener()
  1097. stopTimer()
  1098. })
  1099. window.onRefresh = loadData
  1100. </script>
  1101. <style scoped lang="scss">
  1102. .over-receive-hint {
  1103. display: flex;
  1104. align-items: center;
  1105. gap: 8px;
  1106. margin: 8px 10px 0;
  1107. padding: 8px 10px;
  1108. background: #fff4e5;
  1109. border: 1px solid #f5d4a3;
  1110. border-radius: 6px;
  1111. font-size: 13px;
  1112. color: #8a5a00;
  1113. line-height: 1.4;
  1114. }
  1115. .over-receive-hint__badge {
  1116. flex-shrink: 0;
  1117. padding: 2px 8px;
  1118. font-size: 12px;
  1119. font-weight: 600;
  1120. color: #fff;
  1121. background: linear-gradient(135deg, #e67e22, #d35400);
  1122. border-radius: 4px;
  1123. }
  1124. .over-receive-hint__text {
  1125. flex: 1;
  1126. min-width: 0;
  1127. text-align: left;
  1128. }
  1129. .take-delivery {
  1130. .take-info {
  1131. padding: 6px 10px;
  1132. background: linear-gradient(160deg, #cfe1ff 20%, white 50%, white 100%);
  1133. display: flex;
  1134. flex-direction: column;
  1135. text-align: left;
  1136. .take-info-no {
  1137. flex: 1;
  1138. .info-no-title {
  1139. font-size: 19px;
  1140. font-weight: 500;
  1141. display: flex;
  1142. justify-content: space-between;
  1143. align-items: center;
  1144. }
  1145. .info-no-tips {
  1146. font-size: 14px;
  1147. color: #666666;
  1148. display: flex;
  1149. justify-content: space-between;
  1150. padding: 6px 0;
  1151. }
  1152. }
  1153. .take-info-number {
  1154. flex: 1;
  1155. border-top: 1.5px solid #efefef;
  1156. display: flex;
  1157. justify-content: space-between;
  1158. gap: 10px;
  1159. color: #666;
  1160. font-size: 14px;
  1161. padding-top: 10px;
  1162. .info-number-left {
  1163. flex: 1;
  1164. display: flex;
  1165. justify-content: space-evenly;
  1166. align-items: center;
  1167. .number-left-box {
  1168. flex: 1;
  1169. display: flex;
  1170. flex-direction: column;
  1171. align-items: center;
  1172. .left-box-title {
  1173. font-size: 14px;
  1174. font-weight: bold;
  1175. color: #000;
  1176. line-height: 34px;
  1177. }
  1178. }
  1179. }
  1180. .info-number-right {
  1181. width: 40%;
  1182. text-align: center;
  1183. .van-search {
  1184. padding: 0;
  1185. }
  1186. }
  1187. }
  1188. }
  1189. .take-barcode {
  1190. margin-top: 10px;
  1191. text-align: left;
  1192. background: #ffffff;
  1193. .barcode-input {
  1194. height: 50px;
  1195. ::v-deep(.van-search) {
  1196. padding: 0;
  1197. }
  1198. ::v-deep(.van-search__field) {
  1199. border-bottom: 2px solid #ffffff;
  1200. height: 50px;
  1201. display: flex;
  1202. align-items: center;
  1203. }
  1204. ::v-deep(.van-search__content) {
  1205. background: #fff;
  1206. height: 50px;
  1207. display: flex;
  1208. align-items: center;
  1209. }
  1210. ::v-deep(.van-search__label) {
  1211. font-size: 16px;
  1212. font-weight: bold;
  1213. line-height: 50px;
  1214. height: 50px;
  1215. display: flex;
  1216. align-items: center;
  1217. }
  1218. ::v-deep(.van-field__control) {
  1219. font-size: 16px;
  1220. font-weight: bold;
  1221. height: 50px;
  1222. line-height: 50px;
  1223. display: flex;
  1224. align-items: center;
  1225. }
  1226. .search-input-barcode {
  1227. ::v-deep(.van-search__field) {
  1228. border-bottom: 2px solid #0077ff;
  1229. z-index: 2;
  1230. }
  1231. }
  1232. }
  1233. }
  1234. .take-lot {
  1235. text-align: left;
  1236. margin-top: 5px;
  1237. ::v-deep(.van-cell) {
  1238. padding: 5px 8px;
  1239. }
  1240. .take-lot-title {
  1241. font-size: 15px;
  1242. font-weight: bold;
  1243. padding: 0 5px;
  1244. border-left: 3px solid #1989fa;
  1245. color: #333;
  1246. margin-bottom: 3px;
  1247. }
  1248. }
  1249. .take-button {
  1250. padding: 20px;
  1251. }
  1252. }
  1253. </style>