| | |
| | | <template> |
| | | <view class="sockpage"> |
| | | <view class="column"> |
| | | <!-- 托盘编码栏(对应后端barcode) --> |
| | | <view class="form-card"> |
| | | <view class="form-row"> |
| | | <view class="label"> |
| | | <text class="required">*</text>托盘编码: |
| | | </view> |
| | | <view class="picker-wrap" @click="scan" hover-class="picker-hover"> |
| | | <view class="picker-wrap" hover-class="picker-hover"> |
| | | <view class="input-box"> |
| | | <text class="input-placeholder" v-if="!barcode">点击扫码录入</text> |
| | | <text class="input-value" v-else>{{barcode}}</text> |
| | | <input |
| | | id="pdacode" |
| | | type="text" |
| | | ref="barcodeInput" |
| | | v-model="barcode" |
| | | placeholder="请扫码(12位以下为托盘码)" |
| | | :focus="focusState" |
| | | @focus="focusFn" |
| | | @blur="focusState = false" |
| | | /> |
| | | </view> |
| | | <view class="scan-icon">📷</view> |
| | | </view> |
| | | </view> |
| | | </view> |
| | |
| | | class="material-input" |
| | | type="text" |
| | | v-model="matList.model" |
| | | placeholder="请输入卷号" |
| | | placeholder="请扫码或输入卷号" |
| | | placeholder-class="input-placeholder" |
| | | ref="modelInput" |
| | | /> |
| | | </view> |
| | | <view class="material-item"> |
| | |
| | | class="material-input" |
| | | type="text" |
| | | v-model="matList.batch" |
| | | placeholder="请输入箱号" |
| | | placeholder="请扫码或输入箱号" |
| | | placeholder-class="input-placeholder" |
| | | ref="batchInput" |
| | | /> |
| | | </view> |
| | | <view class="material-item"> |
| | |
| | | class="material-input" |
| | | type="number" |
| | | v-model="matList.rollExtent" |
| | | placeholder="请输入长度(m)" |
| | | placeholder="请输入长度" |
| | | placeholder-class="input-placeholder" |
| | | /> |
| | | </view> |
| | |
| | | class="material-input" |
| | | type="number" |
| | | v-model="matList.weight" |
| | | placeholder="请输入净重(kg)" |
| | | placeholder="请输入净重" |
| | | placeholder-class="input-placeholder" |
| | | /> |
| | | </view> |
| | |
| | | class="material-input" |
| | | type="number" |
| | | v-model="matList.roughWeight" |
| | | placeholder="请输入毛重(kg)" |
| | | placeholder="请输入毛重" |
| | | placeholder-class="input-placeholder" |
| | | /> |
| | | </view> |
| | |
| | | |
| | | <!-- 操作按钮组 --> |
| | | <view class="btn-group"> |
| | | <button class="operate-btn scan-btn" @click="scantwo">扫货物码</button> |
| | | <button class="operate-btn scan-btn" @click="startScan">开始扫码</button> |
| | | <button class="operate-btn submit-btn" @click="submit">成品入库</button> |
| | | </view> |
| | | </view> |
| | |
| | | // 页面加载时 mode 为空,调用接口后才填充数据 |
| | | this.POSTinfo(); |
| | | }, |
| | | |
| | | mounted() { |
| | | this.setupScanListener(); |
| | | }, |
| | | |
| | | beforeDestroy() { |
| | | this.removeScanListener(); |
| | | }, |
| | | |
| | | data() { |
| | | return { |
| | | focusState: false, |
| | | baseUrl: '', |
| | | barcodeNum:'', |
| | | barcode: '', // 托盘码 |
| | |
| | | anfme: 1.0, // 默认值1.0 |
| | | rollExtent: '', // 卷长度 |
| | | joint: 0, // 接头 |
| | | cutting: 2, // 默认“否” |
| | | qualified: 0, // 默认“否” |
| | | cutting: 2, |
| | | qualified: 0, |
| | | modelFront: '', // 分切前箱编号 |
| | | batchFront: '' ,// 分切前卷号 |
| | | }, |
| | | // 关键1:初始化 mode 为空数组 → 页面加载时箱型无数据 |
| | | // 初始化 mode 为空数组 → 页面加载时箱型无数据 |
| | | mode: [], |
| | | index_mode: 0, |
| | | index_num: 0, |
| | | submitData: {} // 最终提交给后端的数据 |
| | | submitData: {} ,// 最终提交给后端的数据 |
| | | scanBuffer: '', // 添加扫码缓冲区 |
| | | scanTimeout: null, // 扫码超时定时器 |
| | | isScanning: false, // 扫码状态标志 |
| | | scanMode: 'tray', // 扫码模式: tray-托盘码, material-物料码 |
| | | lastFillTarget: null // 记录上次填充的目标 |
| | | } |
| | | }, |
| | | methods: { |
| | |
| | | } |
| | | }) |
| | | }, |
| | | |
| | | // 设置扫码监听 |
| | | setupScanListener() { |
| | | // 移除之前的监听 |
| | | this.removeScanListener(); |
| | | |
| | | // 监听键盘输入事件 |
| | | document.addEventListener('keydown', this.handleKeyDown); |
| | | }, |
| | | |
| | | // 移除监听 |
| | | removeScanListener() { |
| | | document.removeEventListener('keydown', this.handleKeyDown); |
| | | if (this.scanTimeout) { |
| | | clearTimeout(this.scanTimeout); |
| | | } |
| | | }, |
| | | |
| | | // 键盘事件处理 |
| | | handleKeyDown(e) { |
| | | // 排除功能键(F1-F12等) |
| | | if (e.key.length > 1 && !['Enter', 'Tab'].includes(e.key)) { |
| | | return; |
| | | } |
| | | |
| | | // PDA扫码通常以回车结束 |
| | | if (e.key === 'Enter') { |
| | | e.preventDefault(); // 阻止默认行为 |
| | | this.processScanCode(); |
| | | } else { |
| | | // 累积扫码字符(排除Shift等修饰键) |
| | | if (!e.ctrlKey && !e.altKey && !e.metaKey && e.key.length === 1) { |
| | | this.scanBuffer += e.key; |
| | | |
| | | // 重置超时定时器 |
| | | if (this.scanTimeout) { |
| | | clearTimeout(this.scanTimeout); |
| | | } |
| | | |
| | | // 设置超时(假设扫码间隔超过200ms表示一次扫码完成) |
| | | this.scanTimeout = setTimeout(() => { |
| | | this.processScanCode(); |
| | | }, 200); |
| | | } |
| | | } |
| | | }, |
| | | |
| | | // 处理扫码结果 |
| | | processScanCode() { |
| | | if (!this.scanBuffer) return; |
| | | |
| | | const scanResult = this.scanBuffer.trim(); |
| | | this.scanBuffer = ''; |
| | | |
| | | // 判断扫码长度决定填充位置 |
| | | if (scanResult.length <= 12) { |
| | | // 12位以下填充到托盘编码 |
| | | this.barcode = scanResult; |
| | | this.lastFillTarget = 'tray'; |
| | | uni.showToast({ |
| | | title: '托盘码已填充', |
| | | icon: 'success', |
| | | duration: 1000 |
| | | }); |
| | | } else { |
| | | // 12位以上填充到物料信息 |
| | | this.autoFillMaterialInfo(scanResult); |
| | | this.lastFillTarget = 'material'; |
| | | uni.showToast({ |
| | | title: '物料码已填充', |
| | | icon: 'success', |
| | | duration: 1000 |
| | | }); |
| | | } |
| | | |
| | | // 清除定时器 |
| | | if (this.scanTimeout) { |
| | | clearTimeout(this.scanTimeout); |
| | | this.scanTimeout = null; |
| | | } |
| | | |
| | | // 自动失焦 |
| | | setTimeout(() => { |
| | | this.focusState = false; |
| | | }, 100); |
| | | }, |
| | | |
| | | // 自动填充物料信息 |
| | | autoFillMaterialInfo(code) { |
| | | // 尝试解析二维码内容 |
| | | if (code.includes('卷号:') && code.includes('箱号:')) { |
| | | // 如果是带格式的二维码 |
| | | const parseResult = this.parseQrCodeText(code); |
| | | this.matList = { ...this.matList, ...parseResult }; |
| | | console.log('解析后的物料信息:', parseResult); |
| | | } else { |
| | | // 如果是纯数字/条码,智能填充 |
| | | this.smartFillMaterialFields(code); |
| | | } |
| | | }, |
| | | |
| | | // 智能填充物料字段 |
| | | smartFillMaterialFields(code) { |
| | | // 根据业务逻辑判断,这里假设长条码可能是卷号或箱号 |
| | | // 如果卷号为空,优先填充卷号 |
| | | if (!this.matList.model) { |
| | | this.matList.model = code; |
| | | } |
| | | // 如果卷号已有值但箱号为空,填充箱号 |
| | | else if (!this.matList.batch) { |
| | | this.matList.batch = code; |
| | | } |
| | | // 如果两者都有值,提示用户 |
| | | else { |
| | | uni.showModal({ |
| | | title: '提示', |
| | | content: `扫描到长条码: ${code}\n请选择填充位置:`, |
| | | showCancel: true, |
| | | cancelText: '卷号', |
| | | confirmText: '箱号', |
| | | success: (res) => { |
| | | if (res.confirm) { |
| | | this.matList.batch = code; |
| | | } else if (res.cancel) { |
| | | this.matList.model = code; |
| | | } |
| | | } |
| | | }); |
| | | } |
| | | }, |
| | | |
| | | // 解析二维码文本(用于扫货物码功能) |
| | | parseQrCodeText(qrText) { |
| | | const result = {}; |
| | | const lines = qrText.split('\n'); |
| | | |
| | | lines.forEach(line => { |
| | | if (line.includes('卷号:')) { |
| | | result.model = line.split('卷号:')[1]?.trim() || ''; |
| | | } |
| | | if (line.includes('箱号:')) { |
| | | result.batch = line.split('箱号:')[1]?.trim() || ''; |
| | | } |
| | | if (line.includes('规格:')) { |
| | | result.matnr = line.split('规格:')[1]?.trim() || ''; |
| | | } |
| | | // 可以根据需要添加更多字段的解析 |
| | | }); |
| | | |
| | | return result; |
| | | }, |
| | | |
| | | // 手动开始扫码 |
| | | startScan() { |
| | | // 清空当前值并聚焦 |
| | | this.scanBuffer = ''; |
| | | |
| | | this.$nextTick(() => { |
| | | this.focusState = true; |
| | | |
| | | // 延迟确保input已聚焦 |
| | | setTimeout(() => { |
| | | const input = document.getElementById('pdacode'); |
| | | if (input) { |
| | | input.focus(); |
| | | input.select(); |
| | | } |
| | | }, 100); |
| | | }); |
| | | }, |
| | | |
| | | // 修改focusFn |
| | | focusFn() { |
| | | this.focusState = true; |
| | | this.scanBuffer = ''; // 聚焦时清空缓冲区 |
| | | }, |
| | | |
| | | // 保留原有的扫货物码功能 |
| | | scantwo() { |
| | | uni.scanCode({ |
| | | autoDecodeCharSet: true, |
| | | scanType: ['qrCode', 'barCode'], |
| | | success: (res) => { |
| | | const qrResult = res.result.trim(); |
| | | this.autoFillMaterialInfo(qrResult); |
| | | |
| | | uni.showToast({ |
| | | title: '物料信息扫码成功', |
| | | icon: 'success', |
| | | duration: 2000 |
| | | }); |
| | | }, |
| | | fail: (err) => { |
| | | console.log('扫码失败:', err); |
| | | uni.showToast({ |
| | | title: '扫码失败或取消', |
| | | icon: 'none' |
| | | }); |
| | | } |
| | | }); |
| | | }, |
| | | |
| | | modePickerChange(e) { |
| | | this.index_mode = e.detail.value; |
| | | this.boxType = this.mode[this.index_mode]; // 同步到boxType |
| | | }, |
| | | |
| | | numPickerChange(e) { |
| | | this.index_num = e.detail.value |
| | | this.index_num = e.detail.value; |
| | | }, |
| | | // 关键2:修改重置方法 → 清空 mode 及相关状态 |
| | | |
| | | // 修改重置方法 |
| | | resetForm() { |
| | | this.barcodeNum = ''; |
| | | this.barcode = ''; |
| | |
| | | this.index_mode = 0; |
| | | this.index_num = 0; |
| | | this.submitData = {}; |
| | | // 清空 mode 数组 → 箱型选择器恢复无数据状态 |
| | | this.mode = []; |
| | | // 重置物料信息(保留默认值) |
| | | this.lastFillTarget = null; |
| | | this.scanBuffer = ''; |
| | | |
| | | // 重置物料信息 |
| | | this.matList = { |
| | | matnr: '', |
| | | maktx: '', |
| | |
| | | modelFront: '', |
| | | batchFront: '' |
| | | }; |
| | | // 可选:重置后重新请求箱型数据(根据需求选择) |
| | | // this.POSTinfo(); |
| | | }, |
| | | submit(){ |
| | | // 校验必填项 |
| | | if(!this.barcode) return uni.showToast({title: '请扫描托盘编码', icon: 'none'}) |
| | | if(!this.boxType) return uni.showToast({title: '请选择箱型', icon: 'none'}) |
| | | if(!this.matList.model || !this.matList.batch) return uni.showToast({title: '请填写卷号/箱号', icon: 'none'}) |
| | | |
| | | // 数据类型转换 |
| | | const matItem = { |
| | | ...this.matList, |
| | | weight: this.matList.weight ? Number(this.matList.weight) : null, |
| | | roughWeight: this.matList.roughWeight ? Number(this.matList.roughWeight) : null, |
| | | rollExtent: this.matList.rollExtent ? Number(this.matList.rollExtent) : null, |
| | | joint: this.matList.joint ? Number(this.matList.joint) : 0, |
| | | anfme: 1.0, |
| | | cutting: 2, |
| | | qualified: 0 |
| | | } |
| | | |
| | | // 组装提交数据 |
| | | this.submitData = { |
| | | barcode: this.barcode, |
| | | palletizingNo: this.palletizingNo, |
| | | boxType: this.boxType, |
| | | matList: [matItem] |
| | | } |
| | | console.log('修正类型后的提交数据:', this.submitData) |
| | | |
| | | // 提交请求 |
| | | uni.request({ |
| | | url: this.baseUrl + '/mobile/truss/comd/auth/v2', |
| | | method: 'POST', |
| | | header: {'Content-Type': 'application/json'}, |
| | | data: this.submitData, |
| | | success: (res) => { |
| | | console.log('后端返回:', res) |
| | | uni.showToast({title:'提交成功',icon: 'success'}) |
| | | // 提交成功后重置所有数据 |
| | | this.resetForm(); |
| | | // 可选:重置后重新拉取箱型数据,方便下次操作 |
| | | // setTimeout(() => { |
| | | // this.POSTinfo(); |
| | | // }, 300); |
| | | }, |
| | | fail: (err) => { |
| | | console.error('请求失败详情:', err) |
| | | uni.showToast({title:'提交失败',icon: 'none'}) |
| | | }, |
| | | complete: (res) => { |
| | | console.log('请求完成状态:', res) |
| | | } |
| | | }) |
| | | }, |
| | | scan(){ |
| | | uni.scanCode({ |
| | | autoDecodeCharSet: true, |
| | | scanType: ['barCode'], |
| | | onlyFromCamera: true, |
| | | continuousScan: false, |
| | | camera: 'back', |
| | | timeout: 5000, |
| | | |
| | | submit() { |
| | | // 校验必填项 |
| | | if (!this.barcode) { |
| | | uni.showToast({title: '请扫描托盘编码', icon: 'none'}); |
| | | return; |
| | | } |
| | | |
| | | if (!this.boxType) { |
| | | uni.showToast({title: '请选择箱型', icon: 'none'}); |
| | | return; |
| | | } |
| | | |
| | | if (!this.matList.model || !this.matList.batch) { |
| | | uni.showToast({title: '请填写卷号和箱号', icon: 'none'}); |
| | | return; |
| | | } |
| | | |
| | | // 数据类型转换 |
| | | const matItem = { |
| | | ...this.matList, |
| | | weight: this.matList.weight ? Number(this.matList.weight) : null, |
| | | roughWeight: this.matList.roughWeight ? Number(this.matList.roughWeight) : null, |
| | | rollExtent: this.matList.rollExtent ? Number(this.matList.rollExtent) : null, |
| | | joint: this.matList.joint ? Number(this.matList.joint) : 0, |
| | | anfme: 1.0, |
| | | cutting: 2, |
| | | qualified: 0 |
| | | }; |
| | | |
| | | // 组装提交数据 |
| | | this.submitData = { |
| | | barcode: this.barcode, |
| | | palletizingNo: this.palletizingNo, |
| | | boxType: this.boxType, |
| | | matList: [matItem] |
| | | }; |
| | | |
| | | console.log('提交数据:', this.submitData); |
| | | |
| | | // 提交请求 |
| | | uni.request({ |
| | | url: this.baseUrl + '/mobile/truss/comd/auth/v2', |
| | | method: 'POST', |
| | | header: {'Content-Type': 'application/json'}, |
| | | data: this.submitData, |
| | | success: (res) => { |
| | | this.barcodeNum = res.result |
| | | this.barcode = res.result |
| | | uni.showToast({title: `托盘编码扫码成功`,icon: 'success'}) |
| | | console.log('后端返回:', res); |
| | | |
| | | if (res.data && res.data.code === 200) { |
| | | uni.showToast({title: '提交成功', icon: 'success'}); |
| | | this.resetForm(); |
| | | } else { |
| | | uni.showToast({title: res.data.message || '提交失败', icon: 'none'}); |
| | | } |
| | | }, |
| | | fail: (err) => { |
| | | uni.showToast({title: '扫码失败或取消',icon: 'none'}) |
| | | } |
| | | }) |
| | | }, |
| | | parseQrCodeText(text) { |
| | | const result = {}; |
| | | const regMap = { |
| | | model: /卷号:(.+)/, |
| | | batch: /箱号:(.+)/, |
| | | matnr: /规格:(.+)/, |
| | | rollExtent: /长度:(.+?)\s*m/, |
| | | weight: /净重:(.+?)\s*kg/, |
| | | roughWeight: /毛重:(.+?)\s*kg/, |
| | | joint: /接头:(.+?)\s*个/ |
| | | }; |
| | | Object.keys(regMap).forEach(key => { |
| | | const match = text.match(regMap[key]); |
| | | if (match && match[1]) { |
| | | result[key] = match[1].trim(); |
| | | console.error('请求失败详情:', err); |
| | | uni.showToast({title: '网络请求失败', icon: 'none'}); |
| | | } |
| | | }); |
| | | return result; |
| | | }, |
| | | scantwo(){ |
| | | uni.scanCode({ |
| | | autoDecodeCharSet:true, |
| | | scanType: ['qrCode'], |
| | | success: (res) => { |
| | | const qrResult = res.result.trim(); |
| | | this.barcodeNum = qrResult; |
| | | let parseResult = {}; |
| | | |
| | | try { |
| | | parseResult = JSON.parse(qrResult); |
| | | parseResult = { |
| | | model: parseResult.卷号 || '', |
| | | batch: parseResult.箱号 || '', |
| | | matnr: parseResult.规格 || '', |
| | | rollExtent: (parseResult.长度 || '').replace(/\s*m/g, '') || '', |
| | | weight: (parseResult.净重 || '').replace(/\s*kg/g, '') || '', |
| | | roughWeight: (parseResult.毛重 || '').replace(/\s*kg/g, '') || '', |
| | | joint: (parseResult.接头 || '').replace(/\s*个/g, '') || '' |
| | | }; |
| | | } catch (e) { |
| | | parseResult = this.parseQrCodeText(qrResult); |
| | | } |
| | | |
| | | this.matList = { ...this.matList, ...parseResult }; |
| | | uni.showToast({title: `物料信息扫码成功`,icon: 'success'}); |
| | | }, |
| | | fail: (err) => { |
| | | uni.showToast({title: '扫码失败或取消',icon: 'none'}) |
| | | } |
| | | }) |
| | | } |
| | | } |
| | | } |
| | |
| | | |
| | | .input-box:focus-within { |
| | | border-color: #f97316; |
| | | } |
| | | |
| | | /* 占位符样式 */ |
| | | .input-placeholder { |
| | | color: #9ca3af; |
| | | } |
| | | |
| | | /* 输入值样式 */ |