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.

1105 lines
30 KiB

1 year ago
1 year ago
12 months ago
12 months ago
12 months ago
12 months ago
1 year ago
12 months ago
12 months ago
12 months ago
1 year ago
12 months ago
1 year ago
12 months ago
1 year ago
12 months ago
1 year ago
12 months ago
1 year ago
12 months ago
1 year ago
1 year ago
12 months ago
1 year ago
12 months ago
1 year ago
12 months ago
1 year ago
12 months ago
1 year ago
12 months ago
1 year ago
1 year ago
12 months ago
1 year ago
12 months ago
12 months ago
12 months ago
12 months ago
12 months ago
12 months ago
12 months ago
12 months ago
12 months ago
11 months ago
12 months ago
1 year ago
1 year ago
12 months ago
4 weeks ago
1 year ago
12 months ago
1 year ago
12 months ago
1 year ago
12 months ago
1 year ago
1 year ago
1 year ago
12 months ago
1 year ago
12 months ago
1 year ago
12 months ago
4 weeks ago
1 year ago
12 months ago
1 year ago
4 weeks ago
12 months ago
12 months ago
1 year ago
12 months ago
1 year ago
1 year ago
12 months ago
1 year ago
12 months ago
1 year ago
12 months ago
1 year ago
12 months ago
1 year ago
12 months ago
1 year ago
12 months ago
1 year ago
12 months ago
1 year ago
12 months ago
1 year ago
12 months ago
12 months ago
12 months ago
12 months ago
1 year ago
12 months ago
1 year ago
12 months ago
1 year ago
12 months ago
4 weeks ago
12 months ago
1 year ago
1 year ago
12 months ago
1 year ago
12 months ago
1 year ago
12 months ago
1 year ago
12 months ago
12 months ago
12 months ago
12 months ago
12 months ago
4 weeks ago
12 months ago
11 months ago
10 months ago
12 months ago
11 months ago
12 months ago
1 year ago
1 year ago
1 year ago
12 months ago
1 year ago
1 year ago
1 year ago
12 months ago
1 year ago
1 year ago
1 year ago
1 year ago
1 year ago
12 months ago
1 year ago
1 year ago
1 year ago
1 year ago
12 months ago
1 year ago
12 months ago
1 year ago
1 year ago
12 months ago
1 year ago
1 year ago
1 year ago
12 months ago
1 year ago
1 year ago
1 year ago
1 year ago
12 months ago
1 year ago
12 months ago
1 year ago
12 months ago
1 year ago
12 months ago
12 months ago
1 year ago
12 months ago
1 year ago
12 months ago
1 year ago
12 months ago
1 year ago
12 months ago
1 year ago
12 months ago
1 year ago
12 months ago
1 year ago
12 months ago
1 year ago
1 year ago
  1. <template>
  2. <div>
  3. <div class="pda-container">
  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. <!-- 全页面Loading -->
  10. <div v-if="loading || scanLoading" class="page-loading-overlay">
  11. <div class="page-loading-content">
  12. <i class="el-icon-loading page-loading-icon"></i>
  13. <div class="page-loading-text">
  14. {{ scanLoading ? '验证中...' : loadingText }}
  15. </div>
  16. </div>
  17. </div>
  18. <div style="overflow-y: auto" :class="{ 'content-disabled': loading || scanLoading }">
  19. <!-- Step 1: 扫描采购单号查询接收记录 -->
  20. <div v-if="processFlag === 1">
  21. <div class="scan-box" style="margin: 2px;">
  22. <el-input clearable v-model="scanCode" placeholder="扫描或输入采购单号"
  23. :editable="false"
  24. @keyup.enter.native="searchReceiptList" ref="scanCodeRef" />
  25. </div>
  26. <div class="item-list" v-if="receiptList.length > 0" style="margin: 2px;">
  27. <el-form label-position="top" style="margin: 3px;">
  28. <el-row :gutter="5" @click.native="selectReceiptItem(receiptDetail)"
  29. v-for="(receiptDetail, index) in receiptList" :key="index"
  30. :class="index < receiptList.length - 1 ? 'bottom-line-row' : ''">
  31. <el-col :span="8">
  32. <el-form-item label="物料编码"><span>{{ receiptDetail.sourcePartNo }}</span></el-form-item>
  33. </el-col>
  34. <el-col :span="8">
  35. <el-form-item label="接收单号"><span>{{ receiptDetail.receiptNo }}</span></el-form-item>
  36. </el-col>
  37. <el-col :span="8">
  38. <el-form-item label="">
  39. <el-button type="text" class="inboundButton" @click="selectReceiptItem(receiptDetail)"
  40. style="margin-top: 10px;margin-left: 20px" size="small">入库</el-button>
  41. </el-form-item>
  42. </el-col>
  43. <el-col :span="24">
  44. <el-form-item label="物料描述"><span>{{ receiptDetail.description }}</span></el-form-item>
  45. </el-col>
  46. <el-col :span="6">
  47. <el-form-item label="到货数量"><span style="color: #67C23A;">{{ receiptDetail.qtyArrived }}</span></el-form-item>
  48. </el-col>
  49. <el-col :span="6">
  50. <el-form-item label="供应商"><span>{{ receiptDetail.senderName }}</span></el-form-item>
  51. </el-col>
  52. <el-col :span="6">
  53. <el-form-item label="PO号"><span>{{ receiptDetail.sourceRef1 }}</span></el-form-item>
  54. </el-col>
  55. <el-col :span="6">
  56. <el-form-item label="到货日期"><span>{{ formatDate(receiptDetail.arrivalDate) }}</span></el-form-item>
  57. </el-col>
  58. </el-row>
  59. </el-form>
  60. </div>
  61. </div>
  62. <!-- Step 2: HandlingUnit扫描和入库确认 -->
  63. <div v-if="processFlag === 2">
  64. <!-- 接收记录信息显示 -->
  65. <div class="material-info-card">
  66. <div class="input-form">
  67. <div class="form-row">
  68. <div class="form-item">
  69. <label class="form-label">物料编码</label>
  70. <span >
  71. {{ selectedReceipt.sourcePartNo }}
  72. </span>
  73. </div>
  74. <div class="form-item">
  75. <label class="form-label">到货数量</label>
  76. <span v-model="selectedReceipt.qtyArrived">
  77. {{selectedReceipt.qtyArrived}}
  78. </span>
  79. </div>
  80. </div>
  81. <div class="form-row">
  82. <div class="form-item">
  83. <label class="form-label">物料描述</label>
  84. <span v-model="selectedReceipt.description" >
  85. {{selectedReceipt.description}}
  86. </span>
  87. </div>
  88. </div>
  89. </div>
  90. </div>
  91. <!-- 扫描区域 -->
  92. <div class="search-container">
  93. <el-input clearable class="compact-input"
  94. v-model="scanCode"
  95. placeholder="请扫描HandlingUnit条码"
  96. prefix-icon="el-icon-search"
  97. @keyup.enter.native="handleScan"
  98. ref="scanInput"
  99. />
  100. <div class="mode-switch">
  101. <el-switch
  102. class="custom-switch"
  103. v-model="isRemoveMode"
  104. active-color="#ff4949"
  105. inactive-color="#13ce66">
  106. </el-switch>
  107. <span v-if="isRemoveMode" class="switch-text">{{ '移除' }}</span>
  108. <span v-else class="switch-text2">{{ '添加' }}</span>
  109. </div>
  110. </div>
  111. <!-- 目标库位输入 -->
  112. <div class="material-info-card">
  113. <div class="input-form">
  114. <div class="form-row">
  115. <div class="form-item">
  116. <el-input
  117. v-model="targetLocationId"
  118. placeholder="请扫描或输入目标库位"
  119. size="small"
  120. @keyup.enter.native="handleLocationScan"
  121. ref="locationInput">
  122. </el-input>
  123. </div>
  124. </div>
  125. </div>
  126. </div>
  127. <!-- 扫描信息确认标题 -->
  128. <div class="section-title">
  129. <div class="title-left">
  130. <i class="el-icon-box"></i>
  131. <span>扫描信息确认</span>
  132. </div>
  133. </div>
  134. <!-- 扫描的HandlingUnit明细列表 -->
  135. <div class="scanned-items" v-if="scannedItems.length > 0" style="margin: 2px;">
  136. <div class="label-list">
  137. <el-form label-position="top" style="margin: 3px;">
  138. <el-row :gutter="5"
  139. v-for="(label, index) in scannedItems"
  140. :key="label.id"
  141. :class="index < scannedItems.length - 1 ? 'bottom-line-row' : ''"
  142. style="border: 1px solid #e0e0e0; border-radius: 4px; margin-bottom: 8px; padding: 8px;">
  143. <el-col :span="16">
  144. <el-form-item label="HandlingUnit">
  145. <span>{{ label.unitId }}</span>
  146. </el-form-item>
  147. </el-col>
  148. <el-col :span="8">
  149. <el-form-item label="物料编码">
  150. <span>{{ label.partNo }}</span>
  151. </el-form-item>
  152. </el-col>
  153. <el-col :span="24" v-if="label.partDesc" class="description-col">
  154. <el-form-item label="物料描述">
  155. <span class="part-description">{{ label.partDesc }}</span>
  156. </el-form-item>
  157. </el-col>
  158. <el-col :span="10" v-if="label.batchNo">
  159. <el-form-item label="批次号">
  160. <span>{{ label.batchNo }}</span>
  161. </el-form-item>
  162. </el-col>
  163. <el-col :span="10" v-if="label.locationId">
  164. <el-form-item label="当前库位">
  165. <span>{{ label.locationId }}</span>
  166. </el-form-item>
  167. </el-col>
  168. <el-col :span="4">
  169. <el-form-item label="数量">
  170. <span>{{ label.qty }} </span>
  171. </el-form-item>
  172. </el-col>
  173. </el-row>
  174. </el-form>
  175. </div>
  176. </div>
  177. <!-- 空状态 -->
  178. <div v-if="scannedItems.length === 0" class="empty-labels">
  179. <div style="text-align: center; padding: 20px;">
  180. <p style="color: #999; margin: 0;">暂无扫描HandlingUnit</p>
  181. </div>
  182. </div>
  183. <!-- 底部操作按钮 -->
  184. <div class="bottom-actions" v-if="scannedItems.length > 0 || targetLocationId">
  185. <button class="action-btn secondary" @click="processFlag = 1">
  186. 回退
  187. </button>
  188. <button class="action-btn primary" @click="confirmStorage">
  189. 确认入库
  190. </button>
  191. </div>
  192. </div>
  193. <!-- Step 3: 入库历史查询 -->
  194. <div v-if="processFlag === 3">
  195. <div class="filter-box" style="margin: 2px;">
  196. <el-button type="text" @click="searchInboundHistory" style="font-size: 16px;">查询入库历史</el-button>
  197. </div>
  198. <div class="item-list" v-if="historyList.length > 0" style="margin: 2px;">
  199. <el-form label-position="top" style="margin: 3px;">
  200. <el-row :gutter="5" v-for="(historyDetail, index) in historyList" :key="index"
  201. :class="index < historyList.length - 1 ? 'bottom-line-row' : ''">
  202. <el-col :span="8">
  203. <el-form-item label="物料编码"><span>{{ historyDetail.partNo }}</span></el-form-item>
  204. </el-col>
  205. <el-col :span="8">
  206. <el-form-item label="入库单号"><span>{{ historyDetail.transNo }}</span></el-form-item>
  207. </el-col>
  208. <el-col :span="8">
  209. <el-form-item label="入库状态">
  210. <span style="color: #67C23A;">已入库</span>
  211. </el-form-item>
  212. </el-col>
  213. <el-col :span="24">
  214. <el-form-item label="物料描述"><span>{{ historyDetail.partDesc }}</span></el-form-item>
  215. </el-col>
  216. <el-col :span="6">
  217. <el-form-item label="入库数量"><span>{{ historyDetail.inboundQty }}</span></el-form-item>
  218. </el-col>
  219. <el-col :span="6">
  220. <el-form-item label="批次号"><span>{{ historyDetail.batchNo }}</span></el-form-item>
  221. </el-col>
  222. <el-col :span="6">
  223. <el-form-item label="操作员"><span>{{ historyDetail.operatorName }}</span></el-form-item>
  224. </el-col>
  225. <el-col :span="6">
  226. <el-form-item label="入库日期"><span>{{ formatDate(historyDetail.inboundDate) }}</span></el-form-item>
  227. </el-col>
  228. </el-row>
  229. </el-form>
  230. </div>
  231. </div>
  232. </div>
  233. <!-- 底部导航 -->
  234. <!-- <div class="bottom-nav">
  235. <el-button :type="processFlag === 1 ? 'primary' : 'text'" @click="processFlag = 1">待入库</el-button>
  236. <el-button :type="processFlag === 3 ? 'primary' : 'text'" @click="processFlag = 3">入库历史</el-button>
  237. </div>-->
  238. </div>
  239. </div>
  240. </template>
  241. <script>
  242. import { getPurchaseOrderReceiptList, confirmQualifiedStorage, validateHandlingUnitForQualifiedStorage, getInboundHistory, validateLocationForPicking } from "@/api/po/po.js";
  243. import { filterRowsByAuthorizedSubSite, getAuthorizedSubSiteCodeList } from "@/utils/subSiteAuth.js";
  244. export default {
  245. data() {
  246. return {
  247. processFlag: 1, // 1-接收记录列表, 2-HandlingUnit扫描入库, 3-入库历史
  248. scanCode: '',
  249. receiptList: [],
  250. historyList: [],
  251. selectedReceipt: {},
  252. scannedItems: [],
  253. targetLocationId: '',
  254. isRemoveMode: false,
  255. loading: false,
  256. scanLoading: false, // 扫描HandlingUnit时的loading状态
  257. loadingText: '处理中...', // loading提示文本
  258. site: localStorage.getItem('site')
  259. };
  260. },
  261. methods: {
  262. handleBack() {
  263. if (this.processFlag === 1) {
  264. this.$router.back();
  265. } else if (this.processFlag === 2) {
  266. this.processFlag = 1;
  267. this.resetData();
  268. } else {
  269. this.processFlag = 1;
  270. }
  271. },
  272. // 查询采购订单接收记录
  273. searchReceiptList() {
  274. if (!this.scanCode.trim()) {
  275. return this.$message.error("请输入采购单号");
  276. }
  277. // 开始loading
  278. this.loadingText = '查询中...';
  279. this.loading = true;
  280. const params = {
  281. purchaseOrderNo: this.scanCode.trim(),
  282. site: this.site+'%'
  283. };
  284. getPurchaseOrderReceiptList(params).then(({ data }) => {
  285. if (data.code === 0) {
  286. const sourceRows = data.rows || [];
  287. const authorizedSubSiteCodeList = getAuthorizedSubSiteCodeList(this.$store.state.user.subSiteCodeList);
  288. // 接口按site前缀查询会返回同前缀下的所有子site,这里按当前用户授权子site做二次过滤
  289. this.receiptList = filterRowsByAuthorizedSubSite(sourceRows, authorizedSubSiteCodeList, ['contract']);
  290. if (this.receiptList.length === 0) {
  291. this.$message.success("暂无To be Received状态的接收记录");
  292. }
  293. } else {
  294. this.$message.error("查询失败");
  295. }
  296. // 查询完成后让输入框失去焦点
  297. this.$nextTick(() => {
  298. if (this.$refs.scanCodeRef) {
  299. this.$refs.scanCodeRef.blur();
  300. }
  301. });
  302. }).catch(error => {
  303. this.$message.error("查询失败");
  304. console.error(error);
  305. // 查询失败时也让输入框失去焦点
  306. this.$nextTick(() => {
  307. if (this.$refs.scanCodeRef) {
  308. this.$refs.scanCodeRef.blur();
  309. }
  310. });
  311. }).finally(() => {
  312. // 结束loading
  313. this.loading = false;
  314. });
  315. this.scanCode = '';
  316. },
  317. // 选择接收记录项目
  318. selectReceiptItem(item) {
  319. this.selectedReceipt = { ...item };
  320. this.processFlag = 2;
  321. this.$nextTick(() => {
  322. if (this.$refs.scanInput) {
  323. this.$refs.scanInput.focus();
  324. }
  325. });
  326. },
  327. // 计算可用数量
  328. getAvailableQty() {
  329. if (!this.selectedReceipt.qtyArrived) return 0;
  330. const arrived = parseFloat(this.selectedReceipt.qtyArrived) || 0;
  331. const scrapped = parseFloat(this.selectedReceipt.scrappedQty) || 0;
  332. const returned = parseFloat(this.selectedReceipt.returnedQty) || 0;
  333. return arrived - scrapped - returned;
  334. },
  335. // 处理扫描
  336. handleScan() {
  337. if (!this.scanCode.trim()) {
  338. return;
  339. }
  340. if (this.isRemoveMode) {
  341. this.removeLabelByCode(this.scanCode.trim());
  342. } else {
  343. this.validateAndAddLabel(this.scanCode.trim());
  344. }
  345. this.scanCode = '';
  346. },
  347. // 处理库位扫描
  348. handleLocationScan() {
  349. if (!this.targetLocationId.trim()) {
  350. return;
  351. }
  352. // 校验库位类型
  353. this.validateLocationForPickingType();
  354. },
  355. // 校验库位是否为PICKING类型
  356. async validateLocationForPickingType() {
  357. if (!this.targetLocationId.trim()) {
  358. return;
  359. }
  360. try {
  361. const { data } = await validateLocationForPicking({
  362. site: this.site,
  363. locationId: this.targetLocationId.trim()
  364. });
  365. if (data.code === 0) {
  366. // 校验通过,失去光标并定位到页面底部
  367. if (this.$refs.locationInput) {
  368. this.$refs.locationInput.blur();
  369. }
  370. this.$nextTick(() => {
  371. const bottomActions = document.querySelector('.bottom-actions');
  372. if (bottomActions) {
  373. bottomActions.scrollIntoView({
  374. behavior: 'smooth',
  375. block: 'end'
  376. });
  377. }
  378. });
  379. } else {
  380. this.$message.error(data.msg || '库位必须是PICKING类型');
  381. // 清空库位输入
  382. this.targetLocationId = '';
  383. // 重新聚焦到库位输入框
  384. this.$nextTick(() => {
  385. if (this.$refs.locationInput) {
  386. this.$refs.locationInput.focus();
  387. }
  388. });
  389. }
  390. } catch (error) {
  391. console.error('库位校验失败:', error);
  392. this.$message.error('库位校验失败,请重试');
  393. // 清空库位输入
  394. this.targetLocationId = '';
  395. // 重新聚焦到库位输入框
  396. this.$nextTick(() => {
  397. if (this.$refs.locationInput) {
  398. this.$refs.locationInput.focus();
  399. }
  400. });
  401. }
  402. },
  403. // 验证标签并添加到列表
  404. validateAndAddLabel(unitId) {
  405. // 检查是否已经扫描过
  406. const exists = this.scannedItems.find(item => item.unitId === unitId);
  407. if (exists) {
  408. this.$message.warning('该HandlingUnit已扫描,请勿重复扫描');
  409. return;
  410. }
  411. // 开始loading
  412. this.scanLoading = true;
  413. const params = {
  414. unitId: unitId,
  415. site: this.selectedReceipt.contract,
  416. expectedPartNo: this.selectedReceipt.sourcePartNo,
  417. expectedBatchNo: this.getBatchNoFromReceipt() // 从接收记录中获取期望的批次号
  418. };
  419. validateHandlingUnitForQualifiedStorage(params).then(({ data }) => {
  420. if (data && data.code === 0 && data.data) {
  421. const huInfo = data.data;
  422. this.processHandlingUnit(unitId, huInfo);
  423. } else {
  424. this.$message.error(data.msg || 'HandlingUnit校验失败');
  425. }
  426. }).catch(error => {
  427. this.$message.error('HandlingUnit校验失败');
  428. console.error(error);
  429. }).finally(() => {
  430. // 结束loading
  431. this.scanLoading = false;
  432. });
  433. },
  434. // 从接收记录中获取批次号(如果有的话)
  435. getBatchNoFromReceipt() {
  436. // 这里可能需要根据实际的接收记录结构来获取批次号
  437. // 如果接收记录中没有批次号信息,可以返回空字符串,让后端从HandlingUnit中获取
  438. return this.selectedReceipt.batchNo || '';
  439. },
  440. // 处理HandlingUnit数据
  441. processHandlingUnit(unitId, huInfo) {
  442. // 所有校验都在后端完成,这里直接添加到列表
  443. this.scannedItems.push({
  444. id: Date.now(),
  445. unitId: unitId,
  446. partNo: huInfo.partNo,
  447. partDesc: huInfo.partDesc,
  448. qty: huInfo.qty,
  449. unit: huInfo.unit,
  450. batchNo: huInfo.batchNo,
  451. locationId: huInfo.locationId,
  452. wdr: huInfo.wdr
  453. });
  454. this.$message.success('扫描成功');
  455. },
  456. // 通过条码移除标签
  457. removeLabelByCode(unitId) {
  458. const index = this.scannedItems.findIndex(item => item.unitId === unitId);
  459. if (index !== -1) {
  460. this.scannedItems.splice(index, 1);
  461. this.$message.success('移除成功');
  462. } else {
  463. this.$message.warning('未找到该HandlingUnit');
  464. }
  465. },
  466. // 确认入库
  467. async confirmStorage() {
  468. // 验证必填字段
  469. if (!this.targetLocationId) {
  470. this.$message.error('请输入目标库位');
  471. return;
  472. }
  473. if (this.scannedItems.length === 0) {
  474. this.$message.warning('请先扫描HandlingUnit');
  475. return;
  476. }
  477. // 提交前再次校验库位类型
  478. try {
  479. const { data } = await validateLocationForPicking({
  480. site: this.site,
  481. locationId: this.targetLocationId.trim()
  482. });
  483. if (data.code !== 0) {
  484. this.$message.error(data.msg || '库位必须是PICKING类型');
  485. return;
  486. }
  487. } catch (error) {
  488. console.error('库位校验失败:', error);
  489. this.$message.error('库位校验失败,请重试');
  490. return;
  491. }
  492. // 计算HandlingUnit总数量
  493. const totalScannedQty = this.scannedItems.reduce((sum, item) => sum + (parseFloat(item.qty) || 0), 0);
  494. const availableQty = this.getAvailableQty();
  495. if (totalScannedQty > availableQty) {
  496. this.$message.error('扫描的HandlingUnit总数量不能大于可用数量');
  497. return;
  498. }
  499. this.$confirm('确认要进行检验合格入库操作吗?', '提示', {
  500. confirmButtonText: '确定',
  501. cancelButtonText: '取消',
  502. type: 'warning'
  503. }).then(() => {
  504. this.submitConfirmStorage();
  505. }).catch(() => {
  506. // 用户取消
  507. });
  508. },
  509. // 提交确认入库
  510. submitConfirmStorage() {
  511. const handlingUnitIds = this.scannedItems.map(item => item.unitId);
  512. const totalQty = this.scannedItems.reduce((sum, item) => sum + (parseFloat(item.qty) || 0), 0);
  513. const params = {
  514. site: this.selectedReceipt.contract,
  515. fuSite: localStorage.getItem('site'),
  516. receiptSequence: this.selectedReceipt.receiptSequence,
  517. sourceRef1: this.selectedReceipt.sourceRef1,
  518. sourceRef2: this.selectedReceipt.sourceRef2,
  519. sourceRef3: this.selectedReceipt.sourceRef3,
  520. sourceRef4: this.selectedReceipt.sourceRef4,
  521. receiptNo: this.selectedReceipt.receiptNo,
  522. partNo: this.selectedReceipt.sourcePartNo,
  523. locationNo: '', // 后端会从HandlingUnit中获取
  524. lotBatchNo: '', // 后端会从HandlingUnit中获取
  525. wdr: '', // 后端会从HandlingUnit中获取
  526. engChgLevel: this.selectedReceipt.engChgLevel || '1',
  527. uom: this.selectedReceipt.uom || '',
  528. toLocationNo: this.targetLocationId,
  529. qtyToMove: totalQty,
  530. qtyArrived: this.selectedReceipt.qtyArrived,
  531. scrappedQty: this.selectedReceipt.scrappedQty || 0,
  532. returnedQty: this.selectedReceipt.returnedQty || 0,
  533. handlingUnitIds: handlingUnitIds
  534. };
  535. // 开始loading
  536. this.loadingText = '入库中...';
  537. this.loading = true;
  538. // 调用后端API
  539. confirmQualifiedStorage(params).then(({ data }) => {
  540. if (data && data.code === 0) {
  541. this.$message({
  542. message: '检验合格入库成功!处理单元: ' + handlingUnitIds.length + '个',
  543. type: 'success',
  544. duration: 3000
  545. });
  546. // 清空页面内容,回到列表页面
  547. this.resetData();
  548. this.processFlag = 1;
  549. } else {
  550. this.$message.error(data.msg || '检验合格入库失败');
  551. }
  552. }).catch(error => {
  553. console.error('检验合格入库失败:', error);
  554. this.$message.error('检验合格入库失败,请重试');
  555. }).finally(() => {
  556. // 结束loading
  557. this.loading = false;
  558. });
  559. },
  560. // 查询入库历史
  561. searchInboundHistory() {
  562. const params = {
  563. site: this.site
  564. };
  565. getInboundHistory(params).then(({ data }) => {
  566. if (data.code === 0) {
  567. this.historyList = data.rows || [];
  568. if (this.historyList.length === 0) {
  569. this.$message.info("暂无入库历史记录");
  570. }
  571. } else {
  572. this.$message.error(data.msg || "查询失败");
  573. }
  574. }).catch(error => {
  575. this.$message.error("查询失败");
  576. console.error(error);
  577. });
  578. },
  579. // 重置数据
  580. resetData() {
  581. this.selectedReceipt = {};
  582. this.scannedItems = [];
  583. this.targetLocationId = '';
  584. this.isRemoveMode = false;
  585. this.scanCode = '';
  586. },
  587. // 格式化日期
  588. formatDate(date) {
  589. if (!date) return '';
  590. const d = new Date(date);
  591. return d.getFullYear() + '-' +
  592. String(d.getMonth() + 1).padStart(2, '0') + '-' +
  593. String(d.getDate()).padStart(2, '0');
  594. }
  595. },
  596. mounted() {
  597. this.$nextTick(() => {
  598. if (this.$refs.scanCodeRef) {
  599. this.$refs.scanCodeRef.focus();
  600. }
  601. });
  602. }
  603. };
  604. </script>
  605. <style scoped>
  606. /* 复用inventory-move.vue的样式 */
  607. .pda-container {
  608. width: 100%;
  609. height: 100vh;
  610. display: flex;
  611. flex-direction: column;
  612. overflow: hidden;
  613. }
  614. .status-bar {
  615. background: #17B3A3;
  616. color: white;
  617. padding: 8px 16px;
  618. display: flex;
  619. justify-content: space-between;
  620. align-items: center;
  621. height: 40px;
  622. min-height: 40px;
  623. }
  624. .goBack {
  625. cursor: pointer;
  626. font-size: 16px;
  627. font-weight: 500;
  628. }
  629. .scan-box input {
  630. width: 100%;
  631. padding: 12px;
  632. font-size: 16px;
  633. }
  634. /* 物料描述样式 - 防止重叠 */
  635. .description-col {
  636. margin-bottom: 15px !important;
  637. }
  638. .part-description {
  639. word-wrap: break-word;
  640. word-break: break-all;
  641. line-height: 1.8;
  642. display: block;
  643. max-width: 100%;
  644. margin-bottom: 10px;
  645. padding-bottom: 5px;
  646. }
  647. /* 确保物料描述的form-item有足够间距 */
  648. .description-col .el-form-item {
  649. margin-bottom: 15px !important;
  650. }
  651. /* 搜索容器 */
  652. .search-container {
  653. padding: 12px 16px;
  654. background: white;
  655. display: flex;
  656. align-items: center;
  657. gap: 12px;
  658. }
  659. .search-container .el-input {
  660. width: 240px;
  661. margin-right: 12px;
  662. }
  663. /* 紧凑型输入框样式 */
  664. .compact-input >>> .el-input__inner {
  665. height: 36px;
  666. padding: 0 12px 0 35px;
  667. font-size: 14px;
  668. }
  669. .compact-input >>> .el-input__prefix {
  670. left: 10px;
  671. }
  672. .compact-input >>> .el-input__suffix {
  673. right: 30px;
  674. }
  675. /* 模式切换开关 */
  676. .mode-switch {
  677. position: relative;
  678. display: inline-block;
  679. }
  680. .custom-switch {
  681. transform: scale(1.3);
  682. }
  683. /* 中间文字 */
  684. .switch-text {
  685. position: absolute;
  686. left: 25%;
  687. transform: translateX(-50%);
  688. top: 50%;
  689. transform: translateY(-50%) translateX(-50%);
  690. font-size: 12px;
  691. font-weight: 500;
  692. color: #606266;
  693. white-space: nowrap;
  694. pointer-events: none;
  695. z-index: 1;
  696. top: 53%;
  697. transform: translate(-50%, -50%);
  698. font-size: 12px;
  699. font-weight: bold;
  700. color: white;
  701. pointer-events: none;
  702. z-index: 2;
  703. }
  704. .switch-text2 {
  705. position: absolute;
  706. left: 75%;
  707. transform: translateX(-50%);
  708. top: 50%;
  709. transform: translateY(-50%) translateX(-50%);
  710. font-size: 12px;
  711. font-weight: 500;
  712. color: #606266;
  713. white-space: nowrap;
  714. pointer-events: none;
  715. z-index: 1;
  716. top: 53%;
  717. transform: translate(-50%, -50%);
  718. font-size: 12px;
  719. font-weight: bold;
  720. color: white;
  721. pointer-events: none;
  722. z-index: 2;
  723. }
  724. /* 调整 switch 尺寸以便容纳文字 */
  725. .custom-switch >>> .el-switch__core {
  726. width: 60px;
  727. height: 22px;
  728. }
  729. /* 物料信息卡片 */
  730. .material-info-card {
  731. background: white;
  732. margin: 4px 4px;
  733. padding: 6px 20px;
  734. border-radius: 8px;
  735. box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
  736. border: 1px solid #f0f0f0;
  737. }
  738. .card-title {
  739. margin-bottom: 8px;
  740. display: flex;
  741. align-items: center;
  742. gap: 8px;
  743. }
  744. .title-label {
  745. font-size: 12px;
  746. color: #666;
  747. font-weight: 500;
  748. }
  749. .title-value {
  750. font-size: 16px;
  751. font-weight: bold;
  752. color: #333;
  753. line-height: 1.2;
  754. }
  755. /* 表单样式 */
  756. .form-row {
  757. display: flex;
  758. gap: 12px;
  759. margin-bottom: 12px;
  760. }
  761. .form-row:last-child {
  762. margin-bottom: 0;
  763. }
  764. .form-item {
  765. flex: 1;
  766. display: flex;
  767. flex-direction: column;
  768. }
  769. .form-label {
  770. font-size: 11px;
  771. color: #666;
  772. font-weight: 500;
  773. margin-bottom: 4px;
  774. display: block;
  775. }
  776. .form-item .el-input {
  777. width: 100%;
  778. }
  779. .form-item .el-date-editor {
  780. width: 100%;
  781. }
  782. /* 区域标题 */
  783. .section-title {
  784. display: flex;
  785. align-items: center;
  786. justify-content: space-between;
  787. padding: 6px 8px;
  788. background: white;
  789. margin: 0 4px;
  790. margin-top: 4px;
  791. border-radius: 8px 8px 0 0;
  792. border-bottom: 2px solid #17B3A3;
  793. }
  794. .title-left {
  795. display: flex;
  796. align-items: center;
  797. }
  798. .title-left i {
  799. color: #17B3A3;
  800. font-size: 16px;
  801. margin-right: 8px;
  802. }
  803. .title-left span {
  804. color: #17B3A3;
  805. font-size: 14px;
  806. font-weight: 500;
  807. }
  808. /* 标签列表 */
  809. .label-list {
  810. background: white;
  811. margin: 4px 4px;
  812. border-radius: 0 0 8px 8px;
  813. overflow: hidden;
  814. }
  815. .label-list .el-form-item {
  816. margin-bottom: 1px;
  817. }
  818. .label-list .el-form-item__label {
  819. padding-bottom: 1px;
  820. margin-bottom: 0;
  821. line-height: 1.1;
  822. font-size: 11px;
  823. }
  824. .label-list .el-form-item__content {
  825. line-height: 1.2;
  826. font-size: 12px;
  827. }
  828. .item-list {
  829. flex: 1;
  830. overflow-y: auto;
  831. margin: 10px 0;
  832. border: 1px solid rgba(200, 200, 200, 0.8);
  833. background: white;
  834. }
  835. .item-list span {
  836. color: #000;
  837. font-size: 15px;
  838. }
  839. .bottom-line-row {
  840. border-bottom: 1px solid #f0f0f0;
  841. margin-bottom: 8px;
  842. padding-bottom: 8px;
  843. }
  844. .inboundButton {
  845. font-size: 16px;
  846. border-radius: 3px;
  847. color: #17b3a3;
  848. }
  849. .item-list .el-row {
  850. cursor: pointer;
  851. transition: background 0.3s;
  852. padding: 10px;
  853. }
  854. .item-list .el-row:hover {
  855. background: #f5f7fa;
  856. }
  857. .empty-labels {
  858. padding: 20px;
  859. text-align: center;
  860. color: #999;
  861. background: white;
  862. margin: 0 4px;
  863. border-radius: 8px;
  864. }
  865. /* 底部操作按钮 */
  866. .bottom-actions {
  867. display: flex;
  868. padding: 16px;
  869. gap: 20px;
  870. background: white;
  871. margin-top: auto;
  872. }
  873. .action-btn {
  874. flex: 1;
  875. padding: 12px;
  876. border: 1px solid #17B3A3;
  877. background: #17B3A3;
  878. color: white;
  879. border-radius: 20px;
  880. font-size: 14px;
  881. cursor: pointer;
  882. transition: all 0.2s ease;
  883. }
  884. .action-btn.secondary {
  885. background: white;
  886. color: #17B3A3;
  887. }
  888. .action-btn:hover {
  889. background: #0d8f7f;
  890. border-color: #0d8f7f;
  891. }
  892. .action-btn.secondary:hover {
  893. background: #17B3A3;
  894. color: white;
  895. }
  896. .action-btn:active {
  897. transform: scale(0.98);
  898. }
  899. .action-btn:disabled {
  900. background: #c0c4cc !important;
  901. border-color: #c0c4cc !important;
  902. color: white !important;
  903. cursor: not-allowed !important;
  904. transform: none !important;
  905. box-shadow: none !important;
  906. }
  907. .action-btn:disabled:hover {
  908. background: #c0c4cc !important;
  909. transform: none !important;
  910. box-shadow: none !important;
  911. }
  912. .action-btn.secondary:disabled {
  913. background: #f5f7fa !important;
  914. color: #c0c4cc !important;
  915. border-color: #e4e7ed !important;
  916. }
  917. /* 全页面Loading样式 */
  918. .page-loading-overlay {
  919. position: fixed;
  920. top: 0;
  921. left: 0;
  922. right: 0;
  923. bottom: 0;
  924. background: rgba(255, 255, 255, 0.5);
  925. display: flex;
  926. align-items: center;
  927. justify-content: center;
  928. z-index: 9999;
  929. }
  930. .page-loading-content {
  931. display: flex;
  932. flex-direction: column;
  933. align-items: center;
  934. justify-content: center;
  935. padding: 30px;
  936. background: white;
  937. border-radius: 12px;
  938. box-shadow: 0 4px 20px rgba(0, 0, 0, 0.1);
  939. min-width: 150px;
  940. }
  941. .page-loading-icon {
  942. font-size: 32px;
  943. color: #17B3A3;
  944. animation: rotating 1s linear infinite;
  945. margin-bottom: 16px;
  946. }
  947. .page-loading-text {
  948. color: #17B3A3;
  949. font-size: 16px;
  950. font-weight: 500;
  951. text-align: center;
  952. }
  953. .content-disabled {
  954. pointer-events: none;
  955. opacity: 0.6;
  956. }
  957. @keyframes rotating {
  958. from {
  959. transform: rotate(0deg);
  960. }
  961. to {
  962. transform: rotate(360deg);
  963. }
  964. }
  965. /* 响应式设计 */
  966. @media (max-width: 360px) {
  967. .status-bar {
  968. padding: 8px 12px;
  969. }
  970. .search-container {
  971. padding: 8px 12px;
  972. margin: 1px 4px;
  973. }
  974. .material-info-card {
  975. margin: 4px 4px;
  976. padding: 6px 16px;
  977. }
  978. .item-list {
  979. margin: 0 12px 8px;
  980. }
  981. .form-row {
  982. gap: 8px;
  983. margin-bottom: 8px;
  984. }
  985. .form-label {
  986. font-size: 10px;
  987. margin-bottom: 2px;
  988. }
  989. }
  990. </style>