shipmentProduct.vue 12 KB

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