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.

722 lines
27 KiB

5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
2 months ago
5 months ago
4 months ago
3 months ago
5 months ago
4 months ago
5 months ago
4 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
4 months ago
5 months ago
5 months ago
3 weeks ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
4 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
4 months ago
3 months ago
5 months ago
3 weeks ago
5 months ago
3 weeks ago
5 months ago
3 weeks ago
5 months ago
5 months ago
4 months ago
3 months ago
5 months ago
3 months ago
5 months ago
5 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
5 months ago
5 months ago
5 months ago
4 months ago
4 months ago
5 months ago
4 months ago
4 months ago
5 months ago
4 months ago
4 months ago
5 months ago
4 months ago
4 months ago
4 months ago
5 months ago
4 months ago
4 months ago
4 months ago
4 months ago
4 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 months ago
5 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="扫描或输入工单号"
  17. @keyup.enter.native="searchShopOrder" ref="scanCodeRef" />
  18. </div>
  19. <div class="item-list" v-if="shopOrderList.length > 0" style="margin: 2px;">
  20. <el-form label-position="top" style="margin: 3px;">
  21. <el-row :gutter="5"
  22. v-for="(orderDetail, index) in shopOrderList" :key="index" :class="index < shopOrderList.length - 1 ? 'bottom-line-row' : ''">
  23. <el-col :span="8">
  24. <el-form-item label="料号"><span>{{ orderDetail.partNo }}</span></el-form-item>
  25. </el-col>
  26. <el-col :span="8">
  27. <el-form-item label="行号/下达号"><span>{{ orderDetail.releaseNo }}/{{ orderDetail.sequenceNo }}</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="selectShopOrder(orderDetail)"
  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>{{ orderDetail.partDesc }}</span></el-form-item>
  37. </el-col>
  38. <el-col :span="6" :class="{ mt10: getTextWidth(orderDetail.partDesc) > 34 }">
  39. <el-form-item label="订单数量"><span>{{ orderDetail.lotSize }}</span></el-form-item>
  40. </el-col>
  41. <el-col :span="6" :class="{ mt10: getTextWidth(orderDetail.partDesc) > 34 }">
  42. <el-form-item label="已完成数"><span>{{ orderDetail.qtyComplete }}</span></el-form-item>
  43. </el-col>
  44. <el-col :span="6" :class="{ mt10: getTextWidth(orderDetail.partDesc) > 34 }">
  45. <el-form-item label="状态">
  46. <el-tag :type="orderDetail.objstate === 'Started' ? 'success' : 'warning'" size="mini">
  47. {{ orderDetail.objstate }}
  48. </el-tag>
  49. </el-form-item>
  50. </el-col>
  51. <el-col :span="6" :class="{ mt10: getTextWidth(orderDetail.partDesc) > 34 }">
  52. <el-form-item style="margin-left: 20px" label="单位"><span>{{ orderDetail.uom }}</span></el-form-item>
  53. </el-col>
  54. </el-row>
  55. </el-form>
  56. </div>
  57. </div>
  58. <!-- Step 2: 入库明细 -->
  59. <div v-if="processFlag === 2">
  60. <el-form label-position="top" class="form-section" style="margin: 5px;">
  61. <el-row :gutter="20">
  62. <el-col :span="12"><el-form-item label="工单号"><el-input v-model="inboundItem.orderNo" disabled /></el-form-item></el-col>
  63. <el-col :span="12"><el-form-item label="下达号/序列号"><el-input v-model="displayReleaseSeq" disabled /></el-form-item></el-col>
  64. <el-col :span="12"><el-form-item label="料号"><el-input v-model="inboundItem.partNo" disabled /></el-form-item></el-col>
  65. <el-col :span="12"><el-form-item label="计量单位"><el-input v-model="inboundItem.uom" disabled /></el-form-item></el-col>
  66. <el-col :span="24"><el-form-item label="物料描述"><el-input v-model="inboundItem.partDesc" disabled /></el-form-item></el-col>
  67. <el-col :span="12"><el-form-item label="订单数量"><el-input v-model="inboundItem.lotSize" disabled /></el-form-item></el-col>
  68. <el-col :span="12"><el-form-item label="已完成数"><el-input v-model="inboundItem.qtyComplete" disabled /></el-form-item></el-col>
  69. <el-col :span="12">
  70. <el-form-item label="此次入库数量">
  71. <el-input v-model="inboundItem.transQty" type="number" @blur="checkQuantity" />
  72. </el-form-item>
  73. </el-col>
  74. <el-col :span="12">
  75. <el-form-item label=" ">
  76. <el-button type="text" @click.stop="handlingUnitStep"
  77. style="font-size: 16px;margin-top: -5px" size="small">包装记录</el-button>
  78. </el-form-item>
  79. </el-col>
  80. <el-col :span="12"><el-form-item label="库位">
  81. <el-input v-model="inboundItem.locationNo" placeholder="请输入库位" />
  82. </el-form-item></el-col>
  83. <el-col :span="12"><el-form-item label="批号">
  84. <el-input v-model="inboundItem.batchNo" placeholder="请输入批号" />
  85. </el-form-item></el-col>
  86. <el-col :span="12"><el-form-item label="WDR">
  87. <el-input v-model="inboundItem.wdr" disabled placeholder="请输入WDR" />
  88. </el-form-item></el-col>
  89. <el-col :span="12"><el-form-item label="高度(mm)">
  90. <el-input v-model="inboundItem.height" type="number" placeholder="请输入高度" />
  91. </el-form-item></el-col>
  92. <el-col :span="12"><el-form-item label="有效期">
  93. <el-date-picker
  94. v-model="inboundItem.ifsExpiryDate"
  95. type="date"
  96. placeholder="选择有效期"
  97. value-format="yyyy-MM-dd"
  98. style="width: 100%"
  99. />
  100. </el-form-item></el-col>
  101. <!-- 勾选框 -->
  102. <el-col :span="12" style="margin-top: 2px">
  103. <el-form-item>
  104. <el-checkbox v-model="inboundItem.ifsAutoReport">Auto Report of Operation</el-checkbox>
  105. </el-form-item>
  106. </el-col>
  107. <el-col :span="12" style="margin-top: 2px">
  108. <el-form-item>
  109. <el-checkbox v-model="inboundItem.ifsSimplifiedMaterial">Simplified Material Check</el-checkbox>
  110. </el-form-item>
  111. </el-col>
  112. </el-row>
  113. <el-row :gutter="20">
  114. <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>
  115. <el-col :span="8" style="margin-top: 10px"><el-form-item><el-button type="text" style="font-size: 16px;margin-left: 20px" @click="confirmInbound">确认</el-button></el-form-item></el-col>
  116. <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>
  117. </el-row>
  118. </el-form>
  119. </div>
  120. <!-- Step 3: 包装记录 -->
  121. <div v-if="processFlag === 3">
  122. <el-form label-position="top" class="form-section" style="margin: 5px;">
  123. <el-row :gutter="20">
  124. <el-col :span="12"><el-form-item label="料号"><el-input v-model="inboundItem.partNo" disabled /></el-form-item></el-col>
  125. <el-col :span="12"><el-form-item label="未完成数量"><el-input v-model="inboundItem.unCompleteQty" disabled /></el-form-item></el-col>
  126. <el-col :span="24"><el-form-item label="物料描述"><el-input v-model="inboundItem.partDesc" disabled /></el-form-item></el-col>
  127. <!-- 自动计算开关 -->
  128. <el-col :span="24" style="margin-bottom: 10px;">
  129. <el-form-item>
  130. <el-switch
  131. v-model="autoCalculate"
  132. active-text="自动计算模式"
  133. inactive-text="手动输入模式"
  134. active-color="#17B3A3"
  135. style="display: block;"
  136. >
  137. </el-switch>
  138. <span v-if="autoCalculate" style="font-size: 13px; color: #17B3A3; margin-top: 5px; display: block; font-weight: bold;">
  139. 💡 输入单包装数量后按回车键或点击确定按钮
  140. </span>
  141. <span v-else style="font-size: 12px; color: #999; margin-top: 5px; display: block;">
  142. 手动输入单包装数量和包装数然后点击"创建"按钮
  143. </span>
  144. </el-form-item>
  145. </el-col>
  146. <el-col :span="8" style="margin-top: 5px"><el-form-item label="单包装数量">
  147. <el-input
  148. v-model="hanlingItem.perQty"
  149. type="number"
  150. :placeholder="autoCalculate ? '输入完按回车或失去焦点' : '请输入单包装数量'"
  151. @blur="autoCalculate && autoCreateHandlingUnits()"
  152. @keyup.enter.native="autoCalculate && autoCreateHandlingUnits()"
  153. />
  154. </el-form-item></el-col>
  155. <el-col :span="8" style="margin-top: 5px" v-if="!autoCalculate"><el-form-item label="包装数">
  156. <el-input v-model="hanlingItem.packageQty" type="number" placeholder="请输入包装数" />
  157. </el-form-item></el-col>
  158. <el-col :span="8" v-if="!autoCalculate" style="margin-top: 16px"><el-form-item>
  159. <el-button type="text" @click="createHandlingUnit" style="font-size: 16px">创建</el-button></el-form-item></el-col>
  160. <el-col :span="8" v-else style="margin-top: 16px"><el-form-item>
  161. <el-button type="text" style="font-size: 16px">确定</el-button></el-form-item></el-col>
  162. <el-table :data="handlingUnit" :row-style="{ height: '30px' }" style="width: 94%; margin-left: 10px;" highlight-current-row>
  163. <el-table-column prop="code" label="序号" />
  164. <el-table-column prop="packageQty" label="包装数" width="80" />
  165. <el-table-column prop="perQty" label="单包装数量" width="100" />
  166. <el-table-column label="操作" v-if="!autoCalculate">
  167. <template slot-scope="scope">
  168. <a @click="removeItem(scope.$index)">删除</a>
  169. </template>
  170. </el-table-column>
  171. </el-table>
  172. <el-col :span="24" v-if="autoCalculate && handlingUnit.length > 0" style="margin-top: 10px;">
  173. <span style="font-size: 12px; color: #999;">
  174. 💡 如需修改请重新输入单包装数量
  175. </span>
  176. </el-col>
  177. <el-col :span="24"><el-form-item><span>合计</span><span style="margin-left: 38%">{{ totalQty }}</span></el-form-item></el-col>
  178. <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>
  179. <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>
  180. </el-row>
  181. </el-form>
  182. </div>
  183. </div>
  184. </div>
  185. </div>
  186. </template>
  187. <script>
  188. import { getShopOrderFromIfs, getNextSequenceNo, submitShopOrderInbound, validateMaterialIssued, printLabelCommon } from "@/api/production/production-inbound.js";
  189. export default {
  190. data() {
  191. return {
  192. processFlag: 1,
  193. scanCode: '',
  194. shopOrderList: [],
  195. inboundItem: {},
  196. handlingUnit: [],
  197. hanlingItem: { code: '', qty: '', perQty: '', packageQty: '' },
  198. site: localStorage.getItem('site'),
  199. warehouseId: localStorage.getItem('selectedWarehouse'),
  200. fullscreenLoading: false,
  201. loadingText: '加载中...',
  202. autoCalculate: false
  203. };
  204. },
  205. computed: {
  206. totalQty() {
  207. // 使用整数运算避免浮点数精度问题
  208. const sumInCents = this.handlingUnit.reduce((sum, item) => {
  209. return sum + Math.round(Number(item.qty) * 100);
  210. }, 0);
  211. const sum = sumInCents / 100;
  212. this.inboundItem.transQty = sum;
  213. return sum;
  214. },
  215. huKey() {
  216. return `hu_shop_${this.inboundItem.orderNo}_${this.inboundItem.releaseNo}`;
  217. },
  218. displayReleaseSeq() {
  219. return `${this.inboundItem.releaseNo || ''}/${this.inboundItem.sequenceNo || ''}`;
  220. }
  221. },
  222. watch: {
  223. autoCalculate(newVal, oldVal) {
  224. this.hanlingItem.perQty = '';
  225. this.hanlingItem.packageQty = '';
  226. this.handlingUnit = [];
  227. localStorage.setItem(this.huKey, JSON.stringify(this.handlingUnit));
  228. if (newVal) {
  229. this.$message.success('已切换到自动创建HU模式');
  230. } else {
  231. this.$message.success('已切换到手动输入模式');
  232. }
  233. }
  234. },
  235. methods: {
  236. // 计算"显示宽度"
  237. getTextWidth(text) {
  238. if (!text) return 0
  239. let len = 0
  240. for (let char of text) {
  241. if (/[\u4e00-\u9fa5\u3000-\u303F\uFF00-\uFFEF]/.test(char)) {
  242. len += 2
  243. } else {
  244. len += 1
  245. }
  246. }
  247. return len
  248. },
  249. handleBack() {
  250. if (this.processFlag === 1) this.$router.back();
  251. else if (this.processFlag === 3) this.processFlag = 2;
  252. else this.processFlag = 1;
  253. },
  254. // 搜索工单
  255. searchShopOrder() {
  256. if (!this.scanCode) return this.shopOrderList = [];
  257. this.loadingText = '搜索中...';
  258. this.fullscreenLoading = true;
  259. // 解析工单号:支持 "3038851" 或 "3038851-1-2" 格式
  260. let orderNo = this.scanCode.trim();
  261. let releaseNo = "*";
  262. let sequenceNo = "*";
  263. // 如果包含"-",则按照 orderNo-releaseNo-sequenceNo 格式解析
  264. const parts = orderNo.split('-');
  265. if (parts.length >= 2) {
  266. orderNo = parts[0];
  267. releaseNo = parts[1];
  268. if (parts.length >= 3) {
  269. sequenceNo = parts[2];
  270. }
  271. }
  272. const requestData = {
  273. ifsDBName: "IFST",
  274. domainUserID: "CCL_WMS",
  275. ifsSiteID: this.site,
  276. ifsOrderNo: orderNo,
  277. ifsReleaseNo: releaseNo,
  278. ifsSequenceNo: sequenceNo
  279. };
  280. console.log('工单查询参数:', requestData);
  281. getShopOrderFromIfs(requestData).then(({ data }) => {
  282. if (data.code === 0) {
  283. this.shopOrderList = data.data || []
  284. if (this.shopOrderList.length === 0) {
  285. this.$message.warning('未找到匹配的工单');
  286. }
  287. } else {
  288. this.$message.error(data.msg || '查询失败');
  289. this.shopOrderList = []
  290. }
  291. this.$nextTick(() => {
  292. if (this.$refs.scanCodeRef) {
  293. this.$refs.scanCodeRef.blur();
  294. }
  295. });
  296. }).catch(error => {
  297. console.error('搜索失败:', error);
  298. this.$message.error('搜索失败,请重试');
  299. this.shopOrderList = []
  300. }).finally(() => {
  301. this.fullscreenLoading = false;
  302. });
  303. },
  304. // 选择工单
  305. async selectShopOrder(row) {
  306. // 校验状态
  307. if (row.objstate !== 'Started') {
  308. return this.$message.warning(`工单状态为 ${row.objstate},只有Started状态才能入库`);
  309. }
  310. this.fullscreenLoading = true;
  311. this.loadingText = '检查物料发料状态...';
  312. try {
  313. // 1. 检查物料是否已发料
  314. const validateResult = await validateMaterialIssued({
  315. site: this.site,
  316. orderNo: row.orderNo,
  317. releaseNo: row.releaseNo || '*',
  318. sequenceNo: row.sequenceNo || '*'
  319. });
  320. if (validateResult.data.code !== 0) {
  321. this.$alert(validateResult.data.msg || '物料发料检查失败', '操作提示', {
  322. confirmButtonText: '确定',
  323. type: 'warning'
  324. });
  325. return;
  326. }
  327. this.loadingText = '加载中...';
  328. // 2. 获取下一个序列号
  329. let nextSequence = 1;
  330. try {
  331. const { data } = await getNextSequenceNo({
  332. orderNo: row.orderNo,
  333. releaseNo: row.releaseNo || '*',
  334. sequenceNo: row.sequenceNo || '*'
  335. });
  336. if (data.code === 0) {
  337. nextSequence = data.data;
  338. }
  339. } catch (error) {
  340. console.error('获取序列号失败:', error);
  341. }
  342. this.inboundItem = {
  343. ...row,
  344. transQty: '',
  345. unCompleteQty: parseFloat(row.lotSize) - parseFloat(row.qtyComplete),
  346. sequenceNoNew: nextSequence,
  347. batchNo: `${row.orderNo}-${row.releaseNo}-${row.sequenceNo}-${nextSequence}`,
  348. locationNo: '',
  349. serialNo: '*',
  350. wdr: '*',
  351. ifsAutoReport: true, // Auto Report of Operation
  352. ifsSimplifiedMaterial: true, // Simplified Material Check默认选中
  353. height: this.parseHeightFromPartNo(row.partNo), // 根据物料编码自动填充高度
  354. ifsExpiryDate: '' // 有效期,传给IFS接口
  355. };
  356. this.processFlag = 2;
  357. } catch (error) {
  358. console.error('操作失败:', error);
  359. this.$message.error(error.message || '操作失败');
  360. } finally {
  361. this.fullscreenLoading = false;
  362. }
  363. },
  364. // 检查数量
  365. checkQuantity() {
  366. const transQty = parseFloat(this.inboundItem.transQty) || 0;
  367. const qtyComplete = parseFloat(this.inboundItem.qtyComplete) || 0;
  368. const lotSize = parseFloat(this.inboundItem.lotSize) || 0;
  369. if (qtyComplete + transQty > lotSize) {
  370. this.$confirm(
  371. `当前已完成数量(${qtyComplete}) + 本次入库数量(${transQty}) = ${qtyComplete + transQty},超过订单数量(${lotSize}),是否继续?`,
  372. '数量超出提示',
  373. {
  374. confirmButtonText: '继续',
  375. cancelButtonText: '取消',
  376. type: 'warning'
  377. }
  378. ).catch(() => {
  379. this.inboundItem.transQty = '';
  380. });
  381. }
  382. },
  383. handlingUnitStep() {
  384. this.processFlag = 3;
  385. this.autoCalculate = false; // 默认使用自动计算模式
  386. const saved = localStorage.getItem(this.huKey);
  387. this.handlingUnit = saved ? JSON.parse(saved) : [];
  388. },
  389. /**
  390. * 自动创建HU列表自动模式
  391. */
  392. autoCreateHandlingUnits() {
  393. const perQty = this.hanlingItem.perQty;
  394. if (!perQty || isNaN(perQty) || parseFloat(perQty) <= 0) {
  395. this.$message.warning('请输入有效的单包装数量');
  396. return;
  397. }
  398. const qtyToReceive = parseFloat(this.inboundItem.transQty) || 0;
  399. if (qtyToReceive <= 0) {
  400. this.$message.warning('请先输入入库数量');
  401. return;
  402. }
  403. const perQtyValue = parseFloat(perQty);
  404. const packageQty = Math.floor(qtyToReceive / perQtyValue);
  405. // 使用整数运算避免浮点数精度问题
  406. const remainderInCents = Math.round(qtyToReceive * 100) - Math.round(perQtyValue * 100) * packageQty;
  407. const remainder = remainderInCents / 100;
  408. this.handlingUnit = [];
  409. if (packageQty > 0) {
  410. for (let i = 1; i <= packageQty; i++) {
  411. const qty = perQtyValue;
  412. const code = String(i);
  413. this.handlingUnit.push({
  414. code: code,
  415. perQty: perQtyValue,
  416. packageQty: 1,
  417. qty: qty
  418. });
  419. }
  420. }
  421. if (remainder > 0) {
  422. const code = String(this.handlingUnit.length + 1);
  423. this.handlingUnit.push({
  424. code: code,
  425. perQty: remainder,
  426. packageQty: 1,
  427. qty: remainder
  428. });
  429. }
  430. localStorage.setItem(this.huKey, JSON.stringify(this.handlingUnit));
  431. this.hanlingItem.perQty = '';
  432. this.hanlingItem.packageQty = '';
  433. this.$message.success(`自动创建了${this.handlingUnit.length}个HU,合计数量:${qtyToReceive}`);
  434. },
  435. /**
  436. * 手动创建HU手动模式
  437. */
  438. createHandlingUnit() {
  439. const { perQty, packageQty } = this.hanlingItem;
  440. if (!perQty || !packageQty || isNaN(perQty) || isNaN(packageQty)) {
  441. return this.$message.warning("请填写有效的包装信息");
  442. }
  443. // 使用整数运算避免浮点数精度问题
  444. const qty = Math.round(parseFloat(perQty) * 100) * parseInt(packageQty) / 100;
  445. const code = String(this.handlingUnit.length + 1);
  446. const newItem = { ...this.hanlingItem, qty, code };
  447. this.handlingUnit.push(newItem);
  448. localStorage.setItem(this.huKey, JSON.stringify(this.handlingUnit));
  449. this.hanlingItem.perQty = '';
  450. this.hanlingItem.packageQty = '';
  451. },
  452. removeItem(index) {
  453. this.handlingUnit.splice(index, 1);
  454. localStorage.setItem(this.huKey, JSON.stringify(this.handlingUnit));
  455. },
  456. clearAllHandlingUnitCache() {
  457. this.hanlingItem = { code: '', qty: '', perQty: '', packageQty: '' };
  458. const keys = Object.keys(localStorage);
  459. keys.forEach(key => {
  460. if (key.startsWith('hu_shop_')) {
  461. localStorage.removeItem(key);
  462. }
  463. });
  464. },
  465. // 确认入库
  466. async confirmInbound() {
  467. if (this.fullscreenLoading) return;
  468. const item = this.inboundItem;
  469. if (!item.transQty || !item.locationNo || !item.batchNo) {
  470. return this.$message.error("请填写完整信息");
  471. }
  472. // 检查数量是否超出
  473. const transQty = parseFloat(item.transQty) || 0;
  474. const qtyComplete = parseFloat(item.qtyComplete) || 0;
  475. const lotSize = parseFloat(item.lotSize) || 0;
  476. if (qtyComplete + transQty > lotSize) {
  477. try {
  478. await this.$confirm(
  479. `当前已完成数量(${qtyComplete}) + 本次入库数量(${transQty}) = ${qtyComplete + transQty},超过订单数量(${lotSize}),是否继续?`,
  480. '数量超出提示',
  481. {
  482. confirmButtonText: '继续',
  483. cancelButtonText: '取消',
  484. type: 'warning'
  485. }
  486. );
  487. } catch (error) {
  488. // 用户取消
  489. return;
  490. }
  491. }
  492. this.loadingText = '提交中...';
  493. this.fullscreenLoading = true;
  494. // 构建提交数据
  495. const submitData = {
  496. // 基本字段
  497. site: this.site,
  498. warehouseId: this.warehouseId,
  499. orderNo: item.orderNo,
  500. releaseNo: item.releaseNo,
  501. sequenceNo: item.sequenceNo,
  502. partNo: item.partNo,
  503. partDesc: item.partDesc,
  504. transQty: item.transQty,
  505. batchNo: item.batchNo,
  506. locationNo: item.locationNo,
  507. serialNo: item.serialNo || '*',
  508. engChgLevel: item.engChgLevel || '1',
  509. uom: item.uom,
  510. lotSize: item.lotSize,
  511. qtyComplete: item.qtyComplete,
  512. height: item.height || null, // 高度(mm)
  513. ifsExpiryDate: item.ifsExpiryDate || null, // 有效期
  514. // IFS参数
  515. ifsAutoReport: item.ifsAutoReport ? 'Yes' : 'No',
  516. ifsSimplifiedMaterial: item.ifsSimplifiedMaterial ? 'Yes' : 'No',
  517. ifsAutoClose:item.close_code_db==='Y'?'TRUE':'FALSE',
  518. // 处理单元列表
  519. handlingUnitList: this.handlingUnit.map(hu => ({
  520. perQty: hu.perQty,
  521. packageQty: hu.packageQty
  522. }))
  523. };
  524. submitShopOrderInbound(submitData).then(({ data }) => {
  525. if (data.code === 0) {
  526. this.$message.success("入库成功");
  527. this.clearAllHandlingUnitCache();
  528. // 获取unitIds并打印标签
  529. const unitIds = data.unitIds || [];
  530. if (unitIds.length > 0) {
  531. let printLabelType;
  532. if (this.inboundItem.partNo && this.inboundItem.partNo.startsWith("80")) {
  533. printLabelType = '库存成品标签';
  534. } else {
  535. printLabelType = 'BIL标签';
  536. }
  537. this.printViaServer(unitIds,printLabelType);
  538. }
  539. this.processFlag = 1;
  540. this.scanCode = '';
  541. this.shopOrderList = [];
  542. this.inboundItem = {};
  543. this.handlingUnit = [];
  544. } else {
  545. this.$message.error(data.msg || '入库失败');
  546. }
  547. }).catch(error => {
  548. console.error('入库失败:', error);
  549. this.$message.error('网络错误,请重试');
  550. }).finally(() => {
  551. this.fullscreenLoading = false;
  552. });
  553. },
  554. /**
  555. * 通过服务器打印
  556. * @param {Array} unitIds - HU unitId列表
  557. * @param printLabelType
  558. */
  559. async printViaServer(unitIds, printLabelType) {
  560. if (!unitIds || unitIds.length === 0) {
  561. console.warn('没有可打印的标签');
  562. return;
  563. }
  564. this.$emit('print-start')
  565. try {
  566. const printRequest = {
  567. userId: localStorage.getItem('userName'),
  568. username: localStorage.getItem('userName'),
  569. site: localStorage.getItem('site'),
  570. unitIds: unitIds,
  571. labelType: printLabelType
  572. }
  573. console.log('打印请求:', printRequest);
  574. const { data } = await printLabelCommon(printRequest)
  575. if (data.code === 200 || data.code === 0) {
  576. this.$message.success(`打印任务已发送!共${unitIds.length}个标签`)
  577. } else {
  578. this.$message.error(data.msg || '打印失败')
  579. }
  580. } catch (error) {
  581. console.error('服务器打印失败:', error)
  582. this.$message.error(`打印失败: ${error.message || error}`)
  583. }
  584. },
  585. /**
  586. * 从物料编码中解析高度
  587. * 如果物料编码格式是 70004479-0030则高度为30
  588. * 如果物料编码格式是 70004479则高度为空
  589. */
  590. parseHeightFromPartNo(partNo) {
  591. if (!partNo) return '';
  592. // 检查是否包含横杠
  593. const parts = partNo.split('-');
  594. if (parts.length >= 2) {
  595. // 取横杠后面的数字部分
  596. const heightStr = parts[1];
  597. // 转换为数字,去掉前导0
  598. const height = parseInt(heightStr, 10);
  599. return isNaN(height) ? '' : height;
  600. }
  601. return '';
  602. }
  603. },
  604. mounted() {
  605. this.$nextTick(() => this.$refs.scanCodeRef.focus());
  606. },
  607. };
  608. </script>
  609. <style scoped>
  610. .mt10 {
  611. margin-top: 10px;
  612. }
  613. .scan-box input {
  614. width: 100%;
  615. padding: 12px;
  616. font-size: 16px;
  617. }
  618. .item-list {
  619. flex: 1;
  620. overflow-y: auto;
  621. margin: 10px 0;
  622. border: 1px solid rgba(200, 200, 200, 0.8);
  623. }
  624. .item-list span {
  625. color: #000;
  626. font-size: 15px;
  627. }
  628. .bottom-line-row {
  629. border-bottom: 1px solid rgba(200, 200, 200, 0.8);
  630. }
  631. .recvButton {
  632. font-size: 16px;
  633. border-radius: 3px;
  634. color: #17b3a3;
  635. }
  636. .item-list .el-row {
  637. cursor: pointer;
  638. transition: background 0.3s;
  639. }
  640. .item-list .el-row:hover {
  641. background: #f5f7fa;
  642. }
  643. .disabled-button {
  644. color: #ccc !important;
  645. cursor: not-allowed !important;
  646. }
  647. .form-section >>> .el-col {
  648. margin-bottom: 2px;
  649. }
  650. .status-bar {
  651. display: flex;
  652. justify-content: space-between;
  653. align-items: center;
  654. background: #17b3a3;
  655. color: white;
  656. }
  657. /* 自定义loading样式 */
  658. .pda-container >>> .el-loading-mask {
  659. background-color: rgba(255, 255, 255, 0.3) !important;
  660. }
  661. .pda-container >>> .el-loading-spinner {
  662. margin-top: -25px;
  663. }
  664. .pda-container >>> .el-loading-spinner .circular {
  665. width: 35px;
  666. height: 35px;
  667. }
  668. .pda-container >>> .el-loading-text {
  669. color: #17b3a3 !important;
  670. font-size: 14px;
  671. font-weight: 500;
  672. margin-top: 10px;
  673. }
  674. </style>