shipmentProduct.vue 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550
  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="goBack"
  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. style="font-size: 1rem;"
  15. >出货明细</text>
  16. <!-- 此处加一个右侧展位元素与左侧同宽,实现标题居中 -->
  17. <!-- 实际宽度请根据自己情况设置 -->
  18. <view style="width:40px;" />
  19. <!-- 如果右侧有其他内容可以利用条件编译和定位来实现-->
  20. </view>
  21. </template>
  22. <template #gBody>
  23. <view class="list-content">
  24. <view class="scan">
  25. <view class="scan-card">
  26. <uni-easyinput
  27. ref="easyinput"
  28. v-model="scanNumber"
  29. :disabled="parentData.isItComplete"
  30. :input-border="false"
  31. :clearable="false"
  32. type="text"
  33. focus
  34. @focus="handleInputFocus"
  35. placeholder="扫描成品标签"
  36. @confirm="handleKeydown"
  37. />
  38. <text class="font-icons" @click="handleMapass">&#xe6b7;</text>
  39. </view>
  40. </view>
  41. <view v-if="dataList?.length > 0" class="list-panel">
  42. <div
  43. v-for="(item, key) in dataList"
  44. :id="'panel_'+item.id"
  45. :key="key"
  46. class="panel"
  47. @click="handleShowDetails(item)"
  48. >
  49. <div :class="['panel-row', lightElement === item.id?'panel-animation':'']">
  50. <span>产品</span>
  51. <span>{{ item.productCode }}({{ item.productName }})</span>
  52. </div>
  53. <div class="panel-row">
  54. <span>订单</span>
  55. <span>{{ item.saleOrderNo }}</span>
  56. </div>
  57. <div class="panel-row">
  58. <span>应出货数量</span>
  59. <span>{{ item.planQty * 1 }}</span>
  60. </div>
  61. <div class="panel-row">
  62. <span>已出货数量</span>
  63. <span style="font-weight: bold;color: orange;">{{ item.sendQty * 1 }}</span>
  64. </div>
  65. </div>
  66. </view>
  67. <view v-else>
  68. <view class="bg-image">
  69. <image src="@/static/empty.png" mode="heightFix" />
  70. <text>这里什么都没有...</text>
  71. </view>
  72. </view>
  73. </view>
  74. <uni-popup ref="popup" background-color="#fff">
  75. <view class="popup-content">
  76. <view class="list-title" style="margin-bottom: 8px;">
  77. <span
  78. class="font-icons"
  79. style="font-size: 20px;color: #00a0e9;width: 40px;text-align: center;"
  80. >&#xecc6;</span>
  81. <span>已出明细</span>
  82. </view>
  83. <view v-if="demandDetailsList?.length > 0" class="list-panel" style="margin-top: 40px;">
  84. <div v-for="(item) in demandDetailsList" :key="item.id" class="panel">
  85. <uni-swipe-action>
  86. <!-- 基础用法 -->
  87. <uni-swipe-action-item :right-options="options" @click="handleRemoveDetail(item)">
  88. <template #left>
  89. <view />
  90. </template>
  91. <div class="panel-row">
  92. <span>箱码</span>
  93. <span>{{ item.boxNo }}</span>
  94. </div>
  95. <div class="panel-row">
  96. <span>数量</span>
  97. <span>{{ item.boxQty * 1 }}</span>
  98. </div>
  99. </uni-swipe-action-item>
  100. </uni-swipe-action>
  101. </div>
  102. </view>
  103. <view v-else>
  104. <view class="bg-image">
  105. <image src="@/static/empty.png" mode="heightFix" />
  106. <text>这里什么都没有...</text>
  107. </view>
  108. </view>
  109. </view>
  110. </uni-popup>
  111. <uni-popup ref="errorTip" type="dialog">
  112. <uni-popup-dialog
  113. type="error"
  114. cancel-text="关闭"
  115. confirm-text="确认"
  116. title="提示"
  117. :content="errorTipMessage"
  118. @confirm="handleCloseErrorTipsModal"
  119. @close="handleCloseErrorTipsModal"
  120. />
  121. </uni-popup>
  122. </template>
  123. </gui-page>
  124. </template>
  125. <script>
  126. import {
  127. defineComponent,
  128. nextTick,
  129. onBeforeMount,
  130. ref
  131. } from 'vue'
  132. export default defineComponent({
  133. setup(options) {
  134. const popup = ref()
  135. const errorState = ref(0)
  136. const easyinput = ref()
  137. const errorTip = ref('')
  138. const errorTipMessage = ref('')
  139. const dataList = ref([])
  140. const scanNumber = ref('')
  141. const parentData = JSON.parse(options.detail) ?? {}
  142. const demandDetailsList = ref([])
  143. const rowObject = ref({})
  144. const lightElement = ref('')
  145. const search = function() {
  146. uni.$reqGet('toOutDetailPage', {
  147. pageNo: 1,
  148. PageSize: 100,
  149. id: parentData.id ?? ''
  150. })
  151. .then(({
  152. code,
  153. data,
  154. msg
  155. }) => {
  156. if (code === 0) {
  157. dataList.value = data?.list ?? []
  158. } else {
  159. uni.showToast({
  160. title: msg,
  161. icon: 'none',
  162. duration: 2000
  163. })
  164. }
  165. })
  166. }
  167. onBeforeMount(() => {
  168. search()
  169. })
  170. const saveProductData = function(data) {
  171. const submitParams = [{
  172. boxNo: data.qrCode,
  173. productCode: data.materialNo,
  174. boxQty: data.receiptQty,
  175. saleOrderDetailId: data.saleOrderDetailId,
  176. stockCode: data.erpStockId,
  177. locationCode: data.wmsStoreAreaId,
  178. areaCode: data.wmsStoreId
  179. }]
  180. uni.$reqPost('saveSaleOutOrderDetailBatch', submitParams)
  181. .then(({
  182. code,
  183. data,
  184. msg
  185. }) => {
  186. if (code === 0) {
  187. uni.showToast({
  188. title: '出货成功',
  189. icon: 'none',
  190. duration: 1000
  191. })
  192. setInputFocus()
  193. search()
  194. } else {
  195. // #ifdef APP-PLUS
  196. plus.device.beep(2)
  197. // #endif
  198. errorTipMessage.value = msg
  199. errorTip.value.open()
  200. errorState.value = 0
  201. }
  202. })
  203. }
  204. const handleMapass = function() {
  205. if (parentData.isItComplete) return
  206. // #ifdef APP-PLUS
  207. const mpaasScanModule = uni.requireNativePlugin('Mpaas-Scan-Module')
  208. mpaasScanModule.mpaasScan({
  209. // 扫码识别类型,参数可多选,qrCode、barCode,不设置,默认识别所有
  210. 'scanType': ['qrCode', 'barCode'],
  211. // 是否隐藏相册,默认false不隐藏
  212. 'hideAlbum': false
  213. },
  214. (ret) => {
  215. if (ret.resp_code === 1000) {
  216. uni.$reqGet('scanPrepareMaterial', {
  217. qrCode: ret.resp_result
  218. })
  219. .then(({
  220. code,
  221. data,
  222. msg
  223. }) => {
  224. if (code === 0) {
  225. let fdIndex = -1
  226. let saleOrderDetailId = ''
  227. dataList.value.forEach((res, index) => {
  228. if (res.productCode === data[0].materialNo) {
  229. fdIndex = index
  230. saleOrderDetailId = res.id
  231. }
  232. })
  233. if (fdIndex !== -1) {
  234. saveProductData({
  235. ...data[0],
  236. saleOrderDetailId
  237. })
  238. // 滚动到指定位置
  239. handleScrollPage(dataList.value[fdIndex]?.id)
  240. } else {
  241. // #ifdef APP-PLUS
  242. plus.device.beep(2)
  243. // #endif
  244. errorTipMessage.value = '请扫描正确的成品标签'
  245. errorTip.value.open()
  246. errorState.value = 0
  247. }
  248. } else {
  249. // #ifdef APP-PLUS
  250. plus.device.beep(2)
  251. // #endif
  252. errorTipMessage.value = msg
  253. errorTip.value.open()
  254. errorState.value = 0
  255. }
  256. })
  257. }
  258. })
  259. // #endif
  260. }
  261. // 扫描完成品标签输入框回车事件
  262. const handleKeydown = function(e) {
  263. uni.$reqGet('scanPrepareMaterial', {
  264. qrCode: e
  265. })
  266. .then(({
  267. code,
  268. data,
  269. msg
  270. }) => {
  271. if (code === 0) {
  272. let fdIndex = -1
  273. let saleOrderDetailId = ''
  274. dataList.value.forEach((res, index) => {
  275. if (res.productCode === data[0].materialNo) {
  276. fdIndex = index
  277. saleOrderDetailId = res.id
  278. }
  279. })
  280. if (fdIndex !== -1) {
  281. saveProductData({
  282. ...data[0],
  283. saleOrderDetailId
  284. })
  285. // 滚动到指定位置
  286. handleScrollPage(dataList.value[fdIndex]?.id)
  287. } else {
  288. // #ifdef APP-PLUS
  289. plus.device.beep(2)
  290. // #endif
  291. errorTipMessage.value = '请扫描正确的成品标签'
  292. errorTip.value.open()
  293. errorState.value = 0
  294. }
  295. } else {
  296. // #ifdef APP-PLUS
  297. plus.device.beep(2)
  298. // #endif
  299. errorTipMessage.value = msg
  300. errorTip.value.open()
  301. errorState.value = 0
  302. }
  303. })
  304. }
  305. const handleScrollPage = function(id) {
  306. nextTick(() => {
  307. const obj = uni.createSelectorQuery().in(this).select(
  308. '#panel_' + id)
  309. lightElement.value = id
  310. obj.boundingClientRect(data => {
  311. if (data) {
  312. uni.pageScrollTo({
  313. scrollTop: data.top,
  314. duration: 100
  315. })
  316. }
  317. }).exec()
  318. })
  319. }
  320. const handleShowDetails = function(ret) {
  321. uni.$reqGet('saleOutOrderDetailPage', {
  322. pageNo: 1,
  323. pageSize: 100,
  324. id: ret?.id
  325. })
  326. .then(({
  327. code,
  328. data,
  329. msg
  330. }) => {
  331. if (code === 0) {
  332. if (popup.value?.showPopup === false) {
  333. popup.value.open('bottom')
  334. }
  335. rowObject.value = ret
  336. demandDetailsList.value = data?.list ?? []
  337. } else {
  338. // #ifdef APP-PLUS
  339. plus.device.beep(2)
  340. // #endif
  341. errorTipMessage.value = msg
  342. errorTip.value.open()
  343. errorState.value = -1
  344. }
  345. })
  346. }
  347. const handleRemoveDetail = function(ret) {
  348. if (rowObject.value?.planQty === rowObject.value?.sendQty || parentData.isItComplete) {
  349. uni.showToast({
  350. title: '订单已完成, 无法删除',
  351. icon: 'none',
  352. duration: 2000
  353. })
  354. return
  355. }
  356. uni.$reqDelete('deleteSaleOutOrderDetail', {
  357. id: ret.id ?? ''
  358. })
  359. .then(({
  360. code,
  361. data,
  362. msg
  363. }) => {
  364. if (code === 0) {
  365. uni.showToast({
  366. title: '删除成功',
  367. icon: 'none',
  368. duration: 2000
  369. })
  370. handleShowDetails(rowObject.value)
  371. search()
  372. } else {
  373. // #ifdef APP-PLUS
  374. plus.device.beep(2)
  375. // #endif
  376. errorTipMessage.value = msg
  377. errorTip.value.open()
  378. errorState.value = -1
  379. }
  380. })
  381. }
  382. const goBack = function() {
  383. uni.$goBack('/pages/workbranch/production/shipmentMHT/shipmentPage')
  384. }
  385. const setInputFocus = function() {
  386. scanNumber.value = ''
  387. easyinput.value.onBlur()
  388. easyinput.value.onFocus()
  389. }
  390. // 关闭错误信息弹窗
  391. const handleCloseErrorTipsModal = async function() {
  392. errorTip.value.close()
  393. if (errorState.value === 0) {
  394. await setInputFocus()
  395. }
  396. }
  397. // 禁用软键盘
  398. const handleInputFocus = function() {
  399. setTimeout(() => {
  400. uni.hideKeyboard()
  401. }, 100)
  402. }
  403. return {
  404. options: [{
  405. text: '删除',
  406. style: {
  407. backgroundColor: '#dd524d'
  408. }
  409. }],
  410. goBack,
  411. popup,
  412. easyinput,
  413. errorTip,
  414. errorState,
  415. errorTipMessage,
  416. lightElement,
  417. scanNumber,
  418. handleMapass,
  419. handleKeydown,
  420. handleInputFocus,
  421. dataList,
  422. parentData,
  423. handleRemoveDetail,
  424. demandDetailsList,
  425. handleShowDetails,
  426. handleCloseErrorTipsModal
  427. }
  428. }
  429. })
  430. </script>
  431. <style lang="scss" scoped>
  432. .gui-header-leader-btns {
  433. color: black;
  434. font-size: 24px !important;
  435. }
  436. .list-content {
  437. margin-top: 80px;
  438. min-height: calc(100vh - 80px);
  439. background-color: #edeeee;
  440. }
  441. .input-200 {
  442. width: 200px;
  443. padding-left: 10px;
  444. }
  445. span,
  446. text {
  447. font-size: 12px;
  448. }
  449. .font-icons {
  450. width: 40px;
  451. font-size: 20px;
  452. text-align: right;
  453. }
  454. .scan {
  455. position: fixed;
  456. top: 66px;
  457. left: 0;
  458. height: 60px;
  459. width: 100%;
  460. margin: 12px 0;
  461. padding: 0 12px;
  462. box-sizing: border-box;
  463. display: flex;
  464. justify-content: space-between;
  465. align-items: center;
  466. border-bottom: 2px solid rgba(237, 238, 238, 1);
  467. background-color: white;
  468. .scan-card {
  469. width: 100%;
  470. display: grid;
  471. grid-template-rows: 1fr;
  472. grid-template-columns: 7fr 2fr;
  473. align-items: center;
  474. input {
  475. height: 35px;
  476. line-height: 35px;
  477. }
  478. text {
  479. width: 100%;
  480. text-align: right;
  481. }
  482. }
  483. }
  484. .list-title {
  485. position: fixed;
  486. top: 0;
  487. left: 0;
  488. z-index: 99999;
  489. height: 40px;
  490. line-height: 40px;
  491. width: calc(100% - 24px);
  492. padding: 0 12px;
  493. display: flex;
  494. align-items: center;
  495. font-size: 16px;
  496. font-weight: bold;
  497. border-top-left-radius: 16px;
  498. border-top-right-radius: 16px;
  499. border-bottom: 2px solid rgba(237, 238, 238, 1);
  500. background-color: white;
  501. }
  502. .popup-content {
  503. height: 75vh;
  504. overflow-y: scroll;
  505. background-color: #edeeee;
  506. }
  507. .custom-table {
  508. height: calc(100vh - 215px);
  509. min-height: 230px;
  510. margin: 5px 0;
  511. overflow-y: scroll;
  512. }
  513. .list-panel {
  514. margin-top: 66px;
  515. padding: 5px 8px;
  516. overflow-y: scroll;
  517. }
  518. .panel-animation {
  519. color: white !important;
  520. background-color: #00a0e9 !important;
  521. }
  522. </style>