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.

616 lines
17 KiB

  1. <template>
  2. <div class="customer-css">
  3. <el-dialog :title="titleCon" v-drag v-bind="$attrs" v-on="$listeners"
  4. width="600px" class="material-dialog">
  5. <div class="material-content">
  6. <el-form :model="pageData" label-position="top" label-width="100px">
  7. <!-- 材料信息卡片 -->
  8. <div class="material-info-card">
  9. <div class="info-header">
  10. <i class="el-icon-document"></i>
  11. <span>材料信息</span>
  12. </div>
  13. <div class="info-content">
  14. <el-row :gutter="15">
  15. <el-col :span="12">
  16. <div class="info-row">
  17. <label class="info-label">材料卷号</label>
  18. <span class="info-value">{{ pageData.rmRollNo || '-' }}</span>
  19. </div>
  20. </el-col>
  21. <el-col :span="12">
  22. <div class="info-row">
  23. <label class="info-label">零部件编码</label>
  24. <span class="info-value">{{ pageData.partNo || '-' }}</span>
  25. </div>
  26. </el-col>
  27. </el-row>
  28. <el-row :gutter="15">
  29. <el-col :span="24">
  30. <div class="info-row">
  31. <label class="info-label">零部件名称</label>
  32. <span class="info-value">{{ pageData.partDesc || '-' }}</span>
  33. </div>
  34. </el-col>
  35. </el-row>
  36. <el-row :gutter="15">
  37. <el-col :span="12">
  38. <div class="info-row">
  39. <label class="info-label">规格型号</label>
  40. <span class="info-value">{{ pageData.spec || '-' }}</span>
  41. </div>
  42. </el-col>
  43. </el-row>
  44. </div>
  45. </div>
  46. <!-- 损耗设置区域 -->
  47. <div class="return-settings">
  48. <el-row :gutter="15">
  49. <el-col :span="8">
  50. <el-form-item label="上机数量" class="form-item-enhanced">
  51. <el-input
  52. v-model="displayOnMachineQty"
  53. disabled
  54. size="large"
  55. style="width: 100%">
  56. </el-input>
  57. </el-form-item>
  58. </el-col>
  59. <el-col :span="8">
  60. <el-form-item label="暂存数量" class="form-item-enhanced">
  61. <el-input class="inlineNumber numInput"
  62. v-model="pageData.returnQty"
  63. type="number"
  64. placeholder="请输入暂存数量"
  65. size="large"
  66. @input="calculateRemainQty"
  67. style="width: 100%">
  68. </el-input>
  69. </el-form-item>
  70. </el-col>
  71. <el-col :span="8">
  72. <el-form-item label="剩余数量" class="form-item-enhanced">
  73. <el-input
  74. v-model="displayRemainQty"
  75. disabled
  76. size="large"
  77. :class="{'remain-negative': pageData.remainQty < 0}"
  78. style="width: 100%">
  79. </el-input>
  80. </el-form-item>
  81. </el-col>
  82. </el-row>
  83. <!-- 备注 -->
  84. <el-row style="margin-bottom: 30px">
  85. <el-col :span="24">
  86. <el-form-item label="备注" >
  87. <el-input
  88. v-model="pageData.remark"
  89. type="textarea"
  90. :rows="3"
  91. placeholder="请输入备注"
  92. show-word-limit>
  93. </el-input>
  94. </el-form-item>
  95. </el-col>
  96. </el-row>
  97. </div>
  98. </el-form>
  99. </div>
  100. <span slot="footer" class="dialog-footer">
  101. <button class="action-btn secondary" @click="submitMaterialLoss">
  102. {{ buttons.confirmButton }}
  103. </button>
  104. <button class="action-btn secondary" @click="closeDialog">
  105. {{ buttons.closeButton }}
  106. </button>
  107. </span>
  108. </el-dialog>
  109. </div>
  110. </template>
  111. <script>
  112. import {
  113. materialProcessLoss
  114. } from '@/api/yieldReport/com_material_loss.js';
  115. import getLodop from '@/utils/LodopFuncs.js'; // 打印控件
  116. import labelPrintTemplates from '@/mixins/labelPrintTemplates.js'; // 打印模板mixin
  117. var functionId = 'C10000019';
  118. export default {
  119. mixins: [labelPrintTemplates], // 添加打印模板mixin
  120. data() {
  121. return {
  122. titleCon: '材料暂存',
  123. scheduleData: {
  124. site: this.$store.state.user.site,
  125. username: this.$store.state.user.name,
  126. seqNo: '',
  127. orderNo: '',
  128. itemNo: 0,
  129. },
  130. pageData: {
  131. site: this.$store.state.user.site,
  132. orderNo: '',
  133. itemNo: '',
  134. seqNo: '',
  135. rollNo: '',
  136. rmRollNo: '',
  137. partNo: '',
  138. partDesc: '',
  139. spec: '',
  140. onMachineQty: 0, // 上机数量(来自行数据的数量)
  141. returnQty: 0, // 损耗数量
  142. remainQty: 0, // 剩余数量
  143. remark: '', // 备注
  144. histSeqNo: '',
  145. operatorId: '',
  146. },
  147. operatorData: {
  148. site: this.$store.state.user.site,
  149. username: this.$store.state.user.name,
  150. operatorId: '',
  151. operatorName: '',
  152. },
  153. buttons: {
  154. confirmButton: '确定',
  155. closeButton: '关闭',
  156. },
  157. }
  158. },
  159. computed: {
  160. // 显示上机数量(如果为0或空则显示空字符串)
  161. displayOnMachineQty() {
  162. return this.pageData.onMachineQty || this.pageData.onMachineQty === 0 ? String(this.pageData.onMachineQty) : '';
  163. },
  164. // 显示剩余数量(如果为0或空则显示空字符串)
  165. displayRemainQty() {
  166. return this.pageData.remainQty || this.pageData.remainQty === 0 ? String(this.pageData.remainQty) : '';
  167. }
  168. },
  169. methods: {
  170. //初始化组件的参数
  171. init(scheduleData, operatorData, materialRow) {
  172. //初始化参数
  173. this.scheduleData = scheduleData;
  174. this.operatorData = JSON.parse(JSON.stringify(operatorData));
  175. //设置材料信息
  176. this.pageData.orderNo = scheduleData.orderNo;
  177. this.pageData.itemNo = scheduleData.itemNo;
  178. this.pageData.seqNo = scheduleData.seqNo;
  179. this.pageData.rollNo = scheduleData.rollNo;
  180. this.pageData.operatorId = operatorData.operatorId;
  181. // 从行数据获取材料信息
  182. this.pageData.rmRollNo = materialRow.rmRollNo || '';
  183. this.pageData.partNo = materialRow.partNo || '';
  184. this.pageData.partDesc = materialRow.partDesc || '';
  185. this.pageData.spec = materialRow.spec || '';
  186. this.pageData.histSeqNo = materialRow.histSeqNo || '';
  187. // 获取上机数量(从行数据的 transQty 字段)
  188. const transQty = parseFloat(materialRow.transQty);
  189. this.pageData.onMachineQty = isNaN(transQty) ? 0 : transQty;
  190. // 重置损耗数量、剩余数量和备注
  191. this.pageData.returnQty = 0;
  192. this.pageData.remainQty = this.pageData.onMachineQty;
  193. this.pageData.remark = '';
  194. this.titleCon = '材料暂存';
  195. },
  196. /*关闭modal*/
  197. closeDialog(){
  198. //刷新报工的页面
  199. this.$emit('refreshPageData');
  200. //关闭当前的页面
  201. this.$emit('update:visible', false);
  202. },
  203. /*计算剩余数量*/
  204. calculateRemainQty() {
  205. const onMachineQty = parseFloat(this.pageData.onMachineQty) || 0;
  206. const returnQty = parseFloat(this.pageData.returnQty) || 0;
  207. this.pageData.remainQty = onMachineQty - returnQty;
  208. },
  209. /*提交损耗*/
  210. submitMaterialLoss() {
  211. // 如果没有填写损耗数量,默认为0
  212. if (!this.pageData.returnQty || this.pageData.returnQty === '' || this.pageData.returnQty < 0) {
  213. this.pageData.returnQty = 0;
  214. }
  215. // 计算剩余数量
  216. this.calculateRemainQty();
  217. // 验证剩余数量必须大于等于0
  218. if (this.pageData.remainQty < 0) {
  219. this.$message.warning('暂存数量不能大于上机数量!');
  220. return false;
  221. }
  222. // 构建提交数据(只传损耗数量和备注,不传上机数量和剩余数量)
  223. const submitData = {
  224. site: this.pageData.site,
  225. orderNo: this.pageData.orderNo,
  226. itemNo: this.pageData.itemNo,
  227. seqNo: this.pageData.seqNo,
  228. rollNo: this.pageData.rmRollNo,
  229. returnQty: this.pageData.returnQty,
  230. histSeqNo: this.pageData.histSeqNo,
  231. operatorId: this.pageData.operatorId,
  232. remark: this.pageData.remark || '' // 备注(可选,默认为空字符串)
  233. };
  234. // 调用后端API(调用 MaterialProcessLoss 存储过程)
  235. materialProcessLoss(submitData).then(({data}) => {
  236. //判断是否存在异常
  237. if(data.code == 500 || data.code == 400){
  238. this.$message.error(data.msg || data.message);
  239. } else if (data.code == 201) {
  240. // code=201 表示需要打印标签
  241. this.$message.success(data.msg || '操作成功');
  242. // 获取打印参数并打印(存储过程返回的打印参数在printData中)
  243. if (data.printData) {
  244. // 将单个打印数据对象包装成数组
  245. const printDataList = [data.printData];
  246. this.executePrint(printDataList);
  247. } else {
  248. console.warn('未获取到打印参数');
  249. }
  250. //关闭当前的页面
  251. this.closeDialog();
  252. } else {
  253. //先提示 后关闭
  254. this.$message.success(data.msg || data.message || '操作成功');
  255. //关闭当前的页面
  256. this.closeDialog();
  257. }
  258. }).catch((error) => {
  259. this.$message.error('操作失败:' + (error.message || '未知错误'));
  260. });
  261. },
  262. /**
  263. * 执行打印直接使用存储过程返回的打印参数
  264. * @param {Array} printDataList - 存储过程返回的打印数据列表
  265. */
  266. executePrint(printDataList) {
  267. try {
  268. // 1. 获取 LODOP 打印控件
  269. const LODOP = getLodop();
  270. if (!LODOP) {
  271. this.$message.warning('无法连接到打印控件,跳过打印');
  272. return;
  273. }
  274. // 2. 获取默认打印机
  275. const printerCount = LODOP.GET_PRINTER_COUNT();
  276. if (printerCount === 0) {
  277. this.$message.warning('未检测到打印机,跳过打印');
  278. return;
  279. }
  280. const defaultPrinterName = LODOP.GET_PRINTER_NAME(0);
  281. // 3. 检查打印数据中的labelNo(标签模板编号)
  282. if (!printDataList || printDataList.length === 0) {
  283. console.warn('打印数据为空');
  284. return;
  285. }
  286. // 获取第一条数据的labelNo
  287. const labelNo = printDataList[0].labelNo;
  288. if (!labelNo) {
  289. console.warn('未找到标签模板编号');
  290. return;
  291. }
  292. // 4. 执行模板打印
  293. this.executePrintWithTemplate(LODOP, printDataList, labelNo, defaultPrinterName);
  294. this.$message.success('标签打印任务已发送!');
  295. } catch (error) {
  296. console.error('打印失败:', error);
  297. this.$message.warning('打印失败: ' + error.message);
  298. }
  299. },
  300. /**
  301. * 执行模板打印
  302. * @param {Object} LODOP - 打印控件对象
  303. * @param {Array} printDataList - 打印数据列表
  304. * @param {String} labelNo - 标签模板编号 (A001/A002/A003)
  305. * @param {String} printerName - 打印机名称
  306. */
  307. executePrintWithTemplate(LODOP, printDataList, labelNo, printerName) {
  308. LODOP.PRINT_INIT('材料暂存标签打印');
  309. // 设置打印模式,隐藏水印
  310. LODOP.SET_PRINT_MODE("PRINT_NOCOLLATE", true);
  311. // 设置打印机
  312. LODOP.SET_PRINTER_INDEX(printerName);
  313. // 循环打印每个标签
  314. for (let i = 0; i < printDataList.length; i++) {
  315. const printData = printDataList[i];
  316. const isNewPage = i > 0;
  317. // 根据 labelNo 调用不同的打印方法(来自 labelPrintTemplates mixin)
  318. if (labelNo === 'A001') {
  319. this.printLabelA001(LODOP, printData, isNewPage);
  320. } else if (labelNo === 'A002') {
  321. this.printLabelA002(LODOP, printData, isNewPage);
  322. } else if (labelNo === 'A003') {
  323. this.printLabelA003(LODOP, printData, isNewPage);
  324. } else {
  325. console.warn(`未知的标签模板:${labelNo}`);
  326. }
  327. }
  328. // 预览打印(避免水印)
  329. LODOP.PREVIEW();
  330. },
  331. },
  332. }
  333. </script>
  334. <style scoped lang="scss">
  335. .numInput /deep/ .el-input__inner{
  336. text-align: right;
  337. }
  338. /deep/ .inlineNumber input::-webkit-outer-spin-button,
  339. /deep/ .inlineNumber input::-webkit-inner-spin-button {
  340. -webkit-appearance: none;
  341. }
  342. /deep/ .inlineNumber input[type="number"]{
  343. -moz-appearance: textfield;
  344. padding-right: 5px !important;
  345. }
  346. // 材料暂存对话框样式
  347. .material-dialog {
  348. ::v-deep .el-dialog {
  349. border-radius: 8px;
  350. overflow: hidden;
  351. .el-dialog__header {
  352. background: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
  353. padding: 18px 20px;
  354. .el-dialog__title {
  355. color: #fff;
  356. font-size: 16px;
  357. font-weight: 600;
  358. }
  359. .el-dialog__headerbtn {
  360. .el-dialog__close {
  361. color: #fff;
  362. font-size: 18px;
  363. &:hover {
  364. color: #f0f0f0;
  365. }
  366. }
  367. }
  368. }
  369. .el-dialog__body {
  370. padding: 25px;
  371. background-color: #f8f9fa;
  372. }
  373. .el-dialog__footer {
  374. padding: 15px 20px;
  375. background-color: #fff;
  376. border-top: 1px solid #e9ecef;
  377. }
  378. }
  379. }
  380. .material-content {
  381. .form-item-enhanced {
  382. margin-bottom: 10px;
  383. ::v-deep .el-form-item__label {
  384. color: #495057;
  385. font-weight: 500;
  386. font-size: 14px;
  387. margin-bottom: 8px;
  388. }
  389. ::v-deep .el-input {
  390. .el-input__inner {
  391. border-radius: 6px;
  392. border: 1px solid #dee2e6;
  393. transition: all 0.3s;
  394. height: 42px;
  395. line-height: 42px;
  396. font-size: 15px;
  397. &:focus {
  398. border-color: #667eea;
  399. box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1);
  400. }
  401. }
  402. }
  403. // 文本域样式
  404. ::v-deep .el-textarea {
  405. .el-textarea__inner {
  406. border-radius: 6px;
  407. border: 1px solid #dee2e6;
  408. transition: all 0.3s;
  409. font-size: 14px;
  410. padding: 8px 12px;
  411. line-height: 1.5;
  412. &:focus {
  413. border-color: #667eea;
  414. box-shadow: 0 0 0 3px rgba(102, 126, 234, 0.1);
  415. }
  416. }
  417. }
  418. // 剩余数量为负数时的红色警告样式
  419. ::v-deep .remain-negative {
  420. .el-input__inner {
  421. color: #f56c6c;
  422. font-weight: 600;
  423. border-color: #f56c6c;
  424. background-color: #fef0f0;
  425. }
  426. }
  427. }
  428. }
  429. // 物料信息卡片样式 - 白色框连起来
  430. .material-info-card {
  431. background: linear-gradient(135deg, #667eea15 0%, #764ba215 100%);
  432. border-radius: 8px;
  433. padding: 16px;
  434. margin-bottom: 20px;
  435. border: 1px solid #e3e8f0;
  436. box-shadow: 0 2px 8px rgba(102, 126, 234, 0.08);
  437. animation: fadeInDown 0.5s ease-out;
  438. .info-header {
  439. display: flex;
  440. align-items: center;
  441. margin-bottom: 12px;
  442. padding-bottom: 10px;
  443. border-bottom: 1px solid #dee2e6;
  444. i {
  445. font-size: 18px;
  446. color: #667eea;
  447. margin-right: 8px;
  448. }
  449. span {
  450. font-size: 14px;
  451. font-weight: 600;
  452. color: #495057;
  453. }
  454. }
  455. .info-content {
  456. background-color: rgba(255, 255, 255, 0.8);
  457. border-radius: 4px;
  458. padding: 12px;
  459. .info-row {
  460. display: flex;
  461. align-items: center;
  462. padding: 4px 0;
  463. &:not(:last-child) {
  464. border-bottom: 1px dashed #e9ecef;
  465. }
  466. .info-label {
  467. font-size: 13px;
  468. color: #6c757d;
  469. min-width: 90px;
  470. font-weight: 500;
  471. }
  472. .info-value {
  473. font-size: 13px;
  474. color: #212529;
  475. font-weight: 600;
  476. flex: 1;
  477. }
  478. }
  479. }
  480. }
  481. // 暂存设置区域
  482. .return-settings {
  483. background: #fff;
  484. border-radius: 8px;
  485. padding: 16px;
  486. border: 1px solid #e3e8f0;
  487. box-shadow: 0 2px 8px rgba(102, 126, 234, 0.05);
  488. }
  489. // 底部按钮样式
  490. .dialog-footer {
  491. display: flex;
  492. justify-content: center;
  493. gap: 12px;
  494. padding: 0;
  495. .action-btn {
  496. min-width: 80px;
  497. padding: 6px 16px;
  498. border-radius: 16px;
  499. font-size: 13px;
  500. font-weight: 500;
  501. cursor: pointer;
  502. transition: all 0.2s ease;
  503. display: inline-flex;
  504. align-items: center;
  505. justify-content: center;
  506. gap: 4px;
  507. i {
  508. font-size: 13px;
  509. }
  510. &.primary {
  511. background: #17B3A3;
  512. border: none;
  513. color: white;
  514. &:hover {
  515. background: #13998b;
  516. box-shadow: 0 4px 12px rgba(23, 179, 163, 0.4);
  517. transform: translateY(-1px);
  518. }
  519. &:active {
  520. transform: translateY(0);
  521. }
  522. }
  523. &.secondary {
  524. background: white;
  525. border: 1px solid #17B3A3;
  526. color: #17B3A3;
  527. &:hover {
  528. background: #17B3A3;
  529. color: white;
  530. }
  531. &:active {
  532. transform: scale(0.98);
  533. }
  534. }
  535. }
  536. }
  537. // 动画效果
  538. @keyframes fadeInDown {
  539. from {
  540. opacity: 0;
  541. transform: translateY(-10px);
  542. }
  543. to {
  544. opacity: 1;
  545. transform: translateY(0);
  546. }
  547. }
  548. </style>