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.

425 lines
18 KiB

8 months ago
8 months ago
4 months ago
6 months ago
4 months ago
6 months ago
6 months ago
6 months ago
6 months ago
4 months ago
6 months ago
4 months ago
6 months ago
4 months ago
6 months ago
4 months ago
4 months ago
6 months ago
7 months ago
6 months ago
6 months ago
6 months ago
4 months ago
6 months ago
7 months ago
6 months ago
7 months ago
4 months ago
6 months ago
7 months ago
8 months ago
6 months ago
4 months ago
6 months ago
8 months ago
7 months ago
8 months ago
6 months ago
8 months ago
7 months ago
6 months ago
4 months ago
4 months ago
4 months ago
6 months ago
8 months ago
6 months ago
6 months ago
6 months ago
6 months ago
4 months ago
6 months ago
8 months ago
4 months ago
6 months ago
7 months ago
6 months ago
6 months ago
4 months ago
6 months ago
4 months ago
4 months ago
6 months ago
4 months ago
4 months ago
6 months ago
6 months ago
4 months ago
6 months ago
7 months ago
6 months ago
7 months ago
6 months ago
8 months ago
4 months ago
6 months ago
4 months ago
6 months ago
4 months ago
6 months ago
6 months ago
6 months ago
4 months ago
6 months ago
4 months ago
6 months ago
4 months ago
6 months ago
4 months ago
6 months ago
4 months ago
4 months ago
4 months ago
6 months ago
4 months ago
6 months ago
4 months ago
6 months ago
6 months ago
4 months ago
6 months ago
6 months ago
6 months ago
6 months ago
6 months ago
4 months ago
6 months ago
8 months ago
6 months ago
8 months ago
6 months ago
8 months ago
4 months ago
8 months ago
7 months ago
6 months ago
7 months ago
6 months ago
7 months ago
8 months ago
7 months ago
8 months ago
7 months ago
6 months ago
7 months ago
6 months ago
6 months ago
6 months ago
6 months ago
8 months ago
  1. <template>
  2. <div>
  3. <div class="pda-container" v-loading.fullscreen.lock="fullscreenLoading">
  4. <div class="status-bar">
  5. <div class="goBack" @click="handleBack"><i class="el-icon-arrow-left"></i>上一页</div>
  6. <div class="goBack">采购订单接收</div>
  7. <div class="network" style="color: #fff" @click="$router.push({ path: '/' })">🏠首页</div>
  8. </div>
  9. <div style="overflow-y: auto">
  10. <!-- Step 1: 扫描 -->
  11. <div v-if="processFlag === 1">
  12. <div class="scan-box" style="margin: 2px;">
  13. <el-input clearable v-model="scanCode" placeholder="扫描PO条码或输入PO号"
  14. @keyup.enter.native="searchPoList" ref="scanCodeRef" />
  15. </div>
  16. <div class="item-list" v-if="poList.length > 0" style="margin: 2px;">
  17. <el-form label-position="top" style="margin: 3px;">
  18. <el-row :gutter="5" @click.native="recvLine(poDetail)"
  19. v-for="(poDetail, index) in poList" :key="index" :class="index < poList.length - 1 ? 'bottom-line-row' : ''">
  20. <el-col :span="8">
  21. <el-form-item label="商品编码"><span>{{ poDetail.partNo }}</span></el-form-item>
  22. </el-col>
  23. <el-col :span="8">
  24. <el-form-item label="行号/下达号"><span>{{ poDetail.lineNo }}/{{ poDetail.wdr || '*' }}</span></el-form-item>
  25. </el-col>
  26. <el-col :span="8">
  27. <el-form-item label="">
  28. <el-button type="text" class="recvButton" @click="recvLine(poDetail)"
  29. style="margin-top: 10px;margin-left: 20px" size="small">接收</el-button>
  30. </el-form-item>
  31. </el-col>
  32. <el-col :span="24">
  33. <el-form-item label="商品描述"><span>{{ poDetail.description }}</span></el-form-item>
  34. </el-col>
  35. <el-col :span="6" :class="{ mt10: getTextWidth(poDetail.description) > 34 }">
  36. <el-form-item label="订单数量"><span>{{ poDetail.purchaseQty }}</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.qtyToReceive }}</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.invQtyToReceive }}</span></el-form-item>
  43. </el-col>
  44. <el-col :span="6" :class="{ mt10: getTextWidth(poDetail.description) > 34 }">
  45. <el-form-item style="margin-left: 20px" label="单位"><span>{{ poDetail.purchaseUOM }}</span></el-form-item>
  46. </el-col>
  47. </el-row>
  48. </el-form>
  49. </div>
  50. </div>
  51. <!-- Step 2: 收货明细 -->
  52. <div v-if="processFlag === 2">
  53. <el-form label-position="top" class="form-section" style="margin: 5px;">
  54. <el-row :gutter="20">
  55. <el-col :span="12"><el-form-item label="PO号码"><el-input v-model="recvItem.orderNo" disabled /></el-form-item></el-col>
  56. <el-col :span="12"><el-form-item label="行号/下达号"><el-input v-model="displayLineWdr" disabled /></el-form-item></el-col>
  57. <el-col :span="12"><el-form-item label="商品编码"><el-input v-model="recvItem.partNo" disabled /></el-form-item></el-col>
  58. <el-col :span="12"><el-form-item label="计量单位"><el-input v-model="recvItem.purchaseUOM" disabled /></el-form-item></el-col>
  59. <el-col :span="24"><el-form-item label="商品名称"><el-input v-model="recvItem.description" disabled /></el-form-item></el-col>
  60. <el-col :span="12"><el-form-item label="订单数量"><el-input v-model="recvItem.purchaseQty" disabled /></el-form-item></el-col>
  61. <el-col :span="12"><el-form-item label="待收数量"><el-input v-model="recvItem.qtyToReceive" disabled /></el-form-item></el-col>
  62. <el-col :span="12">
  63. <el-form-item label="此次接收数量">
  64. <el-input v-model="recvItem.transQty" :disabled="recvItem.needHandlingUnit === 'Y'" />
  65. </el-form-item>
  66. </el-col>
  67. <el-col :span="12">
  68. <el-form-item label=" ">
  69. <el-button type="text" @click.stop="handlingUnitStep" :disabled="recvItem.needHandlingUnit !== 'Y'"
  70. :class="{ 'disabled-button': recvItem.needHandlingUnit !== 'Y' }"
  71. style="font-size: 16px" size="small">包装记录</el-button>
  72. </el-form-item>
  73. </el-col>
  74. <el-col :span="12"><el-form-item label="制造日期">
  75. <el-date-picker v-model="recvItem.manufactureDate"
  76. type="date" format="yyyy-MM-dd" value-format="yyyy-MM-dd"
  77. placeholder="选择制造日期" style="width: 100%" />
  78. </el-form-item></el-col>
  79. <el-col :span="12"><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="交货日期">
  83. <el-date-picker v-model="recvItem.deliveryDate"
  84. type="date" format="yyyy-MM-dd" value-format="yyyy-MM-dd"
  85. placeholder="选择交货日期" style="width: 100%" />
  86. </el-form-item></el-col>
  87. <el-col :span="12"><el-form-item label="到达日期">
  88. <el-date-picker v-model="recvItem.arrivalDate" type="date" format="yyyy-MM-dd HH:mm:ss"
  89. value-format="yyyy-MM-dd HH:mm:ss" placeholder="选择到达日期" style="width: 100%" />
  90. </el-form-item></el-col>
  91. <el-col :span="12"><el-form-item label="库位"><el-input v-model="recvItem.locationNo" placeholder="请输入库位" /></el-form-item></el-col>
  92. <el-col :span="12"><el-form-item label="批号"><el-input v-model="recvItem.batchNo" placeholder="请输入批号" /></el-form-item></el-col>
  93. <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>
  94. <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>
  95. <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>
  96. </el-row>
  97. </el-form>
  98. </div>
  99. <!-- Step 3: 包装记录 -->
  100. <div v-if="processFlag === 3">
  101. <el-form label-position="top" class="form-section" style="margin: 5px;">
  102. <el-row :gutter="20">
  103. <el-col :span="12"><el-form-item label="商品编码"><el-input v-model="recvItem.partNo" disabled /></el-form-item></el-col>
  104. <el-col :span="12"><el-form-item label="计量单位"><el-input v-model="recvItem.purchaseUOM" disabled /></el-form-item></el-col>
  105. <el-col :span="24"><el-form-item label="商品名称"><el-input v-model="recvItem.description" disabled /></el-form-item></el-col>
  106. <el-col :span="8"><el-form-item label="单包装数量"><el-input v-model="hanlingItem.perQty" /></el-form-item></el-col>
  107. <el-col :span="8"><el-form-item label="包装数"><el-input v-model="hanlingItem.packageQty" /></el-form-item></el-col>
  108. <el-col :span="8" style="margin-top: 24px"><el-form-item>
  109. <el-button type="text" @click="createHandlingUnit" style="font-size: 16px">创建</el-button></el-form-item></el-col>
  110. <el-table :data="handlingUnit" :row-style="{ height: '30px' }" style="width: 94%; margin-left: 10px;" highlight-current-row>
  111. <el-table-column prop="code" label="Unit ID" />
  112. <el-table-column prop="qty" label="数量" width="60" />
  113. <el-table-column label="操作">
  114. <template slot-scope="scope">
  115. <a @click="removeItem(scope.$index)">删除</a>
  116. </template>
  117. </el-table-column>
  118. </el-table>
  119. <el-col :span="24"><el-form-item><span>合计</span><span style="margin-left: 38%">{{ totalQty }}</span></el-form-item></el-col>
  120. <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>
  121. <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>
  122. </el-row>
  123. </el-form>
  124. </div>
  125. </div>
  126. </div>
  127. </div>
  128. </template>
  129. <script>
  130. import { getPoList, receivePo, printLabel, getNextItemNo } from "@/api/po/po.js";
  131. export default {
  132. data() {
  133. return {
  134. processFlag: 1,
  135. scanCode: '',
  136. poList: [],
  137. recvItem: {},
  138. handlingUnit: [],
  139. hanlingItem: { code: '', qty: '', perQty: '', packageQty: '' },
  140. site:localStorage.getItem('site'),
  141. warehouseId:localStorage.getItem('selectedWarehouse'),
  142. fullscreenLoading: false // 控制全屏loading
  143. };
  144. },
  145. computed: {
  146. totalQty() {
  147. const sum = this.handlingUnit.reduce((sum, item) => sum + Number(item.qty), 0);
  148. this.recvItem.transQty = sum;
  149. return sum;
  150. },
  151. huKey() {
  152. return `hu_${this.recvItem.poNumber}_${this.recvItem.lineNo}`;
  153. },
  154. displayLineWdr() {
  155. return `${this.recvItem.lineNo || ''}/${this.recvItem.wdr || '*'}`;
  156. }
  157. },
  158. methods: {
  159. // 计算“显示宽度”
  160. getTextWidth(text) {
  161. if (!text) return 0
  162. let len = 0
  163. for (let char of text) {
  164. // 中文、全角符号
  165. if (/[\u4e00-\u9fa5\u3000-\u303F\uFF00-\uFFEF]/.test(char)) {
  166. len += 2
  167. } else {
  168. len += 1
  169. }
  170. }
  171. return len
  172. },
  173. handleBack() {
  174. if (this.processFlag === 1) this.$router.back();
  175. else if (this.processFlag === 3) this.processFlag = 2;
  176. else this.processFlag = 1;
  177. },
  178. searchPoList() {
  179. if (!this.scanCode) return this.poList = [];
  180. getPoList({ poNumber: this.scanCode,site: this.site }).then(({ data }) => {
  181. if (data.code === 0) {
  182. this.poList = data.rows
  183. } else {
  184. this.$message.error(data.msg || '操作失败');
  185. }
  186. });
  187. },
  188. async recvLine(row) {
  189. /* if (row.receiveCaseDB!='INVDIR' && row.receiveCaseDB!='QAINV') {
  190. return this.$message.warning("该采购订单行的收货方式为"+row.receiveCase+",无法接收");
  191. }*/
  192. if (row.poStatus === 'Stopped' || row.poStatus === 'Closed' || row.poStatus === 'Cancelled' || row.poStatus === 'Planned') {
  193. return this.$message.warning("该采购订单状态为"+row.poStatus+",无法接收");
  194. }
  195. if (row.status === 'Stopped' || row.status === 'Closed' || row.status === 'Cancelled') {
  196. return this.$message.warning("该采购订单行状态为"+row.status+",无法接收");
  197. }
  198. if (row.convFactor !== 1) {
  199. return this.$message.warning("采购计量单位和库存计量单位不一致,无法接收");
  200. }
  201. // 获取下一个itemNo
  202. let nextItemNo = 1;
  203. try {
  204. const { data } = await getNextItemNo({
  205. orderNo: row.orderNo,
  206. lineNo: row.lineNo,
  207. releaseNo: row.releaseNo || ''
  208. });
  209. if (data.code === 0) {
  210. nextItemNo = data.data;
  211. }
  212. } catch (error) {
  213. console.error('获取itemNo失败:', error);
  214. // 失败时使用默认值1
  215. }
  216. this.recvItem = {
  217. ...row,
  218. poNo: row.poNumber || this.scanCode,
  219. dueinQty: row.qtyToReceive || row.invQtyToReceive,
  220. transQty: '',
  221. itemNo: nextItemNo,
  222. batchNo: row.partNo+'-'+row.lineNo+'-'+row.releaseNo+'-'+nextItemNo,
  223. deliveryDate: row.plannedDeliveryDate || '',
  224. arrivalDate: this.getCurrentDate(),
  225. supplierBatchNo: '',
  226. };
  227. this.processFlag = 2;
  228. },
  229. // 获取当前日期
  230. getCurrentDate() {
  231. const now = new Date();
  232. return now.getFullYear() + '-' +
  233. String(now.getMonth() + 1).padStart(2, '0') + '-' +
  234. String(now.getDate()).padStart(2, '0') + ' ' +
  235. String(now.getHours()).padStart(2, '0') + ':' +
  236. String(now.getMinutes()).padStart(2, '0') + ':' +
  237. String(now.getSeconds()).padStart(2, '0');
  238. },
  239. handlingUnitStep() {
  240. this.processFlag = 3;
  241. const saved = localStorage.getItem(this.huKey);
  242. this.handlingUnit = saved ? JSON.parse(saved) : [];
  243. },
  244. createHandlingUnit() {
  245. const { perQty, packageQty } = this.hanlingItem;
  246. if (!perQty || !packageQty || isNaN(perQty) || isNaN(packageQty)) {
  247. return this.$message.warning("请填写有效的包装信息");
  248. }
  249. const qty = parseFloat(perQty) * parseInt(packageQty);
  250. const code = 'H' + String(this.handlingUnit.length + 1).padStart(3, '0');
  251. const newItem = { ...this.hanlingItem, qty, code };
  252. this.handlingUnit.push(newItem);
  253. localStorage.setItem(this.huKey, JSON.stringify(this.handlingUnit));
  254. },
  255. removeItem(index) {
  256. this.handlingUnit.splice(index, 1);
  257. localStorage.setItem(this.huKey, JSON.stringify(this.handlingUnit));
  258. },
  259. // 清除所有handlingUnit缓存
  260. clearAllHandlingUnitCache() {
  261. this.hanlingItem = { code: '', qty: '', perQty: '', packageQty: '' };
  262. const keys = Object.keys(localStorage);
  263. keys.forEach(key => {
  264. if (key.startsWith('hu_')) {
  265. localStorage.removeItem(key);
  266. }
  267. });
  268. },
  269. receivePo() {
  270. if (this.fullscreenLoading) return; // 防止重复点
  271. this.fullscreenLoading = true;
  272. const item = this.recvItem;
  273. if (!item.transQty || !item.locationNo || !item.batchNo) {
  274. this.fullscreenLoading = false;
  275. return this.$message.error("请填写完整信息");
  276. }
  277. // 构建符合服务端TransDetailDto结构的数据
  278. const receiveData = {
  279. // 基本字段
  280. site: this.site,
  281. warehouseId: this.warehouseId,
  282. partNo: item.partNo,
  283. partDesc: item.description,
  284. transQty: item.transQty,
  285. batchNo: item.batchNo,
  286. locationNo: item.locationNo,
  287. itemNo: item.itemNo,
  288. wdr: item.wdr || '*',
  289. deliveryDate: item.deliveryDate,
  290. arrivalDate: item.arrivalDate,
  291. supplierBatchNo: item.supplierBatchNo,
  292. // PO相关字段
  293. poNo: item.poNumber || item.poNo,
  294. orderNo: item.orderNo,
  295. lineNo: item.lineNo,
  296. releaseNo: item.releaseNo,
  297. receiptNo: item.receiptNo,
  298. orderRef1: item.orderNo,
  299. supplierNo: item.supplierNo,
  300. purchaseUOM: item.purchaseUOM,
  301. receiveCase: item.receiveCase,
  302. receiveCaseDB: item.receiveCaseDB,
  303. inventoryPartDB: item.inventoryPartDB,
  304. // 业务控制字段
  305. needHandlingUnit: item.needHandlingUnit ,
  306. needCheck: item.needCheck ,
  307. warehouseType: item.warehouseType ,
  308. // 日期字段
  309. manufactureDate: item.manufactureDate,
  310. // 处理单元列表
  311. handlingUnitList: item.needHandlingUnit === 'Y' ? this.handlingUnit.map(hu => ({
  312. perQty: hu.perQty,
  313. packageQty: hu.packageQty
  314. })) : []
  315. };
  316. receivePo(receiveData).then(({ data }) => {
  317. if (data.code === 0) {
  318. this.$message.success("操作成功");
  319. this.clearAllHandlingUnitCache();
  320. this.printViaServer(data.data,item.needCheck); // 调用打印
  321. this.processFlag = 1;
  322. this.scanCode = '';
  323. this.poList = [];
  324. this.recvItem = {};
  325. this.handlingUnit = [];
  326. } else {
  327. this.$message.error(data.msg || '操作失败');
  328. }
  329. }).catch(error => {
  330. console.error('接收失败:', error);
  331. this.$message.error('网络错误,请重试');
  332. }).finally(() => {
  333. this.fullscreenLoading = false; // 结束后关闭loading
  334. });
  335. },
  336. /**
  337. * 通过服务器打印
  338. */
  339. async printViaServer(receiptNo,needCheck) {
  340. this.$emit('print-start')
  341. try {
  342. const printRequest = {
  343. reportId: this.reportId,
  344. zplCode: this.zplCode,
  345. paperSize: this.paperSize,
  346. orientation: this.orientation,
  347. dpi: this.dpi,
  348. userId: localStorage.getItem('userName'),
  349. username: localStorage.getItem('userName'),
  350. site: localStorage.getItem('site'),
  351. receiptNo: receiptNo,
  352. needCheck:needCheck
  353. }
  354. const { data } = await printLabel(printRequest)
  355. if (data.code === 200) {
  356. this.$message.success(`打印任务已发送!`)
  357. }
  358. } catch (error) {
  359. console.error('服务器打印失败:', error)
  360. this.$message.error(`打印失败: ${error.message || error}`)
  361. }
  362. }
  363. },
  364. mounted() {
  365. this.$nextTick(() => this.$refs.scanCodeRef.focus());
  366. }
  367. };
  368. </script>
  369. <style scoped>
  370. .mt10 {
  371. margin-top: 10px;
  372. }
  373. .scan-box input {
  374. width: 100%;
  375. padding: 12px;
  376. font-size: 16px;
  377. }
  378. .item-list {
  379. flex: 1;
  380. overflow-y: auto;
  381. margin: 10px 0;
  382. border: 1px solid rgba(200, 200, 200, 0.8);
  383. }
  384. .item-list span {
  385. color: #000;
  386. font-size: 15px;
  387. }
  388. .bottom-line-row {
  389. border-bottom: 1px solid rgba(200, 200, 200, 0.8);
  390. }
  391. .recvButton {
  392. font-size: 16px;
  393. border-radius: 3px;
  394. color: #17b3a3;
  395. }
  396. .item-list .el-row {
  397. cursor: pointer;
  398. transition: background 0.3s;
  399. }
  400. .item-list .el-row:hover {
  401. background: #f5f7fa;
  402. }
  403. .disabled-button {
  404. color: #ccc !important;
  405. cursor: not-allowed !important;
  406. }
  407. .form-section >>> .el-col {
  408. margin-bottom: 12px;
  409. }
  410. .status-bar {
  411. display: flex;
  412. justify-content: space-between;
  413. align-items: center;
  414. background: #17b3a3;
  415. color: white;
  416. }
  417. </style>