speedy-check.vue 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404
  1. <template>
  2. <view class="container" style="padding-top: 44px;padding-bottom: 10px;">
  3. <u-navbar title="快速盘点" :border="false" fixed safe-area-inset-top bgColor="#22ac38"
  4. titleStyle="font-size:36rpx;color:#fff">
  5. <template #left>
  6. <u-icon name="arrow-left" color="#fff" size="20" @click="goBack"></u-icon>
  7. </template>
  8. <template #right>
  9. <text style="color: #ffffff;" @click="onSubmit">提交</text>
  10. </template>
  11. </u-navbar>
  12. <!-- 盘点信息选择区域 -->
  13. <view class="select-area" style="margin-top: 44px">
  14. <u-cell-group :border="true">
  15. <u-cell
  16. title="盘点方式"
  17. :value="checkMethod"
  18. @click="showCheckMethodPicker = true"
  19. :isLink="true"
  20. :border="true"
  21. />
  22. <u-cell
  23. title="目标库位"
  24. :value="location"
  25. @click="handleLocationSelect"
  26. :isLink="true"
  27. :border="true"
  28. />
  29. </u-cell-group>
  30. </view>
  31. <!-- 盘点方式选择器 -->
  32. <u-picker
  33. :show="showCheckMethodPicker"
  34. :columns="[checkMethodOptions]"
  35. @confirm="onCheckMethodConfirm"
  36. @cancel="showCheckMethodPicker = false"
  37. :showToolbar="true"
  38. title="选择盘点方式"
  39. :closeOnClickOverlay="true"
  40. cancelText="取消"
  41. confirmText="确定"
  42. :itemHeight="44"
  43. :visibleItemCount="5"
  44. />
  45. <!-- 库位选择弹出层 -->
  46. <u-popup
  47. :show="showLocationPopup"
  48. mode="bottom"
  49. @close="close"
  50. @open="open"
  51. :closeOnClickOverlay="true"
  52. :round="10"
  53. :safeAreaInsetBottom="true"
  54. >
  55. <view class="location-popup">
  56. <view class="location-popup-header">
  57. <text style="flex: 1">选择库位</text>
  58. <u-text
  59. style="width: 40px; flex: none"
  60. type="primary"
  61. text="确定"
  62. @click="onLocationConfirm"
  63. ></u-text>
  64. </view>
  65. <view class="location-popup-content">
  66. <u-input
  67. v-model="selectedLocation"
  68. placeholder="请输入库位"
  69. customStyle="background-color: #f6f6f6; border-radius: 4px;"
  70. ></u-input>
  71. <view class="location-list">
  72. <view
  73. class="location-item"
  74. v-for="(item, index) in locationList"
  75. :key="index"
  76. @click="selectLocation(item)"
  77. :class="{ 'location-item--selected': selectedLocation === item }"
  78. >
  79. <text>{{ item }}</text>
  80. <u-icon
  81. v-if="selectedLocation === item"
  82. name="checkmark"
  83. color="#19be6b"
  84. size="16"
  85. ></u-icon>
  86. </view>
  87. </view>
  88. </view>
  89. </view>
  90. </u-popup>
  91. <!-- 订单列表 -->
  92. <view class="product-details">
  93. <LocationOrderItem v-for="(item, index) in products" :isLink :key="index" :item="item" />
  94. </view>
  95. <view class="add-btn" @click="handleAdd">
  96. <u-icon name="plus-circle" size="40" color="#19be6b"></u-icon>
  97. </view>
  98. <!-- 底部扫码输入框 -->
  99. <view class="fixed-bottom pad-20" style="background: #ffffff">
  100. <u-search
  101. placeholder="请输入快递单号/订单编号"
  102. v-model="searchValue"
  103. @search="onSearch"
  104. :show-action="false"
  105. custom-style="margin-right:10px"
  106. ></u-search>
  107. <u-icon name="scan" size="28" color="#19be6b" @click="openScan"></u-icon>
  108. </view>
  109. </view>
  110. </template>
  111. <script setup>
  112. import { reactive, ref } from "vue";
  113. import { onLoad,onUnload } from "@dcloudio/uni-app";
  114. import LocationOrderItem from "./components/LocationOrderItem.vue";
  115. const goBack = () => {
  116. uni.navigateBack();
  117. };
  118. // 盘点方式相关
  119. const showCheckMethodPicker = ref(false);
  120. const checkMethod = ref("实际数量");
  121. const resetType = ref(1);
  122. const checkMethodOptions = ["实际数量", "增加数量", "减少数量"];
  123. //获取用户的默认仓库
  124. const getUserDefaultWarehouse = () => {
  125. uni.$u.http.get("/app/appUser/getUserBindGodown").then((res) => {
  126. if (res.code == 200) {
  127. getWarehouseLocationList(res.data.id);
  128. }
  129. });
  130. };
  131. // 库位相关
  132. const location = ref("");
  133. const showLocationPopup = ref(false);
  134. const locationList = ref([]);
  135. const selectedLocation = ref("");
  136. //根据仓库获取库位列表
  137. const godownId = ref()
  138. const getWarehouseLocationList = (id) => {
  139. godownId.value = id
  140. uni.$u.http.get("/app/stock/getGodownPosition?godownId=" + id).then((res) => {
  141. if (res.code == 200) {
  142. locationList.value = res.data;
  143. }
  144. });
  145. };
  146. // 其他数据
  147. const searchValue = ref("");
  148. const products = ref([]);
  149. // 方法
  150. const onCheckMethodConfirm = (e) => {
  151. checkMethod.value = e.value[0];
  152. showCheckMethodPicker.value = false;
  153. resetType.value = e.indexs[0] + 1;
  154. };
  155. const handleLocationSelect = () => {
  156. showLocationPopup.value = true;
  157. selectedLocation.value = location.value;
  158. };
  159. const onLocationConfirm = () => {
  160. if (selectedLocation.value) {
  161. location.value = selectedLocation.value;
  162. showLocationPopup.value = false;
  163. selectedLocation.value = "";
  164. } else {
  165. uni.$u.toast("请选择库位");
  166. }
  167. };
  168. const selectLocation = (item) => {
  169. selectedLocation.value = item;
  170. };
  171. //盘点提交
  172. const onSubmit = () => {
  173. if (products.value.length === 0) {
  174. uni.$u.toast('请添加盘点订单');
  175. return;
  176. }
  177. // 构建请求参数
  178. const params = {
  179. godownId: godownId.value,
  180. positionCode: location.value,
  181. orderInfo: products.value.map(item => ({
  182. orderId: item.orderId,
  183. waybillCode: item.waybillCode,
  184. bookNum: item.badNum,
  185. remark: item.remark || ''
  186. })),
  187. resetType: resetType.value
  188. };
  189. // 调用重置库存API
  190. uni.$u.http.post('/app/stock/resetStock', params).then(res => {
  191. if (res.code === 200) {
  192. uni.$u.toast('盘点成功');
  193. location.value = ""
  194. products.value = []
  195. searchValue.value = ""
  196. } else {
  197. uni.$u.toast(res.msg || '提交失败');
  198. }
  199. }).catch(err => {
  200. uni.$u.toast('提交失败');
  201. console.error(err);
  202. });
  203. }
  204. const onSearch = () => {
  205. if (!searchValue.value) {
  206. uni.$u.toast('请输入快递单号或订单编号');
  207. return;
  208. }
  209. // 判断搜索类型:纯数字为订单号(searchType=1),字母+数字组合为物流单号(searchType=2)
  210. const searchType = /^[0-9]+$/.test(searchValue.value) ? 1 : 2;
  211. // 调用接口获取订单数据
  212. uni.$u.http.get('/app/stock/searchBadOrderForReset', {
  213. params: {
  214. searchType,
  215. search: searchValue.value
  216. }
  217. }).then(res => {
  218. if (res.code === 200) {
  219. // 检查是否已存在相同订单
  220. const exists = products.value.some(item =>
  221. item.orderId === res.data.orderId ||
  222. item.waybillCode === res.data.waybillCode
  223. );
  224. if (exists) {
  225. uni.$u.toast('该订单已添加');
  226. return;
  227. }
  228. // 添加新订单到列表
  229. products.value.push(res.data);
  230. searchValue.value = ''; // 清空搜索框
  231. } else {
  232. uni.$u.toast(res.msg || '查询失败');
  233. }
  234. }).catch(err => {
  235. uni.$u.toast('查询失败');
  236. console.error(err);
  237. });
  238. };
  239. const openScan = () => {
  240. // #ifdef APP-PLUS || MP-WEIXIN
  241. uni.scanCode({
  242. success: (res) => {
  243. searchValue.value = res.result;
  244. onSearch();
  245. },
  246. fail: (err) => {
  247. uni.showToast({
  248. title: '扫码失败',
  249. icon: 'error'
  250. });
  251. }
  252. });
  253. // #endif
  254. };
  255. function handleAdd() {
  256. if (!location.value) {
  257. uni.$u.toast("请选择库位");
  258. return;
  259. }
  260. uni.navigateTo({
  261. url: "/pages/index/wms/speedy-check-add?location=" + location.value,
  262. });
  263. }
  264. const close = () => {
  265. showLocationPopup.value = false;
  266. };
  267. const open = () => {
  268. showLocationPopup.value = true;
  269. };
  270. onLoad(() => {
  271. getUserDefaultWarehouse();
  272. uni.$on('selectedProducts', (list) => {
  273. products.value = list;
  274. });
  275. // #ifdef APP-PLUS
  276. uni.$u.useGlobalEvent((e) => {
  277. if (e.barcode) {
  278. searchValue.value = e.barcode;
  279. onSearch();
  280. }
  281. });
  282. // #endif
  283. });
  284. onUnload(() => {
  285. uni.$off('selectedProducts');
  286. });
  287. </script>
  288. <style lang="scss" scoped>
  289. .select-area {
  290. background-color: #fff;
  291. }
  292. .product-details {
  293. margin-bottom: 120rpx; // 为底部搜索框留出空间
  294. }
  295. .fixed-bottom {
  296. position: fixed;
  297. bottom: 0;
  298. left: 0;
  299. right: 0;
  300. display: flex;
  301. align-items: center;
  302. padding: 20rpx;
  303. background: #ffffff;
  304. box-shadow: 0 -2rpx 6rpx rgba(0, 0, 0, 0.1);
  305. }
  306. .add-btn {
  307. position: fixed;
  308. right: 0;
  309. bottom: 30%;
  310. z-index: 99;
  311. cursor: pointer;
  312. }
  313. .location-input-wrapper {
  314. display: flex;
  315. align-items: center;
  316. justify-content: flex-end;
  317. }
  318. .location-popup {
  319. background-color: #fff;
  320. padding: 20rpx;
  321. &-header {
  322. display: flex;
  323. justify-content: space-between;
  324. align-items: center;
  325. padding-bottom: 24rpx;
  326. border-bottom: 1px solid #eee;
  327. position: relative;
  328. text {
  329. font-size: 32rpx;
  330. font-weight: 500;
  331. flex: 1;
  332. }
  333. .u-button {
  334. position: absolute;
  335. right: 20rpx;
  336. }
  337. }
  338. &-content {
  339. padding: 20rpx 0;
  340. max-height: 600rpx;
  341. overflow-y: auto;
  342. }
  343. }
  344. .location-list {
  345. margin-top: 20rpx;
  346. max-height: 400rpx;
  347. overflow-y: auto;
  348. }
  349. .location-item {
  350. padding: 20rpx;
  351. border-bottom: 1px solid #eee;
  352. display: flex;
  353. align-items: center;
  354. justify-content: space-between;
  355. &--selected {
  356. color: #19be6b;
  357. }
  358. }
  359. </style>