codeMerge.vue 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703
  1. <template>
  2. <gui-page :custom-header="true" :header-class="['gui-theme-background-color']">
  3. <template #gHeader>
  4. <view style="height:44px;" class="gui-flex gui-nowrap gui-rows gui-align-items-center">
  5. <!-- 使用组件实现返回按钮及返回首页按钮 -->
  6. <text
  7. style="font-size:44rpx;"
  8. class="gui-header-leader-btns gui-color-white font-icons"
  9. @tap="goHome"
  10. >&#xe6c5;</text>
  11. <!-- 导航文本此处也可以是其他自定义内容 -->
  12. <text
  13. class="gui-h4 gui-blod gui-flex1 gui-text-center gui-ellipsis gui-color-white gui-primary-text"
  14. >条码合并</text>
  15. <!-- 此处加一个右侧展位元素与左侧同宽,实现标题居中 -->
  16. <!-- 实际宽度请根据自己情况设置 -->
  17. <view style="width:40px;" />
  18. <!-- 如果右侧有其他内容可以利用条件编译和定位来实现-->
  19. </view>
  20. </template>
  21. <template #gBody>
  22. <view class="list-content">
  23. <view class="scan">
  24. <view class="scan-card">
  25. <uni-easyinput
  26. ref="easyinput"
  27. v-model="scanNumber"
  28. :input-border="false"
  29. :clearable="false"
  30. type="text"
  31. focus
  32. @focus="handleInputFocus"
  33. placeholder="扫描内部标签"
  34. @confirm="handleKeydown"
  35. />
  36. <text class="font-icons" @click="handleMapass">&#xe6b7;</text>
  37. </view>
  38. </view>
  39. <view class="table-title">
  40. <view><text style="font-size: 16px;">合并列表</text></view>
  41. </view>
  42. <view v-if="packageList?.length > 0" class="list-panel">
  43. <!-- eslint-disable-next-line vue/valid-v-for -->
  44. <view v-for="(item, key) in packageList" v-key="key" class="panel">
  45. <uni-swipe-action>
  46. <!-- 基础用法 -->
  47. <uni-swipe-action-item :right-options="options" @click="handleRemoveDetail(key)">
  48. <template #left>
  49. <view />
  50. </template>
  51. <view class="panel-row">
  52. <text>批次</text>
  53. <text>{{ item.batchNumber }}</text>
  54. </view>
  55. <view class="panel-row">
  56. <text>数量</text>
  57. <text>{{ item.qty }} PCS</text>
  58. </view>
  59. <view class="panel-row">
  60. <text>产品编码</text>
  61. <text>{{ item.materialNo }}</text>
  62. </view>
  63. </uni-swipe-action-item>
  64. </uni-swipe-action>
  65. </view>
  66. </view>
  67. <view v-else>
  68. <view class="bg-image" style="height: 70vh !important;">
  69. <image src="@/static/empty.png" mode="heightFix" />
  70. <text>这里什么都没有...</text>
  71. </view>
  72. </view>
  73. <view class="operation-panel">
  74. <view class="operation-title">合并数量:
  75. <text style="font-size: 16px;font-weight: bold;">{{ computedRemainder }}</text>
  76. <text
  77. style="color: #f85661;font-size: 16px;font-weight: bold;"
  78. >&nbsp;&nbsp;{{ computedReadonlyText }}</text>
  79. </view>
  80. <button
  81. type="primary"
  82. :disabled="packageList?.length < 2? true: false"
  83. style="width: calc(100% - 8px);margin: 0 4px;"
  84. @click="hanleSplitLabelCode"
  85. >合并</button>
  86. </view>
  87. </view>
  88. <uni-popup ref="errorTip" type="dialog">
  89. <uni-popup-dialog
  90. type="error"
  91. cancel-text="关闭"
  92. confirm-text="确认"
  93. title="提示"
  94. :content="errorTipMessage"
  95. @confirm="handleCloseErrorTipsModal"
  96. @close="handleCloseErrorTipsModal"
  97. />
  98. </uni-popup>
  99. </template>
  100. </gui-page>
  101. </template>
  102. <script>
  103. import {
  104. computed,
  105. defineComponent,
  106. onBeforeMount,
  107. ref
  108. } from 'vue'
  109. import {
  110. add
  111. } from '@/unit/Math.js'
  112. import tsc from '@/unit/CHITEN_SDK_APP/tsc.js'
  113. export default defineComponent({
  114. setup() {
  115. const bluetooth = ref()
  116. const modalForm = ref('')
  117. const errorState = ref(0)
  118. const easyinput = ref()
  119. const errorTip = ref('')
  120. const errorTipMessage = ref('')
  121. const scanNumber = ref('')
  122. const packageList = ref([])
  123. const blueConfig = ref({
  124. loopBuffer: 0,
  125. currentTime: 1,
  126. lastData: 0,
  127. onBufferSize: 20,
  128. printerNum: 1,
  129. currentPrint: 1,
  130. isLabelSend: false
  131. })
  132. const computedRemainder = computed(() => {
  133. let remainder = 0
  134. for (let i = 0; i < packageList.value.length; i++) {
  135. remainder = add(packageList.value[i].qty, remainder)
  136. }
  137. return remainder
  138. })
  139. const computedReadonlyText = computed(() => {
  140. const materialNo = packageList.value[0]?.materialNo
  141. const isTrue = packageList.value.every(item => item.materialNo === materialNo)
  142. return !isTrue ? '列表产品编码不一致' : ''
  143. })
  144. onBeforeMount(() => {
  145. // #ifdef APP-PLUS
  146. const bluetoothConfig = uni.getStorageSync('bluetoothConfig')
  147. if ([null, '', undefined].includes(bluetoothConfig)) {
  148. bluetooth.value = {
  149. 'isOpenBle': false,
  150. 'deviceId': '',
  151. 'serviceId': '',
  152. 'writeId': '',
  153. 'notifyId': ''
  154. }
  155. setTimeout(() => {
  156. // #ifdef APP-PLUS
  157. plus.device.beep(2)
  158. // #endif
  159. errorTipMessage.value = '蓝牙打印机未连接'
  160. errorState.value = -1
  161. errorTip.value.open()
  162. }, 800)
  163. } else {
  164. bluetooth.value = JSON.parse(bluetoothConfig)
  165. }
  166. // #endif
  167. })
  168. const validataPackageRepeat = function(objectData) {
  169. const batchNumbers = []
  170. batchNumbers.push(objectData.batchNumber)
  171. packageList.value.forEach(ret => {
  172. batchNumbers.push(ret.batchNumber)
  173. })
  174. uni.$reqPost('blendingCheck', {
  175. batchNumbers
  176. })
  177. .then(({
  178. code,
  179. data,
  180. msg
  181. }) => {
  182. if (code === 0 && data) {
  183. packageList.value.push(objectData)
  184. setInputFocus()
  185. } else {
  186. // #ifdef APP-PLUS
  187. plus.device.beep(2)
  188. // #endif
  189. errorTipMessage.value = msg
  190. errorTip.value.open()
  191. errorState.value = 0
  192. }
  193. })
  194. }
  195. const handleMapass = function() {
  196. // #ifdef APP-PLUS
  197. const mpaasScanModule = uni.requireNativePlugin('Mpaas-Scan-Module')
  198. mpaasScanModule.mpaasScan({
  199. // 扫码识别类型,参数可多选,qrCode、barCode,不设置,默认识别所有
  200. 'scanType': ['qrCode', 'barCode'],
  201. // 是否隐藏相册,默认false不隐藏
  202. 'hideAlbum': false
  203. },
  204. (ret) => {
  205. if (ret.resp_code === 1000) {
  206. uni.$reqGet('getQrCodeInfo', {
  207. qrCode: ret.resp_result
  208. })
  209. .then(({
  210. code,
  211. data,
  212. msg
  213. }) => {
  214. if (code === 0) {
  215. validataPackageRepeat(data)
  216. } else {
  217. // #ifdef APP-PLUS
  218. plus.device.beep(2)
  219. // #endif
  220. errorTipMessage.value = msg
  221. errorTip.value.open()
  222. errorState.value = 0
  223. }
  224. })
  225. }
  226. })
  227. // #endif
  228. }
  229. // 扫描完成品标签输入框回车事件
  230. const handleKeydown = function(e) {
  231. uni.$reqGet('getQrCodeInfo', {
  232. qrCode: e
  233. })
  234. .then(({
  235. code,
  236. data,
  237. msg
  238. }) => {
  239. if (code === 0) {
  240. validataPackageRepeat(data)
  241. } else {
  242. // #ifdef APP-PLUS
  243. plus.device.beep(2)
  244. // #endif
  245. errorTipMessage.value = msg
  246. errorTip.value.open()
  247. errorState.value = 0
  248. }
  249. })
  250. }
  251. const Send = function(buff) {
  252. var currentTime = blueConfig.value.currentTime
  253. var loopBuffer = blueConfig.value.loopBuffer
  254. var lastData = blueConfig.value.lastData
  255. var onBufferSize = blueConfig.value.onBufferSize
  256. var printNum = blueConfig.value.printerNum
  257. var currentPrint = blueConfig.value.currentPrint
  258. var buf
  259. var dataView
  260. if (currentTime < loopBuffer) {
  261. buf = new ArrayBuffer(onBufferSize)
  262. dataView = new DataView(buf)
  263. for (let i = 0; i < onBufferSize; ++i) {
  264. dataView.setUint8(i, buff[(currentTime - 1) * onBufferSize + i])
  265. }
  266. } else {
  267. buf = new ArrayBuffer(lastData)
  268. dataView = new DataView(buf)
  269. for (let i = 0; i < lastData; ++i) {
  270. dataView.setUint8(i, buff[(currentTime - 1) * onBufferSize + i])
  271. }
  272. }
  273. plus.bluetooth.writeBLECharacteristicValue({
  274. deviceId: bluetooth.value.deviceId,
  275. serviceId: bluetooth.value.serviceId,
  276. characteristicId: bluetooth.value.writeId,
  277. value: buf,
  278. success: function(res) {
  279. if (currentPrint <= printNum) {
  280. wx.showToast({
  281. title: '数据打印中',
  282. icon: 'loading'
  283. })
  284. }
  285. // 将complete中处理数据的代码挪过来,增加30ms延迟, 解决蓝牙打印数据传输10007问题
  286. setTimeout(() => {
  287. currentTime += 1
  288. if (currentTime <= loopBuffer) {
  289. blueConfig.value.isLabelSend = true
  290. blueConfig.value.currentTime = currentTime
  291. Send(buff)
  292. }
  293. // end
  294. if (currentTime === loopBuffer) {
  295. blueConfig.value.currentPrint += 1
  296. blueConfig.value.isLabelSend = false
  297. }
  298. }, 10)
  299. },
  300. fail: function(e) {
  301. wx.showToast({
  302. title: '打印第' + currentPrint + '张失败' + 'error: code ' + e
  303. ?.code + ', message ' + e?.message,
  304. icon: 'none',
  305. duration: 4000
  306. })
  307. },
  308. complete: function() {}
  309. })
  310. }
  311. const prepareSend = function(buff) {
  312. var time = blueConfig.value.onBufferSize
  313. var loopBuffer = parseInt(buff.length / time)
  314. var lastData = parseInt(buff.length % time)
  315. blueConfig.value.loopBuffer = loopBuffer + 1
  316. blueConfig.value.lastData = lastData
  317. blueConfig.value.currentTime = 1
  318. Send(buff)
  319. }
  320. const labelTest = function(ret) {
  321. var command = tsc.dlabelPrinter.createNew()
  322. command.setBackFeed(6)
  323. // (480 * 200)dots
  324. command.setSize(60, 25)
  325. command.setCls()
  326. command.setGap(6)
  327. command.setBox(0, 0, 475, 200, 1)
  328. // 1
  329. command.setBar(108, 0, 2, 40)
  330. command.setBar(108, 40, 2, 40)
  331. command.setBar(108, 80, 2, 40)
  332. command.setBar(108, 120, 2, 40)
  333. command.setBar(108, 160, 2, 40)
  334. command.setBar(108, 200, 2, 40)
  335. // 竖
  336. command.setBar(210, 80, 2, 40)
  337. command.setBar(210, 120, 2, 40)
  338. command.setBar(210, 160, 2, 40)
  339. command.setBar(210, 200, 2, 40)
  340. // 横
  341. command.setBar(0, 40, 480, 2)
  342. command.setBar(0, 80, 320, 2)
  343. command.setBar(0, 120, 210, 2)
  344. command.setBar(0, 160, 210, 2)
  345. // 横
  346. command.setBar(320, 80, 480, 2)
  347. command.setBar(320, 120, 480, 2)
  348. command.setBar(320, 160, 480, 2)
  349. // 竖
  350. command.setBar(322, 40, 2, 40)
  351. command.setBar(322, 80, 2, 40)
  352. command.setBar(322, 120, 2, 40)
  353. command.setBar(322, 160, 2, 40)
  354. // 竖
  355. command.setBar(378, 40, 2, 40)
  356. command.setBar(378, 80, 2, 40)
  357. command.setBar(378, 120, 2, 40)
  358. command.setBar(378, 160, 2, 40)
  359. // row1
  360. command.setText(4, 8, 'TSS24.BF2', 0, 1, 1, '料号')
  361. command.setText(112, 10, '2', 0, 1, 1, ret.pn)
  362. // row2
  363. command.setText(4, 48, 'TSS24.BF2', 0, 1, 1, '工令单')
  364. command.setText(112, 50, '2', 0, 1, 1, ret.wordOrderNo)
  365. command.setText(328, 48, 'TSS24.BF2', 0, 1, 1, '数量')
  366. command.setText(384, 50, '2', 0, 1, 1, ret.qty)
  367. // row3
  368. command.setText(4, 88, 'TSS24.BF2', 0, 1, 1, '生产日期')
  369. command.setText(110, 90, '1', 0, 1, 1, ret.dateOfManufacture)
  370. command.setText(328, 88, 'TSS24.BF2', 0, 1, 1, '库位')
  371. command.setText(384, 90, '1', 0, 1, 1, ret.storageLocation)
  372. // row4
  373. command.setText(4, 128, 'TSS24.BF2', 0, 1, 1, '供应商')
  374. command.setText(112, 130, '3', 0, 1, 1, ret.vendor)
  375. command.setText(328, 128, 'TSS24.BF2', 0, 1, 1, '后制')
  376. command.setText(384, 130, '1', 0, 1, 1, ret.postProduction)
  377. // row5
  378. command.setText(4, 168, 'TSS24.BF2', 0, 1, 1, '包装')
  379. command.setText(112, 170, '1', 0, 1, 1, ret.packaging)
  380. command.setText(328, 168, 'TSS24.BF2', 0, 1, 1, 'QC')
  381. command.setText(384, 170, '1', 0, 1, 1, ret.qc)
  382. command.setQrcode(214, 88, 'L', 5, 'A', ret.qrCode)
  383. command.setPagePrint()
  384. prepareSend(command.getData())
  385. }
  386. const hanleSplitLabelCode = function() {
  387. const list = []
  388. const batchNumbers = []
  389. packageList.value.forEach(ret => {
  390. batchNumbers.push(ret.batchNumber)
  391. })
  392. uni.$reqPost('blending', {
  393. batchNumbers
  394. })
  395. .then(({
  396. code,
  397. data,
  398. msg
  399. }) => {
  400. if (code === 0) {
  401. list.push(data)
  402. blueConfig.value.currentPrint = 1
  403. blueConfig.value.printNum = list.length
  404. const timer = setInterval(async() => {
  405. const currentPrint = blueConfig.value.currentPrint - 1
  406. if (blueConfig.value.isLabelSend === false &&
  407. currentPrint <
  408. blueConfig.value.printNum) {
  409. const paramsObj = {
  410. pn: list[currentPrint]
  411. .pn ?? '',
  412. wordOrderNo: list[currentPrint]
  413. .wordOrderNo ??
  414. '',
  415. qty: list[currentPrint]
  416. .qty ??
  417. '',
  418. dateOfManufacture: list[currentPrint]
  419. .dateOfManufacture ??
  420. '',
  421. storageLocation: list[currentPrint]
  422. .storageLocation ??
  423. '',
  424. postProduction: list[currentPrint]
  425. .postProduction ??
  426. '',
  427. vendor: list[currentPrint]
  428. .vendor ??
  429. '',
  430. packaging: list[currentPrint]
  431. .packaging ??
  432. '',
  433. qc: list[currentPrint]
  434. .qc ??
  435. '',
  436. qrCode: list[currentPrint]
  437. .qrCode ??
  438. ''
  439. }
  440. await labelTest(paramsObj)
  441. }
  442. if (currentPrint === blueConfig.value.printNum) {
  443. await clearInterval(timer)
  444. }
  445. }, 3000)
  446. packageList.value.length = 0
  447. setInputFocus()
  448. } else {
  449. // #ifdef APP-PLUS
  450. plus.device.beep(2)
  451. // #endif
  452. errorTipMessage.value = msg
  453. errorTip.value.open()
  454. errorState.value = 0
  455. }
  456. })
  457. }
  458. const goHome = function() {
  459. uni.$goHome()
  460. }
  461. const setInputFocus = function() {
  462. scanNumber.value = ''
  463. easyinput.value.onBlur()
  464. easyinput.value.onFocus()
  465. }
  466. // 关闭错误信息弹窗
  467. const handleCloseErrorTipsModal = async function() {
  468. errorTip.value.close()
  469. if (errorState.value === 0) {
  470. await setInputFocus()
  471. }
  472. }
  473. const handleRemoveDetail = function(key) {
  474. packageList.value.splice(key, 1)
  475. }
  476. // 禁用软键盘
  477. const handleInputFocus = function() {
  478. setTimeout(() => {
  479. uni.hideKeyboard()
  480. }, 100)
  481. }
  482. return {
  483. options: [{
  484. text: '删除',
  485. style: {
  486. backgroundColor: '#dd524d'
  487. }
  488. }],
  489. goHome,
  490. computedReadonlyText,
  491. easyinput,
  492. errorTip,
  493. errorTipMessage,
  494. modalForm,
  495. packageList,
  496. handleInputFocus,
  497. scanNumber,
  498. handleMapass,
  499. handleKeydown,
  500. handleRemoveDetail,
  501. computedRemainder,
  502. handleCloseErrorTipsModal,
  503. hanleSplitLabelCode
  504. }
  505. }
  506. })
  507. </script>
  508. <style lang="scss" scoped>
  509. .gui-header-leader-btns {
  510. color: black;
  511. font-size: 24px !important;
  512. }
  513. .list-content {
  514. margin-top: 80px;
  515. background-color: #edeeee;
  516. }
  517. .input-200 {
  518. width: 200px;
  519. padding-left: 10px;
  520. }
  521. .font-icons {
  522. width: 40px;
  523. font-size: 20px;
  524. text-align: right;
  525. }
  526. .scan {
  527. height: 45px;
  528. width: calc(100% - 48px);
  529. margin: 12px;
  530. padding: 0 12px;
  531. display: flex;
  532. justify-content: space-between;
  533. align-items: center;
  534. border-radius: 6px;
  535. background-color: white;
  536. .scan-card {
  537. width: 100%;
  538. display: grid;
  539. grid-template-rows: 1fr;
  540. grid-template-columns: 7fr 2fr;
  541. align-items: center;
  542. input {
  543. height: 35px;
  544. line-height: 35px;
  545. }
  546. text {
  547. width: 100%;
  548. text-align: right;
  549. }
  550. }
  551. }
  552. .card-list-flexbox {
  553. width: calc(100vw - 24px);
  554. height: 150px;
  555. margin: 0 12px;
  556. display: flex;
  557. justify-content: center;
  558. flex-direction: column;
  559. align-items: center;
  560. overflow-y: scroll;
  561. .card-list-title {
  562. width: 100%;
  563. height: 35px;
  564. line-height: 35px;
  565. margin: 0 4px 0 4px;
  566. border-radius: 4px 4px 0 0;
  567. text-align: center;
  568. color: white;
  569. background-color: rgba(0, 160, 233, 1.0);
  570. }
  571. .card-list-item,
  572. .card-list-item-operation {
  573. width: 100%;
  574. height: 35px;
  575. margin: 0 4px 3rpx 4px;
  576. border-radius: 4px;
  577. display: flex;
  578. flex-direction: row;
  579. align-items: center;
  580. justify-content: space-between;
  581. background-color: #fff;
  582. uni-text {
  583. font-size: 14px;
  584. height: 50rpx;
  585. text-align: left;
  586. padding: 0 8px;
  587. display: flex;
  588. flex-direction: row;
  589. align-items: center;
  590. }
  591. .text-1 {
  592. flex: 1;
  593. height: 35px;
  594. justify-content: flex-start;
  595. }
  596. .text-2 {
  597. flex: 3;
  598. height: 35px;
  599. justify-content: flex-end;
  600. margin-right: 4px;
  601. padding: 2px 6px;
  602. }
  603. }
  604. }
  605. .table-title {
  606. height: 40px;
  607. line-height: 40px;
  608. margin: 4px 0 0px 0;
  609. padding: 4px 12px;
  610. font-size: 16px;
  611. font-weight: bold;
  612. display: flex;
  613. border-bottom: 1px solid #dddddd;
  614. background-color: white;
  615. & view {
  616. flex: 1;
  617. display: flex;
  618. }
  619. }
  620. .c-button {
  621. display: flex;
  622. width: 40px;
  623. height: 30px;
  624. margin: 0 6px;
  625. border: 1px solid #c3c3c3;
  626. border-radius: 4px;
  627. justify-content: center;
  628. align-items: center;
  629. cursor: pointer;
  630. }
  631. .custom-table {
  632. height: calc(100vh - 265px);
  633. min-height: 230px;
  634. margin: 5px 0;
  635. overflow-y: scroll;
  636. }
  637. .operation-panel {
  638. padding: 0 0 12px 0;
  639. position: fixed;
  640. bottom: 0;
  641. width: 100%;
  642. display: grid;
  643. grid-template-columns: 1fr;
  644. grid-template-rows: 1fr;
  645. background-color: white;
  646. .operation-title {
  647. height: 40px;
  648. line-height: 40px;
  649. padding: 0 14px;
  650. font-size: 16px;
  651. font-weight: bold;
  652. }
  653. }
  654. .list-panel {
  655. margin-top: 6px;
  656. margin-bottom: 96px;
  657. padding: 5px 8px;
  658. overflow-y: scroll;
  659. }
  660. </style>