StockIn.vue 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378
  1. <template>
  2. <van-nav-bar
  3. :title="title" left-arrow left-text="返回" right-text="扫描" fixed placeholder @click-left="goBack()"
  4. @click-right="goTakePhoto"
  5. />
  6. <van-search v-model="stockInSearch" placeholder="请输入搜索关键词" @search="searchStockIn" />
  7. <div class="content">
  8. <div class="van-list-stock-in">
  9. <van-list
  10. v-model:loading="loading" class="list-stock-in" :finished="finished" finished-text=""
  11. :immediate-check="false" @load="loadStockInList"
  12. >
  13. <div v-for="(item, index) in stockInList" :key="item.id" class="list-container">
  14. <van-form :scroll-to-error="true">
  15. <div class="in-header">
  16. <strong>{{ index + 1 }}. {{ item.name }}</strong>
  17. <van-button type="primary" plain @click="stockIn(item)">入库</van-button>
  18. </div>
  19. <van-field v-model="item.no" name="no" label="物料编号:" readonly />
  20. <van-field v-model="item.name" name="name" label="物料名称:" readonly />
  21. <van-field v-model="item.type" name="type" label="规格型号:" readonly />
  22. </van-form>
  23. </div>
  24. </van-list>
  25. </div>
  26. <van-empty v-if="stockInList.length === 0" description="暂无入库物料" />
  27. </div>
  28. <van-dialog v-model:show="isShowStockIn" title="填写入库信息" :show-confirm-button="false">
  29. <van-form :scroll-to-error="true">
  30. <van-field v-model="formData.no" name="no" label="物料编号:" readonly />
  31. <van-field v-model="formData.name" name="name" label="物料名称:" readonly />
  32. <van-field v-model="formData.type" name="type" label="规格型号:" readonly />
  33. <van-field v-model="formData.batchNo" name="batchNo" label="批号:" placeholder="点击输入批号" />
  34. <van-field v-model="formData.num" name="num" label="入库数量:" placeholder="点击输入数量" />
  35. <van-field name="inventoryPackaged" label="是否包装">
  36. <template #input>
  37. <van-switch v-model="formData.inventoryPackaged" size="20px" />
  38. </template>
  39. </van-field>
  40. <van-field
  41. v-model="formData.carrierTypeName" name="carrierTypeName" label="托盘类型:" placeholder="点击选择托盘类型" is-link
  42. @click="showCarrierTypePicker = true"
  43. />
  44. <van-field
  45. v-model="formData.transferName" is-link readonly name="transfer" label="中转区货位:" placeholder="点击选择中转区货位"
  46. @click="isShowTransfer = true"
  47. />
  48. <van-field
  49. v-model="formData.idleName" is-link readonly name="warehouse" label="入库货位:" placeholder="点击选择入库货位"
  50. @click="isShowIdle = true"
  51. />
  52. </van-form>
  53. <template #footer>
  54. <div class="footer-btn">
  55. <van-button style="width: 40%;" plain type="primary" @click="cancelStockIn">
  56. 取消
  57. </van-button>
  58. <van-button style="width: 40%;" type="primary" @click="stockInConfirm">
  59. 提交
  60. </van-button>
  61. </div>
  62. </template>
  63. </van-dialog>
  64. <position-selector
  65. ref="transferPositionSelector" v-model:show="isShowTransfer" position-type="transfer"
  66. :default-selected-id="formData.transferId" @confirm="onTransferPositionSelected"
  67. />
  68. <position-selector
  69. ref="idlePositionSelector" v-model:show="isShowIdle" position-type="idle" :is-default="true"
  70. type="stockIn" :dialog-show="isShowStockIn" :default-selected-id="formData.idleId"
  71. @confirm="onIdlePositionSelected"
  72. />
  73. <van-popup v-model:show="showCarrierTypePicker" destroy-on-close round position="bottom">
  74. <van-picker
  75. :model-value="carrierType" :columns="columns" @cancel="showCarrierTypePicker = false"
  76. @confirm="onConfirm"
  77. />
  78. </van-popup>
  79. </template>
  80. <script setup>
  81. import { ref, onMounted } from 'vue';
  82. import { useRouter, useRoute } from 'vue-router';
  83. import PositionSelector from './PositionSelector.vue';
  84. import { processException } from '../common/Common.js';
  85. import { ajaxApiGet, ajaxApiPost } from '../common/utils.js';
  86. import { showFailToast, showSuccessToast, showConfirmDialog } from 'vant';
  87. const route = useRoute();
  88. const router = useRouter();
  89. const stockInSearch = ref('');
  90. const transferPositionSelector = ref(null);
  91. const idlePositionSelector = ref(null);
  92. const stockInList = ref([]);
  93. const loading = ref(false);
  94. const finished = ref(false);
  95. const page = ref(1);
  96. const total = ref(0);
  97. const pageSize = ref(10);
  98. const formData = ref({
  99. id: '',
  100. no: '',
  101. name: '',
  102. type: '',
  103. num: '',
  104. batchNo: '',
  105. transferName: '',
  106. transferId: '',
  107. transferNo: '',
  108. idleName: '',
  109. idleId: '',
  110. idleNo: '',
  111. inventoryPackaged: false,
  112. carrierType: '',
  113. carrierTypeName: '',
  114. });
  115. const isShowStockIn = ref(false);
  116. const isShowTransfer = ref(false);
  117. const isShowIdle = ref(false);
  118. const warehouseId = ref(null);
  119. const warehouseName = ref(null);
  120. const title = ref('');
  121. const columns = ref([]);
  122. const showCarrierTypePicker = ref(false);
  123. const carrierType = ref([]);
  124. const onConfirm = ({ selectedValues, selectedOptions }) => {
  125. showCarrierTypePicker.value = false;
  126. formData.value.carrierType = selectedValues[0];
  127. formData.value.carrierTypeName = selectedOptions[0].text;
  128. };
  129. const goBack = () => {
  130. router.push('/warehouse-selector?page=stock-in&warehouseId=' + warehouseId.value);
  131. };
  132. // 搜索入库物料
  133. const searchStockIn = value => {
  134. page.value = 1;
  135. total.value = 0;
  136. stockInList.value = [];
  137. loadStockInList();
  138. };
  139. // 处理中转区货位选择结果
  140. const onTransferPositionSelected = item => {
  141. formData.value.transferId = item.id;
  142. formData.value.transferNo = item.no;
  143. formData.value.transferName = item.name;
  144. };
  145. // 处理入库货位选择结果
  146. const onIdlePositionSelected = item => {
  147. formData.value.idleId = item.id;
  148. formData.value.idleNo = item.no;
  149. formData.value.idleName = item.name;
  150. };
  151. // 入库
  152. const stockIn = item => {
  153. console.log(item, '入库');
  154. formData.value.id = item.id;
  155. formData.value.no = item.no;
  156. formData.value.name = item.name;
  157. formData.value.type = item.type;
  158. isShowStockIn.value = true;
  159. };
  160. // 入库确认
  161. const stockInConfirm = () => {
  162. console.log(formData.value, '入库确认');
  163. if (!formData.value.num) {
  164. showFailToast('请输入入库数量');
  165. return;
  166. }
  167. if (!formData.value.transferId) {
  168. showFailToast('请选择中转区货位');
  169. return;
  170. }
  171. if (!formData.value.idleId) {
  172. showFailToast('请选择入库货位');
  173. return;
  174. }
  175. if (!formData.value.carrierType) {
  176. showFailToast('请选择托盘类型');
  177. return;
  178. }
  179. showConfirmDialog({
  180. title: '确认要入库吗?',
  181. message: '如果确认要入库,请点击【确认】按钮,否则点击【取消】按钮。',
  182. })
  183. .then(() => {
  184. submitStockIn();
  185. })
  186. .catch(() => {
  187. console.log('取消');
  188. });
  189. };
  190. // 加载入库物料列表
  191. const loadStockInList = async () => {
  192. try {
  193. const res = await getList(page.value, pageSize.value);
  194. // 搜索时替换数据,上拉加载时追加数据
  195. stockInList.value =
  196. page.value === 1
  197. ? res.data
  198. : [...stockInList.value, ...res.data];
  199. total.value = res.total;
  200. // 检查是否已加载全部数据
  201. if (stockInList.value.length >= total.value) {
  202. finished.value = true;
  203. } else {
  204. page.value++; // 只有在成功加载后才增加页码
  205. }
  206. } catch (error) {
  207. finished.value = true;
  208. processException(error);
  209. } finally {
  210. loading.value = false;
  211. }
  212. };
  213. // 获取入库物料列表API
  214. const getList = (page, pageSize) => {
  215. const start = (page - 1) * pageSize;
  216. const length = pageSize;
  217. const filter = stockInSearch.value;
  218. const url = `/api/InventoryResource/queryStockInInventory?start=${start}&length=${length}&filter=${filter}`;
  219. return new Promise((resolve, reject) => {
  220. ajaxApiGet(url).then(
  221. success => {
  222. const { errorCode, errorMessage, datas, total } = success;
  223. if (errorCode === 0) {
  224. if (datas && datas.length) {
  225. resolve({ data: datas, total: total });
  226. } else {
  227. resolve({ data: [], total: 0 });
  228. }
  229. } else {
  230. const error = { status: 200, responseText: errorMessage };
  231. reject(error);
  232. }
  233. },
  234. error => {
  235. reject(error);
  236. },
  237. );
  238. });
  239. };
  240. const getCarrierTypeList = () => {
  241. const url = '/api/CarrierTypeResource/queryAllType';
  242. ajaxApiGet(url).then(
  243. success => {
  244. const { errorCode, errorMessage, datas, total } = success;
  245. if (errorCode === 0) {
  246. if (datas && datas.length) {
  247. columns.value = datas.map(item => ({ text: item.name, value: item.id }));
  248. } else {
  249. columns.value = [];
  250. }
  251. } else {
  252. showFailToast(errorMessage);
  253. }
  254. },
  255. error => {
  256. processException(error);
  257. },
  258. );
  259. };
  260. // 提交API
  261. const submitStockIn = () => {
  262. const url = '/api/stockInResource/scanGeneratorStockIn';
  263. const params = {
  264. ...formData.value,
  265. };
  266. ajaxApiPost(url, params).then(
  267. success => {
  268. if (success.errorCode === 0) {
  269. const filter = stockInSearch.value;
  270. clearFormData();
  271. searchStockIn(filter);
  272. showSuccessToast('入库成功');
  273. } else {
  274. showFailToast(success.errorMessage);
  275. }
  276. },
  277. error => {
  278. processException(error);
  279. },
  280. );
  281. };
  282. // 取消入库
  283. const cancelStockIn = () => {
  284. clearFormData();
  285. };
  286. // 清除表单数据
  287. const clearFormData = () => {
  288. formData.value = {
  289. no: '',
  290. name: '',
  291. type: '',
  292. num: '',
  293. batchNo: '',
  294. transferId: '',
  295. transferName: '',
  296. transferNo: '',
  297. idleId: '',
  298. idleName: '',
  299. idleNo: '',
  300. inventoryPackaged: false,
  301. carrierType: '',
  302. carrierTypeName: '',
  303. };
  304. carrierType.value = [];
  305. transferPositionSelector.value.clearSelected();
  306. idlePositionSelector.value.clearSelected();
  307. isShowStockIn.value = false;
  308. };
  309. // 拍照
  310. const goTakePhoto = () => {
  311. router.push('/stock-in-scan?warehouseId=' + warehouseId.value + '&warehouseName=' + warehouseName.value);
  312. };
  313. onMounted(() => {
  314. warehouseId.value = route.query.warehouseId || '';
  315. warehouseName.value = route.query.warehouseName || '';
  316. title.value = route.query.warehouseName ? '入库 - ' + route.query.warehouseName : '入库';
  317. loadStockInList();
  318. getCarrierTypeList();
  319. });
  320. </script>
  321. <style scoped>
  322. .van-search {
  323. padding-bottom: 4px !important;
  324. }
  325. .list-container {
  326. padding: 3px 10px;
  327. border: 1px solid #ccc;
  328. border-radius: 4px;
  329. margin: 6px 12px;
  330. }
  331. .in-header {
  332. display: flex;
  333. justify-content: space-between;
  334. align-items: center;
  335. border-bottom: 1px solid #ccc;
  336. padding-bottom: 3px;
  337. }
  338. .footer-btn {
  339. display: flex;
  340. justify-content: space-around;
  341. align-items: center;
  342. margin-bottom: 10px;
  343. }
  344. </style>