UniqueCodeInput.vue 10.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306
  1. <template>
  2. <div class="unique-code">
  3. <van-dialog v-model:show="uniqueCodeTrueFalseBy"
  4. :keyboardEnabled="false"
  5. :beforeClose="beforeClose"
  6. :confirm-button-text="'完成收货'"
  7. title="唯一码录入"
  8. show-cancel-button>
  9. <div>
  10. <div class="unique-code-type">
  11. <div class="tips">{{ tag }}</div>
  12. <div v-if="!isCombineMode">
  13. <div :class="newCheckAllType?'unique-v-active':'unique-v-no-active'" @click="onCheckAllType"> <van-icon name="exchange" /> 条码校验</div>
  14. <!-- <van-button type="primary" size="mini" :plain="!newCheckAllType" @click="onCheckAllType" icon="exchange" >条码校验</van-button>-->
  15. </div>
  16. </div>
  17. <div v-if="combineSetCount > 0" class="combine-set-info">
  18. 组合{{ combineSetCount }}套 | 唯一码{{ containerList.length }}/{{ searchCount }}
  19. </div>
  20. <div class="unique-input-list" v-if="newCheckAllType && !isCombineMode">
  21. <div class="unique-input-list-left">商品条码:</div>
  22. <div class="unique-input-list-right">
  23. <van-field class="code-input"
  24. v-model="uniqueBarcode"
  25. ref="uniqueBarcodeRef"
  26. clearable
  27. autocomplete="off"
  28. placeholder="请扫描商品条码"
  29. type="text"
  30. @keydown.enter="uniqueBarcodeChange"
  31. :style="uniqueCodeScanType == 'barcode' ? { 'border-bottom': '2px solid #1989fa' } : {}"
  32. @focus="uniqueCodeScanType = 'barcode'"
  33. />
  34. </div>
  35. </div>
  36. <div class="unique-input-list">
  37. <div class="unique-input-list-left">唯&nbsp;&nbsp;一&nbsp;&nbsp;码:</div>
  38. <div class="unique-input-list-right">
  39. <van-field class="code-input"
  40. v-model="uniqueCode"
  41. ref="uniqueCodeRef"
  42. clearable
  43. autocomplete="off"
  44. placeholder="请扫描唯一码"
  45. type="text"
  46. @keydown.enter="onKeydown"
  47. :style="uniqueCodeScanType == 'unique' ? { 'border-bottom': '2px solid #1989fa' } : {}"
  48. @focus="uniqueCodeScanType = 'unique'"
  49. />
  50. </div>
  51. </div>
  52. </div>
  53. <div class="container-list">
  54. <div v-for="(item,i) in containerList" class="container-item" :key="i">
  55. <div style="display: flex;justify-content: space-between;align-items: center;">
  56. <div style="margin-right: 3px;">{{ containerList.length - i }}</div>
  57. <div class="container-item-line"></div>
  58. </div>
  59. <div class="container-item-no">
  60. <van-notice-bar :background="'none'" color="#000" :speed="10" :text="item" />
  61. </div>
  62. <div class="container-item-clear" @click="containerList.splice(i,1)">
  63. <van-icon name="delete-o" color="#ee0a24" size="15" />
  64. </div>
  65. </div>
  66. </div>
  67. </van-dialog>
  68. </div>
  69. </template>
  70. <script setup>
  71. import { computed, nextTick, ref } from 'vue'
  72. import { showNotify } from 'vant'
  73. import { scanError, scanSuccess } from '@/utils/android'
  74. import { barcodeToUpperCase } from '@/utils/dataType.js'
  75. const uniqueCodeTrueFalseBy = ref(false)
  76. //商品条码
  77. const uniqueBarcode=ref('')
  78. const uniqueBarcodeRef=ref(null)
  79. //唯一码
  80. const uniqueCode = ref('')
  81. const uniqueCodeRef = ref(null)
  82. const tag = ref('')
  83. // 定义传入的 props
  84. const props = defineProps({
  85. uniqueCodeList: Array,
  86. scanType: Number,
  87. searchCount: [String, Number],
  88. asnInfo:Object,
  89. checkAllType:Boolean,
  90. /** 组合商品收货套数 */
  91. combineSetCount: { type: Number, default: 0 },
  92. /** 组合外箱码获取内件码 */
  93. resolvePanpassCodes: { type: Function, default: undefined },
  94. })
  95. const isCombineMode = computed(() => Number(props.combineSetCount) > 0)
  96. const getDefaultCheckAllType = () => !isCombineMode.value && !!props.checkAllType
  97. const scanInputType = getDefaultCheckAllType() ? 'barcode' : 'unique'
  98. const uniqueCodeScanType=ref(scanInputType)
  99. // 定义自定义的 emit
  100. const emit = defineEmits(['update:scanType', 'update:uniqueCodeList', 'setUniqueCode','update:checkAllType'])
  101. const containerList = computed(() => {
  102. return props.uniqueCodeList
  103. })
  104. const uniqueRuleMap = ref({})
  105. const show = async (code, desc, tips, uniqueRule) => {
  106. if (tips !== undefined) {
  107. tag.value = tips
  108. }
  109. uniqueRuleMap.value = uniqueRule
  110. newCheckAllType.value = getDefaultCheckAllType()
  111. uniqueCodeScanType.value = newCheckAllType.value ? 'barcode' : 'unique'
  112. uniqueBarcode.value = ''
  113. uniqueCode.value = ''
  114. uniqueCodeTrueFalseBy.value = true
  115. await nextTick()
  116. if (newCheckAllType.value) uniqueBarcodeRef.value?.focus()
  117. else uniqueCodeRef.value?.focus()
  118. }
  119. const newCheckAllType = ref(getDefaultCheckAllType())
  120. // 切换校验类型
  121. const onCheckAllType=()=>{
  122. if (isCombineMode.value) return
  123. newCheckAllType.value = !newCheckAllType.value
  124. uniqueCodeScanType.value = newCheckAllType.value ? 'barcode' : 'unique';
  125. localStorage.setItem('checkAllType',newCheckAllType.value)
  126. emit('update:checkAllType',newCheckAllType.value)
  127. }
  128. const refocusAfterUniqueInput = () => {
  129. uniqueBarcode.value = ''
  130. uniqueCode.value = ''
  131. if (newCheckAllType.value && !isCombineMode.value) uniqueBarcodeRef.value?.focus()
  132. else uniqueCodeRef.value?.focus()
  133. }
  134. //商品条码验证
  135. const uniqueBarcodeChange=()=>{
  136. if (isCombineMode.value) return true
  137. if(newCheckAllType.value){
  138. if (props.resolvePanpassCodes) return true
  139. if(!uniqueBarcode.value){
  140. showNotify({ type: 'danger', duration: 3000, message: `请先扫描商品条码` })
  141. scanError()
  142. return false
  143. }
  144. const barcode = Array.from(new Set([props.asnInfo.barcode, props.asnInfo.barcode2, props.asnInfo.sku].filter(Boolean)))
  145. if (barcode.some(item => barcodeToUpperCase(item) === barcodeToUpperCase(uniqueBarcode.value))) {
  146. scanSuccess()
  147. uniqueCodeRef.value?.focus()
  148. return true
  149. }else {
  150. showNotify({ type: 'danger', duration: 3000, message: `${uniqueBarcode.value}-商品条码不匹配、请重新扫描` })
  151. uniqueBarcode.value=''
  152. scanError()
  153. return false
  154. }
  155. }
  156. return true
  157. }
  158. const onKeydown = async () => {
  159. if(!uniqueBarcodeChange()) return
  160. if (uniqueCode.value) {
  161. if (props.resolvePanpassCodes) {
  162. const result = await props.resolvePanpassCodes(uniqueCode.value)
  163. if (result == null) {
  164. uniqueCode.value = ''
  165. return
  166. }
  167. refocusAfterUniqueInput()
  168. return
  169. }
  170. const uniqueRegExp = uniqueRuleMap.value['sku'] ? uniqueRuleMap.value['sku'] : uniqueRuleMap.value['all']
  171. const isValidCode = new RegExp(uniqueRegExp).test(uniqueCode.value)
  172. if (!isValidCode) {
  173. scanError()
  174. showNotify({ type: 'danger', duration: 3000, message: `唯一码《${uniqueCode.value}》不符合规则、请重新扫描` })
  175. uniqueCode.value = ''
  176. return
  177. }
  178. if (containerList.value.includes(uniqueCode.value)) {
  179. scanError()
  180. showNotify({ type: 'danger', duration: 3000, message: `唯一码《${uniqueCode.value}》已存在列表内请重新扫描` })
  181. uniqueCode.value = ''
  182. return
  183. }
  184. scanSuccess()
  185. containerList.value.unshift(uniqueCode.value)
  186. refocusAfterUniqueInput()
  187. }
  188. emit('update:uniqueCodeList', containerList.value)
  189. }
  190. const beforeClose = (action) =>
  191. new Promise(async (resolve) => {
  192. if (action === 'confirm') {
  193. if (containerList.value.length != props.searchCount) {
  194. scanError()
  195. showNotify({
  196. type: 'danger',
  197. duration: 3000,
  198. message: `当前扫描唯一码数量:${containerList.value.length}、收货数量:${props.searchCount},请扫描相同数量唯一码`,
  199. })
  200. return resolve(false)
  201. }
  202. resolve(true)
  203. emit('update:scanType', 2)
  204. emit('setUniqueCode')
  205. } else {
  206. resolve(true)
  207. emit('update:scanType', 2)
  208. }
  209. })
  210. defineExpose({ show ,uniqueBarcode,uniqueCodeScanType })
  211. </script>
  212. <style scoped lang="sass">
  213. .unique-code
  214. .unique-code-type
  215. display: flex
  216. justify-content: space-between
  217. align-items: center
  218. padding: 0 15px
  219. .unique-v-active
  220. background: #1989fa
  221. font-size: 10px
  222. color: #FFFFFF
  223. border-radius: 5px
  224. padding: 5px
  225. .unique-v-no-active
  226. background: #6e6c6c
  227. color: #FFFFFF
  228. font-size: 10px
  229. border-radius: 5px
  230. padding: 5px
  231. .unique-input-list
  232. display: flex
  233. padding: 0 10px
  234. .unique-input-list-left
  235. display: flex
  236. justify-content: center
  237. align-items: center
  238. gap: 10px
  239. .unique-input-list-right
  240. flex: 1
  241. .code-input
  242. font-size: 22px
  243. font-weight: bold
  244. border-bottom: 2px solid #efefef
  245. margin-top: 10px
  246. .completion
  247. text-align: right
  248. font-size: 12px
  249. padding: 5px 20px
  250. cursor: pointer
  251. text-decoration: underline
  252. .container-list
  253. max-height: 100px
  254. font-size: 13px
  255. font-weight: bold
  256. display: flex
  257. margin: 10px 15px
  258. justify-content: space-between
  259. flex-wrap: wrap
  260. overflow: auto
  261. .container-item
  262. width: 99%
  263. display: flex
  264. justify-items: center
  265. align-items: center
  266. padding: 2px
  267. cursor: pointer
  268. border-bottom: 1px solid #efefef
  269. .container-item-line
  270. width: 5px
  271. height: 5px
  272. background: #0077ff
  273. border-radius: 50%
  274. margin-right: 5px
  275. .container-item-no
  276. flex: 1
  277. text-align: left
  278. text-decoration: underline
  279. .container-item-clear
  280. padding: 0 15px
  281. .container-item:last-child
  282. border-bottom: none
  283. .tips
  284. font-size: 14px
  285. color: #ff4141
  286. line-height: 20px
  287. .combine-set-info
  288. font-size: 14px
  289. color: #1989fa
  290. font-weight: bold
  291. line-height: 22px
  292. padding: 4px 15px 0
  293. width: 100%
  294. box-sizing: border-box
  295. </style>