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.

1360 lines
28 KiB

7 months ago
7 months ago
7 months ago
7 months ago
7 months ago
7 months ago
7 months ago
7 months ago
7 months ago
7 months ago
7 months ago
7 months ago
7 months ago
7 months ago
7 months ago
7 months ago
7 months ago
7 months ago
7 months ago
7 months ago
7 months ago
7 months ago
7 months ago
7 months ago
7 months ago
7 months ago
7 months ago
7 months ago
7 months ago
7 months ago
7 months ago
7 months ago
7 months ago
7 months ago
7 months ago
7 months ago
7 months ago
7 months ago
7 months ago
7 months ago
7 months ago
7 months ago
7 months ago
7 months ago
7 months ago
7 months ago
7 months ago
7 months ago
7 months ago
7 months ago
7 months ago
7 months ago
7 months ago
7 months ago
7 months ago
7 months ago
7 months ago
7 months ago
7 months ago
7 months ago
7 months ago
7 months ago
7 months ago
7 months ago
7 months ago
7 months ago
7 months ago
7 months ago
7 months ago
7 months ago
7 months ago
7 months ago
7 months ago
7 months ago
7 months ago
7 months ago
7 months ago
7 months ago
7 months ago
7 months ago
7 months ago
7 months ago
7 months ago
7 months ago
7 months ago
7 months ago
7 months ago
7 months ago
7 months ago
7 months ago
7 months ago
7 months ago
7 months ago
7 months ago
7 months ago
7 months ago
7 months ago
7 months ago
7 months ago
7 months ago
7 months ago
7 months ago
7 months ago
  1. <template>
  2. <div class="pda-container">
  3. <!-- 头部栏 -->
  4. <div class="header-bar">
  5. <div class="header-left" @click="$router.back()">
  6. <i class="el-icon-arrow-left"></i>
  7. <span>入库上架</span>
  8. </div>
  9. <div class="header-right" @click="$router.push({ path: '/' })">
  10. 首页
  11. </div>
  12. </div>
  13. <!-- 搜索框 -->
  14. <div class="search-container">
  15. <el-input
  16. class="compact-input"
  17. clearable
  18. v-model="scanCode"
  19. placeholder="请扫描标签条码"
  20. prefix-icon="el-icon-search"
  21. @keyup.enter.native="handleScan"
  22. ref="scanInput"
  23. />
  24. <div class="mode-switch">
  25. <el-switch
  26. class="custom-switch"
  27. v-model="isRemoveMode"
  28. active-color="#ff4949"
  29. inactive-color="#13ce66">
  30. </el-switch>
  31. <span v-if="isRemoveMode" class="switch-text">{{ '移除' }}</span>
  32. <span v-else class="switch-text2">{{ '添加' }}</span>
  33. </div>
  34. </div>
  35. <!-- 物料信息卡片 -->
  36. <div class="material-info-card" v-if="materialInfo.inboundNo">
  37. <div class="card-title">
  38. <span class="title-label">入库单号</span>
  39. <span class="title-value">{{ materialInfo.inboundNo }}</span>
  40. </div>
  41. <div class="card-details">
  42. <div class="detail-item">
  43. <div class="detail-label">标签张数</div>
  44. <div class="detail-value">
  45. <span class="qualified">{{ materialInfo.labelinCount }}</span><span class="total">{{ materialInfo.totalLabels }}</span>
  46. </div>
  47. </div>
  48. <div class="detail-item">
  49. <div class="detail-label">物料总数</div>
  50. <div class="detail-value">
  51. <span class="qualified">{{ materialInfo.totalinLabels }}</span><span class="total">{{ materialInfo.labelCount }}</span>
  52. </div>
  53. </div>
  54. <div class="detail-item">
  55. <div class="detail-label">批次号</div>
  56. <div class="detail-value">{{ materialInfo.batchNo }}</div>
  57. </div>
  58. <div class="detail-item">
  59. <div class="detail-label">检验日期</div>
  60. <div class="detail-value">{{ formatDate(materialInfo.inspectionDate) }}</div>
  61. </div>
  62. </div>
  63. </div>
  64. <!-- 入库信息确认标题 -->
  65. <div class="section-title">
  66. <div class="title-left">
  67. <i class="el-icon-circle-check"></i>
  68. <span>入库信息确认</span>
  69. </div>
  70. <div class="title-right">
  71. <span class="material-list-link" @click="showMaterialListDialog">物料清单</span>
  72. </div>
  73. </div>
  74. <!-- 标签列表 -->
  75. <div class="label-list">
  76. <div class="list-header">
  77. <div class="col-no">NO.</div>
  78. <div class="col-label">标签条码</div>
  79. <div class="col-part">物料编码</div>
  80. <div class="col-qty">标签数量</div>
  81. </div>
  82. <div class="list-body">
  83. <div
  84. v-for="(label, index) in labelList"
  85. :key="label.id"
  86. class="list-item"
  87. >
  88. <div class="col-no">{{ labelList.length - index }}</div>
  89. <div class="col-label">{{ label.labelCode }}</div>
  90. <div class="col-part">{{ label.partNo }}</div>
  91. <div class="col-qty">{{ label.quantity }}</div>
  92. </div>
  93. <!-- 空状态 -->
  94. <div v-if="labelList.length === 0" class="empty-labels">
  95. <p>暂无扫描标签</p>
  96. </div>
  97. </div>
  98. </div>
  99. <!-- 底部操作按钮 -->
  100. <div class="bottom-actions">
  101. <button class="action-btn secondary" @click="confirmInbound">
  102. 确定
  103. </button>
  104. <button class="action-btn secondary" style="margin-left: 10px;" @click="printLabels">
  105. 打印
  106. </button>
  107. <button class="action-btn secondary" style="margin-left: 10px;" @click="cancelInbound">
  108. 取消
  109. </button>
  110. </div>
  111. <!-- 库位号输入覆盖层 -->
  112. <div v-if="showLocationDialog" class="location-overlay">
  113. <div class="location-modal">
  114. <div class="modal-header">
  115. <span class="modal-title">扫描库位号</span>
  116. </div>
  117. <div class="modal-body">
  118. <div class="location-input-section">
  119. <div class="input-wrapper">
  120. <i class="el-icon-location-outline input-icon"></i>
  121. <input
  122. v-model="locationCode"
  123. placeholder="请扫描库位号"
  124. ref="locationInput"
  125. class="location-input"
  126. />
  127. </div>
  128. </div>
  129. </div>
  130. <div class="modal-footer">
  131. <button class="btn-cancel" @click="cancelLocationInput">取消</button>
  132. <button class="btn-confirm" @click="submitInbound" :disabled="!locationCode.trim()">
  133. 确认上架
  134. </button>
  135. </div>
  136. </div>
  137. </div>
  138. <!-- 物料清单弹窗 -->
  139. <div v-if="showMaterialDialog" class="material-overlay">
  140. <div class="material-modal">
  141. <div class="modal-header">
  142. <span class="modal-title">物料清单</span>
  143. <i class="el-icon-close close-btn" @click="closeMaterialDialog"></i>
  144. </div>
  145. <div class="modal-body">
  146. <!-- 加载状态 -->
  147. <div v-if="materialListLoading" class="loading-container">
  148. <i class="el-icon-loading"></i>
  149. <span>加载中...</span>
  150. </div>
  151. <!-- 物料表格 -->
  152. <div v-else-if="materialList.length > 0" class="material-table">
  153. <div class="table-header">
  154. <div class="col-no">NO.</div>
  155. <!-- <div class="col-label-code">标签条码</div>-->
  156. <div class="col-material-code">物料编码</div>
  157. <div class="col-required-qty">需求数量</div>
  158. <div class="col-inbound-qty">已入库数</div>
  159. </div>
  160. <div class="table-body">
  161. <div
  162. v-for="(item, index) in materialList"
  163. :key="index"
  164. class="table-row"
  165. >
  166. <div class="col-no">{{ index + 1 }}</div>
  167. <!-- <div class="col-label-code">{{ item.labelCode || item.rollNo }}</div>-->
  168. <div class="col-material-code">{{ item.materialCode || item.partNo }}</div>
  169. <div class="col-required-qty">{{ item.labelCount || 0 }}</div>
  170. <div class="col-inbound-qty">{{ item.totalinLabels || 0 }}</div>
  171. </div>
  172. </div>
  173. </div>
  174. <!-- 空数据状态 -->
  175. <div v-else class="empty-material">
  176. <i class="el-icon-document"></i>
  177. <p>暂无物料数据</p>
  178. </div>
  179. </div>
  180. <div class="modal-footer">
  181. <button class="btn-close" @click="closeMaterialDialog">关闭</button>
  182. </div>
  183. </div>
  184. </div>
  185. </div>
  186. </template>
  187. <script>
  188. import { getInboundDetails, validateLabelWithInbound, confirmInboundStorage, deleteLabel, getMaterialList, getScannedLabelList } from "@/api/inbound.js";
  189. import { getCurrentWarehouse } from '@/utils'
  190. import moment from 'moment';
  191. export default {
  192. data() {
  193. return {
  194. scanCode: '',
  195. materialInfo: {},
  196. labelList: [],
  197. inboundNo: '',
  198. partNo: '',
  199. showLocationDialog: false,
  200. locationCode: '',
  201. showMaterialDialog: false,
  202. materialList: [],
  203. materialListLoading: false,
  204. isRemoveMode: false // 默认为添加模式
  205. };
  206. },
  207. methods: {
  208. formatDate(date) {
  209. return date ? moment(date).format('YYYY-MM-DD') : '';
  210. },
  211. // 处理扫描
  212. handleScan() {
  213. if (!this.scanCode.trim()) {
  214. return;
  215. }
  216. if (this.isRemoveMode) {
  217. this.removeLabelByCode(this.scanCode.trim());
  218. } else {
  219. this.validateAndAddLabel(this.scanCode.trim());
  220. }
  221. this.scanCode = '';
  222. },
  223. // 验证标签并添加到列表(通过存储过程)
  224. validateAndAddLabel(labelCode) {
  225. const params = {
  226. labelCode: labelCode,
  227. inboundNo: this.inboundNo,
  228. site: this.materialInfo.site,
  229. buNo: this.materialInfo.buNo,
  230. operationType: 'I', // I表示添加
  231. warehouseId: getCurrentWarehouse() // 当前仓库
  232. };
  233. validateLabelWithInbound(params).then(({ data }) => {
  234. if (data && data.code === 0) {
  235. this.$message.success('操作成功');
  236. // 重新加载已扫描标签列表
  237. this.loadScannedLabelList();
  238. } else {
  239. this.$message.error(data.msg || '操作失败');
  240. }
  241. }).catch(error => {
  242. console.error('标签验证失败:', error);
  243. this.$message.error('操作失败');
  244. });
  245. },
  246. // 通过条码移除标签(通过存储过程)
  247. removeLabelByCode(labelCode) {
  248. const params = {
  249. labelCode: labelCode,
  250. inboundNo: this.inboundNo,
  251. site: this.materialInfo.site,
  252. buNo: this.materialInfo.buNo,
  253. operationType: 'D', // D表示移除
  254. warehouseId: getCurrentWarehouse() // 当前仓库
  255. };
  256. validateLabelWithInbound(params).then(({ data }) => {
  257. if (data && data.code === 0) {
  258. this.$message.success('操作成功');
  259. // 重新加载已扫描标签列表
  260. this.loadScannedLabelList();
  261. } else {
  262. this.$message.error(data.msg || '操作失败');
  263. }
  264. }).catch(error => {
  265. console.error('标签移除失败:', error);
  266. this.$message.error('操作失败');
  267. });
  268. },
  269. // 确认入库
  270. confirmInbound() {
  271. if (this.labelList.length === 0) {
  272. this.$message.warning('请先扫描标签');
  273. return;
  274. }
  275. // 显示库位号输入对话框
  276. this.showLocationDialog = true;
  277. this.locationCode = '';
  278. // 聚焦到库位号输入框
  279. this.$nextTick(() => {
  280. if (this.$refs.locationInput) {
  281. this.$refs.locationInput.focus();
  282. }
  283. });
  284. },
  285. // 取消库位号输入
  286. cancelLocationInput() {
  287. this.showLocationDialog = false;
  288. this.locationCode = '';
  289. },
  290. // 提交入库(通过存储过程)
  291. submitInbound() {
  292. if (!this.locationCode.trim()) {
  293. this.$message.warning('请输入库位号');
  294. return;
  295. }
  296. const params = {
  297. site: this.materialInfo.site,
  298. buNo: this.materialInfo.buNo,
  299. inboundNo: this.inboundNo,
  300. locationCode: this.locationCode.trim()
  301. };
  302. confirmInboundStorage(params).then(({ data }) => {
  303. if (data && data.code === 0) {
  304. this.$message.success('操作成功')
  305. this.showLocationDialog = false
  306. this.$router.back()
  307. } else {
  308. this.$message.error(data.msg || '操作失败')
  309. }
  310. }).catch(error => {
  311. console.error('上架失败:', error)
  312. this.$message.error('操作失败')
  313. });
  314. },
  315. // 打印标签
  316. printLabels() {
  317. if (this.labelList.length === 0) {
  318. this.$message.warning('暂无标签可打印');
  319. return;
  320. }
  321. this.$message.warning('打印功能开发中...');
  322. },
  323. // 取消入库
  324. cancelInbound() {
  325. if (this.labelList.length > 0) {
  326. this.$confirm('取消后将清空已扫描的标签,确定取消吗?', '提示', {
  327. confirmButtonText: '确定',
  328. cancelButtonText: '继续操作',
  329. type: 'warning'
  330. }).then(() => {
  331. this.$router.back();
  332. }).catch(() => {
  333. // 用户选择继续操作
  334. });
  335. } else {
  336. this.$router.back();
  337. }
  338. },
  339. // 显示物料清单弹窗
  340. showMaterialListDialog() {
  341. this.showMaterialDialog = true;
  342. this.loadMaterialList();
  343. },
  344. // 加载物料清单
  345. loadMaterialList() {
  346. if (!this.materialInfo.site || !this.materialInfo.buNo || !this.inboundNo) {
  347. this.$message.error('缺少必要参数,无法获取物料清单');
  348. return;
  349. }
  350. this.materialListLoading = true;
  351. const params = {
  352. site: this.materialInfo.site,
  353. buNo: this.materialInfo.buNo,
  354. inboundNo: this.inboundNo
  355. };
  356. getMaterialList(params).then(({ data }) => {
  357. this.materialListLoading = false;
  358. if (data && data.code === 0) {
  359. this.materialList = data.data || [];
  360. } else {
  361. this.$message.error(data.msg || '获取物料清单失败');
  362. this.materialList = [];
  363. }
  364. }).catch(error => {
  365. this.materialListLoading = false;
  366. console.error('获取物料清单失败:', error);
  367. this.$message.error('获取物料清单失败');
  368. this.materialList = [];
  369. });
  370. },
  371. // 关闭物料清单弹窗
  372. closeMaterialDialog() {
  373. this.showMaterialDialog = false;
  374. },
  375. // 加载入库单详情
  376. loadInboundDetails() {
  377. const params = {
  378. inboundNo: this.inboundNo,
  379. partNo: this.partNo,
  380. buNo: this.buNo,
  381. warehouseId: getCurrentWarehouse(),
  382. site:localStorage.getItem('site'),
  383. };
  384. getInboundDetails(params).then(({ data }) => {
  385. if (data && data.code === 0) {
  386. this.materialInfo = data.data;
  387. // 加载入库单详情成功后,加载已扫描标签列表
  388. this.loadScannedLabelList();
  389. } else {
  390. this.$message.error(data.msg || '获取入库单详情失败');
  391. }
  392. }).catch(error => {
  393. console.error('获取入库单详情失败:', error);
  394. this.$message.error('获取入库单详情失败');
  395. });
  396. },
  397. // 加载已扫描标签列表(从缓存表)
  398. loadScannedLabelList() {
  399. if (!this.materialInfo.site || !this.materialInfo.buNo || !this.inboundNo) {
  400. return;
  401. }
  402. const params = {
  403. site: this.materialInfo.site,
  404. buNo: this.materialInfo.buNo,
  405. inboundNo: this.inboundNo
  406. };
  407. getScannedLabelList(params).then(({ data }) => {
  408. if (data && data.code === 0) {
  409. // 将查询结果转换为labelList格式
  410. this.labelList = (data.data || []).map((item, index) => ({
  411. id: Date.now() + index,
  412. labelCode: item.labelCode,
  413. partNo: item.partNo,
  414. quantity: item.quantity,
  415. buNo: item.buNo
  416. }));
  417. } else {
  418. console.error('获取已扫描标签列表失败:', data.msg);
  419. }
  420. }).catch(error => {
  421. console.error('获取已扫描标签列表失败:', error);
  422. });
  423. }
  424. },
  425. mounted() {
  426. // 获取路由参数
  427. this.inboundNo = this.$route.params.inboundNo;
  428. this.buNo = this.$route.params.buNo;
  429. if (!this.inboundNo || !this.buNo) {
  430. this.$message.error('参数错误');
  431. this.$router.back();
  432. return;
  433. }
  434. // 聚焦扫描框
  435. this.$nextTick(() => {
  436. if (this.$refs.scanInput) {
  437. this.$refs.scanInput.focus();
  438. }
  439. });
  440. // 加载入库单详情
  441. this.loadInboundDetails();
  442. }
  443. };
  444. </script>
  445. <style scoped>
  446. .pda-container {
  447. width: 100vw;
  448. height: 100vh;
  449. display: flex;
  450. flex-direction: column;
  451. background: #f5f5f5;
  452. }
  453. /* 头部栏 */
  454. .header-bar {
  455. display: flex;
  456. justify-content: space-between;
  457. align-items: center;
  458. padding: 8px 16px;
  459. background: #17B3A3;
  460. color: white;
  461. height: 40px;
  462. min-height: 40px;
  463. }
  464. .header-left {
  465. display: flex;
  466. align-items: center;
  467. cursor: pointer;
  468. font-size: 16px;
  469. font-weight: 500;
  470. }
  471. .header-left i {
  472. margin-right: 8px;
  473. font-size: 18px;
  474. }
  475. .header-right {
  476. cursor: pointer;
  477. font-size: 16px;
  478. font-weight: 500;
  479. }
  480. /* 搜索容器 */
  481. .search-container {
  482. padding: 12px 16px;
  483. background: white;
  484. display: flex;
  485. align-items: center;
  486. gap: 12px;
  487. }
  488. .search-container .el-input {
  489. width: 240px;
  490. margin-right: 12px;
  491. }
  492. /* 紧凑型输入框样式 */
  493. .compact-input ::v-deep .el-input__inner {
  494. height: 36px;
  495. padding: 0 12px 0 35px;
  496. font-size: 14px;
  497. }
  498. .compact-input ::v-deep .el-input__prefix {
  499. left: 10px;
  500. }
  501. .compact-input ::v-deep .el-input__suffix {
  502. right: 30px;
  503. }
  504. /* 模式切换开关 */
  505. .mode-switch {
  506. position: relative;
  507. display: inline-block;
  508. }
  509. .custom-switch {
  510. transform: scale(1.3);
  511. }
  512. /* 中间文字 */
  513. .switch-text {
  514. position: absolute;
  515. left: 25%;
  516. transform: translateX(-50%);
  517. top: 50%;
  518. transform: translateY(-50%) translateX(-50%);
  519. font-size: 12px;
  520. font-weight: 500;
  521. color: #606266;
  522. white-space: nowrap;
  523. pointer-events: none;
  524. z-index: 1;
  525. top: 53%;
  526. transform: translate(-50%, -50%);
  527. font-size: 12px;
  528. font-weight: bold;
  529. color: white;
  530. pointer-events: none;
  531. z-index: 2;
  532. }
  533. .switch-text2 {
  534. position: absolute;
  535. left: 75%;
  536. transform: translateX(-50%);
  537. top: 50%;
  538. transform: translateY(-50%) translateX(-50%);
  539. font-size: 12px;
  540. font-weight: 500;
  541. color: #606266;
  542. white-space: nowrap;
  543. pointer-events: none;
  544. z-index: 1;
  545. top: 53%;
  546. transform: translate(-50%, -50%);
  547. font-size: 12px;
  548. font-weight: bold;
  549. color: white;
  550. pointer-events: none;
  551. z-index: 2;
  552. }
  553. /* 调整 switch 尺寸以便容纳文字 */
  554. .custom-switch ::v-deep .el-switch__core {
  555. width: 60px;
  556. height: 28px;
  557. }
  558. .search-box {
  559. position: relative;
  560. display: flex;
  561. align-items: center;
  562. background: #f8f9fa;
  563. border: 1px solid #e0e0e0;
  564. border-radius: 8px;
  565. padding: 0 12px;
  566. }
  567. .search-icon {
  568. color: #999;
  569. font-size: 16px;
  570. margin-right: 8px;
  571. }
  572. .search-box input {
  573. flex: 1;
  574. border: none;
  575. background: transparent;
  576. padding: 12px 0;
  577. font-size: 14px;
  578. outline: none;
  579. }
  580. .search-box input::placeholder {
  581. color: #999;
  582. }
  583. /* 物料信息卡片 */
  584. .material-info-card {
  585. background: white;
  586. margin: 4px 16px;
  587. padding: 6px 20px;
  588. border-radius: 8px;
  589. box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
  590. border: 1px solid #f0f0f0;
  591. }
  592. /* 卡片标题 */
  593. .card-title {
  594. margin-bottom: 16px;
  595. }
  596. .title-label {
  597. font-size: 11px;
  598. color: #999;
  599. display: block;
  600. margin-bottom: 6px;
  601. font-weight: normal;
  602. }
  603. .title-value {
  604. font-size: 18px;
  605. font-weight: bold;
  606. color: #333;
  607. line-height: 1.2;
  608. margin-left: 20px;
  609. }
  610. /* 卡片详情 */
  611. .card-details {
  612. display: flex;
  613. justify-content: space-between;
  614. align-items: flex-start;
  615. gap: 4px;
  616. }
  617. .detail-item {
  618. flex: 1;
  619. text-align: center;
  620. min-width: 60px;
  621. max-width: 60px;
  622. }
  623. .detail-label {
  624. font-size: 11px;
  625. color: #999;
  626. margin-bottom: 6px;
  627. font-weight: normal;
  628. line-height: 1.2;
  629. margin-left: -12px;
  630. }
  631. .detail-value {
  632. font-size: 13px;
  633. color: #333;
  634. font-weight: 500;
  635. line-height: 1.2;
  636. margin-left: -12px;
  637. }
  638. .detail-value .qualified {
  639. color: #17B3A3;
  640. font-weight: 500;
  641. }
  642. .detail-value .total {
  643. color: #333;
  644. font-weight: 500;
  645. }
  646. .detail-value .total::before {
  647. content: '/';
  648. color: #333;
  649. }
  650. /* 区域标题 */
  651. .section-title {
  652. display: flex;
  653. align-items: center;
  654. justify-content: space-between;
  655. padding: 6px 8px;
  656. background: white;
  657. margin: 0 16px;
  658. margin-top: 4px;
  659. border-radius: 8px 8px 0 0;
  660. border-bottom: 2px solid #17B3A3;
  661. }
  662. .title-left {
  663. display: flex;
  664. align-items: center;
  665. }
  666. .title-left i {
  667. color: #17B3A3;
  668. font-size: 16px;
  669. margin-right: 8px;
  670. }
  671. .title-left span {
  672. color: #17B3A3;
  673. font-size: 14px;
  674. font-weight: 500;
  675. }
  676. .title-right {
  677. display: flex;
  678. align-items: center;
  679. }
  680. .material-list-link {
  681. color: #17B3A3;
  682. font-size: 14px;
  683. font-weight: 500;
  684. cursor: pointer;
  685. text-decoration: underline;
  686. transition: color 0.2s ease;
  687. }
  688. .material-list-link:hover {
  689. color: #0d8f7f;
  690. }
  691. /* 标签列表 */
  692. .label-list {
  693. background: white;
  694. margin: 0 16px 12px;
  695. border-radius: 0 0 8px 8px;
  696. overflow: hidden;
  697. flex: 1;
  698. display: flex;
  699. flex-direction: column;
  700. max-height: 300px; /* 限制最大高度 */
  701. }
  702. .label-list .list-body {
  703. flex: 1;
  704. overflow-y: auto; /* 允许垂直滚动 */
  705. max-height: 250px; /* 内容区域最大高度 */
  706. }
  707. /* 滚动条样式优化 */
  708. .label-list .list-body::-webkit-scrollbar {
  709. width: 4px;
  710. }
  711. .label-list .list-body::-webkit-scrollbar-track {
  712. background: #f1f1f1;
  713. border-radius: 2px;
  714. }
  715. .label-list .list-body::-webkit-scrollbar-thumb {
  716. background: #17B3A3;
  717. border-radius: 2px;
  718. }
  719. .label-list .list-body::-webkit-scrollbar-thumb:hover {
  720. background: #0d8f7f;
  721. }
  722. .list-header {
  723. display: flex;
  724. background: #f8f9fa;
  725. padding: 12px 8px;
  726. border-bottom: 1px solid #e0e0e0;
  727. font-size: 12px;
  728. color: #666;
  729. font-weight: 500;
  730. }
  731. .list-item {
  732. display: flex;
  733. padding: 12px 8px;
  734. border-bottom: 1px solid #f0f0f0;
  735. font-size: 12px;
  736. color: #333;
  737. }
  738. .list-item:last-child {
  739. border-bottom: none;
  740. }
  741. .col-no {
  742. width: 20px;
  743. text-align: center;
  744. }
  745. .col-label {
  746. flex: 2;
  747. text-align: center;
  748. }
  749. .col-part {
  750. flex: 2;
  751. text-align: center;
  752. }
  753. .col-qty {
  754. width: 60px;
  755. text-align: center;
  756. }
  757. .empty-labels {
  758. padding: 40px 20px;
  759. text-align: center;
  760. color: #999;
  761. }
  762. .empty-labels p {
  763. margin: 0;
  764. font-size: 14px;
  765. }
  766. /* 底部操作按钮 */
  767. .bottom-actions {
  768. display: flex;
  769. padding: 16px;
  770. gap: 20px;
  771. background: white;
  772. margin-top: auto;
  773. }
  774. .action-btn {
  775. flex: 1;
  776. padding: 12px;
  777. border: 1px solid #17B3A3;
  778. background: white;
  779. color: #17B3A3;
  780. border-radius: 20px;
  781. font-size: 14px;
  782. cursor: pointer;
  783. transition: all 0.2s ease;
  784. }
  785. .action-btn:hover {
  786. background: #17B3A3;
  787. color: white;
  788. }
  789. .action-btn:active {
  790. transform: scale(0.98);
  791. }
  792. /* 响应式设计 */
  793. @media (max-width: 360px) {
  794. .header-bar {
  795. padding: 8px 12px;
  796. }
  797. .search-container {
  798. padding: 8px 12px;
  799. }
  800. .material-info-card {
  801. margin: 4px 12px;
  802. padding: 6px 16px;
  803. }
  804. .section-title {
  805. margin: 0 12px;
  806. margin-top: 4px;
  807. }
  808. .label-list {
  809. margin: 0 12px 8px;
  810. }
  811. .card-details {
  812. flex-wrap: wrap;
  813. gap: 6px;
  814. }
  815. .detail-item {
  816. flex: 0 0 48%;
  817. margin-bottom: 6px;
  818. min-width: 50px;
  819. }
  820. .list-header, .list-item {
  821. font-size: 11px;
  822. }
  823. .col-label, .col-part {
  824. flex: 1.5;
  825. }
  826. }
  827. /* 库位号覆盖层样式 */
  828. .location-overlay {
  829. position: fixed;
  830. top: 0;
  831. left: 0;
  832. right: 0;
  833. bottom: 0;
  834. background: rgba(0, 0, 0, 0.5);
  835. z-index: 9999;
  836. display: flex;
  837. align-items: center;
  838. justify-content: center;
  839. padding: 20px;
  840. }
  841. .location-modal {
  842. background: white;
  843. border-radius: 12px;
  844. width: 100%;
  845. max-width: 400px;
  846. box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3);
  847. overflow: hidden;
  848. }
  849. .modal-header {
  850. background: #17B3A3;
  851. color: white;
  852. padding: 12px 20px;
  853. text-align: center;
  854. }
  855. .modal-title {
  856. font-size: 16px;
  857. font-weight: 500;
  858. }
  859. .modal-body {
  860. padding: 20px;
  861. }
  862. .location-input-section {
  863. margin-bottom: 20px;
  864. }
  865. .input-label {
  866. font-size: 14px;
  867. color: #333;
  868. margin-bottom: 12px;
  869. font-weight: 500;
  870. }
  871. .input-wrapper {
  872. position: relative;
  873. display: flex;
  874. align-items: center;
  875. }
  876. .input-icon {
  877. position: absolute;
  878. left: 12px;
  879. font-size: 18px;
  880. color: #17B3A3;
  881. z-index: 1;
  882. }
  883. .location-input {
  884. width: 100%;
  885. height: 48px;
  886. padding: 0 16px 0 40px;
  887. border: 1px solid #dcdfe6;
  888. border-radius: 8px;
  889. font-size: 16px;
  890. outline: none;
  891. transition: border-color 0.2s;
  892. }
  893. .location-input:focus {
  894. border-color: #17B3A3;
  895. }
  896. .location-input::placeholder {
  897. color: #c0c4cc;
  898. }
  899. .location-info {
  900. background: #f8f9fa;
  901. border-radius: 8px;
  902. padding: 16px;
  903. border-left: 4px solid #17B3A3;
  904. }
  905. .location-info .info-item {
  906. display: flex;
  907. align-items: center;
  908. }
  909. .location-info .label {
  910. font-size: 14px;
  911. color: #666;
  912. margin-right: 12px;
  913. min-width: 80px;
  914. }
  915. .location-info .value {
  916. font-size: 14px;
  917. color: #333;
  918. font-weight: 500;
  919. }
  920. .modal-footer {
  921. padding: 16px 20px;
  922. display: flex;
  923. justify-content: flex-end;
  924. gap: 12px;
  925. border-top: 1px solid #f0f0f0;
  926. }
  927. .btn-cancel,
  928. .btn-confirm {
  929. padding: 10px 20px;
  930. border-radius: 6px;
  931. font-size: 14px;
  932. cursor: pointer;
  933. transition: all 0.2s;
  934. border: none;
  935. outline: none;
  936. }
  937. .btn-cancel {
  938. background: #f5f5f5;
  939. color: #666;
  940. }
  941. .btn-cancel:hover {
  942. background: #e6e6e6;
  943. }
  944. .btn-confirm {
  945. background: #17B3A3;
  946. color: white;
  947. }
  948. .btn-confirm:hover:not(:disabled) {
  949. background: #0d8f7f;
  950. }
  951. .btn-confirm:disabled {
  952. background: #c0c4cc;
  953. cursor: not-allowed;
  954. }
  955. /* 物料清单弹窗样式 */
  956. .material-overlay {
  957. position: fixed;
  958. top: 0;
  959. left: 0;
  960. right: 0;
  961. bottom: 0;
  962. background: rgba(0, 0, 0, 0.5);
  963. z-index: 9999;
  964. display: flex;
  965. align-items: center;
  966. justify-content: center;
  967. padding: 20px;
  968. }
  969. .material-modal {
  970. background: white;
  971. border-radius: 12px;
  972. width: 100%;
  973. max-width: 800px;
  974. max-height: 80vh;
  975. box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3);
  976. overflow: hidden;
  977. display: flex;
  978. flex-direction: column;
  979. }
  980. .material-modal .modal-header {
  981. background: #17B3A3;
  982. color: white;
  983. padding: 5px 16px;
  984. display: flex;
  985. justify-content: space-between;
  986. align-items: center;
  987. min-height: 28px;
  988. }
  989. .close-btn {
  990. font-size: 16px;
  991. cursor: pointer;
  992. color: white;
  993. transition: color 0.2s ease;
  994. padding: 4px;
  995. display: flex;
  996. align-items: center;
  997. justify-content: center;
  998. }
  999. .close-btn:hover {
  1000. color: #e0e0e0;
  1001. }
  1002. .material-modal .modal-title {
  1003. font-size: 16px;
  1004. font-weight: 500;
  1005. margin: 0;
  1006. line-height: 1.2;
  1007. }
  1008. .material-modal .modal-body {
  1009. flex: 1;
  1010. overflow: auto;
  1011. padding: 0;
  1012. }
  1013. .material-table {
  1014. width: 100%;
  1015. }
  1016. .table-header {
  1017. display: flex;
  1018. background: #f8f9fa;
  1019. padding: 10px 6px;
  1020. border-bottom: 2px solid #17B3A3;
  1021. font-size: 12px;
  1022. color: #333;
  1023. font-weight: 600;
  1024. position: sticky;
  1025. top: 0;
  1026. z-index: 1;
  1027. }
  1028. .table-body {
  1029. max-height: 400px;
  1030. overflow-y: auto;
  1031. }
  1032. .table-row {
  1033. display: flex;
  1034. padding: 10px 6px;
  1035. border-bottom: 1px solid #f0f0f0;
  1036. font-size: 12px;
  1037. color: #333;
  1038. transition: background-color 0.2s ease;
  1039. }
  1040. .table-row:hover {
  1041. background-color: #f8f9fa;
  1042. }
  1043. .table-row:last-child {
  1044. border-bottom: none;
  1045. }
  1046. .material-table .col-no {
  1047. width: 25px;
  1048. text-align: center;
  1049. flex-shrink: 0;
  1050. font-size: 12px;
  1051. }
  1052. .material-table .col-label-code {
  1053. flex: 1.8;
  1054. text-align: center;
  1055. min-width: 100px;
  1056. font-size: 12px;
  1057. word-break: break-all;
  1058. }
  1059. .material-table .col-material-code {
  1060. flex: 1.8;
  1061. text-align: center;
  1062. min-width: 100px;
  1063. font-size: 12px;
  1064. word-break: break-all;
  1065. }
  1066. .material-table .col-required-qty {
  1067. flex: 0.8;
  1068. text-align: center;
  1069. min-width: 65px;
  1070. font-size: 12px;
  1071. }
  1072. .material-table .col-inbound-qty {
  1073. flex: 0.8;
  1074. text-align: center;
  1075. min-width: 65px;
  1076. font-size: 12px;
  1077. }
  1078. .material-modal .modal-footer {
  1079. padding: 15px 20px;
  1080. display: flex;
  1081. justify-content: center;
  1082. border-top: 1px solid #f0f0f0;
  1083. }
  1084. .btn-close {
  1085. padding: 10px 20px;
  1086. border-radius: 6px;
  1087. font-size: 14px;
  1088. cursor: pointer;
  1089. transition: all 0.2s;
  1090. border: 1px solid #17B3A3;
  1091. background: white;
  1092. color: #17B3A3;
  1093. }
  1094. .btn-close:hover {
  1095. background: #17B3A3;
  1096. color: white;
  1097. }
  1098. /* 加载状态样式 */
  1099. .loading-container {
  1100. display: flex;
  1101. flex-direction: column;
  1102. align-items: center;
  1103. justify-content: center;
  1104. padding: 60px 20px;
  1105. color: #666;
  1106. }
  1107. .loading-container i {
  1108. font-size: 24px;
  1109. margin-bottom: 12px;
  1110. color: #17B3A3;
  1111. }
  1112. .loading-container span {
  1113. font-size: 14px;
  1114. }
  1115. /* 空数据状态样式 */
  1116. .empty-material {
  1117. display: flex;
  1118. flex-direction: column;
  1119. align-items: center;
  1120. justify-content: center;
  1121. padding: 60px 20px;
  1122. color: #999;
  1123. }
  1124. .empty-material i {
  1125. font-size: 48px;
  1126. margin-bottom: 16px;
  1127. color: #ddd;
  1128. }
  1129. .empty-material p {
  1130. margin: 0;
  1131. font-size: 14px;
  1132. }
  1133. /* 响应式设计 - 物料清单弹窗 */
  1134. @media (max-width: 768px) {
  1135. .material-modal {
  1136. max-width: 98vw;
  1137. max-height: 85vh;
  1138. }
  1139. .material-table .col-no {
  1140. width: 25px;
  1141. font-size: 11px;
  1142. }
  1143. .material-table .col-label-code,
  1144. .material-table .col-material-code {
  1145. flex: 1.5;
  1146. min-width: 85px;
  1147. font-size: 11px;
  1148. }
  1149. .material-table .col-required-qty,
  1150. .material-table .col-inbound-qty {
  1151. flex: 0.7;
  1152. min-width: 50px;
  1153. font-size: 11px;
  1154. }
  1155. .table-header,
  1156. .table-row {
  1157. font-size: 11px;
  1158. padding: 8px 4px;
  1159. }
  1160. }
  1161. @media (max-width: 480px) {
  1162. .material-modal {
  1163. max-width: 100vw;
  1164. max-height: 90vh;
  1165. border-radius: 0;
  1166. }
  1167. .material-table .col-no {
  1168. width: 25px;
  1169. font-size: 10px;
  1170. }
  1171. .material-table .col-label-code,
  1172. .material-table .col-material-code {
  1173. flex: 1.3;
  1174. min-width: 65px;
  1175. font-size: 10px;
  1176. }
  1177. .material-table .col-required-qty,
  1178. .material-table .col-inbound-qty {
  1179. flex: 0.6;
  1180. min-width: 55px;
  1181. font-size: 10px;
  1182. }
  1183. .table-header,
  1184. .table-row {
  1185. font-size: 10px;
  1186. padding: 6px 2px;
  1187. }
  1188. }
  1189. </style>