You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

703 lines
27 KiB

10 months ago
10 months ago
5 months ago
5 months ago
8 months ago
5 months ago
8 months ago
4 months ago
8 months ago
5 months ago
8 months ago
8 months ago
8 months ago
8 months ago
6 months ago
8 months ago
6 months ago
8 months ago
6 months ago
8 months ago
6 months ago
6 months ago
8 months ago
8 months ago
8 months ago
8 months ago
8 months ago
6 months ago
8 months ago
5 months ago
8 months ago
8 months ago
5 months ago
8 months ago
8 months ago
3 months ago
5 months ago
6 months ago
3 months ago
3 months ago
6 months ago
2 months ago
6 months ago
5 months ago
6 months ago
3 weeks ago
8 months ago
8 months ago
10 months ago
8 months ago
5 months ago
6 months ago
5 months ago
3 weeks ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
8 months ago
5 months ago
8 months ago
5 months ago
8 months ago
5 months ago
8 months ago
10 months ago
4 months ago
8 months ago
10 months ago
8 months ago
10 months ago
8 months ago
8 months ago
6 months ago
6 months ago
5 months ago
5 months ago
8 months ago
10 months ago
3 weeks ago
8 months ago
8 months ago
8 months ago
8 months ago
5 months ago
8 months ago
10 months ago
5 months ago
10 months ago
6 months ago
8 months ago
8 months ago
8 months ago
5 months ago
5 months ago
5 months ago
8 months ago
6 months ago
5 months ago
8 months ago
5 months ago
6 months ago
5 months ago
4 months ago
3 weeks ago
4 months ago
3 weeks ago
4 months ago
8 months ago
5 months ago
6 months ago
5 months ago
6 months ago
5 months ago
4 months ago
3 weeks ago
8 months ago
8 months ago
6 months ago
8 months ago
3 weeks ago
8 months ago
5 months ago
3 weeks ago
5 months ago
3 weeks ago
5 months ago
3 weeks ago
5 months ago
8 months ago
8 months ago
3 weeks ago
8 months ago
8 months ago
8 months ago
10 months ago
6 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
6 months ago
5 months ago
6 months ago
8 months ago
6 months ago
8 months ago
8 months ago
5 months ago
8 months ago
5 months ago
5 months ago
5 months ago
8 months ago
6 months ago
8 months ago
6 months ago
8 months ago
6 months ago
5 months ago
5 months ago
4 months ago
8 months ago
6 months ago
8 months ago
6 months ago
6 months ago
6 months ago
8 months ago
6 months ago
8 months ago
5 months ago
8 months ago
5 months ago
6 months ago
5 months ago
8 months ago
8 months ago
6 months ago
8 months ago
8 months ago
8 months ago
8 months ago
8 months ago
6 months ago
8 months ago
5 months ago
10 months ago
8 months ago
8 months ago
10 months ago
6 months ago
10 months ago
8 months ago
8 months ago
8 months ago
8 months ago
8 months ago
10 months ago
8 months ago
10 months ago
8 months ago
8 months ago
8 months ago
8 months ago
8 months ago
8 months ago
5 months ago
8 months ago
8 months ago
5 months ago
10 months ago
  1. <template>
  2. <div>
  3. <div class="pda-container" v-loading.fullscreen.lock="fullscreenLoading"
  4. element-loading-background="rgba(255, 255, 255, 0.3)"
  5. element-loading-spinner="el-icon-loading"
  6. :element-loading-text="loadingText">
  7. <div class="status-bar">
  8. <div class="goBack" @click="handleBack"><i class="el-icon-arrow-left"></i>上一页</div>
  9. <div class="goBack">登记到达</div>
  10. <div class="network" style="color: #fff" @click="$router.push({ path: '/' })">🏠首页</div>
  11. </div>
  12. <div style="overflow-y: auto">
  13. <!-- Step 1: 扫描 -->
  14. <div v-if="processFlag === 1">
  15. <div class="scan-box" style="margin: 2px;">
  16. <el-input clearable v-model="scanCode" placeholder="扫描PO条码或输入PO号"
  17. @keyup.enter.native="searchPoList" ref="scanCodeRef" />
  18. </div>
  19. <div class="item-list" v-if="poList.length > 0" style="margin: 2px;">
  20. <el-form label-position="top" style="margin: 3px;">
  21. <el-row :gutter="5"
  22. v-for="(poDetail, index) in poList" :key="index" :class="index < poList.length - 1 ? 'bottom-line-row' : ''">
  23. <el-col :span="8">
  24. <el-form-item label="商品编码"><span>{{ poDetail.partNo }}</span></el-form-item>
  25. </el-col>
  26. <el-col :span="8">
  27. <el-form-item label="行号/下达号"><span>{{ poDetail.lineNo }}/{{ poDetail.releaseNo }}</span></el-form-item>
  28. </el-col>
  29. <el-col :span="8">
  30. <el-form-item label="">
  31. <el-button type="text" class="recvButton" @click="recvLine(poDetail)"
  32. style="margin-top: 10px;margin-left: 20px" size="small">接收</el-button>
  33. </el-form-item>
  34. </el-col>
  35. <el-col :span="24">
  36. <el-form-item label="商品描述"><span>{{ poDetail.description }}</span></el-form-item>
  37. </el-col>
  38. <el-col :span="6" :class="{ mt10: getTextWidth(poDetail.description) > 34 }">
  39. <el-form-item label="订单数量"><span>{{ poDetail.purchaseQty }}</span></el-form-item>
  40. </el-col>
  41. <el-col :span="6" :class="{ mt10: getTextWidth(poDetail.description) > 34 }">
  42. <el-form-item label="待收数量"><span>{{ poDetail.qtyToReceive }}</span></el-form-item>
  43. </el-col>
  44. <el-col :span="6" :class="{ mt10: getTextWidth(poDetail.description) > 34 }">
  45. <el-form-item label="计划数量"><span>{{ poDetail.invQtyToReceive }}</span></el-form-item>
  46. </el-col>
  47. <el-col :span="6" :class="{ mt10: getTextWidth(poDetail.description) > 34 }">
  48. <el-form-item style="margin-left: 20px" label="单位"><span>{{ poDetail.purchaseUOM }}</span></el-form-item>
  49. </el-col>
  50. </el-row>
  51. </el-form>
  52. </div>
  53. </div>
  54. <!-- Step 2: 收货明细 -->
  55. <div v-if="processFlag === 2">
  56. <el-form label-position="top" class="form-section" style="margin: 5px;">
  57. <el-row :gutter="20">
  58. <el-col :span="12"><el-form-item label="PO号码"><el-input v-model="recvItem.orderNo" disabled /></el-form-item></el-col>
  59. <el-col :span="12"><el-form-item label="行号/下达号"><el-input v-model="displayLineWdr" disabled /></el-form-item></el-col>
  60. <el-col :span="12"><el-form-item label="商品编码"><el-input v-model="recvItem.partNo" disabled /></el-form-item></el-col>
  61. <el-col :span="12"><el-form-item label="计量单位"><el-input v-model="recvItem.purchaseUOM" disabled /></el-form-item></el-col>
  62. <el-col :span="24"><el-form-item label="商品名称"><el-input v-model="recvItem.description" disabled /></el-form-item></el-col>
  63. <el-col :span="12"><el-form-item label="订单数量"><el-input v-model="recvItem.purchaseQty" disabled /></el-form-item></el-col>
  64. <el-col :span="12"><el-form-item label="待收数量"><el-input v-model="recvItem.qtyToReceive" disabled /></el-form-item></el-col>
  65. <el-col :span="12">
  66. <el-form-item label="此次接收数量">
  67. <el-input v-model="recvItem.transQty" />
  68. </el-form-item>
  69. </el-col>
  70. <el-col :span="12">
  71. <el-form-item label=" ">
  72. <el-button type="text" @click.stop="handlingUnitStep"
  73. style="font-size: 16px;margin-top: -5px" size="small">包装记录</el-button>
  74. </el-form-item>
  75. </el-col>
  76. <el-col :span="6"> <el-form-item label="高度(mm)">
  77. <el-input v-model="recvItem.height" />
  78. </el-form-item></el-col>
  79. <el-col :span="18"><el-form-item label="送货单信息">
  80. <el-input v-model="recvItem.supplierBatchNo" placeholder="请输入送货单信息" />
  81. </el-form-item></el-col>
  82. <el-col :span="12"><el-form-item label="WDR">
  83. <el-input v-model="recvItem.wdr" disabled placeholder="请输入WDR" />
  84. </el-form-item></el-col>
  85. <el-col :span="12"><el-form-item label="到达日期">
  86. <el-date-picker v-model="recvItem.arrivalDate" type="datetime" format="yyyy-MM-dd HH:mm:ss"
  87. value-format="yyyy-MM-dd HH:mm:ss" placeholder="选择到达日期" style="width: 100%"
  88. inputmode="none"
  89. autocomplete="off"
  90. autocorrect="off"
  91. spellcheck="false" />
  92. </el-form-item></el-col>
  93. <el-col :span="12"><el-form-item label="库位">
  94. <el-input v-model="recvItem.locationNo" placeholder="请输入库位" /></el-form-item></el-col>
  95. <el-col :span="12"><el-form-item label="批号"><el-input v-model="recvItem.batchNo" placeholder="请输入批号" /></el-form-item></el-col>
  96. <el-col :span="8" style="margin-top: 10px"><el-form-item><el-button type="text" style="font-size: 16px;margin-left: 30px" @click="processFlag = 1">回退</el-button></el-form-item></el-col>
  97. <el-col :span="8" style="margin-top: 10px"><el-form-item><el-button type="text" style="font-size: 16px;margin-left: 20px" @click="receivePo">保存</el-button></el-form-item></el-col>
  98. <el-col :span="8" style="margin-top: 10px"><el-form-item><el-button type="text" style="font-size: 16px;margin-left: 10px" @click="$router.push('/')">退出</el-button></el-form-item></el-col>
  99. </el-row>
  100. </el-form>
  101. </div>
  102. <!-- Step 3: 包装记录 -->
  103. <div v-if="processFlag === 3">
  104. <el-form label-position="top" class="form-section" style="margin: 5px;">
  105. <el-row :gutter="20">
  106. <el-col :span="12"><el-form-item label="商品编码"><el-input v-model="recvItem.partNo" disabled /></el-form-item></el-col>
  107. <el-col :span="12"><el-form-item label="待收数量"><el-input v-model="recvItem.qtyToReceive" disabled /></el-form-item></el-col>
  108. <el-col :span="24"><el-form-item label="商品名称"><el-input v-model="recvItem.description" disabled /></el-form-item></el-col>
  109. <!-- 自动计算开关 -->
  110. <el-col :span="24" style="margin-bottom: 10px;">
  111. <el-form-item>
  112. <el-switch
  113. v-model="autoCalculate"
  114. active-text="自动计算模式"
  115. inactive-text="手动输入模式"
  116. active-color="#17B3A3"
  117. style="display: block;"
  118. >
  119. </el-switch>
  120. <span v-if="autoCalculate" style="font-size: 13px; color: #17B3A3; margin-top: 5px; display: block; font-weight: bold;">
  121. 💡 输入单包装数量后按回车将根据待收数量自动分配包装
  122. </span>
  123. <span v-else style="font-size: 12px; color: #999; margin-top: 5px; display: block;">
  124. 手动输入单包装数量和包装数然后点击"创建"按钮
  125. </span>
  126. </el-form-item>
  127. </el-col>
  128. <el-col :span="8" style="margin-top: 5px"><el-form-item label="单包装数量">
  129. <el-input
  130. v-model="hanlingItem.perQty"
  131. type="number"
  132. :placeholder="autoCalculate ? '输入完按回车或失去焦点' : '请输入单包装数量'"
  133. @blur="autoCalculate && autoCreateHandlingUnits()"
  134. @keyup.enter.native="autoCalculate && autoCreateHandlingUnits()"
  135. />
  136. </el-form-item></el-col>
  137. <el-col :span="8" style="margin-top: 5px" v-if="!autoCalculate"><el-form-item label="包装数">
  138. <el-input v-model="hanlingItem.packageQty" type="number" placeholder="请输入包装数" />
  139. </el-form-item></el-col>
  140. <el-col :span="8" v-if="!autoCalculate" style="margin-top: 16px"><el-form-item>
  141. <el-button type="text" @click="createHandlingUnit" style="font-size: 16px">创建</el-button></el-form-item></el-col>
  142. <el-col :span="8" v-else style="margin-top: 16px"><el-form-item>
  143. <el-button type="text" style="font-size: 16px">确定</el-button></el-form-item></el-col>
  144. <el-table :data="handlingUnit" :row-style="{ height: '30px' }" style="width: 94%; margin-left: 10px;" highlight-current-row>
  145. <el-table-column prop="code" label="序号" />
  146. <el-table-column prop="packageQty" label="包装数" width="80" />
  147. <el-table-column prop="perQty" label="单包装数量" width="100" />
  148. <el-table-column label="操作" v-if="!autoCalculate">
  149. <template slot-scope="scope">
  150. <a @click="removeItem(scope.$index)">删除</a>
  151. </template>
  152. </el-table-column>
  153. </el-table>
  154. <el-col :span="24" v-if="autoCalculate && handlingUnit.length > 0" style="margin-top: 10px;">
  155. <span style="font-size: 12px; color: #999;">
  156. 💡 如需修改请重新输入单包装数量
  157. </span>
  158. </el-col>
  159. <el-col :span="24"><el-form-item><span>合计</span><span style="margin-left: 38%">{{ totalQty }}</span></el-form-item></el-col>
  160. <el-col :span="12"><el-form-item><el-button type="text" @click="processFlag = 2" style="font-size: 18px;margin-left: 60px">回退</el-button></el-form-item></el-col>
  161. <el-col :span="12"><el-form-item><el-button type="text" @click="processFlag = 2" style="font-size: 18px;">确定</el-button></el-form-item></el-col>
  162. </el-row>
  163. </el-form>
  164. </div>
  165. </div>
  166. </div>
  167. </div>
  168. </template>
  169. <script>
  170. import { getPoList, receivePo, printLabel, getNextItemNo, validateLocationForReceiveCase,getPartAttributeInfo } from "@/api/po/po.js";
  171. export default {
  172. data() {
  173. return {
  174. processFlag: 1,
  175. scanCode: '',
  176. poList: [],
  177. recvItem: {},
  178. handlingUnit: [],
  179. hanlingItem: { code: '', qty: '', perQty: '', packageQty: '' },
  180. site:localStorage.getItem('site'),
  181. warehouseId:localStorage.getItem('selectedWarehouse'),
  182. fullscreenLoading: false, // 控制全屏loading
  183. loadingText: '加载中...', // 动态loading文本
  184. autoCalculate: true // 自动计算包装数开关
  185. };
  186. },
  187. computed: {
  188. totalQty() {
  189. // 使用整数运算避免浮点数精度问题
  190. const sumInCents = this.handlingUnit.reduce((sum, item) => {
  191. return sum + Math.round(Number(item.qty) * 100);
  192. }, 0);
  193. const sum = sumInCents / 100;
  194. this.recvItem.transQty = sum;
  195. return sum;
  196. },
  197. huKey() {
  198. return `hu_${this.recvItem.poNumber}_${this.recvItem.lineNo}`;
  199. },
  200. displayLineWdr() {
  201. return `${this.recvItem.lineNo || ''}/${this.recvItem.releaseNo}`;
  202. }
  203. },
  204. watch: {
  205. // 监听自动计算开关变化
  206. autoCalculate(newVal, oldVal) {
  207. // 切换模式时清空输入框和HU列表
  208. this.hanlingItem.perQty = '';
  209. this.hanlingItem.packageQty = '';
  210. this.handlingUnit = [];
  211. localStorage.setItem(this.huKey, JSON.stringify(this.handlingUnit));
  212. if (newVal) {
  213. this.$message.success('已切换到自动创建HU模式');
  214. } else {
  215. this.$message.success('已切换到手动输入模式');
  216. }
  217. }
  218. },
  219. methods: {
  220. // 计算“显示宽度”
  221. getTextWidth(text) {
  222. if (!text) return 0
  223. let len = 0
  224. for (let char of text) {
  225. // 中文、全角符号
  226. if (/[\u4e00-\u9fa5\u3000-\u303F\uFF00-\uFFEF]/.test(char)) {
  227. len += 2
  228. } else {
  229. len += 1
  230. }
  231. }
  232. return len
  233. },
  234. handleBack() {
  235. if (this.processFlag === 1) this.$router.back();
  236. else if (this.processFlag === 3) this.processFlag = 2;
  237. else this.processFlag = 1;
  238. },
  239. searchPoList() {
  240. if (!this.scanCode) return this.poList = [];
  241. // 开始搜索时显示loading
  242. this.loadingText = '搜索中...';
  243. this.fullscreenLoading = true;
  244. getPoList({ poNumber: this.scanCode,site: this.site }).then(({ data }) => {
  245. if (data.code === 0) {
  246. this.poList = data.rows
  247. } else {
  248. this.$message.error(data.msg || '操作失败');
  249. }
  250. // 搜索完成后让输入框失去焦点
  251. this.$nextTick(() => {
  252. if (this.$refs.scanCodeRef) {
  253. this.$refs.scanCodeRef.blur();
  254. }
  255. });
  256. }).catch(error => {
  257. console.error('搜索失败:', error);
  258. this.$message.error('搜索失败,请重试');
  259. }).finally(() => {
  260. // 搜索完成后关闭loading
  261. this.fullscreenLoading = false;
  262. });
  263. },
  264. async recvLine(row) {
  265. if (row.authorizationRequired=='TRUE') {
  266. return this.$message.warning("该采购订单需要审核,无法接收");
  267. }
  268. if (row.receiveCaseDB!='INVDIR' && row.receiveCaseDB!='QAINV' && row.receiveCaseDB!='ARRINV') {
  269. return this.$message.warning("该采购订单行的收货方式为"+row.receiveCase+",无法接收");
  270. }
  271. if (row.poStatus === 'Stopped' || row.poStatus === 'Closed' || row.poStatus === 'Cancelled' || row.poStatus === 'Planned') {
  272. return this.$message.warning("该采购订单状态为"+row.poStatus+",无法接收");
  273. }
  274. if (row.status === 'Stopped' || row.status === 'Closed' || row.status === 'Cancelled') {
  275. return this.$message.warning("该采购订单行状态为"+row.status+",无法接收");
  276. }
  277. if (row.convFactor !== 1) {
  278. return this.$message.warning("采购计量单位和库存计量单位不一致,无法接收");
  279. }
  280. // 获取下一个itemNo
  281. let nextItemNo = 1;
  282. try {
  283. const { data } = await getNextItemNo({
  284. orderNo: row.orderNo,
  285. lineNo: row.lineNo,
  286. releaseNo: row.releaseNo || ''
  287. });
  288. if (data.code === 0) {
  289. nextItemNo = data.data;
  290. }
  291. } catch (error) {
  292. console.error('获取itemNo失败:', error);
  293. // 失败时使用默认值1
  294. }
  295. let site = this.site;
  296. let partNo = row.partNo;
  297. let height = '';
  298. let locationNo = '';
  299. try {
  300. const { data } = await getPartAttributeInfo({ site, partNo });
  301. if (data.code === 0) {
  302. height = data.data.height;
  303. if (data.partDefLoc && data.partDefLoc.length > 0) {
  304. locationNo = data.partDefLoc[0].warehouse==='INK'?data.partDefLoc[0].locationNo:'';
  305. }
  306. }
  307. } catch (error) {
  308. console.error('获取物料高度失败:', error);
  309. }
  310. this.recvItem = {
  311. ...row,
  312. poNo: row.orderNo || this.scanCode,
  313. dueinQty: row.qtyToReceive || row.invQtyToReceive,
  314. transQty: '',
  315. itemNo: nextItemNo,
  316. batchNo: row.orderNo+'-'+row.lineNo+'-'+row.releaseNo+'-'+nextItemNo,
  317. deliveryDate: row.plannedDeliveryDate || '',
  318. arrivalDate: this.getCurrentDate(),
  319. supplierBatchNo: '',
  320. wdr:"*",
  321. height: height,
  322. locationNo: locationNo
  323. };
  324. this.processFlag = 2;
  325. },
  326. // 获取当前日期
  327. getCurrentDate() {
  328. const now = new Date();
  329. return now.getFullYear() + '-' +
  330. String(now.getMonth() + 1).padStart(2, '0') + '-' +
  331. String(now.getDate()).padStart(2, '0') + ' ' +
  332. String(now.getHours()).padStart(2, '0') + ':' +
  333. String(now.getMinutes()).padStart(2, '0') + ':' +
  334. String(now.getSeconds()).padStart(2, '0');
  335. },
  336. handlingUnitStep() {
  337. this.processFlag = 3;
  338. this.autoCalculate = false; // 默认使用手动输入模式
  339. const saved = localStorage.getItem(this.huKey);
  340. this.handlingUnit = saved ? JSON.parse(saved) : [];
  341. },
  342. /**
  343. * 自动创建HU列表自动模式
  344. */
  345. autoCreateHandlingUnits() {
  346. const perQty = this.hanlingItem.perQty;
  347. // 验证输入
  348. if (!perQty || isNaN(perQty) || parseFloat(perQty) <= 0) {
  349. this.$message.warning('请输入有效的单包装数量');
  350. return;
  351. }
  352. // 优先使用"此次接收数量",如果没有则使用"待收数量"
  353. let qtyToReceive = parseFloat(this.recvItem.transQty) || 0;
  354. if (qtyToReceive <= 0) {
  355. qtyToReceive = parseFloat(this.recvItem.qtyToReceive) || 0;
  356. }
  357. if (qtyToReceive <= 0) {
  358. this.$message.warning('待收数量为0,无法自动计算包装');
  359. this.hanlingItem.perQty = '';
  360. return;
  361. }
  362. const perQtyValue = parseFloat(perQty);
  363. // 计算需要多少个完整包装
  364. const packageQty = Math.floor(qtyToReceive / perQtyValue);
  365. // 计算余数(最后一个包装的数量)- 使用整数运算避免浮点数精度问题
  366. const remainderInCents = Math.round(qtyToReceive * 100) - Math.round(perQtyValue * 100) * packageQty;
  367. const remainder = remainderInCents / 100;
  368. // 清空现有的HU列表
  369. this.handlingUnit = [];
  370. // 创建完整包装的HU
  371. if (packageQty > 0) {
  372. for (let i = 1; i <= packageQty; i++) {
  373. const qty = perQtyValue;
  374. const code = String(i);
  375. this.handlingUnit.push({
  376. code: code,
  377. perQty: perQtyValue,
  378. packageQty: 1,
  379. qty: qty
  380. });
  381. }
  382. }
  383. // 如果有余数,创建最后一个HU
  384. if (remainder > 0) {
  385. const code = String(this.handlingUnit.length + 1);
  386. this.handlingUnit.push({
  387. code: code,
  388. perQty: remainder,
  389. packageQty: 1,
  390. qty: remainder
  391. });
  392. }
  393. // 保存到localStorage
  394. localStorage.setItem(this.huKey, JSON.stringify(this.handlingUnit));
  395. // 清空输入框
  396. this.hanlingItem.perQty = '';
  397. this.hanlingItem.packageQty = '';
  398. this.$message.success(`自动创建了${this.handlingUnit.length}个HU,合计数量:${qtyToReceive}`);
  399. },
  400. /**
  401. * 手动创建HU手动模式
  402. */
  403. createHandlingUnit() {
  404. const { perQty, packageQty } = this.hanlingItem;
  405. if (!perQty || !packageQty || isNaN(perQty) || isNaN(packageQty)) {
  406. return this.$message.warning("请填写有效的包装信息");
  407. }
  408. // 使用整数运算避免浮点数精度问题
  409. const qty = Math.round(parseFloat(perQty) * 100) * parseInt(packageQty) / 100;
  410. const code = String(this.handlingUnit.length + 1);
  411. const newItem = { ...this.hanlingItem, qty, code };
  412. this.handlingUnit.push(newItem);
  413. localStorage.setItem(this.huKey, JSON.stringify(this.handlingUnit));
  414. // 创建HU后清空包装数和单包装数量
  415. this.hanlingItem.perQty = '';
  416. this.hanlingItem.packageQty = '';
  417. },
  418. removeItem(index) {
  419. this.handlingUnit.splice(index, 1);
  420. localStorage.setItem(this.huKey, JSON.stringify(this.handlingUnit));
  421. },
  422. // 清除所有handlingUnit缓存
  423. clearAllHandlingUnitCache() {
  424. this.hanlingItem = { code: '', qty: '', perQty: '', packageQty: '' };
  425. const keys = Object.keys(localStorage);
  426. keys.forEach(key => {
  427. if (key.startsWith('hu_')) {
  428. localStorage.removeItem(key);
  429. }
  430. });
  431. },
  432. // 校验库位
  433. async validateLocation() {
  434. if (!this.recvItem.locationNo || !this.recvItem.receiveCaseDB) {
  435. return;
  436. }
  437. try {
  438. const { data } = await validateLocationForReceiveCase({
  439. site: this.site,
  440. locationId: this.recvItem.locationNo,
  441. receiveCaseDB: this.recvItem.receiveCaseDB
  442. });
  443. if (data.code !== 0) {
  444. this.$message.error(data.msg || '库位校验失败');
  445. // 清空库位输入
  446. this.recvItem.locationNo = '';
  447. }
  448. } catch (error) {
  449. console.error('库位校验失败:', error);
  450. this.$message.error('库位校验失败,请重试');
  451. // 清空库位输入
  452. this.recvItem.locationNo = '';
  453. }
  454. },
  455. async receivePo() {
  456. const item = this.recvItem;
  457. if (item.transQty > item.qtyToReceive) {
  458. try {
  459. await this.$confirm(
  460. `本次入库数量(${item.transQty}) ,超过待收数量(${item.qtyToReceive}),是否继续?`,
  461. '数量超出提示',
  462. {
  463. confirmButtonText: '继续',
  464. cancelButtonText: '取消',
  465. type: 'warning'
  466. }
  467. );
  468. } catch (error) {
  469. // 用户取消
  470. return;
  471. }
  472. }
  473. if (this.fullscreenLoading) return; // 防止重复点
  474. this.loadingText = '提交中...';
  475. this.fullscreenLoading = true;
  476. if (!item.transQty || !item.locationNo || !item.batchNo) {
  477. this.fullscreenLoading = false;
  478. return this.$message.error("请填写完整信息");
  479. }
  480. if(item.qtyToReceive>item.qtyToReceive){
  481. this.fullscreenLoading = false;
  482. return this.$message.error("接收数量不能大于待收数量");
  483. }
  484. // 提交前再次校验库位
  485. try {
  486. const { data: validationData } = await validateLocationForReceiveCase({
  487. site: this.site,
  488. locationId: item.locationNo,
  489. receiveCaseDB: item.receiveCaseDB
  490. });
  491. if (validationData.code !== 0) {
  492. this.fullscreenLoading = false;
  493. return this.$message.error(validationData.msg || '库位校验失败');
  494. }
  495. } catch (error) {
  496. console.error('库位校验失败:', error);
  497. this.fullscreenLoading = false;
  498. return this.$message.error('库位校验失败,请重试');
  499. }
  500. // 构建符合服务端TransDetailDto结构的数据
  501. const receiveData = {
  502. // 基本字段
  503. site: this.site,
  504. warehouseId: this.warehouseId,
  505. partNo: item.partNo,
  506. partDesc: item.description,
  507. transQty: item.transQty,
  508. batchNo: item.batchNo,
  509. locationNo: item.locationNo,
  510. itemNo: item.itemNo,
  511. wdr: item.wdr || '*',
  512. deliveryDate: item.deliveryDate,
  513. arrivalDate: item.arrivalDate,
  514. supplierBatchNo: item.supplierBatchNo,
  515. samplePercent: item.samplePercent || 0,
  516. sampleQty: item.sampleQty || 0,
  517. qtyToReceive: item.qtyToReceive,
  518. engChgLevel: item.engChgLevel,
  519. // PO相关字段
  520. poNo: item.poNumber || item.poNo,
  521. orderNo: item.orderNo,
  522. lineNo: item.lineNo,
  523. releaseNo: item.releaseNo,
  524. receiptNo: item.receiptNo,
  525. orderRef1: item.orderNo,
  526. supplierNo: item.supplierNo,
  527. purchaseUOM: item.purchaseUOM,
  528. receiveCase: item.receiveCase,
  529. receiveCaseDB: item.receiveCaseDB,
  530. inventoryPartDB: item.inventoryPartDB,
  531. // 业务控制字段
  532. needHandlingUnit: item.needHandlingUnit ,
  533. needCheck: item.needCheck ,
  534. warehouseType: item.warehouseType ,
  535. // 日期字段
  536. manufactureDate: item.manufactureDate,
  537. height: item.height,
  538. // 处理单元列表
  539. handlingUnitList: this.handlingUnit.map(hu => ({
  540. perQty: hu.perQty,
  541. packageQty: hu.packageQty
  542. }))
  543. };
  544. receivePo(receiveData).then(({ data }) => {
  545. if (data.code === 0) {
  546. this.$message.success("操作成功");
  547. this.clearAllHandlingUnitCache();
  548. let printLabelType;
  549. if (item.partNo && item.partNo.startsWith("80")) {
  550. printLabelType = '库存成品标签';
  551. } else {
  552. printLabelType = 'BIL标签';
  553. }
  554. this.printViaServer(data.data,item.needCheck,printLabelType); // 调用打印
  555. this.processFlag = 1;
  556. this.scanCode = '';
  557. this.poList = [];
  558. this.recvItem = {};
  559. this.handlingUnit = [];
  560. } else {
  561. this.$message.error(data.msg || '操作失败');
  562. }
  563. }).catch(error => {
  564. console.error('接收失败:', error);
  565. this.$message.error('网络错误,请重试');
  566. }).finally(() => {
  567. this.fullscreenLoading = false; // 结束后关闭loading
  568. });
  569. },
  570. /**
  571. * 通过服务器打印
  572. */
  573. async printViaServer(receiptNo,needCheck,printLabelType) {
  574. this.$emit('print-start')
  575. try {
  576. const printRequest = {
  577. reportId: this.reportId,
  578. zplCode: this.zplCode,
  579. paperSize: this.paperSize,
  580. orientation: this.orientation,
  581. dpi: this.dpi,
  582. userId: localStorage.getItem('userName'),
  583. username: localStorage.getItem('userName'),
  584. site: localStorage.getItem('site'),
  585. receiptNo: receiptNo,
  586. needCheck:needCheck,
  587. labelType:printLabelType
  588. }
  589. const { data } = await printLabel(printRequest)
  590. if (data.code === 200) {
  591. this.$message.success(`打印任务已发送!`)
  592. }
  593. } catch (error) {
  594. console.error('服务器打印失败:', error)
  595. this.$message.error(`打印失败: ${error.message || error}`)
  596. }
  597. },
  598. },
  599. mounted() {
  600. this.$nextTick(() => this.$refs.scanCodeRef.focus());
  601. },
  602. };
  603. </script>
  604. <style scoped>
  605. .mt10 {
  606. margin-top: 10px;
  607. }
  608. .scan-box input {
  609. width: 100%;
  610. padding: 12px;
  611. font-size: 16px;
  612. }
  613. .item-list {
  614. flex: 1;
  615. overflow-y: auto;
  616. margin: 10px 0;
  617. border: 1px solid rgba(200, 200, 200, 0.8);
  618. }
  619. .item-list span {
  620. color: #000;
  621. font-size: 15px;
  622. }
  623. .bottom-line-row {
  624. border-bottom: 1px solid rgba(200, 200, 200, 0.8);
  625. }
  626. .recvButton {
  627. font-size: 16px;
  628. border-radius: 3px;
  629. color: #17b3a3;
  630. }
  631. .item-list .el-row {
  632. cursor: pointer;
  633. transition: background 0.3s;
  634. }
  635. .item-list .el-row:hover {
  636. background: #f5f7fa;
  637. }
  638. .disabled-button {
  639. color: #ccc !important;
  640. cursor: not-allowed !important;
  641. }
  642. .form-section >>> .el-col {
  643. margin-bottom: 2px;
  644. }
  645. .status-bar {
  646. display: flex;
  647. justify-content: space-between;
  648. align-items: center;
  649. background: #17b3a3;
  650. color: white;
  651. }
  652. /* 自定义loading样式 */
  653. .pda-container >>> .el-loading-mask {
  654. background-color: rgba(255, 255, 255, 0.3) !important;
  655. }
  656. .pda-container >>> .el-loading-spinner {
  657. margin-top: -25px;
  658. }
  659. .pda-container >>> .el-loading-spinner .circular {
  660. width: 35px;
  661. height: 35px;
  662. }
  663. .pda-container >>> .el-loading-text {
  664. color: #17b3a3 !important;
  665. font-size: 14px;
  666. font-weight: 500;
  667. margin-top: 10px;
  668. }
  669. </style>