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.

1047 lines
23 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
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
3 months ago
5 months ago
3 months ago
5 months ago
3 months ago
5 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 clearable class="compact-input"
  16. v-model="scanCode"
  17. placeholder="请扫描标签条码"
  18. prefix-icon="el-icon-search"
  19. @keyup.enter.native="handleScan"
  20. ref="scanInput"
  21. />
  22. <div class="mode-switch">
  23. <el-switch
  24. class="custom-switch"
  25. v-model="isRemoveMode"
  26. active-color="#ff4949"
  27. inactive-color="#13ce66">
  28. </el-switch>
  29. <span v-if="isRemoveMode" class="switch-text">{{ '移除' }}</span>
  30. <span v-else class="switch-text2">{{ '添加' }}</span>
  31. </div>
  32. </div>
  33. <!-- 出库单信息卡片 -->
  34. <div class="material-info-card" v-if="outboundNo">
  35. <div class="card-title">
  36. <span class="title-label">出库单号:{{ outboundInfo.outboundNo }}</span>
  37. </div>
  38. <div class="card-title">
  39. <span class="title-label">工单号:{{ outboundInfo.orderNo }}</span>
  40. </div>
  41. <div class="card-title">
  42. <span class="title-label">物料号:{{ outboundInfo.componentPartNo }}</span>
  43. </div>
  44. <div class="card-details">
  45. <div class="detail-item">
  46. <div class="detail-label">行号</div>
  47. <div class="detail-value">
  48. <span class="qualified">{{ outboundInfo.lineItemNo }}</span>
  49. </div>
  50. </div>
  51. <div class="detail-item">
  52. <div class="detail-label">需求数量</div>
  53. <div class="detail-value">
  54. <span class="qualified">{{ outboundInfo.qtyToIssue }}</span>
  55. </div>
  56. </div>
  57. <div class="detail-item">
  58. <div class="detail-label">本次数量</div>
  59. <div class="detail-value">
  60. <span class="qualified">{{ totalScannedQty }}</span>
  61. </div>
  62. </div>
  63. </div>
  64. </div>
  65. <!-- 出库信息确认标题 -->
  66. <div class="section-title">
  67. <div class="title-left">
  68. <i class="el-icon-circle-check"></i>
  69. <span>出库信息确认</span>
  70. </div>
  71. <!-- <div class="title-right">
  72. <span class="material-list-link" @click="showMaterialListDialog">物料清单</span>
  73. </div> -->
  74. </div>
  75. <!-- 标签列表 -->
  76. <div class="label-list">
  77. <div class="list-header">
  78. <div class="col-no">NO.</div>
  79. <div class="col-label">标签条码</div>
  80. <div class="col-part">库位</div>
  81. <div class="col-qty">标签数量</div>
  82. </div>
  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.locationId }}</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. <!-- 底部操作按钮 -->
  99. <div class="bottom-actions">
  100. <el-button class="action-btn secondary" @click="confirmOutbound" :loading="pickingLoading">
  101. 确定
  102. </el-button>
  103. <!-- <button class="action-btn secondary" style="margin-left: 10px;" @click="printLabels">
  104. 打印
  105. </button> -->
  106. <button class="action-btn secondary" style="margin-left: 10px;" @click="cancelOutbound">
  107. 取消
  108. </button>
  109. </div>
  110. <!-- 物料清单弹窗 -->
  111. <div v-if="showMaterialDialog" class="material-overlay">
  112. <div class="material-modal">
  113. <div class="modal-header">
  114. <span class="modal-title">物料清单</span>
  115. <i class="el-icon-close close-btn" @click="closeMaterialDialog"></i>
  116. </div>
  117. <div class="modal-body">
  118. <!-- 加载状态 -->
  119. <div v-if="materialListLoading" class="loading-container">
  120. <i class="el-icon-loading"></i>
  121. <span>加载中...</span>
  122. </div>
  123. <!-- 物料表格 -->
  124. <div v-else-if="materialList.length > 0" class="material-table">
  125. <div class="table-header">
  126. <div class="col-no">NO.</div>
  127. <div class="col-material-code">物料编码</div>
  128. <div class="col-required-qty">需求数量</div>
  129. <div class="col-picked-qty">已领数量</div>
  130. </div>
  131. <div class="table-body">
  132. <div
  133. v-for="(item, index) in materialList"
  134. :key="index"
  135. class="table-row"
  136. >
  137. <div class="col-no">{{ index + 1 }}</div>
  138. <div class="col-material-code">{{ item.materialCode || item.partNo }}</div>
  139. <div class="col-required-qty">{{ item.requiredQty || 0 }}</div>
  140. <div class="col-picked-qty">{{ item.pickedQty || 0 }}</div>
  141. </div>
  142. </div>
  143. </div>
  144. <!-- 空数据状态 -->
  145. <div v-else class="empty-material">
  146. <i class="el-icon-document"></i>
  147. <p>暂无物料数据</p>
  148. </div>
  149. </div>
  150. <div class="modal-footer">
  151. <button class="btn-close" @click="closeMaterialDialog">关闭</button>
  152. </div>
  153. </div>
  154. </div>
  155. </div>
  156. </template>
  157. <script>
  158. import { scanMaterialLabelIssue,getRequestMaterials,confirmProductionPicking } from '@/api/production/production-issue';
  159. import moment from 'moment';
  160. import { notify } from 'node-notifier';
  161. export default {
  162. data() {
  163. return {
  164. scanCode: '',
  165. outboundInfo: {},
  166. labelList: [],
  167. outboundNo: '',
  168. buNo: '',
  169. showMaterialDialog: false,
  170. materialList: [],
  171. materialListLoading: false,
  172. isRemoveMode: false ,// 默认为添加模式
  173. pickingLoading: false
  174. };
  175. },
  176. computed: {
  177. totalScannedQty() {
  178. return this.labelList.reduce(
  179. (sum, l) => sum + (Number(l.quantity) || 0),
  180. 0
  181. );
  182. },
  183. },
  184. methods: {
  185. formatDate(date) {
  186. return date ? moment(date).format('YYYY-MM-DD') : '';
  187. },
  188. // 处理扫描
  189. handleScan() {
  190. if (!this.scanCode.trim()) {
  191. return;
  192. }
  193. if (this.isRemoveMode) {
  194. this.removeLabelByCode(this.scanCode.trim());
  195. } else {
  196. this.validateAndAddLabel(this.scanCode.trim());
  197. }
  198. this.scanCode = '';
  199. },
  200. // 验证标签并添加到列表
  201. validateAndAddLabel(labelCode) {
  202. const params = {
  203. scannedLabel: labelCode,
  204. notifyNo: this.outboundNo,
  205. site: localStorage.getItem('site'),
  206. componentPartNo: this.outboundInfo.componentPartNo
  207. };
  208. scanMaterialLabelIssue(params).then(({ data }) => {
  209. if (data && data.code === 0) {
  210. // 检查是否已经扫描过
  211. const exists = this.labelList.find(item => item.labelCode === labelCode);
  212. if (exists) {
  213. this.$message.warning('该标签已扫描,请勿重复扫描');
  214. return;
  215. }
  216. // 添加到列表
  217. this.labelList.push({
  218. id: Date.now(),
  219. labelCode: labelCode,
  220. partNo: data.labelInfo.partNo,
  221. quantity: data.labelInfo.availableQty,
  222. unit: data.labelInfo.unit,
  223. batchNo: data.labelInfo.batchNo,
  224. locationId: data.labelInfo.locationId,
  225. warehouseId: data.labelInfo.warehouseId,
  226. wdrNo: data.labelInfo.wdrNo,
  227. });
  228. this.$message.success('操作成功');
  229. } else {
  230. this.$message.error(data.msg || '该标签与出库单不符,请检查');
  231. }
  232. }).catch(error => {
  233. this.$message.error('操作失败');
  234. });
  235. },
  236. // 通过条码移除标签
  237. removeLabelByCode(labelCode) {
  238. const index = this.labelList.findIndex(item => item.labelCode === labelCode);
  239. if (index !== -1) {
  240. this.labelList.splice(index, 1);
  241. this.$message.success('操作成功');
  242. } else {
  243. this.$message.warning('未找到该标签');
  244. }
  245. },
  246. // 确认出库
  247. confirmOutbound() {
  248. if (this.labelList.length === 0) {
  249. this.$message.warning('请先扫描标签');
  250. return;
  251. }
  252. let qty = 0;
  253. for(const label of this.labelList) {
  254. if (label.quantity <= 0) {
  255. this.$message.warning(`标签 ${label.labelCode} 数量无效`);
  256. return;
  257. }
  258. qty += label.quantity;
  259. }
  260. if (qty > this.outboundInfo.qtyToIssue) {
  261. this.$message.warning('扫描标签总数量超过需求数量');
  262. this.$confirm('取消后将清空已扫描的标签,确定取消吗?', '提示', {
  263. confirmButtonText: '确定',
  264. cancelButtonText: '继续操作',
  265. type: 'warning'
  266. }).then(() => {
  267. this.confirmProductionPicking();
  268. })
  269. }else{
  270. this.confirmProductionPicking();
  271. }
  272. },
  273. confirmProductionPicking(){
  274. const params = {
  275. site: localStorage.getItem('site'),
  276. workOrderNo: this.outboundInfo.orderNo,
  277. componentPartNo: this.outboundInfo.componentPartNo,
  278. operatorName: localStorage.getItem('userName'),
  279. itemNo: this.outboundInfo.lineItemNo,
  280. releaseNo:this.outboundInfo.releaseNo,
  281. sequenceNo:this.outboundInfo.sequenceNo,
  282. selectedMaterials: this.labelList.map((l, i) => ({
  283. labelCode: l.labelCode,
  284. issueQty: l.quantity,
  285. batchNo: l.batchNo,
  286. warehouseId: l.warehouseId,
  287. locationId: l.locationId,
  288. materialCode: l.materialCode,
  289. wdrNo: l.wdrNo
  290. })),
  291. };
  292. this.pickingLoading = true
  293. confirmProductionPicking(params).then(({ data }) => {
  294. if (data && data.code === 0) {
  295. this.$message.success('操作成功');
  296. this.$router.back();
  297. } else {
  298. this.$message.error(data.msg || '操作失败');
  299. }
  300. }).catch(error => {
  301. console.error('出库确认失败:', error);
  302. this.$message.error('操作失败');
  303. }).finally(() => {
  304. this.pickingLoading = false
  305. });
  306. },
  307. // 打印标签
  308. printLabels() {
  309. if (this.labelList.length === 0) {
  310. this.$message.warning('暂无标签可打印');
  311. return;
  312. }
  313. this.$message.warning('打印功能开发中...');
  314. },
  315. // 取消出库
  316. cancelOutbound() {
  317. if (this.labelList.length > 0) {
  318. this.$confirm('取消后将清空已扫描的标签,确定取消吗?', '提示', {
  319. confirmButtonText: '确定',
  320. cancelButtonText: '继续操作',
  321. type: 'warning'
  322. }).then(() => {
  323. this.$router.back();
  324. }).catch(() => {
  325. // 用户选择继续操作
  326. });
  327. } else {
  328. this.$router.back();
  329. }
  330. },
  331. // 显示物料清单弹窗
  332. showMaterialListDialog() {
  333. this.showMaterialDialog = true;
  334. this.loadMaterialList();
  335. },
  336. // 加载物料清单
  337. loadMaterialList() {
  338. console.log('加载物料清单', this.outboundInfo, this.outboundNo);
  339. if (!localStorage.getItem('site') || !this.outboundNo) {
  340. this.$message.error('缺少必要参数,无法获取物料清单');
  341. return;
  342. }
  343. this.materialListLoading = true;
  344. const params = {
  345. site: localStorage.getItem('site'),
  346. outboundNo: this.outboundNo
  347. };
  348. getRequestMaterials(params).then(({ data }) => {
  349. this.materialListLoading = false;
  350. if (data && data.code === 0) {
  351. this.materialList = data.materials || [];
  352. } else {
  353. this.$message.error(data.msg || '获取物料清单失败');
  354. this.materialList = [];
  355. }
  356. }).catch(error => {
  357. this.materialListLoading = false;
  358. this.$message.error('获取物料清单失败');
  359. this.materialList = [];
  360. });
  361. },
  362. // 关闭物料清单弹窗
  363. closeMaterialDialog() {
  364. this.showMaterialDialog = false;
  365. },
  366. // 加载出库单详情
  367. loadOutboundDetails() {
  368. const params = {
  369. outboundNo: this.outboundNo,
  370. site: localStorage.getItem('site'),
  371. };
  372. console.log('加载出库单详情参数:', params);
  373. /* getOutboundDetails(params).then(({ data }) => {
  374. if (data && data.code === 0) {
  375. this.outboundInfo = data.data;
  376. } else {
  377. this.$message.error(data.msg || '获取出库单详情失败');
  378. }
  379. }).catch(error => {
  380. console.error('获取出库单详情失败:', error);
  381. this.$message.error('获取出库单详情失败');
  382. }); */
  383. }
  384. },
  385. mounted() {
  386. // 获取路由参数
  387. this.outboundNo = this.$route.query.outboundNo
  388. this.outboundInfo.outboundNo = this.$route.query.outboundNo
  389. this.outboundInfo.orderNo = this.$route.query.notifyInfo.orderNo
  390. this.outboundInfo.componentPartNo = this.$route.query.notifyInfo.componentPartNo
  391. this.outboundInfo.qtyToIssue = this.$route.query.notifyInfo.qtyToIssue
  392. this.outboundInfo.lineItemNo = this.$route.query.notifyInfo.lineItemNo
  393. this.outboundInfo.releaseNo = this.$route.query.notifyInfo.releaseNo
  394. this.outboundInfo.sequenceNo = this.$route.query.notifyInfo.sequenceNo
  395. console.log("路由参数:", this.$route.query.notifyInfo );
  396. if (!this.outboundNo) {
  397. this.$message.error('参数错误');
  398. this.$router.back();
  399. return;
  400. }
  401. // 聚焦扫描框
  402. this.$nextTick(() => {
  403. if (this.$refs.scanInput) {
  404. this.$refs.scanInput.focus();
  405. }
  406. });
  407. // 加载出库单详情
  408. this.loadOutboundDetails();
  409. }
  410. };
  411. </script>
  412. <style scoped>
  413. /* 复用入库页面的样式,只修改必要的部分 */
  414. .pda-container {
  415. width: 100vw;
  416. height: 100vh;
  417. display: flex;
  418. flex-direction: column;
  419. background: #f5f5f5;
  420. }
  421. /* 头部栏 */
  422. .header-bar {
  423. display: flex;
  424. justify-content: space-between;
  425. align-items: center;
  426. padding: 8px 16px;
  427. background: #17B3A3;
  428. color: white;
  429. height: 40px;
  430. min-height: 40px;
  431. }
  432. .header-left {
  433. display: flex;
  434. align-items: center;
  435. cursor: pointer;
  436. font-size: 16px;
  437. font-weight: 500;
  438. }
  439. .header-left i {
  440. margin-right: 8px;
  441. font-size: 18px;
  442. }
  443. .header-right {
  444. cursor: pointer;
  445. font-size: 16px;
  446. font-weight: 500;
  447. }
  448. /* 搜索容器 */
  449. .search-container {
  450. padding: 12px 16px;
  451. background: white;
  452. display: flex;
  453. align-items: center;
  454. gap: 12px;
  455. }
  456. .search-container .el-input {
  457. width: 240px;
  458. margin-right: 12px;
  459. }
  460. /* 紧凑型输入框样式 */
  461. .compact-input ::v-deep .el-input__inner {
  462. height: 36px;
  463. padding: 0 12px 0 35px;
  464. font-size: 14px;
  465. }
  466. .compact-input ::v-deep .el-input__prefix {
  467. left: 10px;
  468. }
  469. .compact-input ::v-deep .el-input__suffix {
  470. right: 30px;
  471. }
  472. /* 模式切换开关 */
  473. .mode-switch {
  474. position: relative;
  475. display: inline-block;
  476. }
  477. .custom-switch {
  478. transform: scale(1.3);
  479. }
  480. /* 中间文字 */
  481. .switch-text {
  482. position: absolute;
  483. left: 25%;
  484. transform: translateX(-50%);
  485. top: 50%;
  486. transform: translateY(-50%) translateX(-50%);
  487. font-size: 12px;
  488. font-weight: 500;
  489. color: #606266;
  490. white-space: nowrap;
  491. pointer-events: none;
  492. z-index: 1;
  493. top: 53%;
  494. transform: translate(-50%, -50%);
  495. font-size: 12px;
  496. font-weight: bold;
  497. color: white;
  498. pointer-events: none;
  499. z-index: 2;
  500. }
  501. .switch-text2 {
  502. position: absolute;
  503. left: 75%;
  504. transform: translateX(-50%);
  505. top: 50%;
  506. transform: translateY(-50%) translateX(-50%);
  507. font-size: 12px;
  508. font-weight: 500;
  509. color: #606266;
  510. white-space: nowrap;
  511. pointer-events: none;
  512. z-index: 1;
  513. top: 53%;
  514. transform: translate(-50%, -50%);
  515. font-size: 12px;
  516. font-weight: bold;
  517. color: white;
  518. pointer-events: none;
  519. z-index: 2;
  520. }
  521. /* 调整 switch 尺寸以便容纳文字 */
  522. .custom-switch ::v-deep .el-switch__core {
  523. width: 60px;
  524. height: 28px;
  525. }
  526. /* 物料信息卡片 */
  527. .material-info-card {
  528. background: white;
  529. margin: 4px 10px;
  530. padding: 6px 20px;
  531. border-radius: 8px;
  532. box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
  533. border: 1px solid #f0f0f0;
  534. }
  535. .card-title {
  536. margin-bottom: 16px;
  537. }
  538. .title-label {
  539. font-size: 11px;
  540. color: #999;
  541. display: block;
  542. margin-bottom: 6px;
  543. font-weight: normal;
  544. }
  545. .title-value {
  546. font-size: 18px;
  547. font-weight: bold;
  548. color: #333;
  549. line-height: 1.2;
  550. margin-left: 20px;
  551. }
  552. .card-details {
  553. display: flex;
  554. justify-content: space-between;
  555. align-items: flex-start;
  556. gap: 4px;
  557. }
  558. .detail-item {
  559. flex: 1;
  560. text-align: center;
  561. min-width: 60px;
  562. max-width: 60px;
  563. }
  564. .detail-label {
  565. font-size: 11px;
  566. color: #999;
  567. margin-bottom: 6px;
  568. font-weight: normal;
  569. line-height: 1.2;
  570. margin-left: -12px;
  571. }
  572. .detail-value {
  573. font-size: 13px;
  574. color: #333;
  575. font-weight: 500;
  576. line-height: 1.2;
  577. margin-left: -12px;
  578. }
  579. .detail-value .qualified {
  580. color: #17B3A3;
  581. font-weight: 500;
  582. }
  583. .detail-value .total {
  584. color: #333;
  585. font-weight: 500;
  586. }
  587. .detail-value .total::before {
  588. content: '/';
  589. color: #333;
  590. }
  591. /* 区域标题 */
  592. .section-title {
  593. display: flex;
  594. align-items: center;
  595. justify-content: space-between;
  596. padding: 6px 8px;
  597. background: white;
  598. margin: 0 10px;
  599. margin-top: 4px;
  600. border-radius: 8px 8px 0 0;
  601. border-bottom: 2px solid #17B3A3;
  602. }
  603. .title-left {
  604. display: flex;
  605. align-items: center;
  606. }
  607. .title-left i {
  608. color: #17B3A3;
  609. font-size: 16px;
  610. margin-right: 8px;
  611. }
  612. .title-left span {
  613. color: #17B3A3;
  614. font-size: 14px;
  615. font-weight: 500;
  616. }
  617. .title-right {
  618. display: flex;
  619. align-items: center;
  620. }
  621. .material-list-link {
  622. color: #17B3A3;
  623. font-size: 14px;
  624. font-weight: 500;
  625. cursor: pointer;
  626. text-decoration: underline;
  627. transition: color 0.2s ease;
  628. }
  629. .material-list-link:hover {
  630. color: #0d8f7f;
  631. }
  632. /* 标签列表 */
  633. .label-list {
  634. background: white;
  635. margin: 0 16px 12px;
  636. border-radius: 0 0 8px 8px;
  637. overflow: hidden;
  638. }
  639. .list-header {
  640. display: flex;
  641. background: #f8f9fa;
  642. padding: 12px 8px;
  643. border-bottom: 1px solid #e0e0e0;
  644. font-size: 12px;
  645. color: #666;
  646. font-weight: 500;
  647. }
  648. .list-item {
  649. display: flex;
  650. padding: 12px 8px;
  651. border-bottom: 1px solid #f0f0f0;
  652. font-size: 12px;
  653. color: #333;
  654. align-items: flex-start;
  655. min-height: 40px;
  656. }
  657. .list-item:last-child {
  658. border-bottom: none;
  659. }
  660. .col-no {
  661. width: 20px;
  662. text-align: center;
  663. }
  664. .col-label {
  665. flex: 1.5;
  666. text-align: center;
  667. word-break: break-all;
  668. white-space: normal;
  669. line-height: 1.2;
  670. }
  671. .col-part {
  672. flex: 2;
  673. text-align: center;
  674. }
  675. .col-unit {
  676. width: 40px;
  677. text-align: center;
  678. }
  679. .col-qty {
  680. width: 60px;
  681. text-align: center;
  682. }
  683. .empty-labels {
  684. padding: 40px 20px;
  685. text-align: center;
  686. color: #999;
  687. }
  688. .empty-labels p {
  689. margin: 0;
  690. font-size: 14px;
  691. }
  692. /* 底部操作按钮 */
  693. .bottom-actions {
  694. display: flex;
  695. padding: 16px;
  696. gap: 20px;
  697. background: white;
  698. margin-top: auto;
  699. }
  700. .action-btn {
  701. flex: 1;
  702. padding: 12px;
  703. border: 1px solid #17B3A3;
  704. background: white;
  705. color: #17B3A3;
  706. border-radius: 20px;
  707. font-size: 14px;
  708. cursor: pointer;
  709. transition: all 0.2s ease;
  710. }
  711. .action-btn:hover {
  712. background: #17B3A3;
  713. color: white;
  714. }
  715. .action-btn:active {
  716. transform: scale(0.98);
  717. }
  718. /* 物料清单弹窗样式 */
  719. .material-overlay {
  720. position: fixed;
  721. top: 0;
  722. left: 0;
  723. right: 0;
  724. bottom: 0;
  725. background: rgba(0, 0, 0, 0.5);
  726. z-index: 9999;
  727. display: flex;
  728. align-items: center;
  729. justify-content: center;
  730. padding: 20px;
  731. }
  732. .material-modal {
  733. background: white;
  734. border-radius: 12px;
  735. width: 100%;
  736. max-width: 800px;
  737. max-height: 80vh;
  738. box-shadow: 0 8px 32px rgba(0, 0, 0, 0.3);
  739. overflow: hidden;
  740. display: flex;
  741. flex-direction: column;
  742. }
  743. .material-modal .modal-header {
  744. background: #17B3A3;
  745. color: white;
  746. padding: 5px 16px;
  747. display: flex;
  748. justify-content: space-between;
  749. align-items: center;
  750. min-height: 28px;
  751. }
  752. .close-btn {
  753. font-size: 16px;
  754. cursor: pointer;
  755. color: white;
  756. transition: color 0.2s ease;
  757. padding: 4px;
  758. display: flex;
  759. align-items: center;
  760. justify-content: center;
  761. }
  762. .close-btn:hover {
  763. color: #e0e0e0;
  764. }
  765. .material-modal .modal-title {
  766. font-size: 16px;
  767. font-weight: 500;
  768. margin: 0;
  769. line-height: 1.2;
  770. }
  771. .material-modal .modal-body {
  772. flex: 1;
  773. overflow: auto;
  774. padding: 0;
  775. }
  776. .material-table {
  777. width: 100%;
  778. }
  779. .table-header {
  780. display: flex;
  781. background: #f8f9fa;
  782. padding: 10px 6px;
  783. border-bottom: 2px solid #17B3A3;
  784. font-size: 12px;
  785. color: #333;
  786. font-weight: 600;
  787. position: sticky;
  788. top: 0;
  789. z-index: 1;
  790. }
  791. .table-body {
  792. max-height: 400px;
  793. overflow-y: auto;
  794. }
  795. .table-row {
  796. display: flex;
  797. padding: 10px 6px;
  798. border-bottom: 1px solid #f0f0f0;
  799. font-size: 12px;
  800. color: #333;
  801. transition: background-color 0.2s ease;
  802. }
  803. .table-row:hover {
  804. background-color: #f8f9fa;
  805. }
  806. .table-row:last-child {
  807. border-bottom: none;
  808. }
  809. .material-table .col-no {
  810. width: 25px;
  811. text-align: center;
  812. flex-shrink: 0;
  813. font-size: 12px;
  814. }
  815. .material-table .col-material-code {
  816. flex: 1.8;
  817. text-align: center;
  818. min-width: 100px;
  819. font-size: 12px;
  820. word-break: break-all;
  821. }
  822. .material-table .col-required-qty {
  823. flex: 0.8;
  824. text-align: center;
  825. min-width: 65px;
  826. font-size: 12px;
  827. }
  828. .material-table .col-picked-qty {
  829. flex: 0.8;
  830. text-align: center;
  831. min-width: 65px;
  832. font-size: 12px;
  833. }
  834. .material-modal .modal-footer {
  835. padding: 15px 20px;
  836. display: flex;
  837. justify-content: center;
  838. border-top: 1px solid #f0f0f0;
  839. }
  840. .btn-close {
  841. padding: 10px 20px;
  842. border-radius: 6px;
  843. font-size: 14px;
  844. cursor: pointer;
  845. transition: all 0.2s;
  846. border: 1px solid #17B3A3;
  847. background: white;
  848. color: #17B3A3;
  849. }
  850. .btn-close:hover {
  851. background: #17B3A3;
  852. color: white;
  853. }
  854. /* 加载状态样式 */
  855. .loading-container {
  856. display: flex;
  857. flex-direction: column;
  858. align-items: center;
  859. justify-content: center;
  860. padding: 60px 20px;
  861. color: #666;
  862. }
  863. .loading-container i {
  864. font-size: 24px;
  865. margin-bottom: 12px;
  866. color: #17B3A3;
  867. }
  868. .loading-container span {
  869. font-size: 14px;
  870. }
  871. /* 空数据状态样式 */
  872. .empty-material {
  873. display: flex;
  874. flex-direction: column;
  875. align-items: center;
  876. justify-content: center;
  877. padding: 60px 20px;
  878. color: #999;
  879. }
  880. .empty-material i {
  881. font-size: 48px;
  882. margin-bottom: 16px;
  883. color: #ddd;
  884. }
  885. .empty-material p {
  886. margin: 0;
  887. font-size: 14px;
  888. }
  889. /* 响应式设计 */
  890. @media (max-width: 360px) {
  891. .header-bar {
  892. padding: 8px 12px;
  893. }
  894. .search-container {
  895. padding: 8px 12px;
  896. }
  897. .material-info-card {
  898. margin: 4px 12px;
  899. padding: 6px 16px;
  900. }
  901. .section-title {
  902. margin: 0 12px;
  903. margin-top: 4px;
  904. }
  905. .label-list {
  906. margin: 0 12px 8px;
  907. }
  908. .card-details {
  909. flex-wrap: wrap;
  910. gap: 6px;
  911. }
  912. .detail-item {
  913. flex: 0 0 48%;
  914. margin-bottom: 6px;
  915. min-width: 50px;
  916. }
  917. .list-header, .list-item {
  918. font-size: 11px;
  919. }
  920. .col-label, .col-part {
  921. flex: 1.5;
  922. }
  923. }
  924. </style>